| // 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); |
| } |