libsec: add chacha cipher (from charles forsyth)

This commit is contained in:
cinap_lenrek 2015-10-22 07:48:26 +02:00
parent 7effba9d98
commit 847f3a0cf5
5 changed files with 370 additions and 0 deletions

View file

@ -70,6 +70,37 @@ void bfCBCdecrypt(uchar*, int, BFstate*);
void bfECBencrypt(uchar*, int, BFstate*); void bfECBencrypt(uchar*, int, BFstate*);
void bfECBdecrypt(uchar*, int, BFstate*); void bfECBdecrypt(uchar*, int, BFstate*);
/*
* Chacha definitions
*/
enum
{
ChachaBsize= 64,
ChachaKeylen= 256/8,
ChachaIVlen= 96/8,
};
typedef struct Chachastate Chachastate;
struct Chachastate
{
union{
u32int input[16];
struct {
u32int constant[4];
u32int key[8];
u32int counter;
u32int iv[3];
};
};
int rounds;
};
void setupChachastate(Chachastate*, uchar*, ulong, uchar*, int);
void chacha_setblock(Chachastate*, u32int);
void chacha_encrypt(uchar*, ulong, Chachastate*);
void chacha_encrypt2(uchar*, uchar*, ulong, Chachastate*);
/* /*
* DES definitions * DES definitions
*/ */

93
sys/man/2/chacha Normal file
View file

@ -0,0 +1,93 @@
.TH CHACHA 2
.SH NAME
setupChachastate, chacha_setblock, chacha_encrypt, chacha_encrypt2 - chacha encryption
.SH SYNOPSIS
.B #include <u.h>
.br
.B #include <libc.h>
.br
.B #include <mp.h>
.br
.B #include <libsec.h>
.PP
.B
void setupChachastate(Chachastate *s, uchar key[], ulong keylen, uchar *nonce, int rounds)
.PP
.B
void chacha_encrypt(uchar *data, ulong len, Chachastate *s)
.PP
.B
void chacha_encrypt2(uchar *src, uchar *dst, ulong len, Chachastate *s)
.PP
.B
void chacha_setblock(Chachastate *s, u32int blockno)
.SH DESCRIPTION
.PP
Chacha is D J Berstein's symmetric stream cipher, as modified by RFC7539. It supports
keys of 256 bits (128 bits is supported here for special purposes). It has an underlying block size of 64 bytes
(named as constant
.BR ChachaBsize ).
.PP
.I SetupChachastate
takes a reference to a
.B Chachastate
structure, a
.I key
of
.I keylen
bytes, which should normally be
.BR ChachaKeylen ,
a
.I nonce
or initialisation vector of
.B ChachaIVlen
bytes (set to all zeros if the argument is nil),
and the number of
.I rounds
(set to the default of 20 if the argument is zero).
With a keylength of 256 bits (32 bytes) and 20
.IR rounds ,
the function implements the Chacha20 encryption function of RFC7539.
.PP
.I Chacha_encrypt
encrypts
.I len
bytes of
.I buf
in place using the
.B Chachastate
in
.IR s .
.I Len
can be any byte length.
Encryption and decryption are the same operation given the same starting state
.IR s .
.PP
.I Chacha_encrypt2
is similar, but encrypts
.I len
bytes of
.I src
into
.I dst
without modifying
.IR src .
.PP
.I Chacha_setblock
sets the Chacha block counter for the next encryption to
.IR blockno ,
allowing seeking in an encrypted stream.
.SH SOURCE
.B /sys/src/libsec
.SH SEE ALSO
.IR mp (2),
.IR aes (2),
.IR blowfish (2),
.IR des (2),
.IR dsa (2),
.IR elgamal (2),
.IR rc4 (2),
.IR rsa (2),
.IR sechash (2),
.IR prime (2),
.IR rand (2)

View file

