ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/auth/aescbc.b/
implement Aescbc;
#
# broadly transliterated from the Plan 9 command
#
include "sys.m";
sys: Sys;
include "draw.m";
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "keyring.m";
kr: Keyring;
AESbsize, MD5dlen, SHA1dlen: import Keyring;
include "arg.m";
Aescbc: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
#
# encrypted file: v2hdr, 16 byte IV, AES-CBC(key, random || file), HMAC_SHA1(md5(key), AES-CBC(random || file))
#
Checkpat: con "XXXXXXXXXXXXXXXX";
Checklen: con len Checkpat;
Bufsize: con 4096;
AESmaxkey: con 32;
V2hdr: con "AES CBC SHA1 2\n";
bin: ref Iobuf;
bout: ref Iobuf;
stderr: ref Sys->FD;
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
kr = load Keyring Keyring->PATH;
bufio = load Bufio Bufio->PATH;
sys->pctl(Sys->FORKFD, nil);
stderr = sys->fildes(2);
arg := load Arg Arg->PATH;
arg->init(args);
arg->setusage("auth/aescbc -d [-k key] [-f keyfile] <file.aes >clear.txt\n or: auth/aescbc -e [-k key] [-f keyfile] <clear.txt >file.aes");
encrypt := -1;
keyfile: string;
pass: string;
while((o := arg->opt()) != 0)
case o {
'd' or 'e' =>
if(encrypt >= 0)
arg->usage();
encrypt = o == 'e';
'f' =>
keyfile = arg->earg();
'k' =>
pass = arg->earg();
* =>
arg->usage();
}
args = arg->argv();
if(args != nil || encrypt < 0)
arg->usage();
arg = nil;
bin = bufio->fopen(sys->fildes(0), Bufio->OREAD);
bout = bufio->fopen(sys->fildes(1), Bufio->OWRITE);
buf := array[Bufsize+SHA1dlen] of byte; # Checklen <= SHA1dlen
pwd: array of byte;
if(keyfile != nil){
fd := sys->open(keyfile, Sys->OREAD);
if(fd == nil)
error(sys->sprint("can't open %q: %r", keyfile), "keyfile");
n := sys->readn(fd, buf, len buf);
while(n > 0 && buf[n-1] == byte '\n')
n--;
if(n <= 0)
error("no key", "no key");
pwd = buf[0:n];
}else{
if(pass == nil)
pass = readpassword("password");
if(pass == nil)
error("no key", "no key");
pwd = array of byte pass;
for(i := 0; i < len pass; i++)
pass[i] = 0;
}
key := array[AESmaxkey] of byte;
key2 := array[SHA1dlen] of byte;
dstate := kr->sha1(array of byte "aescbc file", 11, nil, nil);
kr->sha1(pwd, len pwd, key2, dstate);
for(i := 0; i < len pwd; i++)
pwd[i] = byte 0;
key[0:] = key2[0:MD5dlen];
nkey := MD5dlen;
kr->md5(key, nkey, key2, nil); # protect key even if HMAC_SHA1 is broken
key2 = key2[0:MD5dlen];
if(encrypt){
Write(array of byte V2hdr, AESbsize);
genrandom(buf, 2*AESbsize); # CBC is semantically secure if IV is unpredictable.
aes := kr->aessetup(key[0:nkey], buf); # use first AESbsize bytes as IV
kr->aescbc(aes, buf[AESbsize:], AESbsize, Keyring->Encrypt); # use second AESbsize bytes as initial plaintext
Write(buf, 2*AESbsize);
dstate = kr->hmac_sha1(buf[AESbsize:], AESbsize, key2, nil, nil);
while((n := bin.read(buf, Bufsize)) > 0){
kr->aescbc(aes, buf, n, Keyring->Encrypt);
Write(buf, n);
dstate = kr->hmac_sha1(buf, n, key2, nil, dstate);
if(n < Bufsize)
break;
}
if(n < 0)
error(sys->sprint("read error: %r"), "read error");
kr->hmac_sha1(nil, 0, key2, buf, dstate);
Write(buf, SHA1dlen);
}else{ # decrypt
Read(buf, AESbsize);
if(string buf[0:AESbsize] == V2hdr){
Read(buf, 2*AESbsize); # read IV and random initial plaintext
aes := kr->aessetup(key[0:nkey], buf);
dstate = kr->hmac_sha1(buf[AESbsize:], AESbsize, key2, nil, nil);
kr->aescbc(aes, buf[AESbsize:], AESbsize, Keyring->Decrypt);
Read(buf, SHA1dlen);
while((n := bin.read(buf[SHA1dlen:], Bufsize)) > 0){
dstate = kr->hmac_sha1(buf, n, key2, nil, dstate);
kr->aescbc(aes, buf, n, Keyring->Decrypt);
Write(buf, n);
buf[0:] = buf[n:n+SHA1dlen]; # these bytes are not yet decrypted
}
kr->hmac_sha1(nil, 0, key2, buf[SHA1dlen:], dstate);
if(!eqbytes(buf, buf[SHA1dlen:], SHA1dlen))
error("decrypted file failed to authenticate", "failed to authenticate");
}else{ # compatibility with past mistake; assume we're decrypting secstore files
aes := kr->aessetup(key[0:AESbsize], buf);
Read(buf, Checklen);
kr->aescbc(aes, buf, Checklen, Keyring->Decrypt);
while((n := bin.read(buf[Checklen:], Bufsize)) > 0){
kr->aescbc(aes, buf[Checklen:], n, Keyring->Decrypt);
Write(buf, n);
buf[0:] = buf[n:n+Checklen];
}
if(string buf[0:Checklen] != Checkpat)
error("decrypted file failed to authenticate", "failed to authenticate");
}
}
bout.flush();
}
error(s: string, why: string)
{
bout.flush();
sys->fprint(stderr, "aescbc: %s\n", s);
raise "fail:"+why;
}
eqbytes(a: array of byte, b: array of byte, n: int): int
{
if(len a < n || len b < n)
return 0;
for(i := 0; i < n; i++)
if(a[i] != b[i])
return 0;
return 1;
}
Read(buf: array of byte, n: int)
{
if(bin.read(buf, n) != n){
sys->fprint(sys->fildes(2), "aescbc: unexpectedly short read\n");
raise "fail:read error";
}
}
Write(buf: array of byte, n: int)
{
if(bout.write(buf, n) != n){
sys->fprint(sys->fildes(2), "aescbc: write error: %r\n");
raise "fail:write error";
}
}
readpassword(prompt: string): string
{
cons := sys->open("/dev/cons", Sys->ORDWR);
if(cons == nil)
return nil;
stdin := bufio->fopen(cons, Sys->OREAD);
if(stdin == nil)
return nil;
cfd := sys->open("/dev/consctl", Sys->OWRITE);
if (cfd == nil || sys->fprint(cfd, "rawon") <= 0)
sys->fprint(stderr, "aescbc: warning: cannot hide typed password\n");
s: string;
L:
for(;;){
sys->fprint(cons, "%s: ", prompt);
s = "";
while ((c := stdin.getc()) >= 0){
case c {
'\n' =>
break L;
'\b' or 8r177 =>
if(len s > 0)
s = s[0:len s - 1];
'u' & 8r037 =>
sys->fprint(cons, "\n");
continue L;
* =>
s[len s] = c;
}
}
}
sys->fprint(cons, "\n");
return s;
}
genrandom(b: array of byte, n: int)
{
fd := sys->open("/dev/notquiterandom", Sys->OREAD);
if(fd == nil){
sys->fprint(stderr, "aescbc: can't open /dev/notquiterandom: %r\n");
raise "fail:random";
}
if(sys->read(fd, b, n) != n){
sys->fprint(stderr, "aescbc: can't read random numbers: %r\n");
raise "fail:read random";
}
}