diff --git a/Makefile.in b/Makefile.in index ece3067..365e403 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 $@ diff --git a/README.md b/README.md index 275de8d..46ed50a 100644 --- a/README.md +++ b/README.md @@ -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 ' + 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 diff --git a/config b/config index c68901e..b048d43 100755 --- a/config +++ b/config @@ -13,20 +13,21 @@ ${prog} [] --enable-debug-stats Enable printing of some verbose debug info (default: disabled). --with-libbsc= 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= (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= (Default: System) - Enable building against an alternate Zlib installation. + Enable building against an alternate Zlib installation. --with-bzlib= (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 ' 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 diff --git a/crypto/aes/crypto_aes.c b/crypto/aes/crypto_aes.c index d1f6fb6..e29b375 100644 --- a/crypto/aes/crypto_aes.c +++ b/crypto/aes/crypto_aes.c @@ -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 diff --git a/crypto/aes/crypto_aes.h b/crypto/aes/crypto_aes.h index d315654..3da6e97 100644 --- a/crypto/aes/crypto_aes.h +++ b/crypto/aes/crypto_aes.h @@ -30,20 +30,19 @@ #include #endif #include +#include #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, diff --git a/crypto/crypto_utils.c b/crypto/crypto_utils.c index 34963fd..cf53dcd 100644 --- a/crypto/crypto_utils.c +++ b/crypto/crypto_utils.c @@ -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) { /* diff --git a/crypto/crypto_utils.h b/crypto/crypto_utils.h index a07fd39..867765f 100644 --- a/crypto/crypto_utils.h +++ b/crypto/crypto_utils.h @@ -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); diff --git a/main.c b/main.c index de2b1b2..701dcb4 100644 --- a/main.c +++ b/main.c @@ -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 '\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 \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);