Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -297,10 +297,11 @@ 4B55A113133AC24600B58A93 /* OFReadFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A10D133AC24500B58A93 /* OFReadFailedException.m */; }; 4B55A114133AC24600B58A93 /* OFReadOrWriteFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A10E133AC24500B58A93 /* OFReadOrWriteFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B55A115133AC24600B58A93 /* OFReadOrWriteFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A10F133AC24500B58A93 /* OFReadOrWriteFailedException.m */; }; 4B55A116133AC24600B58A93 /* OFWriteFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A110133AC24500B58A93 /* OFWriteFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B55A117133AC24600B58A93 /* OFWriteFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A111133AC24600B58A93 /* OFWriteFailedException.m */; }; + 4B5AC54F1D97DEB100FECFAA /* ScryptTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B5AC54E1D97DEB100FECFAA /* ScryptTests.m */; }; 4B5B02BE18B288A400CE6AE4 /* OFINIFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B5B02BC18B288A400CE6AE4 /* OFINIFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B5B02BF18B288A400CE6AE4 /* OFINIFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B5B02BD18B288A400CE6AE4 /* OFINIFile.m */; }; 4B5B02C118B2897500CE6AE4 /* OFINIFileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B5B02C018B2897500CE6AE4 /* OFINIFileTests.m */; }; 4B5B02C418B28A1B00CE6AE4 /* testfile.ini in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B5B02C218B289ED00CE6AE4 /* testfile.ini */; }; 4B5C112F17E9AB3E003C917F /* forwarding.S in Sources */ = {isa = PBXBuildFile; fileRef = 4B5C112C17E9AAED003C917F /* forwarding.S */; }; @@ -327,10 +328,12 @@ 4B674402163C395900EB1E59 /* OFLocking.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6743F9163C395900EB1E59 /* OFLocking.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B674403163C395900EB1E59 /* OFMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6743FA163C395900EB1E59 /* OFMutex.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B674404163C395900EB1E59 /* OFMutex.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6743FB163C395900EB1E59 /* OFMutex.m */; }; 4B674405163C395900EB1E59 /* OFRecursiveMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6743FC163C395900EB1E59 /* OFRecursiveMutex.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B674406163C395900EB1E59 /* OFRecursiveMutex.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6743FD163C395900EB1E59 /* OFRecursiveMutex.m */; }; + 4B6994471D47FB1A007F34DF /* scrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6994451D47FB1A007F34DF /* scrypt.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B6994481D47FB1A007F34DF /* scrypt.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6994461D47FB1A007F34DF /* scrypt.m */; }; 4B6C8AD817BD5C2E00B194F2 /* OFRunLoop+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD117BD5C2E00B194F2 /* OFRunLoop+Private.h */; }; 4B6C8AD917BD5C2E00B194F2 /* OFStream+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD217BD5C2E00B194F2 /* OFStream+Private.h */; }; 4B6C8ADB17BD5C2E00B194F2 /* OFString_UTF8+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD417BD5C2E00B194F2 /* OFString_UTF8+Private.h */; }; 4B6C8ADC17BD5C2E00B194F2 /* OFThread+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD517BD5C2E00B194F2 /* OFThread+Private.h */; }; 4B6C8ADD17BD5C2E00B194F2 /* OFTimer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD617BD5C2E00B194F2 /* OFTimer+Private.h */; }; @@ -761,10 +764,11 @@ 4B55A10D133AC24500B58A93 /* OFReadFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFReadFailedException.m; path = src/exceptions/OFReadFailedException.m; sourceTree = ""; }; 4B55A10E133AC24500B58A93 /* OFReadOrWriteFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFReadOrWriteFailedException.h; path = src/exceptions/OFReadOrWriteFailedException.h; sourceTree = ""; }; 4B55A10F133AC24500B58A93 /* OFReadOrWriteFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFReadOrWriteFailedException.m; path = src/exceptions/OFReadOrWriteFailedException.m; sourceTree = ""; }; 4B55A110133AC24500B58A93 /* OFWriteFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFWriteFailedException.h; path = src/exceptions/OFWriteFailedException.h; sourceTree = ""; }; 4B55A111133AC24600B58A93 /* OFWriteFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFWriteFailedException.m; path = src/exceptions/OFWriteFailedException.m; sourceTree = ""; }; + 4B5AC54E1D97DEB100FECFAA /* ScryptTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ScryptTests.m; path = tests/ScryptTests.m; sourceTree = ""; }; 4B5B02BC18B288A400CE6AE4 /* OFINIFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFINIFile.h; path = src/OFINIFile.h; sourceTree = ""; }; 4B5B02BD18B288A400CE6AE4 /* OFINIFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFINIFile.m; path = src/OFINIFile.m; sourceTree = ""; }; 4B5B02C018B2897500CE6AE4 /* OFINIFileTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFINIFileTests.m; path = tests/OFINIFileTests.m; sourceTree = ""; }; 4B5B02C218B289ED00CE6AE4 /* testfile.ini */ = {isa = PBXFileReference; lastKnownFileType = text; name = testfile.ini; path = tests/testfile.ini; sourceTree = ""; }; 4B5C112817E9AAED003C917F /* apple-forwarding-arm.S */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; name = "apple-forwarding-arm.S"; path = "src/forwarding/apple-forwarding-arm.S"; sourceTree = ""; }; @@ -834,10 +838,12 @@ 4B6799881099E7C50041064A /* OFXMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLElement.m; path = src/OFXMLElement.m; sourceTree = ""; }; 4B6799891099E7C50041064A /* OFXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFXMLParser.h; path = src/OFXMLParser.h; sourceTree = ""; }; 4B67998A1099E7C50041064A /* OFXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFXMLParser.m; path = src/OFXMLParser.m; sourceTree = ""; }; 4B67998B1099E7C50041064A /* threading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = threading.h; path = src/threading.h; sourceTree = ""; }; 4B67998C1099E7C50041064A /* unicode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unicode.h; path = src/unicode.h; sourceTree = ""; }; + 4B6994451D47FB1A007F34DF /* scrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scrypt.h; path = src/scrypt.h; sourceTree = ""; }; + 4B6994461D47FB1A007F34DF /* scrypt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = scrypt.m; path = src/scrypt.m; sourceTree = ""; }; 4B6AF96F10A8D40E0003FB0A /* iso_8859_15.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = iso_8859_15.m; path = src/iso_8859_15.m; sourceTree = ""; }; 4B6AF97210A8D42E0003FB0A /* windows_1252.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = windows_1252.m; path = src/windows_1252.m; sourceTree = ""; }; 4B6AF97310A8D4450003FB0A /* ObjFW.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjFW.h; path = src/ObjFW.h; sourceTree = ""; }; 4B6C8AD117BD5C2E00B194F2 /* OFRunLoop+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFRunLoop+Private.h"; path = "src/OFRunLoop+Private.h"; sourceTree = ""; }; 4B6C8AD217BD5C2E00B194F2 /* OFStream+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFStream+Private.h"; path = "src/OFStream+Private.h"; sourceTree = ""; }; @@ -1586,10 +1592,12 @@ 4BD306321D46CEE300E2F372 /* pbkdf2.h */, 4BD306331D46CEE300E2F372 /* pbkdf2.m */, 4BF69CE51BD44F8B00DFFC1B /* platform.h */, 4B7769EB1895C07D00D12284 /* resolver.h */, 4B7769EC1895C07D00D12284 /* resolver.m */, + 4B6994451D47FB1A007F34DF /* scrypt.h */, + 4B6994461D47FB1A007F34DF /* scrypt.m */, 4B7DD58718943D4A00990FD6 /* socket.h */, 4B40EC1A189FE2650031E19E /* socket.m */, 4B7DD58118942FE200990FD6 /* socket_helpers.h */, 4B67998B1099E7C50041064A /* threading.h */, 4B3379CE1979326A0088E97E /* threading.m */, @@ -1641,10 +1649,11 @@ 4B6EF67C1235358D0076B512 /* OFXMLElementBuilderTests.m */, 4B49EA65143B39CE0005BBC6 /* OFXMLNodeTests.m */, 4B6EF67E1235358D0076B512 /* OFXMLParserTests.m */, 4BD306301D46CEAE00E2F372 /* PBKDF2Tests.m */, 4B44836F1D049887005D12A7 /* RuntimeTests.m */, + 4B5AC54E1D97DEB100FECFAA /* ScryptTests.m */, 4B6EF6801235358D0076B512 /* TestsAppDelegate.h */, 4B6EF6811235358D0076B512 /* TestsAppDelegate.m */, ); name = Tests; sourceTree = ""; @@ -1839,10 +1848,11 @@ 4B3D23E91337FCB000DD29B8 /* of_asprintf.h in Headers */, 4BA355BD14879BF700442EF4 /* of_strptime.h in Headers */, 4BD306341D46CEE300E2F372 /* pbkdf2.h in Headers */, 4BF69CE61BD44F8B00DFFC1B /* platform.h in Headers */, 4B7769ED1895C07D00D12284 /* resolver.h in Headers */, + 4B6994471D47FB1A007F34DF /* scrypt.h in Headers */, 4B7DD58818943D4A00990FD6 /* socket.h in Headers */, 4B3D23EA1337FCB000DD29B8 /* threading.h in Headers */, 4B3D23EB1337FCB000DD29B8 /* unicode.h in Headers */, 4B90B79E133AD87D00BD33CB /* OFAcceptFailedException.h in Headers */, 4B90B7A0133AD87D00BD33CB /* OFAddressTranslationFailedException.h in Headers */, @@ -2230,10 +2240,11 @@ 4B3D23B51337FC0D00DD29B8 /* foundation-compat.m in Sources */, 4B3D23EE1337FFD000DD29B8 /* of_asprintf.m in Sources */, 4BA355BA14879BDD00442EF4 /* of_strptime.m in Sources */, 4BD306351D46CEE300E2F372 /* pbkdf2.m in Sources */, 4B7769EE1895C07D00D12284 /* resolver.m in Sources */, + 4B6994481D47FB1A007F34DF /* scrypt.m in Sources */, 4B40EC1B189FE2650031E19E /* socket.m in Sources */, 4B3379CF1979326A0088E97E /* threading.m in Sources */, 4B3D23B91337FC0D00DD29B8 /* unicode.m in Sources */, 4B3D23BA1337FC0D00DD29B8 /* windows_1252.m in Sources */, 4B90B79F133AD87D00BD33CB /* OFAcceptFailedException.m in Sources */, @@ -2338,10 +2349,11 @@ 4BF33B0E133807A20059CEF7 /* OFXMLElementBuilderTests.m in Sources */, 4B49EA66143B39CE0005BBC6 /* OFXMLNodeTests.m in Sources */, 4BF33B10133807A20059CEF7 /* OFXMLParserTests.m in Sources */, 4BD306311D46CEAE00E2F372 /* PBKDF2Tests.m in Sources */, 4B4483711D04989C005D12A7 /* RuntimeTests.m in Sources */, + 4B5AC54F1D97DEB100FECFAA /* ScryptTests.m in Sources */, 4BF33B12133807A20059CEF7 /* TestsAppDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -86,10 +86,11 @@ base64.m \ crc32.m \ of_asprintf.m \ of_strptime.m \ pbkdf2.m \ + scrypt.m \ ${UNICODE_M} \ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ ${USE_SRCS_SOCKETS} \ ${USE_SRCS_THREADS} Index: src/macros.h ================================================================== --- src/macros.h +++ src/macros.h @@ -598,5 +598,14 @@ memcpy(copy, string, length + 1); return copy; } + +static OF_INLINE void +of_explicit_memset(void *buffer_, int character, size_t length) +{ + volatile unsigned char *buffer = buffer_; + + while (buffer < (unsigned char*)buffer_ + length) + *buffer++ = character; +} Index: src/pbkdf2.m ================================================================== --- src/pbkdf2.m +++ src/pbkdf2.m @@ -34,11 +34,11 @@ 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 || + if (HMAC == nil || iterations == 0 || salt == NULL || password == NULL || key == NULL || keyLength == 0) @throw [OFInvalidArgumentException exception]; blocks = keyLength / digestSize; if (keyLength % digestSize != 0) @@ -89,14 +89,14 @@ 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); + of_explicit_memset(extendedSalt, 0, saltLength + 4); + of_explicit_memset(buffer, 0, digestSize); + of_explicit_memset(digest, 0, digestSize); [HMAC zero]; free(extendedSalt); } } ADDED src/scrypt.h Index: src/scrypt.h ================================================================== --- src/scrypt.h +++ src/scrypt.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * 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 +extern void of_salsa20_8_core(uint32_t buffer[16]); +extern void of_scrypt_block_mix(uint32_t *output, const uint32_t *input, + size_t blockSize); +extern void of_scrypt_romix(uint32_t *buffer, size_t blockSize, + size_t costFactor, uint32_t *tmp); +extern void of_scrypt(size_t blockSize, size_t costFactor, + size_t parallelization, 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/scrypt.m Index: src/scrypt.m ================================================================== --- src/scrypt.m +++ src/scrypt.m @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * 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" + +#import "OFHMAC.h" +#import "OFSHA256Hash.h" + +#import "OFInvalidArgumentException.h" +#import "OFOutOfMemoryException.h" +#import "OFOutOfRangeException.h" + +#import "scrypt.h" +#import "pbkdf2.h" + +void +of_salsa20_8_core(uint32_t buffer[16]) +{ + uint32_t tmp[16]; + + for (uint_fast8_t i = 0; i < 16; i++) + tmp[i] = buffer[i]; + + for (uint_fast8_t i = 0; i < 8; i+= 2) { + tmp[ 4] ^= OF_ROL(tmp[ 0] + tmp[12], 7); + tmp[ 8] ^= OF_ROL(tmp[ 4] + tmp[ 0], 9); + tmp[12] ^= OF_ROL(tmp[ 8] + tmp[ 4], 13); + tmp[ 0] ^= OF_ROL(tmp[12] + tmp[ 8], 18); + tmp[ 9] ^= OF_ROL(tmp[ 5] + tmp[ 1], 7); + tmp[13] ^= OF_ROL(tmp[ 9] + tmp[ 5], 9); + tmp[ 1] ^= OF_ROL(tmp[13] + tmp[ 9], 13); + tmp[ 5] ^= OF_ROL(tmp[ 1] + tmp[13], 18); + tmp[14] ^= OF_ROL(tmp[10] + tmp[ 6], 7); + tmp[ 2] ^= OF_ROL(tmp[14] + tmp[10], 9); + tmp[ 6] ^= OF_ROL(tmp[ 2] + tmp[14], 13); + tmp[10] ^= OF_ROL(tmp[ 6] + tmp[ 2], 18); + tmp[ 3] ^= OF_ROL(tmp[15] + tmp[11], 7); + tmp[ 7] ^= OF_ROL(tmp[ 3] + tmp[15], 9); + tmp[11] ^= OF_ROL(tmp[ 7] + tmp[ 3], 13); + tmp[15] ^= OF_ROL(tmp[11] + tmp[ 7], 18); + tmp[ 1] ^= OF_ROL(tmp[ 0] + tmp[ 3], 7); + tmp[ 2] ^= OF_ROL(tmp[ 1] + tmp[ 0], 9); + tmp[ 3] ^= OF_ROL(tmp[ 2] + tmp[ 1], 13); + tmp[ 0] ^= OF_ROL(tmp[ 3] + tmp[ 2], 18); + tmp[ 6] ^= OF_ROL(tmp[ 5] + tmp[ 4], 7); + tmp[ 7] ^= OF_ROL(tmp[ 6] + tmp[ 5], 9); + tmp[ 4] ^= OF_ROL(tmp[ 7] + tmp[ 6], 13); + tmp[ 5] ^= OF_ROL(tmp[ 4] + tmp[ 7], 18); + tmp[11] ^= OF_ROL(tmp[10] + tmp[ 9], 7); + tmp[ 8] ^= OF_ROL(tmp[11] + tmp[10], 9); + tmp[ 9] ^= OF_ROL(tmp[ 8] + tmp[11], 13); + tmp[10] ^= OF_ROL(tmp[ 9] + tmp[ 8], 18); + tmp[12] ^= OF_ROL(tmp[15] + tmp[14], 7); + tmp[13] ^= OF_ROL(tmp[12] + tmp[15], 9); + tmp[14] ^= OF_ROL(tmp[13] + tmp[12], 13); + tmp[15] ^= OF_ROL(tmp[14] + tmp[13], 18); + } + + for (uint_fast8_t i = 0; i < 16; i++) + buffer[i] += tmp[i]; + + of_explicit_memset(tmp, 0, sizeof(tmp)); +} + +void +of_scrypt_block_mix(uint32_t *output, const uint32_t *input, size_t blockSize) +{ + uint32_t tmp[16]; + + /* Check defined here and executed in of_scrypt() */ +#define OVERFLOW_CHECK_1 \ + if (blockSize > SIZE_MAX / 2 || \ + 2 * blockSize - 1 > SIZE_MAX / 16) \ + @throw [OFOutOfRangeException exception]; + + memcpy(tmp, input + (2 * blockSize - 1) * 16, 64); + + for (size_t i = 0; i < 2 * blockSize; i++) { + for (size_t j = 0; j < 16; j++) + tmp[j] ^= input[i * 16 + j]; + + of_salsa20_8_core(tmp); + + /* + * Even indices are stored in the first half and odd ones in + * the second. + */ + memcpy(output + ((i / 2) + (i & 1) * blockSize) * 16, tmp, 64); + } + + of_explicit_memset(tmp, 0, sizeof(tmp)); +} + +void +of_scrypt_romix(uint32_t *buffer, size_t blockSize, size_t costFactor, + uint32_t *tmp) +{ + /* Check defined here and executed in of_scrypt() */ +#define OVERFLOW_CHECK_2 \ + if (blockSize > SIZE_MAX / 128 / costFactor) \ + @throw [OFOutOfRangeException exception]; + + uint32_t *tmp2 = tmp + 32 * blockSize; + + memcpy(tmp, buffer, 128 * blockSize); + + for (size_t i = 0; i < costFactor; i++) { + memcpy(tmp2 + i * 32 * blockSize, tmp, 128 * blockSize); + of_scrypt_block_mix(tmp, tmp2 + i * 32 * blockSize, blockSize); + } + + for (size_t i = 0; i < costFactor; i++) { + uint32_t j = tmp[(2 * blockSize - 1) * 16] & (costFactor - 1); + + for (size_t k = 0; k < 32 * blockSize; k++) + tmp[k] ^= tmp2[j * 32 * blockSize + k]; + + of_scrypt_block_mix(buffer, tmp, blockSize); + + if (i < costFactor - 1) + memcpy(tmp, buffer, 128 * blockSize); + } +} + +void of_scrypt(size_t blockSize, size_t costFactor, + size_t parallelization, const unsigned char *salt, size_t saltLength, + const char *password, size_t passwordLength, + unsigned char *key, size_t keyLength) +{ + uint32_t *tmp = NULL, *buffer = NULL; + OFHMAC *HMAC = nil; + + if (blockSize == 0 || costFactor <= 1 || + (costFactor & (costFactor - 1)) != 0 || parallelization == 0) + @throw [OFInvalidArgumentException exception]; + + /* + * These are defined by the functions above. They are defined there so + * that the check is next to the code and easy to verify, but actually + * checked here for performance. + */ + OVERFLOW_CHECK_1 + OVERFLOW_CHECK_2 + + @try { + if (costFactor > SIZE_MAX - 1 || + (costFactor + 1) > SIZE_MAX / 128 || + (costFactor + 1) * 128 > SIZE_MAX / blockSize) + @throw [OFOutOfRangeException exception]; + + if ((tmp = malloc((costFactor + 1) * 128 * blockSize)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: (blockSize + + costFactor) * 128]; + + if (parallelization > SIZE_MAX / 128 || + parallelization * 128 > SIZE_MAX / blockSize) + @throw [OFOutOfRangeException exception]; + + if ((buffer = malloc(parallelization * 128 * + blockSize)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: parallelization * 128 * + blockSize]; + + HMAC = [[OFHMAC alloc] initWithHashClass: [OFSHA256Hash class]]; + + of_pbkdf2(HMAC, 1, salt, saltLength, password, passwordLength, + (unsigned char*)buffer, parallelization * 128 * blockSize); + + for (size_t i = 0; i < parallelization; i++) + of_scrypt_romix(buffer + i * 32 * blockSize, blockSize, + costFactor, tmp); + + of_pbkdf2(HMAC, 1, (unsigned char*)buffer, parallelization * + 128 * blockSize, password, passwordLength, key, keyLength); + } @finally { + of_explicit_memset(tmp, 0, (costFactor + 1) * blockSize * 128); + free(tmp); + + of_explicit_memset(buffer, 0, + parallelization * 128 * blockSize); + free(buffer); + + [HMAC release]; + } +} Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -26,10 +26,11 @@ OFXMLElementBuilderTests.m \ OFXMLNodeTests.m \ OFXMLParserTests.m \ PBKDF2Tests.m \ RuntimeTests.m \ + ScryptTests.m \ TestsAppDelegate.m \ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ ${USE_SRCS_SOCKETS} \ ${USE_SRCS_THREADS} \ ADDED tests/ScryptTests.m Index: tests/ScryptTests.m ================================================================== --- tests/ScryptTests.m +++ tests/ScryptTests.m @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 + * Jonathan Schleifer + * + * 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 + +#import "OFString.h" +#import "OFAutoreleasePool.h" + +#import "scrypt.h" + +#import "TestsAppDelegate.h" + +static OFString *module = @"scrypt"; +/* Test vectors form RFC 7914 */ +static const unsigned char salsa20Input[64] = { + 0x7E, 0x87, 0x9A, 0x21, 0x4F, 0x3E, 0xC9, 0x86, 0x7C, 0xA9, 0x40, 0xE6, + 0x41, 0x71, 0x8F, 0x26, 0xBA, 0xEE, 0x55, 0x5B, 0x8C, 0x61, 0xC1, 0xB5, + 0x0D, 0xF8, 0x46, 0x11, 0x6D, 0xCD, 0x3B, 0x1D, 0xEE, 0x24, 0xF3, 0x19, + 0xDF, 0x9B, 0x3D, 0x85, 0x14, 0x12, 0x1E, 0x4B, 0x5A, 0xC5, 0xAA, 0x32, + 0x76, 0x02, 0x1D, 0x29, 0x09, 0xC7, 0x48, 0x29, 0xED, 0xEB, 0xC6, 0x8D, + 0xB8, 0xB8, 0xC2, 0x5E +}; +static const unsigned char salsa20Output[64] = { + 0xA4, 0x1F, 0x85, 0x9C, 0x66, 0x08, 0xCC, 0x99, 0x3B, 0x81, 0xCA, 0xCB, + 0x02, 0x0C, 0xEF, 0x05, 0x04, 0x4B, 0x21, 0x81, 0xA2, 0xFD, 0x33, 0x7D, + 0xFD, 0x7B, 0x1C, 0x63, 0x96, 0x68, 0x2F, 0x29, 0xB4, 0x39, 0x31, 0x68, + 0xE3, 0xC9, 0xE6, 0xBC, 0xFE, 0x6B, 0xC5, 0xB7, 0xA0, 0x6D, 0x96, 0xBA, + 0xE4, 0x24, 0xCC, 0x10, 0x2C, 0x91, 0x74, 0x5C, 0x24, 0xAD, 0x67, 0x3D, + 0xC7, 0x61, 0x8F, 0x81 +}; +static const union { + unsigned char uc[128]; + uint32_t u32[32]; +} blockMixInput = { .uc = { + 0xF7, 0xCE, 0x0B, 0x65, 0x3D, 0x2D, 0x72, 0xA4, 0x10, 0x8C, 0xF5, 0xAB, + 0xE9, 0x12, 0xFF, 0xDD, 0x77, 0x76, 0x16, 0xDB, 0xBB, 0x27, 0xA7, 0x0E, + 0x82, 0x04, 0xF3, 0xAE, 0x2D, 0x0F, 0x6F, 0xAD, 0x89, 0xF6, 0x8F, 0x48, + 0x11, 0xD1, 0xE8, 0x7B, 0xCC, 0x3B, 0xD7, 0x40, 0x0A, 0x9F, 0xFD, 0x29, + 0x09, 0x4F, 0x01, 0x84, 0x63, 0x95, 0x74, 0xF3, 0x9A, 0xE5, 0xA1, 0x31, + 0x52, 0x17, 0xBC, 0xD7, + 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, 0xBB, 0x22, 0x6C, 0x25, 0xB5, 0x4D, + 0xA8, 0x63, 0x70, 0xFB, 0xCD, 0x98, 0x43, 0x80, 0x37, 0x46, 0x66, 0xBB, + 0x8F, 0xFC, 0xB5, 0xBF, 0x40, 0xC2, 0x54, 0xB0, 0x67, 0xD2, 0x7C, 0x51, + 0xCE, 0x4A, 0xD5, 0xFE, 0xD8, 0x29, 0xC9, 0x0B, 0x50, 0x5A, 0x57, 0x1B, + 0x7F, 0x4D, 0x1C, 0xAD, 0x6A, 0x52, 0x3C, 0xDA, 0x77, 0x0E, 0x67, 0xBC, + 0xEA, 0xAF, 0x7E, 0x89 +}}; +static const unsigned char blockMixOutput[128] = { + 0xA4, 0x1F, 0x85, 0x9C, 0x66, 0x08, 0xCC, 0x99, 0x3B, 0x81, 0xCA, 0xCB, + 0x02, 0x0C, 0xEF, 0x05, 0x04, 0x4B, 0x21, 0x81, 0xA2, 0xFD, 0x33, 0x7D, + 0xFD, 0x7B, 0x1C, 0x63, 0x96, 0x68, 0x2F, 0x29, 0xB4, 0x39, 0x31, 0x68, + 0xE3, 0xC9, 0xE6, 0xBC, 0xFE, 0x6B, 0xC5, 0xB7, 0xA0, 0x6D, 0x96, 0xBA, + 0xE4, 0x24, 0xCC, 0x10, 0x2C, 0x91, 0x74, 0x5C, 0x24, 0xAD, 0x67, 0x3D, + 0xC7, 0x61, 0x8F, 0x81, + 0x20, 0xED, 0xC9, 0x75, 0x32, 0x38, 0x81, 0xA8, 0x05, 0x40, 0xF6, 0x4C, + 0x16, 0x2D, 0xCD, 0x3C, 0x21, 0x07, 0x7C, 0xFE, 0x5F, 0x8D, 0x5F, 0xE2, + 0xB1, 0xA4, 0x16, 0x8F, 0x95, 0x36, 0x78, 0xB7, 0x7D, 0x3B, 0x3D, 0x80, + 0x3B, 0x60, 0xE4, 0xAB, 0x92, 0x09, 0x96, 0xE5, 0x9B, 0x4D, 0x53, 0xB6, + 0x5D, 0x2A, 0x22, 0x58, 0x77, 0xD5, 0xED, 0xF5, 0x84, 0x2C, 0xB9, 0xF1, + 0x4E, 0xEF, 0xE4, 0x25 +}; +static const unsigned char ROMixInput[128] = { + 0xF7, 0xCE, 0x0B, 0x65, 0x3D, 0x2D, 0x72, 0xA4, 0x10, 0x8C, 0xF5, 0xAB, + 0xE9, 0x12, 0xFF, 0xDD, 0x77, 0x76, 0x16, 0xDB, 0xBB, 0x27, 0xA7, 0x0E, + 0x82, 0x04, 0xF3, 0xAE, 0x2D, 0x0F, 0x6F, 0xAD, 0x89, 0xF6, 0x8F, 0x48, + 0x11, 0xD1, 0xE8, 0x7B, 0xCC, 0x3B, 0xD7, 0x40, 0x0A, 0x9F, 0xFD, 0x29, + 0x09, 0x4F, 0x01, 0x84, 0x63, 0x95, 0x74, 0xF3, 0x9A, 0xE5, 0xA1, 0x31, + 0x52, 0x17, 0xBC, 0xD7, 0x89, 0x49, 0x91, 0x44, 0x72, 0x13, 0xBB, 0x22, + 0x6C, 0x25, 0xB5, 0x4D, 0xA8, 0x63, 0x70, 0xFB, 0xCD, 0x98, 0x43, 0x80, + 0x37, 0x46, 0x66, 0xBB, 0x8F, 0xFC, 0xB5, 0xBF, 0x40, 0xC2, 0x54, 0xB0, + 0x67, 0xD2, 0x7C, 0x51, 0xCE, 0x4A, 0xD5, 0xFE, 0xD8, 0x29, 0xC9, 0x0B, + 0x50, 0x5A, 0x57, 0x1B, 0x7F, 0x4D, 0x1C, 0xAD, 0x6A, 0x52, 0x3C, 0xDA, + 0x77, 0x0E, 0x67, 0xBC, 0xEA, 0xAF, 0x7E, 0x89 +}; +static const unsigned char ROMixOutput[128] = { + 0x79, 0xCC, 0xC1, 0x93, 0x62, 0x9D, 0xEB, 0xCA, 0x04, 0x7F, 0x0B, 0x70, + 0x60, 0x4B, 0xF6, 0xB6, 0x2C, 0xE3, 0xDD, 0x4A, 0x96, 0x26, 0xE3, 0x55, + 0xFA, 0xFC, 0x61, 0x98, 0xE6, 0xEA, 0x2B, 0x46, 0xD5, 0x84, 0x13, 0x67, + 0x3B, 0x99, 0xB0, 0x29, 0xD6, 0x65, 0xC3, 0x57, 0x60, 0x1F, 0xB4, 0x26, + 0xA0, 0xB2, 0xF4, 0xBB, 0xA2, 0x00, 0xEE, 0x9F, 0x0A, 0x43, 0xD1, 0x9B, + 0x57, 0x1A, 0x9C, 0x71, 0xEF, 0x11, 0x42, 0xE6, 0x5D, 0x5A, 0x26, 0x6F, + 0xDD, 0xCA, 0x83, 0x2C, 0xE5, 0x9F, 0xAA, 0x7C, 0xAC, 0x0B, 0x9C, 0xF1, + 0xBE, 0x2B, 0xFF, 0xCA, 0x30, 0x0D, 0x01, 0xEE, 0x38, 0x76, 0x19, 0xC4, + 0xAE, 0x12, 0xFD, 0x44, 0x38, 0xF2, 0x03, 0xA0, 0xE4, 0xE1, 0xC4, 0x7E, + 0xC3, 0x14, 0x86, 0x1F, 0x4E, 0x90, 0x87, 0xCB, 0x33, 0x39, 0x6A, 0x68, + 0x73, 0xE8, 0xF9, 0xD2, 0x53, 0x9A, 0x4B, 0x8E +}; +static const unsigned char testVector1[64] = { + 0x77, 0xD6, 0x57, 0x62, 0x38, 0x65, 0x7B, 0x20, 0x3B, 0x19, 0xCA, 0x42, + 0xC1, 0x8A, 0x04, 0x97, 0xF1, 0x6B, 0x48, 0x44, 0xE3, 0x07, 0x4A, 0xE8, + 0xDF, 0xDF, 0xFA, 0x3F, 0xED, 0xE2, 0x14, 0x42, 0xFC, 0xD0, 0x06, 0x9D, + 0xED, 0x09, 0x48, 0xF8, 0x32, 0x6A, 0x75, 0x3A, 0x0F, 0xC8, 0x1F, 0x17, + 0xE8, 0xD3, 0xE0, 0xFB, 0x2E, 0x0D, 0x36, 0x28, 0xCF, 0x35, 0xE2, 0x0C, + 0x38, 0xD1, 0x89, 0x06 +}; +static const unsigned char testVector2[64] = { + 0xFD, 0xBA, 0xBE, 0x1C, 0x9D, 0x34, 0x72, 0x00, 0x78, 0x56, 0xE7, 0x19, + 0x0D, 0x01, 0xE9, 0xFE, 0x7C, 0x6A, 0xD7, 0xCB, 0xC8, 0x23, 0x78, 0x30, + 0xE7, 0x73, 0x76, 0x63, 0x4B, 0x37, 0x31, 0x62, 0x2E, 0xAF, 0x30, 0xD9, + 0x2E, 0x22, 0xA3, 0x88, 0x6F, 0xF1, 0x09, 0x27, 0x9D, 0x98, 0x30, 0xDA, + 0xC7, 0x27, 0xAF, 0xB9, 0x4A, 0x83, 0xEE, 0x6D, 0x83, 0x60, 0xCB, 0xDF, + 0xA2, 0xCC, 0x06, 0x40 +}; +static const unsigned char testVector3[64] = { + 0x70, 0x23, 0xBD, 0xCB, 0x3A, 0xFD, 0x73, 0x48, 0x46, 0x1C, 0x06, 0xCD, + 0x81, 0xFD, 0x38, 0xEB, 0xFD, 0xA8, 0xFB, 0xBA, 0x90, 0x4F, 0x8E, 0x3E, + 0xA9, 0xB5, 0x43, 0xF6, 0x54, 0x5D, 0xA1, 0xF2, 0xD5, 0x43, 0x29, 0x55, + 0x61, 0x3F, 0x0F, 0xCF, 0x62, 0xD4, 0x97, 0x05, 0x24, 0x2A, 0x9A, 0xF9, + 0xE6, 0x1E, 0x85, 0xDC, 0x0D, 0x65, 0x1E, 0x40, 0xDF, 0xCF, 0x01, 0x7B, + 0x45, 0x57, 0x58, 0x87 +}; +/* The forth test vector is too expensive to include it in the tests. */ +#if 0 +static const unsigned char testVector4[64] = { + 0x21, 0x01, 0xCB, 0x9B, 0x6A, 0x51, 0x1A, 0xAE, 0xAD, 0xDB, 0xBE, 0x09, + 0xCF, 0x70, 0xF8, 0x81, 0xEC, 0x56, 0x8D, 0x57, 0x4A, 0x2F, 0xFD, 0x4D, + 0xAB, 0xE5, 0xEE, 0x98, 0x20, 0xAD, 0xAA, 0x47, 0x8E, 0x56, 0xFD, 0x8F, + 0x4B, 0xA5, 0xD0, 0x9F, 0xFA, 0x1C, 0x6D, 0x92, 0x7C, 0x40, 0xF4, 0xC3, + 0x37, 0x30, 0x40, 0x49, 0xE8, 0xA9, 0x52, 0xFB, 0xCB, 0xF4, 0x5C, 0x6F, + 0xA7, 0x7A, 0x41, 0xA4 +}; +#endif + +@implementation TestsAppDelegate (ScryptTests) +- (void)scryptTests +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + uint32_t salsa20Buffer[16]; + uint32_t blockMixBuffer[32]; + uint32_t ROMixBuffer[32], ROMixTmp[17 * 32]; + unsigned char output[64]; + + TEST(@"Salsa20/8 Core", + R(memcpy(salsa20Buffer, salsa20Input, 64)) && + R(of_salsa20_8_core(salsa20Buffer)) && + memcmp(salsa20Buffer, salsa20Output, 64) == 0) + + TEST(@"Block mix", + R(of_scrypt_block_mix(blockMixBuffer, blockMixInput.u32, 1)) && + memcmp(blockMixBuffer, blockMixOutput, 128) == 0) + + TEST(@"ROMix", + R(memcpy(ROMixBuffer, ROMixInput, 128)) && + R(of_scrypt_romix(ROMixBuffer, 1, 16, ROMixTmp)) && + memcmp(ROMixBuffer, ROMixOutput, 128) == 0) + + TEST(@"scrypt test vector #1", + R(of_scrypt(1, 16, 1, (unsigned char*)"", 0, "", 0, output, 64)) && + memcmp(output, testVector1, 64) == 0) + + TEST(@"scrypt test vector #2", + R(of_scrypt(8, 1024, 16, (unsigned char*)"NaCl", 4, "password", 8, + output, 64)) && memcmp(output, testVector2, 64) == 0) + + TEST(@"scrypt test vector #3", + R(of_scrypt(8, 16384, 1, (unsigned char*)"SodiumChloride", 14, + "pleaseletmein", 13, output, 64)) && + memcmp(output, testVector3, 64) == 0) + + /* The forth test vector is too expensive to include it in the tests. */ +#if 0 + TEST(@"scrypt test vector #4", + R(of_scrypt(8, 1048576, 1, (unsigned char*)"SodiumChloride", 14, + "pleaseletmein", 13, output, 64)) && + memcmp(output, testVector4, 64) == 0) +#endif + + [pool drain]; +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -141,10 +141,14 @@ @end @interface TestsAppDelegate (OFRIPEMD160HashTests) - (void)RIPEMD160HashTests; @end + +@interface TestsAppDelegate (ScryptTests) +- (void)scryptTests; +@end @interface TestsAppDelegate (OFSerializationTests) - (void)serializationTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -384,10 +384,11 @@ [self SHA256HashTests]; [self SHA384HashTests]; [self SHA512HashTests]; [self HMACTests]; [self PBKDF2Tests]; + [self scryptTests]; [self INIFileTests]; #endif #ifdef OF_HAVE_SOCKETS [self TCPSocketTests]; [self UDPSocketTests];