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