static void aesctrespinit(Espcb *ecb, char *name, uint8_t *k, unsigned n) { uint8_t key[Aesblk], ivec[Aesblk]; int i; n = BITS2BYTES(n); if(n > Aeskeysz) n = Aeskeysz; memset(key, 0, sizeof(key)); memmove(key, k, n); for(i = 0; i < Aesblk; i++) ivec[i] = nrand(256); ecb->espalg = name; ecb->espblklen = Aesblk; ecb->espivlen = Aesblk; ecb->cipher = aesctrcipher; ecb->espstate = smalloc(sizeof(AESstate)); setupAESstate(ecb->espstate, key, n /* keybytes */, ivec); }
static int getfile(SConn *conn, char *gf, uchar **buf, ulong *buflen, uchar *key, int nkey) { int fd = -1; int i, n, nr, nw, len; char s[Maxmsg+1]; uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw, *bufw, *bufe; AESstate aes; DigestState *sha; if(strchr(gf, '/')){ fprint(2, "simple filenames, not paths like %s\n", gf); return -1; } memset(&aes, 0, sizeof aes); snprint(s, Maxmsg, "GET %s\n", gf); conn->write(conn, (uchar*)s, strlen(s)); /* get file size */ s[0] = '\0'; bufw = bufe = nil; if(readstr(conn, s) < 0){ fprint(2, "remote: %s\n", s); return -1; } len = atoi(s); if(len == -1){ fprint(2, "remote file %s does not exist\n", gf); return -1; }else if(len == -3){ fprint(2, "implausible filesize for %s\n", gf); return -1; }else if(len < 0){ fprint(2, "GET refused for %s\n", gf); return -1; } if(buf != nil){ *buflen = len - AESbsize - CHK; *buf = bufw = emalloc(len); bufe = bufw + len; } /* directory listing */ if(strcmp(gf,".")==0){ if(buf != nil) *buflen = len; for(i=0; i < len; i += n){ if((n = conn->read(conn, (uchar*)s, Maxmsg)) <= 0){ fprint(2, "empty file chunk\n"); return -1; } if(buf == nil) write(1, s, n); else memmove((*buf)+i, s, n); } return 0; } /* conn is already encrypted against wiretappers, but gf is also encrypted against server breakin. */ if(buf == nil && (fd =create(gf, OWRITE, 0600)) < 0){ fprint(2, "can't open %s: %r\n", gf); return -1; } ibr = ibw = ib; for(nr=0; nr < len;){ if((n = conn->read(conn, ibw, Maxmsg)) <= 0){ fprint(2, "empty file chunk n=%d nr=%d len=%d: %r\n", n, nr, len); return -1; } nr += n; ibw += n; if(!aes.setup){ /* first time, read 16 byte IV */ if(n < AESbsize){ fprint(2, "no IV in file\n"); return -1; } sha = sha1((uchar*)"aescbc file", 11, nil, nil); sha1(key, nkey, skey, sha); setupAESstate(&aes, skey, AESbsize, ibr); memset(skey, 0, sizeof skey); ibr += AESbsize; n -= AESbsize; } aesCBCdecrypt(ibw-n, n, &aes); n = ibw-ibr-CHK; if(n > 0){ if(buf == nil){ nw = write(fd, ibr, n); if(nw != n){ fprint(2, "write error on %s", gf); return -1; } }else{ assert(bufw+n <= bufe); memmove(bufw, ibr, n); bufw += n; } ibr += n; } memmove(ib, ibr, ibw-ibr); ibw = ib + (ibw-ibr); ibr = ib; } if(buf == nil) close(fd); n = ibw-ibr; if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){ fprint(2,"decrypted file failed to authenticate!\n"); return -1; } return 0; }
/* decrypted by the program aescbc.c. */ static int putfile(SConn *conn, char *pf, uchar *buf, ulong len, uchar *key, int nkey) { int i, n, fd, ivo, bufi, done; char s[Maxmsg]; uchar skey[SHA1dlen], b[CHK+Maxmsg], IV[AESbsize]; AESstate aes; DigestState *sha; /* create initialization vector */ srand(time(0)); /* doesn't need to be unpredictable */ for(i=0; i<AESbsize; i++) IV[i] = 0xff & rand(); sha = sha1((uchar*)"aescbc file", 11, nil, nil); sha1(key, nkey, skey, sha); setupAESstate(&aes, skey, AESbsize, IV); memset(skey, 0, sizeof skey); snprint(s, Maxmsg, "PUT %s\n", pf); conn->write(conn, (uchar*)s, strlen(s)); if(buf == nil){ /* get file size */ if((fd = open(pf, OREAD)) < 0){ fprint(2, "can't open %s: %r\n", pf); return -1; } len = seek(fd, 0, 2); seek(fd, 0, 0); } else { fd = -1; } if(len > MAXFILESIZE){ fprint(2, "implausible filesize %ld for %s\n", len, pf); return -1; } /* send file size */ snprint(s, Maxmsg, "%ld", len+AESbsize+CHK); conn->write(conn, (uchar*)s, strlen(s)); /* send IV and file+XXXXX in Maxmsg chunks */ ivo = AESbsize; bufi = 0; memcpy(b, IV, ivo); for(done = 0; !done; ){ if(buf == nil){ n = read(fd, b+ivo, Maxmsg-ivo); if(n < 0){ fprint(2, "read error on %s: %r\n", pf); return -1; } }else{ if((n = len - bufi) > Maxmsg-ivo) n = Maxmsg-ivo; memcpy(b+ivo, buf+bufi, n); bufi += n; } n += ivo; ivo = 0; if(n < Maxmsg){ /* EOF on input; append XX... */ memset(b+n, 'X', CHK); n += CHK; /* might push n>Maxmsg */ done = 1; } aesCBCencrypt(b, n, &aes); if(n > Maxmsg){ assert(done==1); conn->write(conn, b, Maxmsg); n -= Maxmsg; memmove(b, b+Maxmsg, n); } conn->write(conn, b, n); } if(buf == nil) close(fd); fprint(2, "saved %ld bytes\n", len); return 0; }
int main(int argc, char **argv) { int encrypt = 0; /* 0=decrypt, 1=encrypt */ int n, nkey, pass_stdin = 0, pass_nvram = 0; char *pass; unsigned char key[AESmaxkey], key2[SHA1dlen]; unsigned char buf[BUF+SHA1dlen]; /* assumption: CHK <= SHA1dlen */ AESstate aes; DigestState *dstate; Nvrsafe nvr; ARGBEGIN{ case 'e': encrypt = 1; break; case 'i': pass_stdin = 1; break; case 'n': pass_nvram = 1; break; }ARGEND; if(argc!=0){ fprint(2,"usage: %s -d < cipher.aes > clear.txt\n", argv0); fprint(2," or: %s -e < clear.txt > cipher.aes\n", argv0); exits("usage"); } Binit(&bin, 0, OREAD); Binit(&bout, 1, OWRITE); if(pass_stdin){ n = readn(3, buf, (sizeof buf)-1); if(n < 1) exits("usage: echo password |[3=1] auth/aescbc -i ..."); buf[n] = 0; while(buf[n-1] == '\n') buf[--n] = 0; }else if(pass_nvram){ if(readnvram(&nvr, 0) < 0) exits("readnvram: %r"); strecpy((char*)buf, (char*)buf+sizeof buf, (char*)nvr.config); n = strlen((char*)buf); }else{ pass = getpassm("aescbc key:"); n = strlen(pass); if(n >= BUF) exits("key too long"); strcpy((char*)buf, pass); memset(pass, 0, n); free(pass); } if(n <= 0) sysfatal("no key"); dstate = sha1((unsigned char*)"aescbc file", 11, nil, nil); sha1(buf, n, key2, dstate); memcpy(key, key2, 16); nkey = 16; md5(key, nkey, key2, 0); /* so even if HMAC_SHA1 is broken, encryption key is protected */ if(encrypt){ safewrite(v2hdr, AESbsize); genrandom(buf,2*AESbsize); /* CBC is semantically secure if IV is unpredictable. */ setupAESstate(&aes, key, nkey, buf); /* use first AESbsize bytes as IV */ aesCBCencrypt(buf+AESbsize, AESbsize, &aes); /* use second AESbsize bytes as initial plaintext */ safewrite(buf, 2*AESbsize); dstate = hmac_sha1(buf+AESbsize, AESbsize, key2, MD5dlen, 0, 0); for(;;){ n = Bread(&bin, buf, BUF); if(n < 0) sysfatal("read error"); aesCBCencrypt(buf, n, &aes); safewrite(buf, n); dstate = hmac_sha1(buf, n, key2, MD5dlen, 0, dstate); if(n < BUF) break; /* EOF */ } hmac_sha1(0, 0, key2, MD5dlen, buf, dstate); safewrite(buf, SHA1dlen); }else{ /* decrypt */ saferead(buf, AESbsize); if(memcmp(buf, v2hdr, AESbsize) == 0){ saferead(buf, 2*AESbsize); /* read IV and random initial plaintext */ setupAESstate(&aes, key, nkey, buf); dstate = hmac_sha1(buf+AESbsize, AESbsize, key2, MD5dlen, 0, 0); aesCBCdecrypt(buf+AESbsize, AESbsize, &aes); saferead(buf, SHA1dlen); while((n = Bread(&bin, buf+SHA1dlen, BUF)) > 0){ dstate = hmac_sha1(buf, n, key2, MD5dlen, 0, dstate); aesCBCdecrypt(buf, n, &aes); safewrite(buf, n); memmove(buf, buf+n, SHA1dlen); /* these bytes are not yet decrypted */ } hmac_sha1(0, 0, key2, MD5dlen, buf+SHA1dlen, dstate); if(memcmp(buf, buf+SHA1dlen, SHA1dlen) != 0) sysfatal("decrypted file failed to authenticate"); }else{ /* compatibility with past mistake */ // if file was encrypted with bad aescbc use this: // memset(key, 0, AESmaxkey); // else assume we're decrypting secstore files setupAESstate(&aes, key, AESbsize, buf); saferead(buf, CHK); aesCBCdecrypt(buf, CHK, &aes); while((n = Bread(&bin, buf+CHK, BUF)) > 0){ aesCBCdecrypt(buf+CHK, n, &aes); safewrite(buf, n); memmove(buf, buf+n, CHK); } if(memcmp(buf, "XXXXXXXXXXXXXXXX", CHK) != 0) sysfatal("decrypted file failed to authenticate"); } } exits(""); return 1; /* keep other compilers happy */ }
int convert(char **db, int len) { int i, nu, keydblen, keydboff, keydbaes; char *p = *db; keydblen = KEYDBLEN; keydboff = KEYDBOFF; keydbaes = len > 24 && memcmp(p, "AES KEYS", 8) == 0; if(keydbaes) { keydblen += AESKEYLEN; keydboff = 8+16; /* signature[8] + iv[16] */ } len -= keydboff; if(len % keydblen) { fprint(2, "%s: file odd length; not converting %d bytes\n", argv0, len % keydblen); len -= len % keydblen; } len += keydboff; if(keydbaes) { AESstate s; /* make sure we have aes key for decryption */ if(memcmp(okey.aes, zeros, AESKEYLEN) == 0) { fprint(2, "%s: no aes key in NVRAM\n", argv0); exits("no aes key"); } setupAESstate(&s, okey.aes, AESKEYLEN, zeros); aesCBCdecrypt((uchar*)p+8, len-8, &s); } else { DESstate s; uchar k[8]; des56to64((uchar*)okey.des, k); setupDESstate(&s, k, zeros); desCBCdecrypt((uchar*)p, len, &s); } nu = 0; for(i = keydboff; i < len; i += keydblen) { if (badname(&p[i])) { fprint(2, "%s: bad name %.30s... - aborting\n", argv0, &p[i]); exits("bad name"); } nu++; } if(verb) { for(i = keydboff; i < len; i += keydblen) print("%s\n", &p[i]); exits(nil); } if(convaes && !keydbaes) { char *s, *d; keydboff = 8+16; keydblen += AESKEYLEN; len = keydboff + keydblen*nu; p = realloc(p, len); if(p == nil) error("out of memory"); *db = p; s = p + KEYDBOFF + nu*KEYDBLEN; d = p + keydboff + nu*keydblen; for(i=0; i<nu; i++) { s -= KEYDBLEN; d -= keydblen; memmove(d, s, KEYDBLEN); memset(d + KEYDBLEN, 0, keydblen-KEYDBLEN); } keydbaes = 1; } genrandom((uchar*)p, keydboff); if(keydbaes) { AESstate s; memmove(p, "AES KEYS", 8); setupAESstate(&s, nkey.aes, AESKEYLEN, zeros); aesCBCencrypt((uchar*)p+8, len-8, &s); } else { DESstate s; uchar k[8]; des56to64((uchar*)nkey.des, k); setupDESstate(&s, k, zeros); desCBCencrypt((uchar*)p, len, &s); } return len; }