/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* lib/crypto/builtin/pbkdf2.c - Implementation of PBKDF2 from RFC 2898 */ /* * Copyright 2002, 2008 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include #include "crypto_int.h" /* * RFC 2898 specifies PBKDF2 in terms of an underlying pseudo-random * function with two arguments (password and salt||blockindex). Right * now we only use PBKDF2 with the hmac-sha1 PRF, also specified in * RFC 2898, which invokes HMAC with the password as the key and the * second argument as the text. (HMAC accepts any key size up to the * block size; the password is pre-hashed with unkeyed SHA1 if it is * longer than the block size.) * * For efficiency, it is better to generate the key from the password * once at the beginning, so we specify prf_fn in terms of a * krb5_key first argument. That might not be convenient for a PRF * which uses the password in some other way, so this might need to be * adjusted in the future. */ typedef krb5_error_code (*prf_fn)(krb5_key pass, krb5_data *salt, krb5_data *out); static int debug_hmac = 0; static void printd (const char *descr, krb5_data *d) { unsigned int i, j; const int r = 16; printf("%s:", descr); for (i = 0; i < d->length; i += r) { printf("\n %04x: ", i); for (j = i; j < i + r && j < d->length; j++) printf(" %02x", 0xff & d->data[j]); for (; j < i + r; j++) printf(" "); printf(" "); for (j = i; j < i + r && j < d->length; j++) { int c = 0xff & d->data[j]; printf("%c", isprint(c) ? c : '.'); } } printf("\n"); } /* * Implements the hmac-sha1 PRF. pass has been pre-hashed (if * necessary) and converted to a key already; salt has had the block * index appended to the original salt. * * NetBSD 8 declares an hmac() function in stdlib.h, so avoid that name. */ static krb5_error_code k5_hmac(const struct krb5_hash_provider *hash, krb5_keyblock *pass, krb5_data *salt, krb5_data *out) { krb5_error_code err; krb5_crypto_iov iov; if (debug_hmac) printd(" hmac input", salt); iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *salt; err = krb5int_hmac_keyblock(hash, pass, &iov, 1, out); if (err == 0 && debug_hmac) printd(" hmac output", out); return err; } static krb5_error_code F(char *output, char *u_tmp1, char *u_tmp2, const struct krb5_hash_provider *hash, size_t hlen, krb5_keyblock *pass, const krb5_data *salt, unsigned long count, int i) { unsigned char ibytes[4]; unsigned int j, k; krb5_data sdata; krb5_data out; krb5_error_code err; /* Compute U_1. */ store_32_be(i, ibytes); memcpy(u_tmp2, salt->data, salt->length); memcpy(u_tmp2 + salt->length, ibytes, 4); sdata = make_data(u_tmp2, salt->length + 4); out = make_data(u_tmp1, hlen); err = k5_hmac(hash, pass, &sdata, &out); if (err) return err; memcpy(output, u_tmp1, hlen); /* Compute U_2, .. U_c. */ sdata.length = hlen; for (j = 2; j <= count; j++) { memcpy(u_tmp2, u_tmp1, hlen); err = k5_hmac(hash, pass, &sdata, &out); if (err) return err; /* And xor them together. */ for (k = 0; k < hlen; k++) output[k] ^= u_tmp1[k]; } return 0; } static krb5_error_code pbkdf2(const struct krb5_hash_provider *hash, krb5_keyblock *pass, const krb5_data *salt, unsigned long count, const krb5_data *output) { size_t hlen = hash->hashsize; int l, i; char *utmp1, *utmp2; char utmp3[128]; /* XXX length shouldn't be hardcoded! */ if (output->length == 0 || hlen == 0) abort(); /* Step 1 & 2. */ if (output->length / hlen > 0xffffffff) abort(); /* Step 2. */ l = (output->length + hlen - 1) / hlen; utmp1 = /*output + dklen; */ malloc(hlen); if (utmp1 == NULL) return ENOMEM; utmp2 = /*utmp1 + hlen; */ malloc(salt->length + 4 + hlen); if (utmp2 == NULL) { free(utmp1); return ENOMEM; } /* Step 3. */ for (i = 1; i <= l; i++) { krb5_error_code err; char *out; if (i == l) out = utmp3; else out = output->data + (i-1) * hlen; err = F(out, utmp1, utmp2, hash, hlen, pass, salt, count, i); if (err) { free(utmp1); free(utmp2); return err; } if (i == l) memcpy(output->data + (i-1) * hlen, utmp3, output->length - (i-1) * hlen); } free(utmp1); free(utmp2); return 0; } krb5_error_code krb5int_pbkdf2_hmac(const struct krb5_hash_provider *hash, const krb5_data *out, unsigned long count, const krb5_data *pass, const krb5_data *salt) { krb5_keyblock keyblock; char tmp[128]; krb5_data d; krb5_crypto_iov iov; krb5_error_code err; assert(hash->hashsize <= sizeof(tmp)); if (pass->length > hash->blocksize) { d = make_data(tmp, hash->hashsize); iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *pass; err = hash->hash(&iov, 1, &d); if (err) return err; keyblock.length = d.length; keyblock.contents = (krb5_octet *) d.data; } else { keyblock.length = pass->length; keyblock.contents = (krb5_octet *) pass->data; } keyblock.enctype = ENCTYPE_NULL; err = pbkdf2(hash, &keyblock, salt, count, out); return err; }