Encrypting many files but still keeping a password change easy

91 views
Skip to first unread message

Udo Thiel

unread,
Apr 16, 2016, 10:21:06 PM4/16/16
to rncryptor
In an upcoming app I'd like to encrypt many files but in order to avoid complete re-encryption of all files after the user had changed the password, my current thinking is to generate one very long and therefore secure password A and use it to encrypt all files, while using a user-generated password B to encrypt password A. We would then store the encrypted password A' together with the other encrypted data. Hence when the user changes B, we only have to re-create A' once and overwrite it in all files instead of re-encrypting the complete bulk of the actual data.

Additionally, I want to increase security and give the user the option to print  A as a QR-Code and read it back using the built-in camera, thus avoiding password and it's lower security completely. 

So far this should be commen knowledge. My question is: what are the best practices to implement this using RNCryptor, how would I create an optimal password A using RNCryptor under these two boundary conditions?

Udo Thiel

unread,
Apr 17, 2016, 10:02:07 PM4/17/16
to rncryptor
EDIT: I'm using Objective-C and V4 of RNCryptor

Rob Napier

unread,
Apr 17, 2016, 10:22:02 PM4/17/16
to rncr...@googlegroups.com
This approach sounds fine (and as you suspect, it's a very common approach in crypto). Just a few thoughts.

Rather than generating a password A, generate two keys A1 and A2. Use these to encrypt using the key-based API rather than the password API. The two keys are the encryption key and the hmac key. When you use the password interface, RNCryptor expands the password into those two keys. This is intentionally very time-intensive to strengthen weak passwords that humans generate. There's no reason for that expensive step if you're using random keys.

To generate the random keys, just use +randomDataOfLength:. Look in the test cases for several examples. The results should be ideal for your purposes.

-Rob


On Sun, Apr 17, 2016 at 10:02 PM, Udo Thiel <uthi...@gmail.com> wrote:
EDIT: I'm using Objective-C and V4 of RNCryptor

--
You received this message because you are subscribed to the Google Groups "rncryptor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rncryptor+...@googlegroups.com.
Visit this group at https://groups.google.com/group/rncryptor.
For more options, visit https://groups.google.com/d/optout.



--
Rob Napier
Cocoaphony blog -- http://robnapier.net/blog
iOS Programming Pushing the Limits -- http://robnapier.net/book

Udo Thiel

unread,
Apr 18, 2016, 7:37:27 PM4/18/16
to rncryptor


On Monday, 18 April 2016 04:22:02 UTC+2, Rob Napier wrote:
This approach sounds fine (and as you suspect, it's a very common approach in crypto). Just a few thoughts.

Rather than generating a password A, generate two keys A1 and A2. Use these to encrypt using the key-based API rather than the password API. The two keys are the encryption key and the hmac key. When you use the password interface, RNCryptor expands the password into those two keys. This is intentionally very time-intensive to strengthen weak passwords that humans generate. There's no reason for that expensive step if you're using random keys.

To generate the random keys, just use +randomDataOfLength:. Look in the test cases for several examples. The results should be ideal for your purposes.

-Rob


Thanks Rob for your swift reply. I've spent about an hour implementing this and then about 5 hours debugging a problem which which always occured after the second time I've tried to decrypt data using the same decryptor, until I saw the small hint in the description 

Takes a data, returns a processed data, and invalidates the cryptor.


 So apparently you can't do something like this:

              RNEncryptorV3 *enc      = [[RNEncryptorV3 alloc] initWithEncryptionKey:decryptedFEK hmacKey:decryptedHMAC];

             tempEnc1  = [enc encryptData:self.rawData1];

             tempEnc2  = [enc encryptData:self.rawData2];

             tempEnc3  = [enc encryptData:self.rawData3];

             tempEnc4  = [enc encryptData:self.rawData4];



It would be nice if the Readme.md would include an appropriate hint in the chapter Key-Based Encryption.

One more question: is the HMAC secret, i.e. do I need to encrypt it?

Udo Thiel

unread,
Apr 18, 2016, 7:53:10 PM4/18/16
to rncryptor
I also found out that although the description doesn't mention it, RNDecryptorV3 can't be re-used as well.

So the key-based API is only faster if you keep the cleartext FEK and HMAC in memory. Hmmm, not sure if I want that.
Message has been deleted

Rob Napier

unread,
Jun 2, 2016, 12:05:57 PM6/2/16
to rncr...@googlegroups.com
I don't understand the use of the TSDebugPassword. This feels very insecure. If you need to store the keys, they should be stored in (iCloud) Keychain. That's the tool designed for this.

eraseKeys is doing nothing at all. This doesn't overwrite memory, it just releases the NSData (which may or may not be deallocated, and definitely won't be overwritten) and creates a new NSData. Securely overwriting memory is extremely difficult with modern compilers and hardware, even in C. I can't even imagine a way to do it against an NSData without explicit support from Foundation. MITRE has a framework called memory-security that attempts to provide some of the features you need (https://github.com/project-imas/memory-security), but given how old it is, I would be very dubious about its efficacy on recent versions of iOS with recent compilers and recent hardware. (Not saying it doesn't still work, but such things can be very fragile unless constantly and laboriously tested.) I can't even begin to say how you'd integrate it with RNCryptor or if it would be worth the trouble. Anyone with memory-level access to the device is probably just going to put a debugger on the app and pull the key out directly.

