From f539ce2d08335740e1027430e8cb424db8b36555 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 13 Dec 1995 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1995-12-13 --- port/devssl.c | 733 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 733 insertions(+) create mode 100644 port/devssl.c diff --git a/port/devssl.c b/port/devssl.c new file mode 100644 index 0000000000000000000000000000000000000000..7a7194d47a6998332beb036d3bfe549f8cbbc82f --- /dev/null +++ b/port/devssl.c @@ -0,0 +1,733 @@ +/* + * template for making a new device + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include + +#include "devtab.h" + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock; + + void *state; /* encryption state */ + int slen; /* hash data length */ + uchar *secret; /* secret */ + ulong mid; /* message id */ +}; + +enum +{ + /* connection states */ + Algwait= 0, /* waiting for user to write algorithm */ + Fdwait= 1, /* waiting for user to write fd */ + Secretinwait= 2, /* waiting for user to write input secret */ + Secretoutwait= 3, /* waiting for user to write output secret */ + Established= 4, + Closed= 5, + + /* encryption algorithms */ + DESCBC= 1, +}; + +typedef struct Dstate Dstate; +struct Dstate +{ + Chan *c; /* io channel */ + uchar state; /* state of connection */ + uchar encryptalg; /* encryption algorithm */ + ushort blocking; /* blocking length */ + + ushort diglen; /* length of digest */ + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ + + int max; /* maximum unpadded data per msg */ + int maxpad; /* maximum padded data per msg */ + + /* input side */ + OneWay in; + Block *processed; + Block *unprocessed; + + /* output side */ + OneWay out; +}; + +enum +{ + Maxdmsg= 1<<16, +}; + +enum{ + Qdir, + Qclone, +}; +Dirtab digesttab[]={ + "digestclone", {Qclone, 0}, 0, 0600, +}; +#define Ndigesttab (sizeof(digesttab)/sizeof(Dirtab)) + +/* a circular list of random numbers */ +typedef struct +{ + uchar *rp; + uchar *wp; + uchar buf[1024]; + uchar *ep; +} Randq; +Randq randq; + +void producerand(void); + +void +sslreset(void) +{ + randq.ep = randq.buf + sizeof(randq.buf); + randq.rp = randq.wp = randq.buf; +} + +void +sslinit(void) +{ +} + +Chan * +sslattach(char *spec) +{ + return devattach('D', spec); +} + +Chan * +sslclone(Chan *c, Chan *nc) +{ + return devclone(c, nc); +} + +int +sslwalk(Chan *c, char *name) +{ + return devwalk(c, name, digesttab, Ndigesttab, devgen); +} + +void +sslstat(Chan *c, char *db) +{ + devstat(c, db, digesttab, Ndigesttab, devgen); +} + +Chan * +sslopen(Chan *c, int omode) +{ + Dstate *s; + + switch(c->qid.path & ~CHDIR){ + case Qclone: + s = smalloc(sizeof(Dstate)); + memset(s, 0, sizeof(*s)); + s->state = Algwait; + c->aux = s; + break; + } + return devopen(c, omode, digesttab, Ndigesttab, devgen); +} + +void +sslcreate(Chan *c, char *name, int omode, ulong perm) +{ + USED(c, name, omode, perm); + error(Eperm); +} + +void +sslremove(Chan *c) +{ + USED(c); + error(Eperm); +} + +void +sslwstat(Chan *c, char *dp) +{ + USED(c, dp); + error(Eperm); +} + +static void +dighangup(Dstate *s) +{ + Block *b; + + qlock(&s->in); + for(b = s->processed; b; b = s->processed){ + s->processed = b->next; + freeb(b); + } + if(s->unprocessed){ + freeb(s->unprocessed); + s->unprocessed = 0; + } + s->state = Closed; + qunlock(&s->in); +} + +void +sslclose(Chan *c) +{ + Dstate *s; + + if(c->aux){ + s = c->aux; + dighangup(s); + if(s->c) + close(s->c); + if(s->in.secret) + free(s->in.secret); + if(s->out.secret) + free(s->out.secret); + free(s); + } +} + +long +sslread(Chan *c, void *a, long n, ulong offset) +{ + Block *b; + + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, digesttab, Ndigesttab, devgen); + } + + b = sslbread(c, n, offset); + + if(waserror()){ + freeb(b); + nexterror(); + } + + n = BLEN(b); + memmove(a, b->rp, n); + freeb(b); + + poperror(); + + return n; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + w->secret = smalloc(n); + memmove(w->secret, secret, n); + w->slen = n; + w->mid = 0; + + switch(s->encryptalg){ + case DESCBC: + w->state = smalloc(sizeof(DESstate)); + setupDESstate(w->state, secret, 0); + break; + case DESCBC: + w->state = smalloc(sizeof(DESstate)); + setupDESstate(w->state, secret, secret+8); + break; + } +} + +long +sslwrite(Chan *c, char *a, long n, ulong offset) +{ + Dstate *s; + Block *b; + int m, sofar; + char buf[32]; + + switch(c->qid.path & ~CHDIR){ + case Qclone: + break; + default: + error(Ebadusefd); + } + + s = c->aux; + if(s == 0) + error(Ebadusefd); + + switch(s->state){ + case Algwait: + /* get algorithm */ + if(n >= sizeof(buf)) + Ebadarg; + strncpy(buf, a, n); + buf[n] = 0; + s->blocklen = 1; + s->diglen = 0; + if(strcmp(buf, "md5") == 0){ + s->hf = md5; + s->diglen = MD5dlen; + } else if(strcmp(buf, "sha") == 0){ + s->hf = sha; + s->diglen = SHAdlen; + } else if(strcmp(buf, "descbc") == 0){ + s->encryptalg = DESCBC; + s->blocklen = 8; + } else if(strcmp(buf, "desebc") == 0){ + s->encryptalg = DESEBC; + s->blocklen = 8; + } else + error(Ebadarg); + s->state = Fdwait; + break; + case Fdwait: + /* get communications channel */ + s->c = buftochan(a, n); + s->state = Secretinwait; + break; + case Secretinwait: + /* get secret for incoming messages */ + setsecret(&s->in, a, n); + s->state = Secretoutwait; + break; + case Secretoutwait: + /* get secret for outgoing messages */ + setsecret(&s->out, a, n); + if(s->blocklen != 1){ + s->max = (1<<15) - s->diglen; + s->max -= s->max % s->blocklen; + s->maxpad = (1<<14) - s->diglen; + s->maxpad -= s->maxpad % s->blocklen; + } else + s->maxpad = s->max = (1<<15) - s->diglen; + s->state = Established; + break; + case Established: + sofar = 0; + do { + m = n - sofar; + if(m > s->max) + m = s->max; + + b = allocb(m); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, a+sofar, m); + poperror(); + b->wp += m; + + sslbwrite(c, b, offset); + + sofar += m; + } while(sofar < n); + break; + default: + error(Ebadusefd); + } + + return n; +} + +void +digestb(Dstate *s, Block *b, OneWay *w) +{ + Block *nb; + uchar *p; + DigestState ss; + uchar msgid[4]; + ulong n, h; + + memset(&ss, 0, sizeof(ss)); + h = s->diglen + 2; + n = BLEN(b) - h; + + /* hash secret + message */ + (*s->hf)(w->secret, w->slen, 0, &ss); + (*s->hf)(nb->rp + h, n, 0, &ss); + + /* hash message id */ + p = msgid; + n = w->mid++; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->func)(msgid, 4, nb->rp + 2, &ss); +} + +long +encryptb(Dstate *s, Block *b) +{ + ulong n, h; + int j; + uchar *p, *ep, *ip; + DESstate *ds; + + h = s->diglen + 2; + + switch(s->encryptalg){ + case DESEBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + h; p < ep; p += 8) + block_cipher(ds->expanded, p, 0); + break; + case DESCBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + h; p < ep; p += 8) + bCBCEncrypt(p, ds->ivec, ds->expanded, 8); + break; + } + +} + +long +decryptb(Dstate *s, Block *b) +{ + ulong n, h; + uchar *p, *ep; + DESstate *ds; + + h = s->diglen + 2; + + switch(s->encryptalg){ + case DESEBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp + h; p < ep; p += 8) + block_cipher(ds->expanded, p, 1); + break; + case DESCBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp + h; p < ep; p += 8) + bCBCDecrypt(p, ds->ivec, ds->expanded, 8); + break; + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(Dstate *s, Block **l, int n) +{ + int i, sofar; + Block *b; + + b = *l; + if(b){ + sofar = BLEN(b); + l = &b->next; + } else + sofar = 0; + + while(sofar < n){ + b = (*devtab[s->c->type].bread)(s->c, Maxdmsg, 0); + if(b == 0) + error(Ehungup); + i = BLEN(b); + if(i <= 0){ + freeb(b); + continue; + } + + *l = b; + l = &b->next; + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +static Block* +digestbread(Dstate *s, long n) +{ + Block *b; + int i, m, len; + uchar *p; + uchar *dp; + uchar digestin[32]; + uchar digest[32]; + DigestState ss; + + memset(&ss, 0, sizeof(ss)); + + ensure(s, &s->unprocessed, s->diglen); + len = 0; + for(i = 0; i < 4; i++){ + consume(&s->unprocessed, digestin+i, 1); + m = digestin[i]; + if((m & 0x80) == 0) + break; + len = (len<<7) | (m & 0x7f); + } + + /* digest count */ + p = &digestin[s->diglen]; + (*s->func)(p, i, 0, &ss); + ensure(s, &s->unprocessed, s->diglen); + + /* get message */ + s->processed = s->unprocessed; + s->unprocessed = 0; + ensure(s, &s->processed, len); + + /* digest message */ + i = 0; + for(b = s->processed; b; b = b->next){ + i = BLEN(b); + if(i >= len) + break; + (*s->func)(b->rp, i, 0, &ss); + len -= i; + } + if(b == 0) + panic("digestbread"); + if(i > len){ + i -= len; + s->unprocessed = allocb(i); + memmove(s->unprocessed->wp, b->rp+len, i); + s->unprocessed->wp += i; + b->wp -= i; + } + (*s->func)(b->rp, len, 0, &ss); + + /* digest secret & message id */ + p = s->in.secret; + m = s->in.mid++; + *p++ = m>>24; + *p++ = m>>16; + *p++ = m>>8; + *p = m; + (*s->func)(s->in.secret, s->in.slen, digest, &ss); + + if(memcmp(digest, digestin, s->diglen) != 0) + error("bad digest"); + } + + b = s->processed; + if(BLEN(b) > n){ + b = allocb(n); + memmove(b->wp, s->processed->rp, n); + b->wp += n; + s->processed->rp += n; + } else + s->processed = b->next; + + return b; +} + +static Block* +sslbread(Chan *c, long n, ulong offset) +{ + Block *bp; + uchar count[2]; + int len; + int pad; + + USED(offset); + + s = c->aux; + if(s == 0 || s->state != Established) + error(Ebadusefd); + + if(waserror()){ + qunlock(&s->in); + dighangup(s); + nexterror(); + } + + qlock(&s->in); + + /* get the whole message */ + ensure(s, &s->unprocessed, 2); + consume(&s->unprocessed, count, 2); + if(count[0] & 0x80){ + len = ((count[0] & 0x7f)<<8) | count[1]; + pad = 0; + } else { + len = ((count[0] & 0x3f)<<8) | count[1]; + ensure(s, &s->unprocessed, 1); + consume(&s->unprocessed, count, 1); + pad = count[0]; + } + ensure(s, &s->unprocessed, len); + + if(s->encrypalg) + b = decryptb(s, len); + else + b = digestb(s, len); + + if(pad) + + qunlock(&s->in); + poperror(); + + return b; +} + +static Chan* +buftochan(char *a, long n) +{ + Chan *c; + int fd; + char buf[32]; + + if(n >= sizeof buf) + error(Egreg); + memmove(buf, a, n); /* so we can NUL-terminate */ + buf[n] = 0; + fd = strtoul(buf, 0, 0); + + c = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +/* + * use SSL record format, add in count and digest or encrypt + */ +long +sslbwrite(Chan *c, Block *b, ulong offset) +{ + Dstate *s; + Block *nb; + int h, n, m, pad, rv; + + s = c->aux; + if(s == 0 || s->state != Established) + error(Ebadusefd); + + if(waserror()){ + qunlock(&s->out); + if(b) + freeb(b); + dighangup(s); + nexterror(); + } + qlock(&s->out); + + rv = 0; + while(b){ + m = n = BLEN(b); + h = s->diglen + 2; + + /* padded blocks are shorter than unpadded ones (strange) */ + pad = 0; + if(m > s->max){ + m = s->max; + } else if(s->blocklen != 1){ + pad = m%s->blocklen; + if(pad){ + pad = s->blocklen - pad; + if(m > s->maxpad){ + pad = 0; + m = s->maxpad; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, m, nb->rptr); + nb->wp += m + h; + b->rp += m; + } else { + /* add header */ + nb = padblock(b, h); + nb->rp -= h; + + /* add pad */ + if(pad) + nb = padblock(nb, -pad); + b = 0; + } + + /* SSL style count */ + if(pad){ + memset(nb->wp, 0, pad); + m += pad; + nb->wp += pad; + } else + m |= 0x8000; + np->rp[0] = (m>>8); + np->rp[1] = m; + + if(encryptalg) + encryptb(s, nb); + else + digestb(s, nb); + + (*devtab[s->c->type].bwrite)(s->c, nb, offset); + + } + qunlock(&s->out); + poperror(); + + return rv; +} + +/* + * crypt's interface to system, included here to override the + * library version + */ +void +handle_exception(int type, char *exception) +{ + if(type == CRITICAL) + panic("kernel ssl: %s", exception); + else + print("kernel ssl: %s\n", exception); +} + +void* +crypt_malloc(int size) +{ + void *x; + + x = smalloc(size); + if(x == 0) + handle_exception(CRITICAL, "out of memory"); + return x; +} + +void +crypt_free(void *x) +{ + if(x == 0) + handle_exception(CRITICAL, "freeing null pointer"); + free(x); +} +