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