Support for chunk-level encryption using AES and Scrypt based PBE.

Couple of minor fixes.
This commit is contained in:
Moinak Ghosh 2012-10-15 12:10:00 +05:30
parent 21cbef6d60
commit f2d7bea902
16 changed files with 1962 additions and 11 deletions

View file

@ -24,7 +24,9 @@
PROG= pcompress PROG= pcompress
MAINSRCS = main.c utils/utils.c allocator.c zlib_compress.c bzip2_compress.c \ 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 \ 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 \ MAINHDRS = allocator.h pcompress.h utils/utils.h utils/xxhash.h utils/heapq.h \
utils/cpuid.h utils/cpuid.h
MAINOBJS = $(MAINSRCS:.c=.o) MAINOBJS = $(MAINSRCS:.c=.o)
@ -90,16 +92,16 @@ LIBBSCGEN_OPT = -fopenmp
LIBBSCCPPFLAGS = -I$(LIBBSCDIR)/libbsc -DENABLE_PC_LIBBSC LIBBSCCPPFLAGS = -I$(LIBBSCDIR)/libbsc -DENABLE_PC_LIBBSC
BAKFILES = *~ lzma/*~ lzfx/*~ lz4/*~ rabin/*~ bsdiff/*~ lzp/*~ utils/*~ crypto/sha2/*~ \ BAKFILES = *~ lzma/*~ lzfx/*~ lz4/*~ rabin/*~ bsdiff/*~ lzp/*~ utils/*~ crypto/sha2/*~ \
crypto/sha2/intel/*~ crypto/sha2/intel/*~ crypto/aes/*~ crypto/scrypt/*~
RM = rm -f RM = rm -f
COMMON_CPPFLAGS = -I. -I./lzma -I./lzfx -I./lz4 -I./rabin -I./bsdiff -DNODEFAULT_PROPS \ 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 \ -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./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_VEC_FLAGS = -ftree-vectorize
COMMON_LOOP_OPTFLAGS = $(VEC_FLAGS) -floop-interchange -floop-block 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) \ OBJS = $(MAINOBJS) $(LZMAOBJS) $(PPMDOBJS) $(LZFXOBJS) $(LZ4OBJS) $(CRCOBJS) \
$(RABINOBJS) $(BSDIFFOBJS) $(LZPOBJS) @LIBBSCWRAPOBJ@ $(SKEINOBJS) $(SKEIN_BLOCK_OBJ) \ $(RABINOBJS) $(BSDIFFOBJS) $(LZPOBJS) @LIBBSCWRAPOBJ@ $(SKEINOBJS) $(SKEIN_BLOCK_OBJ) \
@SHA256ASM_OBJS@ @SHA256_OBJS@ @SHA256ASM_OBJS@ @SHA256_OBJS@

View file

@ -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 one for the given chunk. Finally it supports 14 compression levels to allow
for ultra compression modes in some algorithms. 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 NOTE: This utility is Not an archiver. It compresses only single files or
datastreams. To archive use something else like tar, cpio or pax. 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 NOTE: It is recommended not to use '-L' with libbsc compression since libbsc uses
LZP internally as well. 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 <pathname>'
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 Environment Variables
===================== =====================

7
config
View file

@ -17,6 +17,7 @@ ${prog} [<options>]
--with-openssl=<path to OpenSSL installation tree> (Default: System) --with-openssl=<path to OpenSSL installation tree> (Default: System)
This defaults to the system's OpenSSL library. You can use this option This defaults to the system's OpenSSL library. You can use this option
if you want to use an alternate OpenSSL installation. 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. --help Display this help message.
_EOF _EOF
@ -38,6 +39,7 @@ openssl_libdir=
openssl_incdir= openssl_incdir=
sha256asmobjs= sha256asmobjs=
sha256objs= sha256objs=
keylen=
yasm=yasm yasm=yasm
while [ "${arg1}" != "" ] while [ "${arg1}" != "" ]
@ -67,6 +69,9 @@ do
--with-openssl=*) --with-openssl=*)
openssl_prefix=`echo ${arg1} | cut -f2 -d"="` openssl_prefix=`echo ${arg1} | cut -f2 -d"="`
;; ;;
--use-key256)
keylen='-DKEYLEN=32'
;;
--help) usage $0;; --help) usage $0;;
*) *)
echo "Unrecognized option: ${arg1}" echo "Unrecognized option: ${arg1}"
@ -195,6 +200,7 @@ noslabcppflagsvar="NO_SLAB_CPPFLAGS"
debugstatscppflagsvar="DEBUG_STATS_CPPFLAGS" debugstatscppflagsvar="DEBUG_STATS_CPPFLAGS"
prefixvar="PREFIX" prefixvar="PREFIX"
skeinblockvar="SKEIN_BLOCK" skeinblockvar="SKEIN_BLOCK"
keylenvar="KEYLEN"
libbscdirvar="LIBBSCDIR" libbscdirvar="LIBBSCDIR"
libbsclibvar="LIBBSCLIB" libbsclibvar="LIBBSCLIB"
@ -239,5 +245,6 @@ s#@${opensslincdirvar}@#${openssl_incdir}#g
s#@${sha256asmobjsvar}@#${sha256asmobjs}#g s#@${sha256asmobjsvar}@#${sha256asmobjs}#g
s#@${sha256objsvar}@#${sha256objs}#g s#@${sha256objsvar}@#${sha256objs}#g
s#@${yasmvar}@#${yasm}#g s#@${yasmvar}@#${yasm}#g
s#@${keylenvar}@#${keylen}#g
" > Makefile " > Makefile

219
crypto/aes/crypto_aes.c Normal file
View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/evp.h>
#include <crypto_scrypt.h>
#include <crypto_aesctr.h>
#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; i<sizeof (key); i++)
k2[i] = k1[i];
// Init counter mode AES from scrypt
strm = crypto_aesctr_init(&key, ctx->nonce + 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; i<sizeof (key); i++)
k2[i] = k1[i];
// Init counter mode AES from scrypt
strm = crypto_aesctr_init(&key, ctx->nonce + 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);
}

59
crypto/aes/crypto_aes.h Normal file
View file

@ -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 <utils.h>
#include <openssl/aes.h>
#ifdef _USE_PBK
#include <openssl/evp.h>
#endif
#include <openssl/opensslv.h>
#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

View file

@ -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 <stdint.h>
#include <stdlib.h>
#include <openssl/aes.h>
#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);
}

View file

@ -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 <stdint.h>
#include <openssl/aes.h>
/**
* 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_ */

