| /* Copyright (c) 2023, Google LLC |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ |
| |
| #include <openssl/base.h> |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <openssl/sha.h> |
| |
| #include "./spx_params.h" |
| #include "./spx_util.h" |
| #include "./spx_thash.h" |
| |
| static void spx_thash(uint8_t *output, const uint8_t *input, |
| size_t input_blocks, const uint8_t pk_seed[SPX_N], |
| uint8_t addr[32]) { |
| uint8_t hash[32]; |
| SHA256_CTX sha256; |
| SHA256_Init(&sha256); |
| |
| // Process pubseed with padding to full block. |
| // TODO: This could be precomputed instead as it will be the same across all |
| // hash calls. |
| uint8_t padded_pk_seed[64] = {0}; |
| memcpy(padded_pk_seed, pk_seed, SPX_N); |
| |
| SHA256_Update(&sha256, padded_pk_seed, sizeof(padded_pk_seed)); |
| SHA256_Update(&sha256, addr, SPX_SHA256_ADDR_BYTES); |
| SHA256_Update(&sha256, input, input_blocks * SPX_N); |
| |
| SHA256_Final(hash, &sha256); |
| memcpy(output, hash, SPX_N); |
| } |
| |
| void spx_thash_f(uint8_t *output, const uint8_t input[SPX_N], |
| const uint8_t pk_seed[SPX_N], uint8_t addr[32]) { |
| spx_thash(output, input, 1, pk_seed, addr); |
| } |
| |
| void spx_thash_h(uint8_t *output, const uint8_t input[2 * SPX_N], |
| const uint8_t pk_seed[SPX_N], uint8_t addr[32]) { |
| spx_thash(output, input, 2, pk_seed, addr); |
| } |
| |
| void spx_thash_hmsg(uint8_t *output, const uint8_t r[SPX_N], |
| const uint8_t pk_seed[SPX_N], const uint8_t pk_root[SPX_N], |
| const uint8_t *msg, size_t msg_len) { |
| // MGF1-SHA-256(R || PK.seed || SHA-256(R || PK.seed || PK.root || M), m) |
| // input_buffer stores R || PK_SEED || SHA256(..) || 4-byte index |
| uint8_t input_buffer[2 * SPX_N + 32 + 4] = {0}; |
| memcpy(input_buffer, r, SPX_N); |
| memcpy(input_buffer + SPX_N, pk_seed, SPX_N); |
| |
| // Inner hash |
| SHA256_CTX ctx; |
| SHA256_Init(&ctx); |
| SHA256_Update(&ctx, r, SPX_N); |
| SHA256_Update(&ctx, pk_seed, SPX_N); |
| SHA256_Update(&ctx, pk_root, SPX_N); |
| SHA256_Update(&ctx, msg, msg_len); |
| // Write directly into the input buffer |
| SHA256_Final(input_buffer + 2 * SPX_N, &ctx); |
| |
| // MGF1-SHA-256 |
| uint8_t output_buffer[3 * 32]; |
| // Need to call SHA256 3 times for message digest. |
| static_assert(SPX_DIGEST_SIZE <= sizeof(output_buffer), |
| "not enough room for hashes"); |
| SHA256(input_buffer, sizeof(input_buffer), output_buffer); |
| input_buffer[2 * SPX_N + 32 + 3] = 1; |
| SHA256(input_buffer, sizeof(input_buffer), output_buffer + 32); |
| input_buffer[2 * SPX_N + 32 + 3] = 2; |
| SHA256(input_buffer, sizeof(input_buffer), output_buffer + 64); |
| |
| memcpy(output, output_buffer, SPX_DIGEST_SIZE); |
| } |
| |
| void spx_thash_prf(uint8_t *output, const uint8_t pk_seed[SPX_N], |
| const uint8_t sk_seed[SPX_N], uint8_t addr[32]) { |
| spx_thash(output, sk_seed, 1, pk_seed, addr); |
| } |
| |
| void spx_thash_prfmsg(uint8_t *output, const uint8_t sk_prf[SPX_N], |
| const uint8_t opt_rand[SPX_N], const uint8_t *msg, |
| size_t msg_len) { |
| // Compute HMAC-SHA256(sk_prf, opt_rand || msg). We inline HMAC to avoid an |
| // allocation. |
| uint8_t hmac_key[SHA256_CBLOCK] = {0}; |
| static_assert(SPX_N <= SHA256_CBLOCK, "HMAC key is larger than block size"); |
| memcpy(hmac_key, sk_prf, SPX_N); |
| for (size_t i = 0; i < sizeof(hmac_key); i++) { |
| hmac_key[i] ^= 0x36; |
| } |
| |
| uint8_t hash[SHA256_DIGEST_LENGTH]; |
| SHA256_CTX ctx; |
| SHA256_Init(&ctx); |
| SHA256_Update(&ctx, hmac_key, sizeof(hmac_key)); |
| SHA256_Update(&ctx, opt_rand, SPX_N); |
| SHA256_Update(&ctx, msg, msg_len); |
| SHA256_Final(hash, &ctx); |
| |
| for (size_t i = 0; i < sizeof(hmac_key); i++) { |
| hmac_key[i] ^= 0x36 ^ 0x5c; |
| } |
| SHA256_Init(&ctx); |
| SHA256_Update(&ctx, hmac_key, sizeof(hmac_key)); |
| SHA256_Update(&ctx, hash, sizeof(hash)); |
| SHA256_Final(hash, &ctx); |
| |
| // Truncate to SPX_N bytes |
| memcpy(output, hash, SPX_N); |
| } |
| |
| void spx_thash_tl(uint8_t *output, const uint8_t input[SPX_WOTS_BYTES], |
| const uint8_t pk_seed[SPX_N], uint8_t addr[32]) { |
| spx_thash(output, input, SPX_WOTS_LEN, pk_seed, addr); |
| } |
| |
| void spx_thash_tk(uint8_t *output, const uint8_t input[SPX_FORS_TREES * SPX_N], |
| const uint8_t pk_seed[SPX_N], uint8_t addr[32]) { |
| spx_thash(output, input, SPX_FORS_TREES, pk_seed, addr); |
| } |