/*
* lib
/crypto/pbkdf2
.c
*
* Copyright 2002 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.
*
*
* Implementation of PBKDF2 from RFC 2898.
* Not currently used; likely to be used when we get around to AES support.
*/
/* Not exported,
for
now. */
static krb5_error_code
krb5int_pbkdf2 (krb5_error_code (*prf)(krb5_keyblock *, krb5_data *,
krb5_data *),
size_t hlen, const krb5_data *pass, const krb5_data *salt,
unsigned long count, const krb5_data *output);
static int debug_hmac = 0;
static void printd (const char *descr, krb5_data *d) {
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"
);
}
static void printk(const char *descr, krb5_keyblock *k) {
krb5_data d;
d.data = (char *) k->contents;
d.length = k->length;
printd(descr, &d);
}
static krb5_error_code
F(char *output, char *u_tmp1, char *u_tmp2,
krb5_error_code (*prf)(krb5_keyblock *, krb5_data *, krb5_data *),
size_t hlen,
const krb5_data *pass, const krb5_data *salt,
unsigned long count, int i)
{
unsigned char ibytes[4];
size_t tlen;
int j, k;
krb5_keyblock pdata;
krb5_data sdata;
krb5_data out;
krb5_error_code err;
pdata.contents = pass->data;
pdata.length = pass->length;
printf
(
"F(i=%d, count=%lu, pass=%d:%s)\n"
, i, count,
pass->length, pass->data);
printk(
"F password"
, &pdata);
/* Compute U_1. */
ibytes[3] = i & 0xff;
ibytes[2] = (i >> 8) & 0xff;
ibytes[1] = (i >> 16) & 0xff;
ibytes[0] = (i >> 24) & 0xff;
tlen = salt->length;
memcpy(u_tmp2, salt->data, tlen);
memcpy(u_tmp2 + tlen, ibytes, 4);
tlen += 4;
sdata.data = u_tmp2;
sdata.length = tlen;
printd(
"initial salt"
, &sdata);
out.data = u_tmp1;
out.length = hlen;
printf
(
"F: computing hmac #1 (U_1) with %s\n"
, pdata.contents);
err = (*prf)(&pdata, &sdata, &out);
if
(err)
return
err;
printd(
"F: prf return value"
, &out);
memcpy(output, u_tmp1, hlen);
/* Compute U_2, .. U_c. */
sdata.length = hlen;
for
(j = 2; j <= count; j++) {
printf
(
"F: computing hmac #%d (U_%d)\n"
, j, j);
memcpy(u_tmp2, u_tmp1, hlen);
err = (*prf)(&pdata, &sdata, &out);
if
(err)
return
err;
printd(
"F: prf return value"
, &out);
/* And xor them together. */
for
(k = 0; k < hlen; k++)
output[k] ^= u_tmp1[k];
printf
(
"F: xor result:\n"
);
for
(k = 0; k < hlen; k++)
printf
(
" %02x"
, 0xff & output[k]);
printf
(
"\n"
);
}
return
0;
}
static krb5_error_code
krb5int_pbkdf2 (krb5_error_code (*prf)(krb5_keyblock *, krb5_data *,
krb5_data *),
size_t hlen,
const krb5_data *pass, const krb5_data *salt,
unsigned long count, const krb5_data *output)
{
int l, r, i;
char *utmp1, *utmp2;
char utmp3[20]; /* 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;
r = output->length - (l - 1) * hlen;
utmp1 = /*output + dklen; */ malloc(hlen);
if
(utmp1 == NULL)
return
errno;
utmp2 = /*utmp1 + hlen; */ malloc(salt->length + 4 + hlen);
if
(utmp2 == NULL) {
free
(utmp1);
return
errno;
}
/* Step 3. */
for
(i = 1; i <= l; i++) {
int j;
krb5_error_code err;
char *out;
if
(i == l)
out = utmp3;
else
out = output->data + (i-1) * hlen;
err = F(out, utmp1, utmp2, prf, 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);
printf
(
"after F(%d), @%p:\n"
, i, output->data);
for
(j = (i-1) * hlen; j < i * hlen; j++)
printf
(
" %02x"
, 0xff & output->data[j]);
printf
(
"\n"
);
}
free
(utmp1);
free
(utmp2);
return
0;
}
static krb5_error_code hmac1(const struct krb5_hash_provider *h,
krb5_keyblock *key, krb5_data *
in
, krb5_data *out)
{
char tmp[40];
size_t blocksize, hashsize;
krb5_error_code err;
krb5_keyblock k;
k = *key;
key = &k;
if
(debug_hmac)
printk(
" test key"
, key);
blocksize = h->blocksize;
hashsize = h->hashsize;
if
(hashsize > sizeof(tmp))
abort();
if
(key->length > blocksize) {
krb5_data d, d2;
d.data = (char *) key->contents;
d.length = key->length;
d2.data = tmp;
d2.length = hashsize;
err = h->
hash
(1, &d, &d2);
if
(err)
return
err;
key->length = d2.length;
key->contents = (krb5_octet *) d2.data;
if
(debug_hmac)
printk(
" pre-hashed key"
, key);
}
if
(debug_hmac)
printd(
" hmac input"
,
in
);
err = krb5_hmac(h, key, 1,
in
, out);
if
(err == 0 && debug_hmac)
printd(
" hmac output"
, out);
return
err;
}
static krb5_error_code
foo(krb5_keyblock *pass, krb5_data *salt, krb5_data *out)
{
krb5_error_code err;
memset(out->data, 0, out->length);
err = hmac1 (&krb5int_hash_sha1, pass, salt, out);
return
err;
}
krb5_error_code
krb5int_pbkdf2_hmac_sha1 (const krb5_data *out, unsigned long count,
const krb5_data *pass, const krb5_data *salt)
{
return
krb5int_pbkdf2 (foo, 20, pass, salt, count, out);
}