The implementation of AES CTR mode had a flaw resulting in a divide by zero when incrementing the counter, if its size was 32 bits or larger. While fixing this, the temptation was irresistible to insert a fast path (avoiding mp arithmetic) for the special case of a 32-bit counter (which is the size mandated by RFC 3686 and used in ssh2). Reference: /n/sources/patch/aes-ctr Date: Mon Apr 9 13:35:14 GMT 2018 Signed-off-by: miller@hamnavoe.com --- /sys/src/libsec/port/aes.c Mon Apr 9 13:29:10 2018 +++ /sys/src/libsec/port/aes.c Mon Apr 9 13:29:03 2018 @@ -77,6 +77,7 @@ keybytes = AESmaxkey; memmove(s->key, key, keybytes); s->keybytes = keybytes; + s->ctrsz = 4; /* default counter size from rfc3686 */ s->rounds = aes_setup(s->ekey, s->dkey, s->key, keybytes * 8); if(ivec != nil) memmove(s->ivec, ivec, AESbsize); @@ -230,12 +231,23 @@ incrementCTR(uchar *p, uint ctrsz) { int len; + ulong c; uchar *ctr; mpint *mpctr, *mpctrsz; ctr = p + AESbsize - ctrsz; + if(ctrsz == 4){ + /* + * If counter is 32 bits (as in rfc3686 and ssh2) there's + * no need to use extended precision. + */ + c = 1 + (ctr[0]<<24 | ctr[1]<<16 | ctr[2]<<8 | ctr[3]); + ctr[0] = c>>24; ctr[1] = c>>16; ctr[2] = c>>8; ctr[3] = c; + return; + } mpctr = betomp(ctr, ctrsz, nil); - mpctrsz = itomp(1 << (ctrsz*8), nil); + mpctrsz = mpnew(ctrsz*8 + 1); + mpleft(mpone, ctrsz*8, mpctrsz); mpadd(mpctr, mpone, mpctr); mpmod(mpctr, mpctrsz, mpctr); len = mptobe(mpctr, ctr, ctrsz, nil);