9a4ef46d23
The multigest utility calculates message digests of files or, if no file is specified, standard input. The list of possible algorithms is given in the libmultigest(3) manual page. The multigest utility is a simple wrapper for the various different algorithm implementations, , and was designed to be scalable as new message digest algorithms are developed.
419 lines
10 KiB
C
419 lines
10 KiB
C
/*-
|
|
* Copyright (c) 2013 Alistair Crooks <agc@NetBSD.org>
|
|
* 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 ``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 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 <stdint.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "blake2.h"
|
|
|
|
#define BLAKE2B_SALTBYTES 16
|
|
#define BLAKE2B_PERSONALBYTES 16
|
|
#define BLAKE2B_OUTBYTES 64
|
|
#define BLAKE2B_KEYBYTES 64
|
|
|
|
typedef struct __blake2b_param {
|
|
uint8_t digest_length; // 1
|
|
uint8_t key_length; // 2
|
|
uint8_t fanout; // 3
|
|
uint8_t depth; // 4
|
|
uint32_t leaf_length; // 8
|
|
uint64_t node_offset; // 16
|
|
uint8_t node_depth; // 17
|
|
uint8_t inner_length; // 18
|
|
uint8_t reserved[14]; // 32
|
|
uint8_t salt[BLAKE2B_SALTBYTES]; // 48
|
|
uint8_t personal[BLAKE2B_PERSONALBYTES]; // 64
|
|
} blake2b_param;
|
|
|
|
static const uint64_t blake2b_IV[8] = {
|
|
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
|
|
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
|
|
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
|
|
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
|
|
};
|
|
|
|
static const uint8_t blake2b_sigma[12][16] = {
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
|
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
|
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
|
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
|
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
|
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
|
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
|
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
|
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
|
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
|
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
|
|
};
|
|
|
|
/* prevents compiler optimizing out memset() */
|
|
static inline void secure_zero_memory(void *v, size_t n)
|
|
{
|
|
volatile uint8_t *p = (volatile uint8_t *)v;
|
|
|
|
while (n--) {
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static inline uint64_t
|
|
rotr64(const uint64_t w, const unsigned c)
|
|
{
|
|
return (w >> c) | (w << (64 - c));
|
|
}
|
|
|
|
|
|
static inline uint64_t
|
|
load64(const void *src)
|
|
{
|
|
const uint8_t *p = (const uint8_t *)src;
|
|
uint64_t w;
|
|
int indian = 1;
|
|
|
|
if (*(char *)(void *)&indian) {
|
|
return *(const uint64_t *)(src);
|
|
}
|
|
w = *p++;
|
|
w |= (uint64_t)(*p++) << 8;
|
|
w |= (uint64_t)(*p++) << 16;
|
|
w |= (uint64_t)(*p++) << 24;
|
|
w |= (uint64_t)(*p++) << 32;
|
|
w |= (uint64_t)(*p++) << 40;
|
|
w |= (uint64_t)(*p++) << 48;
|
|
w |= (uint64_t)(*p++) << 56;
|
|
return w;
|
|
}
|
|
|
|
static inline void
|
|
store32(void *dst, uint32_t w)
|
|
{
|
|
uint8_t *p = (uint8_t *)dst;
|
|
int indian = 1;
|
|
|
|
if (*(char *)(void *)&indian) {
|
|
*(uint32_t *)(dst) = w;
|
|
}
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w;
|
|
}
|
|
|
|
static inline void
|
|
store64(void *dst, uint64_t w)
|
|
{
|
|
uint8_t *p = (uint8_t *)dst;
|
|
int indian = 1;
|
|
|
|
if (*(char *)(void *)&indian) {
|
|
*(uint64_t *)(dst) = w;
|
|
}
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w; w >>= 8;
|
|
*p++ = (uint8_t)w;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_set_lastnode(BLAKE2_CTX *S)
|
|
{
|
|
S->f[1] = ~0ULL;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_clear_lastnode(BLAKE2_CTX *S)
|
|
{
|
|
S->f[1] = 0ULL;
|
|
return 0;
|
|
}
|
|
|
|
/* Some helper functions, not necessarily useful */
|
|
static inline int
|
|
blake2b_set_lastblock(BLAKE2_CTX *S)
|
|
{
|
|
if (S->last_node) {
|
|
blake2b_set_lastnode(S);
|
|
}
|
|
S->f[0] = ~0ULL;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_clear_lastblock(BLAKE2_CTX *S)
|
|
{
|
|
if (S->last_node) {
|
|
blake2b_clear_lastnode(S);
|
|
}
|
|
S->f[0] = 0ULL;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_increment_counter(BLAKE2_CTX *S, const uint64_t inc)
|
|
{
|
|
S->t[0] += inc;
|
|
S->t[1] += (S->t[0] < inc);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* Parameter-related functions */
|
|
static inline int
|
|
blake2b_param_set_digest_length(blake2b_param *P, const uint8_t digest_length)
|
|
{
|
|
P->digest_length = digest_length;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_fanout(blake2b_param *P, const uint8_t fanout)
|
|
{
|
|
P->fanout = fanout;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_max_depth(blake2b_param *P, const uint8_t depth)
|
|
{
|
|
P->depth = depth;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_leaf_length(blake2b_param *P, const uint32_t leaf_length)
|
|
{
|
|
store32(&P->leaf_length, leaf_length);
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_node_offset(blake2b_param *P, const uint64_t node_offset)
|
|
{
|
|
store64(&P->node_offset, node_offset);
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_node_depth(blake2b_param *P, const uint8_t node_depth)
|
|
{
|
|
P->node_depth = node_depth;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_inner_length(blake2b_param *P, const uint8_t inner_length)
|
|
{
|
|
P->inner_length = inner_length;
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_salt(blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES])
|
|
{
|
|
memcpy(P->salt, salt, BLAKE2B_SALTBYTES);
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_param_set_personal(blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES])
|
|
{
|
|
memcpy(P->personal, personal, BLAKE2B_PERSONALBYTES);
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
blake2b_init0(BLAKE2_CTX *S)
|
|
{
|
|
int i;
|
|
|
|
memset(S, 0, sizeof(*S));
|
|
for (i = 0; i < 8; ++i) {
|
|
S->h[i] = blake2b_IV[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* init xors IV with input parameter block */
|
|
static int
|
|
blake2b_init_param(BLAKE2_CTX *S, const blake2b_param *P)
|
|
{
|
|
const uint8_t *p;
|
|
size_t i;
|
|
|
|
blake2b_init0(S);
|
|
p = (const uint8_t *)(const void *)(P);
|
|
/* IV XOR ParamBlock */
|
|
for (i = 0; i < 8; ++i) {
|
|
S->h[i] ^= load64(p + sizeof(S->h[i]) * i);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
blake2b_init(BLAKE2_CTX *S, const uint8_t outlen)
|
|
{
|
|
blake2b_param P;
|
|
|
|
if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) {
|
|
return -1;
|
|
}
|
|
memset(&P, 0x0, sizeof(P));
|
|
P.digest_length = outlen;
|
|
P.key_length = 0;
|
|
P.fanout = 1;
|
|
P.depth = 1;
|
|
store32(&P.leaf_length, 0);
|
|
store64(&P.node_offset, 0);
|
|
P.node_depth = 0;
|
|
P.inner_length = 0;
|
|
memset(P.reserved, 0, sizeof(P.reserved));
|
|
memset(P.salt, 0, sizeof(P.salt));
|
|
memset(P.personal, 0, sizeof(P.personal));
|
|
return blake2b_init_param(S, &P);
|
|
}
|
|
|
|
|
|
static int
|
|
blake2b_compress(BLAKE2_CTX *S, const uint8_t block[BLAKE2B_BLOCKBYTES])
|
|
{
|
|
uint64_t m[16];
|
|
uint64_t v[16];
|
|
int i;
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
m[i] = load64(block + i * sizeof(m[i]));
|
|
}
|
|
for (i = 0; i < 8; ++i) {
|
|
v[i] = S->h[i];
|
|
}
|
|
v[8] = blake2b_IV[0];
|
|
v[9] = blake2b_IV[1];
|
|
v[10] = blake2b_IV[2];
|
|
v[11] = blake2b_IV[3];
|
|
v[12] = S->t[0] ^ blake2b_IV[4];
|
|
v[13] = S->t[1] ^ blake2b_IV[5];
|
|
v[14] = S->f[0] ^ blake2b_IV[6];
|
|
v[15] = S->f[1] ^ blake2b_IV[7];
|
|
#define G(r,i,a,b,c,d) do { \
|
|
a = a + b + m[blake2b_sigma[r][i + i]]; \
|
|
d = rotr64(d ^ a, 32); \
|
|
c = c + d; \
|
|
b = rotr64(b ^ c, 24); \
|
|
a = a + b + m[blake2b_sigma[r][i + i + 1]]; \
|
|
d = rotr64(d ^ a, 16); \
|
|
c = c + d; \
|
|
b = rotr64(b ^ c, 63); \
|
|
} while(/*CONSTCOND*/0)
|
|
#define ROUND(r) do { \
|
|
G(r,0,v[0],v[4],v[8],v[12]); \
|
|
G(r,1,v[1],v[5],v[9],v[13]); \
|
|
G(r,2,v[2],v[6],v[10],v[14]); \
|
|
G(r,3,v[3],v[7],v[11],v[15]); \
|
|
G(r,4,v[0],v[5],v[10],v[15]); \
|
|
G(r,5,v[1],v[6],v[11],v[12]); \
|
|
G(r,6,v[2],v[7],v[8],v[13]); \
|
|
G(r,7,v[3],v[4],v[9],v[14]); \
|
|
} while(/*CONSTCOND*/0)
|
|
ROUND(0);
|
|
ROUND(1);
|
|
ROUND(2);
|
|
ROUND(3);
|
|
ROUND(4);
|
|
ROUND(5);
|
|
ROUND(6);
|
|
ROUND(7);
|
|
ROUND(8);
|
|
ROUND(9);
|
|
ROUND(10);
|
|
ROUND(11);
|
|
for (i = 0; i < 8; ++i) {
|
|
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
|
|
}
|
|
#undef G
|
|
#undef ROUND
|
|
return 0;
|
|
}
|
|
|
|
/* inlen now in bytes */
|
|
int
|
|
blake2b_update(BLAKE2_CTX *S, const uint8_t *in, uint64_t inlen)
|
|
{
|
|
while (inlen > 0) {
|
|
size_t left = S->buflen;
|
|
size_t fill = 2 * BLAKE2B_BLOCKBYTES - left;
|
|
|
|
if (inlen > fill) {
|
|
memcpy(S->buf + left, in, fill); // Fill buffer
|
|
S->buflen += fill;
|
|
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
|
blake2b_compress(S, S->buf); // Compress
|
|
memcpy(S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES); // Shift buffer left
|
|
S->buflen -= BLAKE2B_BLOCKBYTES;
|
|
in += fill;
|
|
inlen -= fill;
|
|
} else {
|
|
// inlen <= fill
|
|
memcpy(S->buf + left, in, inlen);
|
|
S->buflen += inlen; // Be lazy, do not compress
|
|
in += inlen;
|
|
inlen -= inlen;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Is this correct? */
|
|
int
|
|
blake2b_final(BLAKE2_CTX *S, uint8_t *out, uint8_t outlen)
|
|
{
|
|
uint8_t buffer[BLAKE2B_OUTBYTES];
|
|
int i;
|
|
|
|
if (S->buflen > BLAKE2B_BLOCKBYTES) {
|
|
blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);
|
|
blake2b_compress(S, S->buf);
|
|
S->buflen -= BLAKE2B_BLOCKBYTES;
|
|
memcpy(S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen);
|
|
}
|
|
blake2b_increment_counter(S, S->buflen);
|
|
blake2b_set_lastblock(S);
|
|
memset(S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */
|
|
blake2b_compress(S, S->buf);
|
|
for (i = 0; i < 8; ++i) {
|
|
/* Output full hash to temp buffer */
|
|
store64(buffer + sizeof(S->h[i]) * i, S->h[i]);
|
|
}
|
|
memcpy(out, buffer, outlen);
|
|
return 0;
|
|
}
|