Change default encryption key length to 256 bits.

Add optional ability to change key length at runtime via cli option.
Include key length property in archive header.
Fix header HMAC to include salt, nonce and key length properties.
Retain backward compatibility to handle older format archives.
Fix compilation of AES ASM code.
This commit is contained in:
Moinak Ghosh 2013-03-03 20:02:14 +05:30
parent 72b23dac1a
commit 7a29c7be1e
8 changed files with 132 additions and 58 deletions

View file

@ -287,7 +287,7 @@ $(CRYPTO_OBJS): $(CRYPTO_SRCS) $(CRYPTO_HDRS) $(CRYPTO_ASM_OBJS)
$(COMPILE) $(GEN_OPT) $(CRYPTO_CPPFLAGS) $(CPPFLAGS) $(@:.o=.c) -o $@
$(CRYPTO_ASM_OBJS): $(CRYPTO_ASM_SRCS) $(CRYPTO_ASM_HDRS)
$(YASM) -o $@ $(@:.o=.s)
$(COMPILE) $(GEN_OPT) $(CRYPTO_CPPFLAGS) $(CPPFLAGS) -o $@ $(@:.o=.s)
$(CRYPTO_COMPAT_OBJS): $(CRYPTO_COMPAT_SRCS) $(CRYPTO_COMPAT_HDRS)
$(COMPILE) $(GEN_OPT) $(CRYPTO_CPPFLAGS) $(CPPFLAGS) $(@:.o=.c) -o $@

View file

@ -131,13 +131,11 @@ NOTE: The option "libbsc" uses Ilya Grebnov's block sorting compression library
'-C' - Display compression statistics
Encryption flags:
'-e' Encrypt chunks with AES. The password can be prompted from the user
or read from a file. Whether 128-Bit or 256-Bit keys are used depends
on how the pcompress binary was built. Default build uses 128-Bit keys.
Unique keys are generated every time pcompress is run even when giving
the same password. Of course enough info is stored in the compressed
file so that the key used for the file can be re-created given the
correct password.
'-e' Encrypt chunks with AES-CTR. The password can be prompted from the user
or read from a file. Unique keys are generated every time pcompress is
run even when giving the same password. Of course enough info is stored\
in the compresse file so that the key used for the file can be
re-created given the correct password.
The Scrypt algorithm from Tarsnap is used
(See: http://www.tarsnap.com/scrypt.html) for generating keys from
@ -148,6 +146,10 @@ NOTE: The option "libbsc" uses Ilya Grebnov's block sorting compression library
be readable and writable since it is zeroed out after the password is
read.
'-k <key length>'
Specify the key length. Can be 16 for 128 bit keys or 32 for 256 bit
keys. Default value is 23 for 256 bit keys.
NOTE: When using pipe-mode via -p the only way to provide a password is to use '-w'.
Environment Variables

21
config
View file

@ -13,20 +13,21 @@ ${prog} [<options>]
--enable-debug-stats Enable printing of some verbose debug info (default: disabled).
--with-libbsc=<path to libbsc source>
Enable support for libbsc (See: libbsc.com). Full path to the libbsc
source tree must be provided. It links the library statically.
source tree must be provided. It links the library statically.
--with-openssl=<path to OpenSSL installation tree> (Default: System)
This defaults to the system's OpenSSL library. You can use this option
if you want to use an alternate OpenSSL installation.
This defaults to the system's OpenSSL library. You can use this option
if you want to use an alternate OpenSSL installation.
--with-zlib=<path to zlib installation tree> (Default: System)
Enable building against an alternate Zlib installation.
Enable building against an alternate Zlib installation.
--with-bzlib=<path to Bzip2 library installation tree> (Default: System)
Enable building against an alternate Bzip2 and library installation.
Enable building against an alternate Bzip2 and library installation.
--no-sse-detect Do NOT attempt to probe the system's SSE/AVX capability for build flags.
--no-1.3-archive-compat Disable compatibility with compressed archives created with Pcompress
version 1.3 (default: retain compatibility). Hash formats changed from
version 1.3 to 1.4 so this option is required if files created using
1.3 need to be decompressed by version 1.4 onwards.
--use-key256 Use 256-bit encryption keys. Default key length is 128-bit.
version 1.3 (default: retain compatibility). Hash formats changed from
version 1.3 to 1.4 so this option is required if files created using
1.3 need to be decompressed by version 1.4 onwards.
--limit-key128 Limit key length to 128-bit encryption keys. Otherwise the default is to
use 256-bit keys changeable at runtime via the '-k <keylen>' option.
--help Display this help message.
_EOF
@ -145,7 +146,7 @@ do
bzlib_prefix=`echo ${arg1} | cut -f2 -d"="`
;;
--use-key256)
keylen='-DKEYLEN=32'
keylen='-DDEFAULT_KEYLEN=16'
;;
--no-sse-detect)
sse_detect=0

