shithub: riscv

Download patch

ref: 254031cf7020f1b185c6d0af89c653a271e0ed01
parent: 90695e2eb24430a984e76afb6aaf1c10e2d4809e
author: cinap_lenrek <[email protected]>
date: Thu Nov 26 10:25:10 EST 2015

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.

--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -94,13 +94,18 @@
 		};
 	};
 	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 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);
--- a/sys/man/2/chacha
+++ b/sys/man/2/chacha
@@ -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_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 @@
 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 @@
 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
--- /dev/null
+++ b/sys/src/libsec/port/ccpoly.c
@@ -1,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;
+}
--- a/sys/src/libsec/port/chacha.c
+++ b/sys/src/libsec/port/chacha.c
@@ -54,10 +54,12 @@
 }
 
 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 @@
 		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 @@
 	}
 #endif
 
-	s->input[12]++;
+	if(++s->input[12] == 0 && s->ivwords == 2)
+		s->input[13]++;
 }
 
 void
--- a/sys/src/libsec/port/chachatest.c
+++ b/sys/src/libsec/port/chachatest.c
@@ -31,7 +31,7 @@
 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 @@
 	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,7 +70,7 @@
 	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);
@@ -61,10 +77,44 @@
 	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);
 }
--- a/sys/src/libsec/port/mkfile
+++ b/sys/src/libsec/port/mkfile
@@ -27,6 +27,8 @@
 	curve25519_dh.c\
 	pbkdf2.c\
 	hkdf.c\
+	ccpoly.c\
+	tsmemcmp.c\
 
 ALLOFILES=${CFILES:%.c=%.$O}
 
--- /dev/null
+++ b/sys/src/libsec/port/tsmemcmp.c
@@ -1,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;
+}