// 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 #include #include #include #include #include #include #include #include // See note in //zircon/third_party/ulib/uboringssl/rules.mk #define BORINGSSL_NO_CXX #include #include #include "error.h" #define ZXDEBUG 0 namespace crypto { namespace { const uint16_t kAllFlags = HMAC::ALLOW_TRUNCATION | HMAC::ALLOW_WEAK_KEY; } // The previously opaque crypto implementation context. Guaranteed to clean up on destruction. struct HMAC::Context { Context() { HMAC_CTX_init(&impl); } ~Context() { HMAC_CTX_cleanup(&impl); } HMAC_CTX impl; }; HMAC::HMAC() {} HMAC::~HMAC() {} zx_status_t HMAC::Create(digest::Algorithm digest, const Secret& key, const void* in, size_t in_len, Bytes* out, uint16_t flags) { zx_status_t rc; HMAC hmac; if ((rc = hmac.Init(digest, key, flags)) != ZX_OK || (rc = hmac.Update(in, in_len)) != ZX_OK || (rc = hmac.Final(out)) != ZX_OK) { return rc; } return ZX_OK; } zx_status_t HMAC::Verify(digest::Algorithm digest, const Secret& key, const void* in, size_t in_len, const Bytes& hmac, uint16_t flags) { zx_status_t rc; Bytes tmp; if ((rc = HMAC::Create(digest, key, in, in_len, &tmp, flags)) != ZX_OK) { return rc; } size_t hmac_len = hmac.len(); size_t tmp_len = tmp.len(); if (hmac_len != tmp_len) { // According to RFC 2104, section 5, the digest can be truncated to half its original size. // We enforce a more stringent minimum than the RFC of 128 bits if ((flags & ALLOW_TRUNCATION) == 0 || hmac_len < tmp_len / 2 || hmac_len < 16) { xprintf("digest to verify is too short: %zu\n", hmac_len); return ZX_ERR_INVALID_ARGS; } if ((rc = tmp.Resize(hmac.len())) != ZX_OK) { return rc; } } if (tmp != hmac) { xprintf("HMAC verification failed\n"); return ZX_ERR_IO_DATA_INTEGRITY; } return ZX_OK; } zx_status_t HMAC::Init(digest::Algorithm digest, const Secret& key, uint16_t flags) { zx_status_t rc; if ((flags & (~kAllFlags)) != 0) { xprintf("invalid flags: %04x\n", flags); return ZX_ERR_INVALID_ARGS; } fbl::AllocChecker ac; ctx_.reset(new (&ac) Context()); if (!ac.check()) { xprintf("allocation failed: %zu bytes\n", sizeof(Context)); return ZX_ERR_NO_MEMORY; } // Get the digest algorithm uintptr_t ptr; if ((rc = digest::GetDigest(digest, &ptr)) != ZX_OK) { return rc; } const EVP_MD* md = reinterpret_cast(ptr); // Check key length. Keys less than digest length are invalid (RFC 2104, section 2). size_t key_len = key.len(); if ((flags & ALLOW_WEAK_KEY) == 0 && key_len < EVP_MD_size(md)) { xprintf("weak key: %zu bytes\n", key_len); return ZX_ERR_INVALID_ARGS; } // Initialize the HMAC context if (HMAC_Init_ex(&ctx_->impl, key.get(), key_len, md, nullptr) != 1) { xprintf_crypto_errors(&rc); return rc; } return ZX_OK; } zx_status_t HMAC::Update(const void* in, size_t in_len) { zx_status_t rc; if (!ctx_) { xprintf("not initialized\n"); return ZX_ERR_BAD_STATE; } if (in_len == 0) { return ZX_OK; } if (!in) { xprintf("input is null\n"); return ZX_ERR_INVALID_ARGS; } if (HMAC_Update(&ctx_->impl, static_cast(in), in_len) != 1) { xprintf_crypto_errors(&rc); return rc; } return ZX_OK; } zx_status_t HMAC::Final(Bytes* out) { zx_status_t rc; if (!out) { xprintf("null parameter: out\n"); return ZX_ERR_INVALID_ARGS; } if (!ctx_) { xprintf("not initialized\n"); return ZX_ERR_BAD_STATE; } Bytes tmp; if ((rc = tmp.Resize(EVP_MAX_MD_SIZE)) != ZX_OK) { return rc; } unsigned int out_len; if (HMAC_Final(&ctx_->impl, tmp.get(), &out_len) != 1) { xprintf_crypto_errors(&rc); return rc; } if ((rc = out->Resize(out_len)) != ZX_OK || (rc = out->Copy(tmp.get(), out_len)) != ZX_OK) { return rc; } return ZX_OK; } } // namespace crypto