@ -0,0 +1,175 @@
/*
Adapted from chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
modified for use in Plan 9 and Inferno (no algorithmic changes),
and including the changes to block number and nonce defined in RFC7539
*/
#include <u.h>
#include <libc.h>
#include <libsec.h>
enum{
Blockwords= ChachaBsize/sizeof(u32int)
};
/* little-endian data order */
#define GET4(p) ((((((p)[3]<<8) | (p)[2])<<8) | (p)[1])<<8 | (p)[0])
#define PUT4(p, v) (((p)[0]=v), (v>>=8), ((p)[1]=v), (v>>=8), ((p)[2]=v), (v>>=8), ((p)[3]=v))
#define ROTATE(v,c) ((u32int)((v) << (c)) | ((v) >> (32 - (c))))
#define QUARTERROUND(ia,ib,ic,id) { \
u32int a, b, c, d, t;\
a = x[ia]; b = x[ib]; c = x[ic]; d = x[id]; \
a += b; t = d^a; d = ROTATE(t,16); \
c += d; t = b^c; b = ROTATE(t,12); \
a += b; t = d^a; d = ROTATE(t, 8); \
c += d; t = b^c; b = ROTATE(t, 7); \
x[ia] = a; x[ib] = b; x[ic] = c; x[id] = d; \
}
#define ENCRYPT(s, x, y, d) {\
u32int v; \
uchar *sp, *dp; \
sp = (s); \
v = GET4(sp); \
v ^= (x)+(y); \
dp = (d); \
PUT4(dp, v); \
}
static uchar sigma[16] = "expand 32-byte k";
static uchar tau[16] = "expand 16-byte k";
static void
load(u32int *d, uchar *s, int nw)
{
int i;
for(i = 0; i < nw; i++, s+=4)
d[i] = GET4(s);
}
void
setupChachastate(Chachastate *s, uchar *key, ulong keylen, uchar *iv, int rounds)
{
if(keylen != 256/8 && keylen != 128/8)
sysfatal("invalid chacha key length");
if(rounds == 0)
rounds = 20;
s->rounds = rounds;
if(keylen == 256/8) { /* recommended */
load(&s->input[0], sigma, 4);
load(&s->input[4], key, 8);
}else{
load(&s->input[0], tau, 4);
load(&s->input[4], key, 4);
load(&s->input[8], key, 4);
}
s->input[12] = 0;
if(iv == nil){
s->input[13] = 0;
s->input[14] = 0;
s->input[15] = 0;
}else
load(&s->input[13], iv, 3);
}
void
chacha_setblock(Chachastate *s, u32int blockno)
{
s->input[12] = blockno;
}
static void
encryptblock(Chachastate *s, uchar *src, uchar *dst)
{
u32int x[Blockwords];
int i, rounds;
rounds = s->rounds;
x[0] = s->input[0];
x[1] = s->input[1];
x[2] = s->input[2];
x[3] = s->input[3];
x[4] = s->input[4];
x[5] = s->input[5];
x[6] = s->input[6];
x[7] = s->input[7];
x[8] = s->input[8];
x[9] = s->input[9];
x[10] = s->input[10];
x[11] = s->input[11];
x[12] = s->input[12];
x[13] = s->input[13];
x[14] = s->input[14];
x[15] = s->input[15];
for(i = rounds; i > 0; i -= 2) {
QUARTERROUND(0, 4, 8,12)
QUARTERROUND(1, 5, 9,13)
QUARTERROUND(2, 6,10,14)
QUARTERROUND(3, 7,11,15)
QUARTERROUND(0, 5,10,15)
QUARTERROUND(1, 6,11,12)
QUARTERROUND(2, 7, 8,13)
QUARTERROUND(3, 4, 9,14)
}
#ifdef FULL_UNROLL
ENCRYPT(src+0*4, x[0], s->input[0], dst+0*4);
ENCRYPT(src+1*4, x[1], s->input[1], dst+1*4);
ENCRYPT(src+2*4, x[2], s->input[2], dst+2*4);
ENCRYPT(src+3*4, x[3], s->input[3], dst+3*4);
ENCRYPT(src+4*4, x[4], s->input[4], dst+4*4);
ENCRYPT(src+5*4, x[5], s->input[5], dst+5*4);
ENCRYPT(src+6*4, x[6], s->input[6], dst+6*4);
ENCRYPT(src+7*4, x[7], s->input[7], dst+7*4);
ENCRYPT(src+8*4, x[8], s->input[8], dst+8*4);
ENCRYPT(src+9*4, x[9], s->input[9], dst+9*4);
ENCRYPT(src+10*4, x[10], s->input[10], dst+10*4);
ENCRYPT(src+11*4, x[11], s->input[11], dst+11*4);
ENCRYPT(src+12*4, x[12], s->input[12], dst+12*4);
ENCRYPT(src+13*4, x[13], s->input[13], dst+13*4);
ENCRYPT(src+14*4, x[14], s->input[14], dst+14*4);
ENCRYPT(src+15*4, x[15], s->input[15], dst+15*4);
#else
for(i=0; i<nelem(x); i+=4){
ENCRYPT(src, x[i], s->input[i], dst);
ENCRYPT(src+4, x[i+1], s->input[i+1], dst+4);
ENCRYPT(src+8, x[i+2], s->input[i+2], dst+8);
ENCRYPT(src+12, x[i+3], s->input[i+3], dst+12);
src += 16;
dst += 16;
}
#endif
s->input[12]++;
}
void
chacha_encrypt2(uchar *src, uchar *dst, ulong bytes, Chachastate *s)
{
uchar tmp[ChachaBsize];
for(; bytes >= ChachaBsize; bytes -= ChachaBsize){
encryptblock(s, src, dst);
src += ChachaBsize;
dst += ChachaBsize;
}
if(bytes > 0){
memmove(tmp, src, bytes);
encryptblock(s, tmp, tmp);
memmove(dst, tmp, bytes);
}
}
void
chacha_encrypt(uchar *buf, ulong bytes, Chachastate *s)
{
chacha_encrypt2(buf, buf, bytes, s);
}

