1e1051a39Sopenharmony_ci/*-
2e1051a39Sopenharmony_ci * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
3e1051a39Sopenharmony_ci *
4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License").  You may not use
5e1051a39Sopenharmony_ci * this file except in compliance with the License.  You can obtain a copy
6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at
7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html
8e1051a39Sopenharmony_ci */
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_ci#include <stdio.h>
11e1051a39Sopenharmony_ci#include <string.h>
12e1051a39Sopenharmony_ci#include <openssl/err.h>
13e1051a39Sopenharmony_ci#include <openssl/evp.h>
14e1051a39Sopenharmony_ci#include <openssl/core_names.h>
15e1051a39Sopenharmony_ci
16e1051a39Sopenharmony_ci/*
17e1051a39Sopenharmony_ci * Example of using an extendable-output hash function (XOF). A XOF is a hash
18e1051a39Sopenharmony_ci * function with configurable output length and which can generate an
19e1051a39Sopenharmony_ci * arbitrarily large output.
20e1051a39Sopenharmony_ci *
21e1051a39Sopenharmony_ci * This example uses SHAKE256, an extendable output variant of SHA3 (Keccak).
22e1051a39Sopenharmony_ci *
23e1051a39Sopenharmony_ci * To generate different output lengths, you can pass a single integer argument
24e1051a39Sopenharmony_ci * on the command line, which is the output size in bytes. By default, a 20-byte
25e1051a39Sopenharmony_ci * output is generated and (for this length only) a known answer test is
26e1051a39Sopenharmony_ci * performed.
27e1051a39Sopenharmony_ci */
28e1051a39Sopenharmony_ci
29e1051a39Sopenharmony_ci/* Our input to the XOF hash function. */
30e1051a39Sopenharmony_ciconst char message[] = "This is a test message.";
31e1051a39Sopenharmony_ci
32e1051a39Sopenharmony_ci/* Expected output when an output length of 20 bytes is used. */
33e1051a39Sopenharmony_cistatic const char known_answer[] = {
34e1051a39Sopenharmony_ci  0x52, 0x97, 0x93, 0x78, 0x27, 0x58, 0x7d, 0x62,
35e1051a39Sopenharmony_ci  0x8b, 0x00, 0x25, 0xb5, 0xec, 0x39, 0x5e, 0x2d,
36e1051a39Sopenharmony_ci  0x7f, 0x3e, 0xd4, 0x19
37e1051a39Sopenharmony_ci};
38e1051a39Sopenharmony_ci
39e1051a39Sopenharmony_ci/*
40e1051a39Sopenharmony_ci * A property query used for selecting the SHAKE256 implementation.
41e1051a39Sopenharmony_ci */
42e1051a39Sopenharmony_cistatic const char *propq = NULL;
43e1051a39Sopenharmony_ci
44e1051a39Sopenharmony_ciint main(int argc, char **argv)
45e1051a39Sopenharmony_ci{
46e1051a39Sopenharmony_ci    int rv = 1;
47e1051a39Sopenharmony_ci    OSSL_LIB_CTX *libctx = NULL;
48e1051a39Sopenharmony_ci    EVP_MD *md = NULL;
49e1051a39Sopenharmony_ci    EVP_MD_CTX *ctx = NULL;
50e1051a39Sopenharmony_ci    unsigned int digest_len = 20;
51e1051a39Sopenharmony_ci    int digest_len_i;
52e1051a39Sopenharmony_ci    unsigned char *digest = NULL;
53e1051a39Sopenharmony_ci
54e1051a39Sopenharmony_ci    /* Allow digest length to be changed for demonstration purposes. */
55e1051a39Sopenharmony_ci    if (argc > 1) {
56e1051a39Sopenharmony_ci        digest_len_i = atoi(argv[1]);
57e1051a39Sopenharmony_ci        if (digest_len_i <= 0) {
58e1051a39Sopenharmony_ci            fprintf(stderr, "Specify a non-negative digest length\n");
59e1051a39Sopenharmony_ci            goto end;
60e1051a39Sopenharmony_ci        }
61e1051a39Sopenharmony_ci
62e1051a39Sopenharmony_ci        digest_len = (unsigned int)digest_len_i;
63e1051a39Sopenharmony_ci    }
64e1051a39Sopenharmony_ci
65e1051a39Sopenharmony_ci    /*
66e1051a39Sopenharmony_ci     * Retrieve desired algorithm. This must be a hash algorithm which supports
67e1051a39Sopenharmony_ci     * XOF.
68e1051a39Sopenharmony_ci     */
69e1051a39Sopenharmony_ci    md = EVP_MD_fetch(libctx, "SHAKE256", propq);
70e1051a39Sopenharmony_ci    if (md == NULL) {
71e1051a39Sopenharmony_ci        fprintf(stderr, "Failed to retrieve SHAKE256 algorithm\n");
72e1051a39Sopenharmony_ci        goto end;
73e1051a39Sopenharmony_ci    }
74e1051a39Sopenharmony_ci
75e1051a39Sopenharmony_ci    /* Create context. */
76e1051a39Sopenharmony_ci    ctx = EVP_MD_CTX_new();
77e1051a39Sopenharmony_ci    if (ctx == NULL) {
78e1051a39Sopenharmony_ci        fprintf(stderr, "Failed to create digest context\n");
79e1051a39Sopenharmony_ci        goto end;
80e1051a39Sopenharmony_ci    }
81e1051a39Sopenharmony_ci
82e1051a39Sopenharmony_ci    /* Initialize digest context. */
83e1051a39Sopenharmony_ci    if (EVP_DigestInit(ctx, md) == 0) {
84e1051a39Sopenharmony_ci        fprintf(stderr, "Failed to initialize digest\n");
85e1051a39Sopenharmony_ci        goto end;
86e1051a39Sopenharmony_ci    }
87e1051a39Sopenharmony_ci
88e1051a39Sopenharmony_ci    /*
89e1051a39Sopenharmony_ci     * Feed our message into the digest function.
90e1051a39Sopenharmony_ci     * This may be called multiple times.
91e1051a39Sopenharmony_ci     */
92e1051a39Sopenharmony_ci    if (EVP_DigestUpdate(ctx, message, sizeof(message)) == 0) {
93e1051a39Sopenharmony_ci        fprintf(stderr, "Failed to hash input message\n");
94e1051a39Sopenharmony_ci        goto end;
95e1051a39Sopenharmony_ci    }
96e1051a39Sopenharmony_ci
97e1051a39Sopenharmony_ci    /* Allocate enough memory for our digest length. */
98e1051a39Sopenharmony_ci    digest = OPENSSL_malloc(digest_len);
99e1051a39Sopenharmony_ci    if (digest == NULL) {
100e1051a39Sopenharmony_ci        fprintf(stderr, "Failed to allocate memory for digest\n");
101e1051a39Sopenharmony_ci        goto end;
102e1051a39Sopenharmony_ci    }
103e1051a39Sopenharmony_ci
104e1051a39Sopenharmony_ci    /* Get computed digest. The digest will be of whatever length we specify. */
105e1051a39Sopenharmony_ci    if (EVP_DigestFinalXOF(ctx, digest, digest_len) == 0) {
106e1051a39Sopenharmony_ci        fprintf(stderr, "Failed to finalize hash\n");
107e1051a39Sopenharmony_ci        goto end;
108e1051a39Sopenharmony_ci    }
109e1051a39Sopenharmony_ci
110e1051a39Sopenharmony_ci    printf("Output digest:\n");
111e1051a39Sopenharmony_ci    BIO_dump_indent_fp(stdout, digest, digest_len, 2);
112e1051a39Sopenharmony_ci
113e1051a39Sopenharmony_ci    /* If digest length is 20 bytes, check it matches our known answer. */
114e1051a39Sopenharmony_ci    if (digest_len == 20) {
115e1051a39Sopenharmony_ci        /*
116e1051a39Sopenharmony_ci         * Always use a constant-time function such as CRYPTO_memcmp
117e1051a39Sopenharmony_ci         * when comparing cryptographic values. Do not use memcmp(3).
118e1051a39Sopenharmony_ci         */
119e1051a39Sopenharmony_ci        if (CRYPTO_memcmp(digest, known_answer, sizeof(known_answer)) != 0) {
120e1051a39Sopenharmony_ci            fprintf(stderr, "Output does not match expected result\n");
121e1051a39Sopenharmony_ci            goto end;
122e1051a39Sopenharmony_ci        }
123e1051a39Sopenharmony_ci    }
124e1051a39Sopenharmony_ci
125e1051a39Sopenharmony_ci    rv = 0;
126e1051a39Sopenharmony_ciend:
127e1051a39Sopenharmony_ci    OPENSSL_free(digest);
128e1051a39Sopenharmony_ci    EVP_MD_CTX_free(ctx);
129e1051a39Sopenharmony_ci    EVP_MD_free(md);
130e1051a39Sopenharmony_ci    OSSL_LIB_CTX_free(libctx);
131e1051a39Sopenharmony_ci    return rv;
132e1051a39Sopenharmony_ci}
133