libsec: add chacha20 poly1305 aead, allow 64 bit iv's for chacha, add tsmemcmp()

chacha20 comes in two variants: ietf rfc7539, using 96 bit iv and 32 bit counter
and draft-agl-tls-chacha20poly1305 using 64 bit iv and a 64 bit counter. so
setupChachastate() now takes a ivlen argument which sets the mode.

add ccpoly_encrypt()/ccpoly_decrypt() routines.

to implement timing safe ccpoly_decrypt(), a constant time memcmp was needed, so
adding tsmemcmp() to libsec.
This commit is contained in:
cinap_lenrek 2015-11-26 15:25:10 +01:00
parent 90695e2eb2
commit 254031cf70
7 changed files with 244 additions and 20 deletions

View file

@ -94,13 +94,18 @@ struct Chachastate
}; };
}; };
int rounds; int rounds;
int ivwords;
}; };
void setupChachastate(Chachastate*, uchar*, ulong, uchar*, int); void setupChachastate(Chachastate*, uchar*, ulong, uchar*, ulong, int);
void chacha_setblock(Chachastate*, u32int); void chacha_setiv(Chachastate *, uchar*);
void chacha_setblock(Chachastate*, u64int);
void chacha_encrypt(uchar*, ulong, Chachastate*); void chacha_encrypt(uchar*, ulong, Chachastate*);
void chacha_encrypt2(uchar*, uchar*, ulong, Chachastate*); void chacha_encrypt2(uchar*, uchar*, ulong, Chachastate*);
void ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
int ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
/* /*
* DES definitions * DES definitions
*/ */
@ -505,3 +510,5 @@ void pbkdf2_x(uchar *p, ulong plen, uchar *s, ulong slen, ulong rounds, uchar *d
void hkdf_x(uchar *salt, ulong nsalt, uchar *info, ulong ninfo, uchar *key, ulong nkey, uchar *d, ulong dlen, void hkdf_x(uchar *salt, ulong nsalt, uchar *info, ulong ninfo, uchar *key, ulong nkey, uchar *d, ulong dlen,
DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen); DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen);
/* timing safe memcmp() */
int tsmemcmp(void*, void*, ulong);

View file

@ -1,17 +1,15 @@
.TH CHACHA 2 .TH CHACHA 2
.SH NAME .SH NAME
setupChachastate, chacha_setblock, chacha_encrypt, chacha_encrypt2 - chacha encryption setupChachastate, chacha_setblock, chacha_setiv, chacha_encrypt, chacha_encrypt2, ccpoly_encrypt, ccpoly_decrypt \- chacha encryption
.SH SYNOPSIS .SH SYNOPSIS
.B #include <u.h> .B #include <u.h>
.br .br
.B #include <libc.h> .B #include <libc.h>
.br .br
.B #include <mp.h>
.br
.B #include <libsec.h> .B #include <libsec.h>
.PP .PP
.B .B
void setupChachastate(Chachastate *s, uchar key[], ulong keylen, uchar *nonce, int rounds) void setupChachastate(Chachastate *s, uchar key[], ulong keylen, uchar *iv, ulong ivlen, int rounds)
.PP .PP
.B .B
void chacha_encrypt(uchar *data, ulong len, Chachastate *s) void chacha_encrypt(uchar *data, ulong len, Chachastate *s)
@ -20,7 +18,16 @@ void chacha_encrypt(uchar *data, ulong len, Chachastate *s)
void chacha_encrypt2(uchar *src, uchar *dst, ulong len, Chachastate *s) void chacha_encrypt2(uchar *src, uchar *dst, ulong len, Chachastate *s)
.PP .PP
.B .B
void chacha_setblock(Chachastate *s, u32int blockno) void chacha_setblock(Chachastate *s, u64int blockno)
.PP
.B
void chacha_setiv(Chachastate *s, uchar *iv);
.PP
.B
void ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
.PP
.B
int ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs);
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
Chacha is D J Berstein's symmetric stream cipher, as modified by RFC7539. It supports Chacha is D J Berstein's symmetric stream cipher, as modified by RFC7539. It supports
@ -38,14 +45,19 @@ of
bytes, which should normally be bytes, which should normally be
.BR ChachaKeylen , .BR ChachaKeylen ,
a a
.I nonce .I iv
or initialisation vector of or nonce of
.B ChachaIVlen .I ivlen
bytes (set to all zeros if the argument is nil), bytes (can be
.BR ChachaIVlen =12
or 8, set to all zeros if the
.I iv
argument is nil),
and the number of and the number of
.I rounds .I rounds
(set to the default of 20 if the argument is zero). (set to the default of 20 if the argument is zero).
With a keylength of 256 bits (32 bytes) and 20 With a key length of 256 bits (32 bytes), a nonce of 96 bits (12 bytes)
and 20
.IR rounds , .IR rounds ,
the function implements the Chacha20 encryption function of RFC7539. the function implements the Chacha20 encryption function of RFC7539.
.PP .PP
@ -77,6 +89,37 @@ without modifying
sets the Chacha block counter for the next encryption to sets the Chacha block counter for the next encryption to
.IR blockno , .IR blockno ,
allowing seeking in an encrypted stream. allowing seeking in an encrypted stream.
.PP
.I Chacha_setiv
sets the the initialization vector (nonce) to
.IR iv .
.PP
.I Ccpoly_encrypt
and
.I ccpoly_decrypt
implement authenticated encryption with associated data (AEAD)
using Chacha cipher and Poly1305 message authentication code
as specified in RFC7539.
These routines require a
.I Chachastate
that has been setup with a new (per key unique) initialization
vector (nonce) on each invocation. The referenced data
.IR dat [ ndat ]
is in-place encrypted or decrypted.
.I Ccpoly_encrypt
produces a 16 byte authentication
.IR tag ,
while
.I ccpoly_decrypt
verifies the
.IR tag ,
returning zero on success or negative on a mismatch.
The
.IR aad [ naad ]
arguments refer to the additional authenticated data
that is included in the
.I tag
calculation, but not encrypted.
.SH SOURCE .SH SOURCE
.B /sys/src/libsec .B /sys/src/libsec
.SH SEE ALSO .SH SEE ALSO

