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:

  1. Weak key derivation function.
  2. No authentication.
  3. 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.