1

I am planning to implement the AES encryption in my application and for this I went through an informative tutorial by Rob Napier :

It was a wonderful read and I was able to encrypt few strings using :

USING ROB NAPIER RNCRYPTOR CLASS

NSString * const
kRNCryptManagerErrorDomain = @"net.robnapier.RNCryptManager";

const CCAlgorithm kAlgorithm = kCCAlgorithmAES128;
const NSUInteger kAlgorithmKeySize = kCCKeySizeAES128;
const NSUInteger kAlgorithmBlockSize = kCCBlockSizeAES128;
const NSUInteger kAlgorithmIVSize = kCCBlockSizeAES128;
const NSUInteger kPBKDFSaltSize = 8;
const NSUInteger kPBKDFRounds = 10000;  // ~80ms on an iPhone 4

// ===================

+ (NSData *)encryptedDataForData:(NSData *)data
                        password:(NSString *)password
                              iv:(NSData **)iv
                            salt:(NSData **)salt
                           error:(NSError **)error {
  NSAssert(iv, @"IV must not be NULL");
  NSAssert(salt, @"salt must not be NULL");
  
  *iv = [self randomDataOfLength:kAlgorithmIVSize];
  *salt = [self randomDataOfLength:kPBKDFSaltSize];
  
  NSData *key = [self AESKeyForPassword:password salt:*salt];
  
  size_t outLength;
  NSMutableData *
  cipherData = [NSMutableData dataWithLength:data.length +
                kAlgorithmBlockSize];

  CCCryptorStatus
  result = CCCrypt(kCCEncrypt, // operation
                   kAlgorithm, // Algorithm
                   kCCOptionPKCS7Padding, // options
                   key.bytes, // key
                   key.length, // keylength
                   (*iv).bytes,// iv
                   data.bytes, // dataIn
                   data.length, // dataInLength,
                   cipherData.mutableBytes, // dataOut
                   cipherData.length, // dataOutAvailable
                   &outLength); // dataOutMoved

  if (result == kCCSuccess) {
    cipherData.length = outLength;
  }
  else {
    if (error) {
      *error = [NSError errorWithDomain:kRNCryptManagerErrorDomain
                                   code:result
                               userInfo:nil];
    }
    return nil;
  }
  
  return cipherData;
}

// ===================

+ (NSData *)randomDataOfLength:(size_t)length {
  NSMutableData *data = [NSMutableData dataWithLength:length];
  
  int result = SecRandomCopyBytes(kSecRandomDefault, 
                                  length,
                                  data.mutableBytes);
  NSAssert(result == 0, @"Unable to generate random bytes: %d",
           errno);
  
  return data;
}

// ===================

// Replace this with a 10,000 hash calls if you don't have CCKeyDerivationPBKDF
+ (NSData *)AESKeyForPassword:(NSString *)password 
                         salt:(NSData *)salt {
  NSMutableData *
  derivedKey = [NSMutableData dataWithLength:kAlgorithmKeySize];
  
  int 
  result = CCKeyDerivationPBKDF(kCCPBKDF2,            // algorithm
                                password.UTF8String,  // password
                                [password lengthOfBytesUsingEncoding:NSUTF8StringEncoding],  // passwordLength
                                salt.bytes,           // salt
                                salt.length,          // saltLen
                                kCCPRFHmacAlgSHA1,    // PRF
                                kPBKDFRounds,         // rounds
                                derivedKey.mutableBytes, // derivedKey
                                derivedKey.length); // derivedKeyLen
  
  // Do not log password here
  NSAssert(result == kCCSuccess,
           @"Unable to create AES key for password: %d", result);
  
  return derivedKey;
}

But while decrypting I am not able to decrypt properly and I am getting null in the scenario: For your reference the decrypt code is :

    + (NSData*)decryptData:(NSData*)data key:(NSData*)key error:(NSError **)error
{
    if (key.length != 16 && key.length != 24 && key.length != 32) {
        *error = [NSError errorWithDomain:@"keyLengthError" code:-1 userInfo:nil];
        return nil;
    }

    
    CCCryptorStatus ccStatus   = kCCSuccess;
    int             ivLength   = kCCBlockSizeAES128;
    size_t          clearBytes = 0;
    NSMutableData *dataOut     = [NSMutableData dataWithLength:data.length - ivLength];
    
    NSLog(@"Data Out String Decrypt%@", dataOut);

    ccStatus = CCCrypt(kCCDecrypt,
                       kCCAlgorithmAES,
                       kCCOptionPKCS7Padding,
                       key.bytes,
                       key.length,
                       data.bytes,
                       data.bytes + ivLength,
                       data.length - ivLength,
                       dataOut.mutableBytes,
                       dataOut.length,
                       &clearBytes);

    if (ccStatus == kCCSuccess) {
        dataOut.length = clearBytes;
    }
    else {
        if (error) {
            *error = [NSError errorWithDomain:@"kEncryptionError" code:ccStatus userInfo:nil];
        }
        dataOut = nil;
    }

    return dataOut;
}

