| #include <u.h> |
| #include <libc.h> |
| #include <mp.h> |
| #include <libsec.h> |
| #include "SConn.h" |
| |
| extern int verbose; |
| |
| typedef struct ConnState { |
| uchar secret[SHA1dlen]; |
| ulong seqno; |
| RC4state rc4; |
| } ConnState; |
| |
| #undef SS |
| typedef struct SS { |
| int fd; /* file descriptor for read/write of encrypted data */ |
| int alg; /* if nonzero, "alg sha rc4_128" */ |
| ConnState in, out; |
| } SS; |
| |
| static int |
| SC_secret(SConn *conn, uchar *sigma, int direction) |
| { |
| SS *ss = (SS*)(conn->chan); |
| int nsigma = conn->secretlen; |
| |
| if(direction != 0){ |
| hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->out.secret, nil); |
| hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->in.secret, nil); |
| }else{ |
| hmac_sha1(sigma, nsigma, (uchar*)"two", 3, ss->out.secret, nil); |
| hmac_sha1(sigma, nsigma, (uchar*)"one", 3, ss->in.secret, nil); |
| } |
| setupRC4state(&ss->in.rc4, ss->in.secret, 16); /* restrict to 128 bits */ |
| setupRC4state(&ss->out.rc4, ss->out.secret, 16); |
| ss->alg = 1; |
| return 0; |
| } |
| |
| static void |
| hash(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) |
| { |
| DigestState sha; |
| uchar seq[4]; |
| |
| seq[0] = seqno>>24; |
| seq[1] = seqno>>16; |
| seq[2] = seqno>>8; |
| seq[3] = seqno; |
| memset(&sha, 0, sizeof sha); |
| sha1(secret, SHA1dlen, nil, &sha); |
| sha1(data, len, nil, &sha); |
| sha1(seq, 4, d, &sha); |
| } |
| |
| static int |
| verify(uchar secret[SHA1dlen], uchar *data, int len, int seqno, uchar d[SHA1dlen]) |
| { |
| DigestState sha; |
| uchar seq[4]; |
| uchar digest[SHA1dlen]; |
| |
| seq[0] = seqno>>24; |
| seq[1] = seqno>>16; |
| seq[2] = seqno>>8; |
| seq[3] = seqno; |
| memset(&sha, 0, sizeof sha); |
| sha1(secret, SHA1dlen, nil, &sha); |
| sha1(data, len, nil, &sha); |
| sha1(seq, 4, digest, &sha); |
| return memcmp(d, digest, SHA1dlen); |
| } |
| |
| static int |
| SC_read(SConn *conn, uchar *buf, int n) |
| { |
| SS *ss = (SS*)(conn->chan); |
| uchar count[2], digest[SHA1dlen]; |
| int len, nr; |
| |
| if(read(ss->fd, count, 2) != 2 || (count[0]&0x80) == 0){ |
| snprint((char*)buf,n,"!SC_read invalid count"); |
| return -1; |
| } |
| len = (count[0]&0x7f)<<8 | count[1]; /* SSL-style count; no pad */ |
| if(ss->alg){ |
| len -= SHA1dlen; |
| if(len <= 0 || readn(ss->fd, digest, SHA1dlen) != SHA1dlen){ |
| snprint((char*)buf,n,"!SC_read missing sha1"); |
| return -1; |
| } |
| if(len > n || readn(ss->fd, buf, len) != len){ |
| snprint((char*)buf,n,"!SC_read missing data"); |
| return -1; |
| } |
| rc4(&ss->in.rc4, digest, SHA1dlen); |
| rc4(&ss->in.rc4, buf, len); |
| if(verify(ss->in.secret, buf, len, ss->in.seqno, digest) != 0){ |
| snprint((char*)buf,n,"!SC_read integrity check failed"); |
| return -1; |
| } |
| }else{ |
| if(len <= 0 || len > n){ |
| snprint((char*)buf,n,"!SC_read implausible record length"); |
| return -1; |
| } |
| if( (nr = readn(ss->fd, buf, len)) != len){ |
| snprint((char*)buf,n,"!SC_read expected %d bytes, but got %d", len, nr); |
| return -1; |
| } |
| } |
| ss->in.seqno++; |
| return len; |
| } |
| |
| static int |
| SC_write(SConn *conn, uchar *buf, int n) |
| { |
| SS *ss = (SS*)(conn->chan); |
| uchar count[2], digest[SHA1dlen], enc[Maxmsg+1]; |
| int len; |
| |
| if(n <= 0 || n > Maxmsg+1){ |
| werrstr("!SC_write invalid n %d", n); |
| return -1; |
| } |
| len = n; |
| if(ss->alg) |
| len += SHA1dlen; |
| count[0] = 0x80 | len>>8; |
| count[1] = len; |
| if(write(ss->fd, count, 2) != 2){ |
| werrstr("!SC_write invalid count"); |
| return -1; |
| } |
| if(ss->alg){ |
| hash(ss->out.secret, buf, n, ss->out.seqno, digest); |
| rc4(&ss->out.rc4, digest, SHA1dlen); |
| memcpy(enc, buf, n); |
| rc4(&ss->out.rc4, enc, n); |
| if(write(ss->fd, digest, SHA1dlen) != SHA1dlen || |
| write(ss->fd, enc, n) != n){ |
| werrstr("!SC_write error on send"); |
| return -1; |
| } |
| }else{ |
| if(write(ss->fd, buf, n) != n){ |
| werrstr("!SC_write error on send"); |
| return -1; |
| } |
| } |
| ss->out.seqno++; |
| return n; |
| } |
| |
| static void |
| SC_free(SConn *conn) |
| { |
| SS *ss = (SS*)(conn->chan); |
| |
| close(ss->fd); |
| free(ss); |
| free(conn); |
| } |
| |
| SConn* |
| newSConn(int fd) |
| { |
| SS *ss; |
| SConn *conn; |
| |
| if(fd < 0) |
| return nil; |
| ss = (SS*)emalloc(sizeof(*ss)); |
| conn = (SConn*)emalloc(sizeof(*conn)); |
| ss->fd = fd; |
| ss->alg = 0; |
| conn->chan = (void*)ss; |
| conn->secretlen = SHA1dlen; |
| conn->free = SC_free; |
| conn->secret = SC_secret; |
| conn->read = SC_read; |
| conn->write = SC_write; |
| return conn; |
| } |
| |
| void |
| writerr(SConn *conn, char *s) |
| { |
| char buf[Maxmsg]; |
| |
| snprint(buf, Maxmsg, "!%s", s); |
| conn->write(conn, (uchar*)buf, strlen(buf)); |
| } |
| |
| int |
| readstr(SConn *conn, char *s) |
| { |
| int n; |
| |
| n = conn->read(conn, (uchar*)s, Maxmsg); |
| if(n >= 0){ |
| s[n] = 0; |
| if(s[0] == '!'){ |
| memmove(s, s+1, n); |
| n = -1; |
| } |
| }else{ |
| strcpy(s, "read error"); |
| } |
| return n; |
| } |
| |