| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifdef FREEBL_NO_DEPEND |
| #include "stubs.h" |
| #endif |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "seccomon.h" |
| #include "secerr.h" |
| #include "blapit.h" |
| #include "blapii.h" |
| |
| #ifndef NSS_DISABLE_CHACHAPOLY |
| #include "chacha20poly1305.h" |
| // Forward declaration from "Hacl_Chacha20_Vec128.h". |
| extern void Hacl_Chacha20_Vec128_chacha20(uint8_t *output, uint8_t *plain, |
| uint32_t len, uint8_t *k, uint8_t *n1, |
| uint32_t ctr); |
| // Forward declaration from "Hacl_Chacha20.h". |
| extern void Hacl_Chacha20_chacha20(uint8_t *output, uint8_t *plain, uint32_t len, |
| uint8_t *k, uint8_t *n1, uint32_t ctr); |
| |
| #if defined(HAVE_INT128_SUPPORT) && (defined(NSS_X86_OR_X64) || defined(__aarch64__)) |
| /* Use HACL* Poly1305 on 64-bit Intel and ARM */ |
| #include "verified/Hacl_Poly1305_64.h" |
| #define NSS_POLY1305_64 1 |
| #define Hacl_Poly1305_update Hacl_Poly1305_64_update |
| #define Hacl_Poly1305_mk_state Hacl_Poly1305_64_mk_state |
| #define Hacl_Poly1305_init Hacl_Poly1305_64_init |
| #define Hacl_Poly1305_finish Hacl_Poly1305_64_finish |
| typedef Hacl_Impl_Poly1305_64_State_poly1305_state Hacl_Impl_Poly1305_State_poly1305_state; |
| #else |
| /* All other platforms get the 32-bit poly1305 HACL* implementation. */ |
| #include "verified/Hacl_Poly1305_32.h" |
| #define NSS_POLY1305_32 1 |
| #define Hacl_Poly1305_update Hacl_Poly1305_32_update |
| #define Hacl_Poly1305_mk_state Hacl_Poly1305_32_mk_state |
| #define Hacl_Poly1305_init Hacl_Poly1305_32_init |
| #define Hacl_Poly1305_finish Hacl_Poly1305_32_finish |
| typedef Hacl_Impl_Poly1305_32_State_poly1305_state Hacl_Impl_Poly1305_State_poly1305_state; |
| #endif /* HAVE_INT128_SUPPORT */ |
| |
| static void |
| Poly1305PadUpdate(Hacl_Impl_Poly1305_State_poly1305_state state, |
| unsigned char *block, const unsigned char *p, |
| const unsigned int pLen) |
| { |
| unsigned int pRemLen = pLen % 16; |
| Hacl_Poly1305_update(state, (uint8_t *)p, (pLen / 16)); |
| if (pRemLen > 0) { |
| memcpy(block, p + (pLen - pRemLen), pRemLen); |
| Hacl_Poly1305_update(state, block, 1); |
| } |
| } |
| |
| /* Poly1305Do writes the Poly1305 authenticator of the given additional data |
| * and ciphertext to |out|. */ |
| static void |
| Poly1305Do(unsigned char *out, const unsigned char *ad, unsigned int adLen, |
| const unsigned char *ciphertext, unsigned int ciphertextLen, |
| const unsigned char key[32]) |
| { |
| #ifdef NSS_POLY1305_64 |
| uint64_t stateStack[6U] = { 0U }; |
| size_t offset = 3; |
| #elif defined NSS_POLY1305_32 |
| uint32_t stateStack[10U] = { 0U }; |
| size_t offset = 5; |
| #else |
| #error "This can't happen." |
| #endif |
| Hacl_Impl_Poly1305_State_poly1305_state state = |
| Hacl_Poly1305_mk_state(stateStack, stateStack + offset); |
| |
| unsigned char block[16] = { 0 }; |
| Hacl_Poly1305_init(state, (uint8_t *)key); |
| |
| Poly1305PadUpdate(state, block, ad, adLen); |
| memset(block, 0, 16); |
| Poly1305PadUpdate(state, block, ciphertext, ciphertextLen); |
| |
| unsigned int i; |
| unsigned int j; |
| for (i = 0, j = adLen; i < 8; i++, j >>= 8) { |
| block[i] = j; |
| } |
| for (i = 8, j = ciphertextLen; i < 16; i++, j >>= 8) { |
| block[i] = j; |
| } |
| |
| Hacl_Poly1305_update(state, block, 1); |
| Hacl_Poly1305_finish(state, out, (uint8_t *)(key + 16)); |
| #undef NSS_POLY1305_64 |
| #undef NSS_POLY1305_32 |
| } |
| #endif /* NSS_DISABLE_CHACHAPOLY */ |
| |
| SECStatus |
| ChaCha20Poly1305_InitContext(ChaCha20Poly1305Context *ctx, |
| const unsigned char *key, unsigned int keyLen, |
| unsigned int tagLen) |
| { |
| #ifdef NSS_DISABLE_CHACHAPOLY |
| return SECFailure; |
| #else |
| if (keyLen != 32) { |
| PORT_SetError(SEC_ERROR_BAD_KEY); |
| return SECFailure; |
| } |
| if (tagLen == 0 || tagLen > 16) { |
| PORT_SetError(SEC_ERROR_INPUT_LEN); |
| return SECFailure; |
| } |
| |
| PORT_Memcpy(ctx->key, key, sizeof(ctx->key)); |
| ctx->tagLen = tagLen; |
| |
| return SECSuccess; |
| #endif |
| } |
| |
| ChaCha20Poly1305Context * |
| ChaCha20Poly1305_CreateContext(const unsigned char *key, unsigned int keyLen, |
| unsigned int tagLen) |
| { |
| #ifdef NSS_DISABLE_CHACHAPOLY |
| return NULL; |
| #else |
| ChaCha20Poly1305Context *ctx; |
| |
| ctx = PORT_New(ChaCha20Poly1305Context); |
| if (ctx == NULL) { |
| return NULL; |
| } |
| |
| if (ChaCha20Poly1305_InitContext(ctx, key, keyLen, tagLen) != SECSuccess) { |
| PORT_Free(ctx); |
| ctx = NULL; |
| } |
| |
| return ctx; |
| #endif |
| } |
| |
| void |
| ChaCha20Poly1305_DestroyContext(ChaCha20Poly1305Context *ctx, PRBool freeit) |
| { |
| #ifndef NSS_DISABLE_CHACHAPOLY |
| PORT_Memset(ctx, 0, sizeof(*ctx)); |
| if (freeit) { |
| PORT_Free(ctx); |
| } |
| #endif |
| } |
| |
| void |
| ChaCha20Xor(uint8_t *output, uint8_t *block, uint32_t len, uint8_t *k, |
| uint8_t *nonce, uint32_t ctr) |
| { |
| if (ssse3_support() || arm_neon_support()) { |
| Hacl_Chacha20_Vec128_chacha20(output, block, len, k, nonce, ctr); |
| } else { |
| Hacl_Chacha20_chacha20(output, block, len, k, nonce, ctr); |
| } |
| } |
| |
| SECStatus |
| ChaCha20Poly1305_Seal(const ChaCha20Poly1305Context *ctx, unsigned char *output, |
| unsigned int *outputLen, unsigned int maxOutputLen, |
| const unsigned char *input, unsigned int inputLen, |
| const unsigned char *nonce, unsigned int nonceLen, |
| const unsigned char *ad, unsigned int adLen) |
| { |
| #ifdef NSS_DISABLE_CHACHAPOLY |
| return SECFailure; |
| #else |
| unsigned char block[64]; |
| unsigned char tag[16]; |
| |
| if (nonceLen != 12) { |
| PORT_SetError(SEC_ERROR_INPUT_LEN); |
| return SECFailure; |
| } |
| *outputLen = inputLen + ctx->tagLen; |
| if (maxOutputLen < *outputLen) { |
| PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
| return SECFailure; |
| } |
| |
| PORT_Memset(block, 0, sizeof(block)); |
| // Generate a block of keystream. The first 32 bytes will be the poly1305 |
| // key. The remainder of the block is discarded. |
| ChaCha20Xor(block, (uint8_t *)block, sizeof(block), (uint8_t *)ctx->key, |
| (uint8_t *)nonce, 0); |
| ChaCha20Xor(output, (uint8_t *)input, inputLen, (uint8_t *)ctx->key, |
| (uint8_t *)nonce, 1); |
| |
| Poly1305Do(tag, ad, adLen, output, inputLen, block); |
| PORT_Memcpy(output + inputLen, tag, ctx->tagLen); |
| |
| return SECSuccess; |
| #endif |
| } |
| |
| SECStatus |
| ChaCha20Poly1305_Open(const ChaCha20Poly1305Context *ctx, unsigned char *output, |
| unsigned int *outputLen, unsigned int maxOutputLen, |
| const unsigned char *input, unsigned int inputLen, |
| const unsigned char *nonce, unsigned int nonceLen, |
| const unsigned char *ad, unsigned int adLen) |
| { |
| #ifdef NSS_DISABLE_CHACHAPOLY |
| return SECFailure; |
| #else |
| unsigned char block[64]; |
| unsigned char tag[16]; |
| unsigned int ciphertextLen; |
| |
| if (nonceLen != 12) { |
| PORT_SetError(SEC_ERROR_INPUT_LEN); |
| return SECFailure; |
| } |
| if (inputLen < ctx->tagLen) { |
| PORT_SetError(SEC_ERROR_INPUT_LEN); |
| return SECFailure; |
| } |
| ciphertextLen = inputLen - ctx->tagLen; |
| *outputLen = ciphertextLen; |
| if (maxOutputLen < *outputLen) { |
| PORT_SetError(SEC_ERROR_OUTPUT_LEN); |
| return SECFailure; |
| } |
| |
| PORT_Memset(block, 0, sizeof(block)); |
| // Generate a block of keystream. The first 32 bytes will be the poly1305 |
| // key. The remainder of the block is discarded. |
| ChaCha20Xor(block, (uint8_t *)block, sizeof(block), (uint8_t *)ctx->key, |
| (uint8_t *)nonce, 0); |
| Poly1305Do(tag, ad, adLen, input, ciphertextLen, block); |
| if (NSS_SecureMemcmp(tag, &input[ciphertextLen], ctx->tagLen) != 0) { |
| PORT_SetError(SEC_ERROR_BAD_DATA); |
| return SECFailure; |
| } |
| |
| ChaCha20Xor(output, (uint8_t *)input, ciphertextLen, (uint8_t *)ctx->key, |
| (uint8_t *)nonce, 1); |
| |
| return SECSuccess; |
| #endif |
| } |