summaryrefslogtreecommitdiff
path: root/appl/cmd/auth/aescbc.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/auth/aescbc.b')
-rw-r--r--appl/cmd/auth/aescbc.b254
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";
+ }
+}