tinc/src/openssl/prf.c
Guus Sliepen feb3f22fff Add PRF to derive key material from the ECDH shared secret.
It is modelled after the pseudorandom function from RFC4346 (TLS 1.1), the only
significant change is the use of SHA512 and Whirlpool instead of MD5 and SHA1.
2011-07-03 15:26:58 +02:00

76 lines
2.3 KiB
C

/*
prf.c -- Pseudo-Random Function for key material generation
Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>
This program 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 2 of the License, or
(at your option) any later version.
This program 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-1301 USA.
*/
#include "system.h"
#include "digest.h"
#include "prf.h"
/* Generate key material from a master secret and a seed, based on RFC 2246.
We use SHA512 and Whirlpool instead of MD5 and SHA1.
*/
static bool prf_xor(int nid, char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, ssize_t outlen) {
digest_t digest;
if(!digest_open_by_nid(&digest, nid, 0))
return false;
if(!digest_set_key(&digest, secret, secretlen))
return false;
size_t len = digest_length(&digest);
/* Data is what the "inner" HMAC function processes.
It consists of the previous HMAC result plus the seed.
*/
char data[len + seedlen];
memset(data, 0, len);
memcpy(data + len, seed, seedlen);
char hash[len];
while(outlen > 0) {
/* Inner HMAC */
digest_create(&digest, data, len + seedlen, data);
/* Outer HMAC */
digest_create(&digest, data, len + seedlen, hash);
/* XOR the results of the outer HMAC into the out buffer */
for(int i = 0; i < len && i < outlen; i++)
*out++ ^= hash[i];
outlen -= len;
}
digest_close(&digest);
return true;
}
bool prf(char *secret, size_t secretlen, char *seed, size_t seedlen, char *out, size_t outlen) {
/* Split secret in half, generate outlen bits with two different hash algorithms,
and XOR the results. */
memset(out, 0, outlen);
return prf_xor(NID_sha512, secret, secretlen / 2, seed, seedlen, out, outlen)
&& prf_xor(NID_whirlpool, secret, secretlen / 2, seed, seedlen, out, outlen);
}