#include "lib/crypt_ops/crypto_hkdf.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/crypt_ops/crypto_digest.h"
#include "lib/crypt_ops/crypto_openssl_mgt.h"
#include "lib/intmath/cmp.h"
#include "lib/log/util_bug.h"
#ifdef ENABLE_OPENSSL
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#if defined(HAVE_ERR_LOAD_KDF_STRINGS)
#include <openssl/kdf.h>
#define HAVE_OPENSSL_HKDF 1
#endif
#endif
#include <string.h>
int
crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
uint8_t *key_out, size_t key_out_len)
{
int i, r = -1;
uint8_t *cp, *tmp = tor_malloc(key_in_len+1);
uint8_t digest[DIGEST_LEN];
tor_assert(key_out_len <= DIGEST_LEN*256);
memcpy(tmp, key_in, key_in_len);
for (cp = key_out, i=0; cp < key_out+key_out_len;
++i, cp += DIGEST_LEN) {
tmp[key_in_len] = i;
if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1) < 0)
goto exit;
memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out)));
}
r = 0;
exit:
memwipe(tmp, 0, key_in_len+1);
tor_free(tmp);
memwipe(digest, 0, sizeof(digest));
return r;
}
#ifdef HAVE_OPENSSL_HKDF
static int
crypto_expand_key_material_rfc5869_sha256_openssl(
const uint8_t *key_in, size_t key_in_len,
const uint8_t *salt_in, size_t salt_in_len,
const uint8_t *info_in, size_t info_in_len,
uint8_t *key_out, size_t key_out_len)
{
int r;
EVP_PKEY_CTX *evp_pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
tor_assert(evp_pkey_ctx);
tor_assert(key_in_len != 0);
tor_assert(salt_in_len <= 1024);
r = EVP_PKEY_derive_init(evp_pkey_ctx);
tor_assert(r == 1);
r = EVP_PKEY_CTX_set_hkdf_md(evp_pkey_ctx, EVP_sha256());
tor_assert(r == 1);
r = EVP_PKEY_CTX_set1_hkdf_salt(evp_pkey_ctx, salt_in, (int)salt_in_len);
tor_assert(r == 1);
r = EVP_PKEY_CTX_set1_hkdf_key(evp_pkey_ctx, key_in, (int)key_in_len);
tor_assert(r == 1);
r = EVP_PKEY_CTX_add1_hkdf_info(evp_pkey_ctx, info_in, (int)info_in_len);
tor_assert(r == 1);
r = EVP_PKEY_derive(evp_pkey_ctx, key_out, &key_out_len);
tor_assert(r == 1);
EVP_PKEY_CTX_free(evp_pkey_ctx);
return 0;
}
#else
static int
crypto_expand_key_material_rfc5869_sha256_legacy(
const uint8_t *key_in, size_t key_in_len,
const uint8_t *salt_in, size_t salt_in_len,
const uint8_t *info_in, size_t info_in_len,
uint8_t *key_out, size_t key_out_len)
{
uint8_t prk[DIGEST256_LEN];
uint8_t tmp[DIGEST256_LEN + 128 + 1];
uint8_t mac[DIGEST256_LEN];
int i;
uint8_t *outp;
size_t tmp_len;
crypto_hmac_sha256((char*)prk,
(const char*)salt_in, salt_in_len,
(const char*)key_in, key_in_len);
tor_assert(key_out_len <= DIGEST256_LEN * 256);
tor_assert(info_in_len <= 128);
memset(tmp, 0, sizeof(tmp));
outp = key_out;
i = 1;
while (key_out_len) {
size_t n;
if (i > 1) {
memcpy(tmp, mac, DIGEST256_LEN);
memcpy(tmp+DIGEST256_LEN, info_in, info_in_len);
tmp[DIGEST256_LEN+info_in_len] = i;
tmp_len = DIGEST256_LEN + info_in_len + 1;
} else {
memcpy(tmp, info_in, info_in_len);
tmp[info_in_len] = i;
tmp_len = info_in_len + 1;
}
crypto_hmac_sha256((char*)mac,
(const char*)prk, DIGEST256_LEN,
(const char*)tmp, tmp_len);
n = key_out_len < DIGEST256_LEN ? key_out_len : DIGEST256_LEN;
memcpy(outp, mac, n);
key_out_len -= n;
outp += n;
++i;
}
memwipe(tmp, 0, sizeof(tmp));
memwipe(mac, 0, sizeof(mac));
return 0;
}
#endif
int
crypto_expand_key_material_rfc5869_sha256(
const uint8_t *key_in, size_t key_in_len,
const uint8_t *salt_in, size_t salt_in_len,
const uint8_t *info_in, size_t info_in_len,
uint8_t *key_out, size_t key_out_len)
{
tor_assert(key_in);
tor_assert(key_in_len > 0);
#ifdef HAVE_OPENSSL_HKDF
return crypto_expand_key_material_rfc5869_sha256_openssl(key_in,
key_in_len, salt_in,
salt_in_len, info_in,
info_in_len,
key_out, key_out_len);
#else
return crypto_expand_key_material_rfc5869_sha256_legacy(key_in,
key_in_len, salt_in,
salt_in_len, info_in,
info_in_len,
key_out, key_out_len);
#endif
}