Skip to content

Commit 95bb58f

Browse files
committed
FEAT: new native chacha20 for cipher stream encryption/decryption
1 parent 7f0cace commit 95bb58f

File tree

7 files changed

+328
-0
lines changed

7 files changed

+328
-0
lines changed

NOTICE

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ ECDH:
1515
Copyright (c) 2014, Kenneth MacKay - https://github.com/kmackay/micro-ecc
1616
All rights reserved.
1717

18+
CHACHA20:
19+
Copyright (c) 2014, insane coder - http://chacha20.insanecoding.org/
20+
1821
MD5:
1922
This software contains code derived from the RSA Data Security
2023
Inc. MD5 Message-Digest Algorithm, including various

src/core/n-crypt.c

+85
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "sys-aes.h"
3434
#include "sys-rsa.h"
3535
#include "sys-dh.h"
36+
#include "sys-chacha20.h"
3637

3738
/***********************************************************************
3839
**
@@ -667,3 +668,87 @@ typedef struct {
667668
}
668669
return R_ARG1;
669670
}
671+
672+
673+
/***********************************************************************
674+
**
675+
*/ REBNATIVE(chacha20)
676+
/*
677+
// chacha20: native [
678+
// "Encrypt/decrypt data using ChaCha20 algorithm. Returns stream cipher context handle or encrypted/decrypted data."
679+
// /key "Provided only for the first time to get stream HANDLE!"
680+
// crypt-key [binary!] "Crypt key (16 or 32 bytes)."
681+
// nonce [binary!] "Initialization nonce (8 bytes)."
682+
// count [integer!] "A 32-bit block count parameter"
683+
// /stream
684+
// ctx [handle!] "Stream cipher context."
685+
// data [binary!] "Data to encrypt/decrypt."
686+
// /into
687+
// out [binary!] "Output buffer (NOT YET IMPLEMENTED)"
688+
// ]
689+
***********************************************************************/
690+
{
691+
REBOOL ref_key = D_REF(1);
692+
REBVAL *val_crypt_key = D_ARG(2);
693+
REBVAL *val_nonce = D_ARG(3);
694+
REBVAL *val_count = D_ARG(4);
695+
REBOOL ref_stream = D_REF(5);
696+
REBVAL *val_ctx = D_ARG(6);
697+
REBVAL *val_data = D_ARG(7);
698+
REBOOL ref_into = D_REF(8);
699+
700+
REBVAL *ret = D_RET;
701+
REBSER *ctx_ser;
702+
REBINT len;
703+
704+
if (ref_key) {
705+
//key defined - setup new context
706+
707+
len = VAL_LEN(val_crypt_key);
708+
709+
if (len != 16 && len != 32 && VAL_LEN(val_nonce) != 8) {
710+
return R_NONE;
711+
}
712+
713+
//making series from POOL so it will be GCed automaticaly
714+
ctx_ser = Make_Series(sizeof(chacha20_ctx), (REBCNT)1, FALSE);
715+
SERIES_TAIL(ctx_ser) = sizeof(chacha20_ctx);
716+
717+
chacha20_setup(
718+
(chacha20_ctx*)ctx_ser->data,
719+
VAL_BIN_AT(val_crypt_key),
720+
len,
721+
VAL_BIN_AT(val_nonce)
722+
);
723+
chacha20_counter_set((chacha20_ctx*)ctx_ser->data, VAL_INT64(val_count));
724+
725+
SET_HANDLE(ret, ctx_ser, SYM_CHACHA20, HANDLE_SERIES);
726+
// the ctx in the handle is released by GC once the handle is not referenced
727+
728+
} else if(ref_stream) {
729+
730+
ctx_ser = VAL_HANDLE_DATA(val_ctx);
731+
732+
if (VAL_HANDLE_TYPE(val_ctx) != SYM_CHACHA20 || ctx_ser == NULL || SERIES_TAIL(ctx_ser) != sizeof(chacha20_ctx)){
733+
Trap0(RE_INVALID_HANDLE);
734+
}
735+
736+
len = VAL_LEN(val_data);
737+
if (len == 0) return R_NONE;
738+
739+
REBYTE *data = VAL_BIN_AT(val_data);
740+
REBSER *binaryOut = Make_Binary(len);
741+
742+
chacha20_encrypt(
743+
(chacha20_ctx *)ctx_ser->data,
744+
(const uint8_t*)data,
745+
( uint8_t*)BIN_DATA(binaryOut),
746+
len
747+
);
748+
749+
SET_BINARY(ret, binaryOut);
750+
VAL_TAIL(ret) = len;
751+
752+
}
753+
return R_RET;
754+
}

