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

#include "crypto/lms.h"
#include "crypto/lms_sig.h"
#include "crypto/lms_util.h"

/* Refer to SP800-208 Section 4 LM-OTS parameter sets */
static const LM_OTS_PARAMS lm_ots_params[] = {
    { OSSL_LM_OTS_TYPE_SHA256_N32_W1, 32, 1, 265, 7, "SHA256" },
    { OSSL_LM_OTS_TYPE_SHA256_N32_W2, 32, 2, 133, 6, "SHA256" },
    { OSSL_LM_OTS_TYPE_SHA256_N32_W4, 32, 4, 67, 4, "SHA256" },
    { OSSL_LM_OTS_TYPE_SHA256_N32_W8, 32, 8, 34, 0, "SHA256" },
    { OSSL_LM_OTS_TYPE_SHA256_N24_W1, 24, 1, 200, 8, "SHA256-192" },
    { OSSL_LM_OTS_TYPE_SHA256_N24_W2, 24, 2, 101, 6, "SHA256-192" },
    { OSSL_LM_OTS_TYPE_SHA256_N24_W4, 24, 4, 51, 4, "SHA256-192" },
    { OSSL_LM_OTS_TYPE_SHA256_N24_W8, 24, 8, 26, 0, "SHA256-192" },
    { OSSL_LM_OTS_TYPE_SHAKE_N32_W1, 32, 1, 265, 7, "SHAKE-256" },
    { OSSL_LM_OTS_TYPE_SHAKE_N32_W2, 32, 2, 133, 6, "SHAKE-256" },
    { OSSL_LM_OTS_TYPE_SHAKE_N32_W4, 32, 4, 67, 4, "SHAKE-256" },
    { OSSL_LM_OTS_TYPE_SHAKE_N32_W8, 32, 8, 34, 0, "SHAKE-256" },
    /* SHAKE-256/192 - OpenSSL does not support this as a name */
    { OSSL_LM_OTS_TYPE_SHAKE_N24_W1, 24, 1, 200, 8, "SHAKE-256" },
    { OSSL_LM_OTS_TYPE_SHAKE_N24_W2, 24, 2, 101, 6, "SHAKE-256" },
    { OSSL_LM_OTS_TYPE_SHAKE_N24_W4, 24, 4, 51, 4, "SHAKE-256" },
    { OSSL_LM_OTS_TYPE_SHAKE_N24_W8, 24, 8, 26, 0, "SHAKE-256" },
    { 0, 0, 0, 0, 0, NULL },
};

/**
 * @brief A getter to convert an |ots_type| into a LM_OTS_PARAMS object.
 *
 * @param ots_type The type such as OSSL_LM_OTS_TYPE_SHA256_N32_W1
 * @returns The LM_OTS_PARAMS object associated with the |ots_type|, or
 *          NULL if |ots_type| is undefined.
 */
const LM_OTS_PARAMS *ossl_lm_ots_params_get(uint32_t ots_type)
{
    const LM_OTS_PARAMS *p;

    for (p = lm_ots_params; p->digestname != NULL; ++p)
        if (p->lm_ots_type == ots_type)
            return p;
    return NULL;
}

/* See RFC 8554 Section 4.4 Checksum */
uint16_t ossl_lm_ots_params_checksum(const LM_OTS_PARAMS *params,
    const unsigned char *S)
{
    uint16_t sum = 0;
    uint16_t i;
    /* Largest size is 8 * 32 / 1 = 256 (which doesn't quite fit into 8 bits) */
    uint16_t bytes = (8 * params->n / params->w);
    uint16_t end = (1 << params->w) - 1;

    for (i = 0; i < bytes; ++i)
        sum += end - lms_ots_coef(S, i, params->w);
    return (sum << params->ls);
}
