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
This commit is contained in:
cinap_lenrek 2016-10-24 20:56:11 +02:00
parent f3feafc476
commit 234137bce3
10 changed files with 226 additions and 384 deletions

View file

@ -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 @@ is the last path element of the
.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)

View file

@ -11,9 +11,9 @@ $objtype
cat
cfs
chmod
cryptsetup
dd
disk
cryptsetup
edisk
fdisk
prep

View file

@ -90,7 +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 */
Key *key; /* crypt key */
};
struct Tree
@ -351,6 +351,7 @@ mdeldev(Fsdev *mp)
}
wunlock(&lck);
secfree(mp->key);
free(mp->name);
for(i = 0; i < mp->ndevs; i++){
in = mp->inner[i];
@ -359,8 +360,6 @@ mdeldev(Fsdev *mp)
free(in->iname);
free(in);
}
if(debug)
memset(mp, 9, sizeof *mp); /* poison */
free(mp);
}
@ -553,7 +552,7 @@ mconfig(char* a, long n)
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 @@ mconfig(char* a, long n)
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 @@ Fail:
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 @@ Fail:
}
setdsize(mp, ilen);
poperror();
wunlock(&lck);
free(idev);
@ -1033,7 +1029,7 @@ cryptio(Fsdev *mp, int isread, uchar *a, long l, vlong off)
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)

View file

@ -1,14 +0,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;

View file

@ -1,189 +0,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;
}
}

View file

@ -1,10 +0,0 @@
</$objtype/mkfile
BIN=/$objtype/bin
TARG=cryptsetup
OFILES=\
cryptsetup.$O\
readcons.$O\
pbkdf2.$O\
</sys/src/cmd/mkone

View file

@ -1,77 +0,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;
}

View file

@ -1,77 +0,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];
}
}
}

View file

@ -0,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);
}

View file

@ -6,6 +6,7 @@ TARG=exsort\
mkext\
mkfs\
partfs\
cryptsetup\
DIRS=\
9660\