ObjFW  Check-in [b9641347e3]

Overview
Comment:Move parameters for of_pbkdf2() to a struct

This should make it more readable for such a large number of parameters.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: b9641347e3283399a085af807f6f98c8a0a15060c0110d170d294338d8b1508b
User & Date: js on 2020-06-21 21:30:23
Other Links: manifest | tags
Context
2020-06-21
22:08
Move parameters for of_scrypt() to a struct check-in: 63f5276b33 user: js tags: trunk
21:30
Move parameters for of_pbkdf2() to a struct check-in: b9641347e3 user: js tags: trunk
17:53
Throw an exception when there is no name server check-in: f3573582e1 user: js tags: trunk
Changes

Modified src/pbkdf2.h from [e1a5b71855] to [139e147fae].

26
27
28
29
30
31
32




























33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFHMAC;





























#ifdef __cplusplus
extern "C" {
#endif
/*!
 * @brief Derives a key from a password and a salt using PBKDF2.
 *
 * @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
 */
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);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










<
<
<
<
|
<
<
<
<
<

|
<
<
<





26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70




71





72
73



74
75
76
77
78

OF_ASSUME_NONNULL_BEGIN

/*! @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.
 *
 * @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 param The parameters to use





 */
extern void of_pbkdf2(of_pbkdf2_parameters_t param);



#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/pbkdf2.m from [7bd85babf9] to [ae8f98e1e6].

24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

#import "OFInvalidArgumentException.h"
#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 *pool = objc_autoreleasePoolPush();
	size_t blocks, digestSize = HMAC.digestSize;
	OFSecureData *buffer = [OFSecureData
		    dataWithCount: digestSize
	    allowsSwappableMemory: allowsSwappableMemory];
	OFSecureData *digest = [OFSecureData
		    dataWithCount: digestSize
	    allowsSwappableMemory: 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)
		@throw [OFInvalidArgumentException exception];

	blocks = keyLength / digestSize;
	if (keyLength % digestSize != 0)
		blocks++;

	if (saltLength > SIZE_MAX - 4 || blocks > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	extendedSalt = [OFSecureData dataWithCount: saltLength + 4

			     allowsSwappableMemory: allowsSwappableMemory];
	extendedSaltItems = extendedSalt.mutableItems;

	@try {
		uint32_t i = OF_BSWAP32_IF_LE(1);

		[HMAC setKey: password
		      length: passwordLength];

		memcpy(extendedSaltItems, salt, saltLength);

		while (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);


				for (size_t k = 0; k < digestSize; k++)
					bufferItems[k] ^= digestItems[k];
			}

			length = digestSize;
			if (length > keyLength)
				length = keyLength;

			memcpy(key, bufferItems, length);
			key += length;
			keyLength -= length;

			i = OF_BSWAP32_IF_LE(OF_BSWAP32_IF_LE(i) + 1);
		}
	} @catch (id e) {
		[extendedSalt zero];
		[buffer zero];
		[digest zero];

		@throw e;
	} @finally {
		[HMAC zero];
	}

	objc_autoreleasePoolPop(pool);
}







>
|
<
<
<


|


|


|





|
|


|
|


|


|
>
|





|
|

|

|


|

|
|
|
|
|

|
|
|
|
|
>






|
|

|
|
|










|




24
25
26
27
28
29
30
31
32



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

#import "OFInvalidArgumentException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#import "pbkdf2.h"

void
of_pbkdf2(of_pbkdf2_parameters_t param)



{
	void *pool = objc_autoreleasePoolPush();
	size_t blocks, digestSize = param.HMAC.digestSize;
	OFSecureData *buffer = [OFSecureData
		    dataWithCount: digestSize
	    allowsSwappableMemory: param.allowsSwappableMemory];
	OFSecureData *digest = [OFSecureData
		    dataWithCount: digestSize
	    allowsSwappableMemory: param.allowsSwappableMemory];
	unsigned char *bufferItems = buffer.mutableItems;
	unsigned char *digestItems = digest.mutableItems;
	OFSecureData *extendedSalt;
	unsigned char *extendedSaltItems;

	if (param.HMAC == nil || param.iterations == 0 || param.salt == NULL ||
	    param.password == NULL || param.key == NULL || param.keyLength == 0)
		@throw [OFInvalidArgumentException exception];

	blocks = param.keyLength / digestSize;
	if (param.keyLength % digestSize != 0)
		blocks++;

	if (param.saltLength > SIZE_MAX - 4 || blocks > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	extendedSalt = [OFSecureData
		    dataWithCount: param.saltLength + 4
	    allowsSwappableMemory: param.allowsSwappableMemory];
	extendedSaltItems = extendedSalt.mutableItems;

	@try {
		uint32_t i = OF_BSWAP32_IF_LE(1);

		[param.HMAC setKey: param.password
			    length: param.passwordLength];

		memcpy(extendedSaltItems, param.salt, param.saltLength);

		while (param.keyLength > 0) {
			size_t length;

			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 > param.keyLength)
				length = param.keyLength;

			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];
		[buffer zero];
		[digest zero];

		@throw e;
	} @finally {
		[param.HMAC zero];
	}

	objc_autoreleasePoolPop(pool);
}

Modified src/scrypt.m from [bf0f4f5521] to [42e8413fd6].

181
182
183
184
185
186
187






188
189
190


191
192
193
194
195



196
197




198

199
200
201
202
203
204
		    allowsSwappableMemory: allowsSwappableMemory];
		bufferItems = buffer.mutableItems;

		HMAC = [[OFHMAC alloc]
			initWithHashClass: [OFSHA256Hash class]
		    allowsSwappableMemory: allowsSwappableMemory];







		of_pbkdf2(HMAC, 1, salt, saltLength, password, passwordLength,
		    (unsigned char *)bufferItems,
		    parallelization * 128 * blockSize, 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);

	} @finally {
		[tmp release];
		[buffer release];
		[HMAC release];
	}
}







>
>
>
>
>
>
|
|
|
>
>





>
>
>
|
|
>
>
>
>
|
>






181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
		    allowsSwappableMemory: allowsSwappableMemory];
		bufferItems = buffer.mutableItems;

		HMAC = [[OFHMAC alloc]
			initWithHashClass: [OFSHA256Hash class]
		    allowsSwappableMemory: 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((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];
	}
}

Modified tests/PBKDF2Tests.m from [ce7ed4d1cb] to [f87038c7c8].

30
31
32
33
34
35
36



37






38
39
40
41
42



43






44
45
46
47
48



49






50
51
52
53
54
55
56



57






58
59
60
61
62
63
64


65

66





67
68
69
70



71






72
73
74
75
76
77
78
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			   allowsSwappableMemory: true];
	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)) &&
	    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)) &&
	    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)) &&
	    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)) &&
	    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)) &&





	    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)) &&
	    memcmp(key, "\x56\xFA\x6A\xA7\x55\x48\x09\x9D\xCC\x37\xD7\xF0\x34"
		"\x25\xE0\xC3", 16) == 0)

	objc_autoreleasePoolPop(pool);
}
@end







>
>
>
|
>
>
>
>
>
>
|




>
>
>
|
>
>
>
>
>
>
|




>
>
>
|
>
>
>
>
>
>
|






>
>
>
|
>
>
>
>
>
>
|





|
>
>
|
>
|
>
>
>
>
>




>
>
>
|
>
>
>
>
>
>
|






30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]
			   allowsSwappableMemory: true];
	unsigned char key[25];

	/* Test vectors from RFC 6070 */

	TEST(@"PBKDF2-SHA1, 1 iteration",
	    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((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((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((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((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((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