diff options
Diffstat (limited to 'appl/cmd/auth/aescbc.b')
| -rw-r--r-- | appl/cmd/auth/aescbc.b | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/appl/cmd/auth/aescbc.b b/appl/cmd/auth/aescbc.b new file mode 100644 index 00000000..c5b6e301 --- /dev/null +++ b/appl/cmd/auth/aescbc.b @@ -0,0 +1,254 @@ +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 := 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; +} + +readn(fd: ref Sys->FD, buf: array of byte, nb: int): int +{ + for(nr := 0; nr < nb;){ + n := sys->read(fd, buf[nr:], nb-nr); + if(n <= 0){ + if(nr == 0) + return n; + break; + } + nr += n; + } + return nr; +} + +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"; + } +} |
