diff options
-rw-r--r-- | src/cmd/aescbc.c | 144 | ||||
-rw-r--r-- | src/cmd/mkfile | 2 |
2 files changed, 145 insertions, 1 deletions
diff --git a/src/cmd/aescbc.c b/src/cmd/aescbc.c new file mode 100644 index 00000000..9f4bfff9 --- /dev/null +++ b/src/cmd/aescbc.c @@ -0,0 +1,144 @@ +/* encrypt file by writing + v2hdr, + 16byte initialization vector, + AES-CBC(key, random | file), + HMAC_SHA1(md5(key), AES-CBC(random | file)) + +With CBC, if the first plaintext block is 0, the first ciphertext block is +E(IV). Using the overflow technique adopted for compatibility with cryptolib +makes the last cipertext block decryptable. Hence the random prefix to file. +*/ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <mp.h> +#include <libsec.h> + +enum{ CHK = 16, BUF = 4096 }; + +uchar v2hdr[AESbsize+1] = "AES CBC SHA1 2\n"; +Biobuf bin; +Biobuf bout; + +void +safewrite(uchar *buf, int n) +{ + int i = Bwrite(&bout, buf, n); + + if(i == n) + return; + fprint(2, "write error\n"); + exits("write error"); +} + +void +saferead(uchar *buf, int n) +{ + int i = Bread(&bin, buf, n); + + if(i == n) + return; + fprint(2, "read error\n"); + exits("read error"); +} + +int +main(int argc, char **argv) +{ + int encrypt = 0; /* 0=decrypt, 1=encrypt */ + int n, nkey; + char *hex, *msg = nil; + uchar key[AESmaxkey], key2[MD5dlen]; + uchar buf[BUF+SHA1dlen]; /* assumption: CHK <= SHA1dlen */ + AESstate aes; + DigestState *dstate; + + if(argc!=2 || argv[1][0]!='-'){ + fprint(2,"usage: HEX=key %s -d < cipher.aes > clear.txt\n", argv[0]); + fprint(2," or: HEX=key %s -e < clear.txt > cipher.aes\n", argv[0]); + exits("usage"); + } + if(argv[1][1] == 'e') + encrypt = 1; + Binit(&bin, 0, OREAD); + Binit(&bout, 1, OWRITE); + + if((hex = getenv("HEX")) == nil) + hex = getpass("enter key: "); + nkey = 0; + if(hex != nil) + nkey = strlen(hex); + if(nkey == 0 || (nkey&1) || nkey>2*AESmaxkey){ + fprint(2,"key should be 32 hex digits\n"); + exits("key"); + } + nkey = dec16(key, sizeof key, hex, nkey); + 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); + while(1){ + n = Bread(&bin, buf, BUF); + if(n < 0){ + msg = "read error"; + goto Exit; + } + 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 */ + Bread(&bin, 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){ + msg = "decrypted file failed to authenticate!"; + goto Exit; + } + }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, 0, 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){ + msg = "decrypted file failed to authenticate"; + goto Exit; + } + } + } + Exit: + memset(key, 0, sizeof(key)); + memset(key2, 0, sizeof(key2)); + memset(buf, 0, sizeof(buf)); + if(msg != nil) + fprint(2, "%s\n", msg); + exits(msg); + return 1; /* gcc */ +} diff --git a/src/cmd/mkfile b/src/cmd/mkfile index 87ca653e..480987d6 100644 --- a/src/cmd/mkfile +++ b/src/cmd/mkfile @@ -6,7 +6,7 @@ SHORTLIB=sec fs mux regexp9 draw thread bio 9 <$PLAN9/src/mkmany -BUGGERED='CVS|faces|factotum|mailfs|mk|upas|vac|venti|tcs' +BUGGERED='CVS|faces|factotum|mailfs|mk|upas|vac|venti|tcs|lex' DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERED)$"` <$PLAN9/src/mkdirs |