ref: 9655db255097b84cf742b7a33c74432d4eb3425a
parent: f34231e16a9780a1aa1b9fe5dad1776dd82caa44
author: taruti <[email protected]>
date: Tue May 24 18:19:33 EDT 2011
devfs crypto code - alpha version
--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -400,3 +400,7 @@
/* 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
--- a/sys/src/9/port/devfs.c
+++ b/sys/src/9/port/devfs.c
@@ -21,7 +21,10 @@
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
+#include "libsec.h"
+int dec16(uchar *out, int lim, char *in, int n);
+
enum
{
Fnone,
@@ -32,6 +35,7 @@
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 @@
typedef struct Inner Inner;
typedef struct Fsdev Fsdev;
typedef struct Tree Tree;
+typedef struct Key Key;
struct Inner
{
@@ -85,6 +90,7 @@
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 @@
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 @@
[Fcat] "cat",
[Finter] "inter",
[Fpart] "part",
+ [Fcrypt] "crypt",
};
static Cmdtab configs[] = {
@@ -131,6 +142,7 @@
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 @@
case Fmirror:
case Fcat:
case Finter:
+ case Fcrypt:
s = strecpy(s, e, "\n");
break;
case Fpart:
@@ -434,6 +447,13 @@
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 @@
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 @@
vlong size, start;
vlong *ilen;
char *tname, *dname, *fakef[4];
+ uchar key[32];
Chan **idev;
Cmdbuf *cb;
Cmdtab *ct;
@@ -575,6 +600,10 @@
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 @@
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 @@
}
setdsize(mp, ilen);
+
+
poperror();
wunlock(&lck);
free(idev);
@@ -970,6 +1010,63 @@
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; wl<wo; wl+=512) {
+ aes_xts_decrypt(k->tweak.ekey, k->ecb.dkey, off, buf+wl, a+ws+wl, 512);
+ off += 512;
+ }
+ } else {
+ for(wl=0; wl<wo; wl+=512) {
+ aes_xts_encrypt(k->tweak.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 @@
"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();
@@ -1234,6 +1334,9 @@
"to mirror: %s\n", mp->name, off, n,
(up && up->errstr? up->errstr: ""));
+ break;
+ case Fcrypt:
+ res = cryptio(mp, Iswrite, a, n, off);
break;
}
Done:
--- /dev/null
+++ b/sys/src/cmd/cryptsetup/crypt.h
@@ -1,0 +1,14 @@
+// Author Taru Karttunen <[email protected]>
+// This file can be used as both Public Domain or Creative Commons CC0.
+#include <libsec.h>
+
+typedef struct {
+ unsigned char Salt[16];
+ unsigned char Key[32];
+} Slot;
+
+typedef struct {
+ unsigned char Master[32];
+ Slot Slots[8];
+ AESstate C1, C2;
+} XtsState;
--- /dev/null
+++ b/sys/src/cmd/cryptsetup/cryptsetup.c
@@ -1,0 +1,173 @@
+// Author Taru Karttunen <[email protected]>
+// This file can be used as both Public Domain or Creative Commons CC0.
+#include <u.h>
+#include <libc.h>
+#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
--- /dev/null
+++ b/sys/src/cmd/cryptsetup/mkfile
@@ -1,0 +1,10 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=cryptsetup
+OFILES=\
+ cryptsetup.$O\
+ readcons.$O\
+ pbkdf2.$O\
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/cryptsetup/pbkdf2.c
@@ -1,0 +1,77 @@
+/* $OpenBSD: pbkdf2.c,v 1.1 2008/06/14 06:28:27 djm Exp $ */
+
+/*-
+ * Copyright (c) 2008 Damien Bergamini <[email protected]>
+ *
+ * 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 <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/cryptsetup/readcons.c
@@ -1,0 +1,77 @@
+/* From /sys/src/libauthsrv/readnvram.c, LPL licensed */
+#include <u.h>
+#include <libc.h>
+
+
+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];
+ }
+ }
+}
+
--- /dev/null
+++ b/sys/src/libsec/port/aes_xts.c
@@ -1,0 +1,69 @@
+// Author Taru Karttunen <[email protected]>
+// This file can be used as both Public Domain or Creative Commons CC0.
+#include <u.h>
+#include <libsec.h>
+
+#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<AesBlockSize; i++) {
+ T[i] = (uchar)(sectorNumber & 0xFF);
+ sectorNumber = sectorNumber >> 8;
+ }
+
+ aes_encrypt(tweak, 10, T, T);
+
+ for (i=0; i<len; i+=AesBlockSize) {
+ xor128(&x[0], &input[i], &T[0]);
+ aes_encrypt(ecb, 10, x, x);
+ xor128(&output[i], &x[0], &T[0]);
+ gf_mulx(&T[0]);
+ }
+ return 0;
+}
+
+int aes_xts_decrypt(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<AesBlockSize; i++) {
+ T[i] = (uchar)(sectorNumber & 0xFF);
+ sectorNumber = sectorNumber >> 8;
+ }
+
+ aes_encrypt(tweak, 10, T, T);
+
+ for (i=0; i<len; i+=AesBlockSize) {
+ xor128(&x[0], &input[i], &T[0]);
+ aes_decrypt(ecb, 10, x, x);
+ xor128(&output[i], &x[0], &T[0]);
+ gf_mulx(&T[0]);
+ }
+ return 0;
+}
+
--- a/sys/src/libsec/port/mkfile
+++ b/sys/src/libsec/port/mkfile
@@ -17,6 +17,7 @@
egsign.c egverify.c \
dsagen.c dsaalloc.c dsaprivtopub.c dsasign.c dsaverify.c \
tlshand.c thumb.c readcert.c \
+ aes_xts.c \
ALLOFILES=${CFILES:%.c=%.$O}