#include <libecc/lib_ecc_config.h>
#if defined(WITH_X25519) || defined(WITH_X448)
#if defined(USE_SMALL_STACK)
#error "Error: X25519 and X448 are incompatible with USE_SMALL_STACK (devices low on memory)"
#endif
#if defined(WITH_X25519) && !defined(WITH_CURVE_WEI25519)
#error "Error: X25519 needs curve WEI25519 to be defined! Please define it in libecc config file"
#endif
#if defined(WITH_X448) && !defined(WITH_CURVE_WEI448)
#error "Error: X448 needs curve WEI448 to be defined! Please define it in libecc config file"
#endif
#include <libecc/ecdh/x25519_448.h>
#include <libecc/curves/curves.h>
#include <libecc/sig/ec_key.h>
#include <libecc/utils/utils.h>
#include <libecc/fp/fp_sqrt.h>
#include <libecc/external_deps/rand.h>
ATTRIBUTE_WARN_UNUSED_RET static int decode_scalar(u8 *scalar_decoded, const u8 *scalar, u8 len)
{
int ret;
u8 i;
MUST_HAVE((scalar != scalar_decoded), ret, err);
MUST_HAVE((len > 0), ret, err);
for(i = 0; i < len; i++){
scalar_decoded[len - 1 - i] = scalar[i];
}
if(len == X25519_SIZE){
scalar_decoded[len - 1] &= 248;
scalar_decoded[0] &= 127;
scalar_decoded[0] |= 64;
}
else if(len == X448_SIZE){
scalar_decoded[len - 1] &= 252;
scalar_decoded[0] |= 128;
}
else{
ret = -1;
goto err;
}
ret = 0;
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int decode_u_coordinate(u8 *u_decoded, const u8 *u, u8 len)
{
int ret;
u8 i;
MUST_HAVE((u != u_decoded), ret, err);
MUST_HAVE((len > 0), ret, err);
for(i = 0; i < len; i++){
u_decoded[len - 1 - i] = u[i];
}
ret = 0;
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int encode_u_coordinate(u8 *u_encoded, const u8 *u, u8 len)
{
return decode_u_coordinate(u_encoded, u, len);
}
ATTRIBUTE_WARN_UNUSED_RET static int compute_v_from_u(fp_src_t u, fp_t v, ec_montgomery_crv_src_t crv)
{
int ret;
fp tmp;
tmp.magic = 0;
ret = aff_pt_montgomery_v_from_u(v, &tmp, u, crv);
fp_uninit(&tmp);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_core(const u8 *k, const u8 *u, u8 *res, u8 len)
{
int ret, iszero, loaded, cmp;
#if defined(WITH_X448)
u8 k_[X448_SIZE], u_[X448_SIZE];
#else
u8 k_[X25519_SIZE], u_[X25519_SIZE];
#endif
aff_pt_montgomery _Tmp;
prj_pt Q;
ec_montgomery_crv montgomery_curve;
ec_params shortw_curve_params;
ec_shortw_crv_src_t shortw_curve;
fp_src_t alpha_montgomery;
fp_src_t gamma_montgomery;
nn scalar;
nn_t v_coord_nn;
fp_t u_coord, v_coord;
nn_t cofactor;
_Tmp.magic = montgomery_curve.magic = Q.magic = WORD(0);
scalar.magic = WORD(0);
MUST_HAVE((k != NULL) && (u != NULL) && (res != NULL), ret, err);
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
MUST_HAVE(((sizeof(k_) >= len) && (sizeof(u_) >= len)), ret, err);
ret = decode_scalar(k_, k, len); EG(ret, err);
ret = decode_u_coordinate(u_, u, len); EG(ret, err);
loaded = 0;
#if defined(WITH_X25519)
if(len == X25519_SIZE){
ret = import_params(&shortw_curve_params, &wei25519_str_params); EG(ret, err);
loaded = 1;
}
#endif
#if defined(WITH_X448)
if(len == X448_SIZE){
ret = import_params(&shortw_curve_params, &wei448_str_params); EG(ret, err);
loaded = 1;
}
#endif
MUST_HAVE((loaded == 1), ret, err);
shortw_curve = &(shortw_curve_params.ec_curve);
cofactor = &(shortw_curve_params.ec_gen_cofactor);
alpha_montgomery = &(shortw_curve_params.ec_alpha_montgomery);
gamma_montgomery = &(shortw_curve_params.ec_gamma_montgomery);
u_coord = &(Q.X);
v_coord = &(Q.Y);
v_coord_nn = &(v_coord->fp_val);
ret = curve_shortw_to_montgomery(shortw_curve, &montgomery_curve, alpha_montgomery,
gamma_montgomery); EG(ret, err);
ret = nn_init_from_buf(v_coord_nn, u_, len); EG(ret, err);
ret = nn_cmp(v_coord_nn, &(montgomery_curve.A.ctx->p), &cmp); EG(ret, err);
MUST_HAVE((cmp < 0), ret, err);
ret = fp_init(u_coord, montgomery_curve.A.ctx); EG(ret, err);
ret = fp_set_nn(u_coord, v_coord_nn); EG(ret, err);
ret = compute_v_from_u(u_coord, v_coord, &montgomery_curve); EG(ret, err);
ret = aff_pt_montgomery_init_from_coords(&_Tmp, &montgomery_curve, u_coord, v_coord); EG(ret, err);
ret = aff_pt_montgomery_to_prj_pt_shortw(&_Tmp, shortw_curve, &Q); EG(ret, err);
ret = check_prj_pt_order(&Q, cofactor, PUBLIC_PT, &cmp); EG(ret, err);
MUST_HAVE((!cmp), ret, err);
ret = nn_init_from_buf(&scalar, k_, len); EG(ret, err);
#ifdef USE_SIG_BLINDING
ret = prj_pt_mul_blind(&Q, &scalar, &Q); EG(ret, err);
#else
ret = prj_pt_mul(&Q, &scalar, &Q); EG(ret, err);
#endif
ret = prj_pt_shortw_to_aff_pt_montgomery(&Q, &montgomery_curve, &_Tmp); EG(ret, err);
ret = fp_iszero(&(_Tmp.u), &iszero); EG(ret, err);
MUST_HAVE((!iszero), ret, err);
ret = fp_export_to_buf(u_, len, &(_Tmp.u)); EG(ret, err);
ret = encode_u_coordinate(res, u_, len);
err:
IGNORE_RET_VAL(local_memset(u_, 0, sizeof(u_)));
IGNORE_RET_VAL(local_memset(k_, 0, sizeof(k_)));
IGNORE_RET_VAL(local_memset(&shortw_curve_params, 0, sizeof(shortw_curve_params)));
nn_uninit(&scalar);
aff_pt_montgomery_uninit(&_Tmp);
prj_pt_uninit(&Q);
ec_montgomery_crv_uninit(&montgomery_curve);
PTR_NULLIFY(shortw_curve);
PTR_NULLIFY(alpha_montgomery);
PTR_NULLIFY(gamma_montgomery);
PTR_NULLIFY(u_coord);
PTR_NULLIFY(v_coord);
PTR_NULLIFY(v_coord_nn);
PTR_NULLIFY(cofactor);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_gen_priv_key(u8 *priv_key, u8 len)
{
int ret;
MUST_HAVE((priv_key != NULL), ret, err);
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
ret = get_random(priv_key, len);
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_init_pub_key(const u8 *priv_key, u8 *pub_key, u8 len)
{
int ret;
MUST_HAVE((priv_key != NULL) && (pub_key != NULL), ret, err);
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
if(len == X25519_SIZE){
u8 u[X25519_SIZE];
ret = local_memset(u, 0, sizeof(u)); EG(ret, err);
u[0] = 0x09;
ret = x25519_448_core(priv_key, u, pub_key, len);
}
else if(len == X448_SIZE){
u8 u[X448_SIZE];
ret = local_memset(u, 0, sizeof(u)); EG(ret, err);
u[0] = 0x05;
ret = x25519_448_core(priv_key, u, pub_key, len);
}
else{
ret = -1;
}
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_derive_secret(const u8 *priv_key, const u8 *peer_pub_key, u8 *shared_secret, u8 len)
{
int ret;
MUST_HAVE((priv_key != NULL) && (peer_pub_key != NULL) && (shared_secret != NULL), ret, err);
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
ret = x25519_448_core(priv_key, peer_pub_key, shared_secret, len);
err:
return ret;
}
#if defined(WITH_X25519)
int x25519(const u8 k[X25519_SIZE], const u8 u[X25519_SIZE], u8 res[X25519_SIZE])
{
return x25519_448_core(k, u, res, X25519_SIZE);
}
int x25519_gen_priv_key(u8 priv_key[X25519_SIZE])
{
return x25519_448_gen_priv_key(priv_key, X25519_SIZE);
}
int x25519_init_pub_key(const u8 priv_key[X25519_SIZE], u8 pub_key[X25519_SIZE])
{
return x25519_448_init_pub_key(priv_key, pub_key, X25519_SIZE);
}
int x25519_derive_secret(const u8 priv_key[X25519_SIZE], const u8 peer_pub_key[X25519_SIZE], u8 shared_secret[X25519_SIZE])
{
return x25519_448_derive_secret(priv_key, peer_pub_key, shared_secret, X25519_SIZE);
}
#endif
#if defined(WITH_X448)
int x448(const u8 k[X448_SIZE], const u8 u[X448_SIZE], u8 res[X448_SIZE])
{
return x25519_448_core(k, u, res, X448_SIZE);
}
int x448_gen_priv_key(u8 priv_key[X448_SIZE])
{
return x25519_448_gen_priv_key(priv_key, X448_SIZE);
}
int x448_init_pub_key(const u8 priv_key[X448_SIZE], u8 pub_key[X448_SIZE])
{
return x25519_448_init_pub_key(priv_key, pub_key, X448_SIZE);
}
int x448_derive_secret(const u8 priv_key[X448_SIZE], const u8 peer_pub_key[X448_SIZE], u8 shared_secret[X448_SIZE])
{
return x25519_448_derive_secret(priv_key, peer_pub_key, shared_secret, X448_SIZE);
}
#endif
#else
typedef int dummy;
#endif