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