andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #ifdef FREEBL_NO_DEPEND andre@0: #include "stubs.h" andre@0: #endif andre@0: andre@0: #include andre@0: #include andre@0: andre@0: #include "seccomon.h" andre@0: #include "secerr.h" andre@0: #include "blapit.h" andre@0: #include "poly1305/poly1305.h" andre@0: #include "chacha20/chacha20.h" andre@0: #include "chacha20poly1305.h" andre@0: andre@0: /* Poly1305Do writes the Poly1305 authenticator of the given additional data andre@0: * and ciphertext to |out|. */ andre@0: static void andre@0: Poly1305Do(unsigned char *out, andre@0: const unsigned char *ad, unsigned int adLen, andre@0: const unsigned char *ciphertext, unsigned int ciphertextLen, andre@0: const unsigned char key[32]) andre@0: { andre@0: poly1305_state state; andre@0: unsigned int j; andre@0: unsigned char lengthBytes[8]; andre@0: unsigned int i; andre@0: andre@0: Poly1305Init(&state, key); andre@0: j = adLen; andre@0: for (i = 0; i < sizeof(lengthBytes); i++) { andre@0: lengthBytes[i] = j; andre@0: j >>= 8; andre@0: } andre@0: Poly1305Update(&state, ad, adLen); andre@0: Poly1305Update(&state, lengthBytes, sizeof(lengthBytes)); andre@0: j = ciphertextLen; andre@0: for (i = 0; i < sizeof(lengthBytes); i++) { andre@0: lengthBytes[i] = j; andre@0: j >>= 8; andre@0: } andre@0: Poly1305Update(&state, ciphertext, ciphertextLen); andre@0: Poly1305Update(&state, lengthBytes, sizeof(lengthBytes)); andre@0: Poly1305Finish(&state, out); andre@0: } andre@0: andre@0: SECStatus andre@0: ChaCha20Poly1305_InitContext(ChaCha20Poly1305Context *ctx, andre@0: const unsigned char *key, unsigned int keyLen, andre@0: unsigned int tagLen) andre@0: { andre@0: if (keyLen != 32) { andre@0: PORT_SetError(SEC_ERROR_BAD_KEY); andre@0: return SECFailure; andre@0: } andre@0: if (tagLen == 0 || tagLen > 16) { andre@0: PORT_SetError(SEC_ERROR_INPUT_LEN); andre@0: return SECFailure; andre@0: } andre@0: andre@0: memcpy(ctx->key, key, sizeof(ctx->key)); andre@0: ctx->tagLen = tagLen; andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: ChaCha20Poly1305Context * andre@0: ChaCha20Poly1305_CreateContext(const unsigned char *key, unsigned int keyLen, andre@0: unsigned int tagLen) andre@0: { andre@0: ChaCha20Poly1305Context *ctx; andre@0: andre@0: ctx = PORT_New(ChaCha20Poly1305Context); andre@0: if (ctx == NULL) { andre@0: return NULL; andre@0: } andre@0: andre@0: if (ChaCha20Poly1305_InitContext(ctx, key, keyLen, tagLen) != SECSuccess) { andre@0: PORT_Free(ctx); andre@0: ctx = NULL; andre@0: } andre@0: andre@0: return ctx; andre@0: } andre@0: andre@0: void andre@0: ChaCha20Poly1305_DestroyContext(ChaCha20Poly1305Context *ctx, PRBool freeit) andre@0: { andre@0: memset(ctx, 0, sizeof(*ctx)); andre@0: if (freeit) { andre@0: PORT_Free(ctx); andre@0: } andre@0: } andre@0: andre@0: SECStatus andre@0: ChaCha20Poly1305_Seal(const ChaCha20Poly1305Context *ctx, andre@0: unsigned char *output, unsigned int *outputLen, andre@0: unsigned int maxOutputLen, andre@0: const unsigned char *input, unsigned int inputLen, andre@0: const unsigned char *nonce, unsigned int nonceLen, andre@0: const unsigned char *ad, unsigned int adLen) andre@0: { andre@0: unsigned char block[64]; andre@0: unsigned char tag[16]; andre@0: andre@0: if (nonceLen != 8) { andre@0: PORT_SetError(SEC_ERROR_INPUT_LEN); andre@0: return SECFailure; andre@0: } andre@0: *outputLen = inputLen + ctx->tagLen; andre@0: if (maxOutputLen < *outputLen) { andre@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); andre@0: return SECFailure; andre@0: } andre@0: andre@0: memset(block, 0, sizeof(block)); andre@0: // Generate a block of keystream. The first 32 bytes will be the poly1305 andre@0: // key. The remainder of the block is discarded. andre@0: ChaCha20XOR(block, block, sizeof(block), ctx->key, nonce, 0); andre@0: ChaCha20XOR(output, input, inputLen, ctx->key, nonce, 1); andre@0: andre@0: Poly1305Do(tag, ad, adLen, output, inputLen, block); andre@0: memcpy(output + inputLen, tag, ctx->tagLen); andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: SECStatus andre@0: ChaCha20Poly1305_Open(const ChaCha20Poly1305Context *ctx, andre@0: unsigned char *output, unsigned int *outputLen, andre@0: unsigned int maxOutputLen, andre@0: const unsigned char *input, unsigned int inputLen, andre@0: const unsigned char *nonce, unsigned int nonceLen, andre@0: const unsigned char *ad, unsigned int adLen) andre@0: { andre@0: unsigned char block[64]; andre@0: unsigned char tag[16]; andre@0: andre@0: if (nonceLen != 8) { andre@0: PORT_SetError(SEC_ERROR_INPUT_LEN); andre@0: return SECFailure; andre@0: } andre@0: if (inputLen < ctx->tagLen) { andre@0: PORT_SetError(SEC_ERROR_INPUT_LEN); andre@0: return SECFailure; andre@0: } andre@0: *outputLen = inputLen - ctx->tagLen; andre@0: if (maxOutputLen < *outputLen) { andre@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); andre@0: return SECFailure; andre@0: } andre@0: andre@0: memset(block, 0, sizeof(block)); andre@0: // Generate a block of keystream. The first 32 bytes will be the poly1305 andre@0: // key. The remainder of the block is discarded. andre@0: ChaCha20XOR(block, block, sizeof(block), ctx->key, nonce, 0); andre@0: Poly1305Do(tag, ad, adLen, input, inputLen - ctx->tagLen, block); andre@0: if (NSS_SecureMemcmp(tag, &input[inputLen - ctx->tagLen], ctx->tagLen) != 0) { andre@0: PORT_SetError(SEC_ERROR_BAD_DATA); andre@0: return SECFailure; andre@0: } andre@0: andre@0: ChaCha20XOR(output, input, inputLen - ctx->tagLen, ctx->key, nonce, 1); andre@0: andre@0: return SECSuccess; andre@0: }