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