1

I am decrypting an image sent from a php server. I am using the CryptoSwift library to decrypt the image, the image comes in as NSData, and after the decryption, I create an UIImage from the NSData.

But the decryption takes about 1minute and 10 seconds, which is very slow. The size of the image data:

println(imageData.length)
result: 32592

I believe that's not a big file right? This is the code I use to decrypt the image data:

let aes = AES(key: keyData, iv: ivData, blockMode: .ECB) 
let decryptedData = aes?.decrypt(encryptedSnap, removePadding: true)
let image = UIImage(data: decryptedData!)

I've tried running the process on different threads, but it gave the same result. When I am decrypting the image, the simulator uses 100% CPU, and about 21.5MB of ram.

Any help is appreciated, thanks!

Rick
  • 3,168
  • 3
  • 17
  • 16
  • 3
    1. There is something else going on, the encryption should be almost intantaneous for a file size of 32,592 bytes. 2. Do not use ECB mode, see [here](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) (scroll down to the penguin image) for why. – zaph Mar 16 '15 at 14:36
  • Definitely depends on the device and whether it has hardware support for encryption. – qwerty_so Mar 16 '15 at 14:36
  • Well, I am not the one who encrypts the image, so I can't change the encryption mode. But that's what I thought as well, it should be instant right? What am I missing here? – Rick Mar 16 '15 at 14:38
  • Try using the code from this [SO answer](http://stackoverflow.com/a/25755864/451475) to see if it is the library you are using is the problem. Just add the `kCCOptionECBMode` option and ignore the iv. – zaph Mar 16 '15 at 14:43
  • The SO Answer gives an example on encrypting, but I need decrypting. – Rick Mar 16 '15 at 16:23
  • The code is virtually the same, a quick guess is to just change `kCCEncrypt` to `kCCDecrypt`. I will verify that later. – zaph Mar 16 '15 at 18:20
  • I've tried that already and it seemed to do something, I mean I was getting NSData back, but when I printed it to the console it looked more like a encrypted string, and when I created a new UIImage with that data, the UIImage returned nil, whereas the lib I was using worked fine, but very very slow. – Rick Mar 16 '15 at 18:31
  • Is the key data (not a string) and exactly the correct length? What key size are you using? What are the actual lengths of the key? Did you add `kCCOptionECBMode` to the options, CBC is the default. There is no need for an iv in ECB mode. – zaph Mar 16 '15 at 19:39
  • Note: CryptoSwift is over 1000 times slower than Apple's Common Crypto CCCrypt on an iPhone 6. – zaph Aug 10 '15 at 23:23

1 Answers1

1

Swift 2.0

Here is some simple sample code encrypting and decrypting NSData with ECB mode and a 128-bit AES key.

Test code

let keyString = "M02cnQ51Ji97vwT4"
let keyData = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!

let message = "Don´t try to read this text. Top Secret Stuff"
let data = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!

print("data: \(data)")
if let encryptedData = testCrypt(data, keyData:keyData, operation:UInt32(kCCEncrypt)) {
    print("encryptedData: \(encryptedData)")
    if let decryptedData = testCrypt(encryptedData, keyData:keyData, operation:UInt32(kCCDecrypt)) {
        print("decryptedData: \(decryptedData)")
    }
}

Crypto method:

func testCrypt(data:NSData, keyData:NSData, operation:CCOperation) -> NSData? {
    let keyBytes = UnsafePointer<UInt8>(keyData.bytes)
    print("keyLength   = \(keyData.length), keyData   = \(keyData)")

    let dataLength = Int(data.length)
    let dataBytes  = UnsafePointer<UInt8>(data.bytes)
    print("dataLength  = \(dataLength), data      = \(data)")

    let cryptData: NSMutableData! = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
    let cryptPointer = UnsafeMutablePointer<UInt8>(cryptData.mutableBytes)
    let cryptLength  = size_t(cryptData.length)

    let keyLength              = size_t(kCCKeySizeAES128)
    let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
    let options:   CCOptions   = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)

    var numBytesEncrypted :size_t = 0

    let cryptStatus = CCCrypt(operation,
        algoritm,
        options,
        keyBytes, keyLength,
        nil,
        dataBytes, dataLength,
        cryptPointer, cryptLength,
        &numBytesEncrypted)

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.length = Int(numBytesEncrypted)
        print("cryptLength = \(numBytesEncrypted), cryptData = \(cryptData)")

    } else {
        print("Error: \(cryptStatus)")
    }

    return cryptData;
}

Output:

data:                         <446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666>
keyLength   = 16, keyData   = <4d303263 6e513531 4a693937 76775434>
dataLength  = 46, data      = <446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666>

