/* Copyright (c) (2014,2015,2016,2017,2018,2019) Apple Inc. All rights reserved.
 *
 * corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
 * is contained in the License.txt file distributed with corecrypto) and only to 
 * people who accept that license. IMPORTANT:  Any license rights granted to you by 
 * Apple Inc. (if any) are limited to internal use within your organization only on 
 * devices and computers you own or control, for the sole purpose of verifying the 
 * security characteristics and correct functioning of the Apple Software.  You may 
 * not, directly or indirectly, redistribute the Apple Software or any portions thereof.
 */

/*
    Based on reference code from <http://ed25519.cr.yp.to/> and <http://bench.cr.yp.to/supercop.html>.
*/

#include <corecrypto/ccec25519.h>
#include <corecrypto/ccdigest.h>
#include <corecrypto/ccsha2.h>
#include <corecrypto/ccrng.h>
#include "cced25519_priv.h"
#include "cc_priv.h"

//===========================================================================================================================
//	cced25519_sign
//===========================================================================================================================

/*!
    @function cced25519_sign_internal
    @abstract Produces an (optionally randomized) Ed25519 signature.

    @param  di          A valid descriptor for a 512 bit hash function for the platform
    @param  sig         Receives the 64-byte signature.
    @param  mlen        Number of bytes to sign.
    @param  inMsg       Data to sign.
    @param  pk          32-byte public key as generated by cced25519_make_key_pair().
    @param  sk          32-byte secret key as generated by cced25519_make_key_pair().
    @param  Z           32-byte random noise to be mixed with the private key.
                        Pass all zeros for deterministic signatures.
    @param  rng         RNG for masking.
*/
CC_NONNULL_ALL
static int cced25519_sign_internal(const struct ccdigest_info *di,
                                   ccec25519signature sig,
                                   size_t mlen,
                                   const void *inMsg,
                                   const ccec25519pubkey pk,
                                   const ccec25519secretkey sk,
                                   const uint8_t *Z,
                                   struct ccrng_state *rng)
{
    int rv = CCERR_OK;
    const uint8_t *const m = (const uint8_t *)inMsg;
    ccdigest_di_decl(di, dc);
    uint8_t az[64];
    uint8_t r[64];
    uint8_t hram[64];
    ge_p3 R;

    ASSERT_DIGEST_SIZE(di);
    ccdigest(di, 32, sk, az);
    az[0] &= 248;
    az[31] &= 63;
    az[31] |= 64;

    // XOR random noise with the second half of the hashed private key.
    cc_xor(32, az + 32, az + 32, Z);

    ccdigest_init(di, dc);
    ccdigest_update(di, dc, 32, az + 32);
    ccdigest_update(di, dc, mlen, m);
    ccdigest_final(di, dc, r);
    ccdigest_di_clear(di, dc);

    sc_reduce(r);
    rv = ge_scalarmult_base_masked(&R, r, rng);
    if (rv) {
        goto err;
    }
    ge_p3_tobytes(sig, &R);

    ccdigest_init(di, dc);
    ccdigest_update(di, dc, 32, sig);
    ccdigest_update(di, dc, 32, pk);
    ccdigest_update(di, dc, mlen, m);
    ccdigest_final(di, dc, hram);
    ccdigest_di_clear(di, dc);

    sc_reduce(hram);
    sc_muladd(sig + 32, hram, az, r);

err:
    cc_clear(sizeof(az), az);
    return rv;
}

void cced25519_sign(const struct ccdigest_info *di,
                    ccec25519signature sig,
                    size_t len,
                    const void *msg,
                    const ccec25519pubkey pk,
                    const ccec25519secretkey sk)
{
    uint8_t Z[32];
    struct ccrng_state *rng = ccrng(NULL);
    ccrng_generate(rng, sizeof(Z), Z);

    (void)cced25519_sign_internal(di, sig, len, msg, pk, sk, Z, rng);
}

int cced25519_sign_deterministic(const struct ccdigest_info *di,
                                 ccec25519signature sig,
                                 size_t msg_len,
                                 const void *msg,
                                 const ccec25519pubkey pk,
                                 const ccec25519secretkey sk,
                                 struct ccrng_state *rng)
{
    const uint8_t Z[32] = { 0 };
    return cced25519_sign_internal(di, sig, msg_len, msg, pk, sk, Z, rng);
}
