Blame crypto/siphash/siphash.c

Packit c4476c
/*
Packit c4476c
 * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved.
Packit c4476c
 *
Packit c4476c
 * Licensed under the OpenSSL license (the "License").  You may not use
Packit c4476c
 * this file except in compliance with the License.  You can obtain a copy
Packit c4476c
 * in the file LICENSE in the source distribution or at
Packit c4476c
 * https://www.openssl.org/source/license.html
Packit c4476c
 */
Packit c4476c
Packit c4476c
/* Based on https://131002.net/siphash C reference implementation */
Packit c4476c
/*
Packit c4476c
   SipHash reference C implementation
Packit c4476c
Packit c4476c
   Copyright (c) 2012-2016 Jean-Philippe Aumasson
Packit c4476c
   Copyright (c) 2012-2014 Daniel J. Bernstein
Packit c4476c
Packit c4476c
   To the extent possible under law, the author(s) have dedicated all copyright
Packit c4476c
   and related and neighboring rights to this software to the public domain
Packit c4476c
   worldwide. This software is distributed without any warranty.
Packit c4476c
Packit c4476c
   You should have received a copy of the CC0 Public Domain Dedication along
Packit c4476c
   with this software. If not, see
Packit c4476c
   <http://creativecommons.org/publicdomain/zero/1.0/>.
Packit c4476c
 */
Packit c4476c
Packit c4476c
#include <stdlib.h>
Packit c4476c
#include <string.h>
Packit c4476c
#include <openssl/crypto.h>
Packit c4476c
Packit c4476c
#include "crypto/siphash.h"
Packit c4476c
#include "siphash_local.h"
Packit c4476c
Packit c4476c
/* default: SipHash-2-4 */
Packit c4476c
#define SIPHASH_C_ROUNDS 2
Packit c4476c
#define SIPHASH_D_ROUNDS 4
Packit c4476c
Packit c4476c
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
Packit c4476c
Packit c4476c
#define U32TO8_LE(p, v)                                                        \
Packit c4476c
    (p)[0] = (uint8_t)((v));                                                   \
Packit c4476c
    (p)[1] = (uint8_t)((v) >> 8);                                              \
Packit c4476c
    (p)[2] = (uint8_t)((v) >> 16);                                             \
Packit c4476c
    (p)[3] = (uint8_t)((v) >> 24);
Packit c4476c
Packit c4476c
#define U64TO8_LE(p, v)                                                        \
Packit c4476c
    U32TO8_LE((p), (uint32_t)((v)));                                           \
Packit c4476c
    U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
Packit c4476c
Packit c4476c
#define U8TO64_LE(p)                                                           \
Packit c4476c
    (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) |                        \
Packit c4476c
     ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) |                 \
Packit c4476c
     ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) |                 \
Packit c4476c
     ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
Packit c4476c
Packit c4476c
#define SIPROUND                                                               \
Packit c4476c
    do {                                                                       \
Packit c4476c
        v0 += v1;                                                              \
Packit c4476c
        v1 = ROTL(v1, 13);                                                     \
Packit c4476c
        v1 ^= v0;                                                              \
Packit c4476c
        v0 = ROTL(v0, 32);                                                     \
Packit c4476c
        v2 += v3;                                                              \
Packit c4476c
        v3 = ROTL(v3, 16);                                                     \
Packit c4476c
        v3 ^= v2;                                                              \
Packit c4476c
        v0 += v3;                                                              \
Packit c4476c
        v3 = ROTL(v3, 21);                                                     \
Packit c4476c
        v3 ^= v0;                                                              \
Packit c4476c
        v2 += v1;                                                              \
Packit c4476c
        v1 = ROTL(v1, 17);                                                     \
Packit c4476c
        v1 ^= v2;                                                              \
Packit c4476c
        v2 = ROTL(v2, 32);                                                     \
Packit c4476c
    } while (0)
