/*
 * 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_sig.h"
#include "crypto/lms_util.h"

/**
 * @brief Decode a byte array containing XDR signature data into a LMS_SIG object.
 *
 * This is used for LMS Signature Verification.
 * This function may be called multiple times.
 * See RFC 8554 Algorithm 6a: Steps 1 and 2.
 * It uses shallow copies for C, y and path.
 *
 * @param pkt Contains the signature data to decode. There may still be data
 *            remaining in pkt after decoding.
 * @param pub A public key that contains LMS_PARAMS and LM_OTS_PARAMS associated
 *            with the signature.
 * @returns The created LMS_SIG object if successful, or NULL on failure. A
 *          failure may occur if the passed in LMS public key |pub| is not
 *          compatible with the decoded LMS_SIG object,
 */
LMS_SIG *ossl_lms_sig_from_pkt(PACKET *pkt, const LMS_KEY *pub)
{
    uint32_t sig_ots_type = 0, sig_lms_type = 0;
    const LMS_PARAMS *lparams = pub->lms_params;
    const LM_OTS_PARAMS *pub_ots_params = pub->ots_params;
    const LM_OTS_PARAMS *sig_params;
    LMS_SIG *lsig = NULL;

    lsig = ossl_lms_sig_new();
    if (lsig == NULL)
        return NULL;

    if (!PACKET_get_net_4_len_u32(pkt, &lsig->q) /* q = Leaf Index */
        || !PACKET_get_net_4_len_u32(pkt, &sig_ots_type)
        || pub_ots_params->lm_ots_type != sig_ots_type)
        goto err;
    sig_params = pub_ots_params;
    lsig->sig.params = sig_params;
    lsig->params = lparams;

    if (!PACKET_get_bytes(pkt, (const unsigned char **)&lsig->sig.C,
            sig_params->n)
        || !PACKET_get_bytes(pkt, (const unsigned char **)&lsig->sig.y,
            sig_params->p * sig_params->n)
        || !PACKET_get_net_4_len_u32(pkt, &sig_lms_type)
        || (lparams->lms_type != sig_lms_type)
        || HASH_NOT_MATCHED(lparams, sig_params)
        || lsig->q >= (uint32_t)(1 << lparams->h)
        || !PACKET_get_bytes(pkt, (const unsigned char **)&lsig->paths,
            lparams->h * lparams->n)
        || PACKET_remaining(pkt) > 0)
        goto err;
    return lsig;
err:
    ossl_lms_sig_free(lsig);
    return NULL;
}

/**
 * @brief Decode a byte array of LMS signature data.
 *
 * This function does not duplicate any of the byte data contained within
 * |sig|. So it is expected that |sig| will exist for the duration of the
 * returned signature |out|.
 *
 * @param out Used to return the LMS_SIG object.
 * @param pub The root public LMS key
 * @param sig A input byte array of signature data.
 * @param siglen The size of sig.
 * @returns 1 if the signature is successfully decoded,
 *          otherwise it returns 0.
 */
int ossl_lms_sig_decode(LMS_SIG **out, LMS_KEY *pub,
    const unsigned char *sig, size_t siglen)
{
    PACKET pkt;
    LMS_SIG *s = NULL;

    if (pub == NULL)
        return 0;

    if (!PACKET_buf_init(&pkt, sig, siglen))
        return 0;

    s = ossl_lms_sig_from_pkt(&pkt, pub);
    if (s == NULL)
        return 0;

    /* Fail if there are trailing bytes */
    if (PACKET_remaining(&pkt) > 0)
        goto err;
    *out = s;
    return 1;
err:
    ossl_lms_sig_free(s);
    return 0;
}
