diff --git a/sys/include/libsec.h b/sys/include/libsec.h index 4a4f8ee9..cf45b248 100644 --- a/sys/include/libsec.h +++ b/sys/include/libsec.h @@ -21,6 +21,7 @@ typedef struct AESstate AESstate; struct AESstate { ulong setup; + ulong offset; int rounds; int keybytes; uint ctrsz; @@ -38,12 +39,28 @@ void aes_decrypt(ulong rk[], int Nr, uchar ct[16], uchar pt[16]); void setupAESstate(AESstate *s, uchar key[], int keybytes, uchar *ivec); void aesCBCencrypt(uchar *p, int len, AESstate *s); void aesCBCdecrypt(uchar *p, int len, AESstate *s); +void aesCFBencrypt(uchar *p, int len, AESstate *s); +void aesCFBdecrypt(uchar *p, int len, AESstate *s); void aesCTRdecrypt(uchar *p, int len, AESstate *s); void aesCTRencrypt(uchar *p, int len, AESstate *s); void setupAESXCBCstate(AESstate *s); uchar* aesXCBCmac(uchar *p, int len, AESstate *s); +typedef struct AESGCMstate AESGCMstate; +struct AESGCMstate +{ + AESstate; + + ulong H[4]; + ulong M[16][256][4]; +}; + +void setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen); +void aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen); +void aesgcm_encrypt(uchar *p, ulong n, uchar *a, ulong na, uchar tag[16], AESGCMstate *s); +int aesgcm_decrypt(uchar *p, ulong n, uchar *a, ulong na, uchar tag[16], AESGCMstate *s); + /* * Blowfish Definitions */ @@ -442,3 +459,20 @@ PEMChain*readcertchain(char *filename); /* password-based key derivation function 2 (rfc2898) */ void pbkdf2_x(uchar *p, ulong plen, uchar *s, ulong slen, ulong rounds, uchar *d, ulong dlen, DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen); + +/* + * Diffie-Hellman key exchange + */ + +typedef struct DHstate DHstate; +struct DHstate +{ + mpint *g; /* base g */ + mpint *p; /* large prime */ + mpint *q; /* subgroup prime */ + mpint *x; /* random secret */ + mpint *y; /* public key y = g**x & p */ +}; + +mpint *dh_new(DHstate *s, mpint *p, mpint *q, mpint *g); +mpint *dh_finish(DHstate *s, mpint *y); diff --git a/sys/src/9/port/devtls.c b/sys/src/9/port/devtls.c index b980df24..7cbdbf59 100644 --- a/sys/src/9/port/devtls.c +++ b/sys/src/9/port/devtls.c @@ -1487,6 +1487,20 @@ initAESkey(Encalg *ea, Secret *s, uchar *p, uchar *iv) setupAESstate(s->enckey, p, ea->keylen, iv); } +static void +initAESGCMkey(Encalg *ea, Secret *s, uchar *p, uchar *iv) +{ + /* https://code.9front.org/hg/plan9front/file/808eb735fc45/sys/src/9/port/devtls.c#l1510 */ + s->enckey = smalloc(sizeof(AESGCMstate)); + s->aead_enc = aesgcm_aead_enc; + s->aead_dec = aesgcm_aead_dec; + s->maclen = 16; + s->recivlen = 8; + memmove(s->mackey, iv, ea->ivlen); + prng(s->mackey + ea->ivlen, s->recivlen); + setupAESGCMstate(s->enckey, p, ea->keylen, iv, ea->ivlen); +} + static void initclearenc(Encalg *, Secret *s, uchar *, uchar *) { @@ -1502,6 +1516,7 @@ static Encalg encrypttab[] = { "3des_ede_cbc", 3 * 8, 8, initDES3key }, { "aes_128_cbc", 128/8, 16, initAESkey }, { "aes_256_cbc", 256/8, 16, initAESkey }, + { "aes_128_gcm", 128/8, 4, initAESGCMkey }, { 0 } }; @@ -2093,6 +2108,44 @@ aesdec(Secret *sec, uchar *buf, int n) return (*sec->unpad)(buf, n, 16); } +static int +aesgcm_aead_enc(Secret *sec, uchar *a, int na, uchar *reciv, uchar *buf, int n) +{ + uchar iv[12]; + int i; + + memmove(iv, sec->mackey, 4+8); + for(i = 0; i < 8; i++) + iv[4+i] ^= a[i]; + memmove(reciv, iv+4, 8); + aesgcm_setiv(sec->enckey, iv, 12); + memset(iv, 0, sizeof iv); + aesgcm_encrypt(buf, n, a, na, buf+n, sec->enckey); + return n + sec->maclen; +} + +static int +aesgcm_aead_dec(Secret *sec, uchar *a, int na, uchar *reciv, uchar *buf, int n) +{ + uchar iv[12]; + + n -= sec->maclen; + if(n < 0) + return -1; + memmove(iv, sec->mackey, 4); + memmove(iv+4, reciv, 8); + aesgcm_setiv(sec->enckey, iv, 12); + memset(iv, 0, sizeof iv); + if(aesgcm_decrypt(buf, n, a, na, buf+n, sec->enckey) != 0) + return -1; + return n; +} + +static int +aesgcmdec(Secret *sec, uchar *buf, int n) +{ +} + static DigestState* nomac(uchar *, ulong, uchar *, ulong, uchar *, DigestState *) { diff --git a/sys/src/libsec/port/aesCFB.c b/sys/src/libsec/port/aesCFB.c new file mode 100644 index 00000000..f4d576a9 --- /dev/null +++ b/sys/src/libsec/port/aesCFB.c @@ -0,0 +1,63 @@ +#include +#include +#include + +void +aesCFBencrypt(uchar *p, int len, AESstate *s) +{ + int i; + u32int a, o; + + o = s->offset; + while(len > 0){ + if(o % 16){ + odd: + a = s->ivec[o++ % 16] ^= *p; + *p++ = a; + len--; + continue; + } + aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec); + if(len < 16 || ((p-(uchar*)0) & 3) != 0) + goto odd; + ((u32int*)p)[0] = (((u32int*)s->ivec)[0] ^= ((u32int*)p)[0]); + ((u32int*)p)[1] = (((u32int*)s->ivec)[1] ^= ((u32int*)p)[1]); + ((u32int*)p)[2] = (((u32int*)s->ivec)[2] ^= ((u32int*)p)[2]); + ((u32int*)p)[3] = (((u32int*)s->ivec)[3] ^= ((u32int*)p)[3]); + o += 16; + p += 16; + len -= 16; + } + s->offset = o; +} + +void +aesCFBdecrypt(uchar *p, int len, AESstate *s) +{ + int i; + u32int a, o; + + o = s->offset; + while(len > 0){ + if(o % 16){ + odd: + a = *p; + *p++ ^= s->ivec[o % 16]; + s->ivec[o++ % 16] = a; + len--; + continue; + } + aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec); + if(len < 16 || ((p-(uchar*)0) & 3) != 0) + goto odd; + for(i = 0; i < 4; i++){ + a = ((u32int*)p)[i]; + ((u32int*)p)[i] ^= ((u32int*)s->ivec)[i]; + ((u32int*)s->ivec)[i] = a; + } + o += 16; + p += 16; + len -= 16; + } + s->offset = o; +} diff --git a/sys/src/libsec/port/aesgcm.c b/sys/src/libsec/port/aesgcm.c new file mode 100644 index 00000000..c9c0e71f --- /dev/null +++ b/sys/src/libsec/port/aesgcm.c @@ -0,0 +1,209 @@ +#include +#include +#include + +extern int tsmemcmp(void*, void*, ulong); + +static void +load128(uchar b[16], ulong w[4]) +{ + w[0] = (ulong)b[15] | (ulong)b[14]<<8 | (ulong)b[13]<<16 | (ulong)b[12]<<24; + w[1] = (ulong)b[11] | (ulong)b[10]<<8 | (ulong)b[9]<<16 | (ulong)b[8]<<24; + w[2] = (ulong)b[7] | (ulong)b[6]<<8 | (ulong)b[5]<<16 | (ulong)b[4]<<24; + w[3] = (ulong)b[3] | (ulong)b[2]<<8 | (ulong)b[1]<<16 | (ulong)b[0]<<24; +} + +static void +store128(ulong w[4], uchar b[16]) +{ + ulong *e; + + b += 16; + e = w+4; + for(e = w+4; w < e; w++){ + *--b = *w; + *--b = *w>>8; + *--b = *w>>16; + *--b = *w>>24; + } +} + +static void +gfmul(ulong x[4], ulong y[4], ulong z[4]) +{ + long m, i; + + z[0] = z[1] = z[2] = z[3] = 0; + for(i = 127; i >= 0; i--){ + m = ((long)y[i>>5] << 31-(i&31)) >> 31; + z[0] ^= x[0] & m; + z[1] ^= x[1] & m; + z[2] ^= x[2] & m; + z[3] ^= x[3] & m; + m = ((long)x[0]<<31) >> 31; + x[0] = x[0]>>1 | x[1]<<31; + x[1] = x[1]>>1 | x[2]<<31; + x[2] = x[2]>>1 | x[3]<<31; + x[3] = x[3]>>1 ^ (0xe1000000 & m); + } +} + +static void +prepareM(ulong h[4], ulong m[16][256][4]) +{ + ulong x[4], i, j; + + for(i = 0; i < nelem(m); i++) + for(j = 0; j < nelem(m[i]); j++){ + x[0] = x[1] = x[2] = x[3] = 0; + x[i>>2] = j<<((i&3)<<3); + gfmul(x, h, m[i][j]); + } +} + +static void +ghash1(AESGCMstate *s, ulong x[4], ulong y[4]) +{ + ulong *xi, i; + + x[0] ^= y[0]; + x[1] ^= y[1]; + x[2] ^= y[2]; + x[3] ^= y[3]; + + y[0] = y[1] = y[2] = y[3] = 0; + for(i = 0; i < 16; i++){ + xi = s->M[i][(x[i>>2]>>((i&3)<<3))&0xff]; + y[0] ^= xi[0]; + y[1] ^= xi[1]; + y[2] ^= xi[2]; + y[3] ^= xi[3]; + } +} + +static void +ghashn(AESGCMstate *s, uchar *p, ulong len, ulong y[4]) +{ + uchar tmp[16]; + ulong x[4]; + + while(len >= 16){ + load128(p, x); + ghash1(s, x, y); + p += 16; + len -= 16; + } + if(len > 0){ + memmove(tmp, p, len); + memset(tmp+len, 0, 16-len); + load128(tmp, x); + ghash1(s, x, y); + } +} + +static ulong +aesxctr1(AESstate *s, uchar ctr[AESbsize], uchar *p, ulong len) +{ + uchar tmp[AESbsize]; + ulong i; + + aes_encrypt(s->ekey, s->rounds, ctr, tmp); + if(len > AESbsize) + len = AESbsize; + for(i = 0; i < len; i++) + p[i] ^= tmp[i]; + return len; +} + +static void +aesxctrn(AESstate *s, uchar *p, ulong len) +{ + uchar ctr[AESbsize]; + ulong i; + + memmove(ctr, s->ivec, AESbsize); + while(len > 0){ + for(i = AESbsize-1; i >= AESbsize-4; i--) + if(++ctr[i] != 0) + break; + if(aesxctr1(s, ctr, p, len) < AESbsize) + break; + p += AESbsize; + len -= AESbsize; + } +} + +void +aesgcm_setiv(AESGCMstate *s, uchar *iv, int ivlen) +{ + ulong l[4], y[4]; + + if(ivlen == 96/8){ + memmove(s->ivec, iv, ivlen); + memset(s->ivec+ivlen, 0, AESbsize-ivlen); + s->ivec[AESbsize-1] = 1; + return; + } + memset(y, 0, sizeof y); + ghashn(s, iv, ivlen, y); + l[0] = ivlen << 3; + l[1] = ivlen >> 29; + l[2] = l[3] = 0; + ghash1(s, l, y); + store128(y, s->ivec); +} + +void +setupAESGCMstate(AESGCMstate *s, uchar *key, int keylen, uchar *iv, int ivlen) +{ + setupAESstate(s, key, keylen, nil); + + memset(s->ivec, 0, AESbsize); + aes_encrypt(s->ekey, s->rounds, s->ivec, s->ivec); + load128(s->ivec, s->H); + memset(s->ivec, 0, AESbsize); + prepareM(s->H, s->M); + + if(iv != nil && ivlen > 0) + aesgcm_setiv(s, iv, ivlen); +} + +void +aesgcm_encrypt(uchar *p, ulong n, uchar *a, ulong na, uchar tag[16], AESGCMstate *s) +{ + ulong l[4], y[4]; + + memset(y, 0, sizeof y); + ghashn(s, a, na, y); + aesxctrn(s, p, n); + ghashn(s, p, n, y); + l[0] = n<<3; + l[1] = n>>29; + l[2] = na<<3; + l[3] = na>>29; + ghash1(s, l, y); + store128(y, tag); + aesxctr1(s, s->ivec, tag, 16); +} + +int +aesgcm_decrypt(uchar *p, ulong n, uchar *a, ulong na, uchar tag[16], AESGCMstate *s) +{ + ulong l[4], y[4]; + uchar tmp[16]; + + memset(y, 0, sizeof y); + ghashn(s, a, na, y); + ghash(s, p, n, y); + l[0] = n<<3; + l[1] = n>>29; + l[2] = na<<3; + l[3] = na>>29; + ghash1(s, l, y); + store128(y, tmp); + aesxctr1(s, s->ivec, tmp, 16); + if(tsmemcmp(tag, tmp, 16) != 0) + return -1; + aesxctrn(s, p, n); + return 0; +} diff --git a/sys/src/libsec/port/dh.c b/sys/src/libsec/port/dh.c new file mode 100644 index 00000000..5215f540 --- /dev/null +++ b/sys/src/libsec/port/dh.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +mpint * +dh_new(DHstate *s, mpint *p, mpint *q, mpint *g) +{ + mpint *pm1; + int n; + + memset(s, 0, sizeof *s); + if(mpcmp(g, mpone) <= 0) + return nil; + + n = mpsignif(p); + pm1 = mpnew(n); + mpsub(p, mpone, pm1); + s->p = mpcopy(p); + s->g = mpcopy(g); + s->q = mpcopy(q != nil ? q : pm1); + s->x = mpnew(npsignif(s->q)); + s->y = mpnew(n); + for(;;){ + mpnrand(s->q, genrandom, s->x); + mpexp(s->g, s->x, s->p, s->y); + if(mpcmp(s->y, mpone) > 0 && mpcmp(s->y, pm1) < 0) + break; + } + mpfree(pm1); + return s->y; +} + +mpint * +dh_finish(DHstate *s, mpint *y) +{ + mpint *k; + + k = nil; + if(y == nil || s->x == nil || s->p == nil || s->q == nil) + goto out; + if(mpcmp(y, mpone) <= 0) + goto out; + k = mpnew(mpsignif(s->p)); + mpsub(s->p, mpone, k); + if(mpcmp(y, k) >= 0){ + bad: + mpfree(k); + k = nil; + goto out; + } + if(mpcmp(s->q, k) < 0){ + mpexp(y, s->q, s->p, k); + if(mpcmp(k, mpone) != 0) + goto bad; + } + +out: + mpfree(s->p); + mpfree(s->q); + mpfree(s->g); + mpfree(s->x); + mpfree(s->y); + memset(s, 0, sizeof *s); + return k; +} diff --git a/sys/src/libsec/port/mkfile b/sys/src/libsec/port/mkfile index 16a1c8a7..e013a439 100644 --- a/sys/src/libsec/port/mkfile +++ b/sys/src/libsec/port/mkfile @@ -3,7 +3,7 @@ LIB=/$objtype/lib/libsec.a CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\ - aes.c blowfish.c chacha.c \ + aes.c aesCFB.c aesgcm.c blowfish.c chacha.c \ hmac.c md5.c md5block.c md4.c sha1.c sha1block.c\ sha2_64.c sha2_128.c sha2block64.c sha2block128.c\ sha1pickle.c md5pickle.c\ @@ -17,7 +17,9 @@ CFILES = des.c desmodes.c desECB.c desCBC.c des3ECB.c des3CBC.c\ egsign.c egverify.c \ dsagen.c dsaalloc.c dsaprivtopub.c dsasign.c dsaverify.c \ tlshand.c thumb.c readcert.c \ + dh.c\ pbkdf2.c\ + tsmemcmp.c\ ALLOFILES=${CFILES:%.c=%.$O} diff --git a/sys/src/libsec/port/tlshand.c b/sys/src/libsec/port/tlshand.c index a36f2674..ccfc7691 100644 --- a/sys/src/libsec/port/tlshand.c +++ b/sys/src/libsec/port/tlshand.c @@ -236,6 +236,7 @@ enum { TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0X0038, TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0X0039, TLS_DH_anon_WITH_AES_256_CBC_SHA = 0X003A, + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0X009C, CipherMax }; @@ -251,6 +252,7 @@ static Algs cipherAlgs[] = { {"3des_ede_cbc", "sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA}, {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA}, {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA} + {"aes_128_gcm", "sha256", 2*(16+12+SHA2_256dlen), TLS_RSA_WITH_AES_128_GCM_SHA256}, }; static uchar compressors[] = { diff --git a/sys/src/libsec/port/tsmemcmp.c b/sys/src/libsec/port/tsmemcmp.c new file mode 100644 index 00000000..cc9a7587 --- /dev/null +++ b/sys/src/libsec/port/tsmemcmp.c @@ -0,0 +1,25 @@ +#include +#include + +/* + * 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; +}