ref: 234137bce39a03eab02044234c8f970498ccc5b0
parent: f3feafc476ff108231dd6e0e3ac3cd420a62a81c
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Oct 24 16:56:11 EDT 2016
fix bugs and cleanup cryptsetup code devfs: - fix memory leak in devfs leaking the aes key - allocate aes-xts cipher state in secure memory - actually check if the hexkey got fully parsed cryptsetup: - get rid of stupid "type YES" prompt - use genrandom() to generate salts and keys - rewrite cryptsetup to use common pbkdf2 and readcons routines - fix alot of error handling and simplify the code - move cryptsetup command to disk/cryptsetup - update cryptsetup(8) manual page
--- a/sys/man/8/cryptsetup
+++ b/sys/man/8/cryptsetup
@@ -2,15 +2,15 @@
.SH NAME
cryptsetup \- setup encrypted partition
.SH SYNOPSIS
-.B cryptsetup
+.B disk/cryptsetup
.B -f
.I files ...
.br
-.B cryptsetup
+.B disk/cryptsetup
.B -o
.I files ...
.br
-.B cryptsetup
+.B disk/cryptsetup
.B -i
.I files ...
.SH DESCRIPTION
@@ -49,7 +49,7 @@
.I files
argument.
.SH SOURCE
-.B /sys/src/cmd/cryptsetup
+.B /sys/src/cmd/disk/cryptsetup.c
.SH SEE ALSO
.IR aes (2) ,
.IR fs (3)
--- a/sys/src/9/boot/bootfs.proto
+++ b/sys/src/9/boot/bootfs.proto
@@ -11,9 +11,9 @@
cat
cfs
chmod
- cryptsetup
dd
disk
+ cryptsetup
edisk
fdisk
prep
--- a/sys/src/9/port/devfs.c
+++ b/sys/src/9/port/devfs.c
@@ -90,7 +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 */
+ Key *key; /* crypt key */
};
struct Tree
@@ -351,6 +351,7 @@
}
wunlock(&lck);
+ secfree(mp->key);
free(mp->name);
for(i = 0; i < mp->ndevs; i++){
in = mp->inner[i];
@@ -359,8 +360,6 @@
free(in->iname);
free(in);
}
- if(debug)
- memset(mp, 9, sizeof *mp); /* poison */
free(mp);
}
@@ -553,7 +552,7 @@
vlong size, start;
vlong *ilen;
char *tname, *dname, *fakef[4];
- uchar key[32];
+ uchar key[32];
Chan **idev;
Cmdbuf *cb;
Cmdtab *ct;
@@ -601,7 +600,8 @@
mdelctl("*", "*"); /* del everything */
return;
case Fcrypt:
- dec16(key, 32, cb->f[2], 64);
+ if(dec16(key, 32, cb->f[2], strlen(cb->f[2])) != 32)
+ error("bad hexkey");
cb->nf -= 1;
break;
case Fpart:
@@ -693,13 +693,11 @@
mp->size = size * sectorsz;
}
if(mp->type == Fcrypt) {
- Key *k = mallocz(sizeof(Key), 1);
- if(k == nil)
- error(Enomem);
+ Key *k = secalloc(sizeof(Key));
setupAESstate(&k->tweak, &key[0], 16, nil);
setupAESstate(&k->ecb, &key[16], 16, nil);
memset(key, 0, 32);
- mp->extra = k;
+ mp->key = k;
}
for(i = 1; i < cb->nf; i++){
inprv = mp->inner[i-1] = mallocz(sizeof(Inner), 1);
@@ -712,8 +710,6 @@
}
setdsize(mp, ilen);
-
-
poperror();
wunlock(&lck);
free(idev);
@@ -1033,7 +1029,7 @@
if(off < 0 || l <= 0 || ((off|l) & (Sectsz-1)))
error(Ebadarg);
- k = mp->extra;
+ k = mp->key;
in = mp->inner[0];
mc = in->idev;
if(mc == nil)
--- a/sys/src/cmd/cryptsetup/crypt.h
+++ /dev/null
@@ -1,14 +1,0 @@
-// Author Taru Karttunen <taruti@taruti.net>
-// 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;
--- a/sys/src/cmd/cryptsetup/cryptsetup.c
+++ /dev/null
@@ -1,189 +1,0 @@
-// Author Taru Karttunen <taruti@taruti.net>
-// 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[], int);
-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 files \t\t# Format file or device\ncryptsetup -o files \t\t# Print commandline for open\ncryptsetup -i files\t\t# Install (open) files\n");
- exits("usage");
-}
-
-enum
-{
- NoMode,
- Format,
- Open,
- Install,
-};
-
-
-void
-main(int argc, char *argv[])
-{
- int mode;
-
- mode = 0;
-
- ARGBEGIN {
- default:
- usage();
- case 'f':
- mode = Format;
- break;
- case 'o':
- mode = Open;
- break;
- case 'i':
- mode = Install;
- break;
- } ARGEND;
-
- if((mode == NoMode) || (argc < 1))
- usage();
-
- switch(mode) {
- case Format:
- format(argv);
- break;
- case Install:
- case Open:
- copen(argv, mode);
- break;
- }
-}
-
-void
-format(char *files[])
-{
- 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(;*files;files++) {
-
- 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(*files, 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 %s written\n", *files);
- }
-}
-
-void copen(char *files[], int mode) {
- unsigned char pass[32], buf[1024*64], tkey[16], tivec[16], cbuf[16];
- XtsState s;
- int i,j,fd, oldpass;
- AESstate cbc;
- char *base, fdpath[1024];
-
- oldpass = 0;
- for(;*files; files++) {
- if((fd = open(*files, OREAD)) < 0)
- exits("Cannot open disk");
-
- if(read(fd, buf, 1024*64) != 1024*64)
- exits("Cannot read disk");
-
- openpass:
- 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];
- }
-
- if(!oldpass)
- 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);
-
- memcpy(cbuf, &buf[(64*1024)-16], 16);
- aes_decrypt(cbc.dkey, cbc.rounds, cbuf, cbuf);
-
- /* make the pad for checking crypto */
- for(i=0; i<8; i++)
- if((cbuf[i] ^ cbuf[i+8]) != 255) {
- oldpass=0;
- goto openpass;
- }
-
- base = utfrrune(*files, '/');
- fd2path(fd, fdpath, 1024);
- j = sprint((char*)buf, "crypt %s %s ", base ? base+1 : *files, fdpath);
-
- for(i=0; i<32; i++) {
- sprint((char*)&buf[j], "%02X", s.Master[i]);
- j += 2;
- }
- buf[j++] = '\n';
- close(fd);
- if(mode == Install) {
- fd = open("/dev/fs/ctl", OWRITE);
- write(fd, buf, j);
- close(fd);
- } else {
- write(1, buf, j);
- }
- oldpass=1;
- }
-}
\ No newline at end of file
--- a/sys/src/cmd/cryptsetup/mkfile
+++ /dev/null
@@ -1,10 +1,0 @@
-</$objtype/mkfile
-
-BIN=/$objtype/bin
-TARG=cryptsetup
-OFILES=\
- cryptsetup.$O\
- readcons.$O\
- pbkdf2.$O\
-
-</sys/src/cmd/mkone
--- a/sys/src/cmd/cryptsetup/pbkdf2.c
+++ /dev/null
@@ -1,77 +1,0 @@
-/* $OpenBSD: pbkdf2.c,v 1.1 2008/06/14 06:28:27 djm Exp $ */
-
-/*-
- * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
- *
- * 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;
-}
--- a/sys/src/cmd/cryptsetup/readcons.c
+++ /dev/null
@@ -1,77 +1,0 @@
-/* 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/cmd/disk/cryptsetup.c
@@ -1,0 +1,212 @@
+// Original Author Taru Karttunen <taruti@taruti.net>
+// This file can be used as both Public Domain or Creative Commons CC0.
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <authsrv.h>
+
+typedef struct {
+ uchar Salt[16];
+ uchar Key[32];
+} Slot;
+
+typedef struct {
+ uchar Master[32];
+ Slot Slots[8];
+ AESstate C1, C2;
+} XtsState;
+
+uchar zeros[16] = {0};
+uchar buf[64*1024];
+AESstate cbc;
+XtsState s;
+
+void
+setupkey(char *pass, uchar salt[16], AESstate *aes)
+{
+ uchar tkey[32];
+
+ pbkdf2_x((uchar*)pass, strlen(pass), salt, 16, 9999, tkey, 32, hmac_sha1, SHA1dlen);
+ setupAESstate(aes, tkey, 16, zeros);
+ memset(tkey, 0, sizeof(tkey));
+}
+
+void
+freepass(char *pass)
+{
+ if(pass != nil){
+ memset(pass, 0, strlen(pass));
+ free(pass);
+ }
+}
+
+void
+cformat(char *files[])
+{
+ char *pass, *tmp;
+ int fd, i, j;
+
+ pass = nil;
+ do {
+ freepass(pass);
+ pass = readcons("Password", nil, 1);
+ if(pass == nil || pass[0] == 0)
+ sysfatal("input aborted");
+ tmp = readcons("Confirm", nil, 1);
+ if(tmp == nil || tmp[0] == 0)
+ sysfatal("input aborted");
+ i = strcmp(pass, tmp);
+ freepass(tmp);
+ } while(i != 0);
+
+ for(;*files != nil; files++) {
+ genrandom((uchar*)&s, sizeof(s));
+ setupkey(pass, s.Slots[0].Salt, &cbc);
+ memcpy(s.Slots[0].Key, s.Master, 32);
+ aesCBCencrypt(s.Slots[0].Key, 32, &cbc);
+
+ genrandom(buf, 16*4096);
+ for(i=0; i<16; i++)
+ for(j=0; j<8; j++) {
+ 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(*files, OWRITE)) < 0)
+ sysfatal("open disk: %r");
+
+ /* make the pad for checking crypto */
+ for(i=0; i<8; i++)
+ buf[(64*1024)-8+i] = ~buf[(64*1024)-16+i];
+
+ setupAESstate(&cbc, s.Master, 16, zeros);
+ aes_encrypt(cbc.ekey, cbc.rounds, &buf[(64*1024)-16], &buf[(64*1024)-16]);
+
+ if(write(fd, buf, 64*1024) != 64*1024)
+ sysfatal("writing disk: %r");
+ }
+}
+
+void
+copen(char *files[], int ctl)
+{
+ char *pass, *name;
+ uchar cbuf[16];
+ int fd, i, j;
+
+ pass = nil;
+ for(;*files != nil; files++) {
+ memset(&s, 0, sizeof(s));
+ if((fd = open(*files, OREAD)) < 0)
+ sysfatal("open disk: %r");
+
+ if(read(fd, buf, 1024*64) != 1024*64)
+ sysfatal("read disk: %r");
+
+ retrypass:
+ 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];
+ }
+
+ if(pass == nil){
+ pass = readcons("Password", nil, 1);
+ if(pass == nil || pass[0] == 0)
+ sysfatal("input aborted");
+ }
+
+ setupkey(pass, s.Slots[0].Salt, &cbc);
+ memcpy(s.Master, s.Slots[0].Key, 32);
+ aesCBCdecrypt(s.Master, 32, &cbc);
+ setupAESstate(&cbc, s.Master, 16, zeros);
+
+ memcpy(cbuf, &buf[(64*1024)-16], 16);
+ aes_decrypt(cbc.dkey, cbc.rounds, cbuf, cbuf);
+
+ /* make the pad for checking crypto */
+ for(i=0; i<8; i++)
+ if((cbuf[i] ^ cbuf[i+8]) != 255) {
+ freepass(pass);
+ pass = nil;
+ fprint(2, "wrong key\n");
+ goto retrypass;
+ }
+
+ fd2path(fd, (char*)buf, sizeof(buf));
+ close(fd);
+
+ if((name = strrchr(*files, '/')) != nil)
+ name++;
+ else
+ name = *files;
+
+ if(fprint(ctl, "crypt %q %q %.32H\n", name, (char*)buf, s.Master) < 0)
+ sysfatal("write: %r");
+ }
+}
+
+void
+usage(void)
+{
+ print("usage:\n"
+ "%s -f files\t\t# Format file or device\n"
+ "%s -o files\t\t# Print commandline for open\n"
+ "%s -i files\t\t# Install (open) files\n",
+ argv0, argv0, argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ enum {
+ NoMode,
+ Format,
+ Open,
+ Install,
+ };
+ int mode, ctl;
+
+ quotefmtinstall();
+ fmtinstall('H', encodefmt);
+
+ ctl = 1;
+ mode = NoMode;
+
+ ARGBEGIN {
+ default:
+ usage();
+ case 'f':
+ mode = Format;
+ break;
+ case 'o':
+ mode = Open;
+ break;
+ case 'i':
+ mode = Install;
+ break;
+ } ARGEND;
+
+ if(argc < 0)
+ usage();
+
+ switch(mode){
+ default:
+ usage();
+ case Format:
+ cformat(argv);
+ break;
+ case Install:
+ if((ctl = open("/dev/fs/ctl", OWRITE)) < 0)
+ sysfatal("open ctl: %r");
+ /* no break */
+ case Open:
+ copen(argv, ctl);
+ break;
+ }
+
+ exits(nil);
+}
--- a/sys/src/cmd/disk/mkfile
+++ b/sys/src/cmd/disk/mkfile
@@ -6,6 +6,7 @@
mkext\
mkfs\
partfs\
+ cryptsetup\
DIRS=\
9660\