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:
parent
90695e2eb2
commit
254031cf70
7 changed files with 244 additions and 20 deletions
|
@ -94,13 +94,18 @@ struct Chachastate
|
|||
};
|
||||
};
|
||||
int rounds;
|
||||
int ivwords;
|
||||
};
|
||||
|
||||
void setupChachastate(Chachastate*, uchar*, ulong, uchar*, int);
|
||||
void chacha_setblock(Chachastate*, u32int);
|
||||
void setupChachastate(Chachastate*, uchar*, ulong, uchar*, ulong, int);
|
||||
void chacha_setiv(Chachastate *, uchar*);
|
||||
void chacha_setblock(Chachastate*, u64int);
|
||||
void chacha_encrypt(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
|
||||
*/
|
||||
|
@ -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,
|
||||
DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen);
|
||||
|
||||
/* timing safe memcmp() */
|
||||
int tsmemcmp(void*, void*, ulong);
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
.TH CHACHA 2
|
||||
.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
|
||||
.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)
|
||||
void setupChachastate(Chachastate *s, uchar key[], ulong keylen, uchar *iv, ulong ivlen, int rounds)
|
||||
.PP
|
||||
.B
|
||||
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)
|
||||
.PP
|
||||
.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
|
||||
.PP
|
||||
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
|
||||
.BR ChachaKeylen ,
|
||||
a
|
||||
.I nonce
|
||||
or initialisation vector of
|
||||
.B ChachaIVlen
|
||||
bytes (set to all zeros if the argument is nil),
|
||||
.I iv
|
||||
or nonce of
|
||||
.I ivlen
|
||||
bytes (can be
|
||||
.BR ChachaIVlen =12
|
||||
or 8, set to all zeros if the
|
||||
.I iv
|
||||
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
|
||||
With a key length of 256 bits (32 bytes), a nonce of 96 bits (12 bytes)
|
||||
and 20
|
||||
.IR rounds ,
|
||||
the function implements the Chacha20 encryption function of RFC7539.
|
||||
.PP
|
||||
|
@ -77,6 +89,37 @@ without modifying
|
|||
sets the Chacha block counter for the next encryption to
|
||||
.IR blockno ,
|
||||
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
|
||||
.B /sys/src/libsec
|
||||
.SH SEE ALSO
|
||||
|
|
84
sys/src/libsec/port/ccpoly.c
Normal file
84
sys/src/libsec/port/ccpoly.c
Normal 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;
|
||||
}
|
|
@ -54,10 +54,12 @@ load(u32int *d, uchar *s, int nw)
|
|||
}
|
||||
|
||||
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)
|
||||
sysfatal("invalid chacha key length");
|
||||
if(ivlen != 96/8 && ivlen != 64/8)
|
||||
sysfatal("invalid chacha iv length");
|
||||
if(rounds == 0)
|
||||
rounds = 20;
|
||||
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[8], key, 4);
|
||||
}
|
||||
s->ivwords = ivlen/sizeof(u32int);
|
||||
s->input[12] = 0;
|
||||
s->input[13] = 0;
|
||||
if(iv == nil){
|
||||
s->input[13] = 0;
|
||||
s->input[14] = 0;
|
||||
s->input[15] = 0;
|
||||
}else
|
||||
load(&s->input[13], iv, 3);
|
||||
chacha_setiv(s, iv);
|
||||
}
|
||||
|
||||
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;
|
||||
if(s->ivwords == 2)
|
||||
s->input[13] = blockno>>32;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -148,7 +159,8 @@ encryptblock(Chachastate *s, uchar *src, uchar *dst)
|
|||
}
|
||||
#endif
|
||||
|
||||
s->input[12]++;
|
||||
if(++s->input[12] == 0 && s->ivwords == 2)
|
||||
s->input[13]++;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -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, "
|
||||
"sunscreen would be it.";
|
||||
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,
|
||||
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,
|
||||
|
@ -42,10 +42,26 @@ uchar rfcref[3*ChachaBsize] = {
|
|||
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
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Chachastate s;
|
||||
uchar tag[16];
|
||||
int n;
|
||||
|
||||
ARGBEGIN{
|
||||
|
@ -54,17 +70,51 @@ main(int argc, char **argv)
|
|||
print("key:\n");
|
||||
printblock(rfckey, sizeof(rfckey));
|
||||
n = strlen(rfctext);
|
||||
setupChachastate(&s, rfckey, sizeof(rfckey), rfcnonce, 0);
|
||||
setupChachastate(&s, rfckey, sizeof(rfckey), rfcnonce, sizeof(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){
|
||||
if(memcmp(rfcout, rfcref, sizeof(rfcref)) != 0){
|
||||
print("failure of vision\n");
|
||||
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");
|
||||
exits(nil);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\
|
|||
curve25519_dh.c\
|
||||
pbkdf2.c\
|
||||
hkdf.c\
|
||||
ccpoly.c\
|
||||
tsmemcmp.c\
|
||||
|
||||
ALLOFILES=${CFILES:%.c=%.$O}
|
||||
|
||||
|
|
26
sys/src/libsec/port/tsmemcmp.c
Normal file
26
sys/src/libsec/port/tsmemcmp.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue