/*************************************************************************
* Copyright (C) 2023 Intel Corporation
*
* 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
*
* 	http://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.
*************************************************************************/

#ifndef IPPCP_XMSS_H_
#define IPPCP_XMSS_H_

#include "owndefs.h"
#include "owncp.h"
#include "wots.h"

// The format of an XMSS private key
// +---------------------------------+
// | algorithm OID                   |
// +---------------------------------+
// | index idx                       | 4 bytes
// +---------------------------------+
// |                                 |
// | private key seed                | n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | SK_PRF key                      | n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | root node                       | n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | public key seed                 | n bytes
// |                                 |
// +---------------------------------+

struct _cpXMSSPrivateKeyState {
    IppsXMSSAlgo OIDAlgo;
    Ipp32u idx;
    Ipp8u* pSecretSeed;
    Ipp8u* pSK_PRF;
    Ipp8u* pRoot;
    Ipp8u* pPublicSeed;
};

// The format of an XMSS public key
// +---------------------------------+
// | algorithm OID                   |
// +---------------------------------+
// |                                 |
// | root node                       | n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | SEED                            | n bytes
// |                                 |
// +---------------------------------+

struct _cpXMSSPublicKeyState {
    IppsXMSSAlgo OIDAlgo;
    Ipp8u* pRoot;
    Ipp8u* pSeed;
};

// The data format for a signature
// +---------------------------------+
// |                                 |
// | index idx_sig                   | 4 bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | randomness r                    | n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | WOTS+ signature sig_ots         | len * n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// | auth[0]                         | n bytes
// |                                 |
// +---------------------------------+
// |                                 |
// ˜                 ....            ˜
// |                                 |
// +---------------------------------+
// |                                 |
// | auth[h - 1]                     | n bytes
// |                                 |
// +---------------------------------+

struct _cpXMSSSignatureState {
    Ipp32u idx;
    Ipp8u* r;
    Ipp8u* pOTSSign;
    Ipp8u* pAuthPath;
};

// declarations
#define cp_xmss_ltree OWNAPI(cp_xmss_ltree)
/* clang-format off */
IPP_OWN_DECL(IppStatus, cp_xmss_ltree, (Ipp8u* pk,
                                        Ipp8u* seed,
                                        Ipp8u* adrs,
                                        Ipp8u* temp_buf,
                                        const cpWOTSParams* params))
/* clang-format on */

#define cp_xmss_rand_hash OWNAPI(cp_xmss_rand_hash)
/* clang-format off */
IPP_OWN_DECL(IppStatus, cp_xmss_rand_hash, (Ipp8u* left,
                                            Ipp8u* right,
                                            Ipp8u* seed,
                                            Ipp8u* adrs,
                                            Ipp8u* out,
                                            Ipp8u* temp_buf,
                                            const cpWOTSParams* params))
/* clang-format on */
#define cp_xmss_tree_hash OWNAPI(cp_xmss_tree_hash)
/* clang-format off */
IPP_OWN_DECL(IppStatus, cp_xmss_tree_hash, (Ipp8u isKeyGen,
                                            IppsXMSSPrivateKeyState* pSecretKey,
                                            Ipp8u* adrs,
                                            Ipp8u* out,
                                            Ipp32u idx_leaf,
                                            Ipp8u* temp_buf,
                                            Ipp32s h,
                                            const cpWOTSParams* params))
/* clang-format on */

/*
 * Set XMSS algorithms parameters
 *
 * Returns:                Reason:
 *    ippStsBadArgErr         OIDAlgo > Max value for IppsXMSSAlgo
 *                            OIDAlgo <= 0
 *    ippStsNoErr             no errors
 *
 * Input parameters:
 *    OIDAlgo   id of XMSS set of parameters (algorithm)
 *    h         height of the XMSS tree
 *
 * Output parameters:
 *    params    WOTS parameters (w, log2_w, n, len, len_1, hash_method)
 */

IPPCP_INLINE IppStatus cp_xmss_set_params(IppsXMSSAlgo OIDAlgo, Ipp32s* h, cpWOTSParams* params)
{

    // Digits below are from the XMSS algo spec
    // don't depend on the algo
    params->w      = 16;
    params->log2_w = 4;

    // 256-bit or 512-bit
    if (OIDAlgo > 0 && OIDAlgo < 4) {
        params->n           = 32;
        params->len         = 67;
        params->len_1       = 64;
        params->hash_method = (IppsHashMethod*)ippsHashMethod_SHA256_TT();
    } else if (OIDAlgo > 3 && OIDAlgo < 7) {
        params->n           = 64;
        params->len         = 131;
        params->len_1       = 128;
        params->hash_method = (IppsHashMethod*)ippsHashMethod_SHA512_TT();
    } else {
        return ippStsBadArgErr;
    }

    // tree height
    if (OIDAlgo % 3 == 1) {
        *h = 10;
    } else if (OIDAlgo % 3 == 2) {
        *h = 16;
    } else if (OIDAlgo % 3 == 0) {
        *h = 20;
    }

    return ippStsNoErr;
}

#endif /* #ifndef IPPCP_XMSS_H_ */
