| // Copyright 2024 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <openssl/base.h> | |
| #include <assert.h> | |
| #include <string.h> | |
| #include "../../internal.h" | |
| #include "./address.h" | |
| #include "./fors.h" | |
| #include "./params.h" | |
| #include "./thash.h" | |
| using namespace bssl; | |
| // Compute the base 2^a representation of `message` (algorithm 4, page 16). | |
| static void fors_base_b(const slh_dsa_config *config, uint32_t *indices, | |
| const uint8_t *message) { | |
| const size_t bits_per_index = config->fors_height; | |
| BSSL_CHECK(bits_per_index <= SLHDSA_MAX_FORS_HEIGHT); | |
| const size_t total_bits = bits_per_index * config->fors_trees; | |
| const size_t msg_bytes = slhdsa_fors_msg_bytes(config); | |
| BSSL_CHECK(total_bits <= msg_bytes * 8); | |
| size_t bit_offset = 0; | |
| for (size_t i = 0; i < config->fors_trees; ++i) { | |
| uint32_t value = 0; | |
| for (size_t j = 0; j < bits_per_index; ++j) { | |
| const size_t bit_index = bit_offset + j; | |
| const size_t byte_index = bit_index >> 3; | |
| const size_t bit_in_byte = 7 - (bit_index & 7); | |
| const uint8_t bit = (message[byte_index] >> bit_in_byte) & 0x01; | |
| value = (value << 1) | bit; | |
| } | |
| indices[i] = value; | |
| bit_offset += bits_per_index; | |
| } | |
| } | |
| // Implements Algorithm 14: fors_skGen function (page 29) | |
| void bssl::slhdsa_fors_sk_gen(const slh_dsa_config *config, uint8_t *fors_sk, | |
| uint32_t idx, const uint8_t *sk_seed, | |
| const uint8_t *pk_seed, uint8_t addr[32]) { | |
| uint8_t sk_addr[32]; | |
| OPENSSL_memcpy(sk_addr, addr, sizeof(sk_addr)); | |
| slhdsa_set_type(config, sk_addr, SLHDSA_ADDR_TYPE_FORSPRF); | |
| slhdsa_copy_keypair_addr(config, sk_addr, addr); | |
| slhdsa_set_tree_index(config, sk_addr, idx); | |
| slhdsa_thash_prf(config, fors_sk, pk_seed, sk_seed, sk_addr); | |
| } | |
| // Implements Algorithm 15: fors_node function (page 30) | |
| void bssl::slhdsa_fors_treehash(const slh_dsa_config *config, | |
| uint8_t *root_node, const uint8_t *sk_seed, | |
| uint32_t i /*target node index*/, | |
| uint32_t z /*target node height*/, | |
| const uint8_t *pk_seed, uint8_t addr[32]) { | |
| const uint32_t nodes_in_tree = 1u << (config->fors_height - z); | |
| BSSL_CHECK(z <= config->fors_height); | |
| BSSL_CHECK(i < (uint32_t)(config->fors_trees * nodes_in_tree)); | |
| if (z == 0) { | |
| uint8_t sk[SLHDSA_MAX_N]; | |
| slhdsa_set_tree_height(config, addr, 0); | |
| slhdsa_set_tree_index(config, addr, i); | |
| slhdsa_fors_sk_gen(config, sk, i, sk_seed, pk_seed, addr); | |
| slhdsa_thash_f(config, root_node, sk, pk_seed, addr); | |
| } else { | |
| // Stores left node and right node. | |
| uint8_t nodes[2 * SLHDSA_MAX_N]; | |
| slhdsa_fors_treehash(config, nodes, sk_seed, 2 * i, z - 1, pk_seed, addr); | |
| slhdsa_fors_treehash(config, nodes + config->n, sk_seed, 2 * i + 1, z - 1, | |
| pk_seed, addr); | |
| slhdsa_set_tree_height(config, addr, z); | |
| slhdsa_set_tree_index(config, addr, i); | |
| slhdsa_thash_h(config, root_node, nodes, pk_seed, addr); | |
| } | |
| } | |
| // Implements Algorithm 16: fors_sign function (page 31) | |
| void bssl::slhdsa_fors_sign(const slh_dsa_config *config, uint8_t *fors_sig, | |
| const uint8_t *message, const uint8_t *sk_seed, | |
| const uint8_t *pk_seed, uint8_t addr[32]) { | |
| uint32_t indices[SLHDSA_MAX_FORS_TREES]; | |
| BSSL_CHECK(config->fors_trees <= SLHDSA_MAX_FORS_TREES); | |
| BSSL_CHECK(config->fors_height <= SLHDSA_MAX_FORS_HEIGHT); | |
| // Derive FORS indices compatible with the NIST changes. | |
| fors_base_b(config, indices, message); | |
| const size_t n = config->n; | |
| const size_t node_stride = n * (config->fors_height + 1); | |
| for (uint32_t i = 0; i < config->fors_trees; ++i) { | |
| slhdsa_set_tree_height(config, addr, 0); | |
| // Write the FORS secret key element to the correct position. | |
| uint8_t *tree_sig = fors_sig + i * node_stride; | |
| slhdsa_fors_sk_gen(config, tree_sig, | |
| i * (1u << config->fors_height) + indices[i], sk_seed, | |
| pk_seed, addr); | |
| for (uint32_t j = 0; j < config->fors_height; ++j) { | |
| const size_t sibling = (indices[i] >> j) ^ 1u; | |
| // Write the FORS auth path element to the correct position. | |
| slhdsa_fors_treehash(config, tree_sig + (j + 1) * n, sk_seed, | |
| i * (1u << (config->fors_height - j)) + sibling, j, | |
| pk_seed, addr); | |
| } | |
| } | |
| } | |
| // Implements Algorithm 17: fors_pkFromSig function (page 32) | |
| void bssl::slhdsa_fors_pk_from_sig(const slh_dsa_config *config, | |
| uint8_t *fors_pk, const uint8_t *fors_sig, | |
| const uint8_t *message, | |
| const uint8_t *pk_seed, uint8_t addr[32]) { | |
| uint32_t indices[SLHDSA_MAX_FORS_TREES]; | |
| uint8_t tmp[2 * SLHDSA_MAX_N]; | |
| uint8_t roots[SLHDSA_MAX_FORS_TREES * SLHDSA_MAX_N]; | |
| BSSL_CHECK(config->fors_trees <= SLHDSA_MAX_FORS_TREES); | |
| BSSL_CHECK(config->fors_height <= SLHDSA_MAX_FORS_HEIGHT); | |
| // Derive FORS indices compatible with the NIST changes. | |
| fors_base_b(config, indices, message); | |
| const size_t n = config->n; | |
| const size_t node_stride = n * (config->fors_height + 1); | |
| for (size_t i = 0; i < config->fors_trees; ++i) { | |
| // Pointer to current sk and authentication path | |
| const uint8_t *tree_sig = fors_sig + i * node_stride; | |
| const uint8_t *auth = tree_sig + n; | |
| uint8_t nodes[2 * SLHDSA_MAX_N]; | |
| slhdsa_set_tree_height(config, addr, 0); | |
| uint32_t index = i * (1ULL << config->fors_height) + indices[i]; | |
| slhdsa_set_tree_index(config, addr, index); | |
| slhdsa_thash_f(config, nodes, tree_sig, pk_seed, addr); | |
| for (uint32_t j = 0; j < config->fors_height; ++j) { | |
| slhdsa_set_tree_height(config, addr, j + 1); | |
| index >>= 1; | |
| slhdsa_set_tree_index(config, addr, index); | |
| // Even node | |
| if (((indices[i] >> j) & 1) == 0) { | |
| OPENSSL_memcpy(tmp, nodes, n); | |
| OPENSSL_memcpy(tmp + n, auth + j * n, n); | |
| slhdsa_thash_h(config, nodes + n, tmp, pk_seed, addr); | |
| } else { | |
| OPENSSL_memcpy(tmp, auth + j * n, n); | |
| OPENSSL_memcpy(tmp + n, nodes, n); | |
| slhdsa_thash_h(config, nodes + n, tmp, pk_seed, addr); | |
| } | |
| OPENSSL_memcpy(nodes, nodes + n, n); | |
| } | |
| OPENSSL_memcpy(roots + i * n, nodes, n); | |
| } | |
| uint8_t forspk_addr[32]; | |
| OPENSSL_memcpy(forspk_addr, addr, sizeof(forspk_addr)); | |
| slhdsa_set_type(config, forspk_addr, SLHDSA_ADDR_TYPE_FORSPK); | |
| slhdsa_copy_keypair_addr(config, forspk_addr, addr); | |
| slhdsa_thash_tk(config, fors_pk, roots, pk_seed, forspk_addr); | |
| } |