diff options
Diffstat (limited to 'appl/cmd/crypt.b')
| -rw-r--r-- | appl/cmd/crypt.b | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/appl/cmd/crypt.b b/appl/cmd/crypt.b new file mode 100644 index 00000000..a478abfc --- /dev/null +++ b/appl/cmd/crypt.b @@ -0,0 +1,234 @@ +implement Crypt; + +# encrypt/decrypt from stdin to stdout + +include "sys.m"; + sys: Sys; + stderr: ref Sys->FD; +include "draw.m"; +include "keyring.m"; + keyring: Keyring; +include "security.m"; + ssl: SSL; +include "arg.m"; + +Crypt: module { + init: fn(nil: ref Draw->Context, argv: list of string); +}; + +Ehungup: con "i/o on hungup channel"; + +ALGSTR: con "alg "; +DEFAULTALG: con "md5/ideacbc"; +usage() +{ + sys->fprint(stderr, "usage: crypt [-?] [-d] [-k secret] [-f secretfile] [-a alg[/alg]]\n"); + sys->fprint(stderr, "available algorithms:\n"); + showalgs(stderr); + fail("bad usage"); +} + +badmodule(m: string) +{ + sys->fprint(stderr, "crypt: cannot load %s: %r\n", m); + fail("bad module"); +} + +headers: con 1; +verbose := 0; + +init(nil: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + ssl = load SSL SSL->PATH; + if (ssl == nil) + badmodule(SSL->PATH); + keyring = load Keyring Keyring->PATH; + if (keyring == nil) + badmodule(SSL->PATH); + + arg := load Arg Arg->PATH; + if (arg == nil) + badmodule(SSL->PATH); + + decrypt := 0; + secret: array of byte; + alg := DEFAULTALG; + + arg->init(argv); + while ((opt := arg->opt()) != 0) { + case opt { + 'd' => + decrypt = 1; + 'k' => + if ((s := arg->arg()) == nil) + usage(); + secret = array of byte s; + 'f' => + if ((f := arg->arg()) == nil) + usage(); + secret = readfile(f); + 'a' => + if ((alg = arg->arg()) == nil) + usage(); + '?' => + showalgs(sys->fildes(1)); + return; + 'v' => + verbose = 1; + * => + usage(); + } + } + argv = arg->argv(); + if (argv != nil) + usage(); + if(secret == nil){ + sys->fprint(stderr, "crypt: no secret given\n"); + usage(); + } + sk := array[Keyring->SHA1dlen] of byte; + keyring->sha1(secret, len secret, sk, nil); + if (headers) { + # deal with header - the header encodes the algorithm along with the data. + if (decrypt) { + msg := keyring->getmsg(sys->fildes(0)); + if (msg != nil) + alg = string msg; + if (msg == nil || len alg < len ALGSTR || alg[0:len ALGSTR] != ALGSTR) + error("couldn't get decrypt algorithm"); + alg = alg[len ALGSTR:]; + } else { + msg := array of byte ("alg " + alg); + e := keyring->sendmsg(sys->fildes(1), msg, len msg); + if (e == -1) + error("couldn't write algorithm string"); + } + } + fd := docrypt(decrypt, alg, sk); + if (decrypt) { + # if decrypting, don't use stream, as we want to catch + # decryption or checksum errors when they happen. + buf := array[Sys->ATOMICIO] of byte; + stdout := sys->fildes(1); + while ((n := sys->read(fd, buf, len buf)) > 0) + sys->write(stdout, buf, n); + + if (n == -1) { + err := sys->sprint("%r"); + if (err != Ehungup) + error("decryption failed: " + err); + } + } else { + stream(fd, sys->fildes(1), Sys->ATOMICIO); + } +} + +docrypt(decrypt: int, alg: string, sk: array of byte): ref Sys->FD +{ + if (verbose) + sys->fprint(stderr, "%scrypting with alg %s\n", (array[] of {"en", "de"})[decrypt!=0], alg); + (err, fds, nil, nil) := cryptpipe(decrypt, alg, sk); + if (err != nil) + error(err); + + spawn stream(sys->fildes(0), fds[1], Sys->ATOMICIO); + return fds[0]; +} + +# set up an encrypt/decrypt session; if decrypt is non-zero, then +# decrypt, else encrypt. alg is the algorithm to use; sk is the +# used as the secret key. +# returns tuple (err, fds, cfd, dir) +# where err is non-nil on failure; +# otherwise fds is an array of two fds; writing to fds[1] will make +# crypted/decrypted data available to be read on fds[0]. +# dir is the ssl directory in question. +cryptpipe(decrypt: int, alg: string, sk: array of byte): (string, array of ref Sys->FD, ref Sys->FD, string) +{ + pfd := array[2] of ref Sys->FD; + if (sys->pipe(pfd) == -1) + return ("pipe failed", nil, nil, nil); + + (err, c) := ssl->connect(pfd[1]); + if (err != nil) + return ("could not connect ssl: "+sys->sprint("%r"), nil, nil, nil); + pfd[1] = nil; + err = ssl->secret(c, sk, sk); + if (err != nil) + return ("could not write secret: "+sys->sprint("%r"), nil, nil, nil); + + if (alg != nil) + if (sys->fprint(c.cfd, "alg %s", alg) == -1) + return (sys->sprint("bad algorithm %s: %r", alg), nil, nil, nil); + + fds := array[2] of ref Sys->FD; + if (decrypt) { + fds[1] = pfd[0]; + fds[0] = c.dfd; + } else { + fds[1] = c.dfd; + fds[0] = pfd[0]; + } + return (nil, fds, c.cfd, c.dir); +} + +algnames := array[] of {("crypt", "encalgs"), ("hash", "hashalgs")}; + +# find available algorithms and return as tuple of two lists: +# (err, hashalgs, cryptalgs) +algs(): (string, array of list of string) +{ + (err, nil, nil, dir) := cryptpipe(0, nil, array[100] of byte); + if (err != nil) + return (err, nil); + alglists := array[len algnames] of list of string; + for (i := 0; i < len algnames; i++) { + (nil, f) := algnames[i]; + (nil, alglists[i]) = sys->tokenize(string readfile(dir + "/" + f), " "); + } + return (nil, alglists); +} + +showalgs(fd: ref Sys->FD) +{ + (err, alglists) := algs(); + if (err != nil) + error("cannot get algorithms: " + err); + for (j := 0; j < len alglists; j++) { + (name, nil) := algnames[j]; + sys->fprint(fd, "%s:", name); + for (l := alglists[j]; l != nil; l = tl l) + sys->fprint(fd, " %s", hd l); + sys->fprint(fd, "\n"); + } +} + +stream(src, dst: ref Sys->FD, bufsize: int) +{ + sys->stream(src, dst, bufsize); +} + +readfile(f: string): array of byte +{ + fd := sys->open(f, Sys->OREAD); + if (fd == nil) + error(sys->sprint("cannot read %s: %r", f)); + buf := array[8192] of byte; # >8K key? get real! + n := sys->read(fd, buf, len buf); + if (n <= 0) + return nil; + return buf[0:n]; +} + +error(s: string) +{ + sys->fprint(stderr, "crypt: %s\n", s); + fail("error"); +} + +fail(e: string) +{ + raise "fail: "+e; +} |
