static int aead_chacha20_poly1305_open_gather( const EVP_AEAD_CTX *ctx, uint8_t *out, const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *in_tag, size_t in_tag_len, const uint8_t *ad, size_t ad_len) { const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; if (nonce_len != 12) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); return 0; } if (in_tag_len != ctx->tag_len) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); return 0; } // |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow // individual operations that work on more than 256GB at a time. // |in_len_64| is needed because, on 32-bit platforms, size_t is only // 32-bits and this produces a warning because it's always false. // Casting to uint64_t inside the conditional is not sufficient to stop // the warning. const uint64_t in_len_64 = in_len; if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); return 0; } union open_data data; if (asm_capable()) { OPENSSL_memcpy(data.in.key, c20_ctx->key, 32); data.in.counter = 0; OPENSSL_memcpy(data.in.nonce, nonce, 12); chacha20_poly1305_open(out, in, in_len, ad, ad_len, &data); } else { calc_tag(data.out.tag, c20_ctx, nonce, ad, ad_len, in, in_len, NULL, 0); CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1); } if (CRYPTO_memcmp(data.out.tag, in_tag, ctx->tag_len) != 0) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); return 0; } return 1; }
static int chacha20_poly1305_seal_scatter( const uint8_t *key, uint8_t *out, uint8_t *out_tag, size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in, size_t extra_in_len, const uint8_t *ad, size_t ad_len, size_t tag_len) { if (extra_in_len + tag_len < tag_len) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); return 0; } if (max_out_tag_len < tag_len + extra_in_len) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); return 0; } if (nonce_len != 12) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); return 0; } // |CRYPTO_chacha_20| uses a 32-bit block counter. Therefore we disallow // individual operations that work on more than 256GB at a time. // |in_len_64| is needed because, on 32-bit platforms, size_t is only // 32-bits and this produces a warning because it's always false. // Casting to uint64_t inside the conditional is not sufficient to stop // the warning. const uint64_t in_len_64 = in_len; if (in_len_64 >= (UINT64_C(1) << 32) * 64 - 64) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); return 0; } if (max_out_tag_len < tag_len) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); return 0; } // The the extra input is given, it is expected to be very short and so is // encrypted byte-by-byte first. if (extra_in_len) { static const size_t kChaChaBlockSize = 64; uint32_t block_counter = 1 + (in_len / kChaChaBlockSize); size_t offset = in_len % kChaChaBlockSize; uint8_t block[64 /* kChaChaBlockSize */]; for (size_t done = 0; done < extra_in_len; block_counter++) { memset(block, 0, sizeof(block)); CRYPTO_chacha_20(block, block, sizeof(block), key, nonce, block_counter); for (size_t i = offset; i < sizeof(block) && done < extra_in_len; i++, done++) { out_tag[done] = extra_in[done] ^ block[i]; } offset = 0; } } union seal_data data; if (asm_capable()) { OPENSSL_memcpy(data.in.key, key, 32); data.in.counter = 0; OPENSSL_memcpy(data.in.nonce, nonce, 12); data.in.extra_ciphertext = out_tag; data.in.extra_ciphertext_len = extra_in_len; chacha20_poly1305_seal(out, in, in_len, ad, ad_len, &data); } else { CRYPTO_chacha_20(out, in, in_len, key, nonce, 1); calc_tag(data.out.tag, key, nonce, ad, ad_len, out, in_len, out_tag, extra_in_len); } OPENSSL_memcpy(out_tag + extra_in_len, data.out.tag, tag_len); *out_tag_len = extra_in_len + tag_len; return 1; }