Where I am getting wrong in this scenario? I have been trying for few days to sort it out. Can someone please help me?

User1075
  • 819
  • 15
  • 36
  • https://stackoverflow.com/questions/64679465/how-to-add-salt-to-aes-encryption-in-ios-and-decrypting-it-using-objective-c This is the question which I posted earlier in case if you require more clarification but I wasn’t able to prepend salt value like the IV ... so please suggest me with a proper code ... it would be more helpful – User1075 Nov 05 '20 at 08:00
  • Basically in short I just want to prepend salt before ( IV + Cipher text ) and use this for decrypting – User1075 Nov 05 '20 at 08:01
  • when this `;` is in your code on the implementation side then any outcome must be nil. – Ol Sen Nov 05 '20 at 08:08
  • Yes but still I am not getting a proper decrypting and it returns nil .... I am in search of a good decrypt code by using salt and IV ... so if you can help me please it would be very useful – User1075 Nov 05 '20 at 08:10
  • why are you adding and subtracting BlockSizeAES128 while crypting? Are you trying to crypt only the last bytes? – Ol Sen Nov 05 '20 at 08:56
  • This is for (IV and Cipher text ) which works well in iOS and android the logic for Salt pretending needs to be done while encrypting/decrypting that’s the issue here – User1075 Nov 05 '20 at 09:03
  • So I am not sure about Salt prepending and decrypting hopefully you can help me with that – User1075 Nov 05 '20 at 09:05

1 Answers1

2

the method given in the example you mentioned refers Rob Napiers Github Repo. Just testet it with your given password, salt, etc.. and it just works!
Yes understood, you want to throw out password: and iv: as well the salt: parameter when decrypting and go only with key:. Well you need at least iv: to do that. But again as Rob commented to your other question, don't re-invent the wheel.

The method i linked above is just working fine with your parameters for decrypting. The only difference to your code is that password, iv and salt are given to decrypt.

apart from the idea you want to develop something that can decrypt without a password you will have to digg deeper into how CCKeyDerivationPBKDF() (CommonKeyDerivation.h) is working.

Edit: As you asked to have a way to pack and unpack your salt, iv and cypher thats pretty simple with NSData.

+ (NSData *)packWithSalt:(NSData*)salt IV:(NSData*)iv Cypher:(NSData*)tocypher {
    
    //adding Salt + IV + Cipher text
    NSMutableData *combi = [NSMutableData data];
    
    //[combi appendBytes:salt.bytes length:16];
    //[combi appendBytes:iv.bytes length:16]; //16
    //[combi appendBytes:tocypher.bytes length:tocypher.length];
    
    [combi appendData:salt];
    [combi appendData:iv];
    [combi appendData:tocypher];
    
    return combi;
}

+ (NSData*)cypherUnpackToSalt:(NSMutableData**)salt andIV:(NSMutableData**)iv fromPackData:(NSData*)pack {
    
    void *sBuff[16] = {};
    void *iBuff[16] = {};
    NSUInteger len = pack.length - 16 - 16; //keep length flexible
    void *pBuff = malloc(sizeof(Byte)*len); //needs dynamically size of buff
    [pack getBytes:sBuff range:NSMakeRange(0, 16)];
    [pack getBytes:iBuff range:NSMakeRange(16, 32)];
    [pack getBytes:pBuff range:NSMakeRange(32, len)];
    
    [(*salt) replaceBytesInRange:NSMakeRange(0, 16) withBytes:sBuff];
    [(*iv) replaceBytesInRange:NSMakeRange(0, 16) withBytes:iBuff];

    NSMutableData *unpack = [NSMutableData dataWithLength:len];
    [unpack replaceBytesInRange:NSMakeRange(0, len) withBytes:pBuff];
    free(pBuff);
    return unpack;
} 

it should be pretty simple to integrate the encryption and decryption from these two methods.

Proof of concept: Can we pack all together? and Unpack again?

NSData *salt = [CryptAES randomDataOfLength:16];
NSData *iv = [CryptAES randomDataOfLength:16];
NSData *chunk = [CryptAES packWithSalt:salt IV:iv Cypher:plaintextData];
NSLog(@"salt=%@ iv=%@ pack=%@ ",[salt base64EncodedStringWithOptions:0], [iv base64EncodedStringWithOptions:0], [chunk base64EncodedStringWithOptions:0] );
    
