From 1f81f3c833670727650b6890a88f5dc8c8773054 Mon Sep 17 00:00:00 2001 From: Evan Su <48808396+HACKERALERT@users.noreply.github.com> Date: Mon, 22 Mar 2021 09:14:10 -0400 Subject: [PATCH] Create chacha20.c --- audit/chacha20.c | 343 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 audit/chacha20.c diff --git a/audit/chacha20.c b/audit/chacha20.c new file mode 100644 index 0000000..5358ca4 --- /dev/null +++ b/audit/chacha20.c @@ -0,0 +1,343 @@ +/* =================================================================== + * + * Copyright (c) 2014, Legrandin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * =================================================================== + */ + +#include "common.h" +#include "endianess.h" + +FAKE_INIT(chacha20) + +#define KEY_SIZE 32 + +typedef struct { + /** Initial state for the next iteration **/ + uint32_t h[16]; + size_t nonceSize; /** in bytes **/ + + /** How many bytes at the beginning of the key stream + * have already been used. + */ + unsigned usedKeyStream; + + uint8_t keyStream[sizeof(uint32_t)*16]; +} stream_state; + +#define ROTL(q, n) (((q) << (n)) | ((q) >> (32 - (n)))) + +#define QR(a, b, c, d) {\ + a+=b; d^=a; d=ROTL(d,16); \ + c+=d; b^=c; b=ROTL(b,12); \ + a+=b; d^=a; d=ROTL(d,8); \ + c+=d; b^=c; b=ROTL(b,7); \ +} + +EXPORT_SYM int chacha20_init(stream_state **pState, + const uint8_t *key, + size_t keySize, + const uint8_t *nonce, + size_t nonceSize) +{ + stream_state *hs; + unsigned i; + + if (NULL == pState || NULL == nonce) + return ERR_NULL; + + if (NULL == key || keySize != KEY_SIZE) + return ERR_KEY_SIZE; + + if (nonceSize != 8 && nonceSize != 12 && nonceSize != 16) + return ERR_NONCE_SIZE; + + *pState = hs = (stream_state*) calloc(1, sizeof(stream_state)); + if (NULL == hs) + return ERR_MEMORY; + + hs->h[0] = 0x61707865; + hs->h[1] = 0x3320646e; + hs->h[2] = 0x79622d32; + hs->h[3] = 0x6b206574; + + /** Move 256-bit/32-byte key into h[4..11] **/ + for (i=0; i<32/4; i++) { + hs->h[4+i] = LOAD_U32_LITTLE(key + 4*i); + } + + switch (nonceSize) { + case 8: { + /* + cccccccc cccccccc cccccccc cccccccc + kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk + kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk + bbbbbbbb BBBBBBBB nnnnnnnn nnnnnnnn + + c=constant k=key b=blockcount(low) B=blockcount(high) n=nonce + */ + + /** h[12] remains 0 (offset) **/ + /** h[13] remains 0 (offset) **/ + hs->h[14] = LOAD_U32_LITTLE(nonce + 0); + hs->h[15] = LOAD_U32_LITTLE(nonce + 4); + break; + } + case 12: { + /* + cccccccc cccccccc cccccccc cccccccc + kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk + kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk + bbbbbbbb nnnnnnnn nnnnnnnn nnnnnnnn + + c=constant k=key b=blockcount n=nonce + */ + + /** h[12] remains 0 (offset) **/ + hs->h[13] = LOAD_U32_LITTLE(nonce + 0); + hs->h[14] = LOAD_U32_LITTLE(nonce + 4); + hs->h[15] = LOAD_U32_LITTLE(nonce + 8); + break; + } + case 16: { + /* + cccccccc cccccccc cccccccc cccccccc + kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk + kkkkkkkk kkkkkkkk kkkkkkkk kkkkkkkk + nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn + + c=constant k=key n=nonce + */ + + hs->h[12] = LOAD_U32_LITTLE(nonce + 0); + hs->h[13] = LOAD_U32_LITTLE(nonce + 4); + hs->h[14] = LOAD_U32_LITTLE(nonce + 8); + hs->h[15] = LOAD_U32_LITTLE(nonce + 12); + break; + } + default: + return ERR_NONCE_SIZE; + } + + hs->nonceSize = nonceSize; + hs->usedKeyStream = sizeof hs->keyStream; + + return 0; +} + +EXPORT_SYM int chacha20_destroy(stream_state *state) +{ + if (NULL == state) + return ERR_NULL; + free(state); + return 0; +} + +static int chacha20_core(stream_state *state, uint32_t h[16]) +{ + unsigned i; + + memcpy(h, state->h, sizeof state->h); + + for (i=0; i<10; i++) { + /** Column round **/ + QR(h[0], h[4], h[ 8], h[12]); + QR(h[1], h[5], h[ 9], h[13]); + QR(h[2], h[6], h[10], h[14]); + QR(h[3], h[7], h[11], h[15]); + /** Diagonal round **/ + QR(h[0], h[5], h[10], h[15]); + QR(h[1], h[6], h[11], h[12]); + QR(h[2], h[7], h[ 8], h[13]); + QR(h[3], h[4], h[ 9], h[14]); + } + + for (i=0; i<16; i++) { + uint32_t sum; + + sum = h[i] + state->h[i]; + STORE_U32_LITTLE(state->keyStream + 4*i, sum); + } + + state->usedKeyStream = 0; + + switch (state->nonceSize) { + case 8: { + /** Nonce is 64 bits, counter is two words **/ + if (++state->h[12] == 0) { + if (++state->h[13] == 0) { + return ERR_MAX_DATA; + } + } + break; + } + case 12: { + /** Nonce is 96 bits, counter is one word **/ + if (++state->h[12] == 0) { + return ERR_MAX_DATA; + } + break; + } + case 16: { + /** Nonce is 192 bits, there is no counter as this is intended + * to be run once only (HChaCha20) **/ + break; + } + } + + return 0; +} + +EXPORT_SYM int chacha20_encrypt(stream_state *state, + const uint8_t in[], + uint8_t out[], + size_t len) +{ + if (NULL == state || NULL == in || NULL == out) + return ERR_NULL; + + if ((state->nonceSize != 8) && (state->nonceSize != 12)) + return ERR_NONCE_SIZE; + + while (len>0) { + unsigned keyStreamToUse; + unsigned i; + uint32_t h[16]; + + if (state->usedKeyStream == sizeof state->keyStream) { + int result; + + result = chacha20_core(state, h); + if (result) + return result; + } + + keyStreamToUse = (unsigned)MIN(len, sizeof state->keyStream - state->usedKeyStream); + for (i=0; ikeyStream[i + state->usedKeyStream]; + + len -= keyStreamToUse; + state->usedKeyStream += keyStreamToUse; + } + + return 0; +} + +EXPORT_SYM int chacha20_seek(stream_state *state, + unsigned long block_high, + unsigned long block_low, + unsigned offset) +{ + int result; + uint32_t h[16]; + + if (NULL == state) + return ERR_NULL; + + if ((state->nonceSize != 8) && (state->nonceSize != 12)) + return ERR_NONCE_SIZE; + + if (offset >= sizeof state->keyStream) + return ERR_MAX_OFFSET; + + if (state->nonceSize == 8) { + /** Nonce is 64 bits, counter is two words **/ + state->h[12] = (uint32_t)block_low; + state->h[13] = (uint32_t)block_high; + } else { + /** Nonce is 96 bits, counter is one word **/ + if (block_high > 0) { + return ERR_MAX_OFFSET; + } + state->h[12] = (uint32_t)block_low; + } + + result = chacha20_core(state, h); + if (result) + return result; + + state->usedKeyStream = offset; + + return 0; +} + +/* + * Based on https://tools.ietf.org/html/draft-arciszewski-xchacha-03 + */ +EXPORT_SYM int hchacha20(const uint8_t key[KEY_SIZE], + const uint8_t nonce16[16], /* First 16 bytes of the 24 byte nonce */ + uint8_t subkey[KEY_SIZE]) +{ + stream_state *pState; + uint32_t h[16]; + + if (NULL == key || NULL == nonce16 || NULL == subkey) { + return ERR_NULL; + } + + chacha20_init(&pState, key, KEY_SIZE, nonce16, 16); + if (NULL == pState) + return ERR_MEMORY; + + chacha20_core(pState, h); + /* We only keep first and last row from the new state */ + STORE_U32_LITTLE(subkey + 0, h[0]); + STORE_U32_LITTLE(subkey + 4, h[1]); + STORE_U32_LITTLE(subkey + 8, h[2]); + STORE_U32_LITTLE(subkey + 12, h[3]); + STORE_U32_LITTLE(subkey + 16, h[12]); + STORE_U32_LITTLE(subkey + 20, h[13]); + STORE_U32_LITTLE(subkey + 24, h[14]); + STORE_U32_LITTLE(subkey + 28, h[15]); + chacha20_destroy(pState); + + return 0; +} + +#ifdef PROFILE +int main(void) +{ + const unsigned data_size = 1024*1024; + const uint8_t key[32] = "12345678901234561234567890123456"; + const uint8_t nonce[8] = "12345678"; + stream_state *state; + uint8_t *data; + + data = malloc(data_size); + for (int i=0; i