1implement Crypt; 2 3# encrypt/decrypt from stdin to stdout 4 5include "sys.m"; 6 sys: Sys; 7 stderr: ref Sys->FD; 8include "draw.m"; 9include "keyring.m"; 10 keyring: Keyring; 11include "security.m"; 12 ssl: SSL; 13include "bufio.m"; 14include "msgio.m"; 15 msgio: Msgio; 16include "arg.m"; 17 18Crypt: module { 19 init: fn(nil: ref Draw->Context, argv: list of string); 20}; 21 22Ehungup: con "i/o on hungup channel"; 23 24ALGSTR: con "alg "; 25DEFAULTALG: con "md5/ideacbc"; 26usage() 27{ 28 sys->fprint(stderr, "usage: crypt [-?] [-d] [-k secret] [-f secretfile] [-a alg[/alg]]\n"); 29 sys->fprint(stderr, "available algorithms:\n"); 30 showalgs(stderr); 31 fail("bad usage"); 32} 33 34badmodule(m: string) 35{ 36 sys->fprint(stderr, "crypt: cannot load %s: %r\n", m); 37 fail("bad module"); 38} 39 40headers: con 1; 41verbose := 0; 42 43init(nil: ref Draw->Context, argv: list of string) 44{ 45 sys = load Sys Sys->PATH; 46 stderr = sys->fildes(2); 47 ssl = load SSL SSL->PATH; 48 if (ssl == nil) 49 badmodule(SSL->PATH); 50 keyring = load Keyring Keyring->PATH; 51 if (keyring == nil) 52 badmodule(SSL->PATH); 53 msgio = load Msgio Msgio->PATH; 54 msgio->init(); 55 56 arg := load Arg Arg->PATH; 57 if (arg == nil) 58 badmodule(SSL->PATH); 59 60 decrypt := 0; 61 secret: array of byte; 62 alg := DEFAULTALG; 63 64 arg->init(argv); 65 while ((opt := arg->opt()) != 0) { 66 case opt { 67 'd' => 68 decrypt = 1; 69 'k' => 70 if ((s := arg->arg()) == nil) 71 usage(); 72 secret = array of byte s; 73 'f' => 74 if ((f := arg->arg()) == nil) 75 usage(); 76 secret = readfile(f); 77 'a' => 78 if ((alg = arg->arg()) == nil) 79 usage(); 80 '?' => 81 showalgs(sys->fildes(1)); 82 return; 83 'v' => 84 verbose = 1; 85 * => 86 usage(); 87 } 88 } 89 argv = arg->argv(); 90 if (argv != nil) 91 usage(); 92 if(secret == nil) 93 secret = array of byte readpassword(); 94 sk := array[Keyring->SHA1dlen] of byte; 95 keyring->sha1(secret, len secret, sk, nil); 96 if (headers) { 97 # deal with header - the header encodes the algorithm along with the data. 98 if (decrypt) { 99 msg := msgio->getmsg(sys->fildes(0)); 100 if (msg != nil) 101 alg = string msg; 102 if (msg == nil || len alg < len ALGSTR || alg[0:len ALGSTR] != ALGSTR) 103 error("couldn't get decrypt algorithm"); 104 alg = alg[len ALGSTR:]; 105 } else { 106 msg := array of byte ("alg " + alg); 107 e := msgio->sendmsg(sys->fildes(1), msg, len msg); 108 if (e == -1) 109 error("couldn't write algorithm string"); 110 } 111 } 112 fd := docrypt(decrypt, alg, sk); 113 if (decrypt) { 114 # if decrypting, don't use stream, as we want to catch 115 # decryption or checksum errors when they happen. 116 buf := array[Sys->ATOMICIO] of byte; 117 stdout := sys->fildes(1); 118 while ((n := sys->read(fd, buf, len buf)) > 0) 119 sys->write(stdout, buf, n); 120 121 if (n == -1) { 122 err := sys->sprint("%r"); 123 if (err != Ehungup) 124 error("decryption failed: " + err); 125 } 126 } else { 127 stream(fd, sys->fildes(1), Sys->ATOMICIO); 128 } 129} 130 131docrypt(decrypt: int, alg: string, sk: array of byte): ref Sys->FD 132{ 133 if (verbose) 134 sys->fprint(stderr, "%scrypting with alg %s\n", (array[] of {"en", "de"})[decrypt!=0], alg); 135 (err, fds, nil, nil) := cryptpipe(decrypt, alg, sk); 136 if (err != nil) 137 error(err); 138 139 spawn stream(sys->fildes(0), fds[1], Sys->ATOMICIO); 140 return fds[0]; 141} 142 143# set up an encrypt/decrypt session; if decrypt is non-zero, then 144# decrypt, else encrypt. alg is the algorithm to use; sk is the 145# used as the secret key. 146# returns tuple (err, fds, cfd, dir) 147# where err is non-nil on failure; 148# otherwise fds is an array of two fds; writing to fds[1] will make 149# crypted/decrypted data available to be read on fds[0]. 150# dir is the ssl directory in question. 151cryptpipe(decrypt: int, alg: string, sk: array of byte): (string, array of ref Sys->FD, ref Sys->FD, string) 152{ 153 pfd := array[2] of ref Sys->FD; 154 if (sys->pipe(pfd) == -1) 155 return ("pipe failed", nil, nil, nil); 156 157 (err, c) := ssl->connect(pfd[1]); 158 if (err != nil) 159 return ("could not connect ssl: "+err, nil, nil, nil); 160 pfd[1] = nil; 161 err = ssl->secret(c, sk, sk); 162 if (err != nil) 163 return ("could not write secret: "+err, nil, nil, nil); 164 165 if (alg != nil) 166 if (sys->fprint(c.cfd, "alg %s", alg) == -1) 167 return (sys->sprint("bad algorithm %s: %r", alg), nil, nil, nil); 168 169 fds := array[2] of ref Sys->FD; 170 if (decrypt) { 171 fds[1] = pfd[0]; 172 fds[0] = c.dfd; 173 } else { 174 fds[1] = c.dfd; 175 fds[0] = pfd[0]; 176 } 177 return (nil, fds, c.cfd, c.dir); 178} 179 180algnames := array[] of {("crypt", "encalgs"), ("hash", "hashalgs")}; 181 182# find available algorithms and return as tuple of two lists: 183# (err, hashalgs, cryptalgs) 184algs(): (string, array of list of string) 185{ 186 (err, nil, nil, dir) := cryptpipe(0, nil, array[100] of byte); 187 if (err != nil) 188 return (err, nil); 189 alglists := array[len algnames] of list of string; 190 for (i := 0; i < len algnames; i++) { 191 (nil, f) := algnames[i]; 192 (nil, alglists[i]) = sys->tokenize(string readfile(dir + "/" + f), " "); 193 } 194 return (nil, alglists); 195} 196 197showalgs(fd: ref Sys->FD) 198{ 199 (err, alglists) := algs(); 200 if (err != nil) 201 error("cannot get algorithms: " + err); 202 for (j := 0; j < len alglists; j++) { 203 (name, nil) := algnames[j]; 204 sys->fprint(fd, "%s:", name); 205 for (l := alglists[j]; l != nil; l = tl l) 206 sys->fprint(fd, " %s", hd l); 207 sys->fprint(fd, "\n"); 208 } 209} 210 211readpassword(): string 212{ 213 bufio := load Bufio Bufio->PATH; 214 Iobuf: import bufio; 215 stdin := bufio->open("/dev/cons", Sys->OREAD); 216 217 cfd := sys->open("/dev/consctl", Sys->OWRITE); 218 if (cfd == nil || sys->fprint(cfd, "rawon") <= 0) 219 sys->fprint(stderr, "crypt: warning: cannot hide typed password\n"); 220 sys->fprint(stderr, "password: "); 221 s := ""; 222 while ((c := stdin.getc()) >= 0 && c != '\n'){ 223 case c { 224 '\b' => 225 if (len s > 0) 226 s = s[0:len s - 1]; 227 8r25 => # ^U 228 s = nil; 229 * => 230 s[len s] = c; 231 } 232 } 233 sys->fprint(stderr, "\n"); 234 return s; 235} 236 237stream(src, dst: ref Sys->FD, bufsize: int) 238{ 239 sys->stream(src, dst, bufsize); 240} 241 242readfile(f: string): array of byte 243{ 244 fd := sys->open(f, Sys->OREAD); 245 if (fd == nil) 246 error(sys->sprint("cannot read %s: %r", f)); 247 buf := array[8192] of byte; # >8K key? get real! 248 n := sys->read(fd, buf, len buf); 249 if (n <= 0) 250 return nil; 251 return buf[0:n]; 252} 253 254error(s: string) 255{ 256 sys->fprint(stderr, "crypt: %s\n", s); 257 fail("error"); 258} 259 260fail(e: string) 261{ 262 raise "fail: "+e; 263} 264