View file

@ -0,0 +1,84 @@
#include <u.h>
#include <libc.h>
#include <libsec.h>
static void
ccpolyotk(Chachastate *cs, DigestState *ds)
{
uchar otk[ChachaBsize];
memset(ds, 0, sizeof(*ds));
memset(otk, 0, 32);
chacha_setblock(cs, 0);
chacha_encrypt(otk, ChachaBsize, cs);
poly1305(nil, 0, otk, 32, nil, ds);
}
static void
ccpolymac(uchar *buf, ulong nbuf, DigestState *ds)
{
static uchar zeros[16] = {0};
ulong npad;
if(nbuf == 0)
return;
poly1305(buf, nbuf, nil, 0, nil, ds);
npad = nbuf % 16;
if(npad == 0)
return;
poly1305(zeros, 16 - npad, nil, 0, nil, ds);
}
static void
ccpolytag(ulong ndat, ulong naad, uchar tag[16], DigestState *ds)
{
uchar info[16];
info[0] = naad;
info[1] = naad>>8;
info[2] = naad>>16;
info[3] = naad>>24;
info[4] = 0;
info[5] = 0;
info[6] = 0;
info[7] = 0;
info[8] = ndat;
info[9] = ndat>>8;
info[10] = ndat>>16;
info[11] = ndat>>24;
info[12] = 0;
info[13] = 0;
info[14] = 0;
info[15] = 0;
poly1305(info, 16, nil, 0, tag, ds);
}
void
ccpoly_encrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs)
{
DigestState ds;
ccpolyotk(cs, &ds);
ccpolymac(aad, naad, &ds);
chacha_encrypt(dat, ndat, cs);
ccpolymac(dat, ndat, &ds);
ccpolytag(ndat, naad, tag, &ds);
}
int
ccpoly_decrypt(uchar *dat, ulong ndat, uchar *aad, ulong naad, uchar tag[16], Chachastate *cs)
{
DigestState ds;
uchar tmp[16];
ccpolyotk(cs, &ds);
ccpolymac(aad, naad, &ds);
ccpolymac(dat, ndat, &ds);
ccpolytag(ndat, naad, tmp, &ds);
if(tsmemcmp(tag, tmp, 16) != 0)
return -1;
chacha_encrypt(dat, ndat, cs);
return 0;
}

View file

@ -54,10 +54,12 @@ load(u32int *d, uchar *s, int nw)
} }
void void
setupChachastate(Chachastate *s, uchar *key, ulong keylen, uchar *iv, int rounds) setupChachastate(Chachastate *s, uchar *key, ulong keylen, uchar *iv, ulong ivlen, int rounds)
{ {
if(keylen != 256/8 && keylen != 128/8) if(keylen != 256/8 && keylen != 128/8)
sysfatal("invalid chacha key length"); sysfatal("invalid chacha key length");
if(ivlen != 96/8 && ivlen != 64/8)
sysfatal("invalid chacha iv length");
if(rounds == 0) if(rounds == 0)
rounds = 20; rounds = 20;
s->rounds = rounds; s->rounds = rounds;
@ -69,19 +71,28 @@ setupChachastate(Chachastate *s, uchar *key, ulong keylen, uchar *iv, int rounds
load(&s->input[4], key, 4); load(&s->input[4], key, 4);
load(&s->input[8], key, 4); load(&s->input[8], key, 4);
} }
s->ivwords = ivlen/sizeof(u32int);
s->input[12] = 0; s->input[12] = 0;
s->input[13] = 0;
if(iv == nil){ if(iv == nil){
s->input[13] = 0;
s->input[14] = 0; s->input[14] = 0;
s->input[15] = 0; s->input[15] = 0;
}else }else
load(&s->input[13], iv, 3); chacha_setiv(s, iv);
} }
void void
chacha_setblock(Chachastate *s, u32int blockno) chacha_setiv(Chachastate *s, uchar *iv)
{
load(&s->input[16 - s->ivwords], iv, s->ivwords);
}
void
chacha_setblock(Chachastate *s, u64int blockno)
{ {
s->input[12] = blockno; s->input[12] = blockno;
if(s->ivwords == 2)
s->input[13] = blockno>>32;
} }
static void static void
@ -148,7 +159,8 @@ encryptblock(Chachastate *s, uchar *src, uchar *dst)
} }
#endif #endif
s->input[12]++; if(++s->input[12] == 0 && s->ivwords == 2)
s->input[13]++;
} }
void void

