1implement Aescbc; 2 3# 4# broadly transliterated from the Plan 9 command 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "draw.m"; 11 12include "bufio.m"; 13 bufio: Bufio; 14 Iobuf: import bufio; 15 16include "keyring.m"; 17 kr: Keyring; 18 AESbsize, MD5dlen, SHA1dlen: import Keyring; 19 20include "arg.m"; 21 22Aescbc: module 23{ 24 init: fn(nil: ref Draw->Context, nil: list of string); 25}; 26 27# 28# encrypted file: v2hdr, 16 byte IV, AES-CBC(key, random || file), HMAC_SHA1(md5(key), AES-CBC(random || file)) 29# 30 31Checkpat: con "XXXXXXXXXXXXXXXX"; 32Checklen: con len Checkpat; 33Bufsize: con 4096; 34AESmaxkey: con 32; 35 36V2hdr: con "AES CBC SHA1 2\n"; 37 38bin: ref Iobuf; 39bout: ref Iobuf; 40stderr: ref Sys->FD; 41 42init(nil: ref Draw->Context, args: list of string) 43{ 44 sys = load Sys Sys->PATH; 45 kr = load Keyring Keyring->PATH; 46 bufio = load Bufio Bufio->PATH; 47 48 sys->pctl(Sys->FORKFD, nil); 49 stderr = sys->fildes(2); 50 arg := load Arg Arg->PATH; 51 arg->init(args); 52 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"); 53 encrypt := -1; 54 keyfile: string; 55 pass: string; 56 while((o := arg->opt()) != 0) 57 case o { 58 'd' or 'e' => 59 if(encrypt >= 0) 60 arg->usage(); 61 encrypt = o == 'e'; 62 'f' => 63 keyfile = arg->earg(); 64 'k' => 65 pass = arg->earg(); 66 * => 67 arg->usage(); 68 } 69 args = arg->argv(); 70 if(args != nil || encrypt < 0) 71 arg->usage(); 72 arg = nil; 73 74 bin = bufio->fopen(sys->fildes(0), Bufio->OREAD); 75 bout = bufio->fopen(sys->fildes(1), Bufio->OWRITE); 76 77 buf := array[Bufsize+SHA1dlen] of byte; # Checklen <= SHA1dlen 78 79 pwd: array of byte; 80 if(keyfile != nil){ 81 fd := sys->open(keyfile, Sys->OREAD); 82 if(fd == nil) 83 error(sys->sprint("can't open %q: %r", keyfile), "keyfile"); 84 n := sys->readn(fd, buf, len buf); 85 while(n > 0 && buf[n-1] == byte '\n') 86 n--; 87 if(n <= 0) 88 error("no key", "no key"); 89 pwd = buf[0:n]; 90 }else{ 91 if(pass == nil) 92 pass = readpassword("password"); 93 if(pass == nil) 94 error("no key", "no key"); 95 pwd = array of byte pass; 96 for(i := 0; i < len pass; i++) 97 pass[i] = 0; 98 } 99 key := array[AESmaxkey] of byte; 100 key2 := array[SHA1dlen] of byte; 101 dstate := kr->sha1(array of byte "aescbc file", 11, nil, nil); 102 kr->sha1(pwd, len pwd, key2, dstate); 103 for(i := 0; i < len pwd; i++) 104 pwd[i] = byte 0; 105 key[0:] = key2[0:MD5dlen]; 106 nkey := MD5dlen; 107 kr->md5(key, nkey, key2, nil); # protect key even if HMAC_SHA1 is broken 108 key2 = key2[0:MD5dlen]; 109 110 if(encrypt){ 111 Write(array of byte V2hdr, AESbsize); 112 genrandom(buf, 2*AESbsize); # CBC is semantically secure if IV is unpredictable. 113 aes := kr->aessetup(key[0:nkey], buf); # use first AESbsize bytes as IV 114 kr->aescbc(aes, buf[AESbsize:], AESbsize, Keyring->Encrypt); # use second AESbsize bytes as initial plaintext 115 Write(buf, 2*AESbsize); 116 dstate = kr->hmac_sha1(buf[AESbsize:], AESbsize, key2, nil, nil); 117 while((n := bin.read(buf, Bufsize)) > 0){ 118 kr->aescbc(aes, buf, n, Keyring->Encrypt); 119 Write(buf, n); 120 dstate = kr->hmac_sha1(buf, n, key2, nil, dstate); 121 if(n < Bufsize) 122 break; 123 } 124 if(n < 0) 125 error(sys->sprint("read error: %r"), "read error"); 126 kr->hmac_sha1(nil, 0, key2, buf, dstate); 127 Write(buf, SHA1dlen); 128 }else{ # decrypt 129 Read(buf, AESbsize); 130 if(string buf[0:AESbsize] == V2hdr){ 131 Read(buf, 2*AESbsize); # read IV and random initial plaintext 132 aes := kr->aessetup(key[0:nkey], buf); 133 dstate = kr->hmac_sha1(buf[AESbsize:], AESbsize, key2, nil, nil); 134 kr->aescbc(aes, buf[AESbsize:], AESbsize, Keyring->Decrypt); 135 Read(buf, SHA1dlen); 136 while((n := bin.read(buf[SHA1dlen:], Bufsize)) > 0){ 137 dstate = kr->hmac_sha1(buf, n, key2, nil, dstate); 138 kr->aescbc(aes, buf, n, Keyring->Decrypt); 139 Write(buf, n); 140 buf[0:] = buf[n:n+SHA1dlen]; # these bytes are not yet decrypted 141 } 142 kr->hmac_sha1(nil, 0, key2, buf[SHA1dlen:], dstate); 143 if(!eqbytes(buf, buf[SHA1dlen:], SHA1dlen)) 144 error("decrypted file failed to authenticate", "failed to authenticate"); 145 }else{ # compatibility with past mistake; assume we're decrypting secstore files 146 aes := kr->aessetup(key[0:AESbsize], buf); 147 Read(buf, Checklen); 148 kr->aescbc(aes, buf, Checklen, Keyring->Decrypt); 149 while((n := bin.read(buf[Checklen:], Bufsize)) > 0){ 150 kr->aescbc(aes, buf[Checklen:], n, Keyring->Decrypt); 151 Write(buf, n); 152 buf[0:] = buf[n:n+Checklen]; 153 } 154 if(string buf[0:Checklen] != Checkpat) 155 error("decrypted file failed to authenticate", "failed to authenticate"); 156 } 157 } 158 bout.flush(); 159} 160 161error(s: string, why: string) 162{ 163 bout.flush(); 164 sys->fprint(stderr, "aescbc: %s\n", s); 165 raise "fail:"+why; 166} 167 168eqbytes(a: array of byte, b: array of byte, n: int): int 169{ 170 if(len a < n || len b < n) 171 return 0; 172 for(i := 0; i < n; i++) 173 if(a[i] != b[i]) 174 return 0; 175 return 1; 176} 177 178Read(buf: array of byte, n: int) 179{ 180 if(bin.read(buf, n) != n){ 181 sys->fprint(sys->fildes(2), "aescbc: unexpectedly short read\n"); 182 raise "fail:read error"; 183 } 184} 185 186Write(buf: array of byte, n: int) 187{ 188 if(bout.write(buf, n) != n){ 189 sys->fprint(sys->fildes(2), "aescbc: write error: %r\n"); 190 raise "fail:write error"; 191 } 192} 193 194readpassword(prompt: string): string 195{ 196 cons := sys->open("/dev/cons", Sys->ORDWR); 197 if(cons == nil) 198 return nil; 199 stdin := bufio->fopen(cons, Sys->OREAD); 200 if(stdin == nil) 201 return nil; 202 cfd := sys->open("/dev/consctl", Sys->OWRITE); 203 if (cfd == nil || sys->fprint(cfd, "rawon") <= 0) 204 sys->fprint(stderr, "aescbc: warning: cannot hide typed password\n"); 205 s: string; 206L: 207 for(;;){ 208 sys->fprint(cons, "%s: ", prompt); 209 s = ""; 210 while ((c := stdin.getc()) >= 0){ 211 case c { 212 '\n' => 213 break L; 214 '\b' or 8r177 => 215 if(len s > 0) 216 s = s[0:len s - 1]; 217 'u' & 8r037 => 218 sys->fprint(cons, "\n"); 219 continue L; 220 * => 221 s[len s] = c; 222 } 223 } 224 } 225 sys->fprint(cons, "\n"); 226 return s; 227} 228 229genrandom(b: array of byte, n: int) 230{ 231 fd := sys->open("/dev/notquiterandom", Sys->OREAD); 232 if(fd == nil){ 233 sys->fprint(stderr, "aescbc: can't open /dev/notquiterandom: %r\n"); 234 raise "fail:random"; 235 } 236 if(sys->read(fd, b, n) != n){ 237 sys->fprint(stderr, "aescbc: can't read random numbers: %r\n"); 238 raise "fail:read random"; 239 } 240} 241