pcompress/crypto/crypto_utils.c
Moinak Ghosh 6b756eb165 Fix Delta2 handling for small buffers.
Fix LZP handling during preprocessing.
Fix type flag handling during preprocessing.
Update test cases to use configurable list of test corpus files.
Update some more int64_t datatypes to uint64_t
Add a gitignore.
2013-01-02 22:56:21 +05:30

680 lines
17 KiB
C

/*
* 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
*/
#include <sys/types.h>
#include <sys/param.h>
#include <fcntl.h>
#include <time.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <skein.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <sha256.h>
#include <crypto_aes.h>
#include <KeccakNISTInterface.h>
#include <cpuid.h>
#include "crypto_utils.h"
#define PROVIDER_OPENSSL 0
#define PROVIDER_X64_OPT 1
static void init_sha256(void);
static int geturandom_bytes(uchar_t rbytes[32]);
/*
* Checksum properties
*/
typedef void (*ckinit_func_ptr)(void);
static struct {
const char *name;
cksum_t cksum_id;
int bytes, mac_bytes;
ckinit_func_ptr init_func;
} cksum_props[] = {
{"CRC64", CKSUM_CRC64, 8, 32, NULL},
{"SKEIN256", CKSUM_SKEIN256, 32, 32, NULL},
{"SKEIN512", CKSUM_SKEIN512, 64, 64, NULL},
{"SHA256", CKSUM_SHA256, 32, 32, init_sha256},
{"SHA512", CKSUM_SHA512, 64, 64, NULL},
{"KECCAK256", CKSUM_KECCAK256, 32, 32, NULL},
{"KECCAK512", CKSUM_KECCAK512, 64, 64, NULL}
};
static int cksum_provider = PROVIDER_OPENSSL;
extern uint64_t lzma_crc64(const uint8_t *buf, uint64_t size, uint64_t crc);
extern uint64_t lzma_crc64_8bchk(const uint8_t *buf, uint64_t size,
uint64_t crc, uint64_t *cnt);
int
compute_checksum(uchar_t *cksum_buf, int cksum, uchar_t *buf, uint64_t bytes)
{
DEBUG_STAT_EN(double strt, en);
DEBUG_STAT_EN(strt = get_wtime_millis());
if (cksum == CKSUM_CRC64) {
uint64_t *ck = (uint64_t *)cksum_buf;
*ck = lzma_crc64(buf, bytes, 0);
} else if (cksum == CKSUM_SKEIN256) {
Skein_512_Ctxt_t ctx;
Skein_512_Init(&ctx, 256);
Skein_512_Update(&ctx, buf, bytes);
Skein_512_Final(&ctx, cksum_buf);
} else if (cksum == CKSUM_SKEIN512) {
Skein_512_Ctxt_t ctx;
Skein_512_Init(&ctx, 512);
Skein_512_Update(&ctx, buf, bytes);
Skein_512_Final(&ctx, cksum_buf);
} else if (cksum == CKSUM_SHA256) {
if (cksum_provider == PROVIDER_OPENSSL) {
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, buf, bytes);
SHA256_Final(cksum_buf, &ctx);
} else {
SHA256_Context ctx;
opt_SHA256_Init(&ctx);
opt_SHA256_Update(&ctx, buf, bytes);
opt_SHA256_Final(&ctx, cksum_buf);
}
} else if (cksum == CKSUM_SHA512) {
SHA512_CTX ctx;
SHA512_Init(&ctx);
SHA512_Update(&ctx, buf, bytes);
SHA512_Final(cksum_buf, &ctx);
} else if (cksum == CKSUM_KECCAK256) {
if (Keccak_Hash(256, buf, bytes * 8, cksum_buf) != 0)
return (-1);
} else if (cksum == CKSUM_KECCAK512) {
if (Keccak_Hash(512, buf, bytes * 8, cksum_buf) != 0)
return (-1);
} else {
return (-1);
}
DEBUG_STAT_EN(en = get_wtime_millis());
DEBUG_STAT_EN(fprintf(stderr, "Checksum computed at %.3f MB/s\n", get_mb_s(bytes, strt, en)));
return (0);
}
static void
init_sha256(void)
{
#ifdef WORDS_BIGENDIAN
cksum_provider = PROVIDER_OPENSSL;
#else
#ifdef __x86_64__
processor_info_t pc;
cksum_provider = PROVIDER_OPENSSL;
cpuid_basic_identify(&pc);
if (pc.proc_type == PROC_X64_INTEL || pc.proc_type == PROC_X64_AMD) {
if (opt_Init_SHA(&pc) == 0) {
cksum_provider = PROVIDER_X64_OPT;
}
}
#endif
#endif
}
/*
* Check if either the given checksum name or id is valid and
* return it's properties.
*/
int
get_checksum_props(const char *name, int *cksum, int *cksum_bytes, int *mac_bytes)
{
int i;
for (i=0; i<sizeof (cksum_props); i++) {
if ((name != NULL && strcmp(name, cksum_props[i].name) == 0) ||
(*cksum != 0 && *cksum == cksum_props[i].cksum_id)) {
*cksum = cksum_props[i].cksum_id;
*cksum_bytes = cksum_props[i].bytes;
*mac_bytes = cksum_props[i].mac_bytes;
if (cksum_props[i].init_func)
cksum_props[i].init_func();
return (0);
}
}
return (-1);
}
/*
* Endian independent way of storing the checksum bytes. This is actually
* storing in little endian format and a copy can be avoided in x86 land.
* However unsightly ifdefs are avoided here since this is not so performance
* critical.
*/
void
serialize_checksum(uchar_t *checksum, uchar_t *buf, int cksum_bytes)
{
int i,j;
j = 0;
for (i=cksum_bytes; i>0; i--) {
buf[j] = checksum[i-1];
j++;
}
}
void
deserialize_checksum(uchar_t *checksum, uchar_t *buf, int cksum_bytes)
{
int i,j;
j = 0;
for (i=cksum_bytes; i>0; i--) {
checksum[i-1] = buf[j];
j++;
}
}
/*
* Perform keyed hashing. With Skein, HMAC is not used, rather Skein's
* native MAC is used which is more optimal than HMAC.
*/
int
hmac_init(mac_ctx_t *mctx, int cksum, crypto_ctx_t *cctx)
{
aes_ctx_t *actx = (aes_ctx_t *)(cctx->crypto_ctx);
mctx->mac_cksum = cksum;
if (cksum == CKSUM_SKEIN256) {
Skein_512_Ctxt_t *ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) return (-1);
Skein_512_InitExt(ctx, 256, SKEIN_CFG_TREE_INFO_SEQUENTIAL,
actx->pkey, KEYLEN);
mctx->mac_ctx = ctx;
ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (Skein_512_Ctxt_t));
mctx->mac_ctx_reinit = ctx;
} else if (cksum == CKSUM_SKEIN512) {
Skein_512_Ctxt_t *ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) return (-1);
Skein_512_InitExt(ctx, 512, SKEIN_CFG_TREE_INFO_SEQUENTIAL,
actx->pkey, KEYLEN);
mctx->mac_ctx = ctx;
ctx = (Skein_512_Ctxt_t *)malloc(sizeof (Skein_512_Ctxt_t));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (Skein_512_Ctxt_t));
mctx->mac_ctx_reinit = ctx;
} else if (cksum == CKSUM_SHA256 || cksum == CKSUM_CRC64) {
if (cksum_provider == PROVIDER_OPENSSL) {
HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
if (!ctx) return (-1);
HMAC_CTX_init(ctx);
HMAC_Init_ex(ctx, actx->pkey, KEYLEN, EVP_sha256(), NULL);
mctx->mac_ctx = ctx;
ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
if (!HMAC_CTX_copy(ctx, (HMAC_CTX *)(mctx->mac_ctx))) {
free(ctx);
free(mctx->mac_ctx);
return (-1);
}
mctx->mac_ctx_reinit = ctx;
} else {
HMAC_SHA256_Context *ctx = (HMAC_SHA256_Context *)malloc(sizeof (HMAC_SHA256_Context));
if (!ctx) return (-1);
opt_HMAC_SHA256_Init(ctx, actx->pkey, KEYLEN);
mctx->mac_ctx = ctx;
ctx = (HMAC_SHA256_Context *)malloc(sizeof (HMAC_SHA256_Context));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (HMAC_SHA256_Context));
mctx->mac_ctx_reinit = ctx;
}
} else if (cksum == CKSUM_SHA512) {
HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
if (!ctx) return (-1);
HMAC_CTX_init(ctx);
HMAC_Init_ex(ctx, actx->pkey, KEYLEN, EVP_sha512(), NULL);
mctx->mac_ctx = ctx;
ctx = (HMAC_CTX *)malloc(sizeof (HMAC_CTX));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
if (!HMAC_CTX_copy(ctx, (HMAC_CTX *)(mctx->mac_ctx))) {
free(ctx);
free(mctx->mac_ctx);
return (-1);
}
mctx->mac_ctx_reinit = ctx;
} else if (cksum == CKSUM_KECCAK256 || cksum == CKSUM_KECCAK512) {
hashState *ctx = (hashState *)malloc(sizeof (hashState));
if (!ctx) return (-1);
if (cksum == CKSUM_KECCAK256) {
if (Keccak_Init(ctx, 256) != 0)
return (-1);
} else {
if (Keccak_Init(ctx, 512) != 0)
return (-1);
}
if (Keccak_Update(ctx, actx->pkey, KEYLEN << 3) != 0)
return (-1);
mctx->mac_ctx = ctx;
ctx = (hashState *)malloc(sizeof (hashState));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (hashState));
mctx->mac_ctx_reinit = ctx;
} else {
return (-1);
}
return (0);
}
int
hmac_reinit(mac_ctx_t *mctx)
{
int cksum = mctx->mac_cksum;
if (cksum == CKSUM_SKEIN256 || cksum == CKSUM_SKEIN512) {
memcpy(mctx->mac_ctx, mctx->mac_ctx_reinit, sizeof (Skein_512_Ctxt_t));
} else if (cksum == CKSUM_SHA256 || cksum == CKSUM_CRC64) {
if (cksum_provider == PROVIDER_OPENSSL) {
HMAC_CTX_copy((HMAC_CTX *)(mctx->mac_ctx),
(HMAC_CTX *)(mctx->mac_ctx_reinit));
} else {
memcpy(mctx->mac_ctx, mctx->mac_ctx_reinit, sizeof (HMAC_SHA256_Context));
}
} else if (cksum == CKSUM_SHA512) {
HMAC_CTX_copy((HMAC_CTX *)(mctx->mac_ctx), (HMAC_CTX *)(mctx->mac_ctx_reinit));
} else if (cksum == CKSUM_KECCAK256 || cksum == CKSUM_KECCAK512) {
memcpy(mctx->mac_ctx, mctx->mac_ctx_reinit, sizeof (hashState));
} else {
return (-1);
}
return (0);
}
int
hmac_update(mac_ctx_t *mctx, uchar_t *data, uint64_t len)
{
int cksum = mctx->mac_cksum;
if (cksum == CKSUM_SKEIN256 || cksum == CKSUM_SKEIN512) {
Skein_512_Update((Skein_512_Ctxt_t *)(mctx->mac_ctx), data, len);
} else if (cksum == CKSUM_SHA256 || cksum == CKSUM_CRC64) {
if (cksum_provider == PROVIDER_OPENSSL) {
if (HMAC_Update((HMAC_CTX *)(mctx->mac_ctx), data, len) == 0)
return (-1);
} else {
opt_HMAC_SHA256_Update((HMAC_SHA256_Context *)(mctx->mac_ctx), data, len);
}
} else if (cksum == CKSUM_SHA512) {
if (HMAC_Update((HMAC_CTX *)(mctx->mac_ctx), data, len) == 0)
return (-1);
} else if (cksum == CKSUM_KECCAK256 || cksum == CKSUM_KECCAK512) {
// Keccak takes data length in bits so we have to scale
while (len > KECCAK_MAX_SEG) {
uint64_t blen;
blen = KECCAK_MAX_SEG;
if (Keccak_Update((hashState *)(mctx->mac_ctx), data, blen << 3) != 0)
return (-1);
len -= KECCAK_MAX_SEG;
}
if (Keccak_Update((hashState *)(mctx->mac_ctx), data, len << 3) != 0)
return (-1);
} else {
return (-1);
}
return (0);
}
int
hmac_final(mac_ctx_t *mctx, uchar_t *hash, unsigned int *len)
{
int cksum = mctx->mac_cksum;
if (cksum == CKSUM_SKEIN256) {
Skein_512_Final((Skein_512_Ctxt_t *)(mctx->mac_ctx), hash);
*len = 32;
} else if (cksum == CKSUM_SKEIN512) {
Skein_512_Final((Skein_512_Ctxt_t *)(mctx->mac_ctx), hash);
*len = 64;
} else if (cksum == CKSUM_SHA256 || cksum == CKSUM_CRC64) {
if (cksum_provider == PROVIDER_OPENSSL) {
HMAC_Final((HMAC_CTX *)(mctx->mac_ctx), hash, len);
} else {
opt_HMAC_SHA256_Final((HMAC_SHA256_Context *)(mctx->mac_ctx), hash);
*len = 32;
}
} else if (cksum == CKSUM_SHA512) {
HMAC_Final((HMAC_CTX *)(mctx->mac_ctx), hash, len);
} else if (cksum == CKSUM_KECCAK256 || cksum == CKSUM_KECCAK512) {
if (Keccak_Final((hashState *)(mctx->mac_ctx), hash) != 0)
return (-1);
if (cksum == CKSUM_KECCAK256)
*len = 32;
else
*len = 64;
} else {
return (-1);
}
return (0);
}
int
hmac_cleanup(mac_ctx_t *mctx)
{
int cksum = mctx->mac_cksum;
if (cksum == CKSUM_SKEIN256 || cksum == CKSUM_SKEIN512) {
memset(mctx->mac_ctx, 0, sizeof (Skein_512_Ctxt_t));
memset(mctx->mac_ctx_reinit, 0, sizeof (Skein_512_Ctxt_t));
} else if (cksum == CKSUM_SHA256 || cksum == CKSUM_CRC64) {
if (cksum_provider == PROVIDER_OPENSSL) {
HMAC_CTX_cleanup((HMAC_CTX *)(mctx->mac_ctx));
HMAC_CTX_cleanup((HMAC_CTX *)(mctx->mac_ctx_reinit));
} else {
memset(mctx->mac_ctx, 0, sizeof (HMAC_SHA256_Context));
memset(mctx->mac_ctx_reinit, 0, sizeof (HMAC_SHA256_Context));
}
} else if (cksum == CKSUM_SHA512) {
HMAC_CTX_cleanup((HMAC_CTX *)(mctx->mac_ctx));
HMAC_CTX_cleanup((HMAC_CTX *)(mctx->mac_ctx_reinit));
} else if (cksum == CKSUM_KECCAK256 || cksum == CKSUM_KECCAK512) {
memset(mctx->mac_ctx, 0, sizeof (hashState));
memset(mctx->mac_ctx_reinit, 0, sizeof (hashState));
} else {
return (-1);
}
mctx->mac_cksum = 0;
free(mctx->mac_ctx);
free(mctx->mac_ctx_reinit);
return (0);
}
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 = (aes_ctx_t *)malloc(sizeof (aes_ctx_t));
if (enc_dec) {
/*
* Encryption init.
*/
cctx->salt = (uchar_t *)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 = (uchar_t *)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, uint64_t bytes, uint64_t id)
{
if (cctx->crypto_alg == CRYPTO_ALG_AES) {
if (cctx->enc_dec == ENCRYPT_FLAG) {
return (aes_encrypt((aes_ctx_t *)(cctx->crypto_ctx), from, to, bytes, id));
} else {
return (aes_decrypt((aes_ctx_t *)(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((aes_ctx_t *)(cctx->crypto_ctx)));
}
void
crypto_clean_pkey(crypto_ctx_t *cctx)
{
aes_clean_pkey((aes_ctx_t *)(cctx->crypto_ctx));
}
void
cleanup_crypto(crypto_ctx_t *cctx)
{
aes_cleanup((aes_ctx_t *)(cctx->crypto_ctx));
memset(cctx->salt, 0, 32);
free(cctx->salt);
free(cctx);
}
static int
geturandom_bytes(uchar_t rbytes[32])
{
int fd;
int64_t lenread;
uchar_t * buf = rbytes;
uint64_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(uchar_t pw[MAX_PW_LEN], const char *prompt, int twice)
{
int fd, len;
FILE *input, *strm;
struct termios oldt, newt;
char 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);
}
if (twice) {
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);
}
} else {
tcsetattr(fd, TCSANOW, &oldt);
fflush(strm);
fputs("\n", stderr);
}
len = strlen(pw1);
pw1[len-1] = '\0';
strcpy((char *)pw, (const char *)pw1);
memset(pw1, 0, MAX_PW_LEN);
memset(pw2, 0, MAX_PW_LEN);
return (len);
}