/* See LICENSE file for copyright and license details. */
#include "common.h"
static int
parse_value(size_t *valp, const char *p, const char **end_out)
{
size_t digit;
if (*valp)
return -1;
if (*p++ != '=' || !('1' <= *p && *p <= '9'))
return -1;
while (isdigit(*p)) {
digit = (size_t)(*p++ & 15);
if (*valp > (SIZE_MAX - digit) / 10U)
return -1;
*valp = *valp * 10U + digit;
}
*end_out = p;
return 0;
}
static int
with_n(int (*initfunc)(struct libhashsum_hasher *, size_t), struct libhashsum_hasher *this, const char *algorithm)
{
const char *p;
size_t n = 0;
p = strchr(algorithm, '[');
if (!p || *++p == ']') {
if (p && *++p)
goto einval;
return initfunc(this, 0);
}
if (*p != 'n' && *p != 'N')
goto einval;
if (parse_value(&n, &p[1], &p))
goto einval;
if (*p++ != ']' || *p)
goto einval;
return initfunc(this, n);
einval:
errno = EINVAL;
return -1;
}
static int
with_rcnz(int (*initfunc)(struct libhashsum_hasher *, size_t, size_t, size_t, size_t),
struct libhashsum_hasher *this, const char *algorithm)
{
const char *p;
size_t r = 0, c = 0, n = 0, z = 0;
p = strchr(algorithm, '[');
if (!p || *++p == ']') {
if (p && *++p)
goto einval;
return initfunc(this, 0, 0, 0, 0);
}
for (;;) {
if (*p == 'r' || *p == 'R') {
if (parse_value(&r, &p[1], &p))
goto einval;
} else if (*p == 'c' || *p == 'C') {
if (parse_value(&c, &p[1], &p))
goto einval;
} else if (*p == 'n' || *p == 'N') {
if (parse_value(&n, &p[1], &p))
goto einval;
} else if (*p == 'z' || *p == 'Z') {
if (parse_value(&z, &p[1], &p))
goto einval;
} else if (*p == ']') {
break;
} else {
goto einval;
}
if (*p == ']')
break;
if (*p++ != ',')
goto einval;
}
if (p[1])
goto einval;
return initfunc(this, r, c, n, z);
einval:
errno = EINVAL;
return -1;
}
static int
with_salt(int (*initfunc)(struct libhashsum_hasher *, const void *),
struct libhashsum_hasher *this, const char *algorithm, size_t saltbytes)
{
const char *p;
uint8_t a, b, salt[32];
size_t salti = 0;
p = strchr(algorithm, '[');
if (!p || *++p == ']') {
if (p && *++p)
goto einval;
return initfunc(this, NULL);
}
if (*p != 's' && *p != 'S')
goto einval;
p++;
if (*p != 'a' && *p != 'A')
goto einval;
p++;
if (*p != 'l' && *p != 'L')
goto einval;
p++;
if (*p != 't' && *p != 'T')
goto einval;
p++;
if (*p != '=')
goto einval;
p++;
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
p = &p[2];
for (;;) {
if (isxdigit(p[0]) && isxdigit(p[1])) {
if (!saltbytes)
goto einval;
saltbytes--;
} else if (isxdigit(p[0])) {
goto einval;
} else if (saltbytes) {
goto einval;
} else {
break;
}
a = (uint8_t)(((p[0] & 15) + (p[0] > '9' ? 9 : 0)) << 4);
b = (uint8_t)(((p[1] & 15) + (p[1] > '9' ? 9 : 0)) << 0);
salt[salti++] = (uint8_t)(a | b);
p = &p[2];
}
if (*p++ != ']' || *p)
goto einval;
return initfunc(this, salti ? salt : NULL);
einval:
errno = EINVAL;
return -1;
}
static int
blake2(int (*initfunc)(struct libhashsum_hasher *, size_t, const void *, const void *, const void *, size_t),
struct libhashsum_hasher *this, const char *algorithm, size_t saltbytes, size_t pepperbytes)
{
const char *p;
uint8_t a, b, salt[16], pepper[16], *key = NULL;
size_t n = 0, salti = 0, pepperi = 0, keyi = 0, i;
int r;
p = strchr(algorithm, '[');
if (!p || *++p == ']') {
if (p && *++p)
goto einval;
return initfunc(this, 0, NULL, NULL, NULL, 0);
}
next:
if (!salti && !strncasecmp(p, "salt=", 5U)) {
p = &p[5];
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
p = &p[2];
for (;;) {
if (isxdigit(p[0]) && isxdigit(p[1])) {
if (!saltbytes)
goto einval;
saltbytes--;
} else if (isxdigit(p[0])) {
goto einval;
} else if (saltbytes) {
goto einval;
} else {
break;
}
a = (uint8_t)(((p[0] & 15) + (p[0] > '9' ? 9 : 0)) << 4);
b = (uint8_t)(((p[1] & 15) + (p[1] > '9' ? 9 : 0)) << 0);
salt[salti++] = (uint8_t)(a | b);
p = &p[2];
}
} else if (!pepperi && !strncasecmp(p, "pepper=", 7U)) {
p = &p[7];
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
p = &p[2];
for (;;) {
if (isxdigit(p[0]) && isxdigit(p[1])) {
if (!pepperbytes)
goto einval;
pepperbytes--;
} else if (isxdigit(p[0])) {
goto einval;
} else if (pepperbytes) {
goto einval;
} else {
break;
}
a = (uint8_t)(((p[0] & 15) + (p[0] > '9' ? 9 : 0)) << 4);
b = (uint8_t)(((p[1] & 15) + (p[1] > '9' ? 9 : 0)) << 0);
pepper[pepperi++] = (uint8_t)(a | b);
p = &p[2];
}
} else if (!keyi && !strncasecmp(p, "key=", 4U)) {
p = &p[4];
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
p = &p[2];
while (isxdigit(p[keyi | 0]) && isxdigit(p[keyi | 1]))
keyi += 2U;
if (isxdigit(p[keyi]))
goto einval;
keyi /= 2U;
if (keyi) {
key = malloc(keyi);
if (!key)
goto fail;
}
for (i = 0; i < keyi; i++) {
a = (uint8_t)(((p[0] & 15) + (p[0] > '9' ? 9 : 0)) << 4);
b = (uint8_t)(((p[1] & 15) + (p[1] > '9' ? 9 : 0)) << 0);
key[i] = (uint8_t)(a | b);
p = &p[2];
}
} else if (*p == 'n' || *p == 'N') {
if (parse_value(&n, &p[1], &p))
goto einval;
} else {
goto einval;
}
if (*p == ',') {
p++;
goto next;
}
if (*p++ != ']' || *p)
goto einval;
if (keyi > SIZE_MAX / 8U)
goto einval;
r = initfunc(this, n, salti ? salt : NULL, pepperi ? pepper : NULL, key, keyi * 8U);
free(key);
return r;
einval:
errno = EINVAL;
fail:
free(key);
return -1;
}
int
libhashsum_init_hasher_from_string(struct libhashsum_hasher *this, const char *algorithm)
{
enum libhashsum_algorithm algo;
if (!libhashsum_get_algorithm_from_string(&algo, algorithm)) {
errno = EINVAL;
return -1;
}
switch (algo) {
case LIBHASHSUM_MD2: return libhashsum_init_md2_hasher(this);
case LIBHASHSUM_MD4: return libhashsum_init_md4_hasher(this);
case LIBHASHSUM_MD5: return libhashsum_init_md5_hasher(this);
case LIBHASHSUM_RIPEMD_128: return libhashsum_init_ripemd_128_hasher(this);
case LIBHASHSUM_RIPEMD_160: return libhashsum_init_ripemd_160_hasher(this);
case LIBHASHSUM_RIPEMD_256: return libhashsum_init_ripemd_256_hasher(this);
case LIBHASHSUM_RIPEMD_320: return libhashsum_init_ripemd_320_hasher(this);
case LIBHASHSUM_SHA0: return libhashsum_init_sha0_hasher(this);
case LIBHASHSUM_SHA1: return libhashsum_init_sha1_hasher(this);
case LIBHASHSUM_SHA_224: return libhashsum_init_sha_224_hasher(this);
case LIBHASHSUM_SHA_256: return libhashsum_init_sha_256_hasher(this);
case LIBHASHSUM_SHA_384: return libhashsum_init_sha_384_hasher(this);
case LIBHASHSUM_SHA_512: return libhashsum_init_sha_512_hasher(this);
case LIBHASHSUM_SHA_512_224: return libhashsum_init_sha_512_224_hasher(this);
case LIBHASHSUM_SHA_512_256: return libhashsum_init_sha_512_256_hasher(this);
case LIBHASHSUM_KECCAK_224: return libhashsum_init_keccak_224_hasher(this);
case LIBHASHSUM_KECCAK_256: return libhashsum_init_keccak_256_hasher(this);
case LIBHASHSUM_KECCAK_384: return libhashsum_init_keccak_384_hasher(this);
case LIBHASHSUM_KECCAK_512: return libhashsum_init_keccak_512_hasher(this);
case LIBHASHSUM_SHA3_224: return libhashsum_init_sha3_224_hasher(this);
case LIBHASHSUM_SHA3_256: return libhashsum_init_sha3_256_hasher(this);
case LIBHASHSUM_SHA3_384: return libhashsum_init_sha3_384_hasher(this);
case LIBHASHSUM_SHA3_512: return libhashsum_init_sha3_512_hasher(this);
case LIBHASHSUM_SHAKE128: return with_n(&libhashsum_init_shake128_hasher, this, algorithm);
case LIBHASHSUM_SHAKE256: return with_n(&libhashsum_init_shake256_hasher, this, algorithm);
case LIBHASHSUM_SHAKE512: return with_n(&libhashsum_init_shake512_hasher, this, algorithm);
case LIBHASHSUM_RAWSHAKE128: return with_n(&libhashsum_init_rawshake128_hasher, this, algorithm);
case LIBHASHSUM_RAWSHAKE256: return with_n(&libhashsum_init_rawshake256_hasher, this, algorithm);
case LIBHASHSUM_RAWSHAKE512: return with_n(&libhashsum_init_rawshake512_hasher, this, algorithm);
case LIBHASHSUM_KECCAK: return with_rcnz(&libhashsum_init_keccak_hasher, this, algorithm);
case LIBHASHSUM_BLAKE224: return with_salt(&libhashsum_init_blake224_hasher, this, algorithm, 16U);
case LIBHASHSUM_BLAKE256: return with_salt(&libhashsum_init_blake256_hasher, this, algorithm, 16U);
case LIBHASHSUM_BLAKE384: return with_salt(&libhashsum_init_blake384_hasher, this, algorithm, 32U);
case LIBHASHSUM_BLAKE512: return with_salt(&libhashsum_init_blake512_hasher, this, algorithm, 32U);
case LIBHASHSUM_BLAKE2S: return blake2(&libhashsum_init_blake2s_hasher, this, algorithm, 8U, 8U);
case LIBHASHSUM_BLAKE2B: return blake2(&libhashsum_init_blake2b_hasher, this, algorithm, 16U, 16U);
default:
abort();
}
}