/*
 * Copyright 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
 */

/*
 * Internal LMS/LM_OTS functions for other submodules,
 * not for application use
 */

#ifndef OSSL_CRYPTO_LMS_H
#define OSSL_CRYPTO_LMS_H
#pragma once
#ifndef OPENSSL_NO_LMS
#include "types.h"
#include <openssl/params.h>

/*
 * Numeric identifiers associated with Leighton-Micali Signatures (LMS)
 * parameter sets are defined in
 * https://www.iana.org/assignments/leighton-micali-signatures/leighton-micali-signatures.xhtml
 * which is referenced from SP800-208.
 */
#define OSSL_LMS_TYPE_SHA256_N32_H5 0x00000005
#define OSSL_LMS_TYPE_SHA256_N32_H10 0x00000006
#define OSSL_LMS_TYPE_SHA256_N32_H15 0x00000007
#define OSSL_LMS_TYPE_SHA256_N32_H20 0x00000008
#define OSSL_LMS_TYPE_SHA256_N32_H25 0x00000009
#define OSSL_LMS_TYPE_SHA256_N24_H5 0x0000000A
#define OSSL_LMS_TYPE_SHA256_N24_H10 0x0000000B
#define OSSL_LMS_TYPE_SHA256_N24_H15 0x0000000C
#define OSSL_LMS_TYPE_SHA256_N24_H20 0x0000000D
#define OSSL_LMS_TYPE_SHA256_N24_H25 0x0000000E
#define OSSL_LMS_TYPE_SHAKE_N32_H5 0x0000000F
#define OSSL_LMS_TYPE_SHAKE_N32_H10 0x00000010
#define OSSL_LMS_TYPE_SHAKE_N32_H15 0x00000011
#define OSSL_LMS_TYPE_SHAKE_N32_H20 0x00000012
#define OSSL_LMS_TYPE_SHAKE_N32_H25 0x00000013
#define OSSL_LMS_TYPE_SHAKE_N24_H5 0x00000014
#define OSSL_LMS_TYPE_SHAKE_N24_H10 0x00000015
#define OSSL_LMS_TYPE_SHAKE_N24_H15 0x00000016
#define OSSL_LMS_TYPE_SHAKE_N24_H20 0x00000017
#define OSSL_LMS_TYPE_SHAKE_N24_H25 0x00000018

#define OSSL_LM_OTS_TYPE_SHA256_N32_W1 0x00000001
#define OSSL_LM_OTS_TYPE_SHA256_N32_W2 0x00000002
#define OSSL_LM_OTS_TYPE_SHA256_N32_W4 0x00000003
#define OSSL_LM_OTS_TYPE_SHA256_N32_W8 0x00000004
#define OSSL_LM_OTS_TYPE_SHA256_N24_W1 0x00000005
#define OSSL_LM_OTS_TYPE_SHA256_N24_W2 0x00000006
#define OSSL_LM_OTS_TYPE_SHA256_N24_W4 0x00000007
#define OSSL_LM_OTS_TYPE_SHA256_N24_W8 0x00000008
#define OSSL_LM_OTS_TYPE_SHAKE_N32_W1 0x00000009
#define OSSL_LM_OTS_TYPE_SHAKE_N32_W2 0x0000000A
#define OSSL_LM_OTS_TYPE_SHAKE_N32_W4 0x0000000B
#define OSSL_LM_OTS_TYPE_SHAKE_N32_W8 0x0000000C
#define OSSL_LM_OTS_TYPE_SHAKE_N24_W1 0x0000000D
#define OSSL_LM_OTS_TYPE_SHAKE_N24_W2 0x0000000E
#define OSSL_LM_OTS_TYPE_SHAKE_N24_W4 0x0000000F
#define OSSL_LM_OTS_TYPE_SHAKE_N24_W8 0x00000010

/* Constants used for verifying */
#define LMS_SIZE_q 4