View file

@ -149,21 +149,21 @@ aes_init(aes_ctx_t *ctx, uchar_t *salt, int saltlen, uchar_t *pwd, int pwd_len,
pickparams(&logN, &r, &p);
N = (uint64_t)(1) << logN;
if (crypto_scrypt(pwd, pwd_len, salt, saltlen, N, r, p, key, KEYLEN)) {
if (crypto_scrypt(pwd, pwd_len, salt, saltlen, N, r, p, key, ctx->keylen)) {
fprintf(stderr, "Scrypt failed\n");
return (-1);
}
#else
rv = PKCS5_PBKDF2_HMAC(pwd, pwd_len, salt, saltlen, PBE_ROUNDS, EVP_sha256(),
KEYLEN, key);
if (rv != KEYLEN) {
fprintf(stderr, "Key size is %d bytes - should be %d bits\n", i, KEYLEN);
ctx->keylen, key);
if (rv != ctx->keylen) {
fprintf(stderr, "Key size is %d bytes - should be %d bits\n", i, ctx->keylen);
return (-1);
}
#endif
if (enc) {
enc_setkey(key, (KEYLEN << 3), &(ctx->key));
enc_setkey(key, (ctx->keylen << 3), &(ctx->key));
// Derive nonce from salt
if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) {
time((time_t *)&tv);
@ -181,7 +181,7 @@ aes_init(aes_ctx_t *ctx, uchar_t *salt, int saltlen, uchar_t *pwd, int pwd_len,
tv = 0;
} else {
ctx->nonce = nonce;
enc_setkey(key, (KEYLEN << 3), &(ctx->key));
enc_setkey(key, (ctx->keylen << 3), &(ctx->key));
}
return (0);
}
@ -243,7 +243,7 @@ aes_nonce(aes_ctx_t *ctx)
void
aes_clean_pkey(aes_ctx_t *ctx)
{
memset(ctx->pkey, 0, KEYLEN);
memset(ctx->pkey, 0, ctx->keylen);
}
void

View file

@ -30,20 +30,19 @@
#include <openssl/evp.h>
#endif
#include <openssl/opensslv.h>
#include <crypto_utils.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef KEYLEN
#define KEYLEN 16
#endif
#define PBE_ROUNDS 1000
typedef struct {
uint64_t nonce;
AES_KEY key;
uchar_t pkey[KEYLEN];
int keylen;
uchar_t pkey[MAX_KEYLEN];
} aes_ctx_t;
int aes_init(aes_ctx_t *ctx, uchar_t *salt, int saltlen, uchar_t *pwd, int pwd_len,

View file

@ -407,7 +407,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
if (cksum == CKSUM_BLAKE256) {
blake2b_state *ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
if (!ctx) return (-1);
if (bdsp.blake2b_init_key(ctx, 32, actx->pkey, KEYLEN) != 0)
if (bdsp.blake2b_init_key(ctx, 32, actx->pkey, cctx->keylen) != 0)
return (-1);
mctx->mac_ctx = ctx;
ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
@ -421,7 +421,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
} else if (cksum == CKSUM_BLAKE512) {
blake2b_state *ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
if (!ctx) return (-1);
if (bdsp.blake2b_init_key(ctx, 64, actx->pkey, KEYLEN) != 0)
if (bdsp.blake2b_init_key(ctx, 64, actx->pkey, cctx->keylen) != 0)
return (-1);
mctx->mac_ctx = ctx;
ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
@ -436,7 +436,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
Skein_512_Ctxt_t *ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) return (-1);
Skein_512_InitExt(ctx, 256, SKEIN_CFG_TREE_INFO_SEQUENTIAL,
actx->pkey, KEYLEN);
actx->pkey, cctx->keylen);
mctx->mac_ctx = ctx;
ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) {
@ -450,7 +450,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
Skein_512_Ctxt_t *ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) return (-1);
Skein_512_InitExt(ctx, 512, SKEIN_CFG_TREE_INFO_SEQUENTIAL,
actx->pkey, KEYLEN);
actx->pkey, cctx->keylen);
mctx->mac_ctx = ctx;
ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) {
@ -465,7 +465,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
if (!ctx) return (-1);
HMAC_CTX_init(ctx);
HMAC_Init_ex(ctx, actx->pkey, KEYLEN, EVP_sha256(), NULL);
HMAC_Init_ex(ctx, actx->pkey, cctx->keylen, EVP_sha256(), NULL);
mctx->mac_ctx = ctx;
ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
@ -482,7 +482,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
} else {
HMAC_SHA512_Context *ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
if (!ctx) return (-1);
opt_HMAC_SHA512t256_Init(ctx, actx->pkey, KEYLEN);
opt_HMAC_SHA512t256_Init(ctx, actx->pkey, cctx->keylen);
mctx->mac_ctx = ctx;
ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
@ -498,7 +498,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
if (!ctx) return (-1);
HMAC_CTX_init(ctx);
HMAC_Init_ex(ctx, actx->pkey, KEYLEN, EVP_sha512(), NULL);
HMAC_Init_ex(ctx, actx->pkey, cctx->keylen, EVP_sha512(), NULL);
mctx->mac_ctx = ctx;
ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
@ -515,7 +515,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
} else {
HMAC_SHA512_Context *ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
if (!ctx) return (-1);
opt_HMAC_SHA512_Init(ctx, actx->pkey, KEYLEN);
opt_HMAC_SHA512_Init(ctx, actx->pkey, cctx->keylen);
mctx->mac_ctx = ctx;
ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
@ -538,7 +538,7 @@ hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
if (Keccak_Init(ctx, 512) != 0)
return (-1);
}
if (Keccak_Update(ctx, actx->pkey, KEYLEN << 3) != 0)
if (Keccak_Update(ctx, actx->pkey, cctx->keylen << 3) != 0)
return (-1);
mctx->mac_ctx = ctx;
@ -719,11 +719,13 @@ hmac_cleanup(mac_ctx_t *mctx)
*/
int
init_crypto(crypto_ctx_t *cctx, uchar_t *pwd, int pwd_len, int crypto_alg,
uchar_t *salt, int saltlen, uint64_t nonce, int enc_dec)
uchar_t *salt, int saltlen, int keylen, uint64_t nonce, int enc_dec)
{
if (crypto_alg == CRYPTO_ALG_AES) {
aes_ctx_t *actx = (aes_ctx_t *)malloc(sizeof (aes_ctx_t));
aes_module_init(&proc_info);
cctx->keylen = keylen;
actx->keylen = keylen;
if (enc_dec) {
/*

View file

@ -37,6 +37,16 @@ extern "C" {
#define CKSUM_MAX_BYTES 64
#define DEFAULT_CKSUM "BLAKE256"
/*
* Default key length for Encryption and Decryption
*/
#ifndef DEFAULT_KEYLEN
#define DEFAULT_KEYLEN 32
#define MAX_KEYLEN 32
#else
#define MAX_KEYLEN DEFAULT_KEYLEN
#endif
#define ENCRYPT_FLAG 1
#define DECRYPT_FLAG 0
#define CRYPTO_ALG_AES 0x10
@ -71,6 +81,7 @@ typedef struct {
int enc_dec;
uchar_t *salt;
int saltlen;
int keylen;
} crypto_ctx_t;
typedef struct {
@ -93,7 +104,7 @@ void deserialize_checksum(uchar_t *checksum, uchar_t *buf, int cksum_bytes);
* Encryption related functions.
*/
int init_crypto(crypto_ctx_t *cctx, uchar_t *pwd, int pwd_len, int crypto_alg,
uchar_t *salt, int saltlen, uint64_t nonce, int enc_dec);
uchar_t *salt, int saltlen, int keylen, uint64_t nonce, int enc_dec);
int crypto_buf(crypto_ctx_t *cctx, uchar_t *from, uchar_t *to, uint64_t bytes, uint64_t id);
uint64_t crypto_nonce(crypto_ctx_t *cctx);
void crypto_clean_pkey(crypto_ctx_t *cctx);

95
main.c
View file

@ -98,7 +98,7 @@ static int do_compress = 0;
static int do_uncompress = 0;
static int cksum_bytes, mac_bytes;
static int cksum = 0, t_errored = 0;
static int rab_blk_size = 0;
static int rab_blk_size = 0, keylen;
static crypto_ctx_t crypto_ctx;
static char *pwd_file = NULL, *f_name = NULL;
@ -169,6 +169,18 @@ usage(void)
" - Specify an average Dedupe block size. 1 - 4K, 2 - 8K ... 5 - 64K.\n"
" '-M' - Display memory allocator statistics\n"
" '-C' - Display compression statistics\n\n");
fprintf(stderr, "\n"
"8) Encryption flags:\n"
" '-e' - Encrypt chunks with AES-CTR. The password can be prompted from the\n"
" user or read from a file. Unique keys are generated every time\n"
" pcompress is run even when giving the same password.\n"
" '-w <pathname>'\n"
" - Provide a file which contains the encryption password. This file must\n"
" be readable and writable since it is zeroed out after the password is\n"
" read.\n"
" '-k <key length>\n"
" - Specify key length. Can be 16 for 128 bit or 32 for 256 bit. Default\n"
" is 32 for 256 bit keys.\n\n");
}
void
@ -787,7 +799,7 @@ start_decompress(const char *filename, const char *to_filename)
if (flags & MASK_CRYPTO_ALG) {
int saltlen;
uchar_t *salt1, *salt2;
uint64_t nonce;
uint64_t nonce, d3;
uchar_t pw[MAX_PW_LEN];
int pw_len;
mac_ctx_t hdr_mac;
@ -817,20 +829,34 @@ start_decompress(const char *filename, const char *to_filename)
UNCOMP_BAIL;
}
deserialize_checksum(salt2, salt1, saltlen);
memset(salt1, 0, saltlen);
free(salt1);
if (Read(compfd, &nonce, sizeof (nonce)) < sizeof (nonce)) {
memset(salt2, 0, saltlen);
free(salt2);
memset(salt1, 0, saltlen);
free(salt1);
perror("Read: ");
UNCOMP_BAIL;
}
nonce = ntohll(nonce);
if (version > 6) {
if (Read(compfd, &keylen, sizeof (keylen)) < sizeof (keylen)) {
memset(salt2, 0, saltlen);
free(salt2);
memset(salt1, 0, saltlen);
free(salt1);
perror("Read: ");
UNCOMP_BAIL;
}
keylen = ntohl(keylen);
}
if (Read(compfd, hdr_hash1, mac_bytes) < mac_bytes) {
memset(salt2, 0, saltlen);
free(salt2);
memset(salt1, 0, saltlen);
free(salt1);
perror("Read: ");
UNCOMP_BAIL;
}
@ -842,6 +868,8 @@ start_decompress(const char *filename, const char *to_filename)
if (pw_len == -1) {
memset(salt2, 0, saltlen);
free(salt2);
memset(salt1, 0, saltlen);
free(salt1);
err_exit(0, "Failed to get password.\n");
}
} else {
@ -874,6 +902,8 @@ start_decompress(const char *filename, const char *to_filename)
perror(" ");
memset(salt2, 0, saltlen);
free(salt2);
memset(salt1, 0, saltlen);
free(salt1);
close(uncompfd); unlink(to_filename);
err_exit(0, "Failed to get password.\n");
}
@ -881,16 +911,17 @@ start_decompress(const char *filename, const char *to_filename)
}
if (init_crypto(&crypto_ctx, pw, pw_len, encrypt_type, salt2,
saltlen, nonce, DECRYPT_FLAG) == -1) {
saltlen, keylen, nonce, DECRYPT_FLAG) == -1) {
memset(salt2, 0, saltlen);
free(salt2);
memset(salt1, 0, saltlen);
free(salt1);
memset(pw, 0, MAX_PW_LEN);
close(uncompfd); unlink(to_filename);
err_exit(0, "Failed to initialize crypto\n");
}
memset(salt2, 0, saltlen);
free(salt2);
nonce = 0;
memset(pw, 0, MAX_PW_LEN);
/*
@ -905,12 +936,24 @@ start_decompress(const char *filename, const char *to_filename)
hmac_update(&hdr_mac, (uchar_t *)&d1, sizeof (version));
d1 = htons(flags);
hmac_update(&hdr_mac, (uchar_t *)&d1, sizeof (flags));
nonce = htonll(chunksize);
hmac_update(&hdr_mac, (uchar_t *)&nonce, sizeof (nonce));
d3 = htonll(chunksize);
hmac_update(&hdr_mac, (uchar_t *)&d3, sizeof (nonce));
d2 = htonl(level);
hmac_update(&hdr_mac, (uchar_t *)&d2, sizeof (level));
if (version > 6) {
d2 = htonl(saltlen);
hmac_update(&hdr_mac, (uchar_t *)&d2, sizeof (saltlen));
hmac_update(&hdr_mac, salt1, saltlen);
nonce = htonll(nonce);
hmac_update(&hdr_mac, (uchar_t *)&nonce, sizeof (nonce));
d2 = htonl(keylen);
hmac_update(&hdr_mac, (uchar_t *)&d2, sizeof (keylen));
}
hmac_final(&hdr_mac, hdr_hash1, &hlen);
hmac_cleanup(&hdr_mac);
memset(salt1, 0, saltlen);
free(salt1);
nonce = 0;
if (memcmp(hdr_hash2, hdr_hash1, mac_bytes) != 0) {
close(uncompfd); unlink(to_filename);
err_exit(0, "Header verification failed! File tampered or wrong password.\n");
@ -1600,7 +1643,7 @@ start_compress(const char *filename, uint64_t chunksize, int level)
close(fd);
}
if (init_crypto(&crypto_ctx, pw, pw_len, encrypt_type, NULL,
0, 0, ENCRYPT_FLAG) == -1) {
0, keylen, 0, ENCRYPT_FLAG) == -1) {
memset(pw, 0, MAX_PW_LEN);
err_exit(0, "Failed to initialize crypto\n");
}
@ -1791,14 +1834,28 @@ start_compress(const char *filename, uint64_t chunksize, int level)
pos += sizeof (n_chunksize);
memcpy(pos, &level, sizeof (level));
pos += sizeof (level);
/*
* If encryption is enabled, include salt, nonce and keylen in the header
* to be HMAC-ed (archive version 7 and greater).
*/
if (encrypt_type) {
*((int *)pos) = htonl(crypto_ctx.saltlen);
pos += sizeof (int);
serialize_checksum(crypto_ctx.salt, pos, crypto_ctx.saltlen);
pos += crypto_ctx.saltlen;
*((uint64_t *)pos) = htonll(crypto_nonce(&crypto_ctx));
pos += 8;
*((int *)pos) = htonl(keylen);
pos += sizeof (int);
}
if (Write(compfd, cread_buf, pos - cread_buf) != pos - cread_buf) {
perror("Write ");
COMP_BAIL;
}
/*
* If encryption is enabled, compute header HMAC. Then
* write the salt, nonce and header hmac in that order.
* If encryption is enabled, compute header HMAC and write it.
*/
if (encrypt_type) {
mac_ctx_t hdr_mac;
@ -1817,12 +1874,6 @@ start_compress(const char *filename, uint64_t chunksize, int level)
crypto_clean_pkey(&crypto_ctx);
pos = cread_buf;
*((int *)pos) = htonl(crypto_ctx.saltlen);
pos += sizeof (int);
serialize_checksum(crypto_ctx.salt, pos, crypto_ctx.saltlen);
pos += crypto_ctx.saltlen;
*((uint64_t *)pos) = htonll(crypto_nonce(&crypto_ctx));
pos += 8;
serialize_checksum(hdr_hash, pos, hlen);
pos += hlen;
if (Write(compfd, cread_buf, pos - cread_buf) != pos - cread_buf) {
@ -2196,10 +2247,11 @@ main(int argc, char *argv[])
exec_name = get_execname(argv[0]);
level = 6;
err = 0;
keylen = DEFAULT_KEYLEN;
slab_init();
init_pcompress();
while ((opt = getopt(argc, argv, "dc:s:l:pt:MCDEew:rLPS:B:F")) != -1) {
while ((opt = getopt(argc, argv, "dc:s:l:pt:MCDEew:rLPS:B:Fk:")) != -1) {
int ovr;
switch (opt) {
@ -2297,6 +2349,13 @@ main(int argc, char *argv[])
enable_rabin_split = 0;
break;
case 'k':
keylen = atoi(optarg);
if ((keylen != 16 && keylen != 32) || keylen > MAX_KEYLEN) {
err_exit(0, "Encryption KEY length should be 16 or 32.\n", optarg);
}
break;
case 'S':
if (get_checksum_props(optarg, &cksum, &cksum_bytes, &mac_bytes, 0) == -1) {
err_exit(0, "Invalid checksum type %s\n", optarg);