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/aead.h>
10 #include <crypto/bytes.h>
11 #include <fbl/algorithm.h>
12 #include <fbl/auto_call.h>
13 #include <fbl/unique_ptr.h>
14 #include <lib/fdio/debug.h>
15 #include <zircon/errors.h>
16 #include <zircon/types.h>
17
18 // See note in //zircon/third_party/ulib/uboringssl/rules.mk
19 #define BORINGSSL_NO_CXX
20 #include <openssl/aead.h>
21
22 #include "error.h"
23
24 #define ZXDEBUG 0
25
26 namespace crypto {
27
28 // The previously opaque crypto implementation context. Guaranteed to clean up on destruction.
29 struct AEAD::Context {
Contextcrypto::AEAD::Context30 Context() {}
31
~Contextcrypto::AEAD::Context32 ~Context() { EVP_AEAD_CTX_cleanup(&impl); }
33
34 EVP_AEAD_CTX impl;
35 };
36
37 namespace {
38
39 // Get the aead for the given |version|.
GetAEAD(AEAD::Algorithm aead,const EVP_AEAD ** out)40 zx_status_t GetAEAD(AEAD::Algorithm aead, const EVP_AEAD** out) {
41 switch (aead) {
42 case AEAD::kUninitialized:
43 xprintf("not initialized\n");
44 return ZX_ERR_INVALID_ARGS;
45
46 case AEAD::kAES128_GCM:
47 *out = EVP_aead_aes_128_gcm();
48 return ZX_OK;
49
50 case AEAD::kAES128_GCM_SIV:
51 *out = EVP_aead_aes_128_gcm_siv();
52 return ZX_OK;
53
54 default:
55 xprintf("invalid aead = %u\n", aead);
56 return ZX_ERR_NOT_SUPPORTED;
57 }
58 }
59
60 } // namespace
61
62 // Public methods
63
GetKeyLen(Algorithm algo,size_t * out)64 zx_status_t AEAD::GetKeyLen(Algorithm algo, size_t* out) {
65 zx_status_t rc;
66
67 if (!out) {
68 xprintf("missing output pointer\n");
69 return ZX_ERR_INVALID_ARGS;
70 }
71 const EVP_AEAD* aead;
72 if ((rc = GetAEAD(algo, &aead)) != ZX_OK) {
73 return rc;
74 }
75 *out = EVP_AEAD_key_length(aead);
76
77 return ZX_OK;
78 }
79
GetIVLen(Algorithm algo,size_t * out)80 zx_status_t AEAD::GetIVLen(Algorithm algo, size_t* out) {
81 zx_status_t rc;
82
83 if (!out) {
84 xprintf("missing output pointer\n");
85 return ZX_ERR_INVALID_ARGS;
86 }
87 const EVP_AEAD* aead;
88 if ((rc = GetAEAD(algo, &aead)) != ZX_OK) {
89 return rc;
90 }
91 *out = EVP_AEAD_nonce_length(aead);
92
93 return ZX_OK;
94 }
95
GetTagLen(Algorithm algo,size_t * out)96 zx_status_t AEAD::GetTagLen(Algorithm algo, size_t* out) {
97 zx_status_t rc;
98
99 if (!out) {
100 xprintf("missing output pointer\n");
101 return ZX_ERR_INVALID_ARGS;
102 }
103 const EVP_AEAD* aead;
104 if ((rc = GetAEAD(algo, &aead)) != ZX_OK) {
105 return rc;
106 }
107 *out = EVP_AEAD_max_tag_len(aead);
108
109 return ZX_OK;
110 }
111
AEAD()112 AEAD::AEAD()
113 : ctx_(nullptr), direction_(Cipher::kUnset), tag_len_(0) {}
114
~AEAD()115 AEAD::~AEAD() {}
116
Seal(const Bytes & ptext,const uint8_t * aad,size_t aad_len,uint64_t * out_nonce,Bytes * out_ctext)117 zx_status_t AEAD::Seal(const Bytes& ptext, const uint8_t* aad, size_t aad_len, uint64_t* out_nonce,
118 Bytes* out_ctext) {
119
120 zx_status_t rc;
121
122 if (direction_ != Cipher::kEncrypt) {
123 xprintf("not configured to encrypt\n");
124 return ZX_ERR_BAD_STATE;
125 }
126
127 size_t ptext_len = ptext.len();
128 if (!out_nonce || !out_ctext) {
129 xprintf("bad parameter(s): out_nonce=%p, ctext=%p\n", out_nonce, out_ctext);
130 return ZX_ERR_INVALID_ARGS;
131 }
132
133 // If the caller recycles the |Bytes| used for|ctext|, this becomes a no-op.
134 size_t ctext_len = ptext_len + tag_len_;
135 if ((rc = out_ctext->Resize(ctext_len)) != ZX_OK) {
136 return rc;
137 }
138
139 uint8_t* iv8 = reinterpret_cast<uint8_t*>(iv_.get());
140 size_t out_len;
141 if (EVP_AEAD_CTX_seal(&ctx_->impl, out_ctext->get(), &out_len, ctext_len, iv8, iv_len_,
142 ptext.get(), ptext_len, aad, aad_len) != 1) {
143 xprintf_crypto_errors(&rc);
144 return rc;
145 }
146 if (out_len != ctext_len) {
147 xprintf("length mismatch: expected %zu, got %zu\n", ptext_len, out_len);
148 return ZX_ERR_INTERNAL;
149 }
150
151 // Increment nonce
152 uint64_t nonce = iv_[0];
153 iv_[0] += 1;
154 if (iv_[0] == iv0_) {
155 xprintf("exceeded maximum operations with this key\n");
156 return ZX_ERR_BAD_STATE;
157 }
158
159 *out_nonce = nonce;
160 return ZX_OK;
161 }
162
Open(uint64_t nonce,const Bytes & ctext,const uint8_t * aad,size_t aad_len,Bytes * out_ptext)163 zx_status_t AEAD::Open(uint64_t nonce, const Bytes& ctext, const uint8_t* aad, size_t aad_len, Bytes* out_ptext) {
164 zx_status_t rc;
165
166 if (direction_ != Cipher::kDecrypt) {
167 xprintf("not configured to decrypt\n");
168 return ZX_ERR_BAD_STATE;
169 }
170
171 size_t ctext_len = ctext.len();
172 if (ctext_len < tag_len_ || !out_ptext) {
173 xprintf("bad parameter(s): ctext.len=%zu, ptext=%p\n", ctext_len, out_ptext);
174 return ZX_ERR_INVALID_ARGS;
175 }
176
177 size_t ptext_len = ctext_len - tag_len_;
178 if ((rc = out_ptext->Resize(ptext_len)) != ZX_OK) {
179 return rc;
180 }
181
182 // Inject nonce
183 iv_[0] = nonce;
184 uint8_t* iv8 = reinterpret_cast<uint8_t*>(iv_.get());
185 size_t out_len;
186 if (EVP_AEAD_CTX_open(&ctx_->impl, out_ptext->get(), &out_len, ptext_len, iv8, iv_len_,
187 ctext.get(), ctext_len, aad, aad_len) != 1) {
188 xprintf_crypto_errors(&rc);
189 return rc;
190 }
191 if (out_len != ptext_len) {
192 xprintf("length mismatch: expected %zu, got %zu\n", ptext_len, out_len);
193 return ZX_ERR_INTERNAL;
194 }
195
196 return ZX_OK;
197 }
198
Reset()199 void AEAD::Reset() {
200 ctx_.reset();
201 direction_ = Cipher::kUnset;
202 iv_len_ = 0;
203 tag_len_ = 0;
204 }
205
206 // Private methods
207
Init(Algorithm algo,const Secret & key,const Bytes & iv,Cipher::Direction direction)208 zx_status_t AEAD::Init(Algorithm algo, const Secret& key, const Bytes& iv,
209 Cipher::Direction direction) {
210 zx_status_t rc;
211
212 Reset();
213 auto cleanup = fbl::MakeAutoCall([&] { Reset(); });
214
215 // Look up specific algorithm
216 const EVP_AEAD* aead;
217 if ((rc = GetAEAD(algo, &aead)) != ZX_OK) {
218 return rc;
219 }
220 size_t key_len = EVP_AEAD_key_length(aead);
221 iv_len_ = EVP_AEAD_nonce_length(aead);
222 tag_len_ = EVP_AEAD_max_tag_len(aead);
223
224 // Check parameters
225 if (key.len() != key_len) {
226 xprintf("wrong key length; have %zu, need %zu\n", key.len(), key_len);
227 return ZX_ERR_INVALID_ARGS;
228 }
229 if (iv.len() != iv_len_) {
230 xprintf("wrong IV length; have %zu, need %zu\n", iv.len(), iv_len_);
231 return ZX_ERR_INVALID_ARGS;
232 }
233
234 // Allocate context
235 fbl::AllocChecker ac;
236 ctx_.reset(new (&ac) Context());
237 if (!ac.check()) {
238 xprintf("allocation failed: %zu bytes\n", sizeof(Context));
239 return ZX_ERR_NO_MEMORY;
240 }
241
242 // Initialize context
243 if (EVP_AEAD_CTX_init(&ctx_->impl, aead, key.get(), key.len(), EVP_AEAD_DEFAULT_TAG_LENGTH,
244 nullptr) != 1) {
245 xprintf_crypto_errors(&rc);
246 return rc;
247 }
248 direction_ = direction;
249
250 // Reserve space for IV
251 size_t n = fbl::round_up(iv_len_, sizeof(uint64_t)) / sizeof(uint64_t);
252 iv_.reset(new (&ac) uint64_t[n]{0});
253 if (!ac.check()) {
254 xprintf("failed to allocate %zu bytes\n", n * sizeof(uint64_t));
255 return ZX_ERR_NO_MEMORY;
256 }
257 memcpy(iv_.get(), iv.get(), iv_len_);
258
259 cleanup.cancel();
260 return ZX_OK;
261 }
262
263 } // namespace crypto
264