Packit c4476c
Packit c4476c
size_t SipHash_ctx_size(void)
Packit c4476c
{
Packit c4476c
    return sizeof(SIPHASH);
Packit c4476c
}
Packit c4476c
Packit c4476c
size_t SipHash_hash_size(SIPHASH *ctx)
Packit c4476c
{
Packit c4476c
    return ctx->hash_size;
Packit c4476c
}
Packit c4476c
Packit c4476c
static size_t siphash_adjust_hash_size(size_t hash_size)
Packit c4476c
{
Packit c4476c
    if (hash_size == 0)
Packit c4476c
        hash_size = SIPHASH_MAX_DIGEST_SIZE;
Packit c4476c
    return hash_size;
Packit c4476c
}
Packit c4476c
Packit c4476c
int SipHash_set_hash_size(SIPHASH *ctx, size_t hash_size)
Packit c4476c
{
Packit c4476c
    hash_size = siphash_adjust_hash_size(hash_size);
Packit c4476c
    if (hash_size != SIPHASH_MIN_DIGEST_SIZE
Packit c4476c
        && hash_size != SIPHASH_MAX_DIGEST_SIZE)
Packit c4476c
        return 0;
Packit c4476c
Packit c4476c
    /*
Packit c4476c
     * It's possible that the key was set first.  If the hash size changes,
Packit c4476c
     * we need to adjust v1 (see SipHash_Init().
Packit c4476c
     */
Packit c4476c
Packit c4476c
    /* Start by adjusting the stored size, to make things easier */
Packit c4476c
    ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
Packit c4476c
Packit c4476c
    /* Now, adjust ctx->v1 if the old and the new size differ */
Packit c4476c
    if ((size_t)ctx->hash_size != hash_size) {
Packit c4476c
        ctx->v1 ^= 0xee;
Packit c4476c
        ctx->hash_size = hash_size;
Packit c4476c
    }
Packit c4476c
    return 1;
Packit c4476c
}
Packit c4476c
Packit c4476c
/* hash_size = crounds = drounds = 0 means SipHash24 with 16-byte output */
Packit c4476c
int SipHash_Init(SIPHASH *ctx, const unsigned char *k, int crounds, int drounds)
Packit c4476c
{
Packit c4476c
    uint64_t k0 = U8TO64_LE(k);
Packit c4476c
    uint64_t k1 = U8TO64_LE(k + 8);
Packit c4476c
Packit c4476c
    /* If the hash size wasn't set, i.e. is zero */
Packit c4476c
    ctx->hash_size = siphash_adjust_hash_size(ctx->hash_size);
Packit c4476c
Packit c4476c
    if (drounds == 0)
Packit c4476c
        drounds = SIPHASH_D_ROUNDS;
Packit c4476c
    if (crounds == 0)
Packit c4476c
        crounds = SIPHASH_C_ROUNDS;
Packit c4476c
Packit c4476c
    ctx->crounds = crounds;
Packit c4476c
    ctx->drounds = drounds;
Packit c4476c
Packit c4476c
    ctx->len = 0;
Packit c4476c
    ctx->total_inlen = 0;
Packit c4476c
Packit c4476c
    ctx->v0 = 0x736f6d6570736575ULL ^ k0;
Packit c4476c
    ctx->v1 = 0x646f72616e646f6dULL ^ k1;
Packit c4476c
    ctx->v2 = 0x6c7967656e657261ULL ^ k0;
Packit c4476c
    ctx->v3 = 0x7465646279746573ULL ^ k1;
Packit c4476c
Packit c4476c
    if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
Packit c4476c
        ctx->v1 ^= 0xee;
Packit c4476c
Packit c4476c
    return 1;
Packit c4476c
}
Packit c4476c
Packit c4476c
void SipHash_Update(SIPHASH *ctx, const unsigned char *in, size_t inlen)
Packit c4476c
{
Packit c4476c
    uint64_t m;
Packit c4476c
    const uint8_t *end;
Packit c4476c
    int left;
Packit c4476c
    int i;
Packit c4476c
    uint64_t v0 = ctx->v0;
Packit c4476c
    uint64_t v1 = ctx->v1;
Packit c4476c
    uint64_t v2 = ctx->v2;
Packit c4476c
    uint64_t v3 = ctx->v3;
Packit c4476c
Packit c4476c
    ctx->total_inlen += inlen;
Packit c4476c
Packit c4476c
    if (ctx->len) {
Packit c4476c
        /* deal with leavings */
Packit c4476c
        size_t available = SIPHASH_BLOCK_SIZE - ctx->len;
Packit c4476c
Packit c4476c
        /* not enough to fill leavings */
Packit c4476c
        if (inlen < available) {
Packit c4476c
            memcpy(&ctx->leavings[ctx->len], in, inlen);
Packit c4476c
            ctx->len += inlen;
Packit c4476c
            return;
Packit c4476c
        }
Packit c4476c
Packit c4476c
        /* copy data into leavings and reduce input */
Packit c4476c
        memcpy(&ctx->leavings[ctx->len], in, available);
Packit c4476c
        inlen -= available;
Packit c4476c
        in += available;
Packit c4476c
Packit c4476c
        /* process leavings */
Packit c4476c
        m = U8TO64_LE(ctx->leavings);
Packit c4476c
        v3 ^= m;
Packit c4476c
        for (i = 0; i < ctx->crounds; ++i)
Packit c4476c
            SIPROUND;
Packit c4476c
        v0 ^= m;
Packit c4476c
    }
Packit c4476c
    left = inlen & (SIPHASH_BLOCK_SIZE-1); /* gets put into leavings */
Packit c4476c
    end = in + inlen - left;
Packit c4476c
Packit c4476c
    for (; in != end; in += 8) {
Packit c4476c
        m = U8TO64_LE(in);
Packit c4476c
        v3 ^= m;
Packit c4476c
        for (i = 0; i < ctx->crounds; ++i)
Packit c4476c
            SIPROUND;
Packit c4476c
        v0 ^= m;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    /* save leavings and other ctx */
Packit c4476c
    if (left)
Packit c4476c
        memcpy(ctx->leavings, end, left);
Packit c4476c
    ctx->len = left;
Packit c4476c
Packit c4476c
    ctx->v0 = v0;
Packit c4476c
    ctx->v1 = v1;
Packit c4476c
    ctx->v2 = v2;
Packit c4476c
    ctx->v3 = v3;
Packit c4476c
}
Packit c4476c
Packit c4476c
int SipHash_Final(SIPHASH *ctx, unsigned char *out, size_t outlen)
Packit c4476c
{
Packit c4476c
    /* finalize hash */
Packit c4476c
    int i;
Packit c4476c
    uint64_t b = ctx->total_inlen << 56;
Packit c4476c
    uint64_t v0 = ctx->v0;
Packit c4476c
    uint64_t v1 = ctx->v1;
Packit c4476c
    uint64_t v2 = ctx->v2;
Packit c4476c
    uint64_t v3 = ctx->v3;
Packit c4476c
Packit c4476c
    if (outlen != (size_t)ctx->hash_size)
Packit c4476c
        return 0;
Packit c4476c
Packit c4476c
    switch (ctx->len) {
Packit c4476c
    case 7:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[6]) << 48;
Packit c4476c
        /* fall thru */
Packit c4476c
    case 6:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[5]) << 40;
Packit c4476c
        /* fall thru */
Packit c4476c
    case 5:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[4]) << 32;
Packit c4476c
        /* fall thru */
Packit c4476c
    case 4:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[3]) << 24;
Packit c4476c
        /* fall thru */
Packit c4476c
    case 3:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[2]) << 16;
Packit c4476c
        /* fall thru */
Packit c4476c
    case 2:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[1]) <<  8;
Packit c4476c
        /* fall thru */
Packit c4476c
    case 1:
Packit c4476c
        b |= ((uint64_t)ctx->leavings[0]);
Packit c4476c
    case 0:
Packit c4476c
        break;
Packit c4476c
    }
Packit c4476c
Packit c4476c
    v3 ^= b;
Packit c4476c
    for (i = 0; i < ctx->crounds; ++i)
Packit c4476c
        SIPROUND;
Packit c4476c
    v0 ^= b;
Packit c4476c
    if (ctx->hash_size == SIPHASH_MAX_DIGEST_SIZE)
Packit c4476c
        v2 ^= 0xee;
Packit c4476c
    else
Packit c4476c
        v2 ^= 0xff;
Packit c4476c
    for (i = 0; i < ctx->drounds; ++i)
Packit c4476c
        SIPROUND;
Packit c4476c
    b = v0 ^ v1 ^ v2  ^ v3;
Packit c4476c
    U64TO8_LE(out, b);
Packit c4476c
    if (ctx->hash_size == SIPHASH_MIN_DIGEST_SIZE)
Packit c4476c
        return 1;
Packit c4476c
    v1 ^= 0xdd;
Packit c4476c
    for (i = 0; i < ctx->drounds; ++i)
Packit c4476c
        SIPROUND;
Packit c4476c
    b = v0 ^ v1 ^ v2  ^ v3;
Packit c4476c
    U64TO8_LE(out + 8, b);
Packit c4476c
    return 1;
Packit c4476c
}