/* XDR sizes when encoding and decoding */
#define LMS_SIZE_I 16
#define LMS_SIZE_LMS_TYPE 4
#define LMS_SIZE_OTS_TYPE 4
#define LMS_MAX_DIGEST_SIZE 32
#define LMS_MAX_PUBKEY \
    (LMS_SIZE_LMS_TYPE + LMS_SIZE_OTS_TYPE + LMS_SIZE_I + LMS_MAX_DIGEST_SIZE)

/*
 * Refer to RFC 8554 Section 4.1.
 * See also lm_ots_params[]
 */
typedef struct lm_ots_params_st {
    /*
     * The OTS type associates an id with a set of OTS parameters
     * e.g. OSSL_LM_OTS_TYPE_SHAKE_N32_W1
     */
    uint32_t lm_ots_type;
    uint32_t n; /* Hash output size in bytes (32 or 24) */
    /*
     * The width of the Winternitz coefficients in bits. One of (1, 2, 4, 8)
     * Higher values of w are slower (~2^w computations) but have smaller
     * signatures.
     */
    uint32_t w;
    /*
     * The number of n-byte elements used for an LMOTS signature.
     * One of (265, 133, 67, 34) for n = 32, for w=1,2,4,8
     * One of (200, 101, 51, 26) for n = 24, for w=1,2,4,8
     */
    uint32_t p;
    /*
     * The size of the shift needed to move the checksum so
     * that it appears in the checksum digits.
     * See RFC 8554 Appendix B.  LM-OTS Parameter Options
     */
    uint32_t ls;
    const char *digestname; /* Hash Name */
} LM_OTS_PARAMS;

/* See lms_params[] */
typedef struct lms_params_st {
    /*
     * The lms type associates an id with a set of parameters to define the
     * Digest and Height of a LMS tree.
     * e.g, OSSL_LMS_TYPE_SHA256_N24_H25
     */
    uint32_t lms_type;
    const char *digestname; /* One of SHA256, SHA256-192, or SHAKE256 */
    uint32_t n; /* The Digest size (either 24 or 32), Useful for setting up SHAKE */
    uint32_t h; /* The height of a LMS tree which is one of 5, 10, 15, 20, 25) */
} LMS_PARAMS;

typedef struct lms_pub_key_st {
    /*
     * A buffer containing an encoded public key of the form
     * u32str(lmstype) || u32str(otstype) || I[16] || K[n]
     */
    unsigned char *encoded; /* encoded public key data */
    size_t encodedlen;
    /*
     * K is the LMS tree's root public key (Called T(1))
     * It is n bytes long (the hash size).
     * It is a pointer into the encoded buffer
     */
    unsigned char *K;
} LMS_PUB_KEY;

typedef struct lms_key_st {
    const LMS_PARAMS *lms_params;
    const LM_OTS_PARAMS *ots_params;
    OSSL_LIB_CTX *libctx;
    unsigned char *Id; /* A pointer to 16 bytes (I[16]) */
    LMS_PUB_KEY pub;
} LMS_KEY;

const LMS_PARAMS *ossl_lms_params_get(uint32_t lms_type);
const LM_OTS_PARAMS *ossl_lm_ots_params_get(uint32_t ots_type);

LMS_KEY *ossl_lms_key_new(OSSL_LIB_CTX *libctx);
void ossl_lms_key_free(LMS_KEY *lmskey);
int ossl_lms_key_equal(const LMS_KEY *key1, const LMS_KEY *key2, int selection);
int ossl_lms_key_valid(const LMS_KEY *key, int selection);
int ossl_lms_key_has(const LMS_KEY *key, int selection);

int ossl_lms_pubkey_from_params(const OSSL_PARAM *pub, LMS_KEY *lmskey);
int ossl_lms_pubkey_decode(const unsigned char *pub, size_t publen,
    LMS_KEY *lmskey);
size_t ossl_lms_pubkey_length(const unsigned char *data, size_t datalen);

#endif /* OPENSSL_NO_LMS */
#endif /* OSSL_CRYPTO_LMS_H */
