Thursday, September 14, 2017

hide .json files when included in Cocoa Touch Framework

Leave a Comment

I would like to release a cocoa touch framework including some json files which contain logic I don't want the framwork's user to see; unfortunately inside the .framework file there is still the json visible.

I thought about including it in a swift file:

struct JsonProvider {     static let json = "..." } 

but my json is that large that the file isn't usable any more. I didn't find a solution to command line compile and then include it.

Is there a solution to one of the two problems, i.e.

  • hide the json inside the framework or
  • precompile a swift file and then add it to the framework?

2 Answers

Answers 1

So I created a JSONEncoder struct as follows:

public struct JSONEncoder {    private static var JSON = ""   public static var JSONData = [UInt8]()   private static var cipher = [UInt8]()   public static var encryptedJSONData = [UInt8]()   public static var decryptedJSONData = [UInt8]()   private static var decryptedJSON = ""    public static func JSONFileToString() {             guard let JSONFilePath = Bundle.main.url(forResource: "JSONFile", withExtension:"json"), let cipherFilePath = Bundle.main.url(forResource: "JSONCipher", withExtension:"txt") else {       return     }      do {       let text = try String(contentsOf: cipherFilePath, encoding: String.Encoding.utf8)                 cipher = [UInt8](text.utf8)       let data = try Data(contentsOf:JSONFilePath)        if let JSON = String(data: data, encoding: String.Encoding.utf8) {         self.JSON = JSON         JSONData = [UInt8](JSON.utf8)         //print("data read: \(JSON)")       } else {         print("Data to string conversion failed")       }     } catch {       print(error.localizedDescription)     }   }    public static func encodeJSON() {     // encrypt bytes     for t in JSONData.enumerated() {       encryptedJSONData.append(t.element ^ cipher[t.offset])     }     print(encryptedJSONData)   }    public static func decodeJSON() {     for t in encryptedJSONData.enumerated() {       decryptedJSONData.append(t.element ^ cipher[t.offset])     }     decryptedJSON = String(bytes: decryptedJSONData, encoding: String.Encoding.utf8)!     //print(decryptedJSON)   }  } 

To set this up you need to add a JSONFile.json file containing the JSON you want to encode. Add it to copy bundle resources. Repeat this for the cipher text file JSONCipher.txt. Make sure this file contains no speech marks, as this brings errors and that it is sufficiently large enough to encode the JSON. Don't make it too large as otherwise you get a memory warning.

You can play around with this, to encode a JSON file with XOR, using

JSONEncoder.JSONFileToString() JSONEncoder.encodeJSON() 

This will then print to the console, and you can copy this code to a txt file attached to your framework.

For decrypting this file, you can test as follows:

JSONEncoder.decodeJSON() 

(Uncommenting the print statement in decodeJSON in the JSONEncoder struct, can test this works)

If your JSON file is too large for this program, you can actually use this tool: https://www.browserling.com/tools/xor-encrypt, and similar XOR Decrypter on the same domain. You can then use the output from this encoder to add the encrypted JSON file to your framework. Just make sure you use the same key when you decode the json!

Note that this stores your text file in the actual project: this is potentially insecure. Maybe host the JSONCipher.txt file on a server and fetch that using an authenticated access token. The code I provided can be modified to allow for this.

I hope this has pointed you in the right direction for code obfuscation.

Answers 2

You can add json file content to sources as base64 encoded string.

struct JSONFiles {     internal static let fileOneBase64String = "iVBORw0KGgoAAAANSUhEUgAAAMEAAADLCAYAAADX..."     static var fileOneJSON: [String: Any] {         let data = Data(base64Encoded: fileOneBase64String)!         return try! JSONSerialization.jsonObject(with: data, options: []) as!  [String: Any]     } } 

You can use it later anywhere in you framework by

print(JSONFiles.fileOneJSON) 

Hope it help you

UPDATE: Why as base64 encoded string?

The main benefit base64 encoded string - you don't need to escape special symbols contained in JSON string. Don't need to remove new line symbols/intendation symbols from json string before add to swift source file. See example

let string: String = "{ \"name\":\"John\", \"email\": \"hello@stackoverflow\" \"age\":30, \"car\":null }" 

But if json is more complex? If it contains 10 nested levels, arrays, dictionaries?

You can cover by UnitTests that value contained in fileOneBase64String can be decoded to JSON object

import XCTest @testable import MyAwesomeFramework  class FrameworkTests: XCTestCase {      func testThatBase64EncodedJSONStringCanBeDecoded() {         let data = Data(base64Encoded: JSONFiles.fileOneBase64String)         XCTAssertNotNil(data, "Unable to convert base64 string to Data")         do {             _ = try JSONSerialization.jsonObject(with: data, options: [])         } catch {             XCTFail("Expected decoded JSON ")         }     } } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment