libc: constant time implementation for encode(2) routines, fix base32

the string encoding functions touch secret key material
in a bunch of places (devtls, devcap), so make sure we do
not leak information by cache timing side channels, making
the encoding and decoding routines constant time.

we also expose the alphabets through encXchr()/decXchr()
functions so caller can find the end of a encoded string
before calling decode function (for libmp).

the base32 encoding was broken in several ways. inputs
lengths of len%5 == [2,3,4] had output truncated and
it was using non-standard alphabet. documenting the alphabet
change in the manpage.
This commit is contained in:
cinap_lenrek 2017-12-31 09:06:42 +01:00
parent ee89c82dd3
commit c039b52fc3
6 changed files with 186 additions and 90 deletions

View file

@ -48,17 +48,14 @@ encodefmt(Fmt *f)
// convert
out = buf;
switch(f->r){
case '<':
rv = enc32(out, len, b, ilen);
break;
case '[':
rv = enc64(out, len, b, ilen);
break;
case '<':
rv = enc32(out, len, b, ilen);
break;
case 'H':
rv = enc16(out, len, b, ilen);
if(rv >= 0 && (f->flags & FmtLong))
for(p = buf; *p; p++)
*p = tolower(*p);
break;
default:
rv = -1;
@ -67,6 +64,10 @@ encodefmt(Fmt *f)
if(rv < 0)
goto error;
if((f->flags & FmtLong) != 0 && f->r != '[')
for(p = buf; *p; p++)
*p = tolower(*p);
fmtstrcpy(f, buf);
if(buf != obuf)
free(buf);

View file

@ -1,6 +1,28 @@
#include <u.h>
#include <libc.h>
static char t16e[] = "0123456789ABCDEF";
#define between(x,min,max) (((min-1-x) & (x-max-1))>>8)
int
enc16chr(int o)
{
int c;
c = between(o, 0, 9) & ('0'+o);
c |= between(o, 10, 15) & ('A'+(o-10));
return c;
}
int
dec16chr(int c)
{
int o;
o = between(c, '0', '9') & (1+(c-'0'));
o |= between(c, 'A', 'F') & (1+10+(c-'A'));
o |= between(c, 'a', 'f') & (1+10+(c-'a'));
return o-1;
}
int
dec16(uchar *out, int lim, char *in, int n)
@ -10,14 +32,8 @@ dec16(uchar *out, int lim, char *in, int n)
uchar *eout = out + lim;
while(n-- > 0){
c = *in++;
if('0' <= c && c <= '9')
c = c - '0';
else if('a' <= c && c <= 'z')
c = c - 'a' + 10;
else if('A' <= c && c <= 'Z')
c = c - 'A' + 10;
else
c = dec16chr(*in++);
if(c < 0)
continue;
w = (w<<4) + c;
i++;
@ -44,8 +60,8 @@ enc16(char *out, int lim, uchar *in, int n)
c = *in++;
if(out + 2 >= eout)
goto exhausted;
*out++ = t16e[c>>4];
*out++ = t16e[c&0xf];
*out++ = enc16chr(c>>4);
*out++ = enc16chr(c&15);
}
exhausted:
*out = 0;

View file

@ -1,21 +1,44 @@
#include <u.h>
#include <libc.h>
#define between(x,min,max) (((min-1-x) & (x-max-1))>>8)
int
enc32chr(int o)
{
int c;
c = between(o, 0, 25) & ('A'+o);
c |= between(o, 26, 31) & ('2'+(o-26));
return c;
}
int
dec32chr(int c)
{
int o;
o = between(c, 'A', 'Z') & (1+(c-'A'));
o |= between(c, 'a', 'z') & (1+(c-'a'));
o |= between(c, '2', '7') & (1+26+(c-'2'));
return o-1;
}
int
dec32(uchar *dest, int ndest, char *src, int nsrc)
{
char *s, *tab;
uchar *start;
int i, u[8];
int i, j, u[8];
if(ndest+1 < (5*nsrc+7)/8)
return -1;
start = dest;
tab = "23456789abcdefghijkmnpqrstuvwxyz";
while(nsrc>=8){
for(i=0; i<8; i++){
s = strchr(tab,(int)src[i]);
u[i] = s ? s-tab : 0;
j = dec32chr(src[i]);
if(j < 0)
j = 0;
u[i] = j;
}
*dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
*dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
@ -29,8 +52,10 @@ dec32(uchar *dest, int ndest, char *src, int nsrc)
if(nsrc == 1 || nsrc == 3 || nsrc == 6)
return -1;
for(i=0; i<nsrc; i++){
s = strchr(tab,(int)src[i]);
u[i] = s ? s-tab : 0;
j = dec32chr(src[i]);
if(j < 0)
j = 0;
u[i] = j;
}
*dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
if(nsrc == 2)
@ -50,60 +75,57 @@ out:
int
enc32(char *dest, int ndest, uchar *src, int nsrc)
{
char *tab, *start;
char *start;
int j;
if(ndest <= (8*nsrc+4)/5 )
if(ndest <= (8*nsrc+4)/5)
return -1;
start = dest;
tab = "23456789abcdefghijkmnpqrstuvwxyz";
while(nsrc>=5){
j = (0x1f & (src[0]>>3));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1f & (src[1]>>1));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1f & (src[3]>>2));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1f & (src[4]));
*dest++ = tab[j];
*dest++ = enc32chr(j);
src += 5;
nsrc -= 5;
}
if(nsrc){
j = (0x1f & (src[0]>>3));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1c & (src[0]<<2));
if(nsrc == 1)
goto out;
j |= (0x03 & (src[1]>>6));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1f & (src[1]>>1));
*dest++ = enc32chr(j);
j = (0x10 & (src[1]<<4));
if(nsrc == 2)
goto out;
*dest++ = tab[j];
j = (0x10 & (src[1]<<4));
j |= (0x0f & (src[2]>>4));
*dest++ = enc32chr(j);
j = (0x1e & (src[2]<<1));
if(nsrc == 3)
goto out;
j |= (0x0f & (src[2]>>4));
*dest++ = tab[j];
j = (0x1e & (src[2]<<1));
if(nsrc == 4)
goto out;
j |= (0x01 & (src[3]>>7));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x1f & (src[3]>>2));
*dest++ = tab[j];
*dest++ = enc32chr(j);
j = (0x18 & (src[3]<<3));
out:
*dest++ = tab[j];
*dest++ = enc32chr(j);
}
*dest = 0;
return dest-start;

View file

@ -1,29 +1,33 @@
#include <u.h>
#include <libc.h>
enum {
INVAL= 255
};
#define between(x,min,max) (((min-1-x) & (x-max-1))>>8)
static uchar t64d[256] = {
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, 62,INVAL,INVAL,INVAL, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL
};
static char t64e[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int
enc64chr(int o)
{
int c;
c = between(o, 0, 25) & ('A'+o);
c |= between(o, 26, 51) & ('a'+(o-26));
c |= between(o, 52, 61) & ('0'+(o-52));
c |= between(o, 62, 62) & ('+');
c |= between(o, 63, 63) & ('/');
return c;
}
int
dec64chr(int c)
{
int o;
o = between(c, 'A', 'Z') & (1+(c-'A'));
o |= between(c, 'a', 'z') & (1+26+(c-'a'));
o |= between(c, '0', '9') & (1+52+(c-'0'));
o |= between(c, '+', '+') & (1+62);
o |= between(c, '/', '/') & (1+63);
return o-1;
}
int
dec64(uchar *out, int lim, char *in, int n)
@ -36,9 +40,8 @@ dec64(uchar *out, int lim, char *in, int n)
b24 = 0;
i = 0;
while(n-- > 0){
c = t64d[*(uchar*)in++];
if(c == INVAL)
c = dec64chr(*in++);
if(c < 0)
continue;
switch(i){
case 0:
@ -58,8 +61,8 @@ dec64(uchar *out, int lim, char *in, int n)
*out++ = b24>>16;
*out++ = b24>>8;
*out++ = b24;
i = -1;
break;
i = 0;
continue;
}
i++;
}
@ -89,34 +92,34 @@ enc64(char *out, int lim, uchar *in, int n)
char *e = out + lim;
for(i = n/3; i > 0; i--){
b24 = (*in++)<<16;
b24 |= (*in++)<<8;
b24 = *in++<<16;
b24 |= *in++<<8;
b24 |= *in++;
if(out + 4 >= e)
goto exhausted;
*out++ = t64e[(b24>>18)];
*out++ = t64e[(b24>>12)&0x3f];
*out++ = t64e[(b24>>6)&0x3f];
*out++ = t64e[(b24)&0x3f];
*out++ = enc64chr(b24>>18);
*out++ = enc64chr((b24>>12)&0x3f);
*out++ = enc64chr((b24>>6)&0x3f);
*out++ = enc64chr(b24&0x3f);
}
switch(n%3){
case 2:
b24 = (*in++)<<16;
b24 |= (*in)<<8;
b24 = *in++<<16;
b24 |= *in<<8;
if(out + 4 >= e)
goto exhausted;
*out++ = t64e[(b24>>18)];
*out++ = t64e[(b24>>12)&0x3f];
*out++ = t64e[(b24>>6)&0x3f];
*out++ = enc64chr(b24>>18);
*out++ = enc64chr((b24>>12)&0x3f);
*out++ = enc64chr((b24>>6)&0x3f);
*out++ = '=';
break;
case 1:
b24 = (*in)<<16;
b24 = *in<<16;
if(out + 4 >= e)
goto exhausted;
*out++ = t64e[(b24>>18)];
*out++ = t64e[(b24>>12)&0x3f];
*out++ = enc64chr(b24>>18);
*out++ = enc64chr((b24>>12)&0x3f);
*out++ = '=';
*out++ = '=';
break;