1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <stddef.h> 6#include <stdint.h> 7 8#include <crypto/digest.h> 9#include <crypto/hmac.h> 10#include <explicit-memory/bytes.h> 11#include <fbl/alloc_checker.h> 12#include <lib/fdio/debug.h> 13#include <openssl/digest.h> 14#include <openssl/hmac.h> 15#include <zircon/errors.h> 16#include <zircon/types.h> 17 18#include "error.h" 19 20#define ZXDEBUG 0 21 22namespace crypto { 23namespace { 24 25const uint16_t kAllFlags = HMAC::ALLOW_TRUNCATION | HMAC::ALLOW_WEAK_KEY; 26} 27// The previously opaque crypto implementation context. Guaranteed to clean up on destruction. 28struct HMAC::Context { 29 Context() { HMAC_CTX_init(&impl); } 30 31 ~Context() { HMAC_CTX_cleanup(&impl); } 32 33 HMAC_CTX impl; 34}; 35 36HMAC::HMAC() {} 37HMAC::~HMAC() {} 38 39zx_status_t HMAC::Create(digest::Algorithm digest, const Secret& key, const void* in, size_t in_len, 40 Bytes* out, uint16_t flags) { 41 zx_status_t rc; 42 43 HMAC hmac; 44 if ((rc = hmac.Init(digest, key, flags)) != ZX_OK || (rc = hmac.Update(in, in_len)) != ZX_OK || 45 (rc = hmac.Final(out)) != ZX_OK) { 46 return rc; 47 } 48 49 return ZX_OK; 50} 51 52zx_status_t HMAC::Verify(digest::Algorithm digest, const Secret& key, const void* in, size_t in_len, 53 const Bytes& hmac, uint16_t flags) { 54 zx_status_t rc; 55 56 Bytes tmp; 57 if ((rc = HMAC::Create(digest, key, in, in_len, &tmp, flags)) != ZX_OK) { 58 return rc; 59 } 60 61 size_t hmac_len = hmac.len(); 62 size_t tmp_len = tmp.len(); 63 if (hmac_len != tmp_len) { 64 // According to RFC 2104, section 5, the digest can be truncated to half its original size. 65 // We enforce a more stringent minimum than the RFC of 128 bits 66 if ((flags & ALLOW_TRUNCATION) == 0 || hmac_len < tmp_len / 2 || hmac_len < 16) { 67 xprintf("digest to verify is too short: %zu\n", hmac_len); 68 return ZX_ERR_INVALID_ARGS; 69 } 70 if ((rc = tmp.Resize(hmac.len())) != ZX_OK) { 71 return rc; 72 } 73 } 74 75 if (tmp != hmac) { 76 xprintf("HMAC verification failed\n"); 77 return ZX_ERR_IO_DATA_INTEGRITY; 78 } 79 80 return ZX_OK; 81} 82 83zx_status_t HMAC::Init(digest::Algorithm digest, const Secret& key, uint16_t flags) { 84 zx_status_t rc; 85 86 if ((flags & (~kAllFlags)) != 0) { 87 xprintf("invalid flags: %04x\n", flags); 88 return ZX_ERR_INVALID_ARGS; 89 } 90 91 fbl::AllocChecker ac; 92 ctx_.reset(new (&ac) Context()); 93 if (!ac.check()) { 94 xprintf("allocation failed: %zu bytes\n", sizeof(Context)); 95 return ZX_ERR_NO_MEMORY; 96 } 97 98 // Get the digest algorithm 99 uintptr_t ptr; 100 if ((rc = digest::GetDigest(digest, &ptr)) != ZX_OK) { 101 return rc; 102 } 103 const EVP_MD* md = reinterpret_cast<const EVP_MD*>(ptr); 104 105 // Check key length. Keys less than digest length are invalid (RFC 2104, section 2). 106 size_t key_len = key.len(); 107 if ((flags & ALLOW_WEAK_KEY) == 0 && key_len < EVP_MD_size(md)) { 108 xprintf("weak key: %zu bytes\n", key_len); 109 return ZX_ERR_INVALID_ARGS; 110 } 111 112 // Initialize the HMAC context 113 if (HMAC_Init_ex(&ctx_->impl, key.get(), key_len, md, nullptr) != 1) { 114 xprintf_crypto_errors(&rc); 115 return rc; 116 } 117 118 return ZX_OK; 119} 120 121zx_status_t HMAC::Update(const void* in, size_t in_len) { 122 zx_status_t rc; 123 124 if (!ctx_) { 125 xprintf("not initialized\n"); 126 return ZX_ERR_BAD_STATE; 127 } 128 129 if (in_len == 0) { 130 return ZX_OK; 131 } 132 if (!in) { 133 xprintf("input is null\n"); 134 return ZX_ERR_INVALID_ARGS; 135 } 136 137 if (HMAC_Update(&ctx_->impl, static_cast<const uint8_t*>(in), in_len) != 1) { 138 xprintf_crypto_errors(&rc); 139 return rc; 140 } 141 142 return ZX_OK; 143} 144 145zx_status_t HMAC::Final(Bytes* out) { 146 zx_status_t rc; 147 148 if (!out) { 149 xprintf("null parameter: out\n"); 150 return ZX_ERR_INVALID_ARGS; 151 } 152 153 if (!ctx_) { 154 xprintf("not initialized\n"); 155 return ZX_ERR_BAD_STATE; 156 } 157 158 Bytes tmp; 159 if ((rc = tmp.Resize(EVP_MAX_MD_SIZE)) != ZX_OK) { 160 return rc; 161 } 162 163 unsigned int out_len; 164 if (HMAC_Final(&ctx_->impl, tmp.get(), &out_len) != 1) { 165 xprintf_crypto_errors(&rc); 166 return rc; 167 } 168 if ((rc = out->Resize(out_len)) != ZX_OK || 169 (rc = out->Copy(tmp.get(), out_len)) != ZX_OK) { 170 return rc; 171 } 172 173 return ZX_OK; 174} 175 176} // namespace crypto 177