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