/* pwdbased.c * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include #ifndef NO_PWDBASED #if FIPS_VERSION3_GE(6,0,0) /* set NO_WRAPPERS before headers, use direct internal f()s not wrappers */ #define FIPS_NO_WRAPPERS #ifdef USE_WINDOWS_API #pragma code_seg(".fipsA$h") #pragma const_seg(".fipsB$h") #endif #endif #include #include #include #include #ifdef NO_INLINE #include #else #define WOLFSSL_MISC_INCLUDED #include #endif #if FIPS_VERSION3_GE(6,0,0) const unsigned int wolfCrypt_FIPS_pbkdf_ro_sanity[2] = { 0x1a2b3c4d, 0x00000010 }; int wolfCrypt_FIPS_PBKDF_sanity(void) { return 0; } #endif static int current_wc_pbkdf_max_iterations = WC_PBKDF_DEFAULT_MAX_ITERATIONS; int wc_PBKDF_max_iterations_set(int iters) { if (iters <= 0) return BAD_FUNC_ARG; else { int prev = current_wc_pbkdf_max_iterations; current_wc_pbkdf_max_iterations = iters; return prev; } } int wc_PBKDF_max_iterations_get(void) { return current_wc_pbkdf_max_iterations; } #ifdef HAVE_PBKDF1 /* PKCS#5 v1.5 with non standard extension to optionally derive the extra data (IV) */ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, const byte* passwd, int passwdLen, const byte* salt, int saltLen, int iterations, int hashType, void* heap) { int err; int keyLeft, ivLeft, i; int store; int keyOutput = 0; int digestLen; byte digest[WC_MAX_DIGEST_SIZE]; WC_DECLARE_VAR(hash, wc_HashAlg, 1, 0); enum wc_HashType hashT; (void)heap; if (key == NULL || keyLen < 0 || passwdLen < 0 || saltLen < 0 || ivLen < 0){ return BAD_FUNC_ARG; } if (keyLen > INT_MAX - ivLen) return BAD_FUNC_ARG; if (iterations <= 0) iterations = 1; if (iterations > current_wc_pbkdf_max_iterations) { WOLFSSL_MSG("PBKDF1 iteration count exceeds current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } hashT = wc_HashTypeConvert(hashType); err = wc_HashGetDigestSize(hashT); if (err < 0) return err; digestLen = err; /* initialize hash */ WC_ALLOC_VAR_EX(hash, wc_HashAlg, 1, heap, DYNAMIC_TYPE_HASHCTX, return MEMORY_E); err = wc_HashInit_ex(hash, hashT, heap, INVALID_DEVID); if (err != 0) { WC_FREE_VAR_EX(hash, heap, DYNAMIC_TYPE_HASHCTX); return err; } keyLeft = keyLen; ivLeft = ivLen; while (keyOutput < (keyLen + ivLen)) { int digestLeft = digestLen; /* D_(i - 1) */ if (keyOutput) { /* first time D_0 is empty */ err = wc_HashUpdate(hash, hashT, digest, (word32)digestLen); if (err != 0) break; } /* data */ err = wc_HashUpdate(hash, hashT, passwd, (word32)passwdLen); if (err != 0) break; /* salt */ if (salt) { err = wc_HashUpdate(hash, hashT, salt, (word32)saltLen); if (err != 0) break; } err = wc_HashFinal(hash, hashT, digest); if (err != 0) break; /* count */ for (i = 1; i < iterations; i++) { err = wc_HashUpdate(hash, hashT, digest, (word32)digestLen); if (err != 0) break; err = wc_HashFinal(hash, hashT, digest); if (err != 0) break; } if (err != 0) break; if (keyLeft) { store = (int)min((word32)keyLeft, (word32)digestLen); XMEMCPY(&key[keyLen - keyLeft], digest, (size_t)store); keyOutput += store; keyLeft -= store; digestLeft -= store; } if (ivLeft && digestLeft) { store = (int)min((word32)ivLeft, (word32)digestLeft); if (iv != NULL) XMEMCPY(&iv[ivLen - ivLeft], &digest[digestLen - digestLeft], (size_t)store); keyOutput += store; ivLeft -= store; } } wc_HashFree(hash, hashT); WC_FREE_VAR_EX(hash, heap, DYNAMIC_TYPE_HASHCTX); ForceZero(digest, sizeof(digest)); if (err != 0) return err; if (keyOutput != (keyLen + ivLen)) return BUFFER_E; return err; } /* PKCS#5 v1.5 */ int wc_PBKDF1(byte* output, const byte* passwd, int pLen, const byte* salt, int sLen, int iterations, int kLen, int hashType) { return wc_PBKDF1_ex(output, kLen, NULL, 0, passwd, pLen, salt, sLen, iterations, hashType, NULL); } #endif /* HAVE_PKCS5 */ #if defined(HAVE_PBKDF2) && !defined(NO_HMAC) int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, int sLen, int iterations, int kLen, int hashType, void* heap, int devId) { int hLen; int ret; #ifdef WOLFSSL_SMALL_STACK byte* buffer; Hmac* hmac; #else byte buffer[WC_MAX_DIGEST_SIZE]; Hmac hmac[1]; #endif enum wc_HashType hashT; if (output == NULL || pLen < 0 || sLen < 0 || kLen < 0) { return BAD_FUNC_ARG; } #if FIPS_VERSION3_GE(6,0,0) /* Per SP800-132 section 5 "The kLen value shall be at least 112 bits in * length", ensure the returned bits for the derived master key are at a * minimum 14-bytes or 112-bits after stretching and strengthening * (iterations) */ if (kLen < HMAC_FIPS_MIN_KEY) return BAD_LENGTH_E; #endif #if FIPS_VERSION3_GE(6,0,0) && defined(DEBUG_WOLFSSL) /* SP800-132 section 5.2 recommends an iteration count of 1000 but this is * not strictly enforceable and is listed in Appendix B Table 1 as a * non-testable requirement. wolfCrypt will log it when appropriate but * take no action */ if (iterations < 1000) { WOLFSSL_MSG("WARNING: Iteration < 1,000, see SP800-132 section 5.2"); } #endif if (iterations <= 0) iterations = 1; if (iterations > current_wc_pbkdf_max_iterations) { WOLFSSL_MSG("PBKDF2 iteration count exceeds current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } hashT = wc_HashTypeConvert(hashType); hLen = wc_HashGetDigestSize(hashT); if (hLen < 0) return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK buffer = (byte*)XMALLOC(WC_MAX_DIGEST_SIZE, heap, DYNAMIC_TYPE_TMP_BUFFER); if (buffer == NULL) return MEMORY_E; hmac = (Hmac*)XMALLOC(sizeof(Hmac), heap, DYNAMIC_TYPE_HMAC); if (hmac == NULL) { XFREE(buffer, heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } #endif ret = wc_HmacInit(hmac, heap, devId); if (ret == 0) { word32 i = 1; /* use int hashType here, since HMAC FIPS uses the old unique value */ #if FIPS_VERSION3_GE(6,0,0) { /* Allow passwords that are less than 14-bytes for compatibility * / interoperability, only since module v6.0.0 */ int allowShortPasswd = 1; ret = wc_HmacSetKey_ex(hmac, hashType, passwd, (word32)pLen, allowShortPasswd); } #else ret = wc_HmacSetKey(hmac, hashType, passwd, (word32)pLen); #endif while (ret == 0 && kLen) { int currentLen; int j; ret = wc_HmacUpdate(hmac, salt, (word32)sLen); if (ret != 0) break; /* encode i */ for (j = 0; j < 4; j++) { byte b = (byte)(i >> ((3-j) * 8)); ret = wc_HmacUpdate(hmac, &b, 1); if (ret != 0) break; } /* check ret from inside for loop */ if (ret != 0) break; ret = wc_HmacFinal(hmac, buffer); if (ret != 0) break; currentLen = (int)min((word32)kLen, (word32)hLen); XMEMCPY(output, buffer, (size_t)currentLen); for (j = 1; j < iterations; j++) { ret = wc_HmacUpdate(hmac, buffer, (word32)hLen); if (ret != 0) break; ret = wc_HmacFinal(hmac, buffer); if (ret != 0) break; xorbuf(output, buffer, (word32)currentLen); } /* check ret from inside for loop */ if (ret != 0) break; output += currentLen; kLen -= currentLen; i++; } wc_HmacFree(hmac); } ForceZero(buffer, (word32)hLen); WC_FREE_VAR_EX(buffer, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(hmac, heap, DYNAMIC_TYPE_HMAC); return ret; } int wc_PBKDF2(byte* output, const byte* passwd, int pLen, const byte* salt, int sLen, int iterations, int kLen, int hashType) { return wc_PBKDF2_ex(output, passwd, pLen, salt, sLen, iterations, kLen, hashType, NULL, INVALID_DEVID); } #endif /* HAVE_PBKDF2 && !NO_HMAC */ #ifdef HAVE_PKCS12 /* helper for PKCS12_PBKDF(), does hash operation */ static int DoPKCS12Hash(enum wc_HashType hashT, byte* buffer, word32 totalLen, byte* Ai, word32 u, int iterations) { int i; int ret = 0; WC_DECLARE_VAR(hash, wc_HashAlg, 1, 0); if ((buffer == NULL) || (Ai == NULL)) { return BAD_FUNC_ARG; } /* initialize hash */ WC_ALLOC_VAR_EX(hash, wc_HashAlg, 1, NULL, DYNAMIC_TYPE_HASHCTX, return MEMORY_E); ret = wc_HashInit(hash, hashT); if (ret == 0) { ret = wc_HashUpdate(hash, hashT, buffer, totalLen); if (ret == 0) ret = wc_HashFinal(hash, hashT, Ai); for (i = 1; i < iterations; i++) { if (ret == 0) ret = wc_HashUpdate(hash, hashT, Ai, u); if (ret == 0) ret = wc_HashFinal(hash, hashT, Ai); } wc_HashFree(hash, hashT); } WC_FREE_VAR_EX(hash, NULL, DYNAMIC_TYPE_HASHCTX); return ret; } int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen, const byte* salt, int saltLen, int iterations, int kLen, int hashType, int id) { return wc_PKCS12_PBKDF_ex(output, passwd, passLen, salt, saltLen, iterations, kLen, hashType, id, NULL); } #ifdef WC_PKCS12_PBKDF_USING_MP_API /* extended API that allows a heap hint to be used */ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, const byte* salt, int saltLen, int iterations, int kLen, int hashType, int id, void* heap) { /* all in bytes instead of bits */ word32 u, v, dLen, pLen, iLen, sLen, totalLen; int dynamic = 0; int ret = 0; word32 i; byte *D, *S, *P, *I; #ifdef WOLFSSL_SMALL_STACK byte staticBuffer[1]; /* force dynamic usage */ #else byte staticBuffer[1024]; #endif byte* buffer = staticBuffer; #ifdef WOLFSSL_SMALL_STACK byte* Ai = NULL; byte* B = NULL; mp_int *B1 = NULL; mp_int *i1 = NULL; mp_int *res = NULL; #else byte Ai[WC_MAX_DIGEST_SIZE]; byte B[WC_MAX_BLOCK_SIZE]; mp_int B1[1]; mp_int i1[1]; mp_int res[1]; #endif enum wc_HashType hashT; (void)heap; if (output == NULL || passLen <= 0 || saltLen <= 0 || kLen < 0) { return BAD_FUNC_ARG; } if (iterations <= 0) iterations = 1; if (iterations > current_wc_pbkdf_max_iterations) { WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds " "current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } hashT = wc_HashTypeConvert(hashType); ret = wc_HashGetDigestSize(hashT); if (ret < 0) return ret; if (ret == 0) return BAD_STATE_E; u = (word32)ret; ret = wc_HashGetBlockSize(hashT); if (ret < 0) return ret; if (ret == 0) return BAD_STATE_E; v = (word32)ret; #ifdef WOLFSSL_SMALL_STACK Ai = (byte*)XMALLOC(WC_MAX_DIGEST_SIZE, heap, DYNAMIC_TYPE_TMP_BUFFER); if (Ai == NULL) return MEMORY_E; B = (byte*)XMALLOC(WC_MAX_BLOCK_SIZE, heap, DYNAMIC_TYPE_TMP_BUFFER); if (B == NULL) { XFREE(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } #endif XMEMSET(Ai, 0, WC_MAX_DIGEST_SIZE); XMEMSET(B, 0, WC_MAX_BLOCK_SIZE); dLen = v; sLen = v * (((word32)saltLen + v - 1) / v); /* with passLen checked at the top of the function for >= 0 then passLen * must be 1 or greater here and is always 'true' */ pLen = v * (((word32)passLen + v - 1) / v); if (! WC_SAFE_SUM_UNSIGNED(word32, sLen, pLen, iLen)) { WC_FREE_VAR_EX(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); return BAD_FUNC_ARG; } if (! WC_SAFE_SUM_UNSIGNED(word32, dLen, sLen, totalLen)) { WC_FREE_VAR_EX(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); return BAD_FUNC_ARG; } if (totalLen > sizeof(staticBuffer)) { buffer = (byte*)XMALLOC(totalLen, heap, DYNAMIC_TYPE_KEY); if (buffer == NULL) { WC_FREE_VAR_EX(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } dynamic = 1; } D = buffer; S = D + dLen; P = S + sLen; I = S; XMEMSET(D, id, dLen); for (i = 0; i < sLen; i++) S[i] = salt[i % (word32)saltLen]; for (i = 0; i < pLen; i++) P[i] = passwd[i % (word32)passLen]; #ifdef WOLFSSL_SMALL_STACK if (((B1 = (mp_int *)XMALLOC(sizeof(*B1), heap, DYNAMIC_TYPE_TMP_BUFFER)) == NULL) || ((i1 = (mp_int *)XMALLOC(sizeof(*i1), heap, DYNAMIC_TYPE_TMP_BUFFER)) == NULL) || ((res = (mp_int *)XMALLOC(sizeof(*res), heap, DYNAMIC_TYPE_TMP_BUFFER)) == NULL)) { ret = MEMORY_E; goto out; } #endif while (kLen > 0) { word32 currentLen; ret = DoPKCS12Hash(hashT, buffer, totalLen, Ai, u, iterations); if (ret != 0) break; for (i = 0; i < v; i++) B[i] = Ai[(word32)i % u]; if (mp_init(B1) != MP_OKAY) ret = MP_INIT_E; else if (mp_read_unsigned_bin(B1, B, v) != MP_OKAY) ret = MP_READ_E; else if (mp_add_d(B1, (mp_digit)1, B1) != MP_OKAY) ret = MP_ADD_E; if (ret != 0) { mp_clear(B1); break; } for (i = 0; i < iLen; i += v) { int outSz; if (mp_init_multi(i1, res, NULL, NULL, NULL, NULL) != MP_OKAY) { ret = MP_INIT_E; break; } if (mp_read_unsigned_bin(i1, I + i, v) != MP_OKAY) ret = MP_READ_E; else if (mp_add(i1, B1, res) != MP_OKAY) ret = MP_ADD_E; else if ( (outSz = mp_unsigned_bin_size(res)) < 0) ret = MP_TO_E; else { if (outSz > (int)v) { /* take off MSB */ byte tmp[WC_MAX_BLOCK_SIZE + 1]; ret = mp_to_unsigned_bin(res, tmp); XMEMCPY(I + i, tmp + 1, v); } else if (outSz < (int)v) { XMEMSET(I + i, 0, v - (word32)outSz); ret = mp_to_unsigned_bin(res, I + i + v - (word32)outSz); } else ret = mp_to_unsigned_bin(res, I + i); } mp_clear(i1); mp_clear(res); if (ret < 0) break; } if (ret < 0) { mp_clear(B1); break; } currentLen = min((word32)kLen, u); XMEMCPY(output, Ai, currentLen); output += currentLen; kLen -= (int)currentLen; mp_clear(B1); } #ifdef WOLFSSL_SMALL_STACK out: ForceZero(Ai, WC_MAX_DIGEST_SIZE); XFREE(Ai, heap, DYNAMIC_TYPE_TMP_BUFFER); ForceZero(B, WC_MAX_BLOCK_SIZE); XFREE(B, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(B1, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(i1, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(res, heap, DYNAMIC_TYPE_TMP_BUFFER); #else ForceZero(Ai, WC_MAX_DIGEST_SIZE); ForceZero(B, WC_MAX_BLOCK_SIZE); #endif ForceZero(buffer, totalLen); if (dynamic) XFREE(buffer, heap, DYNAMIC_TYPE_KEY); return ret; } #else #if defined(WC_64BIT_CPU) && defined(HAVE___UINT128_T) && \ !defined(NO_INT128) #define PKCS12_DWORD word128 #define PKCS12_WORD word64 #define PKCS12_ByteReverseWords ByteReverseWords64 #elif defined(WC_32BIT_CPU) || defined(WC_64BIT_CPU) #define PKCS12_DWORD word64 #define PKCS12_WORD word32 #define PKCS12_ByteReverseWords ByteReverseWords #else #define PKCS12_DWORD word16 #define PKCS12_WORD word8 /* No need to byte reverse when handling 1 byte at a time. */ #define PKCS12_ByteReverseWords(r, a, n) WC_DO_NOTHING #endif /* extended API that allows a heap hint to be used */ int wc_PKCS12_PBKDF_ex(byte* output, const byte* passwd, int passLen, const byte* salt, int saltLen, int iterations, int kLen, int hashType, int id, void* heap) { word32 u, v, pLen, iLen, sLen, totalLen; /* nwc: v / sizeof(PKCS12_WORD) - words per v-byte block * (v is always a multiple of sizeof(PKCS12_WORD)) * nBlocks: iLen / v - number of v-byte blocks in I */ word32 nwc, nBlocks; int ret = 0; word32 i, k, blk; byte* I; PKCS12_WORD* Bw; #ifdef WOLFSSL_SMALL_STACK byte staticBuffer[1]; /* force dynamic usage */ byte* B = NULL; #else ALIGN8 byte staticBuffer[1024]; ALIGN8 byte B[WC_MAX_BLOCK_SIZE]; #endif byte* buffer = staticBuffer; enum wc_HashType hashT; (void)heap; if ((output == NULL) || (passLen <= 0) || (saltLen <= 0) || (kLen < 0)) { return BAD_FUNC_ARG; } if (iterations <= 0) { iterations = 1; } if (iterations > current_wc_pbkdf_max_iterations) { WOLFSSL_MSG("PKCS12 PBKDF iteration count exceeds " "current_wc_pbkdf_max_iterations"); return BAD_FUNC_ARG; } /* u = hash output size. */ hashT = wc_HashTypeConvert(hashType); ret = wc_HashGetDigestSize(hashT); if (ret < 0) return ret; if (ret == 0) return BAD_STATE_E; u = (word32)ret; /* v = hash block size. */ ret = wc_HashGetBlockSize(hashT); if (ret < 0) return ret; if (ret == 0) return BAD_STATE_E; v = (word32)ret; /* RFC 7292 B.2 step 2: S = salt repeated to ceil(saltLen/v)*v bytes */ sLen = v * (((word32)saltLen + v - 1) / v); /* RFC 7292 B.2 step 3: P = password repeated to ceil(passLen/v)*v bytes */ pLen = v * (((word32)passLen + v - 1) / v); /* RFC 7292 B.2 step 4: I = S || P */ if (! WC_SAFE_SUM_UNSIGNED(word32, sLen, pLen, iLen)) { return BAD_FUNC_ARG; } if (! WC_SAFE_SUM_UNSIGNED(word32, v, iLen, totalLen)) { return BAD_FUNC_ARG; } nwc = v / (word32)sizeof(PKCS12_WORD); nBlocks = iLen / v; #ifdef WOLFSSL_SMALL_STACK B = (byte*)XMALLOC(WC_MAX_BLOCK_SIZE, heap, DYNAMIC_TYPE_TMP_BUFFER); if (B == NULL) return MEMORY_E; #endif Bw = (PKCS12_WORD*)B; if (totalLen > sizeof(staticBuffer)) { buffer = (byte*)XMALLOC(totalLen, heap, DYNAMIC_TYPE_KEY); if (buffer == NULL) { WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } } /* RFC 7292 B.2 step 1: D = v bytes each set to ID */ /* RFC 7292 B.2 step 4: I = S || P; buffer = D || I */ I = buffer + v; XMEMSET(buffer, id, v); for (i = 0; i < sLen; i++) I[i] = salt[i % (word32)saltLen]; for (i = 0; i < pLen; i++) I[sLen + i] = passwd[i % (word32)passLen]; ret = 0; while ((ret == 0) && (kLen > 0)) { /* RFC 7292 B.2 step 6a: A_i = H^r(D || I) */ ret = DoPKCS12Hash(hashT, buffer, totalLen, B, u, iterations); if (ret != 0) break; /* RFC 7292 B.2 step 7: output A_i bytes (up to kLen) */ i = min((word32)kLen, u); XMEMCPY(output, B, i); output += i; kLen -= (int)i; if (kLen == 0) break; /* RFC 7292 B.2 step 6b: B = A_i repeated to length v */ for (i = u; i < v; i++) B[i] = B[i % u]; /* RFC 7292 B.2 step 6c: I_j = (I_j + B + 1) mod 2^(8v). */ #ifndef BIG_ENDIAN_ORDER PKCS12_ByteReverseWords(Bw, Bw, v); #endif /* Increment B by 1. */ for (k = nwc; k > 0; ) { --k; ++Bw[k]; if (Bw[k] != 0) break; } #ifndef BIG_ENDIAN_ORDER PKCS12_ByteReverseWords((PKCS12_WORD*)I, (PKCS12_WORD*)I, nBlocks * v); #endif /* Add B+1 to each I_j block. */ for (blk = 0; blk < nBlocks; blk++) { PKCS12_DWORD c = 0; PKCS12_WORD* Iw = (PKCS12_WORD*)(I + blk * v); for (k = nwc; k-- > 0; ) { c += (PKCS12_DWORD)Iw[k]; c += (PKCS12_DWORD)Bw[k]; Iw[k] = (PKCS12_WORD)c; c >>= 8 * sizeof(PKCS12_WORD); } } #ifndef BIG_ENDIAN_ORDER PKCS12_ByteReverseWords((PKCS12_WORD*)I, (PKCS12_WORD*)I, nBlocks * v); #endif } WC_FREE_VAR_EX(B, heap, DYNAMIC_TYPE_TMP_BUFFER); if (buffer != staticBuffer) { XFREE(buffer, heap, DYNAMIC_TYPE_KEY); } return ret; } #undef PKCS12_DWORD #undef PKCS12_WORD #undef PKCS12_ByteReverseWords #endif #endif /* HAVE_PKCS12 */ #ifdef HAVE_SCRYPT #ifdef NO_HMAC #error scrypt requires HMAC #endif /* Rotate the 32-bit value a by b bits to the left. * * a 32-bit value. * b Number of bits to rotate. * returns rotated value. */ #define R(a, b) rotlFixed(a, b) /* (2^32 - 1) */ #define SCRYPT_WORD32_MAX 4294967295U /* One round of Salsa20/8. * Code taken from RFC 7914: scrypt PBKDF. * * out Output buffer. * in Input data to hash. */ static void scryptSalsa(word32* out, word32* in) { int i; word32 x[16]; #ifdef LITTLE_ENDIAN_ORDER XMEMCPY(x, in, sizeof(x)); #else for (i = 0; i < 16; i++) x[i] = ByteReverseWord32(in[i]); #endif for (i = 8; i > 0; i -= 2) { x[ 4] ^= R(x[ 0] + x[12], 7); x[ 8] ^= R(x[ 4] + x[ 0], 9); x[12] ^= R(x[ 8] + x[ 4], 13); x[ 0] ^= R(x[12] + x[ 8], 18); x[ 9] ^= R(x[ 5] + x[ 1], 7); x[13] ^= R(x[ 9] + x[ 5], 9); x[ 1] ^= R(x[13] + x[ 9], 13); x[ 5] ^= R(x[ 1] + x[13], 18); x[14] ^= R(x[10] + x[ 6], 7); x[ 2] ^= R(x[14] + x[10], 9); x[ 6] ^= R(x[ 2] + x[14], 13); x[10] ^= R(x[ 6] + x[ 2], 18); x[ 3] ^= R(x[15] + x[11], 7); x[ 7] ^= R(x[ 3] + x[15], 9); x[11] ^= R(x[ 7] + x[ 3], 13); x[15] ^= R(x[11] + x[ 7], 18); x[ 1] ^= R(x[ 0] + x[ 3], 7); x[ 2] ^= R(x[ 1] + x[ 0], 9); x[ 3] ^= R(x[ 2] + x[ 1], 13); x[ 0] ^= R(x[ 3] + x[ 2], 18); x[ 6] ^= R(x[ 5] + x[ 4], 7); x[ 7] ^= R(x[ 6] + x[ 5], 9); x[ 4] ^= R(x[ 7] + x[ 6], 13); x[ 5] ^= R(x[ 4] + x[ 7], 18); x[11] ^= R(x[10] + x[ 9], 7); x[ 8] ^= R(x[11] + x[10], 9); x[ 9] ^= R(x[ 8] + x[11], 13); x[10] ^= R(x[ 9] + x[ 8], 18); x[12] ^= R(x[15] + x[14], 7); x[13] ^= R(x[12] + x[15], 9); x[14] ^= R(x[13] + x[12], 13); x[15] ^= R(x[14] + x[13], 18); } #ifdef LITTLE_ENDIAN_ORDER for (i = 0; i < 16; ++i) out[i] = in[i] + x[i]; #else for (i = 0; i < 16; i++) out[i] = ByteReverseWord32(ByteReverseWord32(in[i]) + x[i]); #endif } /* Mix a block using Salsa20/8. * Based on RFC 7914: scrypt PBKDF. * * b Blocks to mix. * y Temporary storage. * r Size of the block. */ static void scryptBlockMix(byte* b, byte* y, int r) { #ifdef WORD64_AVAILABLE word64 x[8]; word64* b64 = (word64*)b; word64* y64 = (word64*)y; #else word32 x[16]; word32* b32 = (word32*)b; word32* y32 = (word32*)y; #endif int i; int j; /* Step 1. */ XMEMCPY(x, b + (2 * r - 1) * 64, sizeof(x)); /* Step 2. */ for (i = 0; i < 2 * r; i++) { #ifdef WORD64_AVAILABLE for (j = 0; j < 8; j++) x[j] ^= b64[i * 8 + j]; #else for (j = 0; j < 16; j++) x[j] ^= b32[i * 16 + j]; #endif scryptSalsa((word32*)x, (word32*)x); XMEMCPY(y + i * 64, x, sizeof(x)); } /* Step 3. */ for (i = 0; i < r; i++) { #ifdef WORD64_AVAILABLE for (j = 0; j < 8; j++) { b64[i * 8 + j] = y64[2 * i * 8 + j]; b64[(r + i) * 8 + j] = y64[(2 * i + 1) * 8 + j]; } #else for (j = 0; j < 16; j++) { b32[i * 16 + j] = y32[2 * i * 16 + j]; b32[(r + i) * 16 + j] = y32[(2 * i + 1) * 16 + j]; } #endif } } /* Random oracles mix. * Based on RFC 7914: scrypt PBKDF. * * x Data to mix. * v Temporary buffer. * y Temporary buffer for the block mix. * r Block size parameter. * n CPU/Memory cost parameter. */ static void scryptROMix(byte* x, byte* v, byte* y, int r, word32 n) { word32 i; word32 j; word32 k; word32 bSz = (word32)(128 * r); #ifdef WORD64_AVAILABLE word64* x64 = (word64*)x; word64* v64 = (word64*)v; #else word32* x32 = (word32*)x; word32* v32 = (word32*)v; #endif /* Step 1. X = B (B not needed therefore not implemented) */ /* Step 2. */ for (i = 0; i < n; i++) { XMEMCPY(v + i * bSz, x, bSz); scryptBlockMix(x, y, r); } /* Step 3. */ for (i = 0; i < n; i++) { #ifdef LITTLE_ENDIAN_ORDER #ifdef WORD64_AVAILABLE j = (word32)(*(word64*)(x + (2*r - 1) * 64) & (n-1)); #else j = *(word32*)(x + (2*r - 1) * 64) & (n-1); #endif #else byte* t = x + (2*r - 1) * 64; j = (t[0] | (t[1] << 8) | (t[2] << 16) | ((word32)t[3] << 24)) & (n-1); #endif #ifdef WORD64_AVAILABLE for (k = 0; k < bSz / 8; k++) x64[k] ^= v64[j * bSz / 8 + k]; #else for (k = 0; k < bSz / 4; k++) x32[k] ^= v32[j * bSz / 4 + k]; #endif scryptBlockMix(x, y, r); } /* Step 4. B' = X (B = X = B' so not needed, therefore not implemented) */ } /* Generates an key derived from a password and salt using a memory hard * algorithm. * Implements RFC 7914: scrypt PBKDF. * * output The derived key. * passwd The password to derive key from. * passLen The length of the password. * salt The key specific data. * saltLen The length of the salt data. * cost The CPU/memory cost parameter. Range: 1..(128*r/8-1) * (Iterations = 2^cost) * blockSize The number of 128 byte octets in a working block. * parallel The number of parallel mix operations to perform. * (Note: this implementation does not use threads.) * dkLen The length of the derived key in bytes. * returns BAD_FUNC_ARG when: blockSize is too large for cost. */ int wc_scrypt(byte* output, const byte* passwd, int passLen, const byte* salt, int saltLen, int cost, int blockSize, int parallel, int dkLen) { int ret = 0; int i; byte* v = NULL; byte* y = NULL; byte* blocks = NULL; word32 blocksSz; word32 bSz; if (blockSize > 8) return BAD_FUNC_ARG; if (cost < 1 || cost >= 128 * blockSize / 8 || parallel < 1 || dkLen < 1) return BAD_FUNC_ARG; /* The following comparison used to be: * ((word32)parallel > (SCRYPT_MAX / (128 * blockSize))) * where SCRYPT_MAX is (2^32 - 1) * 32. For some compilers, the RHS of * the comparison is greater than parallel's type. It wouldn't promote * both sides to word64. What follows is just arithmetic simplification. */ if (parallel > (int)((SCRYPT_WORD32_MAX / 4) / (word32)blockSize)) return BAD_FUNC_ARG; bSz = 128 * (word32)blockSize; if (parallel > (int)(SCRYPT_WORD32_MAX / bSz)) return BAD_FUNC_ARG; blocksSz = bSz * (word32)parallel; blocks = (byte*)XMALLOC((size_t)blocksSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (blocks == NULL) { ret = MEMORY_E; goto end; } /* Check that (1 << cost) * bSz won't overflow or exceed allowed max */ if (((size_t)1 << cost) * (size_t)bSz > SCRYPT_WORD32_MAX) { ret = BAD_FUNC_ARG; goto end; } /* Temporary for scryptROMix. */ v = (byte*)XMALLOC(((size_t)1 << cost) * (size_t)bSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (v == NULL) { ret = MEMORY_E; goto end; } /* Temporary for scryptBlockMix. */ y = (byte*)XMALLOC((size_t)(blockSize * 128), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (y == NULL) { ret = MEMORY_E; goto end; } XMEMSET(y, 0, (size_t)(blockSize * 128)); /* Step 1. */ ret = wc_PBKDF2(blocks, passwd, passLen, salt, saltLen, 1, (int)blocksSz, WC_SHA256); if (ret != 0) goto end; /* Step 2. */ for (i = 0; i < parallel; i++) scryptROMix(blocks + i * (int)bSz, v, y, (int)blockSize, (word32)((size_t)1 << cost)); /* Step 3. */ ret = wc_PBKDF2(output, passwd, passLen, blocks, (int)blocksSz, 1, dkLen, WC_SHA256); end: if (blocks != NULL) { ForceZero(blocks, blocksSz); } if (v != NULL) { ForceZero(v, ((size_t)1 << cost) * (size_t)bSz); } if (y != NULL) { ForceZero(y, (size_t)blockSize * 128); } XFREE(blocks, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(v, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(y, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } /* Generates an key derived from a password and salt using a memory hard * algorithm. * Implements RFC 7914: scrypt PBKDF. * * output Derived key. * passwd Password to derive key from. * passLen Length of the password. * salt Key specific data. * saltLen Length of the salt data. * iterations Number of iterations to perform. Range: 1 << (1..(128*r/8-1)) * blockSize Number of 128 byte octets in a working block. * parallel Number of parallel mix operations to perform. * (Note: this implementation does not use threads.) * dkLen Length of the derived key in bytes. * returns BAD_FUNC_ARG when: iterations is not a power of 2 or blockSize is too * large for iterations. */ int wc_scrypt_ex(byte* output, const byte* passwd, int passLen, const byte* salt, int saltLen, word32 iterations, int blockSize, int parallel, int dkLen) { int cost; /* Iterations must be a power of 2. */ if ((iterations & (iterations - 1)) != 0) return BAD_FUNC_ARG; for (cost = -1; iterations != 0; cost++) { iterations >>= 1; } return wc_scrypt(output, passwd, passLen, salt, saltLen, cost, blockSize, parallel, dkLen); } #endif /* HAVE_SCRYPT */ #endif /* NO_PWDBASED */