Mac developers: don't use AQDataExtensions
AQDataExtensions
is an NSData
category developed in 2005
and distributed with AquaticPrime framework which “allows for easily encrypting
and decrypting NSData objects with AES/Rijndael (i.e. the Advanced Encryption
Standard)”.
The methods are:
- (NSData*)dataEncryptedWithPassword:(NSString*)password
- (NSData*)dataDecryptedWithPassword:(NSString*)password
Unfortunately, AQDataExtensions
has the following weaknesses:
- Weak key derivation function.
- No authentication.
- Weak random numbers.
Weak key derivation function
As method names indicate, they expect developers to provide password for encryption. As user-provided passwords may be weak and easily brute-forceable, usually computationally expensive function is used to derive encryption keys from passwords.
Here’s how AQDataExtensions
derive AES key from password:
// Create the key from first 128-bits of the 160-bit password hash
unsigned char passwordDigest[20];
SHA1((unsigned char *)[password UTF8String], strlen([password UTF8String]), passwordDigest);
AES_KEY aesKey;
AES_set_encrypt_key(passwordDigest, 128, &aesKey);
Unfortunately, it uses SHA-1 hash function, which is not computationally expensive and is very fast. This means that weak passwords can be brute-forced more easily.
In order to make it safe, you must derive key from password yourself using
a stronger key derivation function, such as PBKDF2 or scrypt before passing it
to AQDataExtensions
.
No authentication
Data encrypted by AQDataExtensions
is not authenticated, which means that
encrypted text can be changed in such way that after decryption the
plain-text will change as well. This is unacceptable for many uses, so
authentication must be applied after encryption. For example, you can apply
HMAC to encrypted text and append the authenticator to encrypted data. Before decrypting,
apply HMAC, and compare the result with the attached authenticator.
Weak random numbers
Since AQDataExtensions
uses CBC encryption mode, it needs random numbers
for initialization vector. Random bytes are generated by the following code:
srand(time(NULL));
int ivIndex;
unsigned char iv[16];
for (ivIndex = 0; ivIndex < 16; ivIndex++)
iv[ivIndex] = rand() & 0xff;
It uses rand()
function to get them, but rand()
is not suitable for
cryptographic purposes. Also, it is predictable in this code because the PRNG
is reseeded for each encryption with current time. You should replace this code
with RAND_bytes(iv, 16)
from openssl/rand.h
.