View file

@ -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 <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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);
}

View file

@ -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 <stdint.h>
#include <sys/types.h>
/**
* 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_ */

429
crypto/scrypt/sha256.c Normal file
View file

@ -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 <sys/types.h>
#include <stdint.h>
#include <string.h>
#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));
}

44
crypto/scrypt/sha256.h Normal file
View file

@ -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 <sys/types.h>
#include <stdint.h>
/**
* 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_ */

138
crypto/scrypt/sysendian.h Normal file
View file

@ -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 <sys/endian.h> we have isn't usable. */
#if !HAVE_DECL_BE64ENC
#undef HAVE_SYS_ENDIAN_H
#endif
#ifdef HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#else
#include <stdint.h>
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_ */

229
main.c
View file

@ -81,6 +81,7 @@ static int enable_delta_encode = 0;
static int enable_rabin_split = 1; static int enable_rabin_split = 1;
static int enable_fixed_scan = 0; static int enable_fixed_scan = 0;
static int lzp_preprocess = 0; static int lzp_preprocess = 0;
static int encrypt_type = 0;
static unsigned int chunk_num; static unsigned int chunk_num;
static uint64_t largest_chunk, smallest_chunk, avg_chunk; static uint64_t largest_chunk, smallest_chunk, avg_chunk;
static const char *exec_name; static const char *exec_name;
@ -91,6 +92,8 @@ static int cksum_bytes;
static int cksum = 0; static int cksum = 0;
static int rab_blk_size = 0; static int rab_blk_size = 0;
static dedupe_context_t *rctx; static dedupe_context_t *rctx;
static crypto_ctx_t crypto_ctx;
static char *pwd_file = NULL;
static void static void
usage(void) usage(void)
@ -302,6 +305,26 @@ redo:
_chunksize = ntohll(*((ssize_t *)rseg)); _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)) { if ((enable_rabin_scan || enable_fixed_scan) && (HDR & CHUNK_FLAG_DEDUP)) {
uchar_t *cmpbuf, *ubuf; uchar_t *cmpbuf, *ubuf;
@ -515,7 +538,7 @@ start_decompress(const char *filename, const char *to_filename)
chunksize = ntohll(chunksize); chunksize = ntohll(chunksize);
level = ntohl(level); level = ntohl(level);
if (version < VERSION-1) { if (version < VERSION-2) {
fprintf(stderr, "Unsupported version: %d\n", version); fprintf(stderr, "Unsupported version: %d\n", version);
err = 1; err = 1;
goto uncomp_done; goto uncomp_done;
@ -548,6 +571,96 @@ start_decompress(const char *filename, const char *to_filename)
UNCOMP_BAIL; 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); nprocs = sysconf(_SC_NPROCESSORS_ONLN);
if (nthreads > 0 && nthreads < nprocs) if (nthreads > 0 && nthreads < nprocs)
nprocs = nthreads; nprocs = nthreads;
@ -572,6 +685,7 @@ start_decompress(const char *filename, const char *to_filename)
} }
tdat = dary[i]; tdat = dary[i];
tdat->compressed_chunk = NULL; tdat->compressed_chunk = NULL;
tdat->uncompressed_chunk = NULL;
tdat->chunksize = chunksize; tdat->chunksize = chunksize;
tdat->compress = _compress_func; tdat->compress = _compress_func;
tdat->decompress = _decompress_func; tdat->decompress = _decompress_func;
@ -880,6 +994,27 @@ plain_compress:
type = COMPRESSED; 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) { if ((enable_rabin_scan || enable_fixed_scan) && tdat->rctx->valid) {
type |= CHUNK_FLAG_DEDUP; type |= CHUNK_FLAG_DEDUP;
} }
@ -1020,6 +1155,56 @@ start_compress(const char *filename, uint64_t chunksize, int level)
enable_delta_encode) - (compressed_chunksize - chunksize)); 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; err = 0;
thread = 0; thread = 0;
single_chunk = 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); set_threadcounts(&props, &nthreads, nprocs, COMPRESS_THREADS);
fprintf(stderr, "Scaling to %d thread", nthreads * props.nthreads); fprintf(stderr, "Scaling to %d thread", nthreads * props.nthreads);
if (nthreads * props.nthreads > 1) fprintf(stderr, "s"); 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->chunksize = chunksize;
tdat->compress = _compress_func; tdat->compress = _compress_func;
tdat->decompress = _decompress_func; tdat->decompress = _decompress_func;
tdat->uncompressed_chunk = (uchar_t *)1;
tdat->cancel = 0; tdat->cancel = 0;
tdat->level = level; tdat->level = level;
sem_init(&(tdat->start_sem), 0, 0); sem_init(&(tdat->start_sem), 0, 0);
@ -1189,6 +1378,23 @@ start_compress(const char *filename, uint64_t chunksize, int level)
COMP_BAIL; 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 * Now read from the uncompressed file in 'chunksize' sized chunks, independently
* compress each chunk and write it out. Chunk sequencing is ensured. * compress each chunk and write it out. Chunk sequencing is ensured.
@ -1531,7 +1737,7 @@ main(int argc, char *argv[])
level = 6; level = 6;
slab_init(); 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; int ovr;
switch (opt) { switch (opt) {
@ -1601,6 +1807,14 @@ main(int argc, char *argv[])
enable_delta_encode = DELTA_EXTRA; enable_delta_encode = DELTA_EXTRA;
break; break;
case 'e':
encrypt_type = CRYPTO_ALG_AES;
break;
case 'w':
pwd_file = strdup(optarg);
break;
case 'F': case 'F':
enable_fixed_scan = 1; enable_fixed_scan = 1;
enable_rabin_split = 0; enable_rabin_split = 0;
@ -1656,6 +1870,14 @@ main(int argc, char *argv[])
exit(1); 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) { if (num_rem == 0 && !pipe_mode) {
usage(); /* At least 1 filename needed. */ usage(); /* At least 1 filename needed. */
exit(1); exit(1);
@ -1701,6 +1923,7 @@ main(int argc, char *argv[])
if (cksum == 0) if (cksum == 0)
get_checksum_props(DEFAULT_CKSUM, &cksum, &cksum_bytes); get_checksum_props(DEFAULT_CKSUM, &cksum, &cksum_bytes);
/* /*
* Start the main routines. * Start the main routines.
*/ */
@ -1709,6 +1932,8 @@ main(int argc, char *argv[])
else if (do_uncompress) else if (do_uncompress)
start_decompress(filename, to_filename); start_decompress(filename, to_filename);
if (pwd_file)
free(pwd_file);
free(filename); free(filename);
free((void *)exec_name); free((void *)exec_name);
return (0); return (0);

View file

@ -37,11 +37,12 @@ extern "C" {
#define CHUNK_FLAG_SZ 1 #define CHUNK_FLAG_SZ 1
#define ALGO_SZ 8 #define ALGO_SZ 8
#define MIN_CHUNK 2048 #define MIN_CHUNK 2048
#define VERSION 3 #define VERSION 4
#define FLAG_DEDUP 1 #define FLAG_DEDUP 1
#define FLAG_DEDUP_FIXED 1 #define FLAG_DEDUP_FIXED 2
#define FLAG_SINGLE_CHUNK 2 #define FLAG_SINGLE_CHUNK 4
#define UTILITY_VERSION "0.8.6" #define UTILITY_VERSION "0.8.6"
#define MASK_CRYPTO_ALG 0x30
#define COMPRESSED 1 #define COMPRESSED 1
#define UNCOMPRESSED 0 #define UNCOMPRESSED 0

View file

@ -23,7 +23,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/param.h> #include <sys/param.h>
#include <fcntl.h>
#include <time.h>
#include <libgen.h> #include <libgen.h>
#include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -34,7 +37,10 @@
#include <rabin_dedup.h> #include <rabin_dedup.h>
#include <skein.h> #include <skein.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <sha256.h> #include <sha256.h>
#include <crypto_aes.h>
#include "utils.h" #include "utils.h"
#include "cpuid.h" #include "cpuid.h"
@ -43,7 +49,7 @@
#define PROVIDER_X64_OPT 1 #define PROVIDER_X64_OPT 1
static void init_sha256(void); static void init_sha256(void);
static int geturandom_bytes(uchar_t rbytes[32]);
/* /*
* Checksum properties * 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(const uint8_t *buf, size_t size, uint64_t crc);
extern uint64_t lzma_crc64_8bchk(const uint8_t *buf, size_t size, 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++; 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);
}

View file

@ -34,6 +34,7 @@ extern "C" {
#define DATA_TEXT 1 #define DATA_TEXT 1
#define DATA_BINARY 2 #define DATA_BINARY 2
#define MAX_PW_LEN 16
#if !defined(sun) && !defined(__sun) #if !defined(sun) && !defined(__sun)
#define uchar_t u_char #define uchar_t u_char
@ -146,6 +147,19 @@ typedef struct {
proc_type_t proc_type; proc_type_t proc_type;
} processor_info_t; } 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 void err_exit(int show_errno, const char *format, ...);
extern const char *get_execname(const char *); extern const char *get_execname(const char *);
extern int parse_numeric(ssize_t *val, const char *str); 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 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 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 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. */ /* Pointer type for compress and decompress functions. */
typedef int (*compress_func_ptr)(void *src, size_t srclen, void *dst, typedef int (*compress_func_ptr)(void *src, size_t srclen, void *dst,