/*
 * Copyright (c) 2017-2021 Emil Lenngren
 * Copyright (c) 2021 Shortcut Labs AB
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef P256_CORTEX_M4_H
#define P256_CORTEX_M4_H

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

#include "p256-cortex-m4-config.h"

/*

Implementation of P-256 Elliptic Curve operations for 32-bit ARMv7E-M processors or later.

The functions below have the following conventions:
- Arrays of type uint32_t represent 256-bit integers, stored using little endian byte order on a 4-byte alignment.
- Pointer/array parameters are input parameters when they are const and output parameters when they are not
    const, unless otherwise stated.
- All functions that take a public key as parameter will validate that the public key corresponds to a valid
    point and return false if validation fails.
- When a function returns false, the output parameters, if any, will not contain valid data and should hence
    not be inspected.
- There are no checks for null pointers, unaligned uint32_t-pointers, invalid private keys etc.

If there is a need to convert a big endian byte string to an array of little endian uint32_t integers or vice
versa, the p256_convert_endianness function may be used for this purpose.

Note: code that processes secret data runs in constant time, in order to mitigate side channel attacks.

*/

/**
 * Converts endianness by reversing the input value.
 *
 * The output and input pointers may refer to the same location and have no alignment requirements.
 */
void p256_convert_endianness(void* output, const void* input, size_t byte_len);

#if include_p256_verify
/**
 * Verifies an ECDSA signature.
 *
 * Returns true if the signature is valid for the given input, otherwise false.
 */
bool p256_verify(const uint32_t public_key_x[8], const uint32_t public_key_y[8],
                 const uint8_t* hash, uint32_t hashlen_in_bytes,
                 const uint32_t r[8], const uint32_t s[8])
                 __attribute__((warn_unused_result));
#endif

#if include_p256_sign
/**
 * Creates an ECDSA signature.
 *
 * The parameter "k" shall consist of a 256-bit random integer value. This random value MUST be generated from
 * a cryptographically secure random number generator, and MUST be unique for every pair of message hash and
 * private key.
 *
 * With a small probability (~ 2^-32), this function will fail and return false for the given "k" and this
 * function MUST in that case be called again with a new random "k", until true is returned. This is in line
 * with the ECDSA standard.
 *
 * As an alternative to using a random "k", "k" might be derived deterministically from the input, using a
 * sophisticated hash construction such as RFC 6979, or e.g. by hashing the private key, message hash and a
 * retry counter, using a secure hash function such as SHA-256.
 */
bool p256_sign(uint32_t r[8], uint32_t s[8],
               const uint8_t* hash, uint32_t hashlen_in_bytes,
               const uint32_t private_key[8], const uint32_t k[8])
               __attribute__((warn_unused_result));

/**
 * Sign precomputation state.
 *
 * The content shall be treated as opaque to the API user and shall not be inspected or modified.
 */
struct SignPrecomp {
    uint32_t r[8];
    uint32_t k_inv[8];
};

/**
 * Creates an ECDSA signature, using a two-step procedure.
 *
 * This function performs the first of two steps, and accounts for 99% of the time spent for generating an
 * ECDSA signature.
 *
 * By splitting up into two steps, most of the work could be spent before deciding what message to sign, or
 * which private key to use.
 *
 * The parameter "k" shall consist of a 256-bit random integer value. This random value MUST be generated from
 * a cryptographically secure random number generator, and MUST be unique for every pair of message hash and
 * private key.
 *
 * With a small probability (~ 2^-32), this function will fail and return false for the given "k" and this
 * function MUST in that case be called again with a new random "k", until true is returned. This is in line
 * with the ECDSA standard.
 *
 * As an alternative to using a random "k", "k" might be derived deterministically from the input, using a
 * sophisticated hash construction such as RFC 6979, or e.g. by hashing the private key, message hash and a
 * retry counter, using a secure hash function such as SHA-256.
 *
 * The "result" parameter will contain the computed state, that is later to be passed to p256_sign_step2.
 * A result state MUST NOT be reused for generating multiple signatures.
 */
bool p256_sign_step1(struct SignPrecomp *result, const uint32_t k[8]) __attribute__((warn_unused_result));

/**
 * Second step of creating an ECDSA signature, using a two-step procedure.
 *
 * This function performs the second of two steps, and accounts for the last 1% of the time spent for generating
 * an ECDSA signature.
 *
 * The "sign_precomp" parameter shall contain a pointer to a state generated by p256_sign_step1.
 *
 * With a small probability (~ 2^-256), this function will fail, due to the given "k" from the first step is
 * not compatible with the rest of the input, and return false. In this case, the procedure MUST be started
 * over from step 1 with a new random "k".  This is in line with the ECDSA standard. Otherwise true is returned
 * and the signature is placed in "r" and "s".
 *
 * When this function returns, "sign_precomp" is also zeroed out and may hence not be reused.
 */
