improve PBKDF2 performance
The PBKDF2 in sys/geom/eli/pkcs5v2.c is around half the speed it could be GELI's PBKDF2 uses a simple benchmark to determine a number of iterations that will takes approximately 2 seconds. The security provided is actually half what is expected, because an attacker could use the optimized algorithm to brute force the key in half the expected time. With this change, all newly generated GELI keys will be approximately 2x as strong. Previously generated keys will talk half as long to calculate, resulting in faster mounting of encrypted volumes. Users may choose to rekey, to generate a new key with the larger default number of iterations using the geli(8) setkey command. Security of existing data is not compromised, as ~1 second per brute force attempt is still a very high threshold. PR: 202365 Original Research: https://jbp.io/2015/08/11/pbkdf2-performance-matters/ Submitted by: Joe Pixton <jpixton@gmail.com> (Original Version), jmg (Later Version) Reviewed by: ed, pjd, delphij Approved by: secteam, pjd (maintainer) MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D8236
This commit is contained in:
parent
d7aee58a78
commit
85c15ab853
@ -425,6 +425,10 @@
|
|||||||
..
|
..
|
||||||
..
|
..
|
||||||
..
|
..
|
||||||
|
eli
|
||||||
|
pbkdf2
|
||||||
|
..
|
||||||
|
..
|
||||||
..
|
..
|
||||||
kern
|
kern
|
||||||
acct
|
acct
|
||||||
|
@ -24,6 +24,10 @@ WARNS?= 0
|
|||||||
.PATH: ${.CURDIR}/../../../lib/libc/string
|
.PATH: ${.CURDIR}/../../../lib/libc/string
|
||||||
SRCS+= bcmp.c bcopy.c bzero.c
|
SRCS+= bcmp.c bcopy.c bzero.c
|
||||||
|
|
||||||
|
# need explicit_bzero for crypto
|
||||||
|
.PATH: ${.CURDIR}/../../../sys/libkern
|
||||||
|
SRCS+= explicit_bzero.c
|
||||||
|
|
||||||
# Our password input method
|
# Our password input method
|
||||||
SRCS+= pwgets.c
|
SRCS+= pwgets.c
|
||||||
|
|
||||||
|
@ -692,8 +692,8 @@ int g_eli_crypto_decrypt(u_int algo, u_char *data, size_t datasize,
|
|||||||
const u_char *key, size_t keysize);
|
const u_char *key, size_t keysize);
|
||||||
|
|
||||||
struct hmac_ctx {
|
struct hmac_ctx {
|
||||||
SHA512_CTX shactx;
|
SHA512_CTX innerctx;
|
||||||
u_char k_opad[128];
|
SHA512_CTX outerctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
void g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey,
|
void g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey,
|
||||||
|
@ -47,7 +47,7 @@ void
|
|||||||
g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey,
|
g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey,
|
||||||
size_t hkeylen)
|
size_t hkeylen)
|
||||||
{
|
{
|
||||||
u_char k_ipad[128], key[128];
|
u_char k_ipad[128], k_opad[128], key[128];
|
||||||
SHA512_CTX lctx;
|
SHA512_CTX lctx;
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
@ -66,13 +66,17 @@ g_eli_crypto_hmac_init(struct hmac_ctx *ctx, const uint8_t *hkey,
|
|||||||
/* XOR key with ipad and opad values. */
|
/* XOR key with ipad and opad values. */
|
||||||
for (i = 0; i < sizeof(key); i++) {
|
for (i = 0; i < sizeof(key); i++) {
|
||||||
k_ipad[i] = key[i] ^ 0x36;
|
k_ipad[i] = key[i] ^ 0x36;
|
||||||
ctx->k_opad[i] = key[i] ^ 0x5c;
|
k_opad[i] = key[i] ^ 0x5c;
|
||||||
}
|
}
|
||||||
bzero(key, sizeof(key));
|
explicit_bzero(key, sizeof(key));
|
||||||
/* Perform inner SHA512. */
|
/* Start inner SHA512. */
|
||||||
SHA512_Init(&ctx->shactx);
|
SHA512_Init(&ctx->innerctx);
|
||||||
SHA512_Update(&ctx->shactx, k_ipad, sizeof(k_ipad));
|
SHA512_Update(&ctx->innerctx, k_ipad, sizeof(k_ipad));
|
||||||
bzero(k_ipad, sizeof(k_ipad));
|
explicit_bzero(k_ipad, sizeof(k_ipad));
|
||||||
|
/* Start outer SHA512. */
|
||||||
|
SHA512_Init(&ctx->outerctx);
|
||||||
|
SHA512_Update(&ctx->outerctx, k_opad, sizeof(k_opad));
|
||||||
|
explicit_bzero(k_opad, sizeof(k_opad));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -80,28 +84,27 @@ g_eli_crypto_hmac_update(struct hmac_ctx *ctx, const uint8_t *data,
|
|||||||
size_t datasize)
|
size_t datasize)
|
||||||
{
|
{
|
||||||
|
|
||||||
SHA512_Update(&ctx->shactx, data, datasize);
|
SHA512_Update(&ctx->innerctx, data, datasize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
g_eli_crypto_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize)
|
g_eli_crypto_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize)
|
||||||
{
|
{
|
||||||
u_char digest[SHA512_MDLEN];
|
u_char digest[SHA512_MDLEN];
|
||||||
SHA512_CTX lctx;
|
|
||||||
|
|
||||||
SHA512_Final(digest, &ctx->shactx);
|
/* Complete inner hash */
|
||||||
/* Perform outer SHA512. */
|
SHA512_Final(digest, &ctx->innerctx);
|
||||||
SHA512_Init(&lctx);
|
|
||||||
SHA512_Update(&lctx, ctx->k_opad, sizeof(ctx->k_opad));
|
/* Complete outer hash */
|
||||||
bzero(ctx, sizeof(*ctx));
|
SHA512_Update(&ctx->outerctx, digest, sizeof(digest));
|
||||||
SHA512_Update(&lctx, digest, sizeof(digest));
|
SHA512_Final(digest, &ctx->outerctx);
|
||||||
SHA512_Final(digest, &lctx);
|
|
||||||
bzero(&lctx, sizeof(lctx));
|
explicit_bzero(ctx, sizeof(*ctx));
|
||||||
/* mdsize == 0 means "Give me the whole hash!" */
|
/* mdsize == 0 means "Give me the whole hash!" */
|
||||||
if (mdsize == 0)
|
if (mdsize == 0)
|
||||||
mdsize = SHA512_MDLEN;
|
mdsize = SHA512_MDLEN;
|
||||||
bcopy(digest, md, mdsize);
|
bcopy(digest, md, mdsize);
|
||||||
bzero(digest, sizeof(digest));
|
explicit_bzero(digest, sizeof(digest));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -56,6 +56,7 @@ pkcs5v2_genkey(uint8_t *key, unsigned keylen, const uint8_t *salt,
|
|||||||
uint8_t *counter, *keyp;
|
uint8_t *counter, *keyp;
|
||||||
u_int i, bsize, passlen;
|
u_int i, bsize, passlen;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
|
struct hmac_ctx startpoint, ctx;
|
||||||
|
|
||||||
passlen = strlen(passphrase);
|
passlen = strlen(passphrase);
|
||||||
bzero(key, keylen);
|
bzero(key, keylen);
|
||||||
@ -66,20 +67,23 @@ pkcs5v2_genkey(uint8_t *key, unsigned keylen, const uint8_t *salt,
|
|||||||
for (count = 1; keylen > 0; count++, keylen -= bsize, keyp += bsize) {
|
for (count = 1; keylen > 0; count++, keylen -= bsize, keyp += bsize) {
|
||||||
bsize = MIN(keylen, sizeof(md));
|
bsize = MIN(keylen, sizeof(md));
|
||||||
|
|
||||||
counter[0] = (count >> 24) & 0xff;
|
be32enc(counter, count);
|
||||||
counter[1] = (count >> 16) & 0xff;
|
|
||||||
counter[2] = (count >> 8) & 0xff;
|
g_eli_crypto_hmac_init(&startpoint, passphrase, passlen);
|
||||||
counter[3] = count & 0xff;
|
ctx = startpoint;
|
||||||
g_eli_crypto_hmac(passphrase, passlen, saltcount,
|
g_eli_crypto_hmac_update(&ctx, saltcount, sizeof(saltcount));
|
||||||
sizeof(saltcount), md, 0);
|
g_eli_crypto_hmac_final(&ctx, md, sizeof(md));
|
||||||
xor(keyp, md, bsize);
|
xor(keyp, md, bsize);
|
||||||
|
|
||||||
for(i = 1; i < iterations; i++) {
|
for(i = 1; i < iterations; i++) {
|
||||||
g_eli_crypto_hmac(passphrase, passlen, md, sizeof(md),
|
ctx = startpoint;
|
||||||
md, 0);
|
g_eli_crypto_hmac_update(&ctx, md, sizeof(md));
|
||||||
|
g_eli_crypto_hmac_final(&ctx, md, sizeof(md));
|
||||||
xor(keyp, md, bsize);
|
xor(keyp, md, bsize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
explicit_bzero(&startpoint, sizeof(startpoint));
|
||||||
|
explicit_bzero(&ctx, sizeof(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _KERNEL
|
#ifndef _KERNEL
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
TESTSDIR= ${TESTSBASE}/sys/geom
|
TESTSDIR= ${TESTSBASE}/sys/geom
|
||||||
|
|
||||||
TESTS_SUBDIRS+= class
|
TESTS_SUBDIRS+= class
|
||||||
|
TESTS_SUBDIRS+= eli
|
||||||
|
|
||||||
.include <bsd.test.mk>
|
.include <bsd.test.mk>
|
||||||
|
9
tests/sys/geom/eli/Makefile
Normal file
9
tests/sys/geom/eli/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
|
TESTSDIR= ${TESTSBASE}/sys/geom/eli
|
||||||
|
|
||||||
|
ATF_TESTS_SUBDIRS+= pbkdf2
|
||||||
|
|
||||||
|
.include <bsd.test.mk>
|
28
tests/sys/geom/eli/pbkdf2/Makefile
Normal file
28
tests/sys/geom/eli/pbkdf2/Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
.include <bsd.own.mk>
|
||||||
|
|
||||||
|
SYSDIR = ../../../../../sys
|
||||||
|
.PATH: ${SYSDIR}/geom/eli
|
||||||
|
.PATH: ${SYSDIR}/crypto/sha2
|
||||||
|
|
||||||
|
TESTSDIR= ${TESTBASE}/sys/geom/eli/pbkdf2
|
||||||
|
|
||||||
|
ATF_TESTS_C= pbkdf2
|
||||||
|
|
||||||
|
CFLAGS.pbkdf2= -I${SYSDIR}
|
||||||
|
|
||||||
|
SRCS.pbkdf2= \
|
||||||
|
hmactest.c \
|
||||||
|
g_eli_crypto.c \
|
||||||
|
g_eli_hmac.c \
|
||||||
|
pkcs5v2.c \
|
||||||
|
sha512c.c \
|
||||||
|
sha256c.c
|
||||||
|
|
||||||
|
LIBADD.pbkdf2= crypto
|
||||||
|
|
||||||
|
testvect.h: gentestvect.py
|
||||||
|
python gentestvect.py > $@
|
||||||
|
|
||||||
|
.include <bsd.test.mk>
|
63
tests/sys/geom/eli/pbkdf2/gentestvect.py
Normal file
63
tests/sys/geom/eli/pbkdf2/gentestvect.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
from hashlib import pbkdf2_hmac
|
||||||
|
import hashlib
|
||||||
|
import itertools
|
||||||
|
import string
|
||||||
|
|
||||||
|
#From: https://stackoverflow.com/questions/14945095/how-to-escape-string-for-generated-c
|
||||||
|
def cstring(s, encoding='ascii'):
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
s = s.encode(encoding)
|
||||||
|
|
||||||
|
result = ''
|
||||||
|
for c in s:
|
||||||
|
if not (32 <= ord(c) < 127) or c in ('\\', '"'):
|
||||||
|
result += '\\%03o' % ord(c)
|
||||||
|
else:
|
||||||
|
result += c
|
||||||
|
|
||||||
|
return '"' + result + '"'
|
||||||
|
|
||||||
|
intarr = lambda y: ', '.join(map(lambda x: str(ord(x)), y))
|
||||||
|
|
||||||
|
_randfd = open('/dev/urandom', 'rb')
|
||||||
|
_maketrans = string.maketrans('', '')
|
||||||
|
def randgen(l, delchrs=None):
|
||||||
|
if delchrs is None:
|
||||||
|
return _randfd.read(l)
|
||||||
|
|
||||||
|
s = ''
|
||||||
|
while len(s) < l:
|
||||||
|
s += string.translate(_randfd.read(l - len(s)), _maketrans,
|
||||||
|
delchrs)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def printhmacres(salt, passwd, itr, hmacout):
|
||||||
|
print '\t{ %s, %d, %s, %d, %s, %d },' % (cstring(salt), len(salt),
|
||||||
|
cstring(passwd), itr, cstring(hmacout), len(hmacout))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
hashfun = 'sha512'
|
||||||
|
else:
|
||||||
|
hashfun = sys.argv[1]
|
||||||
|
|
||||||
|
if hashfun not in hashlib.algorithms:
|
||||||
|
print 'Invalid hash function: %s' % `hashfun`
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print '/* Test Vectors for PBKDF2-%s */' % hashfun.upper()
|
||||||
|
print '\t/* salt, saltlen, passwd, itr, hmacout, hmacoutlen */'
|
||||||
|
for saltl in xrange(8, 64, 8):
|
||||||
|
for itr in itertools.chain(xrange(100, 1000, 100), xrange(1000,
|
||||||
|
10000, 1000)):
|
||||||
|
for passlen in xrange(8, 80, 8):
|
||||||
|
salt = randgen(saltl)
|
||||||
|
passwd = randgen(passlen, '\x00')
|
||||||
|
hmacout = pbkdf2_hmac(hashfun, passwd, salt,
|
||||||
|
itr)
|
||||||
|
printhmacres(salt, passwd, itr, hmacout)
|
41
tests/sys/geom/eli/pbkdf2/hmactest.c
Normal file
41
tests/sys/geom/eli/pbkdf2/hmactest.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* $FreeBSD$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <atf-c.h>
|
||||||
|
|
||||||
|
#include <geom/eli/pkcs5v2.h>
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
char *salt;
|
||||||
|
size_t saltlen;
|
||||||
|
char *passwd;
|
||||||
|
int iterations;
|
||||||
|
char *hmacout;
|
||||||
|
size_t hmaclen;
|
||||||
|
} testdata[] = {
|
||||||
|
#include "testvect.h"
|
||||||
|
};
|
||||||
|
|
||||||
|
ATF_TC_WITHOUT_HEAD(hmactest);
|
||||||
|
ATF_TC_BODY(hmactest, tc)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint8_t hmacout[64];
|
||||||
|
|
||||||
|
for (i = 0; i < nitems(testdata); i++) {
|
||||||
|
pkcs5v2_genkey(hmacout, testdata[i].hmaclen,
|
||||||
|
(uint8_t *)testdata[i].salt, testdata[i].saltlen,
|
||||||
|
testdata[i].passwd, testdata[i].iterations);
|
||||||
|
ATF_REQUIRE(bcmp(hmacout, testdata[i].hmacout,
|
||||||
|
testdata[i].hmaclen) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ATF_TP_ADD_TCS(tp)
|
||||||
|
{
|
||||||
|
ATF_TP_ADD_TC(tp, hmactest);
|
||||||
|
|
||||||
|
return (atf_no_error());
|
||||||
|
}
|
1137
tests/sys/geom/eli/pbkdf2/testvect.h
Normal file
1137
tests/sys/geom/eli/pbkdf2/testvect.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user