src/core/u-chacha20.c

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
Copyright (C) 2014 insane coder (http://insanecoding.blogspot.com/, http://chacha20.insanecoding.org/)
3+
4+
Permission to use, copy, modify, and distribute this software for any
5+
purpose with or without fee is hereby granted, provided that the above
6+
copyright notice and this permission notice appear in all copies.
7+
8+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
16+
This implementation is intended to be simple, many optimizations can be performed.
17+
*/
18+
19+
#include <string.h>
20+
#include "sys-chacha20.h"
21+
22+
void chacha20_setup(chacha20_ctx *ctx, const uint8_t *key, size_t length, uint8_t nonce[8])
23+
{
24+
const char *constants = (length == 32) ? "expand 32-byte k" : "expand 16-byte k";
25+
26+
ctx->schedule[0] = LE(constants + 0);
27+
ctx->schedule[1] = LE(constants + 4);
28+
ctx->schedule[2] = LE(constants + 8);
29+
ctx->schedule[3] = LE(constants + 12);
30+
ctx->schedule[4] = LE(key + 0);
31+
ctx->schedule[5] = LE(key + 4);
32+
ctx->schedule[6] = LE(key + 8);
33+
ctx->schedule[7] = LE(key + 12);
34+
ctx->schedule[8] = LE(key + 16 % length);
35+
ctx->schedule[9] = LE(key + 20 % length);
36+
ctx->schedule[10] = LE(key + 24 % length);
37+
ctx->schedule[11] = LE(key + 28 % length);
38+
//Surprise! This is really a block cipher in CTR mode
39+
ctx->schedule[12] = 0; //Counter
40+
ctx->schedule[13] = 0; //Counter
41+
ctx->schedule[14] = LE(nonce+0);
42+
ctx->schedule[15] = LE(nonce+4);
43+
44+
ctx->available = 0;
45+
}
46+
47+
void chacha20_counter_set(chacha20_ctx *ctx, uint64_t counter)
48+
{
49+
ctx->schedule[12] = counter & UINT32_C(0xFFFFFFFF);
50+
ctx->schedule[13] = counter >> 32;
51+
ctx->available = 0;
52+
}
53+
54+
#define QUARTERROUND(x, a, b, c, d) \
55+
x[a] += x[b]; x[d] = ROTL32(x[d] ^ x[a], 16); \
56+
x[c] += x[d]; x[b] = ROTL32(x[b] ^ x[c], 12); \
57+
x[a] += x[b]; x[d] = ROTL32(x[d] ^ x[a], 8); \
58+
x[c] += x[d]; x[b] = ROTL32(x[b] ^ x[c], 7);
59+
60+
void chacha20_block(chacha20_ctx *ctx, uint32_t output[16])
61+
{
62+
uint32_t *const nonce = ctx->schedule+12; //12 is where the 128 bit counter is
63+
int i = 10;
64+
65+
memcpy(output, ctx->schedule, sizeof(ctx->schedule));
66+
67+
while (i--)
68+
{
69+
QUARTERROUND(output, 0, 4, 8, 12)
70+
QUARTERROUND(output, 1, 5, 9, 13)
71+
QUARTERROUND(output, 2, 6, 10, 14)
72+
QUARTERROUND(output, 3, 7, 11, 15)
73+
QUARTERROUND(output, 0, 5, 10, 15)
74+
QUARTERROUND(output, 1, 6, 11, 12)
75+
QUARTERROUND(output, 2, 7, 8, 13)
76+
QUARTERROUND(output, 3, 4, 9, 14)
77+
}
78+
for (i = 0; i < 16; ++i)
79+
{
80+
uint32_t result = output[i] + ctx->schedule[i];
81+
FROMLE((uint8_t *)(output+i), result);
82+
}
83+
84+
/*
85+
Official specs calls for performing a 64 bit increment here, and limit usage to 2^64 blocks.
86+
However, recommendations for CTR mode in various papers recommend including the nonce component for a 128 bit increment.
87+
This implementation will remain compatible with the official up to 2^64 blocks, and past that point, the official is not intended to be used.
88+
This implementation with this change also allows this algorithm to become compatible for a Fortuna-like construct.
89+
*/
90+
if (!++nonce[0] && !++nonce[1] && !++nonce[2]) { ++nonce[3]; }
91+
}
92+
93+
static inline void chacha20_xor(uint8_t *keystream, const uint8_t **in, uint8_t **out, size_t length)
94+
{
95+
uint8_t *end_keystream = keystream + length;
96+
do { *(*out)++ = *(*in)++ ^ *keystream++; } while (keystream < end_keystream);
97+
}
98+
99+
void chacha20_encrypt(chacha20_ctx *ctx, const uint8_t *in, uint8_t *out, size_t length)
100+
{
101+
if (length)
102+
{
103+
uint8_t *const k = (uint8_t *)ctx->keystream;
104+
105+
//First, use any buffered keystream from previous calls
106+
if (ctx->available)
107+
{
108+
size_t amount = MIN(length, ctx->available);
109+
chacha20_xor(k + (sizeof(ctx->keystream)-ctx->available), &in, &out, amount);
110+
ctx->available -= amount;
111+
length -= amount;
112+
}
113+
114+
//Then, handle new blocks
115+
while (length)
116+
{
117+
size_t amount = MIN(length, sizeof(ctx->keystream));
118+
chacha20_block(ctx, ctx->keystream);
119+
chacha20_xor(k, &in, &out, amount);
120+
length -= amount;
121+
ctx->available = sizeof(ctx->keystream) - amount;
122+
}
123+
}
124+
}
125+
126+
void chacha20_decrypt(chacha20_ctx *ctx, const uint8_t *in, uint8_t *out, size_t length)
127+
{
128+
chacha20_encrypt(ctx, in, out, length);
129+
}

src/include/sys-chacha20.h

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
Copyright (C) 2014 insane coder (http://insanecoding.blogspot.com/, http://chacha20.insanecoding.org/)
3+
4+
Permission to use, copy, modify, and distribute this software for any
5+
purpose with or without fee is hereby granted, provided that the above
6+
copyright notice and this permission notice appear in all copies.
7+
8+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#ifndef CHACHA20_SIMPLE_H
18+
#define CHACHA20_SIMPLE_H
19+
#include <stdint.h>
20+
21+
#define ROTL32(v, n) ((v) << (n)) | ((v) >> (32 - (n)))
22+
23+
#define LE(p) (((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))
24+
#define FROMLE(b, i) (b)[0] = i & 0xFF; (b)[1] = (i >> 8) & 0xFF; (b)[2] = (i >> 16) & 0xFF; (b)[3] = (i >> 24) & 0xFF;
25+
26+
#ifndef MIN
27+
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
28+
#endif
29+
30+
#ifdef __cplusplus
31+
extern "C"
32+
{
33+
#endif
34+
35+
typedef struct
36+
{
37+
uint32_t schedule[16];
38+
uint32_t keystream[16];
39+
size_t available;
40+
} chacha20_ctx;
41+
42+
//Call this to initilize a chacha20_ctx, must be called before all other functions
43+
void chacha20_setup(chacha20_ctx *ctx, const uint8_t *key, size_t length, uint8_t nonce[8]);
44+
45+
//Call this if you need to process a particular block number
46+
void chacha20_counter_set(chacha20_ctx *ctx, uint64_t counter);
47+
48+
//Raw keystream for the current block, convert output to uint8_t[] for individual bytes. Counter is incremented upon use
49+
void chacha20_block(chacha20_ctx *ctx, uint32_t output[16]);
50+
51+
//Encrypt an arbitrary amount of plaintext, call continuously as needed
52+
void chacha20_encrypt(chacha20_ctx *ctx, const uint8_t *in, uint8_t *out, size_t length);
53+
54+
//Decrypt an arbitrary amount of ciphertext. Actually, for chacha20, decryption is the same function as encryption
55+
void chacha20_decrypt(chacha20_ctx *ctx, const uint8_t *in, uint8_t *out, size_t length);
56+
57+
#ifdef __cplusplus
58+
}
59+
#endif
60+
61+
#endif

src/tests/run-tests.r3

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dt [ ;- delta time
2020
wrap load %units/mezz-crypt-test.r3
2121
wrap load %units/rc4-test.r3
2222
wrap load %units/aes-test.r3
23+
wrap load %units/chacha20-test.r3
2324
wrap load %units/rsa-test.r3
2425
wrap load %units/dh-test.r3
2526
wrap load %units/port-test.r3

src/tests/units/chacha20-test.r3

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
Rebol [
2+
Title: "Rebol3 ChaCha20 test script"
3+
Author: "Oldes, Peter W A Wood"
4+
File: %chacha20-test.r3
5+
Tabs: 4
6+
Needs: [%../quick-test-module.r3]
7+
]
8+
9+
~~~start-file~~~ "ChaCha20"
10+
11+
===start-group=== "ChaCha20 test vectors"
12+
foreach [test-id key nonce counter plain cipher] [
13+
1
14+
#{00000000000000000000000000000000}
15+
#{0000000000000000} 0
16+
#{00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000}
17+
#{89670952608364FD00B2F90936F031C8E756E15DBA04B8493D00429259B20F46CC04F111246B6C2CE066BE3BFB32D9AA0FDDFBC12123D4B9E44F34DCA05A103F}
18+
19+
;@@ http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04#appendix-A.2
20+
2
21+
#{0000000000000000000000000000000000000000000000000000000000000000}
22+
#{0000000000000000} 0
23+
#{00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000}
24+
#{76B8E0ADA0F13D90405D6AE55386BD28BDD219B8A08DED1AA836EFCC8B770DC7DA41597C5157488D7724E03FB8D84A376A43B8F41518A11CC387B669B2EE6586}
25+
26+
3
27+
#{0000000000000000000000000000000000000000000000000000000000000001}
28+
#{0000000000000002} 1
29+
#{416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e7472696275746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e20224945544620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c2073746174656d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c656374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207768696368206172652061646472657373656420746f}
30+
#{a3fbf07df3fa2fde4f376ca23e82737041605d9f4f4f57bd8cff2c1d4b7955ec2a97948bd3722915c8f3d337f7d370050e9e96d647b7c39f56e031ca5eb6250d4042e02785ececfa4b4bb5e8ead0440e20b6e8db09d881a7c6132f420e52795042bdfa7773d8a9051447b3291ce1411c680465552aa6c405b7764d5e87bea85ad00f8449ed8f72d0d662ab052691ca66424bc86d2df80ea41f43abf937d3259dc4b2d0dfb48a6c9139ddd7f76966e928e635553ba76c5c879d7b35d49eb2e62b0871cdac638939e25e8a1e0ef9d5280fa8ca328b351c3c765989cbcf3daa8b6ccc3aaf9f3979c92b3720fc88dc95ed84a1be059c6499b9fda236e7e818b04b0bc39c1e876b193bfe5569753f88128cc08aaa9b63d1a16f80ef2554d7189c411f5869ca52c5b83fa36ff216b9c1d30062bebcfd2dc5bce0911934fda79a86f6e698ced759c3ff9b6477338f3da4f9cd8514ea9982ccafb341b2384dd902f3d1ab7ac61dd29c6f21ba5b862f3730e37cfdc4fd806c22f221}
31+
32+
4
33+
#{1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0}
34+
#{0000000000000002} 42
35+
#{2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e642067696d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e6420746865206d6f6d65207261746873206f757467726162652e}
36+
#{62e6347f95ed87a45ffae7426f27a1df5fb69110044c0d73118effa95b01e5cf166d3df2d721caf9b21e5fb14c616871fd84c54f9d65b283196c7fe4f60553ebf39c6402c42234e32a356b3e764312a61a5532055716ead6962568f87d3f3f7704c6a8d1bcd1bf4d50d6154b6da731b187b58dfd728afa36757a797ac188d1}
37+
][
38+
--test-- join "ChaCha20 test " test-id
39+
--assert handle? k1: chacha20/key key nonce counter
40+
--assert cipher = chacha20/stream k1 plain
41+
42+
--assert handle? k2: chacha20/key key nonce counter
43+
--assert plain = chacha20/stream k2 cipher
44+
]
45+
46+
===end-group===
47+
48+
~~~end-file~~~

src/tools/file-base.r

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ core: [
106106
u-bigint.c ;needed for RSA which is needed in TLS protocol (HTTPS)
107107
u-bincode.c
108108
u-bmp.c
109+
u-chacha20.c
109110
u-compress.c
110111
u-dh.c
111112
u-dialect.c

0 commit comments

Comments
 (0)