cryptLength = 48, cryptData = <5fd86c65 6544720c 9659b43f 2e77bf8d 9c2373d9 e1042a3d ce9a19f8 2900521e c3f8075a b6866ba5 2fcd5793 bbeb8e0c>
encryptedData:                <5fd86c65 6544720c 9659b43f 2e77bf8d 9c2373d9 e1042a3d ce9a19f8 2900521e c3f8075a b6866ba5 2fcd5793 bbeb8e0c>
keyLength   = 16, keyData   = <4d303263 6e513531 4a693937 76775434>
dataLength  = 48, data      = <5fd86c65 6544720c 9659b43f 2e77bf8d 9c2373d9 e1042a3d ce9a19f8 2900521e c3f8075a b6866ba5 2fcd5793 bbeb8e0c>

cryptLength = 46, cryptData = <446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666>
decryptedData:                <446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666>
zaph
  • 111,848
  • 21
  • 189
  • 228
  • Thanks, I will try it and let you know :) – Rick Mar 16 '15 at 20:47
  • Still there is the question of the key you are using and the encryption key size: 128-bit or 256-bit. Make sure your key length matchthe encryption key size. Also what padding is used, php does not use standard PKCS#7 padding. – zaph Mar 16 '15 at 20:52
  • Im not sure of the key size, its just a string, how do I know? And its padded with PKCS#5 but that should be compatible with #7 right? – Rick Mar 16 '15 at 21:00
  • PKCS#5 and 7 are the same for padding. The key size is the length in bits of the key you supply, the length of the NSData. The options for AES are: 128, 192 and 256, 128 is sufficient, 192 is rarley used. Note that encryption works with 8-bit **data**, it has no concept of any representation of the data. Text should not be used, if that is going to be the key it needs to be extended with a method such as PKDF2 (Password Key Derivation Function. The actual encryption is easy, getting the security right is hard. – zaph Mar 16 '15 at 21:37
  • Thanks for the help so far! I have the working decryption code in php, but getting it to work in swift is harder than I thought, I will try and figure something out using your answers, but maybe you could take a look at the php code? I would appreciate it since im struggling on this for almost 3 days – Rick Mar 16 '15 at 21:52
  • Do you mind following me back on twitter? Maybe I can explain it a little better in DM. – Rick Mar 16 '15 at 22:07
  • Thanks, the code you provided gives me back a bunch of NSData when I log it, it seems ok, but when I create an UIImage back from it it returns nil again. – Rick Mar 16 '15 at 22:48
  • Basically I am trying to decrypt a snap send with SnapChat, this is a simple documentation of the encryption [Documentation](http://gibsonsec.org/snapchat/fulldisclosure/#encryptingdecrypting-data) Maybe this gives you an idea of how I could do this, thank you once again! – Rick Mar 16 '15 at 22:51
  • Encryption is really simple. Make the key and iv (if not ECB) the correct length. Provide the same options. Provide the same data. The output will be the same. If the output is not the same the input is not the same. The inputs and outputs need to be examined as 8-bit data, that is what they are. The common errors are a short key and/or iv and mis-matching padding. – zaph Mar 16 '15 at 22:56
  • THANK YOU SO MUCH! It finally worked! The reason it didn't work is because the kCCKeySizeAES256 needed to be kCCKeySizeAES128 because the length of the key was 16. Im a noob in encryption/decryption, but thanks to you I got it figured out! – Rick Mar 16 '15 at 23:08
  • SnapChat says they are using AES-128 with the key: M02cnQ51Ji97vwT4 and PCKS#5 padding. I have updated the code sample for that. You get the data back from the server as NSData, just use that. – zaph Mar 16 '15 at 23:08
  • Use of undeclared type 'CCOperation' please help me – Puji Wahono Jul 24 '18 at 05:09
  • See [AES encryption in swift](https://stackoverflow.com/a/37681510/451475) for more complete examples. – zaph Jul 24 '18 at 10:31
  • @jeet.chanchawat Se the link above your comment, it has Swift 2, 3 & 4 code. – zaph Nov 22 '18 at 00:54
  • That link uses iVData. I was looking for a solution without iv. Studied so many things over internet. now, I believe my concept of encryption/decryption is clear. Here is the solution for future users : https://stackoverflow.com/a/53406308/884674 – jeet.chanchawat Nov 22 '18 at 07:21
  • 1
    All that is necessary to make it ECB is delete the IV code and add `kCCOptionECBMode`. Note: Do not use ECB mode in new work and update legacy work ASAP, it is not secure, see [ECB mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29), scroll down to the Penguin. Instead use CBC mode with a random IV, just prefix the encrypted data with the IV for use in decryption, it does not need to be secret. – zaph Nov 22 '18 at 11:49