ObjFW  Check-in [49d1987eaf]

Overview
Comment:Implement PBKDF2
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 49d1987eaf45a854c670e6fa4326b4e5c230543d86b8f1e3b88830ff8bdcd58c
User & Date: js on 2016-07-25 22:30:49
Other Links: manifest | tags
Context
2016-07-25
22:51
Add missing files to Xcode project check-in: a5c7babba2 user: js tags: trunk
22:30
Implement PBKDF2 check-in: 49d1987eaf user: js tags: trunk
2016-07-24
20:51
OFHMAC: Allow resetting while keeping the key check-in: c55c5dff51 user: js tags: trunk
Changes

Modified src/Makefile from [40b697516a] to [6cddaa157f].

83
84
85
86
87
88
89

90
91
92
93
94
95
96
       OFXMLProcessingInstructions.m	\
       OFZIPArchive.m			\
       OFZIPArchiveEntry.m		\
       base64.m				\
       crc32.m				\
       of_asprintf.m			\
       of_strptime.m			\

       unicode.m			\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}
SRCS_FILES = OFFile.m			\
	     OFFileManager.m		\







>







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
       OFXMLProcessingInstructions.m	\
       OFZIPArchive.m			\
       OFZIPArchiveEntry.m		\
       base64.m				\
       crc32.m				\
       of_asprintf.m			\
       of_strptime.m			\
       pbkdf2.m				\
       unicode.m			\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}
SRCS_FILES = OFFile.m			\
	     OFFileManager.m		\

Modified src/OFHMAC.h from [fe33092934] to [72d5a8c0be].

101
102
103
104
105
106
107








108
109
110
 *	 @ref setKey:length:.
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If
 *	    you are still interested in the previous digest, you need to memcpy
 *	    it yourself before calling @ref reset!
 */
- (void)reset;








@end

OF_ASSUME_NONNULL_END







>
>
>
>
>
>
>
>



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
 *	 @ref setKey:length:.
 *
 * @warning This invalidates any pointer previously returned by @ref digest. If
 *	    you are still interested in the previous digest, you need to memcpy
 *	    it yourself before calling @ref reset!
 */
- (void)reset;

/*!
 * @brief This is like @ref reset, but also zeroes the hashed key and all state.
 *
 * @warning After calling this, you *must* set a new key before reusing the
 *	    HMAC!
 */
- (void)zero;
@end

OF_ASSUME_NONNULL_END

Modified src/OFHMAC.m from [8de8a11875] to [25132018d7].

139
140
141
142
143
144
145











146
147
148
	[_outerHash release];
	[_innerHash release];
	_outerHash = _innerHash = nil;

	_outerHash = [_outerHashCopy copy];
	_innerHash = [_innerHashCopy copy];












	_calculated = false;
}
@end







>
>
>
>
>
>
>
>
>
>
>



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
	[_outerHash release];
	[_innerHash release];
	_outerHash = _innerHash = nil;

	_outerHash = [_outerHashCopy copy];
	_innerHash = [_innerHashCopy copy];

	_calculated = false;
}

- (void)zero
{
	[_outerHash release];
	[_innerHash release];
	[_outerHashCopy release];
	[_innerHashCopy release];
	_outerHash = _innerHash = _outerHashCopy = _innerHashCopy = nil;

	_calculated = false;
}
@end

Modified src/ObjFW.h from [99eb763c6d] to [9a3074fd4c].

200
201
202
203
204
205
206

#import "crc32.h"
#import "instance.h"
#import "of_asprintf.h"
#import "of_strptime.h"
#ifdef OF_HAVE_SOCKETS
# import "resolver.h"
#endif








>
200
201
202
203
204
205
206
207
#import "crc32.h"
#import "instance.h"
#import "of_asprintf.h"
#import "of_strptime.h"
#ifdef OF_HAVE_SOCKETS
# import "resolver.h"
#endif
#import "pbkdf2.h"

Added src/pbkdf2.h version [8cf4fe8ea4].



























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
 *   Jonathan Schleifer <js@heap.zone>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS
#endif
#ifndef __STDC_CONSTANT_MACROS
# define __STDC_CONSTANT_MACROS
#endif

#import "macros.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFHMAC;

#ifdef __cplusplus
extern "C" {
#endif
/*!
 * @brief Derive a key from a password and a salt.
 *
 * @note This will call @ref OFHMAC::reset on the @ref OFHMAC first, making it
 *	 possible to reuse the @ref OFHMAC, but also meaning all previous
 *	 results from the @ref OFHMAC 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)
 */
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);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Added src/pbkdf2.m version [e70bcecc17].













































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
 *   Jonathan Schleifer <js@heap.zone>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <stdlib.h>

#import "OFHMAC.h"

