diff --git a/sys/include/libsec.h b/sys/include/libsec.h index e3bbc7693..3bde79323 100644 --- a/sys/include/libsec.h +++ b/sys/include/libsec.h @@ -400,3 +400,7 @@ int okThumbprint(uchar *sha1, Thumbprint *ok); /* readcert.c */ uchar *readcert(char *filename, int *pcertlen); PEMChain*readcertchain(char *filename); + +/* aes_xts.c */ +int aes_xts_encrypt(ulong tweak[], ulong ecb[], vlong sectorNumber, uchar *input, uchar *output, ulong len) ; +int aes_xts_decrypt(ulong tweak[], ulong ecb[], vlong sectorNumber, uchar *input, uchar *output, ulong len); \ No newline at end of file diff --git a/sys/src/9/port/devfs.c b/sys/src/9/port/devfs.c index 8e5fa5606..e515c8a01 100644 --- a/sys/src/9/port/devfs.c +++ b/sys/src/9/port/devfs.c @@ -21,6 +21,9 @@ #include "io.h" #include "ureg.h" #include "../port/error.h" +#include "libsec.h" + +int dec16(uchar *out, int lim, char *in, int n); enum { @@ -32,6 +35,7 @@ enum Fclear, /* start over */ Fdel, /* delete a configure device */ Fdisk, /* set default tree and sector sz*/ + Fcrypt, /* encrypted device */ Sectorsz = 1, Blksize = 8*1024, /* for Finter only */ @@ -65,6 +69,7 @@ enum typedef struct Inner Inner; typedef struct Fsdev Fsdev; typedef struct Tree Tree; +typedef struct Key Key; struct Inner { @@ -85,6 +90,7 @@ struct Fsdev vlong start; /* start address (for Fpart) */ uint ndevs; /* number of inner devices */ Inner *inner[Ndevs]; /* inner devices */ + void *extra; /* extra state for the device */ }; struct Tree @@ -95,6 +101,10 @@ struct Tree uint nadevs; /* number of allocated devices in devs */ }; +struct Key { + AESstate tweak, ecb; +}; + #define dprint if(debug)print extern Dev fsdevtab; /* forward */ @@ -121,6 +131,7 @@ static char* tnames[] = { [Fcat] "cat", [Finter] "inter", [Fpart] "part", + [Fcrypt] "crypt", }; static Cmdtab configs[] = { @@ -131,6 +142,7 @@ static Cmdtab configs[] = { Fclear, "clear", 1, Fdel, "del", 2, Fdisk, "disk", 0, + Fcrypt, "crypt", 0, }; static char Egone[] = "device is gone"; /* file has been removed */ @@ -156,6 +168,7 @@ seprintdev(char *s, char *e, Fsdev *mp) case Fmirror: case Fcat: case Finter: + case Fcrypt: s = strecpy(s, e, "\n"); break; case Fpart: @@ -434,6 +447,13 @@ setdsize(Fsdev* mp, vlong *ilen) mp->size = inlen - mp->start; } break; + case Fcrypt: + if(inlen > (64*1024)) { + mp->size = inlen - (64 * 1024); + } else { + mp->size = 0; + } + break; } } if(mp->type == Finter) @@ -478,6 +498,10 @@ parseconfig(char *a, long n, Cmdbuf **cbp, Cmdtab **ctp) if(cb->nf != 4 && (cb->nf != 3 || source == nil)) error("ctl usage: part new [file] off len"); break; + case Fcrypt: + if(cb->nf != 3) + error("ctl usage: crypt newname device keyhex"); + break; } } @@ -529,6 +553,7 @@ mconfig(char* a, long n) vlong size, start; vlong *ilen; char *tname, *dname, *fakef[4]; + uchar key[32]; Chan **idev; Cmdbuf *cb; Cmdtab *ct; @@ -575,6 +600,10 @@ mconfig(char* a, long n) free(cb); mdelctl("*", "*"); /* del everything */ return; + case Fcrypt: + dec16(key, 32, cb->f[2], 64); + cb->nf -= 1; + break; case Fpart: if(cb->nf == 3){ /* @@ -662,6 +691,15 @@ Fail: mp->start = start * sectorsz; mp->size = size * sectorsz; } + if(mp->type == Fcrypt) { + Key *k = mallocz(sizeof(Key), 1); + if(k == nil) + error(Enomem); + setupAESstate(&k->tweak, &key[0], 16, nil); + setupAESstate(&k->ecb, &key[16], 16, nil); + memset(key, 0, 32); + mp->extra = k; + } for(i = 1; i < cb->nf; i++){ inprv = mp->inner[i-1] = mallocz(sizeof(Inner), 1); if(inprv == nil) @@ -673,6 +711,8 @@ Fail: } setdsize(mp, ilen); + + poperror(); wunlock(&lck); free(idev); @@ -970,6 +1010,63 @@ io(Fsdev *mp, Inner *in, int isread, void *a, long l, vlong off) return wl; } +static long +cryptio(Fsdev *mp, int isread, uchar *a, long l, vlong off) +{ + long wl, ws, wo; + uchar *buf; + Chan *mc; + Inner *in; + Key *k; + + in = mp->inner[0]; + // Header + off += 64*1024; + + mc = in->idev; + if(mc == nil) + error(Egone); + if (waserror()) { + print("#k: %s: byte %,lld count %ld (of #k/%s): %s error: %s\n", + in->iname, off, l, mp->name, (isread? "read": "write"), + (up && up->errstr? up->errstr: "")); + nexterror(); + } + + if(off % 512 != 0 || l%512 !=0) + error(Eio); + + wo = (l > 16384) ? 16384 : l; + buf = mallocz(wo, 1); + if(!buf) + error(Enomem); + k = (Key*)(mp->extra); + + for(ws = 0; ws < l; ws+=wo) { + if (isread) { + wl = devtab[mc->type]->read(mc, buf, wo, off); + if(wl!=wo) + error(Eio); + for(wl=0; wltweak.ekey, k->ecb.dkey, off, buf+wl, a+ws+wl, 512); + off += 512; + } + } else { + for(wl=0; wltweak.ekey, k->ecb.ekey, off, a+ws+wl, buf+wl, 512); + off += 512; + } + + wl = devtab[mc->type]->write(mc, buf, wo, off-wo); + if(wl!=wo) + error(Eio); + } + } + free(buf); + poperror(); + return ws; +} + /* NB: a transfer could span multiple inner devices */ static long catio(Fsdev *mp, int isread, void *a, long n, vlong off) @@ -1140,6 +1237,9 @@ mread(Chan *c, void *a, long n, vlong off) "from mirror: %s\n", mp->name, off, n, (up && up->errstr? up->errstr: "")); break; + case Fcrypt: + res = cryptio(mp, Isread, a, n, off); + break; } Done: poperror(); @@ -1235,6 +1335,9 @@ mwrite(Chan *c, void *a, long n, vlong off) (up && up->errstr? up->errstr: "")); break; + case Fcrypt: + res = cryptio(mp, Iswrite, a, n, off); + break; } Done: poperror(); diff --git a/sys/src/cmd/cryptsetup/crypt.h b/sys/src/cmd/cryptsetup/crypt.h new file mode 100644 index 000000000..314077a31 --- /dev/null +++ b/sys/src/cmd/cryptsetup/crypt.h @@ -0,0 +1,14 @@ +// Author Taru Karttunen +// This file can be used as both Public Domain or Creative Commons CC0. +#include + +typedef struct { + unsigned char Salt[16]; + unsigned char Key[32]; +} Slot; + +typedef struct { + unsigned char Master[32]; + Slot Slots[8]; + AESstate C1, C2; +} XtsState; diff --git a/sys/src/cmd/cryptsetup/cryptsetup.c b/sys/src/cmd/cryptsetup/cryptsetup.c new file mode 100644 index 000000000..f3b0b43ee --- /dev/null +++ b/sys/src/cmd/cryptsetup/cryptsetup.c @@ -0,0 +1,173 @@ +// Author Taru Karttunen +// This file can be used as both Public Domain or Creative Commons CC0. +#include +#include +#include "crypt.h" + +void format(char *file); +void copen(char *file); +char*readcons(char *prompt, char *def, int raw, char *buf, int nbuf); +int pkcs5_pbkdf2(const unsigned char *pass, int pass_len, const unsigned char *salt, int salt_len, unsigned char *key, int key_len, int rounds); + +void +usage(void) +{ + print("usage: \ncryptsetup -f deviceorfile \t\t\t\t\t# Format file or device\ncryptsetup -o deviceorfile >> /dev/fs/ctl \t# Open file or device\n"); + exits("usage"); +} + +enum +{ + NoMode, + Format, + Open, +}; + + +void +main(int argc, char *argv[]) +{ + int mode; + char *file; + + mode = 0; + + ARGBEGIN { + default: + usage(); + case 'f': + mode = Format; + break; + case 'o': + mode = Open; + break; + } ARGEND; + + if((mode == NoMode) || (argc != 1)) + usage(); + + file = argv[0]; + + switch(mode) { + case Format: + format(file); + break; + case Open: + copen(file); + break; + } +} + +void +format(char *file) +{ + char trand[48], pass1[64], pass2[64]; + unsigned char tkey[16], tivec[16], buf[64*1024]; + XtsState s; + AESstate cbc; + int i,j, fd; + + do { + readcons("password", nil, 1, pass1, 64); + readcons("confirm", nil, 1, pass2, 64); + } while(strcmp(pass1, pass2) != 0); + + do { + readcons("Are you sure you want to delete all data? (YES to procees)", nil, 0, (char*)buf, 4); + } while(strcmp((char*)buf, "YES") != 0); + + srand(truerand()); + + for(i = 0; i < 16*4096; i++) + buf[i] = rand(); + + for(i = 0; i < 48; i+=4) + *((unsigned*)&trand[i]) = truerand(); + memcpy(s.Master, trand, 32); + memcpy(s.Slots[0].Salt, trand+32, 16); + + pkcs5_pbkdf2((unsigned char*)pass1, strlen(pass1), s.Slots[0].Salt, 16, (unsigned char*)tkey, 16, 9999); + memset(tivec, 0, 16); + setupAESstate(&cbc, tkey, 16, tivec); + memcpy(s.Slots[0].Key, s.Master, 32); + aesCBCencrypt(s.Slots[0].Key, 32, &cbc); + + for(i=0; i<16; i++) + for(j=0; j<8; j++) { + buf[(4096*i)] = 1; + buf[(4096*i)+(4*j)+1] = s.Slots[j].Salt[i]; + buf[(4096*i)+(4*j)+2] = s.Slots[j].Key[i]; + buf[(4096*i)+(4*j)+3] = s.Slots[j].Key[i+16]; + } + + if((fd = open(file, OWRITE)) < 0) + exits("Cannot open disk"); + + /* make the pad for checking crypto */ + for(i=0; i<8; i++) { + buf[(64*1024)-8+i] = ~buf[(64*1024)-16+i]; + } + memset(tivec, 0, 16); + setupAESstate(&cbc, s.Master, 16, tivec); + aes_encrypt(cbc.ekey, cbc.rounds, &buf[(64*1024)-16], &buf[(64*1024)-16]); + + write(fd, buf, 16*4096); + + print("Disk written\n"); +} + +void copen(char *file) { + unsigned char pass[32], buf[1024*64], tkey[16], tivec[16]; + XtsState s; + int i,j,fd; + AESstate cbc; + char *base, fdpath[1024]; + + + if((fd = open(file, OREAD)) < 0) + exits("Cannot open disk"); + + if(read(fd, buf, 1024*64) != 1024*64) + exits("Cannot read disk"); + + for(i=0; i<16; i++) + for(j=0; j<8; j++) { + s.Slots[j].Salt[i] = buf[(4096*i)+(4*j)+1]; + s.Slots[j].Key[i] = buf[(4096*i)+(4*j)+2]; + s.Slots[j].Key[i+16] = buf[(4096*i)+(4*j)+3]; + } + + + + openpass: + readcons("Password", nil, 1, (char*)pass, 32); + + memcpy(s.Master, s.Slots[0].Key, 32); + + pkcs5_pbkdf2(pass, strlen((char*)pass), s.Slots[0].Salt, 16, tkey, 16, 9999); + memset(tivec, 0, 16); + setupAESstate(&cbc, tkey, 16, tivec); + aesCBCdecrypt(s.Master, 32, &cbc); + + memset(tivec, 0, 16); + setupAESstate(&cbc, s.Master, 16, tivec); + + aes_decrypt(cbc.dkey, cbc.rounds, &buf[(64*1024)-16], &buf[(64*1024)-16]); + + /* make the pad for checking crypto */ + for(i=0; i<8; i++) + if((buf[(64*1024)-8+i] ^ buf[(64*1024)-16+i]) != 255) { + goto openpass; + } + + base = utfrrune(file, '/'); + fd2path(fd, fdpath, 1024); + j = sprint((char*)buf, "crypt %s %s ", base ? base+1 : file, fdpath); + + for(i=0; i<32; i++) { + sprint((char*)&buf[j], "%02X", s.Master[i]); + j += 2; + } + sprint((char*)&buf[j], "\n"); + print("%s\n", (char*)buf); +} \ No newline at end of file diff --git a/sys/src/cmd/cryptsetup/mkfile b/sys/src/cmd/cryptsetup/mkfile new file mode 100644 index 000000000..7dc3ede08 --- /dev/null +++ b/sys/src/cmd/cryptsetup/mkfile @@ -0,0 +1,10 @@ + + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#define DS DigestState /* only to abbreviate SYNOPSIS */ +#define SHA1_DIGEST_LENGTH 20 +#define MIN(a,b) ((a < b) ? a : b) + +/* + * Password-Based Key Derivation Function 2 (PKCS #5 v2.0). + * Code based on IEEE Std 802.11-2007, Annex H.4.2. + */ +int +pkcs5_pbkdf2(const unsigned char *pass, int pass_len, const unsigned char *salt, int salt_len, + unsigned char *key, int key_len, int rounds) +{ + unsigned char *asalt, obuf[SHA1_DIGEST_LENGTH]; + unsigned char d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH]; + unsigned i, j; + unsigned count; + unsigned r; + + if (rounds < 1 || key_len == 0) + return -1; + if (salt_len == 0) + return -1; + if ((asalt = malloc(salt_len + 4)) == nil) + return -1; + + memcpy(asalt, salt, salt_len); + + for (count = 1; key_len > 0; count++) { + asalt[salt_len + 0] = (count >> 24) & 0xff; + asalt[salt_len + 1] = (count >> 16) & 0xff; + asalt[salt_len + 2] = (count >> 8) & 0xff; + asalt[salt_len + 3] = count & 0xff; + hmac_sha1(asalt, salt_len + 4, pass, pass_len, d1, nil); + memcpy(obuf, d1, sizeof(obuf)); + + for (i = 1; i < rounds; i++) { + hmac_sha1(d1, sizeof(d1), pass, pass_len, d2, nil); + memcpy(d1, d2, sizeof(d1)); + for (j = 0; j < sizeof(obuf); j++) + obuf[j] ^= d1[j]; + } + + r = MIN(key_len, SHA1_DIGEST_LENGTH); + memcpy(key, obuf, r); + key += r; + key_len -= r; + }; + memset(asalt, 0, salt_len + 4); + free(asalt); + memset(d1, 0, sizeof(d1)); + memset(d2, 0, sizeof(d2)); + memset(obuf, 0, sizeof(obuf)); + + return 0; +} diff --git a/sys/src/cmd/cryptsetup/readcons.c b/sys/src/cmd/cryptsetup/readcons.c new file mode 100644 index 000000000..cc2926cf0 --- /dev/null +++ b/sys/src/cmd/cryptsetup/readcons.c @@ -0,0 +1,77 @@ +/* From /sys/src/libauthsrv/readnvram.c, LPL licensed */ +#include +#include + + +char* +readcons(char *prompt, char *def, int raw, char *buf, int nbuf) +{ + int fdin, fdout, ctl, n, m; + char line[10]; + + fdin = open("/dev/cons", OREAD); + if(fdin < 0) + fdin = 0; + fdout = open("/dev/cons", OWRITE); + if(fdout < 0) + fdout = 1; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + if(raw){ + ctl = open("/dev/consctl", OWRITE); + if(ctl >= 0) + write(ctl, "rawon", 5); + } else + ctl = -1; + + m = 0; + for(;;){ + n = read(fdin, line, 1); + if(n == 0){ + close(ctl); + werrstr("readcons: EOF"); + return nil; + } + if(n < 0){ + close(ctl); + werrstr("can't read cons"); + return nil; + } + if(line[0] == 0x7f) + exits(0); + if(n == 0 || line[0] == '\n' || line[0] == '\r'){ + if(raw){ + write(ctl, "rawoff", 6); + write(fdout, "\n", 1); + close(ctl); + } + buf[m] = '\0'; + if(buf[0]=='\0' && def) + strcpy(buf, def); + return buf; + } + if(line[0] == '\b'){ + if(m > 0) + m--; + }else if(line[0] == 0x15){ /* ^U: line kill */ + m = 0; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + }else{ + if(m >= nbuf-1){ + fprint(fdout, "line too long\n"); + m = 0; + if(def != nil) + fprint(fdout, "%s[%s]: ", prompt, def); + else + fprint(fdout, "%s: ", prompt); + }else + buf[m++] = line[0]; + } + } +} + diff --git a/sys/src/libsec/port/aes_xts.c b/sys/src/libsec/port/aes_xts.c new file mode 100644 index 000000000..f200e4db2 --- /dev/null +++ b/sys/src/libsec/port/aes_xts.c @@ -0,0 +1,69 @@ +// Author Taru Karttunen +// This file can be used as both Public Domain or Creative Commons CC0. +#include +#include + +#define AesBlockSize 16 + +static void xor128(uchar* o,uchar* i1,uchar* i2) { + ((ulong*)o)[0] = ((ulong*)i1)[0] ^ ((ulong*)i2)[0]; + ((ulong*)o)[1] = ((ulong*)i1)[1] ^ ((ulong*)i2)[1]; + ((ulong*)o)[2] = ((ulong*)i1)[2] ^ ((ulong*)i2)[2]; + ((ulong*)o)[3] = ((ulong*)i1)[3] ^ ((ulong*)i2)[3]; +} + +static void gf_mulx(uchar* x) { + ulong t = ((((ulong*)(x))[3] & 0x80000000u) ? 0x00000087u : 0);; + ((ulong*)(x))[3] = (((ulong*)(x))[3] << 1) | (((ulong*)(x))[2] & 0x80000000u ? 1 : 0); + ((ulong*)(x))[2] = (((ulong*)(x))[2] << 1) | (((ulong*)(x))[1] & 0x80000000u ? 1 : 0); + ((ulong*)(x))[1] = (((ulong*)(x))[1] << 1) | (((ulong*)(x))[0] & 0x80000000u ? 1 : 0); + ((ulong*)(x))[0] = (((ulong*)(x))[0] << 1) ^ t; + +} + +int aes_xts_encrypt(ulong tweak[], ulong ecb[], vlong sectorNumber, uchar *input, uchar *output, ulong len) { + uchar T[16], x[16]; + int i; + + if(len % 16 != 0) + return -1; + + for(i=0; i> 8; + } + + aes_encrypt(tweak, 10, T, T); + + for (i=0; i> 8; + } + + aes_encrypt(tweak, 10, T, T); + + for (i=0; i