pcompress/crypto/crypto_utils.c
Moinak Ghosh 7a29c7be1e Change default encryption key length to 256 bits.
Add optional ability to change key length at runtime via cli option.
Include key length property in archive header.
Fix header HMAC to include salt, nonce and key length properties.
Retain backward compatibility to handle older format archives.
Fix compilation of AES ASM code.
2013-03-03 20:02:14 +05:30

948 lines
24 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 <sha512.h>
#include <blake2_digest.h>
#include <crypto_aes.h>
#include <KeccakNISTInterface.h>
#include <utils.h>
#include "crypto_utils.h"
#include "sha2_utils.h"
#include "sha3_utils.h"
#ifdef __HASH_COMPATIBILITY_
#include "old/sha2_utils_old.h"
#include "old/sha3_utils_old.h"
#endif
#define PROVIDER_OPENSSL 0
#define PROVIDER_X64_OPT 1
static void init_sha512(void);
static void init_blake2(void);
static int geturandom_bytes(uchar_t rbytes[32]);
static struct blake2_dispatch bdsp;
/*
* Checksum properties
*/
typedef void (*ckinit_func_ptr)(void);
static struct {
const char *name;
const char *desc;
cksum_t cksum_id;
int bytes, mac_bytes;
ckinit_func_ptr init_func;
int compatible;
} cksum_props[] = {
{"CRC64", "Extremely Fast 64-bit CRC from LZMA SDK.",
CKSUM_CRC64, 8, 32, NULL, 0},
{"SKEIN256", "256-bit SKEIN a NIST SHA3 runners-up (90% faster than Keccak).",
CKSUM_SKEIN256, 32, 32, NULL, 1},
{"SKEIN512", "512-bit SKEIN",
CKSUM_SKEIN512, 64, 64, NULL, 1},
{"SHA256", "SHA512/256 version of Intel's optimized (SSE,AVX) SHA2 for x86.",
CKSUM_SHA256, 32, 32, init_sha512, 0},
{"SHA512", "SHA512 version of Intel's optimized (SSE,AVX) SHA2 for x86.",
CKSUM_SHA512, 64, 64, init_sha512, 0},
{"KECCAK256", "Official 256-bit NIST SHA3 optimized implementation.",
CKSUM_KECCAK256, 32, 32, NULL, 0},
{"KECCAK512", "Official 512-bit NIST SHA3 optimized implementation.",
CKSUM_KECCAK512, 64, 64, NULL, 0},
{"BLAKE256", "Very fast 256-bit BLAKE2, derived from the NIST SHA3 runner-up BLAKE.",
CKSUM_BLAKE256, 32, 32, init_blake2, 0},
{"BLAKE512", "Very fast 256-bit BLAKE2, derived from the NIST SHA3 runner-up BLAKE.",
CKSUM_BLAKE512, 64, 64, init_blake2, 0}
};
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);
#ifdef __OSSL_OLD__
/*
* The two functions below fill missing functionality in older versions of OpenSSL.
*/
int
HMAC_CTX_copy(HMAC_CTX *dctx, HMAC_CTX *sctx)
{
if (!EVP_MD_CTX_copy(&dctx->i_ctx, &sctx->i_ctx))
return (0);
if (!EVP_MD_CTX_copy(&dctx->o_ctx, &sctx->o_ctx))
return (0);
if (!EVP_MD_CTX_copy(&dctx->md_ctx, &sctx->md_ctx))
return (0);
memcpy(dctx->key, sctx->key, HMAC_MAX_MD_CBLOCK);
dctx->key_length = sctx->key_length;
dctx->md = sctx->md;
return (1);
}
int
PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
const unsigned char *salt, int saltlen, int iter,
const EVP_MD *digest,
int keylen, unsigned char *out)
{
unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
int cplen, j, k, tkeylen, mdlen;
unsigned long i = 1;
HMAC_CTX hctx;
mdlen = EVP_MD_size(digest);
if (mdlen < 0)
return 0;
HMAC_CTX_init(&hctx);
p = out;
tkeylen = keylen;
if(!pass)
passlen = 0;
else if(passlen == -1)
passlen = strlen(pass);
while(tkeylen)
{
if(tkeylen > mdlen)
cplen = mdlen;
else
cplen = tkeylen;
/* We are unlikely to ever use more than 256 blocks (5120 bits!)
* but just in case...
*/
itmp[0] = (unsigned char)((i >> 24) & 0xff);
itmp[1] = (unsigned char)((i >> 16) & 0xff);
itmp[2] = (unsigned char)((i >> 8) & 0xff);
itmp[3] = (unsigned char)(i & 0xff);
HMAC_Init_ex(&hctx, pass, passlen, digest, NULL);
HMAC_Update(&hctx, salt, saltlen);
HMAC_Update(&hctx, itmp, 4);
HMAC_Final(&hctx, digtmp, NULL);
memcpy(p, digtmp, cplen);
for(j = 1; j < iter; j++)
{
HMAC(digest, pass, passlen,
digtmp, mdlen, digtmp, NULL);
for(k = 0; k < cplen; k++)
p[k] ^= digtmp[k];
}
tkeylen-= cplen;
++i;
p+= cplen;
}
HMAC_CTX_cleanup(&hctx);
return (1);
}
#endif
/*
* Compute a digest of the given data segment. The parameter mt indicates whether
* to use the parallel(OpenMP) versions. Parallel versions are only used when
* a single segment is used to hold the entire file - essentially a single-threaded
* compression.
* In other cases segments are handled in separate threads any way and we do not
* need or want another level of parallelism to cause contention.
*/
int
compute_checksum(uchar_t *cksum_buf, int cksum, uchar_t *buf, uint64_t bytes, int mt)
{
DEBUG_STAT_EN(double strt, en);
#ifdef __HASH_COMPATIBILITY_
assert(mt == 0 || mt == 1 || mt == 2);
#else
assert(mt == 0 || mt == 1);
#endif
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_BLAKE256) {
if (!mt) {
if (bdsp.blake2b(cksum_buf, buf, NULL, 32, bytes, 0) != 0)
return (-1);
} else {
if (bdsp.blake2bp(cksum_buf, buf, NULL, 32, bytes, 0) != 0)
return (-1);
}
} else if (cksum == CKSUM_BLAKE512) {
if (!mt) {
if (bdsp.blake2b(cksum_buf, buf, NULL, 64, bytes, 0) != 0)
return (-1);
} else {
if (bdsp.blake2bp(cksum_buf, buf, NULL, 64, bytes, 0) != 0)
return (-1);
}
/*
* No parallelism for SKEIN. It is deprecated and retained here only for
* backwards compatiblity.
*/
} 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) {
if (!mt)
ossl_SHA256(cksum_buf, buf, bytes);
else if (mt == 1)
ossl_SHA256_par(cksum_buf, buf, bytes);
#ifdef __HASH_COMPATIBILITY_
else
ossl_SHA256_par_old(cksum_buf, buf, bytes);
#endif
} else {
if (!mt)
opt_SHA512t256(cksum_buf, buf, bytes);
else if (mt == 1)
opt_SHA512t256_par(cksum_buf, buf, bytes);
#ifdef __HASH_COMPATIBILITY_
else
opt_SHA512t256_par_old(cksum_buf, buf, bytes);
#endif
}
} else if (cksum == CKSUM_SHA512) {
if (cksum_provider == PROVIDER_OPENSSL) {
if (!mt)
ossl_SHA512(cksum_buf, buf, bytes);
else if (mt == 1)
ossl_SHA512_par(cksum_buf, buf, bytes);
#ifdef __HASH_COMPATIBILITY_
else
ossl_SHA512_par_old(cksum_buf, buf, bytes);
#endif
} else {
if (!mt)
opt_SHA512(cksum_buf, buf, bytes);
else if (mt == 1)
opt_SHA512_par(cksum_buf, buf, bytes);
#ifdef __HASH_COMPATIBILITY_
else
opt_SHA512_par_old(cksum_buf, buf, bytes);
#endif
}
} else if (cksum == CKSUM_KECCAK256) {
if (!mt) {
if (Keccak256(cksum_buf, buf, bytes) != 0)
return (-1);
} else if (mt == 1) {
if (Keccak256_par(cksum_buf, buf, bytes) != 0)
return (-1);
#ifdef __HASH_COMPATIBILITY_
} else {
if (Keccak256_par_old(cksum_buf, buf, bytes) != 0)
return (-1);
#endif
}
} else if (cksum == CKSUM_KECCAK512) {
if (!mt) {
if (Keccak512(cksum_buf, buf, bytes) != 0)
return (-1);
} else if (mt == 1) {
if (Keccak512_par(cksum_buf, buf, bytes) != 0)
return (-1);
#ifdef __HASH_COMPATIBILITY_
} else {
if (Keccak512_par_old(cksum_buf, buf, bytes) != 0)
return (-1);
#endif
}
} 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_sha512(void)
{
#ifdef WORDS_BIGENDIAN
cksum_provider = PROVIDER_OPENSSL;
#else
#ifdef __x86_64__
cksum_provider = PROVIDER_OPENSSL;
if (proc_info.proc_type == PROC_X64_INTEL || proc_info.proc_type == PROC_X64_AMD) {
if (opt_Init_SHA512(&proc_info) == 0) {
cksum_provider = PROVIDER_X64_OPT;
}
}
#endif
#endif
}
static void
init_blake2(void)
{
blake2_module_init(&bdsp, &proc_info);
}
void
list_checksums(FILE *strm, char *pad)
{
int i;
for (i=0; i<(sizeof (cksum_props)/sizeof (cksum_props[0])); i++) {
if (!cksum_props[i].compatible)
fprintf(strm, "%s%10s - %s\n", pad, cksum_props[i].name, cksum_props[i].desc);
}
}
/*
* 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 accept_comptible)
{
int i;
for (i=0; i<(sizeof (cksum_props)/sizeof (cksum_props[0])); i++) {
if ((name != NULL && strcmp(name, cksum_props[i].name) == 0) ||
(*cksum != 0 && *cksum == cksum_props[i].cksum_id)) {
if (!accept_comptible && cksum_props[i].compatible)
break;
*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/Blake/Keccak, HMAC is not used, rather
* their native MAC features are used which are 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_BLAKE256) {
blake2b_state *ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
if (!ctx) return (-1);
if (bdsp.blake2b_init_key(ctx, 32, actx->pkey, cctx->keylen) != 0)
return (-1);
mctx->mac_ctx = ctx;
ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (blake2b_state));
mctx->mac_ctx_reinit = ctx;
} else if (cksum == CKSUM_BLAKE512) {
blake2b_state *ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
if (!ctx) return (-1);
if (bdsp.blake2b_init_key(ctx, 64, actx->pkey, cctx->keylen) != 0)
return (-1);
mctx->mac_ctx = ctx;
ctx = (blake2b_state *)malloc(sizeof (blake2b_state));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (blake2b_state));
mctx->mac_ctx_reinit = ctx;
} else 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, cctx->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, cctx->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, cctx->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_SHA512_Context *ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
if (!ctx) return (-1);
opt_HMAC_SHA512t256_Init(ctx, actx->pkey, cctx->keylen);
mctx->mac_ctx = ctx;
ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (HMAC_SHA512_Context));
mctx->mac_ctx_reinit = ctx;
}
} else if (cksum == CKSUM_SHA512) {
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, cctx->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 {
HMAC_SHA512_Context *ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
if (!ctx) return (-1);
opt_HMAC_SHA512_Init(ctx, actx->pkey, cctx->keylen);
mctx->mac_ctx = ctx;
ctx = (HMAC_SHA512_Context *)malloc(sizeof (HMAC_SHA512_Context));
if (!ctx) {
free(mctx->mac_ctx);
return (-1);
}
memcpy(ctx, mctx->mac_ctx, sizeof (HMAC_SHA512_Context));
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, cctx->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_BLAKE256 || cksum == CKSUM_BLAKE512) {
memcpy(mctx->mac_ctx, mctx->mac_ctx_reinit, sizeof (blake2b_state));
} else 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_SHA512 || 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_SHA512_Context));
}
} 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_BLAKE256 || cksum == CKSUM_BLAKE512) {
bdsp.blake2b_update((blake2b_state *)(mctx->mac_ctx), (uint8_t *)data, len);
} else 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) {
#ifndef __OSSL_OLD__
if (HMAC_Update((HMAC_CTX *)(mctx->mac_ctx), data, len) == 0)
return (-1);
#else
HMAC_Update((HMAC_CTX *)(mctx->mac_ctx), data, len);
#endif
} else {
opt_HMAC_SHA512t256_Update((HMAC_SHA512_Context *)(mctx->mac_ctx), data, len);
}
} else if (cksum == CKSUM_SHA512) {
if (cksum_provider == PROVIDER_OPENSSL) {
#ifndef __OSSL_OLD__
if (HMAC_Update((HMAC_CTX *)(mctx->mac_ctx), data, len) == 0)
return (-1);
#else
HMAC_Update((HMAC_CTX *)(mctx->mac_ctx), data, len);
#endif
} else {
opt_HMAC_SHA512_Update((HMAC_SHA512_Context *)(mctx->mac_ctx), data, len);
}
} 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_BLAKE256) {
bdsp.blake2b_final((blake2b_state *)(mctx->mac_ctx), hash, 32);
*len = 32;
} else if (cksum == CKSUM_BLAKE512) {
bdsp.blake2b_final((blake2b_state *)(mctx->mac_ctx), hash, 64);
*len = 64;
} else 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_SHA512t256_Final((HMAC_SHA512_Context *)(mctx->mac_ctx), hash);
*len = 32;
}
} else if (cksum == CKSUM_SHA512) {
if (cksum_provider == PROVIDER_OPENSSL) {
HMAC_Final((HMAC_CTX *)(mctx->mac_ctx), hash, len);
} else {
opt_HMAC_SHA512_Final((HMAC_SHA512_Context *)(mctx->mac_ctx), hash);
*len = 64;
}
} 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_BLAKE256 || cksum == CKSUM_BLAKE512) {
memset(mctx->mac_ctx, 0, sizeof (blake2b_state));
memset(mctx->mac_ctx_reinit, 0, sizeof (blake2b_state));
} else 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_SHA512 || 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_SHA512_Context));
memset(mctx->mac_ctx_reinit, 0, sizeof (HMAC_SHA512_Context));
}
} 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);
}
/*
* Encryption related functions.
*/
int
init_crypto(crypto_ctx_t *cctx, uchar_t *pwd, int pwd_len, int crypto_alg,
uchar_t *salt, int saltlen, int keylen, uint64_t nonce, int enc_dec)
{
if (crypto_alg == CRYPTO_ALG_AES) {
aes_ctx_t *actx = (aes_ctx_t *)malloc(sizeof (aes_ctx_t));
aes_module_init(&proc_info);
cctx->keylen = keylen;
actx->keylen = keylen;
if (enc_dec) {
/*
* 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, 0);
b = 8 + 4;
*((uint32_t *)&sb[b]) = rand();
compute_checksum(salt, CKSUM_SHA256, &sb[b], 32 + 4, 0);
}
}
/*
* 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);
free(actx);
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);
}
/*
* Input password string from terminal without echoing.
*/
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);
}