blob: dcbe2c8470427630913c83fb6084106eaabc083c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <crypto/digest.h>
#include <crypto/error.h>
#include <crypto/hkdf.h>
#include <explicit-memory/bytes.h>
#include <fbl/auto_call.h>
#include <fdio/debug.h>
#include <openssl/digest.h>
#include <openssl/hkdf.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#define MXDEBUG 0
namespace crypto {
namespace {
const uint16_t kAllFlags = HKDF::ALLOW_WEAK_KEY;
}
// Public methods
HKDF::HKDF() : digest_(digest::kUninitialized) {}
HKDF::~HKDF() {}
zx_status_t HKDF::Init(digest::Algorithm digest, const Bytes& key, const Bytes& salt,
uint16_t flags) {
zx_status_t rc;
if ((flags & (~kAllFlags)) != 0) {
xprintf("%s: invalid flagsL %04x\n", __PRETTY_FUNCTION__, flags);
return ZX_ERR_INVALID_ARGS;
}
Bytes prk;
uintptr_t ptr;
if ((rc = prk.Resize(EVP_MAX_MD_SIZE)) != ZX_OK ||
(rc = digest::GetDigest(digest, &ptr)) != ZX_OK) {
return rc;
}
const EVP_MD* md = reinterpret_cast<const EVP_MD*>(ptr);
// Recommended minimum length for the key is the digest output length (RFC 2104, section 2).
if ((flags & ALLOW_WEAK_KEY) == 0 && key.len() < EVP_MD_size(md)) {
xprintf("%s: weak parameter(s): key_len=%zu", __PRETTY_FUNCTION__, key.len());
return ZX_ERR_INVALID_ARGS;
}
// Extract the PRK used to generate other keys.
size_t prk_len;
if (HKDF_extract(prk.get(), &prk_len, md, key.get(), key.len(), salt.get(), salt.len()) < 0) {
xprintf_crypto_errors(__PRETTY_FUNCTION__, &rc);
return rc;
}
digest_ = digest;
prk_.Reset();
return prk_.Copy(prk.get(), prk_len);
}
zx_status_t HKDF::Derive(const char* label, Bytes* out_key) {
zx_status_t rc;
uintptr_t ptr;
if ((rc = digest::GetDigest(digest_, &ptr)) != ZX_OK) {
return rc;
}
const EVP_MD* md = reinterpret_cast<const EVP_MD*>(ptr);
if (!out_key || out_key->len() == 0) {
xprintf("%s: bad parameter(s): out_key=%p\n", __PRETTY_FUNCTION__, out_key);
return ZX_ERR_INVALID_ARGS;
}
// Maybe the platform is weird...
static_assert(sizeof(uint8_t) == sizeof(char), "can't convert char to uint8_t");
const uint8_t* info = reinterpret_cast<const uint8_t*>(label);
size_t len = label ? strlen(label) : 0;
// Generate the key
if (HKDF_expand(out_key->get(), out_key->len(), md, prk_.get(), prk_.len(), info, len) < 0) {
xprintf_crypto_errors(__PRETTY_FUNCTION__, &rc);
return rc;
}
return ZX_OK;
}
} // namespace crypto