summaryrefslogtreecommitdiff
path: root/lib/freebl/chacha20poly1305.c
diff options
context:
space:
mode:
authorTim Taubert <ttaubert@mozilla.com>2016-02-11 08:42:11 +0100
committerTim Taubert <ttaubert@mozilla.com>2016-02-11 08:42:11 +0100
commit7a60a8faa4344d7def3edcdc768d06424811be5f (patch)
tree96abcc00cfb035ffecf5b740f09e7072fc814fc4 /lib/freebl/chacha20poly1305.c
parentd87f2b9b2fb1745b137140e23a5e6b5760682737 (diff)
downloadnss-hg-7a60a8faa4344d7def3edcdc768d06424811be5f.tar.gz
Bug 917571 - Add ChaCha20+Poly1305 cipher r=mt,wtc,ekr
Diffstat (limited to 'lib/freebl/chacha20poly1305.c')
-rw-r--r--lib/freebl/chacha20poly1305.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/lib/freebl/chacha20poly1305.c b/lib/freebl/chacha20poly1305.c
new file mode 100644
index 000000000..e5d0310fb
--- /dev/null
+++ b/lib/freebl/chacha20poly1305.c
@@ -0,0 +1,175 @@
+/* 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 "poly1305.h"
+#include "chacha20.h"
+#include "chacha20poly1305.h"
+
+/* 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])
+{
+ poly1305_state state;
+ unsigned int j;
+ unsigned char lengthBytes[8];
+ static const unsigned char zeros[15];
+ unsigned int i;
+
+ Poly1305Init(&state, key);
+ Poly1305Update(&state, ad, adLen);
+ if (adLen % 16 > 0) {
+ Poly1305Update(&state, zeros, 16 - adLen % 16);
+ }
+ Poly1305Update(&state, ciphertext, ciphertextLen);
+ if (ciphertextLen % 16 > 0) {
+ Poly1305Update(&state, zeros, 16 - ciphertextLen % 16);
+ }
+ j = adLen;
+ for (i = 0; i < sizeof(lengthBytes); i++) {
+ lengthBytes[i] = j;
+ j >>= 8;
+ }
+ Poly1305Update(&state, lengthBytes, sizeof(lengthBytes));
+ j = ciphertextLen;
+ for (i = 0; i < sizeof(lengthBytes); i++) {
+ lengthBytes[i] = j;
+ j >>= 8;
+ }
+ Poly1305Update(&state, lengthBytes, sizeof(lengthBytes));
+ Poly1305Finish(&state, out);
+}
+
+SECStatus
+ChaCha20Poly1305_InitContext(ChaCha20Poly1305Context *ctx,
+ const unsigned char *key, unsigned int keyLen,
+ unsigned int tagLen)
+{
+ 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;
+}
+
+ChaCha20Poly1305Context *
+ChaCha20Poly1305_CreateContext(const unsigned char *key, unsigned int keyLen,
+ unsigned int tagLen)
+{
+ 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;
+}
+
+void
+ChaCha20Poly1305_DestroyContext(ChaCha20Poly1305Context *ctx, PRBool freeit)
+{
+ PORT_Memset(ctx, 0, sizeof(*ctx));
+ if (freeit) {
+ PORT_Free(ctx);
+ }
+}
+
+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)
+{
+ 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, block, sizeof(block), ctx->key, nonce, 0);
+ ChaCha20XOR(output, input, inputLen, ctx->key, nonce, 1);
+
+ Poly1305Do(tag, ad, adLen, output, inputLen, block);
+ PORT_Memcpy(output + inputLen, tag, ctx->tagLen);
+
+ return SECSuccess;
+}
+
+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)
+{
+ 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, block, sizeof(block), ctx->key, 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, input, ciphertextLen, ctx->key, nonce, 1);
+
+ return SECSuccess;
+}