1/* Copyright (c) 2014, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15#include <openssl/aead.h> 16 17#include <openssl/chacha.h> 18#include <openssl/cipher.h> 19#include <openssl/err.h> 20#include <openssl/mem.h> 21#include <openssl/poly1305.h> 22 23#include "internal.h" 24 25 26#define POLY1305_TAG_LEN 16 27#define CHACHA20_NONCE_LEN 8 28 29struct aead_chacha20_poly1305_ctx { 30 unsigned char key[32]; 31 unsigned char tag_len; 32}; 33 34static int aead_chacha20_poly1305_init(EVP_AEAD_CTX *ctx, const uint8_t *key, 35 size_t key_len, size_t tag_len) { 36 struct aead_chacha20_poly1305_ctx *c20_ctx; 37 38 if (tag_len == 0) { 39 tag_len = POLY1305_TAG_LEN; 40 } 41 42 if (tag_len > POLY1305_TAG_LEN) { 43 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_init, CIPHER_R_TOO_LARGE); 44 return 0; 45 } 46 47 if (key_len != sizeof(c20_ctx->key)) { 48 return 0; /* internal error - EVP_AEAD_CTX_init should catch this. */ 49 } 50 51 c20_ctx = OPENSSL_malloc(sizeof(struct aead_chacha20_poly1305_ctx)); 52 if (c20_ctx == NULL) { 53 return 0; 54 } 55 56 memcpy(c20_ctx->key, key, key_len); 57 c20_ctx->tag_len = tag_len; 58 ctx->aead_state = c20_ctx; 59 60 return 1; 61} 62 63static void aead_chacha20_poly1305_cleanup(EVP_AEAD_CTX *ctx) { 64 struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; 65 OPENSSL_cleanse(c20_ctx->key, sizeof(c20_ctx->key)); 66 OPENSSL_free(c20_ctx); 67} 68 69static void poly1305_update_with_length(poly1305_state *poly1305, 70 const uint8_t *data, size_t data_len) { 71 size_t j = data_len; 72 uint8_t length_bytes[8]; 73 unsigned i; 74 75 for (i = 0; i < sizeof(length_bytes); i++) { 76 length_bytes[i] = j; 77 j >>= 8; 78 } 79 80 CRYPTO_poly1305_update(poly1305, data, data_len); 81 CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes)); 82} 83 84#if __arm__ 85#define ALIGNED __attribute__((aligned(16))) 86#else 87#define ALIGNED 88#endif 89 90static int aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, 91 size_t *out_len, size_t max_out_len, 92 const uint8_t *nonce, size_t nonce_len, 93 const uint8_t *in, size_t in_len, 94 const uint8_t *ad, size_t ad_len) { 95 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; 96 uint8_t poly1305_key[32] ALIGNED; 97 poly1305_state poly1305; 98 const uint64_t in_len_64 = in_len; 99 100 /* The underlying ChaCha implementation may not overflow the block 101 * counter into the second counter word. Therefore we disallow 102 * individual operations that work on more than 256GB at a time. 103 * |in_len_64| is needed because, on 32-bit platforms, size_t is only 104 * 32-bits and this produces a warning because it's always false. 105 * Casting to uint64_t inside the conditional is not sufficient to stop 106 * the warning. */ 107 if (in_len_64 >= (1ull << 32) * 64 - 64) { 108 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, CIPHER_R_TOO_LARGE); 109 return 0; 110 } 111 112 if (in_len + c20_ctx->tag_len < in_len) { 113 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, CIPHER_R_TOO_LARGE); 114 return 0; 115 } 116 117 if (max_out_len < in_len + c20_ctx->tag_len) { 118 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, 119 CIPHER_R_BUFFER_TOO_SMALL); 120 return 0; 121 } 122 123 if (nonce_len != CHACHA20_NONCE_LEN) { 124 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_seal, CIPHER_R_IV_TOO_LARGE); 125 return 0; 126 } 127 128 memset(poly1305_key, 0, sizeof(poly1305_key)); 129 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), 130 c20_ctx->key, nonce, 0); 131 132 CRYPTO_poly1305_init(&poly1305, poly1305_key); 133 poly1305_update_with_length(&poly1305, ad, ad_len); 134 CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1); 135 poly1305_update_with_length(&poly1305, out, in_len); 136 137 if (c20_ctx->tag_len != POLY1305_TAG_LEN) { 138 uint8_t tag[POLY1305_TAG_LEN]; 139 CRYPTO_poly1305_finish(&poly1305, tag); 140 memcpy(out + in_len, tag, c20_ctx->tag_len); 141 *out_len = in_len + c20_ctx->tag_len; 142 return 1; 143 } 144 145 CRYPTO_poly1305_finish(&poly1305, out + in_len); 146 *out_len = in_len + c20_ctx->tag_len; 147 return 1; 148} 149 150static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out, 151 size_t *out_len, size_t max_out_len, 152 const uint8_t *nonce, size_t nonce_len, 153 const uint8_t *in, size_t in_len, 154 const uint8_t *ad, size_t ad_len) { 155 const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; 156 uint8_t mac[POLY1305_TAG_LEN]; 157 uint8_t poly1305_key[32] ALIGNED; 158 size_t plaintext_len; 159 poly1305_state poly1305; 160 const uint64_t in_len_64 = in_len; 161 162 if (in_len < c20_ctx->tag_len) { 163 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BAD_DECRYPT); 164 return 0; 165 } 166 167 /* The underlying ChaCha implementation may not overflow the block 168 * counter into the second counter word. Therefore we disallow 169 * individual operations that work on more than 256GB at a time. 170 * |in_len_64| is needed because, on 32-bit platforms, size_t is only 171 * 32-bits and this produces a warning because it's always false. 172 * Casting to uint64_t inside the conditional is not sufficient to stop 173 * the warning. */ 174 if (in_len_64 >= (1ull << 32) * 64 - 64) { 175 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_TOO_LARGE); 176 return 0; 177 } 178 179 if (nonce_len != CHACHA20_NONCE_LEN) { 180 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_IV_TOO_LARGE); 181 return 0; 182 } 183 184 plaintext_len = in_len - c20_ctx->tag_len; 185 186 if (max_out_len < plaintext_len) { 187 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, 188 CIPHER_R_BUFFER_TOO_SMALL); 189 return 0; 190 } 191 192 memset(poly1305_key, 0, sizeof(poly1305_key)); 193 CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), 194 c20_ctx->key, nonce, 0); 195 196 CRYPTO_poly1305_init(&poly1305, poly1305_key); 197 poly1305_update_with_length(&poly1305, ad, ad_len); 198 poly1305_update_with_length(&poly1305, in, plaintext_len); 199 CRYPTO_poly1305_finish(&poly1305, mac); 200 201 if (CRYPTO_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) { 202 OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BAD_DECRYPT); 203 return 0; 204 } 205 206 CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1); 207 *out_len = plaintext_len; 208 return 1; 209} 210 211static const EVP_AEAD aead_chacha20_poly1305 = { 212 32, /* key len */ 213 CHACHA20_NONCE_LEN, /* nonce len */ 214 POLY1305_TAG_LEN, /* overhead */ 215 POLY1305_TAG_LEN, /* max tag length */ 216 aead_chacha20_poly1305_init, aead_chacha20_poly1305_cleanup, 217 aead_chacha20_poly1305_seal, aead_chacha20_poly1305_open, 218}; 219 220const EVP_AEAD *EVP_aead_chacha20_poly1305(void) { 221 return &aead_chacha20_poly1305; 222} 223