/*
 * Copyright 2024-2025 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 "crypto/slh_dsa.h"
#include "slh_hash.h"
#include "slh_params.h"

/*
 * Maximum size of the security parameter |n| in FIPS 205 Section 11. Table 2.
 * This indicates the length in bytes of a message that can be signed.
 * It is the size used by WOTS+ public and private key elements as well as
 * signature elements.
 */
#define SLH_MAX_N 32
/*
 * For the given standard w=16 for all parameter sets.
 * A n byte message is converted into 2 * n base 16 Integers followed
 * by 3 Integers for the checksum of these values.
 */
#define SLH_WOTS_LEN(n) (2 * (n) + 3)

/*
 * FIPS 205 SLH-DSA algorithms have many different parameters which includes
 * the following constants that are stored into a |key|:
 *   - A set of constants (Section 11. contains 12 parameter sets)
 *     such as tree heights and security parameters associated with a algorithm
 *     name such as SLH-DSA-SHA2-128s.
 *   - ADRS functions (such as set_layer_address() in Section 4.3 & 11.2)
 *   - Hash Functions (such as H_MSG() & PRF()) See Sections 11.1, 11.2.1 & 11.2.2.
 *   - prefetched EVP_MD objects used for hashing.
 *
 * When performing operations multiple Hash related objects are also needed
 * such as EVP_MD_CTX and EVP_MAC_CTX (these are independent of the |key|)
 *
 * SLH_DSA_HASH_CTX is a container to hold all of these objects. This object is
 * resolved early and is then passed to most SLH_DSA related functions, since
 * there are many nested layers of calls that require these values.
 *
 * NOTE: Any changes to this structure will need updating in
 * ossl_slh_dsa_hash_ctx_dup().
 */
struct slh_dsa_hash_ctx_st {
    const SLH_DSA_KEY *key; /* This key is not owned by this object */
    EVP_MD_CTX *md_ctx; /* Either SHAKE OR SHA-256 */
    EVP_MD_CTX *md_big_ctx; /* Either SHA-512 or points to |md_ctx| for SHA-256*/
    EVP_MAC_CTX *hmac_ctx; /* required by SHA algorithms for PRFmsg() */
    int hmac_digest_used; /* Used for lazy init of hmac_ctx digest */
};

__owur int ossl_slh_wots_pk_gen(SLH_DSA_HASH_CTX *ctx, const uint8_t *sk_seed,
    const uint8_t *pk_seed, uint8_t *adrs,
    uint8_t *pk_out, size_t pk_out_len);
__owur int ossl_slh_wots_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg,
    const uint8_t *sk_seed, const uint8_t *pk_seed,
    uint8_t *adrs, WPACKET *sig_wpkt);
__owur int ossl_slh_wots_pk_from_sig(SLH_DSA_HASH_CTX *ctx,
    PACKET *sig_rpkt, const uint8_t *msg,
    const uint8_t *pk_seed, uint8_t *adrs,
    uint8_t *pk_out, size_t pk_out_len);

__owur int ossl_slh_xmss_node(SLH_DSA_HASH_CTX *ctx, const uint8_t *sk_seed,
    uint32_t node_id, uint32_t height,
    const uint8_t *pk_seed, uint8_t *adrs,
    uint8_t *pk_out, size_t pk_out_len);
__owur int ossl_slh_xmss_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg,
    const uint8_t *sk_seed, uint32_t node_id,
    const uint8_t *pk_seed, uint8_t *adrs,
    WPACKET *sig_wpkt);
__owur int ossl_slh_xmss_pk_from_sig(SLH_DSA_HASH_CTX *ctx, uint32_t node_id,
    PACKET *sig_rpkt, const uint8_t *msg,
    const uint8_t *pk_seed, uint8_t *adrs,
    uint8_t *pk_out, size_t pk_out_len);

__owur int ossl_slh_ht_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg,
    const uint8_t *sk_seed, const uint8_t *pk_seed,
    uint64_t tree_id, uint32_t leaf_id,
    WPACKET *sig_wpkt);
__owur int ossl_slh_ht_verify(SLH_DSA_HASH_CTX *ctx, const uint8_t *msg,
    PACKET *sig_rpkt, const uint8_t *pk_seed,
    uint64_t tree_id, uint32_t leaf_id,
    const uint8_t *pk_root);

__owur int ossl_slh_fors_sign(SLH_DSA_HASH_CTX *ctx, const uint8_t *md,
    const uint8_t *sk_seed, const uint8_t *pk_seed,
    uint8_t *adrs, WPACKET *sig_wpkt);
__owur int ossl_slh_fors_pk_from_sig(SLH_DSA_HASH_CTX *ctx, PACKET *sig_rpkt,
    const uint8_t *md, const uint8_t *pk_seed,
    uint8_t *adrs,
    uint8_t *pk_out, size_t pk_out_len);