bool p256_sign_step2(uint32_t r[8], uint32_t s[8], const uint8_t* hash, uint32_t hashlen_in_bytes,
                     const uint32_t private_key[8], struct SignPrecomp *sign_precomp)
                     __attribute__((warn_unused_result));
#endif

#if include_p256_keygen
/**
 * Calculates the public key from a given private key for use by either ECDSA or ECDH.
 *
 * The private key shall be taken from a random value that MUST have been generated by a cryptographically
 * secure random number generator that generates 256 random bits. This function validates that the private key
 * lies in the accepted range 1 to n-1, where n is the order of the elliptic curve, and returns true only if
 * this validation succeeds. If random value is out of that range, false is returned and in this case a new
 * random value needs to be generated and this function MUST be called again until true is returned.
 *
 * The public key is created by performing a scalar multiplication of the private key and the base point of
 * the curve.
 *
 * Only use a keypair for either ECDSA or ECDH, not both, and don't use the private key for any other purposes.
 */
bool p256_keygen(uint32_t public_key_x[8], uint32_t public_key_y[8],
                 const uint32_t private_key[8])
                 __attribute__((warn_unused_result));
#endif

#if include_p256_ecdh
/**
 * Generates the shared secret according to the ECDH standard.
 *
 * The shared secret parameter will contain the big endian encoding for the x coordinate of the scalar
 * multiplication of the private key and the input point (other's public key), if the function succeeds.
 *
 * If the other's public key point does not lie on the curve, this function fails and false is returned.
 * Otherwise, shared secret is calculated and true is returned.
 *
 * NOTE: The return value MUST be checked since the other's public key point cannot generally be trusted.
 */
bool p256_ecdh_calc_shared_secret(uint8_t shared_secret[32], const uint32_t private_key[8],
                                  const uint32_t others_public_key_x[8], const uint32_t others_public_key_y[8])
                                  __attribute__((warn_unused_result));
#endif

#if include_p256_raw_scalarmult_base
/**
 * Raw scalar multiplication by the base point of the elliptic curve.
 *
 * This function can be used to implement custom algorithms using the P-256 curve.
 *
 * This function validates that the scalar lies in the accepted range 1 to n-1, where n is the order of the
 * elliptic curve, and returns true only if this validation succeeds. Otherwise false is returned.
 */
bool p256_scalarmult_base(uint32_t result_x[8], uint32_t result_y[8], const uint32_t scalar[8]);
#endif

#if include_p256_raw_scalarmult_generic
/**
 * Raw scalar multiplication by any point on the elliptic curve.
 *
 * This function can be used to implement custom algorithms using the P-256 curve.
 *
 * This function validates all inputs and proceeds only if the scalar is within the range 1 to n-1, where n
 * is the order of the elliptic curve, and the input point's coordinates are each less than the order of
 * the prime field. If validation succeeds, true is returned. Otherwise false is returned.
 */
bool p256_scalarmult_generic(uint32_t result_x[8], uint32_t result_y[8],
						     const uint32_t scalar[8], const uint32_t in_x[8], const uint32_t in_y[8]);
#endif

// These functions create a big endian octet string representation of a point according to the X.92 standard.

#if include_p256_to_octet_string_uncompressed
/**
 * Uncompressed encoding: "04 || Px || Py".
 */
void p256_point_to_octet_string_uncompressed(uint8_t out[65], const uint32_t x[8], const uint32_t y[8]);
#endif

#if include_p256_to_octet_string_compressed
/**
 * Compressed encoding: "02 || Px" if Py is even and "03 || Px" if Py is odd.
 */
void p256_point_to_octet_string_compressed(uint8_t out[33], const uint32_t x[8], const uint32_t y[8]);
#endif

#if include_p256_to_octet_string_hybrid
/**
 * Hybrid encoding: "06 || Px || Py" if Py is even and "07 || Px || Py" if Py is odd (a pretty useless encoding).
 */
void p256_point_to_octet_string_hybrid(uint8_t out[65], const uint32_t x[8], const uint32_t y[8]);
#endif

#if include_p256_decode_point || include_p256_decompress_point
/**
 * Decodes a point according to the three encodings above.
 *
 * include_p256_decode_point: first byte is "04", "06" or "07" and input length is 65 bytes
 * include_p256_decompress_point: first byte is "02" or "03" and input length is 33 bytes
 *
 * Returns true if the input string confirms to a valid encoding and the point lies on the curve,
 * otherwise false.
 *
 * NOTE: The return value MUST be checked in case the point is not guaranteed to lie on the curve (e.g. if it
 * is received from an untrusted party).
 */
bool p256_octet_string_to_point(uint32_t x[8], uint32_t y[8],
                                const uint8_t* input, uint32_t input_len_in_bytes)
                                __attribute__((warn_unused_result));
#endif

#endif
