diff options
Diffstat (limited to 'security/nss/lib/freebl/chacha20poly1305.c')
-rw-r--r-- | security/nss/lib/freebl/chacha20poly1305.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/security/nss/lib/freebl/chacha20poly1305.c b/security/nss/lib/freebl/chacha20poly1305.c new file mode 100644 index 0000000000..29bbc9d1c1 --- /dev/null +++ b/security/nss/lib/freebl/chacha20poly1305.c @@ -0,0 +1,549 @@ +/* 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" +#include "chacha20poly1305.h" + +// There are three implementations of ChaCha20Poly1305: +// 1) 128-bit with AVX hardware acceleration used on x64 +// 2) 256-bit with AVX2 hardware acceleration used on x64 +// 3) 32-bit used on all other platforms + +// On x64 when AVX2 and other necessary registers are available, +// the 256bit-verctorized version will be used. When AVX2 features +// are unavailable or disabled but AVX registers are available, the +// 128bit-vectorized version will be used. In all other cases the +// scalar version of the HACL* code will be used. + +// Instead of including the headers (they bring other things we don't want), +// we declare the functions here. +// Usage is guarded by runtime checks of required hardware features. + +// Forward declaration from Hacl_Chacha20_Vec128.h and Hacl_Chacha20Poly1305_128.h. +extern void Hacl_Chacha20_Vec128_chacha20_encrypt_128(uint32_t len, uint8_t *out, + uint8_t *text, uint8_t *key, + uint8_t *n1, uint32_t ctr); +extern void +Hacl_Chacha20Poly1305_128_aead_encrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); +extern uint32_t +Hacl_Chacha20Poly1305_128_aead_decrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); + +// Forward declaration from Hacl_Chacha20_Vec256.h and Hacl_Chacha20Poly1305_256.h. +extern void Hacl_Chacha20_Vec256_chacha20_encrypt_256(uint32_t len, uint8_t *out, + uint8_t *text, uint8_t *key, + uint8_t *n1, uint32_t ctr); +extern void +Hacl_Chacha20Poly1305_256_aead_encrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); +extern uint32_t +Hacl_Chacha20Poly1305_256_aead_decrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); + +// Forward declaration from Hacl_Chacha20.h and Hacl_Chacha20Poly1305_32.h. +extern void Hacl_Chacha20_chacha20_encrypt(uint32_t len, uint8_t *out, + uint8_t *text, uint8_t *key, + uint8_t *n1, uint32_t ctr); +extern void +Hacl_Chacha20Poly1305_32_aead_encrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); +extern uint32_t +Hacl_Chacha20Poly1305_32_aead_decrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); + +// Forward declaration from chacha20-ppc64le.S +void chacha20vsx(uint32_t len, uint8_t *output, uint8_t *block, uint8_t *k, + uint8_t *nonce, uint32_t ctr); + +// Forward declaration from chacha20poly1305-ppc.c +extern void +Chacha20Poly1305_vsx_aead_encrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); +extern uint32_t +Chacha20Poly1305_vsx_aead_decrypt(uint8_t *k, uint8_t *n1, uint32_t aadlen, + uint8_t *aad, uint32_t mlen, uint8_t *m, + uint8_t *cipher, uint8_t *mac); + +SECStatus +ChaCha20_InitContext(ChaCha20Context *ctx, const unsigned char *key, + unsigned int keyLen, const unsigned char *nonce, + unsigned int nonceLen, PRUint32 ctr) +{ +#ifdef NSS_DISABLE_CHACHAPOLY + return SECFailure; +#else + if (keyLen != 32) { + PORT_SetError(SEC_ERROR_BAD_KEY); + return SECFailure; + } + if (nonceLen != 12) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + ctx->counter = ctr; + PORT_Memcpy(ctx->key, key, sizeof(ctx->key)); + PORT_Memcpy(ctx->nonce, nonce, sizeof(ctx->nonce)); + + return SECSuccess; +#endif +} + +ChaCha20Context * +ChaCha20_CreateContext(const unsigned char *key, unsigned int keyLen, + const unsigned char *nonce, unsigned int nonceLen, + PRUint32 ctr) +{ +#ifdef NSS_DISABLE_CHACHAPOLY + return NULL; +#else + ChaCha20Context *ctx; + + ctx = PORT_New(ChaCha20Context); + if (ctx == NULL) { + return NULL; + } + + if (ChaCha20_InitContext(ctx, key, keyLen, nonce, nonceLen, ctr) != SECSuccess) { + PORT_Free(ctx); + ctx = NULL; + } + + return ctx; +#endif +} + +void +ChaCha20_DestroyContext(ChaCha20Context *ctx, PRBool freeit) +{ +#ifndef NSS_DISABLE_CHACHAPOLY + PORT_Memset(ctx, 0, sizeof(*ctx)); + if (freeit) { + PORT_Free(ctx); + } +#endif +} + +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 != 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 +} + +#ifndef NSS_DISABLE_CHACHAPOLY +void +ChaCha20Xor(uint8_t *output, uint8_t *block, uint32_t len, uint8_t *k, + uint8_t *nonce, uint32_t ctr) +{ +#ifdef NSS_X64 +#ifndef NSS_DISABLE_AVX2 + if (avx2_support()) { + Hacl_Chacha20_Vec256_chacha20_encrypt_256(len, output, block, k, nonce, ctr); + } +#endif + +#ifndef NSS_DISABLE_SSE3 + if (ssse3_support() && sse4_1_support() && avx_support()) { + Hacl_Chacha20_Vec128_chacha20_encrypt_128(len, output, block, k, nonce, ctr); + } +#endif + +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) && \ + !defined(NSS_DISABLE_ALTIVEC) && !defined(NSS_DISABLE_CRYPTO_VSX) + if (ppc_crypto_support()) { + chacha20vsx(len, output, block, k, nonce, ctr); + } else +#endif + { + Hacl_Chacha20_chacha20_encrypt(len, output, block, k, nonce, ctr); + } +} +#endif /* NSS_DISABLE_CHACHAPOLY */ + +SECStatus +ChaCha20_Xor(unsigned char *output, const unsigned char *block, unsigned int len, + const unsigned char *k, const unsigned char *nonce, PRUint32 ctr) +{ +#ifdef NSS_DISABLE_CHACHAPOLY + return SECFailure; +#else + // ChaCha has a 64 octet block, with a 32-bit block counter. + if (sizeof(len) > 4) { + unsigned long long len_ull = len; + if (len_ull >= (1ULL << (6 + 32))) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + } + ChaCha20Xor(output, (uint8_t *)block, len, (uint8_t *)k, + (uint8_t *)nonce, ctr); + return SECSuccess; +#endif +} + +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 + + if (nonceLen != 12) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + // ChaCha has a 64 octet block, with a 32-bit block counter. + if (sizeof(inputLen) > 4) { + unsigned long long inputLen_ull = inputLen; + if (inputLen_ull >= (1ULL << (6 + 32))) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + } + if (maxOutputLen < inputLen + ctx->tagLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + +#ifdef NSS_X64 +#ifndef NSS_DISABLE_AVX2 + if (avx2_support()) { + Hacl_Chacha20Poly1305_256_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, output + inputLen); + goto finish; + } +#endif + +#ifndef NSS_DISABLE_SSE3 + if (ssse3_support() && sse4_1_support() && avx_support()) { + Hacl_Chacha20Poly1305_128_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, output + inputLen); + goto finish; + } +#endif + +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) && \ + !defined(NSS_DISABLE_ALTIVEC) && !defined(NSS_DISABLE_CRYPTO_VSX) + if (ppc_crypto_support()) { + Chacha20Poly1305_vsx_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, output + inputLen); + goto finish; + } +#endif + { + Hacl_Chacha20Poly1305_32_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, output + inputLen); + goto finish; + } + +finish: + *outputLen = inputLen + 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 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; + if (maxOutputLen < ciphertextLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + // ChaCha has a 64 octet block, with a 32-bit block counter. + if (inputLen >= (1ULL << (6 + 32)) + ctx->tagLen) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + uint32_t res = 1; +#ifdef NSS_X64 +#ifndef NSS_DISABLE_AVX2 + if (avx2_support()) { + res = Hacl_Chacha20Poly1305_256_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)input + ciphertextLen); + goto finish; + } +#endif + +#ifndef NSS_DISABLE_SSE3 + if (ssse3_support() && sse4_1_support() && avx_support()) { + res = Hacl_Chacha20Poly1305_128_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)input + ciphertextLen); + goto finish; + } +#endif + +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) && \ + !defined(NSS_DISABLE_ALTIVEC) && !defined(NSS_DISABLE_CRYPTO_VSX) + if (ppc_crypto_support()) { + res = Chacha20Poly1305_vsx_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)input + ciphertextLen); + goto finish; + } +#endif + { + res = Hacl_Chacha20Poly1305_32_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)input + ciphertextLen); + goto finish; + } + +finish: + if (res) { + PORT_SetError(SEC_ERROR_BAD_DATA); + return SECFailure; + } + + *outputLen = ciphertextLen; + return SECSuccess; +#endif +} + +SECStatus +ChaCha20Poly1305_Encrypt(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, unsigned char *outTag) +{ +#ifdef NSS_DISABLE_CHACHAPOLY + return SECFailure; +#else + + if (nonceLen != 12) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + // ChaCha has a 64 octet block, with a 32-bit block counter. + if (sizeof(inputLen) > 4) { + unsigned long long inputLen_ull = inputLen; + if (inputLen_ull >= (1ULL << (6 + 32))) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + } + if (maxOutputLen < inputLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + +#ifdef NSS_X64 +#ifndef NSS_DISABLE_AVX2 + if (avx2_support()) { + Hacl_Chacha20Poly1305_256_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, outTag); + goto finish; + } +#endif + +#ifndef NSS_DISABLE_SSE3 + if (ssse3_support() && sse4_1_support() && avx_support()) { + Hacl_Chacha20Poly1305_128_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, outTag); + goto finish; + } +#endif + + else +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) && \ + !defined(NSS_DISABLE_ALTIVEC) && !defined(NSS_DISABLE_CRYPTO_VSX) + if (ppc_crypto_support()) { + Chacha20Poly1305_vsx_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, outTag); + goto finish; + } else +#endif + { + Hacl_Chacha20Poly1305_32_aead_encrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, inputLen, + (uint8_t *)input, output, outTag); + goto finish; + } + +finish: + *outputLen = inputLen; + return SECSuccess; +#endif +} + +SECStatus +ChaCha20Poly1305_Decrypt(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, const unsigned char *tagIn) +{ +#ifdef NSS_DISABLE_CHACHAPOLY + return SECFailure; +#else + unsigned int ciphertextLen; + + if (nonceLen != 12) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + ciphertextLen = inputLen; + if (maxOutputLen < ciphertextLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + // ChaCha has a 64 octet block, with a 32-bit block counter. + if (sizeof(inputLen) > 4) { + unsigned long long inputLen_ull = inputLen; + if (inputLen_ull >= (1ULL << (6 + 32))) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + } + + uint32_t res = 1; +#ifdef NSS_X64 +#ifndef NSS_DISABLE_AVX2 + if (avx2_support()) { + res = Hacl_Chacha20Poly1305_256_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)tagIn); + goto finish; + } +#endif + +#ifndef NSS_DISABLE_SSE3 + if (ssse3_support() && sse4_1_support() && avx_support()) { + res = Hacl_Chacha20Poly1305_128_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)tagIn); + goto finish; + } +#endif + +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) && \ + !defined(NSS_DISABLE_ALTIVEC) && !defined(NSS_DISABLE_CRYPTO_VSX) + if (ppc_crypto_support()) { + res = Chacha20Poly1305_vsx_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)tagIn); + goto finish; + } +#endif + { + res = Hacl_Chacha20Poly1305_32_aead_decrypt( + (uint8_t *)ctx->key, (uint8_t *)nonce, adLen, (uint8_t *)ad, ciphertextLen, + (uint8_t *)output, (uint8_t *)input, (uint8_t *)tagIn); + goto finish; + } + +finish: + if (res) { + PORT_SetError(SEC_ERROR_BAD_DATA); + return SECFailure; + } + + *outputLen = ciphertextLen; + return SECSuccess; +#endif +} |