diff --git a/Makefile.in b/Makefile.in index c00ac72..4cbaf49 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,7 +24,9 @@ PROG= pcompress MAINSRCS = main.c utils/utils.c allocator.c zlib_compress.c bzip2_compress.c \ lzma_compress.c ppmd_compress.c adaptive_compress.c lzfx_compress.c \ - lz4_compress.c none_compress.c utils/xxhash.c utils/heapq.c utils/cpuid.c + lz4_compress.c none_compress.c utils/xxhash.c utils/heapq.c utils/cpuid.c \ + crypto/aes/crypto_aes.c crypto/scrypt/crypto_scrypt-nosse.c \ + crypto/scrypt/sha256.c crypto/scrypt/crypto_aesctr.c MAINHDRS = allocator.h pcompress.h utils/utils.h utils/xxhash.h utils/heapq.h \ utils/cpuid.h MAINOBJS = $(MAINSRCS:.c=.o) @@ -90,16 +92,16 @@ LIBBSCGEN_OPT = -fopenmp LIBBSCCPPFLAGS = -I$(LIBBSCDIR)/libbsc -DENABLE_PC_LIBBSC BAKFILES = *~ lzma/*~ lzfx/*~ lz4/*~ rabin/*~ bsdiff/*~ lzp/*~ utils/*~ crypto/sha2/*~ \ - crypto/sha2/intel/*~ + crypto/sha2/intel/*~ crypto/aes/*~ crypto/scrypt/*~ RM = rm -f COMMON_CPPFLAGS = -I. -I./lzma -I./lzfx -I./lz4 -I./rabin -I./bsdiff -DNODEFAULT_PROPS \ -DFILE_OFFSET_BITS=64 -D_REENTRANT -D__USE_SSE_INTRIN__ -D_LZMA_PROB32 \ -I./lzp @LIBBSCCPPFLAGS@ -I./crypto/skein -I./utils -I@OPENSSL_INCDIR@ \ - -I./crypto/sha2 + -I./crypto/sha2 -I./crypto/scrypt -I./crypto/aes @KEYLEN@ COMMON_VEC_FLAGS = -ftree-vectorize COMMON_LOOP_OPTFLAGS = $(VEC_FLAGS) -floop-interchange -floop-block -LDLIBS = -ldl -lbz2 $(ZLIB_DIR) -lz -lm @LIBBSCLFLAGS@ -L@OPENSSL_LIBDIR@ -lcrypto +LDLIBS = -ldl -lbz2 $(ZLIB_DIR) -lz -lm @LIBBSCLFLAGS@ -L@OPENSSL_LIBDIR@ -lcrypto -lrt OBJS = $(MAINOBJS) $(LZMAOBJS) $(PPMDOBJS) $(LZFXOBJS) $(LZ4OBJS) $(CRCOBJS) \ $(RABINOBJS) $(BSDIFFOBJS) $(LZPOBJS) @LIBBSCWRAPOBJ@ $(SKEINOBJS) $(SKEIN_BLOCK_OBJ) \ @SHA256ASM_OBJS@ @SHA256_OBJS@ diff --git a/README.md b/README.md index a7b6ed4..f09cade 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,9 @@ modes in which multiple algorithms are tried per chunk to determine the best one for the given chunk. Finally it supports 14 compression levels to allow for ultra compression modes in some algorithms. +Pcompress also supports encryption via AES and uses Scrypt from Tarsnap +for Password Based Key generation. + NOTE: This utility is Not an archiver. It compresses only single files or datastreams. To archive use something else like tar, cpio or pax. @@ -118,6 +121,26 @@ NOTE: The option "libbsc" uses Ilya Grebnov's block sorting compression library NOTE: It is recommended not to use '-L' with libbsc compression since libbsc uses LZP internally as well. + 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. + + The Scrypt algorithm from Tarsnap is used + (See: http://www.tarsnap.com/scrypt.html) for generating keys from + passwords. The CTR mode AES mechanism from Tarsnap is also utilized. + + '-w ' + Provide a file which contains the encryption password. This file must + be readable and writable since it is zeroed out after the password is + read. + +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 8d7ca81..12b1c2e 100755 --- a/config +++ b/config @@ -17,6 +17,7 @@ ${prog} [] --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. +--use-key256 Use 256-bit encryption keys. Default key length is 128-bit. --help Display this help message. _EOF @@ -38,6 +39,7 @@ openssl_libdir= openssl_incdir= sha256asmobjs= sha256objs= +keylen= yasm=yasm while [ "${arg1}" != "" ] @@ -67,6 +69,9 @@ do --with-openssl=*) openssl_prefix=`echo ${arg1} | cut -f2 -d"="` ;; + --use-key256) + keylen='-DKEYLEN=32' + ;; --help) usage $0;; *) echo "Unrecognized option: ${arg1}" @@ -195,6 +200,7 @@ noslabcppflagsvar="NO_SLAB_CPPFLAGS" debugstatscppflagsvar="DEBUG_STATS_CPPFLAGS" prefixvar="PREFIX" skeinblockvar="SKEIN_BLOCK" +keylenvar="KEYLEN" libbscdirvar="LIBBSCDIR" libbsclibvar="LIBBSCLIB" @@ -239,5 +245,6 @@ s#@${opensslincdirvar}@#${openssl_incdir}#g s#@${sha256asmobjsvar}@#${sha256asmobjs}#g s#@${sha256objsvar}@#${sha256objs}#g s#@${yasmvar}@#${yasm}#g +s#@${keylenvar}@#${keylen}#g " > Makefile diff --git a/crypto/aes/crypto_aes.c b/crypto/aes/crypto_aes.c new file mode 100644 index 0000000..f16f806 --- /dev/null +++ b/crypto/aes/crypto_aes.c @@ -0,0 +1,219 @@ +/* + * This file is a part of Pcompress, a chunked parallel multi- + * algorithm lossless compression and decompression program. + * + * Copyright (C) 2012 Moinak Ghosh. All rights reserved. + * Use is subject to license terms. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * moinakg@belenix.org, http://moinakg.wordpress.com/ + * + * This program includes partly-modified public domain source + * code from the LZMA SDK: http://www.7-zip.org/sdk.html + */ + +/*- + * Copyright 2007-2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "crypto_aes.h" + +extern uint64_t lzma_crc64(const uint8_t *buf, size_t size, uint64_t crc); + +/* + * Fixup parameters for scrypt. Memory is hardcoded here for + * reproducibility. + */ +static void +pickparams(int * logN, uint32_t * r, uint32_t * p) +{ + size_t memlimit = 512UL * 1024UL * 1024UL; // 512M + double opslimit = 65536; + double maxN, maxrp; + + *r = 8; + /* + * The memory limit requires that 128Nr <= memlimit, while the CPU + * limit requires that 4Nrp <= opslimit. If opslimit < memlimit/32, + * opslimit imposes the stronger limit on N. + */ + if (opslimit < memlimit/32) { + /* Set p = 1 and choose N based on the CPU limit. */ + *p = 1; + maxN = opslimit / (*r * 4); + for (*logN = 1; *logN < 63; *logN += 1) { + if ((uint64_t)(1) << *logN > maxN / 2) + break; + } + } else { + /* Set N based on the memory limit. */ + maxN = memlimit / (*r * 128); + for (*logN = 1; *logN < 63; *logN += 1) { + if ((uint64_t)(1) << *logN > maxN / 2) + break; + } + + /* Choose p based on the CPU limit. */ + maxrp = (opslimit / 4) / ((uint64_t)(1) << *logN); + if (maxrp > 0x3fffffff) + maxrp = 0x3fffffff; + *p = (uint32_t)(maxrp) / *r; + } +} + +int +aes_init(aes_ctx_t *ctx, uchar_t *salt, int saltlen, uchar_t *pwd, int pwd_len, + uint64_t nonce, int enc) +{ + int rv; + uchar_t key[KEYLEN]; + struct timespec tp; + uint64_t tv; + uchar_t num[25]; + uchar_t IV[32]; + +#ifndef _USE_PBK + int logN; + uint32_t r, p; + uint64_t N; + + pickparams(&logN, &r, &p); + N = (uint64_t)(1) << logN; + if (crypto_scrypt(pwd, pwd_len, salt, saltlen, N, r, p, key, 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); + return (-1); + } +#endif + + if (enc) { + AES_set_encrypt_key(key, (KEYLEN << 3), &(ctx->key)); + // Derive nonce from salt + if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) { + time((time_t *)&tv); + } else { + tv = tp.tv_sec * 1000UL + tp.tv_nsec; + } + sprintf(num, "%llu", tv); + PKCS5_PBKDF2_HMAC(num, strlen(num), salt, saltlen, PBE_ROUNDS, EVP_sha256(), 32, IV); + ctx->nonce = lzma_crc64(IV, 32, 0) & 0xffffffff00000000ULL; + // Nullify stack components + memset(num, 0, 25); + memset(IV, 0, 32); + memset(&tp, 0, sizeof (tp)); + tv = 0; + } else { + ctx->nonce = nonce; + AES_set_encrypt_key(key, (KEYLEN << 3), &(ctx->key)); + } + + memset(key, 0, KEYLEN); + return (0); +} + +int +aes_encrypt(aes_ctx_t *ctx, uchar_t *plaintext, uchar_t *ciphertext, ssize_t len, uint64_t id) { + AES_KEY key; + uchar_t *k1, *k2; + struct crypto_aesctr *strm; + int i; + + k1 = (uchar_t *)&(ctx->key); + k2 = (uchar_t *)&key; + for (i=0; inonce + id); + if (!strm) { + fprintf(stderr, "Failed to init counter mode AES\n"); + return (-1); + } + crypto_aesctr_stream(strm, plaintext, ciphertext, len); + crypto_aesctr_free(strm); + memset(&key, 0, sizeof (key)); +} + +int +aes_decrypt(aes_ctx_t *ctx, uchar_t *ciphertext, uchar_t *plaintext, ssize_t len, uint64_t id) { + AES_KEY key; + uchar_t *k1, *k2; + struct crypto_aesctr *strm; + int i; + + k1 = (uchar_t *)&(ctx->key); + k2 = (uchar_t *)&key; + for (i=0; inonce + id); + if (!strm) { + fprintf(stderr, "Failed to init counter mode AES\n"); + return (-1); + } + crypto_aesctr_stream(strm, ciphertext, plaintext, len); + crypto_aesctr_free(strm); + memset(&key, 0, sizeof (key)); +} + +uint64_t +aes_nonce(aes_ctx_t *ctx) +{ + return (ctx->nonce); +} + +void +aes_cleanup(aes_ctx_t *ctx) +{ + memset((void *)(&ctx->key), 0, sizeof (ctx->key)); + ctx->nonce = 0; + free(ctx); +} diff --git a/crypto/aes/crypto_aes.h b/crypto/aes/crypto_aes.h new file mode 100644 index 0000000..47e85b6 --- /dev/null +++ b/crypto/aes/crypto_aes.h @@ -0,0 +1,59 @@ +/* + * This file is a part of Pcompress, a chunked parallel multi- + * algorithm lossless compression and decompression program. + * + * Copyright (C) 2012 Moinak Ghosh. All rights reserved. + * Use is subject to license terms. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * moinakg@belenix.org, http://moinakg.wordpress.com/ + * + * This program includes partly-modified public domain source + * code from the LZMA SDK: http://www.7-zip.org/sdk.html + */ + +#ifndef _AES_CRYPTO_H +#define _AES_CRYPTO_H + +#include +#include +#ifdef _USE_PBK +#include +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef KEYLEN +#define KEYLEN 16 +#endif +#define PBE_ROUNDS 100 + +typedef struct { + uint64_t nonce; + AES_KEY key; +} aes_ctx_t; + +int aes_init(aes_ctx_t *ctx, uchar_t *salt, int saltlen, uchar_t *pwd, int pwd_len, + uint64_t nonce, int enc); +int aes_encrypt(aes_ctx_t *ctx, uchar_t *plaintext, uchar_t *ciphertext, ssize_t len, uint64_t id); +int aes_decrypt(aes_ctx_t *ctx, uchar_t *ciphertext, uchar_t *plaintext, ssize_t len, uint64_t id); +uint64_t aes_nonce(aes_ctx_t *ctx); +void aes_cleanup(aes_ctx_t *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/crypto/scrypt/crypto_aesctr.c b/crypto/scrypt/crypto_aesctr.c new file mode 100644 index 0000000..bc7479c --- /dev/null +++ b/crypto/scrypt/crypto_aesctr.c @@ -0,0 +1,123 @@ +/*- + * Copyright 2007-2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include +#include + +#include + +#include "sysendian.h" + +#include "crypto_aesctr.h" + +struct crypto_aesctr { + AES_KEY * key; + uint64_t nonce; + uint64_t bytectr; + uint8_t buf[16]; +}; + +/** + * crypto_aesctr_init(key, nonce): + * Prepare to encrypt/decrypt data with AES in CTR mode, using the provided + * expanded key and nonce. The key provided must remain valid for the + * lifetime of the stream. + */ +struct crypto_aesctr * +crypto_aesctr_init(AES_KEY * key, uint64_t nonce) +{ + struct crypto_aesctr * stream; + + /* Allocate memory. */ + if ((stream = malloc(sizeof(struct crypto_aesctr))) == NULL) + goto err0; + + /* Initialize values. */ + stream->key = key; + stream->nonce = nonce; + stream->bytectr = 0; + + /* Success! */ + return (stream); + +err0: + /* Failure! */ + return (NULL); +} + +/** + * crypto_aesctr_stream(stream, inbuf, outbuf, buflen): + * Generate the next ${buflen} bytes of the AES-CTR stream and xor them with + * bytes from ${inbuf}, writing the result into ${outbuf}. If the buffers + * ${inbuf} and ${outbuf} overlap, they must be identical. + */ +void +crypto_aesctr_stream(struct crypto_aesctr * stream, const uint8_t * inbuf, + uint8_t * outbuf, size_t buflen) +{ + uint8_t pblk[16]; + size_t pos; + int bytemod; + + for (pos = 0; pos < buflen; pos++) { + /* How far through the buffer are we? */ + bytemod = stream->bytectr % 16; + + /* Generate a block of cipherstream if needed. */ + if (bytemod == 0) { + be64enc(pblk, stream->nonce); + be64enc(pblk + 8, stream->bytectr / 16); + AES_encrypt(pblk, stream->buf, stream->key); + } + + /* Encrypt a byte. */ + outbuf[pos] = inbuf[pos] ^ stream->buf[bytemod]; + + /* Move to the next byte of cipherstream. */ + stream->bytectr += 1; + } +} + +/** + * crypto_aesctr_free(stream): + * Free the provided stream object. + */ +void +crypto_aesctr_free(struct crypto_aesctr * stream) +{ + int i; + + /* Zero potentially sensitive information. */ + for (i = 0; i < 16; i++) + stream->buf[i] = 0; + stream->bytectr = stream->nonce = 0; + + /* Free the stream. */ + free(stream); +} diff --git a/crypto/scrypt/crypto_aesctr.h b/crypto/scrypt/crypto_aesctr.h new file mode 100644 index 0000000..b50398f --- /dev/null +++ b/crypto/scrypt/crypto_aesctr.h @@ -0,0 +1,59 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _CRYPTO_AESCTR_H_ +#define _CRYPTO_AESCTR_H_ + +#include + +#include + +/** + * crypto_aesctr_init(key, nonce): + * Prepare to encrypt/decrypt data with AES in CTR mode, using the provided + * expanded key and nonce. The key provided must remain valid for the + * lifetime of the stream. + */ +struct crypto_aesctr * crypto_aesctr_init(AES_KEY *, uint64_t); + +/** + * crypto_aesctr_stream(stream, inbuf, outbuf, buflen): + * Generate the next ${buflen} bytes of the AES-CTR stream and xor them with + * bytes from ${inbuf}, writing the result into ${outbuf}. If the buffers + * ${inbuf} and ${outbuf} overlap, they must be identical. + */ +void crypto_aesctr_stream(struct crypto_aesctr *, const uint8_t *, + uint8_t *, size_t); + +/** + * crypto_aesctr_free(stream): + * Free the provided stream object. + */ +void crypto_aesctr_free(struct crypto_aesctr *); + +#endif /* !_CRYPTO_AESCTR_H_ */ diff --git a/crypto/scrypt/crypto_scrypt-nosse.c b/crypto/scrypt/crypto_scrypt-nosse.c new file mode 100644 index 0000000..c541a0c --- /dev/null +++ b/crypto/scrypt/crypto_scrypt-nosse.c @@ -0,0 +1,336 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#include +#include + +#include +#include +#include +#include + +#include "sha256.h" +#include "sysendian.h" + +#include "crypto_scrypt.h" + +static void blkcpy(void *, void *, size_t); +static void blkxor(void *, void *, size_t); +static void salsa20_8(uint32_t[16]); +static void blockmix_salsa8(uint32_t *, uint32_t *, uint32_t *, size_t); +static uint64_t integerify(void *, size_t); +static void smix(uint8_t *, size_t, uint64_t, uint32_t *, uint32_t *); + +static void +blkcpy(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] = S[i]; +} + +static void +blkxor(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] ^= S[i]; +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(uint32_t B[16]) +{ + uint32_t x[16]; + size_t i; + + blkcpy(x, B, 64); + for (i = 0; i < 8; i += 2) { +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + + /* Operate on rows. */ + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); +#undef R + } + for (i = 0; i < 16; i++) + B[i] += x[i]; +} + +/** + * blockmix_salsa8(Bin, Bout, X, r): + * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r + * bytes in length; the output Bout must also be the same size. The + * temporary space X must be 64 bytes. + */ +static void +blockmix_salsa8(uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) +{ + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &Bin[(2 * r - 1) * 16], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < 2 * r; i += 2) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8], X, 64); + + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16 + 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8 + r * 16], X, 64); + } +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(void * B, size_t r) +{ + uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64); + + return (((uint64_t)(X[1]) << 32) + X[0]); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; + * the temporary storage V must be 128rN bytes in length; the temporary + * storage XY must be 256r + 64 bytes in length. The value N must be a + * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a + * multiple of 64 bytes. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, uint32_t * V, uint32_t * XY) +{ + uint32_t * X = XY; + uint32_t * Y = &XY[32 * r]; + uint32_t * Z = &XY[64 * r]; + uint64_t i; + uint64_t j; + size_t k; + + /* 1: X <-- B */ + for (k = 0; k < 32 * r; k++) + X[k] = le32dec(&B[4 * k]); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 3: V_i <-- X */ + blkcpy(&V[i * (32 * r)], X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, Z, r); + + /* 3: V_i <-- X */ + blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(Y, X, Z, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(X, Y, Z, r); + + /* 7: j <-- Integerify(X) mod N */ + j = integerify(Y, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(Y, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(Y, X, Z, r); + } + + /* 10: B' <-- X */ + for (k = 0; k < 32 * r; k++) + le32enc(&B[4 * k], X[k]); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int +crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + void * B0, * V0, * XY0; + uint8_t * B; + uint32_t * V; + uint32_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (((N & (N - 1)) != 0) || (N == 0)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ +#ifdef HAVE_POSIX_MEMALIGN + if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) + goto err0; + B = (uint8_t *)(B0); + if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) + goto err1; + XY = (uint32_t *)(XY0); +#ifndef MAP_ANON + if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) + goto err2; + V = (uint32_t *)(V0); +#endif +#else + if ((B0 = malloc(128 * r * p + 63)) == NULL) + goto err0; + B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); + if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) + goto err1; + XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); +#ifndef MAP_ANON + if ((V0 = malloc(128 * r * N + 63)) == NULL) + goto err2; + V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); +#endif +#endif +#ifdef MAP_ANON + if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, +#ifdef MAP_NOCORE + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, +#else + MAP_ANON | MAP_PRIVATE, +#endif + -1, 0)) == MAP_FAILED) + goto err2; + V = (uint32_t *)(V0); +#endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); + + /* Free memory. */ +#ifdef MAP_ANON + if (munmap(V0, 128 * r * N)) + goto err2; +#else + free(V0); +#endif + free(XY0); + free(B0); + + /* Success! */ + return (0); + +err2: + free(XY0); +err1: + free(B0); +err0: + /* Failure! */ + return (-1); +} diff --git a/crypto/scrypt/crypto_scrypt.h b/crypto/scrypt/crypto_scrypt.h new file mode 100644 index 0000000..2e5f636 --- /dev/null +++ b/crypto/scrypt/crypto_scrypt.h @@ -0,0 +1,49 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _CRYPTO_SCRYPT_H_ +#define _CRYPTO_SCRYPT_H_ + +#include +#include + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, + uint32_t, uint32_t, uint8_t *, size_t); + +#define HAVE_POSIX_MEMALIGN + +#endif /* !_CRYPTO_SCRYPT_H_ */ diff --git a/crypto/scrypt/sha256.c b/crypto/scrypt/sha256.c new file mode 100644 index 0000000..5ae1818 --- /dev/null +++ b/crypto/scrypt/sha256.c @@ -0,0 +1,429 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "sysendian.h" + +#include "sha256.h" + +typedef struct SHA256Context { + uint32_t state[8]; + uint32_t count[2]; + unsigned char buf[64]; +} SHA256_CTX; + +typedef struct HMAC_SHA256Context { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +static void SHA256_Init(SHA256_CTX *); +static void SHA256_Update(SHA256_CTX *, const void *, size_t); +static void SHA256_Final(unsigned char [32], SHA256_CTX *); +static void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); +static void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); +static void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *); + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) + state[i] += S[i]; + + /* Clean the stack. */ + memset(W, 0, 256); + memset(S, 0, 32); + t0 = t1 = 0; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count */ + SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +static void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +static void +SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +static void +SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding */ + SHA256_Pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *)ctx, 0, sizeof(*ctx)); +} + +/* Initialize an HMAC-SHA256 operation with the given key. */ +static void +HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + unsigned char pad[64]; + unsigned char khash[32]; + const unsigned char * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + SHA256_Init(&ctx->ictx); + SHA256_Update(&ctx->ictx, K, Klen); + SHA256_Final(khash, &ctx->ictx); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->ictx, pad, 64); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->octx, pad, 64); + + /* Clean the stack. */ + memset(khash, 0, 32); +} + +/* Add bytes to the HMAC-SHA256 operation. */ +static void +HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len) +{ + + /* Feed data to the inner SHA256 operation. */ + SHA256_Update(&ctx->ictx, in, len); +} + +/* Finish an HMAC-SHA256 operation. */ +static void +HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx) +{ + unsigned char ihash[32]; + + /* Finish the inner SHA256 operation. */ + SHA256_Final(ihash, &ctx->ictx); + + /* Feed the inner hash to the outer SHA256 operation. */ + SHA256_Update(&ctx->octx, ihash, 32); + + /* Finish the outer SHA256 operation. */ + SHA256_Final(digest, &ctx->octx); + + /* Clean the stack. */ + memset(ihash, 0, 32); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX PShctx, hctx; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Compute HMAC state after processing P and S. */ + HMAC_SHA256_Init(&PShctx, passwd, passwdlen); + HMAC_SHA256_Update(&PShctx, salt, saltlen); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + HMAC_SHA256_Update(&hctx, ivec, 4); + HMAC_SHA256_Final(U, &hctx); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + HMAC_SHA256_Init(&hctx, passwd, passwdlen); + HMAC_SHA256_Update(&hctx, U, 32); + HMAC_SHA256_Final(U, &hctx); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean PShctx, since we never called _Final on it. */ + memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX)); +} diff --git a/crypto/scrypt/sha256.h b/crypto/scrypt/sha256.h new file mode 100644 index 0000000..8c3a5fd --- /dev/null +++ b/crypto/scrypt/sha256.h @@ -0,0 +1,44 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#include + +#include + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#endif /* !_SHA256_H_ */ diff --git a/crypto/scrypt/sysendian.h b/crypto/scrypt/sysendian.h new file mode 100644 index 0000000..0495194 --- /dev/null +++ b/crypto/scrypt/sysendian.h @@ -0,0 +1,138 @@ +/*- + * Copyright 2007-2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _SYSENDIAN_H_ +#define _SYSENDIAN_H_ + +/* If we don't have be64enc, the we have isn't usable. */ +#if !HAVE_DECL_BE64ENC +#undef HAVE_SYS_ENDIAN_H +#endif + +#ifdef HAVE_SYS_ENDIAN_H + +#include + +#else + +#include + +static inline uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static inline void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static inline uint64_t +be64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static inline void +be64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static inline uint32_t +le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static inline void +le32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static inline uint64_t +le64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static inline void +le64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} +#endif /* !HAVE_SYS_ENDIAN_H */ + +#endif /* !_SYSENDIAN_H_ */ diff --git a/main.c b/main.c index fcd1af4..6c805a4 100644 --- a/main.c +++ b/main.c @@ -81,6 +81,7 @@ static int enable_delta_encode = 0; static int enable_rabin_split = 1; static int enable_fixed_scan = 0; static int lzp_preprocess = 0; +static int encrypt_type = 0; static unsigned int chunk_num; static uint64_t largest_chunk, smallest_chunk, avg_chunk; static const char *exec_name; @@ -91,6 +92,8 @@ static int cksum_bytes; static int cksum = 0; static int rab_blk_size = 0; static dedupe_context_t *rctx; +static crypto_ctx_t crypto_ctx; +static char *pwd_file = NULL; static void usage(void) @@ -302,6 +305,26 @@ redo: _chunksize = ntohll(*((ssize_t *)rseg)); } + /* + * Decrypt compressed data if necessary. + */ + if (encrypt_type) { + /* + * Encryption algorithm should not change the size and + * encryption is in-place. + */ + rv = crypto_buf(&crypto_ctx, cseg, cseg, tdat->len_cmp, tdat->id); + if (rv == -1) { + /* + * Decryption failure is fatal. + */ + main_cancel = 1; + tdat->len_cmp = 0; + sem_post(&tdat->cmp_done_sem); + return; + } + } + if ((enable_rabin_scan || enable_fixed_scan) && (HDR & CHUNK_FLAG_DEDUP)) { uchar_t *cmpbuf, *ubuf; @@ -515,7 +538,7 @@ start_decompress(const char *filename, const char *to_filename) chunksize = ntohll(chunksize); level = ntohl(level); - if (version < VERSION-1) { + if (version < VERSION-2) { fprintf(stderr, "Unsupported version: %d\n", version); err = 1; goto uncomp_done; @@ -548,6 +571,96 @@ start_decompress(const char *filename, const char *to_filename) UNCOMP_BAIL; } + /* + * If encryption is enabled initialize crypto. + */ + if (flags & MASK_CRYPTO_ALG) { + int saltlen; + uchar_t *salt1, *salt2; + uint64_t nonce; + uchar_t pw[MAX_PW_LEN]; + int pw_len; + + encrypt_type = flags & MASK_CRYPTO_ALG; + if (Read(compfd, &saltlen, sizeof (saltlen)) < sizeof (saltlen)) { + perror("Read: "); + UNCOMP_BAIL; + } + saltlen = ntohl(saltlen); + salt1 = malloc(saltlen); + salt2 = malloc(saltlen); + if (Read(compfd, salt1, saltlen) < saltlen) { + free(salt1); free(salt2); + perror("Read: "); + 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); + perror("Read: "); + UNCOMP_BAIL; + } + nonce = ntohll(nonce); + + if (!pwd_file) { + pw_len = get_pw_string(pw, + "Please enter encryption password"); + if (pw_len == -1) { + memset(salt2, 0, saltlen); + free(salt2); + err_exit(1, "Failed to get password.\n"); + } + } else { + int fd, len; + uchar_t zero[MAX_PW_LEN]; + + /* + * Read password from a file and zero out the file after reading. + */ + memset(zero, 0, MAX_PW_LEN); + fd = open(pwd_file, O_RDWR); + if (fd != -1) { + pw_len = lseek(fd, 0, SEEK_END); + if (pw_len != -1) { + if (pw_len > MAX_PW_LEN) pw_len = MAX_PW_LEN-1; + lseek(fd, 0, SEEK_SET); + len = Read(fd, pw, pw_len); + if (len != -1 && len == pw_len) { + pw[pw_len] = '\0'; + if (isspace(pw[pw_len - 1])) + pw[pw_len-1] = '\0'; + lseek(fd, 0, SEEK_SET); + Write(fd, zero, pw_len); + } else { + pw_len = -1; + } + } + } + if (pw_len == -1) { + perror(" "); + memset(salt2, 0, saltlen); + free(salt2); + err_exit(1, "Failed to get password.\n"); + } + close(fd); + } + if (init_crypto(&crypto_ctx, pw, pw_len, encrypt_type, salt2, + saltlen, nonce, DECRYPT_FLAG) == -1) { + memset(salt2, 0, saltlen); + free(salt2); + memset(pw, 0, MAX_PW_LEN); + err_exit(1, "Failed to initialize crypto\n"); + } + memset(salt2, 0, saltlen); + free(salt2); + nonce = 0; + memset(pw, 0, MAX_PW_LEN); + } + nprocs = sysconf(_SC_NPROCESSORS_ONLN); if (nthreads > 0 && nthreads < nprocs) nprocs = nthreads; @@ -572,6 +685,7 @@ start_decompress(const char *filename, const char *to_filename) } tdat = dary[i]; tdat->compressed_chunk = NULL; + tdat->uncompressed_chunk = NULL; tdat->chunksize = chunksize; tdat->compress = _compress_func; tdat->decompress = _decompress_func; @@ -880,6 +994,27 @@ plain_compress: type = COMPRESSED; } + /* + * Now perform encryption on the compressed data, if requested. + */ + if (encrypt_type) { + /* + * Encryption algorithm should not change the size and + * encryption is in-place. + */ + rv = crypto_buf(&crypto_ctx, compressed_chunk, compressed_chunk, + tdat->len_cmp, tdat->id); + if (rv == -1) { + /* + * Encryption failure is fatal. + */ + main_cancel = 1; + tdat->len_cmp = 0; + sem_post(&tdat->cmp_done_sem); + return; + } + } + if ((enable_rabin_scan || enable_fixed_scan) && tdat->rctx->valid) { type |= CHUNK_FLAG_DEDUP; } @@ -1020,6 +1155,56 @@ start_compress(const char *filename, uint64_t chunksize, int level) enable_delta_encode) - (compressed_chunksize - chunksize)); } + if (encrypt_type) { + uchar_t pw[MAX_PW_LEN]; + int pw_len; + + if (!pwd_file) { + pw_len = get_pw_string(pw, + "Please enter encryption password"); + if (pw_len == -1) { + err_exit(1, "Failed to get password.\n"); + } + } else { + int fd, len; + uchar_t zero[MAX_PW_LEN]; + + /* + * Read password from a file and zero out the file after reading. + */ + memset(zero, 0, MAX_PW_LEN); + fd = open(pwd_file, O_RDWR); + if (fd != -1) { + pw_len = lseek(fd, 0, SEEK_END); + if (pw_len != -1) { + if (pw_len > MAX_PW_LEN) pw_len = MAX_PW_LEN-1; + lseek(fd, 0, SEEK_SET); + len = Read(fd, pw, pw_len); + if (len != -1 && len == pw_len) { + pw[pw_len] = '\0'; + if (isspace(pw[pw_len - 1])) + pw[pw_len-1] = '\0'; + lseek(fd, 0, SEEK_SET); + Write(fd, zero, pw_len); + } else { + pw_len = -1; + } + } + } + if (pw_len == -1) { + perror(" "); + err_exit(1, "Failed to get password.\n"); + } + close(fd); + } + if (init_crypto(&crypto_ctx, pw, pw_len, encrypt_type, NULL, + 0, 0, ENCRYPT_FLAG) == -1) { + memset(pw, 0, MAX_PW_LEN); + err_exit(1, "Failed to initialize crypto\n"); + } + memset(pw, 0, MAX_PW_LEN); + } + err = 0; thread = 0; single_chunk = 0; @@ -1101,6 +1286,9 @@ start_compress(const char *filename, uint64_t chunksize, int level) } } + if (encrypt_type) + flags |= encrypt_type; + set_threadcounts(&props, &nthreads, nprocs, COMPRESS_THREADS); fprintf(stderr, "Scaling to %d thread", nthreads * props.nthreads); if (nthreads * props.nthreads > 1) fprintf(stderr, "s"); @@ -1128,6 +1316,7 @@ start_compress(const char *filename, uint64_t chunksize, int level) tdat->chunksize = chunksize; tdat->compress = _compress_func; tdat->decompress = _decompress_func; + tdat->uncompressed_chunk = (uchar_t *)1; tdat->cancel = 0; tdat->level = level; sem_init(&(tdat->start_sem), 0, 0); @@ -1189,6 +1378,23 @@ start_compress(const char *filename, uint64_t chunksize, int level) COMP_BAIL; } + /* + * If encryption is enabled, write the salt and nonce. + */ + pos = cread_buf; + 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; + if (Write(compfd, cread_buf, pos - cread_buf) != pos - cread_buf) { + perror("Write "); + COMP_BAIL; + } + } + /* * Now read from the uncompressed file in 'chunksize' sized chunks, independently * compress each chunk and write it out. Chunk sequencing is ensured. @@ -1531,7 +1737,7 @@ main(int argc, char *argv[]) level = 6; slab_init(); - while ((opt = getopt(argc, argv, "dc:s:l:pt:MCDErLS:B:F")) != -1) { + while ((opt = getopt(argc, argv, "dc:s:l:pt:MCDEew:rLS:B:F")) != -1) { int ovr; switch (opt) { @@ -1601,6 +1807,14 @@ main(int argc, char *argv[]) enable_delta_encode = DELTA_EXTRA; break; + case 'e': + encrypt_type = CRYPTO_ALG_AES; + break; + + case 'w': + pwd_file = strdup(optarg); + break; + case 'F': enable_fixed_scan = 1; enable_rabin_split = 0; @@ -1656,6 +1870,14 @@ main(int argc, char *argv[]) exit(1); } + if (!do_compress && encrypt_type) { + fprintf(stderr, "Encryption only makes sense when compressing!\n"); + exit(1); + } else if (pipe_mode && !pwd_file) { + fprintf(stderr, "Pipe mode requires password to be provided in a file.\n"); + exit(1); + } + if (num_rem == 0 && !pipe_mode) { usage(); /* At least 1 filename needed. */ exit(1); @@ -1701,6 +1923,7 @@ main(int argc, char *argv[]) if (cksum == 0) get_checksum_props(DEFAULT_CKSUM, &cksum, &cksum_bytes); + /* * Start the main routines. */ @@ -1709,6 +1932,8 @@ main(int argc, char *argv[]) else if (do_uncompress) start_decompress(filename, to_filename); + if (pwd_file) + free(pwd_file); free(filename); free((void *)exec_name); return (0); diff --git a/pcompress.h b/pcompress.h index 6885462..e825ec3 100644 --- a/pcompress.h +++ b/pcompress.h @@ -37,11 +37,12 @@ extern "C" { #define CHUNK_FLAG_SZ 1 #define ALGO_SZ 8 #define MIN_CHUNK 2048 -#define VERSION 3 +#define VERSION 4 #define FLAG_DEDUP 1 -#define FLAG_DEDUP_FIXED 1 -#define FLAG_SINGLE_CHUNK 2 +#define FLAG_DEDUP_FIXED 2 +#define FLAG_SINGLE_CHUNK 4 #define UTILITY_VERSION "0.8.6" +#define MASK_CRYPTO_ALG 0x30 #define COMPRESSED 1 #define UNCOMPRESSED 0 diff --git a/utils/utils.c b/utils/utils.c index 0ce4d27..c175547 100644 --- a/utils/utils.c +++ b/utils/utils.c @@ -23,7 +23,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -34,7 +37,10 @@ #include #include #include +#include +#include #include +#include #include "utils.h" #include "cpuid.h" @@ -43,7 +49,7 @@ #define PROVIDER_X64_OPT 1 static void init_sha256(void); - +static int geturandom_bytes(uchar_t rbytes[32]); /* * Checksum properties */ @@ -62,7 +68,7 @@ static struct { }; -static int cksum_provider = PROVIDER_OPENSSL; +static int cksum_provider = PROVIDER_OPENSSL, ossl_inited = 0; extern uint64_t lzma_crc64(const uint8_t *buf, size_t size, uint64_t crc); extern uint64_t lzma_crc64_8bchk(const uint8_t *buf, size_t size, @@ -447,3 +453,214 @@ deserialize_checksum(uchar_t *checksum, uchar_t *buf, int cksum_bytes) j++; } } + +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) +{ + if (crypto_alg == CRYPTO_ALG_AES) { + aes_ctx_t *actx = malloc(sizeof (aes_ctx_t)); + + if (enc_dec) { + /* + * Encryption init. + */ + cctx->salt = malloc(32); + salt = cctx->salt; + cctx->saltlen = 32; + if (RAND_status() != 1 || RAND_bytes(salt, 32) != 1) { + if (geturandom_bytes(salt) != 0) { + uchar_t sb[64]; + int b; + struct timespec tp; + + b = 0; + /* No good random pool is populated/available. What to do ? */ + if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) { + time((time_t *)&sb[b]); + b += 8; + } else { + uint64_t v; + v = tp.tv_sec * 1000UL + tp.tv_nsec; + *((uint64_t *)&sb[b]) = v; + b += 8; + } + *((uint32_t *)&sb[b]) = rand(); + b += 4; + *((uint32_t *)&sb[b]) = getpid(); + b += 4; + compute_checksum(&sb[b], CKSUM_SHA256, sb, b); + b = 8 + 4; + *((uint32_t *)&sb[b]) = rand(); + compute_checksum(salt, CKSUM_SHA256, &sb[b], 32 + 4); + } + } + + /* + * Zero nonce (arg #6) since it will be generated. + */ + if (aes_init(actx, salt, 32, pwd, pwd_len, 0, enc_dec) != 0) { + fprintf(stderr, "Failed to initialize AES context\n"); + return (-1); + } + } else { + /* + * Decryption init. + * Pass given nonce and salt. + */ + if (saltlen > MAX_SALTLEN) { + fprintf(stderr, "Salt too long. Max allowed length is %d\n", + MAX_SALTLEN); + return (-1); + } + cctx->salt = malloc(saltlen); + memcpy(cctx->salt, salt, saltlen); + + if (aes_init(actx, cctx->salt, saltlen, pwd, pwd_len, nonce, + enc_dec) != 0) { + fprintf(stderr, "Failed to initialize AES context\n"); + return (-1); + } + } + cctx->crypto_ctx = actx; + cctx->crypto_alg = crypto_alg; + cctx->enc_dec = enc_dec; + } else { + fprintf(stderr, "Unrecognized algorithm code: %d\n", crypto_alg); + return (-1); + } + return (0); +} + +int +crypto_buf(crypto_ctx_t *cctx, uchar_t *from, uchar_t *to, ssize_t bytes, uint64_t id) +{ + if (cctx->crypto_alg == CRYPTO_ALG_AES) { + if (cctx->enc_dec == ENCRYPT_FLAG) { + return (aes_encrypt(cctx->crypto_ctx, from, to, bytes, id)); + } else { + return (aes_decrypt(cctx->crypto_ctx, from, to, bytes, id)); + } + } else { + fprintf(stderr, "Unrecognized algorithm code: %d\n", cctx->crypto_alg); + return (-1); + } + return (0); +} + +uint64_t +crypto_nonce(crypto_ctx_t *cctx) +{ + return (aes_nonce(cctx->crypto_ctx)); +} + +void +cleanup_crypto(crypto_ctx_t *cctx) +{ + aes_cleanup(cctx->crypto_ctx); + memset(cctx->salt, 0, 32); + free(cctx->salt); + free(cctx); +} + +static int +geturandom_bytes(uchar_t rbytes[32]) +{ + int fd; + ssize_t lenread; + uchar_t * buf = rbytes; + size_t buflen = 32; + + /* Open /dev/urandom. */ + if ((fd = open("/dev/urandom", O_RDONLY)) == -1) + goto err0; + + /* Read bytes until we have filled the buffer. */ + while (buflen > 0) { + if ((lenread = read(fd, buf, buflen)) == -1) + goto err1; + + /* The random device should never EOF. */ + if (lenread == 0) + goto err1; + + /* We're partly done. */ + buf += lenread; + buflen -= lenread; + } + + /* Close the device. */ + while (close(fd) == -1) { + if (errno != EINTR) + goto err0; + } + + /* Success! */ + return (0); +err1: + close(fd); +err0: + /* Failure! */ + return (4); +} + +int +get_pw_string(char pw[MAX_PW_LEN], char *prompt) +{ + int fd, len; + FILE *input, *strm; + struct termios oldt, newt; + uchar_t pw1[MAX_PW_LEN], pw2[MAX_PW_LEN], *s; + + // Try TTY first + fd = open("/dev/tty", O_RDWR | O_NOCTTY); + if (fd != -1) { + input = fdopen(fd, "w+"); + strm = input; + } else { + // Fall back to stdin + fd = STDIN_FILENO; + input = stdin; + strm = stderr; + } + tcgetattr(fd, &oldt); + newt = oldt; + newt.c_lflag &= ~ECHO; + tcsetattr(fd, TCSANOW, &newt); + + fprintf(stderr, "%s: ", prompt); + fflush(stderr); + s = fgets(pw1, MAX_PW_LEN, input); + fputs("\n", stderr); + + if (s == NULL) { + tcsetattr(fd, TCSANOW, &oldt); + fflush(strm); + return (-1); + } + + fprintf(stderr, "%s (once more): ", prompt); + fflush(stderr); + s = fgets(pw2, MAX_PW_LEN, input); + tcsetattr(fd, TCSANOW, &oldt); + fflush(strm); + fputs("\n", stderr); + + if (s == NULL) { + return (-1); + } + + if (strcmp(pw1, pw2) != 0) { + fprintf(stderr, "Passwords do not match!\n"); + memset(pw1, 0, MAX_PW_LEN); + memset(pw2, 0, MAX_PW_LEN); + return (-1); + } + + len = strlen(pw1); + pw1[len-1] = '\0'; + strcpy(pw, pw1); + memset(pw1, 0, MAX_PW_LEN); + memset(pw2, 0, MAX_PW_LEN); + return (len); +} diff --git a/utils/utils.h b/utils/utils.h index eba9d74..c6c0a61 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -34,6 +34,7 @@ extern "C" { #define DATA_TEXT 1 #define DATA_BINARY 2 +#define MAX_PW_LEN 16 #if !defined(sun) && !defined(__sun) #define uchar_t u_char @@ -146,6 +147,19 @@ typedef struct { proc_type_t proc_type; } processor_info_t; +#define ENCRYPT_FLAG 1 +#define DECRYPT_FLAG 0 +#define CRYPTO_ALG_AES 0x10 +#define MAX_SALTLEN 64 + +typedef struct { + void *crypto_ctx; + int crypto_alg; + int enc_dec; + uchar_t *salt; + int saltlen; +} crypto_ctx_t; + extern void err_exit(int show_errno, const char *format, ...); extern const char *get_execname(const char *); extern int parse_numeric(ssize_t *val, const char *str); @@ -160,6 +174,12 @@ extern int compute_checksum(uchar_t *cksum_buf, int cksum, uchar_t *buf, ssize_t extern int get_checksum_props(char *name, int *cksum, int *cksum_bytes); extern void serialize_checksum(uchar_t *checksum, uchar_t *buf, int cksum_bytes); extern void deserialize_checksum(uchar_t *checksum, uchar_t *buf, int cksum_bytes); +extern 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); +extern int crypto_buf(crypto_ctx_t *cctx, uchar_t *from, uchar_t *to, ssize_t bytes, uint64_t id); +extern uint64_t crypto_nonce(crypto_ctx_t *cctx); +extern void cleanup_crypto(crypto_ctx_t *cctx); +extern int get_pw_string(char pw[MAX_PW_LEN], char *prompt); /* Pointer type for compress and decompress functions. */ typedef int (*compress_func_ptr)(void *src, size_t srclen, void *dst,