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