Index: src/pbkdf2.h ================================================================== --- src/pbkdf2.h +++ src/pbkdf2.h @@ -28,10 +28,38 @@ /*! @file */ @class OFHMAC; +/*! + * @brief The parameters for @ref of_pbkdf2. + */ +typedef struct of_pbkdf2_parameters_t { + /*! @brief The HMAC to use to derive a key. */ + OFHMAC *HMAC; + /*! @brief The number of iterations to perform. */ + size_t iterations; + /*! @brief The salt to derive a key with. */ + const unsigned char *salt; + /*! @brief The length of the salt. */ + size_t saltLength; + /*! @brief The password to derive a key from. */ + const char *password; + /*! @brief The length of the password. */ + size_t passwordLength; + /*! @brief The buffer to write the key to. */ + unsigned char *key; + /*! + * @brief The desired length for the derived key. + * + * @ref key needs to have enough storage). + */ + size_t keyLength; + /*! @brief Whether data may be stored in swappable memory. */ + bool allowsSwappableMemory; +} of_pbkdf2_parameters_t; + #ifdef __cplusplus extern "C" { #endif /*! * @brief Derives a key from a password and a salt using PBKDF2. @@ -38,25 +66,13 @@ * * @note This will call @ref OFHMAC::reset on the `HMAC` first, making it * possible to reuse the `HMAC`, but also meaning all previous results * from the `HMAC` get invalidated if they have not been copied. * - * @param HMAC The HMAC to use to derive a key - * @param iterations The number of iterations to perform - * @param salt The salt to derive a key with - * @param saltLength The length of the salt - * @param password The password to derive a key from - * @param passwordLength The length of the password - * @param key The buffer to write the key to - * @param keyLength The desired length for the derived key (`key` needs to have - * enough storage) - * @param allowsSwappableMemory Whether data may be stored in swappable memory + * @param param The parameters to use */ -extern void of_pbkdf2(OFHMAC *HMAC, size_t iterations, - const unsigned char *salt, size_t saltLength, - const char *password, size_t passwordLength, - unsigned char *key, size_t keyLength, bool allowsSwappableMemory); +extern void of_pbkdf2(of_pbkdf2_parameters_t param); #ifdef __cplusplus } #endif OF_ASSUME_NONNULL_END Index: src/pbkdf2.m ================================================================== --- src/pbkdf2.m +++ src/pbkdf2.m @@ -26,79 +26,79 @@ #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "pbkdf2.h" -void of_pbkdf2(OFHMAC *HMAC, size_t iterations, - const unsigned char *salt, size_t saltLength, - const char *password, size_t passwordLength, - unsigned char *key, size_t keyLength, bool allowsSwappableMemory) +void +of_pbkdf2(of_pbkdf2_parameters_t param) { void *pool = objc_autoreleasePoolPush(); - size_t blocks, digestSize = HMAC.digestSize; + size_t blocks, digestSize = param.HMAC.digestSize; OFSecureData *buffer = [OFSecureData dataWithCount: digestSize - allowsSwappableMemory: allowsSwappableMemory]; + allowsSwappableMemory: param.allowsSwappableMemory]; OFSecureData *digest = [OFSecureData dataWithCount: digestSize - allowsSwappableMemory: allowsSwappableMemory]; + allowsSwappableMemory: param.allowsSwappableMemory]; unsigned char *bufferItems = buffer.mutableItems; unsigned char *digestItems = digest.mutableItems; OFSecureData *extendedSalt; unsigned char *extendedSaltItems; - if (HMAC == nil || iterations == 0 || salt == NULL || - password == NULL || key == NULL || keyLength == 0) + if (param.HMAC == nil || param.iterations == 0 || param.salt == NULL || + param.password == NULL || param.key == NULL || param.keyLength == 0) @throw [OFInvalidArgumentException exception]; - blocks = keyLength / digestSize; - if (keyLength % digestSize != 0) + blocks = param.keyLength / digestSize; + if (param.keyLength % digestSize != 0) blocks++; - if (saltLength > SIZE_MAX - 4 || blocks > UINT32_MAX) + if (param.saltLength > SIZE_MAX - 4 || blocks > UINT32_MAX) @throw [OFOutOfRangeException exception]; - extendedSalt = [OFSecureData dataWithCount: saltLength + 4 - allowsSwappableMemory: allowsSwappableMemory]; + extendedSalt = [OFSecureData + dataWithCount: param.saltLength + 4 + allowsSwappableMemory: param.allowsSwappableMemory]; extendedSaltItems = extendedSalt.mutableItems; @try { uint32_t i = OF_BSWAP32_IF_LE(1); - [HMAC setKey: password - length: passwordLength]; + [param.HMAC setKey: param.password + length: param.passwordLength]; - memcpy(extendedSaltItems, salt, saltLength); + memcpy(extendedSaltItems, param.salt, param.saltLength); - while (keyLength > 0) { + while (param.keyLength > 0) { size_t length; - memcpy(extendedSaltItems + saltLength, &i, 4); - - [HMAC reset]; - [HMAC updateWithBuffer: extendedSaltItems - length: saltLength + 4]; - memcpy(bufferItems, HMAC.digest, digestSize); - memcpy(digestItems, HMAC.digest, digestSize); - - for (size_t j = 1; j < iterations; j++) { - [HMAC reset]; - [HMAC updateWithBuffer: digestItems - length: digestSize]; - memcpy(digestItems, HMAC.digest, digestSize); + memcpy(extendedSaltItems + param.saltLength, &i, 4); + + [param.HMAC reset]; + [param.HMAC updateWithBuffer: extendedSaltItems + length: param.saltLength + 4]; + memcpy(bufferItems, param.HMAC.digest, digestSize); + memcpy(digestItems, param.HMAC.digest, digestSize); + + for (size_t j = 1; j < param.iterations; j++) { + [param.HMAC reset]; + [param.HMAC updateWithBuffer: digestItems + length: digestSize]; + memcpy(digestItems, param.HMAC.digest, + digestSize); for (size_t k = 0; k < digestSize; k++) bufferItems[k] ^= digestItems[k]; } length = digestSize; - if (length > keyLength) - length = keyLength; + if (length > param.keyLength) + length = param.keyLength; - memcpy(key, bufferItems, length); - key += length; - keyLength -= length; + memcpy(param.key, bufferItems, length); + param.key += length; + param.keyLength -= length; i = OF_BSWAP32_IF_LE(OF_BSWAP32_IF_LE(i) + 1); } } @catch (id e) { [extendedSalt zero]; @@ -105,10 +105,10 @@ [buffer zero]; [digest zero]; @throw e; } @finally { - [HMAC zero]; + [param.HMAC zero]; } objc_autoreleasePoolPop(pool); } Index: src/scrypt.m ================================================================== --- src/scrypt.m +++ src/scrypt.m @@ -183,22 +183,38 @@ HMAC = [[OFHMAC alloc] initWithHashClass: [OFSHA256Hash class] allowsSwappableMemory: allowsSwappableMemory]; - of_pbkdf2(HMAC, 1, salt, saltLength, password, passwordLength, - (unsigned char *)bufferItems, - parallelization * 128 * blockSize, allowsSwappableMemory); + of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 1, + .salt = salt, + .saltLength = saltLength, + .password = password, + .passwordLength = passwordLength, + .key = (unsigned char *)bufferItems, + .keyLength = parallelization * 128 * blockSize, + .allowsSwappableMemory = allowsSwappableMemory + }); for (size_t i = 0; i < parallelization; i++) of_scrypt_romix(bufferItems + i * 32 * blockSize, blockSize, costFactor, tmpItems); - of_pbkdf2(HMAC, 1, (unsigned char *)bufferItems, - parallelization * 128 * blockSize, password, passwordLength, - key, keyLength, allowsSwappableMemory); + of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 1, + .salt = (unsigned char *)bufferItems, + .saltLength = parallelization * 128 * blockSize, + .password = password, + .passwordLength = passwordLength, + .key = key, + .keyLength = keyLength, + .allowsSwappableMemory = allowsSwappableMemory + }); } @finally { [tmp release]; [buffer release]; [HMAC release]; } } Index: tests/PBKDF2Tests.m ================================================================== --- tests/PBKDF2Tests.m +++ tests/PBKDF2Tests.m @@ -32,47 +32,100 @@ unsigned char key[25]; /* Test vectors from RFC 6070 */ TEST(@"PBKDF2-SHA1, 1 iteration", - R(of_pbkdf2(HMAC, 1, (unsigned char *)"salt", 4, "password", 8, key, - 20, true)) && + R(of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 1, + .salt = (unsigned char *)"salt", + .saltLength = 4, + .password = "password", + .passwordLength = 8, + .key = key, + .keyLength = 20, + .allowsSwappableMemory = true + })) && memcmp(key, "\x0C\x60\xC8\x0F\x96\x1F\x0E\x71\xF3\xA9\xB5\x24\xAF" "\x60\x12\x06\x2F\xE0\x37\xA6", 20) == 0) TEST(@"PBKDF2-SHA1, 2 iterations", - R(of_pbkdf2(HMAC, 2, (unsigned char *)"salt", 4, "password", 8, key, - 20, true)) && + R(of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 2, + .salt = (unsigned char *)"salt", + .saltLength = 4, + .password = "password", + .passwordLength = 8, + .key = key, + .keyLength = 20, + .allowsSwappableMemory = true + })) && memcmp(key, "\xEA\x6C\x01\x4D\xC7\x2D\x6F\x8C\xCD\x1E\xD9\x2A\xCE" "\x1D\x41\xF0\xD8\xDE\x89\x57", 20) == 0) TEST(@"PBKDF2-SHA1, 4096 iterations", - R(of_pbkdf2(HMAC, 4096, (unsigned char *)"salt", 4, "password", 8, - key, 20, true)) && + R(of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 4096, + .salt = (unsigned char *)"salt", + .saltLength = 4, + .password = "password", + .passwordLength = 8, + .key = key, + .keyLength = 20, + .allowsSwappableMemory = true + })) && memcmp(key, "\x4B\x00\x79\x01\xB7\x65\x48\x9A\xBE\xAD\x49\xD9\x26" "\xF7\x21\xD0\x65\xA4\x29\xC1", 20) == 0) /* This test takes too long, even on a fast machine. */ #if 0 TEST(@"PBKDF2-SHA1, 16777216 iterations", - R(of_pbkdf2(HMAC, 16777216, (unsigned char *)"salt", 4, "password", - 8, key, 20, true)) && + R(of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 16777216, + .salt = (unsigned char *)"salt", + .saltLength = 4, + .password = "password", + .passwordLength = 8, + .key = key, + .keyLength = 20, + .allowsSwappableMemory = true + })) && memcmp(key, "\xEE\xFE\x3D\x61\xCD\x4D\xA4\xE4\xE9\x94\x5B\x3D\x6B" "\xA2\x15\x8C\x26\x34\xE9\x84", 20) == 0) #endif TEST(@"PBKDF2-SHA1, 4096 iterations, key > 1 block", - R(of_pbkdf2(HMAC, 4096, - (unsigned char *)"saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, - "passwordPASSWORDpassword", 24, key, 25, true)) && + R(of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 4096, + .salt = (unsigned char *)"saltSALTsaltSALTsaltSALTsaltSALTsalt", + .saltLength = 36, + .password = "passwordPASSWORDpassword", + .passwordLength = 24, + .key = key, + .keyLength = 25, + .allowsSwappableMemory = true + })) && memcmp(key, "\x3D\x2E\xEC\x4F\xE4\x1C\x84\x9B\x80\xC8\xD8\x36\x62" "\xC0\xE4\x4A\x8B\x29\x1A\x96\x4C\xF2\xF0\x70\x38", 25) == 0) TEST(@"PBKDF2-SHA1, 4096 iterations, key < 1 block", - R(of_pbkdf2(HMAC, 4096, (unsigned char *)"sa\0lt", 5, "pass\0word", - 9, key, 16, true)) && + R(of_pbkdf2((of_pbkdf2_parameters_t){ + .HMAC = HMAC, + .iterations = 4096, + .salt = (unsigned char *)"sa\0lt", + .saltLength = 5, + .password = "pass\0word", + .passwordLength = 9, + .key = key, + .keyLength = 16, + .allowsSwappableMemory = true + })) && memcmp(key, "\x56\xFA\x6A\xA7\x55\x48\x09\x9D\xCC\x37\xD7\xF0\x34" "\x25\xE0\xC3", 16) == 0) objc_autoreleasePoolPop(pool); } @end