#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)
{
	size_t blocks, digestSize = [HMAC digestSize];
	unsigned char *extendedSalt;
	unsigned char buffer[digestSize];
	unsigned char digest[digestSize];

	if (HMAC == nil || iterations == 0 || salt == NULL || saltLength == 0 ||
	    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];

	if ((extendedSalt = malloc(saltLength + 4)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: saltLength + 4];

	@try {
		uint32_t i = OF_BSWAP32_IF_LE(1);

		[HMAC setKey: password
		      length: passwordLength];

		memcpy(extendedSalt, salt, saltLength);

		while (keyLength > 0) {
			size_t length;

			memcpy(extendedSalt + saltLength, &i, 4);

			[HMAC reset];
			[HMAC updateWithBuffer: extendedSalt
					length: saltLength + 4];
			memcpy(buffer, [HMAC digest], digestSize);
			memcpy(digest, [HMAC digest], digestSize);

			for (size_t j = 1; j < iterations; j++) {
				[HMAC reset];
				[HMAC updateWithBuffer: digest
						length: digestSize];
				memcpy(digest, [HMAC digest], digestSize);

				for (size_t k = 0; k < digestSize; k++)
					buffer[k] ^= digest[k];
			}

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

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

			i = OF_BSWAP32_IF_LE(OF_BSWAP32_IF_LE(i) + 1);
		}
	} @finally {
		memset(extendedSalt, 0, saltLength + 4);
		memset(buffer, 0, digestSize);
		memset(digest, 0, digestSize);

		[HMAC zero];

		free(extendedSalt);
	}
}

Modified tests/Makefile from [340c0b79d6] to [ce2c16d1ba].

22
23
24
25
26
27
28

29
30
31
32
33
34
35
       OFSetTests.m			\
       OFStreamTests.m			\
       OFStringTests.m			\
       OFURLTests.m			\
       OFXMLElementBuilderTests.m	\
       OFXMLNodeTests.m			\
       OFXMLParserTests.m		\

       RuntimeTests.m			\
       TestsAppDelegate.m		\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}		\
       ${OFHTTPCLIENTTESTS_M}







>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
       OFSetTests.m			\
       OFStreamTests.m			\
       OFStringTests.m			\
       OFURLTests.m			\
       OFXMLElementBuilderTests.m	\
       OFXMLNodeTests.m			\
       OFXMLParserTests.m		\
       PBKDF2Tests.m			\
       RuntimeTests.m			\
       TestsAppDelegate.m		\
       ${USE_SRCS_FILES}		\
       ${USE_SRCS_PLUGINS}		\
       ${USE_SRCS_SOCKETS}		\
       ${USE_SRCS_THREADS}		\
       ${OFHTTPCLIENTTESTS_M}

Added tests/PBKDF2Tests.m version [0612a8f8d4].







































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
/*
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
 *   Jonathan Schleifer <js@heap.zone>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <string.h>

#import "OFHMAC.h"
#import "OFSHA1Hash.h"
#import "OFString.h"
#import "OFAutoreleasePool.h"

#import "pbkdf2.h"

#import "TestsAppDelegate.h"

static OFString *module = @"PBKDF2";

@implementation TestsAppDelegate (PBKDF2Tests)
- (void)PBKDF2Tests
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFHMAC *HMAC = [OFHMAC HMACWithHashClass: [OFSHA1Hash class]];
	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)) &&
	    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)) &&
	    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)) &&
	    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)) &&
	    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)) &&
	    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)) &&
	    memcmp(key, "\x56\xFA\x6A\xA7\x55\x48\x09\x9D\xCC\x37\xD7\xF0\x34"
		"\x25\xE0\xC3", 16) == 0)

	[pool drain];
}
@end

Modified tests/TestsAppDelegate.h from [6bb325b98c] to [2f2d0cbc9c].

204
205
206
207
208
209
210




- (void)XMLNodeTests;
@end

@interface TestsAppDelegate (OFXMLParserTests)
    <OFXMLParserDelegate, OFXMLElementBuilderDelegate>
- (void)XMLParserTests;
@end











>
>
>
>
204
205
206
207
208
209
210
211
212
213
214
- (void)XMLNodeTests;
@end

@interface TestsAppDelegate (OFXMLParserTests)
    <OFXMLParserDelegate, OFXMLElementBuilderDelegate>
- (void)XMLParserTests;
@end

@interface TestsAppDelegate (PBKDF2Tests)
- (void)PBKDF2Tests;
@end

Modified tests/TestsAppDelegate.m from [60d1893094] to [fd9b7aac68].

381
382
383
384
385
386
387

388
389
390
391
392
393
394
	[self RIPEMD160HashTests];
	[self SHA1HashTests];
	[self SHA224HashTests];
	[self SHA256HashTests];
	[self SHA384HashTests];
	[self SHA512HashTests];
	[self HMACTests];

	[self INIFileTests];
#endif
#ifdef OF_HAVE_SOCKETS
	[self TCPSocketTests];
	[self UDPSocketTests];
	[self kernelEventObserverTests];
#endif







>







381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
	[self RIPEMD160HashTests];
	[self SHA1HashTests];
	[self SHA224HashTests];
	[self SHA256HashTests];
	[self SHA384HashTests];
	[self SHA512HashTests];
	[self HMACTests];
	[self PBKDF2Tests];
	[self INIFileTests];
#endif
#ifdef OF_HAVE_SOCKETS
	[self TCPSocketTests];
	[self UDPSocketTests];
	[self kernelEventObserverTests];
#endif