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: [email protected]
--- /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);
|