View file

@ -31,7 +31,7 @@ u32int rfccount = 1;
char rfctext[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, " 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."; "sunscreen would be it.";
uchar rfcout[3*ChachaBsize]; uchar rfcout[3*ChachaBsize];
uchar rfcref[3*ChachaBsize] = { uchar rfcref[] = {
0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, 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, 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, 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
@ -42,10 +42,26 @@ uchar rfcref[3*ChachaBsize] = {
0x87, 0x4d 0x87, 0x4d
}; };
uchar ccpaad[] = {
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
};
uchar ccpkey[] = {
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
};
uchar ccpiv[] = {
0x07, 0x00, 0x00, 0x00,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
};
uchar ccptag[] = {
0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91,
};
void void
main(int argc, char **argv) main(int argc, char **argv)
{ {
Chachastate s; Chachastate s;
uchar tag[16];
int n; int n;
ARGBEGIN{ ARGBEGIN{
@ -54,17 +70,51 @@ main(int argc, char **argv)
print("key:\n"); print("key:\n");
printblock(rfckey, sizeof(rfckey)); printblock(rfckey, sizeof(rfckey));
n = strlen(rfctext); n = strlen(rfctext);
setupChachastate(&s, rfckey, sizeof(rfckey), rfcnonce, 0); setupChachastate(&s, rfckey, sizeof(rfckey), rfcnonce, sizeof(rfcnonce), 0);
chacha_setblock(&s, rfccount); chacha_setblock(&s, rfccount);
print("rfc in:\n"); print("rfc in:\n");
printblock((uchar*)rfctext, n); printblock((uchar*)rfctext, n);
chacha_encrypt2((uchar*)rfctext, rfcout, n, &s); chacha_encrypt2((uchar*)rfctext, rfcout, n, &s);
print("rfc out:\n"); print("rfc out:\n");
printblock(rfcout, n); printblock(rfcout, n);
if(memcmp(rfcout, rfcref, sizeof(rfcout)) != 0){ if(memcmp(rfcout, rfcref, sizeof(rfcref)) != 0){
print("failure of vision\n"); print("failure of vision\n");
exits("wrong"); exits("wrong");
} }
print("\n");
print("ccpoly key:\n");
printblock(ccpkey, sizeof(ccpkey));
print("ccpoly iv:\n");
printblock(ccpiv, sizeof(ccpiv));
setupChachastate(&s, ccpkey, sizeof(ccpkey), ccpiv, sizeof(ccpiv), 20);
memmove(rfcout, rfctext, sizeof(rfctext)-1);
ccpoly_encrypt(rfcout, sizeof(rfctext)-1, ccpaad, sizeof(ccpaad), tag, &s);
print("ccpoly cipher:\n");
printblock(rfcout, sizeof(rfctext)-1);
print("ccpoly tag:\n");
printblock(tag, sizeof(tag));
if(memcmp(tag, ccptag, sizeof(tag)) != 0){
print("bad ccpoly tag\n");
exits("wrong");
}
if(ccpoly_decrypt(rfcout, sizeof(rfctext)-1, ccpaad, sizeof(ccpaad), tag, &s) != 0){
print("ccpoly decryption failed\n");
exits("wrong");
}
if(memcmp(rfcout, rfctext, sizeof(rfctext)-1) != 0){
print("ccpoly bad decryption\n");
exits("wrong");
}
print("passed\n"); print("passed\n");
exits(nil); exits(nil);
} }

View file

@ -27,6 +27,8 @@ CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\
curve25519_dh.c\ curve25519_dh.c\
pbkdf2.c\ pbkdf2.c\
hkdf.c\ hkdf.c\
ccpoly.c\
tsmemcmp.c\
ALLOFILES=${CFILES:%.c=%.$O} ALLOFILES=${CFILES:%.c=%.$O}

View file

@ -0,0 +1,26 @@
#include <u.h>
#include <libc.h>
#include <libsec.h>
/*
* timing safe memcmp()
*/
int
tsmemcmp(void *a1, void *a2, ulong n)
{
int lt, gt, c1, c2, r, m;
uchar *s1, *s2;
r = m = 0;
s1 = a1;
s2 = a2;
while(n--){
c1 = *s1++;
c2 = *s2++;
lt = (c1 - c2) >> 8;
gt = (c2 - c1) >> 8;
r |= (lt - gt) & ~m;
m |= lt | gt;
}
return r;
}