EVP_MD_xof.c 3.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
/*-
 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#include <stdio.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/core_names.h>

/*
 * Example of using an extendable-output hash function (XOF). A XOF is a hash
 * function with configurable output length and which can generate an
 * arbitrarily large output.
 *
 * This example uses SHAKE256, an extendable output variant of SHA3 (Keccak).
 *
 * To generate different output lengths, you can pass a single integer argument
 * on the command line, which is the output size in bytes. By default, a 20-byte
 * output is generated and (for this length only) a known answer test is
 * performed.
 */

/* Our input to the XOF hash function. */
const char message[] = "This is a test message.";

/* Expected output when an output length of 20 bytes is used. */
static const char known_answer[] = {
  0x52, 0x97, 0x93, 0x78, 0x27, 0x58, 0x7d, 0x62,
  0x8b, 0x00, 0x25, 0xb5, 0xec, 0x39, 0x5e, 0x2d,
  0x7f, 0x3e, 0xd4, 0x19
};

/*
 * A property query used for selecting the SHAKE256 implementation.
 */
static const char *propq = NULL;

int main(int argc, char **argv)
{
    int rv = 1;
    OSSL_LIB_CTX *libctx = NULL;
    EVP_MD *md = NULL;
    EVP_MD_CTX *ctx = NULL;
    unsigned int digest_len = 20;
    int digest_len_i;
    unsigned char *digest = NULL;

    /* Allow digest length to be changed for demonstration purposes. */
    if (argc > 1) {
        digest_len_i = atoi(argv[1]);
        if (digest_len_i <= 0) {
            fprintf(stderr, "Specify a non-negative digest length\n");
            goto end;
        }

        digest_len = (unsigned int)digest_len_i;
    }

    /*
     * Retrieve desired algorithm. This must be a hash algorithm which supports
     * XOF.
     */
    md = EVP_MD_fetch(libctx, "SHAKE256", propq);
    if (md == NULL) {
        fprintf(stderr, "Failed to retrieve SHAKE256 algorithm\n");
        goto end;
    }

    /* Create context. */
    ctx = EVP_MD_CTX_new();
    if (ctx == NULL) {
        fprintf(stderr, "Failed to create digest context\n");
        goto end;
    }

    /* Initialize digest context. */
    if (EVP_DigestInit(ctx, md) == 0) {
        fprintf(stderr, "Failed to initialize digest\n");
        goto end;
    }

    /*
     * Feed our message into the digest function.
     * This may be called multiple times.
     */
    if (EVP_DigestUpdate(ctx, message, sizeof(message)) == 0) {
        fprintf(stderr, "Failed to hash input message\n");
        goto end;
    }

    /* Allocate enough memory for our digest length. */
    digest = OPENSSL_malloc(digest_len);
    if (digest == NULL) {
        fprintf(stderr, "Failed to allocate memory for digest\n");
        goto end;
    }

    /* Get computed digest. The digest will be of whatever length we specify. */
    if (EVP_DigestFinalXOF(ctx, digest, digest_len) == 0) {
        fprintf(stderr, "Failed to finalize hash\n");
        goto end;
    }

    printf("Output digest:\n");
    BIO_dump_indent_fp(stdout, digest, digest_len, 2);

    /* If digest length is 20 bytes, check it matches our known answer. */
    if (digest_len == 20) {
        /*
         * Always use a constant-time function such as CRYPTO_memcmp
         * when comparing cryptographic values. Do not use memcmp(3).
         */
        if (CRYPTO_memcmp(digest, known_answer, sizeof(known_answer)) != 0) {
            fprintf(stderr, "Output does not match expected result\n");
            goto end;
        }
    }

    rv = 0;
end:
    OPENSSL_free(digest);
    EVP_MD_CTX_free(ctx);
    EVP_MD_free(md);
    OSSL_LIB_CTX_free(libctx);
    return rv;
}