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 #include <string.h>
8 
9 #include <crypto/digest.h>
10 #include <crypto/hkdf.h>
11 #include <explicit-memory/bytes.h>
12 #include <fbl/auto_call.h>
13 #include <lib/fdio/debug.h>
14 #include <zircon/errors.h>
15 #include <zircon/types.h>
16 
17 // See note in //zircon/third_party/ulib/uboringssl/rules.mk
18 #define BORINGSSL_NO_CXX
19 #include <openssl/digest.h>
20 #include <openssl/hkdf.h>
21 
22 #include "error.h"
23 
24 #define ZXDEBUG 0
25 
26 namespace crypto {
27 namespace {
28 
29 const uint16_t kAllFlags = HKDF::ALLOW_WEAK_KEY;
30 }
31 // Public methods
32 
HKDF()33 HKDF::HKDF() : digest_(digest::kUninitialized) {}
~HKDF()34 HKDF::~HKDF() {}
35 
Init(digest::Algorithm digest,const Secret & key,const Bytes & salt,uint16_t flags)36 zx_status_t HKDF::Init(digest::Algorithm digest, const Secret& key, const Bytes& salt,
37                        uint16_t flags) {
38     zx_status_t rc;
39 
40     if ((flags & (~kAllFlags)) != 0) {
41         xprintf("invalid flags: %04x\n", flags);
42         return ZX_ERR_INVALID_ARGS;
43     }
44 
45     uintptr_t ptr;
46     if ((rc = digest::GetDigest(digest, &ptr)) != ZX_OK) {
47         return rc;
48     }
49     digest_ = digest;
50     const EVP_MD* md = reinterpret_cast<const EVP_MD*>(ptr);
51 
52     // Reserve space for the pseudo-random key.
53     uint8_t *prk;
54     size_t prk_len = EVP_MD_size(md);
55     if ((rc = prk_.Allocate(prk_len, &prk)) != ZX_OK) {
56         return rc;
57     }
58 
59     // Recommended minimum length for the key is the digest output length (RFC 2104, section 2).
60     if ((flags & ALLOW_WEAK_KEY) == 0 && key.len() < prk_len) {
61         xprintf("weak parameter(s): key_len=%zu", key.len());
62         return ZX_ERR_INVALID_ARGS;
63     }
64 
65     // Extract the PRK used to generate other keys.
66     if (HKDF_extract(prk, &prk_len, md, key.get(), key.len(), salt.get(), salt.len()) < 0) {
67         xprintf_crypto_errors(&rc);
68         return rc;
69     }
70     ZX_DEBUG_ASSERT(prk_len == prk_.len());
71 
72     return ZX_OK;
73 }
74 
Derive(const char * label,size_t len,Bytes * out)75 zx_status_t HKDF::Derive(const char* label, size_t len, Bytes* out) {
76     zx_status_t rc;
77 
78     if ((rc = out->Resize(len)) != ZX_OK ||
79         (rc = Derive(label, out->get(), len)) != ZX_OK) {
80         return rc;
81     }
82 
83     return ZX_OK;
84 }
85 
Derive(const char * label,size_t len,Secret * out)86 zx_status_t HKDF::Derive(const char* label, size_t len, Secret* out) {
87     zx_status_t rc;
88 
89     uint8_t *buf;
90     if ((rc = out->Allocate(len, &buf)) != ZX_OK ||
91         (rc = Derive(label, buf, len)) != ZX_OK) {
92         return rc;
93     }
94 
95     return ZX_OK;
96 }
97 
Derive(const char * label,uint8_t * out,size_t out_len)98 zx_status_t HKDF::Derive(const char* label, uint8_t* out, size_t out_len) {
99     zx_status_t rc;
100 
101     if (!out || out_len == 0) {
102         xprintf("bad parameter(s): out=%p, out_len=%zu\n", out, out_len);
103         return ZX_ERR_INVALID_ARGS;
104     }
105 
106     uintptr_t ptr;
107     if ((rc = digest::GetDigest(digest_, &ptr)) != ZX_OK) {
108         return rc;
109     }
110     const EVP_MD* md = reinterpret_cast<const EVP_MD*>(ptr);
111 
112     // Maybe the platform is weird...
113     static_assert(sizeof(uint8_t) == sizeof(char), "can't convert char to uint8_t");
114     const uint8_t* info = reinterpret_cast<const uint8_t*>(label);
115     size_t info_len = label ? strlen(label) : 0;
116 
117     // Generate the key
118     if (HKDF_expand(out, out_len, md, prk_.get(), prk_.len(), info, info_len) < 0) {
119         xprintf_crypto_errors(&rc);
120         return rc;
121     }
122 
123     return ZX_OK;
124 }
125 
126 } // namespace crypto
127