NSMutableData *unSalt = [NSMutableData dataWithLength:16];
NSMutableData *unIv = [NSMutableData dataWithLength:16];
NSData *unchunk = [CryptAES cypherUnpackToSalt:&unSalt andIV:&unIv fromPackData:chunk];
NSString *plainAgain = [[NSString alloc] initWithData:unchunk encoding:NSUTF8StringEncoding];
NSLog(@"salt=%@ iv=%@ unpack=%@",[unSalt base64EncodedStringWithOptions:0], [unIv base64EncodedStringWithOptions:0], plainAgain );

So your decryption method will still need parameters for a password. Which is not perfect but as you should never throw around encrypted data together with its password - that should be ok - you just keep the handling of a password on the user side. I mean otherwise the whole encryption is useless!

Ol Sen
  • 3,163
  • 2
  • 21
  • 30
  • Thanks OI can you please explain how to generate the random Salt without even passing it ? – User1075 Nov 05 '20 at 10:52
  • Rob Mentioned about adding the salt to the Data Out much as do with IV can you help me in how to achieve the same ? – User1075 Nov 05 '20 at 10:55
  • Thanks again for the explanation but if you see https://stackoverflow.com/questions/64679465/how-to-add-salt-to-aes-encryption-in-ios-and-decrypting-it-using-objective-c I have made a dynamic IV and prepended to the Encrypt key . Much the same Way I want to prepend the dynamic Salt in format is [0-16] salt , [16-32] IV and [16 - length] cypher text. So as a whole I need only to pass the secret key and String as always... :) – User1075 Nov 05 '20 at 11:15
  • ouhh so you mean if I pass salt as Nil and IV as nil the code will automatically generate IV and Salt and prepend it to the Ciphered key ? – User1075 Nov 05 '20 at 11:16
  • Yes I understood but my concern is how to generate a cipher text in the format "[0-16] salt , [16-32] IV and [32 - length] cypher text" because in my other question I have generated in "[0-16] IV and [16 - length] cypher text" so I want to prepend the salt before this. While decrypting I must decrypt this .. Any idea how to do this? – User1075 Nov 05 '20 at 11:25
  • Yes yes ofcourse .... I am doing the same with IV + CHIPHER TEXT so right now I wish to have salt +IV + chipher text... – User1075 Nov 05 '20 at 15:53
  • https://stackoverflow.com/questions/64679465/how-to-add-salt-to-aes-encryption-in-ios-and-decrypting-it-using-objective-c My question will give more clarity – User1075 Nov 05 '20 at 15:55
  • https://stackoverflow.com/questions/46221794/generate-initialization-vector-in-objective-c I followed zaph answer to pass IV along with CHIPHER text and you can see it automatically decrypts without passing IV ... – User1075 Nov 05 '20 at 15:56
  • to me its actually evil to keep salt + iv + cypher in one chunk. May i ask why you want to shrink all down to one parameter? – Ol Sen Nov 05 '20 at 16:18
  • Actually in Android they are performing the same they are adding salt +IV+ Cipher text .. I have added the Android code to this post for your reference. So basically I wish to achieve that for cross platform Same encryption/decryption. Please see the android code – User1075 Nov 05 '20 at 16:25
  • so if any help in this it will be very useful for me.. – User1075 Nov 05 '20 at 16:36
  • 1
    thats droidish sniff-fun. Easy to knock at any door. well but why not.. you are right – Ol Sen Nov 05 '20 at 16:42
  • Yes... So I have to achieve the same so that if they give me a salted cipher text with same secret key I must be able to decrypt it here. and when I give a encrypted key to them then they must be able encrypt there ... Much like a synch for both iOS and Android – User1075 Nov 05 '20 at 16:45
  • Hope you understand my bothering.. in case to synch with iOS and android I need to achieve this as its already implemented in Android and Server ... – User1075 Nov 05 '20 at 16:47
  • https://stackoverflow.com/questions/46221794/generate-initialization-vector-in-objective-c if you are looking to work on it you can work on the code at this link as its already 80 percent done with only Salt value to be prepended .. Hope you understand my concern – User1075 Nov 05 '20 at 16:48
  • thanks a lot again ... I will use this solution and try to get the desired output :) – User1075 Nov 05 '20 at 21:01
  • [unpack replaceBytesInRange:NSMakeRange(0, len) withBytes:pBuff]; free(pBuff); Can you explain these code and what is pBuff ?what does the malloc option does ? – User1075 Nov 05 '20 at 21:25
  • objective-c can be mixed with C because it is actually C. *malloc* claims memory of some size and returns the address so it can be used. The split part of the *pack* holding your cypher is filled into this memory which is then returned as NSData and *malloc* requires to free the memory after use (little overdo but technically correct). `void *iBuff[16] = {};` is actually doing same, but it's a simple local constant with fixed memory size (16). In C you can not place some flexible index amount so malloc was needed for the flexibility. – Ol Sen Nov 05 '20 at 21:45