| /* gc-pbkdf2.c --- Password-Based Key Derivation Function a'la PKCS#5 | |
| Copyright (C) 2002-2006, 2009-2026 Free Software Foundation, Inc. | |
| This file is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This file 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 Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| /* Written by Simon Josefsson. */ | |
| #include <config.h> | |
| #include "gc.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| typedef Gc_rc (*gc_prf_func) (void const *restrict key, size_t keylen, | |
| void const *restrict in, size_t inlen, | |
| char *restrict resbuf); | |
| static Gc_rc | |
| gc_pbkdf2_prf (gc_prf_func prf, size_t hLen, | |
| char const *restrict P, size_t Plen, | |
| char const *restrict S, size_t Slen, | |
| unsigned int c, | |
| char *restrict DK, size_t dkLen) | |
| { | |
| if (c == 0) | |
| return GC_PKCS5_INVALID_ITERATION_COUNT; | |
| if (dkLen == 0) | |
| return GC_PKCS5_INVALID_DERIVED_KEY_LENGTH; | |
| if (dkLen > 4294967295U) | |
| return GC_PKCS5_DERIVED_KEY_TOO_LONG; | |
| unsigned int l = ((dkLen - 1) / hLen) + 1; | |
| unsigned int r = dkLen - (l - 1) * hLen; | |
| size_t tmplen = Slen + 4; | |
| char *tmp = malloc (tmplen); | |
| if (tmp == NULL) | |
| return GC_MALLOC_ERROR; | |
| memcpy (tmp, S, Slen); | |
| char U[GC_MAX_DIGEST_SIZE]; | |
| char T[GC_MAX_DIGEST_SIZE]; | |
| int rc; | |
| for (unsigned int i = 1; i <= l; i++) | |
| { | |
| memset (T, 0, hLen); | |
| for (unsigned int u = 1; u <= c; u++) | |
| { | |
| if (u == 1) | |
| { | |
| tmp[Slen + 0] = (i & 0xff000000) >> 24; | |
| tmp[Slen + 1] = (i & 0x00ff0000) >> 16; | |
| tmp[Slen + 2] = (i & 0x0000ff00) >> 8; | |
| tmp[Slen + 3] = (i & 0x000000ff) >> 0; | |
| rc = prf (P, Plen, tmp, tmplen, U); | |
| } | |
| else | |
| rc = prf (P, Plen, U, hLen, U); | |
| if (rc != GC_OK) | |
| { | |
| free (tmp); | |
| return rc; | |
| } | |
| for (unsigned int k = 0; k < hLen; k++) | |
| T[k] ^= U[k]; | |
| } | |
| memcpy (DK + (i - 1) * hLen, T, i == l ? r : hLen); | |
| } | |
| free (tmp); | |
| return GC_OK; | |
| } | |
| Gc_rc | |
| gc_pbkdf2_hmac (Gc_hash hash, | |
| char const *restrict P, size_t Plen, | |
| char const *restrict S, size_t Slen, | |
| unsigned int c, char *restrict DK, size_t dkLen) | |
| { | |
| gc_prf_func prf; | |
| size_t hLen; | |
| switch (hash) | |
| { | |
| #if GNULIB_GC_HMAC_SHA1 | |
| case GC_SHA1: | |
| prf = gc_hmac_sha1; | |
| hLen = GC_SHA1_DIGEST_SIZE; | |
| break; | |
| #endif | |
| #if GNULIB_GC_HMAC_SHA256 | |
| case GC_SHA256: | |
| prf = gc_hmac_sha256; | |
| hLen = GC_SHA256_DIGEST_SIZE; | |
| break; | |
| #endif | |
| #if GNULIB_GC_HMAC_SHA512 | |
| case GC_SHA512: | |
| prf = gc_hmac_sha512; | |
| hLen = GC_SHA512_DIGEST_SIZE; | |
| break; | |
| #endif | |
| default: | |
| return GC_INVALID_HASH; | |
| } | |
| return gc_pbkdf2_prf (prf, hLen, P, Plen, S, Slen, c, DK, dkLen); | |
| } |