static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len, const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len) { const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; uint8_t mac[POLY1305_TAG_LEN]; uint8_t poly1305_key[32] ALIGNED; size_t plaintext_len; poly1305_state poly1305; const uint64_t in_len_64 = in_len; if (in_len < c20_ctx->tag_len) { OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BAD_DECRYPT); return 0; } /* The underlying ChaCha implementation may not overflow the block * counter into the second counter word. 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. */ if (in_len_64 >= (1ull << 32) * 64 - 64) { OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_TOO_LARGE); return 0; } if (nonce_len != CHACHA20_NONCE_LEN) { OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_IV_TOO_LARGE); return 0; } plaintext_len = in_len - c20_ctx->tag_len; if (max_out_len < plaintext_len) { OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BUFFER_TOO_SMALL); return 0; } memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0); CRYPTO_poly1305_init(&poly1305, poly1305_key); poly1305_update_with_length(&poly1305, ad, ad_len); poly1305_update_with_length(&poly1305, in, plaintext_len); CRYPTO_poly1305_finish(&poly1305, mac); if (CRYPTO_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) { OPENSSL_PUT_ERROR(CIPHER, aead_chacha20_poly1305_open, CIPHER_R_BAD_DECRYPT); return 0; } CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1); *out_len = plaintext_len; return 1; }
static ssize_t aead_chacha20_poly1305_seal(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t max_out_len, const unsigned char *nonce, size_t nonce_len, const unsigned char *in, size_t in_len, const unsigned char *ad, size_t ad_len) { const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; unsigned char poly1305_key[32] ALIGNED; poly1305_state poly1305; const uint64_t in_len_64 = in_len; /* The underlying ChaCha implementation may not overflow the block * counter into the second counter word. Therefore we disallow * individual operations that work on more than 2TB 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. */ if (in_len_64 >= (1ull << 32)*64-64) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_TOO_LARGE); return -1; } if (max_out_len < in_len + c20_ctx->tag_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_BUFFER_TOO_SMALL); return -1; } if (nonce_len != CHACHA20_NONCE_LEN) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_SEAL, EVP_R_IV_TOO_LARGE); return -1; } memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0); CRYPTO_poly1305_init(&poly1305, poly1305_key); poly1305_update_with_length(&poly1305, ad, ad_len); CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1); poly1305_update_with_length(&poly1305, out, in_len); if (c20_ctx->tag_len != POLY1305_TAG_LEN) { unsigned char tag[POLY1305_TAG_LEN]; CRYPTO_poly1305_finish(&poly1305, tag); memcpy(out + in_len, tag, c20_ctx->tag_len); return in_len + c20_ctx->tag_len; } CRYPTO_poly1305_finish(&poly1305, out + in_len); return in_len + POLY1305_TAG_LEN; }
// calc_tag fills |tag| with the authentication tag for the given inputs. static void calc_tag(uint8_t tag[POLY1305_TAG_LEN], const uint8_t *key, const uint8_t nonce[12], const uint8_t *ad, size_t ad_len, const uint8_t *ciphertext, size_t ciphertext_len, const uint8_t *ciphertext_extra, size_t ciphertext_extra_len) { alignas(16) uint8_t poly1305_key[32]; OPENSSL_memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, nonce, 0); static const uint8_t padding[16] = { 0 }; // Padding is all zeros. poly1305_state ctx; CRYPTO_poly1305_init(&ctx, poly1305_key); CRYPTO_poly1305_update(&ctx, ad, ad_len); if (ad_len % 16 != 0) { CRYPTO_poly1305_update(&ctx, padding, sizeof(padding) - (ad_len % 16)); } CRYPTO_poly1305_update(&ctx, ciphertext, ciphertext_len); CRYPTO_poly1305_update(&ctx, ciphertext_extra, ciphertext_extra_len); const size_t ciphertext_total = ciphertext_len + ciphertext_extra_len; if (ciphertext_total % 16 != 0) { CRYPTO_poly1305_update(&ctx, padding, sizeof(padding) - (ciphertext_total % 16)); } poly1305_update_length(&ctx, ad_len); poly1305_update_length(&ctx, ciphertext_total); CRYPTO_poly1305_finish(&ctx, tag); }
static int aead_chacha20_poly1305_seal(aead_poly1305_update poly1305_update, const void *ctx_buf, uint8_t *out, size_t *out_len, size_t max_out_len, const uint8_t nonce[12], const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len) { aead_assert_open_seal_preconditions(alignof(struct aead_chacha20_poly1305_ctx), ctx_buf, out, out_len, nonce, in, in_len, ad, ad_len); const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx_buf; if (!aead_seal_out_max_out_in_tag_len(out_len, max_out_len, in_len, POLY1305_TAG_LEN)) { /* |aead_seal_out_max_out_in_tag_len| already called |OPENSSL_PUT_ERROR|. */ return 0; } CRYPTO_chacha_20(out, in, in_len, c20_ctx->key, nonce, 1); uint8_t tag[POLY1305_TAG_LEN] ALIGNED; aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, out, in_len); /* TODO: Does |tag| really need to be |ALIGNED|? If not, we can avoid this * call to |memcpy|. */ memcpy(out + in_len, tag, POLY1305_TAG_LEN); return 1; }
static int aead_chacha20_poly1305_open(aead_poly1305_update poly1305_update, const void *ctx_buf, uint8_t *out, size_t *out_len, size_t max_out_len, const uint8_t nonce[12], const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len) { aead_assert_open_seal_preconditions(alignof(struct aead_chacha20_poly1305_ctx), ctx_buf, out, out_len, nonce, in, in_len, ad, ad_len); const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx_buf; if (!aead_open_out_max_out_in_tag_len(out_len, max_out_len, in_len, POLY1305_TAG_LEN)) { /* |aead_open_out_max_out_in_tag_len| already called * |OPENSSL_PUT_ERROR|. */ return 0; } size_t plaintext_len; plaintext_len = in_len - POLY1305_TAG_LEN; uint8_t tag[POLY1305_TAG_LEN] ALIGNED; aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, in, plaintext_len); if (CRYPTO_memcmp(tag, in + plaintext_len, POLY1305_TAG_LEN) != 0) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); return 0; } CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1); *out_len = plaintext_len; return 1; }
void CRYPTO_sysrand(uint8_t *out, size_t requested) { static const uint8_t kZeroKey[32]; uint8_t nonce[12]; OPENSSL_memset(nonce, 0, sizeof(nonce)); OPENSSL_memcpy(nonce, &g_num_calls, sizeof(g_num_calls)); OPENSSL_memset(out, 0, requested); CRYPTO_chacha_20(out, out, requested, kZeroKey, nonce, 0); g_num_calls++; }
/* aead_poly1305 fills |tag| with the authentication tag for the given * inputs, using |update| to control the order and format that the inputs are * signed/authenticated. */ static void aead_poly1305(aead_poly1305_update update, uint8_t tag[POLY1305_TAG_LEN], const struct aead_chacha20_poly1305_ctx *c20_ctx, const uint8_t nonce[12], const uint8_t *ad, size_t ad_len, const uint8_t *ciphertext, size_t ciphertext_len) { uint8_t poly1305_key[32] ALIGNED; memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0); poly1305_state ctx; CRYPTO_poly1305_init(&ctx, poly1305_key); update(&ctx, ad, ad_len, ciphertext, ciphertext_len); CRYPTO_poly1305_finish(&ctx, tag); }
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 open_impl(aead_poly1305_update poly1305_update, const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len, const uint8_t nonce[12], const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len) { const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; size_t plaintext_len; const uint64_t in_len_64 = in_len; if (in_len < c20_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. */ if (in_len_64 >= (1ull << 32) * 64 - 64) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); return 0; } plaintext_len = in_len - c20_ctx->tag_len; uint8_t tag[POLY1305_TAG_LEN] ALIGNED; aead_poly1305(poly1305_update, tag, c20_ctx, nonce, ad, ad_len, in, plaintext_len); if (CRYPTO_memcmp(tag, in + plaintext_len, c20_ctx->tag_len) != 0) { OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); return 0; } CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, nonce, 1); *out_len = plaintext_len; return 1; }
static int aead_chacha20_poly1305_open(const EVP_AEAD_CTX *ctx, unsigned char *out, size_t *out_len, size_t max_out_len, const unsigned char *nonce, size_t nonce_len, const unsigned char *in, size_t in_len, const unsigned char *ad, size_t ad_len) { const struct aead_chacha20_poly1305_ctx *c20_ctx = ctx->aead_state; unsigned char mac[POLY1305_TAG_LEN]; unsigned char poly1305_key[32]; const unsigned char *iv = nonce; poly1305_state poly1305; const uint64_t in_len_64 = in_len; size_t plaintext_len; uint64_t ctr = 0; if (in_len < c20_ctx->tag_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT); return 0; } /* The underlying ChaCha implementation may not overflow the block * counter into the second counter word. Therefore we disallow * individual operations that work on more than 2TB 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. */ if (in_len_64 >= (1ULL << 32) * 64 - 64) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_TOO_LARGE); return 0; } if (nonce_len != ctx->aead->nonce_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_IV_TOO_LARGE); return 0; } plaintext_len = in_len - c20_ctx->tag_len; if (max_out_len < plaintext_len) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BUFFER_TOO_SMALL); return 0; } if (nonce_len == CHACHA20_NONCE_LEN_OLD) { /* Google's draft-agl-tls-chacha20poly1305-04, Nov 2013 */ memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, nonce, 0); CRYPTO_poly1305_init(&poly1305, poly1305_key); poly1305_update_with_length(&poly1305, ad, ad_len); poly1305_update_with_length(&poly1305, in, plaintext_len); } else if (nonce_len == CHACHA20_NONCE_LEN) { /* RFC 7539, May 2015 */ ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; iv = nonce + CHACHA20_CONSTANT_LEN; memset(poly1305_key, 0, sizeof(poly1305_key)); CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), c20_ctx->key, iv, ctr); CRYPTO_poly1305_init(&poly1305, poly1305_key); poly1305_update_with_pad16(&poly1305, ad, ad_len); poly1305_update_with_pad16(&poly1305, in, plaintext_len); poly1305_update_with_length(&poly1305, NULL, ad_len); poly1305_update_with_length(&poly1305, NULL, plaintext_len); } CRYPTO_poly1305_finish(&poly1305, mac); if (timingsafe_memcmp(mac, in + plaintext_len, c20_ctx->tag_len) != 0) { EVPerr(EVP_F_AEAD_CHACHA20_POLY1305_OPEN, EVP_R_BAD_DECRYPT); return 0; } CRYPTO_chacha_20(out, in, plaintext_len, c20_ctx->key, iv, ctr + 1); *out_len = plaintext_len; return 1; }
/* Single-shot ChaCha20 using CRYPTO_chacha_20 interface. */ static void crypto_chacha_20_test(struct chacha_tv *tv, unsigned char *out, unsigned char *in) { CRYPTO_chacha_20(out, in, tv->len, tv->key, tv->iv, 0); }
int RAND_bytes(uint8_t *buf, size_t len) { if (len == 0) { return 1; } if (!CRYPTO_have_hwrand() || !CRYPTO_hwrand(buf, len)) { /* Without a hardware RNG to save us from address-space duplication, the OS * entropy is used directly. */ CRYPTO_sysrand(buf, len); return 1; } struct rand_thread_state *state = CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_RAND); if (state == NULL) { state = OPENSSL_malloc(sizeof(struct rand_thread_state)); if (state == NULL || !CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_RAND, state, rand_thread_state_free)) { CRYPTO_sysrand(buf, len); return 1; } memset(state->partial_block, 0, sizeof(state->partial_block)); state->calls_used = kMaxCallsPerRefresh; } if (state->calls_used >= kMaxCallsPerRefresh || state->bytes_used >= kMaxBytesPerRefresh) { CRYPTO_sysrand(state->key, sizeof(state->key)); state->calls_used = 0; state->bytes_used = 0; state->partial_block_used = sizeof(state->partial_block); } if (len >= sizeof(state->partial_block)) { size_t remaining = len; while (remaining > 0) { // kMaxBytesPerCall is only 2GB, while ChaCha can handle 256GB. But this // is sufficient and easier on 32-bit. static const size_t kMaxBytesPerCall = 0x80000000; size_t todo = remaining; if (todo > kMaxBytesPerCall) { todo = kMaxBytesPerCall; } CRYPTO_chacha_20(buf, buf, todo, state->key, (uint8_t *)&state->calls_used, 0); buf += todo; remaining -= todo; state->calls_used++; } } else { if (sizeof(state->partial_block) - state->partial_block_used < len) { CRYPTO_chacha_20(state->partial_block, state->partial_block, sizeof(state->partial_block), state->key, (uint8_t *)&state->calls_used, 0); state->partial_block_used = 0; } unsigned i; for (i = 0; i < len; i++) { buf[i] ^= state->partial_block[state->partial_block_used++]; } state->calls_used++; } state->bytes_used += len; 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; }
int main(void) { static const unsigned num_tests = sizeof(chacha_tests) / sizeof(struct chacha_test); unsigned i; uint8_t key_bytes[32 + 16]; uint8_t nonce_bytes[12 + 16] = { 0 }; uint8_t *key = misalign(key_bytes); uint8_t *nonce = misalign(nonce_bytes); for (i = 0; i < num_tests; i++) { const struct chacha_test *test = &chacha_tests[i]; uint8_t *expected, *out_bytes, *zero_bytes, *out, *zeros; size_t len = strlen(test->outhex); if (strlen(test->keyhex) != 32 * 2 || strlen(test->noncehex) != 12 * 2 || (len & 1) == 1) return 1; len /= 2; hex_decode(key, test->keyhex); hex_decode(nonce, test->noncehex); expected = malloc(len); out_bytes = malloc(len + 16); zero_bytes = malloc(len + 16); /* Attempt to test unaligned inputs. */ out = misalign(out_bytes); zeros = misalign(zero_bytes); memset(zeros, 0, len); hex_decode(expected, test->outhex); CRYPTO_chacha_20(out, zeros, len, key, nonce, 0); if (memcmp(out, expected, len) != 0) { printf("ChaCha20 test #%d failed.\n", i); printf("got: "); hexdump(out, len); printf("\nexpected: "); hexdump(expected, len); printf("\n"); return 1; } /* * The last test has a large output. We test whether the * counter works as expected by skipping the first 64 bytes of * it. */ if (i == num_tests - 1) { CRYPTO_chacha_20(out, zeros, len - 64, key, nonce, 1); if (memcmp(out, expected + 64, len - 64) != 0) { printf("ChaCha20 skip test failed.\n"); return 1; } } free(expected); free(zero_bytes); free(out_bytes); } printf("PASS\n"); return 0; }