| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /* ===================================================================
- *
- * Copyright (c) 2014, Legrandin <helderijs@gmail.com>
- * 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; i<keyStreamToUse; i++)
- *out++ = *in++ ^ state->keyStream[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<data_size; i++) {
- data[i] = (uint8_t) i;
- }
- chacha20_init(&state, key, 32, nonce, 8);
- for (int i=0; i<1024; i++)
- chacha20_encrypt(state, data, data, data_size);
- chacha20_destroy(state);
- free(data);
- }
- #endif
|