-Rob



On Mon, May 30, 2016 at 7:20 AM, Udo Thiel <uthi...@gmail.com> wrote:


On Monday, 18 April 2016 04:22:02 UTC+2, Rob Napier wrote:
This approach sounds fine (and as you suspect, it's a very common approach in crypto). Just a few thoughts.

Rather than generating a password A, generate two keys A1 and A2. Use these to encrypt using the key-based API rather than the password API. The two keys are the encryption key and the hmac key. When you use the password interface, RNCryptor expands the password into those two keys. This is intentionally very time-intensive to strengthen weak passwords that humans generate. There's no reason for that expensive step if you're using random keys.

To generate the random keys, just use +randomDataOfLength:. Look in the test cases for several examples. The results should be ideal for your purposes.

-Rob



So I've come up with this class and I would like to ask you if you could be so kind and verify the implementation:

Udo Thiel

unread,
Jun 2, 2016, 12:33:08 PM6/2/16
to rncryptor
Rob, thanks for your advice, it is very much appreciated!


On Thursday, 2 June 2016 18:05:57 UTC+2, Rob Napier wrote:
I don't understand the use of the TSDebugPassword. This feels very insecure. If you need to store the keys, they should be stored in (iCloud) Keychain. That's the tool designed for this.

The TSDebugPassword is just a placeholder until I have implemented the various authorization input vectors. As I've said above, I'm planning to give the user the option to either
  • type the password on the keyboard
  • read the password from a QR code using the camera
  • read it from the keychain using TouchID
I haven't implemented any of those methods yet so right now I just need a placeholder for the password.
 

eraseKeys is doing nothing at all. 

Right. I've removed it. 

Udo Thiel

unread,
Jun 8, 2016, 10:53:56 PM6/8/16
to rncryptor
The code I've posted previously contained a copy/paste error in the decryptedFEK method which apparently nobody noticed so far. I've also improved the eraseKeys method to actually overwrite the cached memory using NSMutableData's  - (void)resetBytesInRange:(NSRange)range. I've verified in the debugger that this method does overwrite the actual memory with zeros.

To avoid badly formatted code again, I've attached the files this time.


TSCryptoKeys.h
TSCryptoKeys.m

Rob Napier

unread,
Jun 10, 2016, 2:18:04 PM6/10/16
to rncr...@googlegroups.com
You're leaving a copy of the keys in memory because of how you created the keys in the first place:

[_decryptedFEK  appendData:[RNCryptor randomDataOfLength:kCCKeySizeAES256]];


This generates an NSData, copies it into another NSData, and then just releases the original one. That leaves the key hanging in memory indefinitely. You probably need your own version of randomDataOfLength that gives you a mutable data back.

Note that there's no promise that the memory will be overwritten in any case. Apple is free to create optimizations that avoid actually overwriting the data. It could, for instance, return you a fresh, empty block of memory of the appropriate size.

That said, you can decompile this function on x86_64 and see that it will call memset in all cases. I haven't decompiled it on iOS devices, but it's probably the same. And I don't know of any memset optimizations that delay actually setting the memory.

The optimizer probably isn't smart enough to get rid of your dealloc entirely, though it's technically free to do so. It's a reasonable bet that it won't get that smart, but not promised.

There's definitely no promise that the memory wasn't written to disk already. So it's a fine best-effort attempt, but you can't rely on it to clear your keys.

(All of this is highly paranoid, but so is trying to overwrite the memory in the first place. It's important to realize it's just best effort. That doesn't make it useless.)

The way you've written eraseKeys is somewhat fragile.

NSRange range = NSMakeRange(0, _decryptedHMAC.length);

[_decryptedHMAC resetBytesInRange:range];

[_decryptedFEK resetBytesInRange:range];


This happens to be ok because both of these are the same length, but that's not obvious (in fact, the naming makes it even more confusing, because the "decryptedHMAC" is not an HMAC, it's a key). If for any reason the lengths were different, then this would quietly do the wrong thing. If length were too short, of course it wouldn't overwrite everything. But if it were too long it could (surprisingly) overwrite *nothing* because the data could be relocated to some other block of memory before writing the zeros. I would make sure to use the range of each target rather than relying on the fact that they're the same length.

-Rob


On Wed, Jun 8, 2016 at 10:53 PM, Udo Thiel <uthi...@gmail.com> wrote:
The code I've posted previously contained a copy/paste error in the decryptedFEK method which apparently nobody noticed so far. I've also improved the eraseKeys method to actually overwrite the cached memory using NSMutableData's  - (void)resetBytesInRange:(NSRange)range. I've verified in the debugger that this method does overwrite the actual memory with zeros.

To avoid badly formatted code again, I've attached the files this time.


--
You received this message because you are subscribed to the Google Groups "rncryptor" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rncryptor+...@googlegroups.com.
Visit this group at https://groups.google.com/group/rncryptor.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages