aboutsummaryrefslogblamecommitdiff
path: root/eim/sw/hash_eim.c
blob: 281914c49e320dfe9f0416efe4738ec3369ddbf1 (plain) (tree)

















































































































































































































































































































































































































                                                                                           
/* 
 * hash.c
 * ------
 * This program uses the coretest_hashes subsystem to produce a
 * cryptographic hash of a file or input stream. It is a generalization
 * of the hash_tester.c test program.
 * 
 * Authors: Joachim Strömbergson, Paul Selkirk
 * Copyright (c) 2014-2015, NORDUnet A/S All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT
 * HOLDER OR CONTRIBUTORS 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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <assert.h>

#include "tc_eim.h"

char *usage = 
"Usage: %s [-d] [-v] [-q] [algorithm [file]]\n"
"algorithms: sha-1, sha-256, sha-512/224, sha-512/256, sha-384, sha-512\n";

int debug = 0;
int quiet = 0;
int verbose = 0;

/* memory segments for core families */
#define SEGMENT_OFFSET_GLOBALS  EIM_BASE_ADDR + 0x000000
#define SEGMENT_OFFSET_HASHES   EIM_BASE_ADDR + 0x010000
#define SEGMENT_OFFSET_RNGS     EIM_BASE_ADDR + 0x020000
#define SEGMENT_OFFSET_CIPHERS  EIM_BASE_ADDR + 0x030000

#define CORE_SIZE               (0x100 << 2)

/* addresses and codes common to all cores */
#define ADDR_NAME0              (0x0 << 2)
#define ADDR_NAME1              (0x1 << 2)
#define ADDR_VERSION            (0x2 << 2)

/* At segment 0, we have board-level register and communication channel registers */
#define BOARD_ADDR_BASE         SEGMENT_OFFSET_GLOBALS + (0x00 * CORE_SIZE)
#define BOARD_ADDR_NAME0        BOARD_ADDR_BASE + ADDR_NAME0
#define BOARD_ADDR_NAME1        BOARD_ADDR_BASE + ADDR_NAME1
#define BOARD_ADDR_VERSION      BOARD_ADDR_BASE + ADDR_VERSION
#define BOARD_ADDR_DUMMY        BOARD_ADDR_BASE + (0xFF << 2)

#define COMM_ADDR_BASE          SEGMENT_OFFSET_GLOBALS + (0x01 * CORE_SIZE)
#define COMM_ADDR_NAME0         COMM_ADDR_BASE + ADDR_NAME0
#define COMM_ADDR_NAME1         COMM_ADDR_BASE + ADDR_NAME1
#define COMM_ADDR_VERSION       COMM_ADDR_BASE + ADDR_VERSION

/* addresses and codes common to all hash cores */
#define ADDR_CTRL               (0x8 << 2)
#define CTRL_INIT_CMD           1
#define CTRL_NEXT_CMD           2
#define ADDR_STATUS             (9 << 2)
#define STATUS_READY_BIT        1
#define STATUS_VALID_BIT        2
#define ADDR_BLOCK              (0x10 << 2)
#define ADDR_DIGEST             (0x20 << 2)

/* addresses and codes for the specific hash cores */
#define SHA1_ADDR_BASE          SEGMENT_OFFSET_HASHES + (0*CORE_SIZE)
#define SHA1_ADDR_NAME0         SHA1_ADDR_BASE + ADDR_NAME0
#define SHA1_ADDR_NAME1         SHA1_ADDR_BASE + ADDR_NAME1
#define SHA1_ADDR_VERSION       SHA1_ADDR_BASE + ADDR_VERSION
#define SHA1_ADDR_CTRL          SHA1_ADDR_BASE + ADDR_CTRL
#define SHA1_ADDR_STATUS        SHA1_ADDR_BASE + ADDR_STATUS
#define SHA1_ADDR_BLOCK         SHA1_ADDR_BASE + ADDR_BLOCK
#define SHA1_ADDR_DIGEST        SHA1_ADDR_BASE + ADDR_DIGEST
#define SHA1_BLOCK_LEN          512 / 8
#define SHA1_DIGEST_LEN         160 / 8

#define SHA256_ADDR_BASE        SEGMENT_OFFSET_HASHES + (1*CORE_SIZE)
#define SHA256_ADDR_NAME0       SHA256_ADDR_BASE + ADDR_NAME0
#define SHA256_ADDR_NAME1       SHA256_ADDR_BASE + ADDR_NAME1
#define SHA256_ADDR_VERSION     SHA256_ADDR_BASE + ADDR_VERSION
#define SHA256_ADDR_CTRL        SHA256_ADDR_BASE + ADDR_CTRL
#define SHA256_ADDR_STATUS      SHA256_ADDR_BASE + ADDR_STATUS
#define SHA256_ADDR_BLOCK       SHA256_ADDR_BASE + ADDR_BLOCK
#define SHA256_ADDR_DIGEST      SHA256_ADDR_BASE + ADDR_DIGEST
#define SHA256_BLOCK_LEN        512 / 8
#define SHA256_DIGEST_LEN       256 / 8

#define SHA512_ADDR_BASE        SEGMENT_OFFSET_HASHES + (2*CORE_SIZE)
#define SHA512_ADDR_NAME0       SHA512_ADDR_BASE + ADDR_NAME0
#define SHA512_ADDR_NAME1       SHA512_ADDR_BASE + ADDR_NAME1
#define SHA512_ADDR_VERSION     SHA512_ADDR_BASE + ADDR_VERSION
#define SHA512_ADDR_CTRL        SHA512_ADDR_BASE + ADDR_CTRL
#define SHA512_ADDR_STATUS      SHA512_ADDR_BASE + ADDR_STATUS
#define SHA512_ADDR_BLOCK       SHA512_ADDR_BASE + ADDR_BLOCK
#define SHA512_ADDR_DIGEST      SHA512_ADDR_BASE + (0x40 << 2)
#define SHA512_BLOCK_LEN        1024 / 8
#define SHA512_224_DIGEST_LEN   224 / 8
#define SHA512_256_DIGEST_LEN   256 / 8
#define SHA384_DIGEST_LEN       384 / 8
#define SHA512_DIGEST_LEN       512 / 8
#define MODE_SHA_512_224        0 << 2
#define MODE_SHA_512_256        1 << 2
#define MODE_SHA_384            2 << 2
#define MODE_SHA_512            3 << 2

/* ---------------- algorithm lookup code ---------------- */

struct ctrl {
    char *name;
    off_t block_addr;
    int   block_len;
    off_t digest_addr;
    int   digest_len;
    int   mode;
} ctrl[] = {
    { "sha-1",       SHA1_ADDR_BLOCK, SHA1_BLOCK_LEN,
                     SHA1_ADDR_DIGEST, SHA1_DIGEST_LEN, 0 },
    { "sha-256",     SHA256_ADDR_BLOCK, SHA256_BLOCK_LEN,
                     SHA256_ADDR_DIGEST, SHA256_DIGEST_LEN, 0 },
    { "sha-512/224", SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
                     SHA512_ADDR_DIGEST, SHA512_224_DIGEST_LEN, MODE_SHA_512_224 },
    { "sha-512/256", SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
                     SHA512_ADDR_DIGEST, SHA512_256_DIGEST_LEN, MODE_SHA_512_256 },
    { "sha-384",     SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
                     SHA512_ADDR_DIGEST, SHA384_DIGEST_LEN, MODE_SHA_384 },
    { "sha-512",     SHA512_ADDR_BLOCK, SHA512_BLOCK_LEN,
                     SHA512_ADDR_DIGEST, SHA512_DIGEST_LEN, MODE_SHA_512 },
    { NULL, 0, 0, 0 }
};

/* return the control structure for the given algorithm */
struct ctrl *find_algo(char *algo)
{
    int i;

    for (i = 0; ctrl[i].name != NULL; ++i)
        if (strcmp(ctrl[i].name, algo) == 0)
            return &ctrl[i];

    fprintf(stderr, "algorithm \"%s\" not found\n\n", algo);
    fprintf(stderr, usage, "hash");
    return NULL;
}

/* ---------------- test-case low-level code ---------------- */

int tc_init(off_t offset, int mode)
{
    uint8_t buf[4] = { 0, 0, 0, CTRL_INIT_CMD + mode };

    return tc_write(offset, buf, 4);
}

int tc_next(off_t offset, int mode)
{
    uint8_t buf[4] = { 0, 0, 0, CTRL_NEXT_CMD + mode };

    return tc_write(offset, buf, 4);
}

int tc_wait_ready(off_t offset)
{
    return tc_wait(offset, STATUS_READY_BIT, NULL);
}

int tc_wait_valid(off_t offset)
{
    return tc_wait(offset, STATUS_VALID_BIT, NULL);
}

/* ---------------- hash ---------------- */

int transmit(off_t offset, uint8_t *block, int blen, int mode, int first)
{
    off_t base = offset & ~(0xff);

    if (tc_write(offset, block, blen) != 0)
        return 1;

    if (first) {
        if (tc_init(base + ADDR_CTRL, mode) != 0)
            return 1;
    }
    else {
        if (tc_next(base + ADDR_CTRL, mode) != 0)
            return 1;
    }

    return tc_wait_ready(base + ADDR_STATUS);
}

int pad_transmit(off_t offset, uint8_t *block, uint8_t flen, uint8_t blen,
                 uint8_t mode, long long tlen, int first)
{
    assert(flen < blen);

    block[flen++] = 0x80;
    memset(block + flen, 0, blen - flen);

    if (blen - flen < ((blen == 64) ? 8 : 16)) {
        if (transmit(offset, block, blen, mode, first) != 0)
            return 1;
        first = 0;
        memset(block, 0, blen);
    }

    /* properly the length is 128 bits for sha-512, but we can't
     * actually count above 64 bits
     */
    ((uint32_t *)block)[blen/4 - 2] = htonl((tlen >> 32) & 0xffff);
    ((uint32_t *)block)[blen/4 - 1] = htonl(tlen & 0xffff);

    return transmit(offset, block, blen, mode, first);
}

/* return number of digest bytes read */
int hash(char *algo, char *file, uint8_t *digest)
{
    uint8_t block[SHA512_BLOCK_LEN];
    struct ctrl *ctrl;
    int in_fd = 0;      /* stdin */
    off_t baddr, daddr;
    int blen, dlen, mode;
    int nblk, nread, first;
    int ret = -1;
    struct timeval start, stop, difftime;

    ctrl = find_algo(algo);
    if (ctrl == NULL)
        return -1;
    baddr = ctrl->block_addr;
    blen = ctrl->block_len;
    daddr = ctrl->digest_addr;
    dlen = ctrl->digest_len;
    mode = ctrl->mode;

    if (strcmp(file, "-") != 0) {
        in_fd = open(file, O_RDONLY);
        if (in_fd < 0) {
            perror("open");
            return -1;
        }
    }

    if (verbose) {
        if (gettimeofday(&start, NULL) < 0) {
            perror("gettimeofday");
            goto out;
        }
    }

    for (nblk = 0, first = 1; ; ++nblk, first = 0) {
        nread = read(in_fd, block, blen);
        if (nread < 0) {
            /* read error */
            perror("read");
            goto out;
        }
        else if (nread < blen) {
            /* partial read = last block */
            if (pad_transmit(baddr, block, nread, blen, mode,
                             (nblk * blen + nread) * 8, first) != 0)
                goto out;
            break;
        }
        else {
            /* full block read */
            if (transmit(baddr, block, blen, mode, first) != 0)
                goto out;
        }
    }

    /* Strictly speaking we should query "valid" status before reading digest,
     * but transmit() waits for "ready" status before returning, and the SHA
     * cores always assert valid before ready.
     */
    if (tc_read(daddr, digest, dlen) != 0) {
        perror("i2c read failed");
        goto out;
    }

    if (verbose) {
        if (gettimeofday(&stop, NULL) < 0) {
            perror("gettimeofday");
            goto out;
        }
        timersub(&stop, &start, &difftime);
        printf("%d blocks written in %d.%03d sec (%.3f blocks/sec)\n",
               nblk, (int)difftime.tv_sec, (int)difftime.tv_usec/1000,
               (float)nblk / ((float)difftime.tv_sec + ((float)difftime.tv_usec)/1000000));
    }

    ret = dlen;
out:
    if (in_fd != 0)
        close(in_fd);
    return ret;
}

/* ---------------- main ---------------- */

int main(int argc, char *argv[])
{
    int i, opt;
    char *algo = "sha-1";
    char *file = "-";
    uint8_t digest[512/8];
    int dlen;

    while ((opt = getopt(argc, argv, "h?dvq")) != -1) {
        switch (opt) {
        case 'h':
        case '?':
            printf(usage, argv[0]);
            return EXIT_SUCCESS;
        case 'd':
            debug = 1;
            break;
        case 'v':
            verbose = 1;
            break;
        case 'q':
            quiet = 1;
            break;
        default:
            fprintf(stderr, usage, argv[0]);
            return EXIT_FAILURE;
        }
    }

    if (optind < argc) {
        algo = argv[optind];
        ++optind;
    }
    else {
        if (!quiet)
            printf("defaulting to algorithm \"%s\"\n", algo);
    }

    if (optind < argc) {
        file = argv[optind];
        ++optind;
    }
    else {
        if (!quiet)
            printf("reading from stdin\n");
    }

    if (eim_setup() != 0) {
        fprintf(stderr, "EIM setup failed\n");
        return EXIT_FAILURE;
    }

    dlen = hash(algo, file, digest);
    if (dlen < 0)
        return EXIT_FAILURE;

    for (i = 0; i < dlen; ++i) {
        printf("%02x", digest[i]);
        if (i % 16 == 15)
            printf("\n");
        else if (i % 4 == 3)
            printf(" ");
    }
    if (dlen % 16 != 0)
        printf("\n");

    return EXIT_SUCCESS;
}