View file

@ -0,0 +1,70 @@
#include <u.h>
#include <libc.h>
#include <libsec.h>
static void
printblock(uchar *b, usize n)
{
int i;
for(i=0; i+8<=n; i+=8){
print("%#.2ux %#.2ux %#.2ux %#.2ux %#.2ux %#.2ux %#.2ux %#.2ux\n",
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
b += 8;
}
if(i < n){
print("%#.2ux", *b++);
while(++i < n)
print(" %#.2ux", *b++);
print("\n");
}
}
/* test vector from RFC7539 */
uchar rfckey[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
uchar rfcnonce[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00};
u32int rfccount = 1;
char rfctext[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, "
"sunscreen would be it.";
uchar rfcout[3*ChachaBsize];
uchar rfcref[3*ChachaBsize] = {
0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
0x87, 0x4d
};
void
main(int argc, char **argv)
{
Chachastate s;
int n;
ARGBEGIN{
}ARGEND
print("rfc7539:\n");
print("key:\n");
printblock(rfckey, sizeof(rfckey));
n = strlen(rfctext);
setupChachastate(&s, rfckey, sizeof(rfckey), rfcnonce, 0);
chacha_setblock(&s, rfccount);
print("rfc in:\n");
printblock((uchar*)rfctext, n);
chacha_encrypt2((uchar*)rfctext, rfcout, n, &s);
print("rfc out:\n");
printblock(rfcout, n);
if(memcmp(rfcout, rfcref, sizeof(rfcout)) != 0){
print("failure of vision\n");
exits("wrong");
}
print("passed\n");
exits(nil);
}

View file

@ -9,6 +9,7 @@ CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\
sha1pickle.c md5pickle.c\ sha1pickle.c md5pickle.c\
poly1305.c\ poly1305.c\
rc4.c\ rc4.c\
chacha.c\
genrandom.c prng.c fastrand.c nfastrand.c\ genrandom.c prng.c fastrand.c nfastrand.c\
probably_prime.c smallprimetest.c genprime.c dsaprimes.c\ probably_prime.c smallprimetest.c genprime.c dsaprimes.c\
gensafeprime.c genstrongprime.c\ gensafeprime.c genstrongprime.c\