From 40b1f8b85e474063292d1c3c2a8540c890c6f908 Mon Sep 17 00:00:00 2001 From: Mike Hamburg Date: Thu, 18 Jun 2015 22:48:43 -0700 Subject: [PATCH] initial replace 448->255; doesnt compile yet --- Makefile | 2 +- include/decaf.h | 651 +----------------------- include/decaf.hxx | 738 +--------------------------- include/decaf_255.h | 651 ++++++++++++++++++++++++ include/decaf_255.hxx | 738 ++++++++++++++++++++++++++++ include/decaf_448.h | 651 ++++++++++++++++++++++++ include/decaf_448.hxx | 738 ++++++++++++++++++++++++++++ src/decaf_crypto.c | 128 ++--- src/decaf_fast.c | 48 +- src/decaf_gen_tables.c | 16 +- src/p25519/arch_ref64/arch_config.h | 1 + src/p25519/arch_ref64/p25519.c | 318 ++++++++++++ src/p25519/arch_ref64/p25519.h | 155 ++++++ src/p25519/f_arithmetic.c | 67 +++ src/p25519/f_field.h | 32 ++ test/bench_decaf.cxx | 28 +- test/test_decaf.cxx | 32 +- 17 files changed, 3488 insertions(+), 1506 deletions(-) create mode 100644 include/decaf_255.h create mode 100644 include/decaf_255.hxx create mode 100644 include/decaf_448.h create mode 100644 include/decaf_448.hxx create mode 100644 src/p25519/arch_ref64/arch_config.h create mode 100644 src/p25519/arch_ref64/p25519.c create mode 100644 src/p25519/arch_ref64/p25519.h create mode 100644 src/p25519/f_arithmetic.c create mode 100644 src/p25519/f_field.h diff --git a/Makefile b/Makefile index c6f2477..c3a295e 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ else ARCH ?= arch_arm_32 endif -FIELD ?= p448 +FIELD ?= p255 WARNFLAGS = -pedantic -Wall -Wextra -Werror -Wunreachable-code \ -Wmissing-declarations -Wunused-function -Wno-overlength-strings $(EXWARN) diff --git a/include/decaf.h b/include/decaf.h index 6519214..2a55920 100644 --- a/include/decaf.h +++ b/include/decaf.h @@ -1,651 +1,8 @@ -/** - * @file decaf.h - * @author Mike Hamburg - * - * @copyright - * Copyright (c) 2015 Cryptography Research, Inc. \n - * Released under the MIT License. See LICENSE.txt for license information. - * - * @brief A group of prime order p. - * - * The Decaf library implements cryptographic operations on a an elliptic curve - * group of prime order p. It accomplishes this by using a twisted Edwards - * curve (isogenous to Ed448-Goldilocks) and wiping out the cofactor. - * - * The formulas are all complete and have no special cases, except that - * decaf_448_decode can fail because not every sequence of bytes is a valid group - * element. - * - * The formulas contain no data-dependent branches, timing or memory accesses, - * except for decaf_448_base_double_scalarmul_non_secret. - * - * This library may support multiple curves eventually. The Ed448-Goldilocks - * specific identifiers are prefixed with DECAF_448 or decaf_448. - */ -#ifndef __DECAF_448_H__ -#define __DECAF_448_H__ 1 -#include -#include +#ifndef __DECAF_H__ +#define __DECAF_H__ 1 -/* Goldilocks' build flags default to hidden and stripping executables. */ -/** @cond internal */ -#if defined(DOXYGEN) && !defined(__attribute__) -#define __attribute__((x)) -#endif -#define API_VIS __attribute__((visibility("default"))) -#define NOINLINE __attribute__((noinline)) -#define WARN_UNUSED __attribute__((warn_unused_result)) -#define NONNULL1 __attribute__((nonnull(1))) -#define NONNULL2 __attribute__((nonnull(1,2))) -#define NONNULL3 __attribute__((nonnull(1,2,3))) -#define NONNULL4 __attribute__((nonnull(1,2,3,4))) -#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) +#include "decaf_255.h" // MAGIC -/* Internal word types */ -#if (defined(__ILP64__) || defined(__amd64__) || defined(__x86_64__) || (((__UINT_FAST32_MAX__)>>30)>>30)) \ - && !defined(DECAF_FORCE_32_BIT) -#define DECAF_WORD_BITS 64 -typedef uint64_t decaf_word_t, decaf_bool_t; -typedef __uint128_t decaf_dword_t; -#else -#define DECAF_WORD_BITS 32 -typedef uint32_t decaf_word_t, decaf_bool_t; -typedef uint64_t decaf_dword_t; -#endif +#endif /* __DECAF_H__ */ -#define DECAF_448_LIMBS (512/DECAF_WORD_BITS) -#define DECAF_448_SCALAR_BITS 446 -#define DECAF_448_SCALAR_LIMBS (448/DECAF_WORD_BITS) - -/** Galois field element internal structure */ -typedef struct gf_s { - decaf_word_t limb[DECAF_448_LIMBS]; -} __attribute__((aligned(32))) gf_s, gf[1]; -/** @endcond */ - -/** Number of bytes in a serialized point. */ -#define DECAF_448_SER_BYTES 56 - -/** Number of bytes in a serialized scalar. */ -#define DECAF_448_SCALAR_BYTES 56 - -/** Twisted Edwards (-1,d-1) extended homogeneous coordinates */ -typedef struct decaf_448_point_s { /**@cond internal*/gf x,y,z,t;/**@endcond*/ } decaf_448_point_t[1]; - -/** Precomputed table based on a point. Can be trivial implementation. */ -struct decaf_448_precomputed_s; - -/** Precomputed table based on a point. Can be trivial implementation. */ -typedef struct decaf_448_precomputed_s decaf_448_precomputed_s; - -/** Size and alignment of precomputed point tables. */ -extern const size_t sizeof_decaf_448_precomputed_s API_VIS, alignof_decaf_448_precomputed_s API_VIS; - -/** Scalar is stored packed, because we don't need the speed. */ -typedef struct decaf_448_scalar_s { - /** @cond internal */ - decaf_word_t limb[DECAF_448_SCALAR_LIMBS]; - /** @endcond */ -} decaf_448_scalar_t[1]; - -/** DECAF_TRUE = -1 so that DECAF_TRUE & x = x */ -static const decaf_bool_t DECAF_TRUE = -(decaf_bool_t)1, DECAF_FALSE = 0; - -/** NB Success is -1, failure is 0. TODO: see if people would rather the reverse. */ -static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, - DECAF_FAILURE = 0 /*DECAF_FALSE*/; - -/** A scalar equal to 1. */ -extern const decaf_448_scalar_t decaf_448_scalar_one API_VIS; - -/** A scalar equal to 0. */ -extern const decaf_448_scalar_t decaf_448_scalar_zero API_VIS; - -/** The identity point on the curve. */ -extern const decaf_448_point_t decaf_448_point_identity API_VIS; - -/** - * An arbitrarily chosen base point on the curve. - * Equal to Ed448-Goldilocks base point defined by DJB, except of course that - * it's on the twist in this case. TODO: choose a base point with nice encoding? - */ -extern const decaf_448_point_t decaf_448_point_base API_VIS; - -/** Precomputed table for the base point on the curve. */ -extern const struct decaf_448_precomputed_s *decaf_448_precomputed_base API_VIS; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Read a scalar from wire format or from bytes. - * - * @param [in] ser Serialized form of a scalar. - * @param [out] out Deserialized form. - * - * @retval DECAF_SUCCESS The scalar was correctly encoded. - * @retval DECAF_FAILURE The scalar was greater than the modulus, - * and has been reduced modulo that modulus. - */ -decaf_bool_t decaf_448_scalar_decode ( - decaf_448_scalar_t out, - const unsigned char ser[DECAF_448_SCALAR_BYTES] -) API_VIS WARN_UNUSED NONNULL2 NOINLINE; - -/** - * @brief Read a scalar from wire format or from bytes. Reduces mod - * scalar prime. - * - * @param [in] ser Serialized form of a scalar. - * @param [in] ser_len Length of serialized form. - * @param [out] out Deserialized form. - */ -void decaf_448_scalar_decode_long ( - decaf_448_scalar_t out, - const unsigned char *ser, - size_t ser_len -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Serialize a scalar to wire format. - * - * @param [out] ser Serialized form of a scalar. - * @param [in] s Deserialized scalar. - */ -void decaf_448_scalar_encode ( - unsigned char ser[DECAF_448_SCALAR_BYTES], - const decaf_448_scalar_t s -) API_VIS NONNULL2 NOINLINE NOINLINE; - -/** - * @brief Add two scalars. The scalars may use the same memory. - * @param [in] a One scalar. - * @param [in] b Another scalar. - * @param [out] out a+b. - */ -void decaf_448_scalar_add ( - decaf_448_scalar_t out, - const decaf_448_scalar_t a, - const decaf_448_scalar_t b -) API_VIS NONNULL3 NOINLINE; - -/** - * @brief Compare two scalars. - * @param [in] a One scalar. - * @param [in] b Another scalar. - * @retval DECAF_TRUE The scalars are equal. - * @retval DECAF_FALSE The scalars are not equal. - */ -decaf_bool_t decaf_448_scalar_eq ( - const decaf_448_scalar_t a, - const decaf_448_scalar_t b -) API_VIS WARN_UNUSED NONNULL2 NOINLINE; - -/** - * @brief Subtract two scalars. The scalars may use the same memory. - * @param [in] a One scalar. - * @param [in] b Another scalar. - * @param [out] out a-b. - */ -void decaf_448_scalar_sub ( - decaf_448_scalar_t out, - const decaf_448_scalar_t a, - const decaf_448_scalar_t b -) API_VIS NONNULL3 NOINLINE; - -/** - * @brief Multiply two scalars. The scalars may use the same memory. - * @param [in] a One scalar. - * @param [in] b Another scalar. - * @param [out] out a*b. - */ -void decaf_448_scalar_mul ( - decaf_448_scalar_t out, - const decaf_448_scalar_t a, - const decaf_448_scalar_t b -) API_VIS NONNULL3 NOINLINE; - -/** - * @brief Invert a scalar. When passed zero, return 0. The input and output may alias. - * @param [in] a A scalar. - * @param [out] out 1/a. - * @return DECAF_TRUE The input is nonzero. - */ -decaf_bool_t decaf_448_scalar_invert ( - decaf_448_scalar_t out, - const decaf_448_scalar_t a -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Copy a scalar. The scalars may use the same memory, in which - * case this function does nothing. - * @param [in] a A scalar. - * @param [out] out Will become a copy of a. - */ -static inline void NONNULL2 decaf_448_scalar_copy ( - decaf_448_scalar_t out, - const decaf_448_scalar_t a -) { - *out = *a; -} - -/** - * @brief Set a scalar to an integer. - * @param [in] a An integer. - * @param [out] out Will become equal to a. - * @todo Make inline? - */ -void decaf_448_scalar_set( - decaf_448_scalar_t out, - decaf_word_t a -) API_VIS NONNULL1; - -/** - * @brief Encode a point as a sequence of bytes. - * - * @param [out] ser The byte representation of the point. - * @param [in] pt The point to encode. - */ -void decaf_448_point_encode ( - uint8_t ser[DECAF_448_SER_BYTES], - const decaf_448_point_t pt -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Decode a point from a sequence of bytes. - * - * Every point has a unique encoding, so not every - * sequence of bytes is a valid encoding. If an invalid - * encoding is given, the output is undefined. - * - * @param [out] pt The decoded point. - * @param [in] ser The serialized version of the point. - * @param [in] allow_identity DECAF_TRUE if the identity is a legal input. - * @retval DECAF_SUCCESS The decoding succeeded. - * @retval DECAF_FAILURE The decoding didn't succeed, because - * ser does not represent a point. - */ -decaf_bool_t decaf_448_point_decode ( - decaf_448_point_t pt, - const uint8_t ser[DECAF_448_SER_BYTES], - decaf_bool_t allow_identity -) API_VIS WARN_UNUSED NONNULL2 NOINLINE; - -/** - * @brief Copy a point. The input and output may alias, - * in which case this function does nothing. - * - * @param [out] a A copy of the point. - * @param [in] b Any point. - */ -static inline void NONNULL2 decaf_448_point_copy ( - decaf_448_point_t a, - const decaf_448_point_t b -) { - *a=*b; -} - -/** - * @brief Test whether two points are equal. If yes, return - * DECAF_TRUE, else return DECAF_FALSE. - * - * @param [in] a A point. - * @param [in] b Another point. - * @retval DECAF_TRUE The points are equal. - * @retval DECAF_FALSE The points are not equal. - */ -decaf_bool_t decaf_448_point_eq ( - const decaf_448_point_t a, - const decaf_448_point_t b -) API_VIS WARN_UNUSED NONNULL2 NOINLINE; - -/** - * @brief Add two points to produce a third point. The - * input points and output point can be pointers to the same - * memory. - * - * @param [out] sum The sum a+b. - * @param [in] a An addend. - * @param [in] b An addend. - */ -void decaf_448_point_add ( - decaf_448_point_t sum, - const decaf_448_point_t a, - const decaf_448_point_t b -) API_VIS NONNULL3; - -/** - * @brief Double a point. Equivalent to - * decaf_448_point_add(two_a,a,a), but potentially faster. - * - * @param [out] two_a The sum a+a. - * @param [in] a A point. - */ -void decaf_448_point_double ( - decaf_448_point_t two_a, - const decaf_448_point_t a -) API_VIS NONNULL2; - -/** - * @brief Subtract two points to produce a third point. The - * input points and output point can be pointers to the same - * memory. - * - * @param [out] diff The difference a-b. - * @param [in] a The minuend. - * @param [in] b The subtrahend. - */ -void decaf_448_point_sub ( - decaf_448_point_t diff, - const decaf_448_point_t a, - const decaf_448_point_t b -) API_VIS NONNULL3; - -/** - * @brief Negate a point to produce another point. The input - * and output points can use the same memory. - * - * @param [out] nega The negated input point - * @param [in] a The input point. - */ -void decaf_448_point_negate ( - decaf_448_point_t nega, - const decaf_448_point_t a -) API_VIS NONNULL2; - -/** - * @brief Multiply a base point by a scalar: scaled = scalar*base. - * - * @param [out] scaled The scaled point base*scalar - * @param [in] base The point to be scaled. - * @param [in] scalar The scalar to multiply by. - */ -void decaf_448_point_scalarmul ( - decaf_448_point_t scaled, - const decaf_448_point_t base, - const decaf_448_scalar_t scalar -) API_VIS NONNULL3 NOINLINE; - -/** - * @brief Multiply a base point by a scalar: scaled = scalar*base. - * This function operates directly on serialized forms. - * - * @warning This function is experimental. It may not be supported - * long-term. - * - * @param [out] scaled The scaled point base*scalar - * @param [in] base The point to be scaled. - * @param [in] scalar The scalar to multiply by. - * @param [in] allow_identity Allow the input to be the identity. - * @param [in] short_circuit Allow a fast return if the input is illegal. - * - * @retval DECAF_SUCCESS The scalarmul succeeded. - * @retval DECAF_FAILURE The scalarmul didn't succeed, because - * base does not represent a point. - */ -decaf_bool_t decaf_448_direct_scalarmul ( - uint8_t scaled[DECAF_448_SER_BYTES], - const uint8_t base[DECAF_448_SER_BYTES], - const decaf_448_scalar_t scalar, - decaf_bool_t allow_identity, - decaf_bool_t short_circuit -) API_VIS NONNULL3 WARN_UNUSED NOINLINE; - -/** - * @brief Precompute a table for fast scalar multiplication. - * Some implementations do not include precomputed points; for - * those implementations, this implementation simply copies the - * point. - * - * @param [out] a A precomputed table of multiples of the point. - * @param [in] b Any point. - */ -void decaf_448_precompute ( - decaf_448_precomputed_s *a, - const decaf_448_point_t b -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Multiply a precomputed base point by a scalar: - * scaled = scalar*base. - * Some implementations do not include precomputed points; for - * those implementations, this function is the same as - * decaf_448_point_scalarmul - * - * @param [out] scaled The scaled point base*scalar - * @param [in] base The point to be scaled. - * @param [in] scalar The scalar to multiply by. - * - * @todo precomputed dsmul? const or variable time? - */ -void decaf_448_precomputed_scalarmul ( - decaf_448_point_t scaled, - const decaf_448_precomputed_s *base, - const decaf_448_scalar_t scalar -) API_VIS NONNULL3 NOINLINE; - -/** - * @brief Multiply two base points by two scalars: - * scaled = scalar1*base1 + scalar2*base2. - * - * Equivalent to two calls to decaf_448_point_scalarmul, but may be - * faster. - * - * @param [out] combo The linear combination scalar1*base1 + scalar2*base2. - * @param [in] base1 A first point to be scaled. - * @param [in] scalar1 A first scalar to multiply by. - * @param [in] base2 A second point to be scaled. - * @param [in] scalar2 A second scalar to multiply by. - */ -void decaf_448_point_double_scalarmul ( - decaf_448_point_t combo, - const decaf_448_point_t base1, - const decaf_448_scalar_t scalar1, - const decaf_448_point_t base2, - const decaf_448_scalar_t scalar2 -) API_VIS NONNULL5 NOINLINE; - -/** - * @brief Multiply two base points by two scalars: - * scaled = scalar1*decaf_448_point_base + scalar2*base2. - * - * Otherwise equivalent to decaf_448_point_double_scalarmul, but may be - * faster at the expense of being variable time. - * - * @param [out] combo The linear combination scalar1*base + scalar2*base2. - * @param [in] scalar1 A first scalar to multiply by. - * @param [in] base2 A second point to be scaled. - * @param [in] scalar2 A second scalar to multiply by. - * - * @warning: This function takes variable time, and may leak the scalars - * used. It is designed for signature verification. - */ -void decaf_448_base_double_scalarmul_non_secret ( - decaf_448_point_t combo, - const decaf_448_scalar_t scalar1, - const decaf_448_point_t base2, - const decaf_448_scalar_t scalar2 -) API_VIS NONNULL4 NOINLINE; - -/** - * @brief Test that a point is valid, for debugging purposes. - * - * @param [in] toTest The point to test. - * @retval DECAF_TRUE The point is valid. - * @retval DECAF_FALSE The point is invalid. - */ -decaf_bool_t decaf_448_point_valid ( - const decaf_448_point_t toTest -) API_VIS WARN_UNUSED NONNULL1 NOINLINE; - -/** - * @brief 2-torque a point, for debugging purposes. - * - * @param [out] q The point to torque. - * @param [in] p The point to torque. - */ -void decaf_448_point_debugging_2torque ( - decaf_448_point_t q, - const decaf_448_point_t p -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Almost-Elligator-like hash to curve. - * - * Call this function with the output of a hash to make a hash to the curve. - * - * This function runs Elligator2 on the decaf_448 Jacobi quartic model. It then - * uses the isogeny to put the result in twisted Edwards form. As a result, - * it is safe (cannot produce points of order 4), and would be compatible with - * hypothetical other implementations of Decaf using a Montgomery or untwisted - * Edwards model. - * - * Unlike Elligator, this function may be up to 4:1 on [0,(p-1)/2]: - * A factor of 2 due to the isogeny. - * A factor of 2 because we quotient out the 2-torsion. - * - * This makes it about 8:1 overall. - * - * Negating the input (mod q) results in the same point. Inverting the input - * (mod q) results in the negative point. This is the same as Elligator. - * - * This function isn't quite indifferentiable from a random oracle. - * However, it is suitable for many protocols, including SPEKE and SPAKE2 EE. - * Furthermore, calling it twice with independent seeds and adding the results - * is indifferentiable from a random oracle. - * - * @param [in] hashed_data Output of some hash function. - * @param [out] pt The data hashed to the curve. - * @return A "hint" value which can be used to help invert the encoding. - */ -unsigned char -decaf_448_point_from_hash_nonuniform ( - decaf_448_point_t pt, - const unsigned char hashed_data[DECAF_448_SER_BYTES] -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Inverse of elligator-like hash to curve. - * - * This function writes to the buffer, to make it so that - * decaf_448_point_from_hash_nonuniform(buffer) = pt,hint - * if possible. - * - * @param [out] recovered_hash Encoded data. - * @param [in] pt The point to encode. - * @param [in] hint The hint value returned from - * decaf_448_point_from_hash_nonuniform. - * - * @retval DECAF_SUCCESS The inverse succeeded. - * @retval DECAF_FAILURE The pt isn't the image of - * decaf_448_point_from_hash_nonuniform with the given hint. - * - * @warning The hinting system is subject to change, especially in corner cases. - * @warning FIXME The hinting system doesn't work for certain inputs which have many 0xFF. - */ -decaf_bool_t -decaf_448_invert_elligator_nonuniform ( - unsigned char recovered_hash[DECAF_448_SER_BYTES], - const decaf_448_point_t pt, - unsigned char hint -) API_VIS NONNULL2 NOINLINE WARN_UNUSED; - -/** - * @brief Inverse of elligator-like hash to curve, uniform. - * - * This function modifies the first DECAF_448_SER_BYTES of the - * buffer, to make it so that - * decaf_448_point_from_hash_uniform(buffer) = pt,hint - * if possible. - * - * @param [out] recovered_hash Encoded data. - * @param [in] pt The point to encode. - * @param [in] hint The hint value returned from - * decaf_448_point_from_hash_nonuniform. - * - * @retval DECAF_SUCCESS The inverse succeeded. - * @retval DECAF_FAILURE The pt isn't the image of - * decaf_448_point_from_hash_uniform with the given hint. - * - * @warning The hinting system is subject to change, especially in corner cases. - * @warning FIXME The hinting system doesn't work for certain inputs which have many 0xFF. - */ -decaf_bool_t -decaf_448_invert_elligator_uniform ( - unsigned char recovered_hash[2*DECAF_448_SER_BYTES], - const decaf_448_point_t pt, - unsigned char hint -) API_VIS NONNULL2 NOINLINE WARN_UNUSED; - -/** - * @brief Indifferentiable hash function encoding to curve. - * - * Equivalent to calling decaf_448_point_from_hash_nonuniform twice and adding. - * - * @param [in] hashed_data Output of some hash function. - * @param [out] pt The data hashed to the curve. - * @return A "hint" value which can be used to help invert the encoding. - */ -unsigned char decaf_448_point_from_hash_uniform ( - decaf_448_point_t pt, - const unsigned char hashed_data[2*DECAF_448_SER_BYTES] -) API_VIS NONNULL2 NOINLINE; - -/** - * @brief Overwrite data with zeros. Uses memset_s if available. - */ -void decaf_bzero ( - void *data, - size_t size -) NONNULL1 API_VIS NOINLINE; - -/** - * @brief Compare two buffers, returning DECAF_TRUE if they are equal. - */ -decaf_bool_t decaf_memeq ( - const void *data1, - const void *data2, - size_t size -) NONNULL2 WARN_UNUSED API_VIS NOINLINE; - -/** - * @brief Overwrite scalar with zeros. - */ -void decaf_448_scalar_destroy ( - decaf_448_scalar_t scalar -) NONNULL1 API_VIS; - -/** - * @brief Overwrite point with zeros. - * @todo Use this internally. - */ -void decaf_448_point_destroy ( - decaf_448_point_t point -) NONNULL1 API_VIS; - -/** - * @brief Overwrite point with zeros. - * @todo Use this internally. - */ -void decaf_448_precomputed_destroy ( - decaf_448_precomputed_s *pre -) NONNULL1 API_VIS; - -/* TODO: functions to invert point_from_hash?? */ - -#undef API_VIS -#undef WARN_UNUSED -#undef NOINLINE -#undef NONNULL1 -#undef NONNULL2 -#undef NONNULL3 -#undef NONNULL4 -#undef NONNULL5 - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* __DECAF_448_H__ */ diff --git a/include/decaf.hxx b/include/decaf.hxx index 2d82200..4ac29fd 100644 --- a/include/decaf.hxx +++ b/include/decaf.hxx @@ -1,738 +1,8 @@ -/** - * @file decaf.hxx - * @author Mike Hamburg - * - * @copyright - * Copyright (c) 2015 Cryptography Research, Inc. \n - * Released under the MIT License. See LICENSE.txt for license information. - * - * @brief A group of prime order p, C++ wrapper. - * - * The Decaf library implements cryptographic operations on a an elliptic curve - * group of prime order p. It accomplishes this by using a twisted Edwards - * curve (isogenous to Ed448-Goldilocks) and wiping out the cofactor. - * - * The formulas are all complete and have no special cases, except that - * decaf_448_decode can fail because not every sequence of bytes is a valid group - * element. - * - * The formulas contain no data-dependent branches, timing or memory accesses, - * except for decaf_448_base_double_scalarmul_non_secret. - */ -#ifndef __DECAF_448_HXX__ -#define __DECAF_448_HXX__ 1 -/** This code uses posix_memalign. */ -#define _XOPEN_SOURCE 600 -#include -#include /* for memcpy */ +#ifndef __DECAF_HXX__ +#define __DECAF_HXX__ 1 -#include "decaf.h" -#include -#include -#include +#include "decaf_255.hxx" // MAGIC -/* TODO: This is incomplete */ -/* TODO: attribute nonnull */ +#endif /* __DECAF_H__ */ -/** @cond internal */ -#if __cplusplus >= 201103L -#define NOEXCEPT noexcept -#define EXPLICIT_CON explicit -#define GET_DATA(str) ((const unsigned char *)&(str)[0]) -#else -#define NOEXCEPT throw() -#define EXPLICIT_CON -#define GET_DATA(str) ((const unsigned char *)((str).data())) -#endif -/** @endcond */ - -namespace decaf { - -/** @brief An exception for when crypto (ie point decode) has failed. */ -class CryptoException : public std::exception { -public: - /** @return "CryptoException" */ - virtual const char * what() const NOEXCEPT { return "CryptoException"; } -}; - -/** @brief An exception for when crypto (ie point decode) has failed. */ -class LengthException : public std::exception { -public: - /** @return "CryptoException" */ - virtual const char * what() const NOEXCEPT { return "LengthException"; } -}; - -/** - * Securely erase contents of memory. - */ -static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); } - -/** Block object */ -class Block { -protected: - unsigned char *data_; - size_t size_; - -public: - /** Empty init */ - inline Block() NOEXCEPT : data_(NULL), size_(0) {} - - /** Init from C string */ - inline Block(const char *data) NOEXCEPT : data_((unsigned char *)data), size_(strlen(data)) {} - - /** Unowned init */ - inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {} - - /** Block from std::string */ - inline Block(const std::string &s) : data_((unsigned char *)GET_DATA(s)), size_(s.size()) {} - - /** Get const data */ - inline const unsigned char *data() const NOEXCEPT { return data_; } - - /** Get the size */ - inline size_t size() const NOEXCEPT { return size_; } - - /** Autocast to const unsigned char * */ - inline operator const unsigned char*() const NOEXCEPT { return data_; } - - /** Convert to C++ string */ - inline std::string get_string() const { - return std::string((const char *)data_,size_); - } - - /** Slice the buffer*/ - inline Block slice(size_t off, size_t length) const throw(LengthException) { - if (off > size() || length > size() - off) - throw LengthException(); - return Block(data()+off, length); - } - - /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */ - inline virtual ~Block() {}; -}; - -class TmpBuffer; - -class Buffer : public Block { -public: - /** Null init */ - inline Buffer() NOEXCEPT : Block() {} - - /** Unowned init */ - inline Buffer(unsigned char *data, size_t size) NOEXCEPT : Block(data,size) {} - - /** Get unconst data */ - inline unsigned char *data() NOEXCEPT { return data_; } - - /** Get const data */ - inline const unsigned char *data() const NOEXCEPT { return data_; } - - /** Autocast to const unsigned char * */ - inline operator const unsigned char*() const NOEXCEPT { return data_; } - - /** Autocast to unsigned char */ - inline operator unsigned char*() NOEXCEPT { return data_; } - - /** Slice the buffer*/ - inline TmpBuffer slice(size_t off, size_t length) throw(LengthException); -}; - -class TmpBuffer : public Buffer { -public: - /** Unowned init */ - inline TmpBuffer(unsigned char *data, size_t size) NOEXCEPT : Buffer(data,size) {} -}; - -TmpBuffer Buffer::slice(size_t off, size_t length) throw(LengthException) { - if (off > size() || length > size() - off) throw LengthException(); - return TmpBuffer(data()+off, length); -} - -/** A self-erasing block of data */ -class SecureBuffer : public Buffer { -public: - /** Null secure block */ - inline SecureBuffer() NOEXCEPT : Buffer() {} - - /** Construct empty from size */ - inline SecureBuffer(size_t size) { - data_ = new unsigned char[size_ = size]; - memset(data_,0,size); - } - - /** Construct from data */ - inline SecureBuffer(const unsigned char *data, size_t size){ - data_ = new unsigned char[size_ = size]; - memcpy(data_, data, size); - } - - /** Copy constructor */ - inline SecureBuffer(const Block ©) : Buffer() { *this = copy; } - - /** Copy-assign constructor */ - inline SecureBuffer& operator=(const Block ©) throw(std::bad_alloc) { - if (© == this) return *this; - clear(); - data_ = new unsigned char[size_ = copy.size()]; - memcpy(data_,copy.data(),size_); - return *this; - } - - /** Copy-assign constructor */ - inline SecureBuffer& operator=(const SecureBuffer ©) throw(std::bad_alloc) { - if (© == this) return *this; - clear(); - data_ = new unsigned char[size_ = copy.size()]; - memcpy(data_,copy.data(),size_); - return *this; - } - - /** Destructor erases data */ - ~SecureBuffer() NOEXCEPT { clear(); } - - /** Clear data */ - inline void clear() NOEXCEPT { - if (data_ == NULL) return; - really_bzero(data_,size_); - delete[] data_; - data_ = NULL; - size_ = 0; - } - -#if __cplusplus >= 201103L - /** Move constructor */ - inline SecureBuffer(SecureBuffer &&move) { *this = move; } - - /** Move non-constructor */ - inline SecureBuffer(Block &&move) { *this = (Block &)move; } - - /** Move-assign constructor. TODO: check that this actually gets used.*/ - inline SecureBuffer& operator=(SecureBuffer &&move) { - clear(); - data_ = move.data_; move.data_ = NULL; - size_ = move.size_; move.size_ = 0; - return *this; - } - - /** C++11-only explicit cast */ - inline explicit operator std::string() const { return get_string(); } -#endif -}; - - -/** @brief Passed to constructors to avoid (conservative) initialization */ -struct NOINIT {}; - -/**@cond internal*/ -/** Forward-declare sponge RNG object */ -class SpongeRng; -/**@endcond*/ - - -/** - * @brief Ed448-Goldilocks/Decaf instantiation of group. - */ -struct Ed448 { - -/** @cond internal */ -class Point; -class Precomputed; -/** @endcond */ - -/** - * @brief A scalar modulo the curve order. - * Supports the usual arithmetic operations, all in constant time. - */ -class Scalar { -public: - /** @brief Size of a serialized element */ - static const size_t SER_BYTES = DECAF_448_SCALAR_BYTES; - - /** @brief access to the underlying scalar object */ - decaf_448_scalar_t s; - - /** @brief Don't initialize. */ - inline Scalar(const NOINIT &) NOEXCEPT {} - - /** @brief Set to an unsigned word */ - inline Scalar(const decaf_word_t w) NOEXCEPT { *this = w; } - - /** @brief Set to a signed word */ - inline Scalar(const int w) NOEXCEPT { *this = w; } - - /** @brief Construct from RNG */ - inline explicit Scalar(SpongeRng &rng) NOEXCEPT; - - /** @brief Construct from decaf_scalar_t object. */ - inline Scalar(const decaf_448_scalar_t &t = decaf_448_scalar_zero) NOEXCEPT { decaf_448_scalar_copy(s,t); } - - /** @brief Copy constructor. */ - inline Scalar(const Scalar &x) NOEXCEPT { *this = x; } - - /** @brief Construct from arbitrary-length little-endian byte sequence. */ - inline Scalar(const Block &buffer) NOEXCEPT { *this = buffer; } - - /** @brief Assignment. */ - inline Scalar& operator=(const Scalar &x) NOEXCEPT { decaf_448_scalar_copy(s,x.s); return *this; } - - /** @brief Assign from unsigned word. */ - inline Scalar& operator=(decaf_word_t w) NOEXCEPT { decaf_448_scalar_set(s,w); return *this; } - - /** @brief Assign from signed int. */ - inline Scalar& operator=(int w) NOEXCEPT { - Scalar t(-(decaf_word_t)INT_MIN); - decaf_448_scalar_set(s,(decaf_word_t)w - (decaf_word_t)INT_MIN); - *this -= t; - return *this; - } - - /** Destructor securely erases the scalar. */ - inline ~Scalar() NOEXCEPT { decaf_448_scalar_destroy(s); } - - /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ - inline Scalar &operator=(const Block &bl) NOEXCEPT { - decaf_448_scalar_decode_long(s,bl.data(),bl.size()); return *this; - } - - /** - * @brief Decode from correct-length little-endian byte sequence. - * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. - */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Scalar &sc, const unsigned char buffer[SER_BYTES] - ) NOEXCEPT { - return decaf_448_scalar_decode(sc.s,buffer); - } - - /** @brief Decode from correct-length little-endian byte sequence in C++ string. */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Scalar &sc, const Block &buffer - ) NOEXCEPT { - if (buffer.size() != SER_BYTES) return DECAF_FAILURE; - return decaf_448_scalar_decode(sc.s,buffer); - } - - /** @brief Encode to fixed-length string */ - inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { - SecureBuffer buf(SER_BYTES); decaf_448_scalar_encode(buf,s); return buf; - } - - /** @brief Encode to fixed-length buffer */ - inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ - decaf_448_scalar_encode(buffer, s); - } - - /** Add. */ - inline Scalar operator+ (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_add(r.s,s,q.s); return r; } - - /** Add to this. */ - inline Scalar &operator+=(const Scalar &q) NOEXCEPT { decaf_448_scalar_add(s,s,q.s); return *this; } - - /** Subtract. */ - inline Scalar operator- (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_sub(r.s,s,q.s); return r; } - - /** Subtract from this. */ - inline Scalar &operator-=(const Scalar &q) NOEXCEPT { decaf_448_scalar_sub(s,s,q.s); return *this; } - - /** Multiply */ - inline Scalar operator* (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_mul(r.s,s,q.s); return r; } - - /** Multiply into this. */ - inline Scalar &operator*=(const Scalar &q) NOEXCEPT { decaf_448_scalar_mul(s,s,q.s); return *this; } - - /** Negate */ - inline Scalar operator- () const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_sub(r.s,decaf_448_scalar_zero,s); return r; } - - /** @brief Invert with Fermat's Little Theorem (slow!). If *this == 0, return 0. */ - inline Scalar inverse() const NOEXCEPT { Scalar r; decaf_448_scalar_invert(r.s,s); return r; } - - /** @brief Divide by inverting q. If q == 0, return 0. */ - inline Scalar operator/ (const Scalar &q) const NOEXCEPT { return *this * q.inverse(); } - - /** @brief Divide by inverting q. If q == 0, return 0. */ - inline Scalar &operator/=(const Scalar &q) NOEXCEPT { return *this *= q.inverse(); } - - /** @brief Compare in constant time */ - inline bool operator!=(const Scalar &q) const NOEXCEPT { return !(*this == q); } - - /** @brief Compare in constant time */ - inline bool operator==(const Scalar &q) const NOEXCEPT { return !!decaf_448_scalar_eq(s,q.s); } - - /** @brief Scalarmul with scalar on left. */ - inline Point operator* (const Point &q) const NOEXCEPT { return q * (*this); } - - /** @brief Scalarmul-precomputed with scalar on left. */ - inline Point operator* (const Precomputed &q) const NOEXCEPT { return q * (*this); } - - /** @brief Direct scalar multiplication. */ - inline SecureBuffer direct_scalarmul( - const Block &in, - decaf_bool_t allow_identity=DECAF_FALSE, - decaf_bool_t short_circuit=DECAF_TRUE - ) const throw(CryptoException) { - SecureBuffer out(/*FIXME Point::*/SER_BYTES); - if (!decaf_448_direct_scalarmul(out, in.data(), s, allow_identity, short_circuit)) - throw CryptoException(); - return out; - } -}; - -/** - * @brief Element of prime-order group. - */ -class Point { -public: - /** @brief Size of a serialized element */ - static const size_t SER_BYTES = DECAF_448_SER_BYTES; - - /** @brief Size of a stegged element */ - static const size_t STEG_BYTES = DECAF_448_SER_BYTES + 8; - - /** @brief Bytes required for hash */ - static const size_t HASH_BYTES = DECAF_448_SER_BYTES; - - /** The c-level object. */ - decaf_448_point_t p; - - /** @brief Don't initialize. */ - inline Point(const NOINIT &) NOEXCEPT {} - - /** @brief Constructor sets to identity by default. */ - inline Point(const decaf_448_point_t &q = decaf_448_point_identity) NOEXCEPT { decaf_448_point_copy(p,q); } - - /** @brief Copy constructor. */ - inline Point(const Point &q) NOEXCEPT { decaf_448_point_copy(p,q.p); } - - /** @brief Assignment. */ - inline Point& operator=(const Point &q) NOEXCEPT { decaf_448_point_copy(p,q.p); return *this; } - - /** @brief Destructor securely erases the point. */ - inline ~Point() NOEXCEPT { decaf_448_point_destroy(p); } - - /** @brief Construct from RNG */ - inline explicit Point(SpongeRng &rng, bool uniform = true) NOEXCEPT; - - /** - * @brief Initialize from C++ fixed-length byte string. - * The all-zero string maps to the identity. - * - * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, - * or was the identity and allow_identity was DECAF_FALSE. - */ - inline explicit Point(const Block &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { - if (!decode(*this,s,allow_identity)) throw CryptoException(); - } - - /** - * @brief Initialize from C fixed-length byte string. - * The all-zero string maps to the identity. - * - * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, - * or was the identity and allow_identity was DECAF_FALSE. - */ - inline explicit Point(const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE) - throw(CryptoException) { if (!decode(*this,buffer,allow_identity)) throw CryptoException(); } - - /** - * @brief Initialize from C fixed-length byte string. - * The all-zero string maps to the identity. - * - * @retval DECAF_SUCCESS the string was successfully decoded. - * @return DECAF_FAILURE the string wasn't the encoding of a point, or was the identity - * and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. - */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Point &p, const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE - ) NOEXCEPT { - return decaf_448_point_decode(p.p,buffer,allow_identity); - } - - /** - * @brief Initialize from C++ fixed-length byte string. - * The all-zero string maps to the identity. - * - * @retval DECAF_SUCCESS the string was successfully decoded. - * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, - * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. - */ - static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( - Point &p, const Block &buffer, decaf_bool_t allow_identity=DECAF_TRUE - ) NOEXCEPT { - if (buffer.size() != SER_BYTES) return DECAF_FAILURE; - return decaf_448_point_decode(p.p,buffer.data(),allow_identity); - } - - /** - * @brief Map uniformly to the curve from a hash buffer. - * The empty or all-zero string maps to the identity, as does the string "\x01". - * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, - * but the buffer will be zero-padded on the right. - */ - static inline Point from_hash ( const Block &s ) NOEXCEPT { - Point p((NOINIT())); p.set_to_hash(s); return p; - } - - /** - * @brief Map to the curve from a hash buffer. - * The empty or all-zero string maps to the identity, as does the string "\x01". - * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, - * but the buffer will be zero-padded on the right. - */ - inline unsigned char set_to_hash( const Block &s ) NOEXCEPT { - if (s.size() < HASH_BYTES) { - SecureBuffer b(HASH_BYTES); - memcpy(b.data(), s.data(), s.size()); - return decaf_448_point_from_hash_nonuniform(p,b); - } else if (s.size() == HASH_BYTES) { - return decaf_448_point_from_hash_nonuniform(p,s); - } else if (s.size() < 2*HASH_BYTES) { - SecureBuffer b(2*HASH_BYTES); - memcpy(b.data(), s.data(), s.size()); - return decaf_448_point_from_hash_uniform(p,b); - } else { - return decaf_448_point_from_hash_uniform(p,s); - } - } - - /** - * @brief Encode to string. The identity encodes to the all-zero string. - */ - inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { - SecureBuffer buffer(SER_BYTES); - decaf_448_point_encode(buffer, p); - return buffer; - } - - /** - * @brief Encode to a C buffer. The identity encodes to all zeros. - */ - inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ - decaf_448_point_encode(buffer, p); - } - - /** @brief Point add. */ - inline Point operator+ (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_add(r.p,p,q.p); return r; } - - /** @brief Point add. */ - inline Point &operator+=(const Point &q) NOEXCEPT { decaf_448_point_add(p,p,q.p); return *this; } - - /** @brief Point subtract. */ - inline Point operator- (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_sub(r.p,p,q.p); return r; } - - /** @brief Point subtract. */ - inline Point &operator-=(const Point &q) NOEXCEPT { decaf_448_point_sub(p,p,q.p); return *this; } - - /** @brief Point negate. */ - inline Point operator- () const NOEXCEPT { Point r((NOINIT())); decaf_448_point_negate(r.p,p); return r; } - - /** @brief Double the point out of place. */ - inline Point times_two () const NOEXCEPT { Point r((NOINIT())); decaf_448_point_double(r.p,p); return r; } - - /** @brief Double the point in place. */ - inline Point &double_in_place() NOEXCEPT { decaf_448_point_double(p,p); return *this; } - - /** @brief Constant-time compare. */ - inline bool operator!=(const Point &q) const NOEXCEPT { return ! decaf_448_point_eq(p,q.p); } - - /** @brief Constant-time compare. */ - inline bool operator==(const Point &q) const NOEXCEPT { return !!decaf_448_point_eq(p,q.p); } - - /** @brief Scalar multiply. */ - inline Point operator* (const Scalar &s) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_scalarmul(r.p,p,s.s); return r; } - - /** @brief Scalar multiply in place. */ - inline Point &operator*=(const Scalar &s) NOEXCEPT { decaf_448_point_scalarmul(p,p,s.s); return *this; } - - /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ - inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } - - /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ - inline Point &operator/=(const Scalar &s) NOEXCEPT { return (*this) *= s.inverse(); } - - /** @brief Validate / sanity check */ - inline bool validate() const NOEXCEPT { return !!decaf_448_point_valid(p); } - - /** @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. */ - static inline Point double_scalarmul ( - const Point &q, const Scalar &qs, const Point &r, const Scalar &rs - ) NOEXCEPT { - Point p((NOINIT())); decaf_448_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; - } - - /** - * @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. - * For those who like their scalars before the point. - */ - static inline Point double_scalarmul ( - const Scalar &qs, const Point &q, const Scalar &rs, const Point &r - ) NOEXCEPT { - Point p((NOINIT())); decaf_448_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; - } - - /** - * @brief Double-scalar multiply: this point by the first scalar and base by the second scalar. - * @warning This function takes variable time, and may leak the scalars (or points, but currently - * it doesn't). - */ - inline Point non_secret_combo_with_base(const Scalar &s, const Scalar &s_base) NOEXCEPT { - Point r((NOINIT())); decaf_448_base_double_scalarmul_non_secret(r.p,s_base.s,p,s.s); return r; - } - - inline Point& debugging_torque_in_place() { - decaf_448_point_debugging_2torque(p,p); - return *this; - } - - inline bool invert_elligator ( - Buffer &buf, unsigned char hint - ) const NOEXCEPT { - unsigned char buf2[2*HASH_BYTES]; - memset(buf2,0,sizeof(buf2)); - memcpy(buf2,buf,(buf.size() > 2*HASH_BYTES) ? 2*HASH_BYTES : buf.size()); - decaf_bool_t ret; - if (buf.size() > HASH_BYTES) { - ret = decaf_448_invert_elligator_uniform(buf2, p, hint); - } else { - ret = decaf_448_invert_elligator_nonuniform(buf2, p, hint); - } - if (buf.size() < HASH_BYTES) { - ret &= decaf_memeq(&buf2[buf.size()], &buf2[HASH_BYTES], HASH_BYTES - buf.size()); - } - memcpy(buf,buf2,(buf.size() < HASH_BYTES) ? buf.size() : HASH_BYTES); - decaf_bzero(buf2,sizeof(buf2)); - return !!ret; - } - - /** @brief Steganographically encode this */ - inline SecureBuffer steg_encode(SpongeRng &rng) const NOEXCEPT; - - /** @brief Return the base point */ - static inline const Point base() NOEXCEPT { return Point(decaf_448_point_base); } - - /** @brief Return the identity point */ - static inline const Point identity() NOEXCEPT { return Point(decaf_448_point_identity); } -}; - -/** - * @brief Precomputed table of points. - * Minor difficulties arise here because the decaf API doesn't expose, as a constant, how big such an object is. - * Therefore we have to call malloc() or friends, but that's probably for the best, because you don't want to - * stack-allocate a 15kiB object anyway. - */ -class Precomputed { -private: - /** @cond internal */ - union { - decaf_448_precomputed_s *mine; - const decaf_448_precomputed_s *yours; - } ours; - bool isMine; - - inline void clear() NOEXCEPT { - if (isMine) { - decaf_448_precomputed_destroy(ours.mine); - free(ours.mine); - ours.yours = decaf_448_precomputed_base; - isMine = false; - } - } - inline void alloc() throw(std::bad_alloc) { - if (isMine) return; - int ret = posix_memalign((void**)&ours.mine, alignof_decaf_448_precomputed_s,sizeof_decaf_448_precomputed_s); - if (ret || !ours.mine) { - isMine = false; - throw std::bad_alloc(); - } - isMine = true; - } - inline const decaf_448_precomputed_s *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } - /** @endcond */ -public: - /** Destructor securely erases the memory. */ - inline ~Precomputed() NOEXCEPT { clear(); } - - /** - * @brief Initialize from underlying type, declared as a reference to prevent - * it from being called with 0, thereby breaking override. - * - * The underlying object must remain valid throughout the lifetime of this one. - * - * By default, initializes to the table for the base point. - * - * @warning The empty initializer makes this equal to base, unlike the empty - * initializer for points which makes this equal to the identity. - */ - inline Precomputed( - const decaf_448_precomputed_s &yours = *decaf_448_precomputed_base - ) NOEXCEPT { - ours.yours = &yours; - isMine = false; - } - - /** - * @brief Assign. This may require an allocation and memcpy. - */ - inline Precomputed &operator=(const Precomputed &it) throw(std::bad_alloc) { - if (this == &it) return *this; - if (it.isMine) { - alloc(); - memcpy(ours.mine,it.ours.mine,sizeof_decaf_448_precomputed_s); - } else { - clear(); - ours.yours = it.ours.yours; - } - isMine = it.isMine; - return *this; - } - - /** - * @brief Initilaize from point. Must allocate memory, and may throw. - */ - inline Precomputed &operator=(const Point &it) throw(std::bad_alloc) { - alloc(); - decaf_448_precompute(ours.mine,it.p); - return *this; - } - - /** - * @brief Copy constructor. - */ - inline Precomputed(const Precomputed &it) throw(std::bad_alloc) : isMine(false) { *this = it; } - - /** - * @brief Constructor which initializes from point. - */ - inline explicit Precomputed(const Point &it) throw(std::bad_alloc) : isMine(false) { *this = it; } - -#if __cplusplus >= 201103L - inline Precomputed &operator=(Precomputed &&it) NOEXCEPT { - if (this == &it) return *this; - clear(); - ours = it.ours; - isMine = it.isMine; - it.isMine = false; - it.ours.yours = decaf_448_precomputed_base; - return *this; - } - inline Precomputed(Precomputed &&it) NOEXCEPT : isMine(false) { *this = it; } -#endif - - /** @brief Fixed base scalarmul. */ - inline Point operator* (const Scalar &s) const NOEXCEPT { Point r; decaf_448_precomputed_scalarmul(r.p,get(),s.s); return r; } - - /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ - inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } - - /** @brief Return the table for the base point. */ - static inline const Precomputed base() NOEXCEPT { return Precomputed(*decaf_448_precomputed_base); } -}; - -}; /* struct Decaf448 */ - -#undef NOEXCEPT -#undef EXPLICIT_CON -#undef GET_DATA -} /* namespace decaf */ - -#endif /* __DECAF_448_HXX__ */ diff --git a/include/decaf_255.h b/include/decaf_255.h new file mode 100644 index 0000000..10e3b74 --- /dev/null +++ b/include/decaf_255.h @@ -0,0 +1,651 @@ +/** + * @file decaf_255.h + * @author Mike Hamburg + * + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * + * @brief A group of prime order p. + * + * The Decaf library implements cryptographic operations on a an elliptic curve + * group of prime order p. It accomplishes this by using a twisted Edwards + * curve (isogenous to Ed255-Goldilocks) and wiping out the cofactor. + * + * The formulas are all complete and have no special cases, except that + * decaf_255_decode can fail because not every sequence of bytes is a valid group + * element. + * + * The formulas contain no data-dependent branches, timing or memory accesses, + * except for decaf_255_base_double_scalarmul_non_secret. + * + * This library may support multiple curves eventually. The Ed255-Goldilocks + * specific identifiers are prefixed with DECAF_255 or decaf_255. + */ +#ifndef __DECAF_255_H__ +#define __DECAF_255_H__ 1 + +#include +#include + +/* Goldilocks' build flags default to hidden and stripping executables. */ +/** @cond internal */ +#if defined(DOXYGEN) && !defined(__attribute__) +#define __attribute__((x)) +#endif +#define API_VIS __attribute__((visibility("default"))) +#define NOINLINE __attribute__((noinline)) +#define WARN_UNUSED __attribute__((warn_unused_result)) +#define NONNULL1 __attribute__((nonnull(1))) +#define NONNULL2 __attribute__((nonnull(1,2))) +#define NONNULL3 __attribute__((nonnull(1,2,3))) +#define NONNULL4 __attribute__((nonnull(1,2,3,4))) +#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) + +/* Internal word types */ +#if (defined(__ILP64__) || defined(__amd64__) || defined(__x86_64__) || (((__UINT_FAST32_MAX__)>>30)>>30)) \ + && !defined(DECAF_FORCE_32_BIT) +#define DECAF_WORD_BITS 64 +typedef uint64_t decaf_word_t, decaf_bool_t; +typedef __uint128_t decaf_dword_t; +#else +#define DECAF_WORD_BITS 32 +typedef uint32_t decaf_word_t, decaf_bool_t; +typedef uint64_t decaf_dword_t; +#endif + +#define DECAF_255_LIMBS (320/DECAF_WORD_BITS) +#define DECAF_255_SCALAR_BITS 252 +#define DECAF_255_SCALAR_LIMBS (256/DECAF_WORD_BITS) + +/** Galois field element internal structure */ +typedef struct gf_s { + decaf_word_t limb[DECAF_255_LIMBS]; +} __attribute__((aligned(32))) gf_s, gf[1]; +/** @endcond */ + +/** Number of bytes in a serialized point. */ +#define DECAF_255_SER_BYTES 32 + +/** Number of bytes in a serialized scalar. */ +#define DECAF_255_SCALAR_BYTES 32 + +/** Twisted Edwards (-1,d-1) extended homogeneous coordinates */ +typedef struct decaf_255_point_s { /**@cond internal*/gf x,y,z,t;/**@endcond*/ } decaf_255_point_t[1]; + +/** Precomputed table based on a point. Can be trivial implementation. */ +struct decaf_255_precomputed_s; + +/** Precomputed table based on a point. Can be trivial implementation. */ +typedef struct decaf_255_precomputed_s decaf_255_precomputed_s; + +/** Size and alignment of precomputed point tables. */ +extern const size_t sizeof_decaf_255_precomputed_s API_VIS, alignof_decaf_255_precomputed_s API_VIS; + +/** Scalar is stored packed, because we don't need the speed. */ +typedef struct decaf_255_scalar_s { + /** @cond internal */ + decaf_word_t limb[DECAF_255_SCALAR_LIMBS]; + /** @endcond */ +} decaf_255_scalar_t[1]; + +/** DECAF_TRUE = -1 so that DECAF_TRUE & x = x */ +static const decaf_bool_t DECAF_TRUE = -(decaf_bool_t)1, DECAF_FALSE = 0; + +/** NB Success is -1, failure is 0. TODO: see if people would rather the reverse. */ +static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, + DECAF_FAILURE = 0 /*DECAF_FALSE*/; + +/** A scalar equal to 1. */ +extern const decaf_255_scalar_t decaf_255_scalar_one API_VIS; + +/** A scalar equal to 0. */ +extern const decaf_255_scalar_t decaf_255_scalar_zero API_VIS; + +/** The identity point on the curve. */ +extern const decaf_255_point_t decaf_255_point_identity API_VIS; + +/** + * An arbitrarily chosen base point on the curve. + * Equal to Ed255-Goldilocks base point defined by DJB, except of course that + * it's on the twist in this case. TODO: choose a base point with nice encoding? + */ +extern const decaf_255_point_t decaf_255_point_base API_VIS; + +/** Precomputed table for the base point on the curve. */ +extern const struct decaf_255_precomputed_s *decaf_255_precomputed_base API_VIS; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Read a scalar from wire format or from bytes. + * + * @param [in] ser Serialized form of a scalar. + * @param [out] out Deserialized form. + * + * @retval DECAF_SUCCESS The scalar was correctly encoded. + * @retval DECAF_FAILURE The scalar was greater than the modulus, + * and has been reduced modulo that modulus. + */ +decaf_bool_t decaf_255_scalar_decode ( + decaf_255_scalar_t out, + const unsigned char ser[DECAF_255_SCALAR_BYTES] +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Read a scalar from wire format or from bytes. Reduces mod + * scalar prime. + * + * @param [in] ser Serialized form of a scalar. + * @param [in] ser_len Length of serialized form. + * @param [out] out Deserialized form. + */ +void decaf_255_scalar_decode_long ( + decaf_255_scalar_t out, + const unsigned char *ser, + size_t ser_len +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Serialize a scalar to wire format. + * + * @param [out] ser Serialized form of a scalar. + * @param [in] s Deserialized scalar. + */ +void decaf_255_scalar_encode ( + unsigned char ser[DECAF_255_SCALAR_BYTES], + const decaf_255_scalar_t s +) API_VIS NONNULL2 NOINLINE NOINLINE; + +/** + * @brief Add two scalars. The scalars may use the same memory. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @param [out] out a+b. + */ +void decaf_255_scalar_add ( + decaf_255_scalar_t out, + const decaf_255_scalar_t a, + const decaf_255_scalar_t b +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Compare two scalars. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @retval DECAF_TRUE The scalars are equal. + * @retval DECAF_FALSE The scalars are not equal. + */ +decaf_bool_t decaf_255_scalar_eq ( + const decaf_255_scalar_t a, + const decaf_255_scalar_t b +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Subtract two scalars. The scalars may use the same memory. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @param [out] out a-b. + */ +void decaf_255_scalar_sub ( + decaf_255_scalar_t out, + const decaf_255_scalar_t a, + const decaf_255_scalar_t b +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Multiply two scalars. The scalars may use the same memory. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @param [out] out a*b. + */ +void decaf_255_scalar_mul ( + decaf_255_scalar_t out, + const decaf_255_scalar_t a, + const decaf_255_scalar_t b +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Invert a scalar. When passed zero, return 0. The input and output may alias. + * @param [in] a A scalar. + * @param [out] out 1/a. + * @return DECAF_TRUE The input is nonzero. + */ +decaf_bool_t decaf_255_scalar_invert ( + decaf_255_scalar_t out, + const decaf_255_scalar_t a +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Copy a scalar. The scalars may use the same memory, in which + * case this function does nothing. + * @param [in] a A scalar. + * @param [out] out Will become a copy of a. + */ +static inline void NONNULL2 decaf_255_scalar_copy ( + decaf_255_scalar_t out, + const decaf_255_scalar_t a +) { + *out = *a; +} + +/** + * @brief Set a scalar to an integer. + * @param [in] a An integer. + * @param [out] out Will become equal to a. + * @todo Make inline? + */ +void decaf_255_scalar_set( + decaf_255_scalar_t out, + decaf_word_t a +) API_VIS NONNULL1; + +/** + * @brief Encode a point as a sequence of bytes. + * + * @param [out] ser The byte representation of the point. + * @param [in] pt The point to encode. + */ +void decaf_255_point_encode ( + uint8_t ser[DECAF_255_SER_BYTES], + const decaf_255_point_t pt +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Decode a point from a sequence of bytes. + * + * Every point has a unique encoding, so not every + * sequence of bytes is a valid encoding. If an invalid + * encoding is given, the output is undefined. + * + * @param [out] pt The decoded point. + * @param [in] ser The serialized version of the point. + * @param [in] allow_identity DECAF_TRUE if the identity is a legal input. + * @retval DECAF_SUCCESS The decoding succeeded. + * @retval DECAF_FAILURE The decoding didn't succeed, because + * ser does not represent a point. + */ +decaf_bool_t decaf_255_point_decode ( + decaf_255_point_t pt, + const uint8_t ser[DECAF_255_SER_BYTES], + decaf_bool_t allow_identity +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Copy a point. The input and output may alias, + * in which case this function does nothing. + * + * @param [out] a A copy of the point. + * @param [in] b Any point. + */ +static inline void NONNULL2 decaf_255_point_copy ( + decaf_255_point_t a, + const decaf_255_point_t b +) { + *a=*b; +} + +/** + * @brief Test whether two points are equal. If yes, return + * DECAF_TRUE, else return DECAF_FALSE. + * + * @param [in] a A point. + * @param [in] b Another point. + * @retval DECAF_TRUE The points are equal. + * @retval DECAF_FALSE The points are not equal. + */ +decaf_bool_t decaf_255_point_eq ( + const decaf_255_point_t a, + const decaf_255_point_t b +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Add two points to produce a third point. The + * input points and output point can be pointers to the same + * memory. + * + * @param [out] sum The sum a+b. + * @param [in] a An addend. + * @param [in] b An addend. + */ +void decaf_255_point_add ( + decaf_255_point_t sum, + const decaf_255_point_t a, + const decaf_255_point_t b +) API_VIS NONNULL3; + +/** + * @brief Double a point. Equivalent to + * decaf_255_point_add(two_a,a,a), but potentially faster. + * + * @param [out] two_a The sum a+a. + * @param [in] a A point. + */ +void decaf_255_point_double ( + decaf_255_point_t two_a, + const decaf_255_point_t a +) API_VIS NONNULL2; + +/** + * @brief Subtract two points to produce a third point. The + * input points and output point can be pointers to the same + * memory. + * + * @param [out] diff The difference a-b. + * @param [in] a The minuend. + * @param [in] b The subtrahend. + */ +void decaf_255_point_sub ( + decaf_255_point_t diff, + const decaf_255_point_t a, + const decaf_255_point_t b +) API_VIS NONNULL3; + +/** + * @brief Negate a point to produce another point. The input + * and output points can use the same memory. + * + * @param [out] nega The negated input point + * @param [in] a The input point. + */ +void decaf_255_point_negate ( + decaf_255_point_t nega, + const decaf_255_point_t a +) API_VIS NONNULL2; + +/** + * @brief Multiply a base point by a scalar: scaled = scalar*base. + * + * @param [out] scaled The scaled point base*scalar + * @param [in] base The point to be scaled. + * @param [in] scalar The scalar to multiply by. + */ +void decaf_255_point_scalarmul ( + decaf_255_point_t scaled, + const decaf_255_point_t base, + const decaf_255_scalar_t scalar +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Multiply a base point by a scalar: scaled = scalar*base. + * This function operates directly on serialized forms. + * + * @warning This function is experimental. It may not be supported + * long-term. + * + * @param [out] scaled The scaled point base*scalar + * @param [in] base The point to be scaled. + * @param [in] scalar The scalar to multiply by. + * @param [in] allow_identity Allow the input to be the identity. + * @param [in] short_circuit Allow a fast return if the input is illegal. + * + * @retval DECAF_SUCCESS The scalarmul succeeded. + * @retval DECAF_FAILURE The scalarmul didn't succeed, because + * base does not represent a point. + */ +decaf_bool_t decaf_255_direct_scalarmul ( + uint8_t scaled[DECAF_255_SER_BYTES], + const uint8_t base[DECAF_255_SER_BYTES], + const decaf_255_scalar_t scalar, + decaf_bool_t allow_identity, + decaf_bool_t short_circuit +) API_VIS NONNULL3 WARN_UNUSED NOINLINE; + +/** + * @brief Precompute a table for fast scalar multiplication. + * Some implementations do not include precomputed points; for + * those implementations, this implementation simply copies the + * point. + * + * @param [out] a A precomputed table of multiples of the point. + * @param [in] b Any point. + */ +void decaf_255_precompute ( + decaf_255_precomputed_s *a, + const decaf_255_point_t b +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Multiply a precomputed base point by a scalar: + * scaled = scalar*base. + * Some implementations do not include precomputed points; for + * those implementations, this function is the same as + * decaf_255_point_scalarmul + * + * @param [out] scaled The scaled point base*scalar + * @param [in] base The point to be scaled. + * @param [in] scalar The scalar to multiply by. + * + * @todo precomputed dsmul? const or variable time? + */ +void decaf_255_precomputed_scalarmul ( + decaf_255_point_t scaled, + const decaf_255_precomputed_s *base, + const decaf_255_scalar_t scalar +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Multiply two base points by two scalars: + * scaled = scalar1*base1 + scalar2*base2. + * + * Equivalent to two calls to decaf_255_point_scalarmul, but may be + * faster. + * + * @param [out] combo The linear combination scalar1*base1 + scalar2*base2. + * @param [in] base1 A first point to be scaled. + * @param [in] scalar1 A first scalar to multiply by. + * @param [in] base2 A second point to be scaled. + * @param [in] scalar2 A second scalar to multiply by. + */ +void decaf_255_point_double_scalarmul ( + decaf_255_point_t combo, + const decaf_255_point_t base1, + const decaf_255_scalar_t scalar1, + const decaf_255_point_t base2, + const decaf_255_scalar_t scalar2 +) API_VIS NONNULL5 NOINLINE; + +/** + * @brief Multiply two base points by two scalars: + * scaled = scalar1*decaf_255_point_base + scalar2*base2. + * + * Otherwise equivalent to decaf_255_point_double_scalarmul, but may be + * faster at the expense of being variable time. + * + * @param [out] combo The linear combination scalar1*base + scalar2*base2. + * @param [in] scalar1 A first scalar to multiply by. + * @param [in] base2 A second point to be scaled. + * @param [in] scalar2 A second scalar to multiply by. + * + * @warning: This function takes variable time, and may leak the scalars + * used. It is designed for signature verification. + */ +void decaf_255_base_double_scalarmul_non_secret ( + decaf_255_point_t combo, + const decaf_255_scalar_t scalar1, + const decaf_255_point_t base2, + const decaf_255_scalar_t scalar2 +) API_VIS NONNULL4 NOINLINE; + +/** + * @brief Test that a point is valid, for debugging purposes. + * + * @param [in] toTest The point to test. + * @retval DECAF_TRUE The point is valid. + * @retval DECAF_FALSE The point is invalid. + */ +decaf_bool_t decaf_255_point_valid ( + const decaf_255_point_t toTest +) API_VIS WARN_UNUSED NONNULL1 NOINLINE; + +/** + * @brief 2-torque a point, for debugging purposes. + * + * @param [out] q The point to torque. + * @param [in] p The point to torque. + */ +void decaf_255_point_debugging_2torque ( + decaf_255_point_t q, + const decaf_255_point_t p +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Almost-Elligator-like hash to curve. + * + * Call this function with the output of a hash to make a hash to the curve. + * + * This function runs Elligator2 on the decaf_255 Jacobi quartic model. It then + * uses the isogeny to put the result in twisted Edwards form. As a result, + * it is safe (cannot produce points of order 4), and would be compatible with + * hypothetical other implementations of Decaf using a Montgomery or untwisted + * Edwards model. + * + * Unlike Elligator, this function may be up to 4:1 on [0,(p-1)/2]: + * A factor of 2 due to the isogeny. + * A factor of 2 because we quotient out the 2-torsion. + * + * This makes it about 8:1 overall. + * + * Negating the input (mod q) results in the same point. Inverting the input + * (mod q) results in the negative point. This is the same as Elligator. + * + * This function isn't quite indifferentiable from a random oracle. + * However, it is suitable for many protocols, including SPEKE and SPAKE2 EE. + * Furthermore, calling it twice with independent seeds and adding the results + * is indifferentiable from a random oracle. + * + * @param [in] hashed_data Output of some hash function. + * @param [out] pt The data hashed to the curve. + * @return A "hint" value which can be used to help invert the encoding. + */ +unsigned char +decaf_255_point_from_hash_nonuniform ( + decaf_255_point_t pt, + const unsigned char hashed_data[DECAF_255_SER_BYTES] +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Inverse of elligator-like hash to curve. + * + * This function writes to the buffer, to make it so that + * decaf_255_point_from_hash_nonuniform(buffer) = pt,hint + * if possible. + * + * @param [out] recovered_hash Encoded data. + * @param [in] pt The point to encode. + * @param [in] hint The hint value returned from + * decaf_255_point_from_hash_nonuniform. + * + * @retval DECAF_SUCCESS The inverse succeeded. + * @retval DECAF_FAILURE The pt isn't the image of + * decaf_255_point_from_hash_nonuniform with the given hint. + * + * @warning The hinting system is subject to change, especially in corner cases. + * @warning FIXME The hinting system doesn't work for certain inputs which have many 0xFF. + */ +decaf_bool_t +decaf_255_invert_elligator_nonuniform ( + unsigned char recovered_hash[DECAF_255_SER_BYTES], + const decaf_255_point_t pt, + unsigned char hint +) API_VIS NONNULL2 NOINLINE WARN_UNUSED; + +/** + * @brief Inverse of elligator-like hash to curve, uniform. + * + * This function modifies the first DECAF_255_SER_BYTES of the + * buffer, to make it so that + * decaf_255_point_from_hash_uniform(buffer) = pt,hint + * if possible. + * + * @param [out] recovered_hash Encoded data. + * @param [in] pt The point to encode. + * @param [in] hint The hint value returned from + * decaf_255_point_from_hash_nonuniform. + * + * @retval DECAF_SUCCESS The inverse succeeded. + * @retval DECAF_FAILURE The pt isn't the image of + * decaf_255_point_from_hash_uniform with the given hint. + * + * @warning The hinting system is subject to change, especially in corner cases. + * @warning FIXME The hinting system doesn't work for certain inputs which have many 0xFF. + */ +decaf_bool_t +decaf_255_invert_elligator_uniform ( + unsigned char recovered_hash[2*DECAF_255_SER_BYTES], + const decaf_255_point_t pt, + unsigned char hint +) API_VIS NONNULL2 NOINLINE WARN_UNUSED; + +/** + * @brief Indifferentiable hash function encoding to curve. + * + * Equivalent to calling decaf_255_point_from_hash_nonuniform twice and adding. + * + * @param [in] hashed_data Output of some hash function. + * @param [out] pt The data hashed to the curve. + * @return A "hint" value which can be used to help invert the encoding. + */ +unsigned char decaf_255_point_from_hash_uniform ( + decaf_255_point_t pt, + const unsigned char hashed_data[2*DECAF_255_SER_BYTES] +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Overwrite data with zeros. Uses memset_s if available. + */ +void decaf_bzero ( + void *data, + size_t size +) NONNULL1 API_VIS NOINLINE; + +/** + * @brief Compare two buffers, returning DECAF_TRUE if they are equal. + */ +decaf_bool_t decaf_memeq ( + const void *data1, + const void *data2, + size_t size +) NONNULL2 WARN_UNUSED API_VIS NOINLINE; + +/** + * @brief Overwrite scalar with zeros. + */ +void decaf_255_scalar_destroy ( + decaf_255_scalar_t scalar +) NONNULL1 API_VIS; + +/** + * @brief Overwrite point with zeros. + * @todo Use this internally. + */ +void decaf_255_point_destroy ( + decaf_255_point_t point +) NONNULL1 API_VIS; + +/** + * @brief Overwrite point with zeros. + * @todo Use this internally. + */ +void decaf_255_precomputed_destroy ( + decaf_255_precomputed_s *pre +) NONNULL1 API_VIS; + +/* TODO: functions to invert point_from_hash?? */ + +#undef API_VIS +#undef WARN_UNUSED +#undef NOINLINE +#undef NONNULL1 +#undef NONNULL2 +#undef NONNULL3 +#undef NONNULL4 +#undef NONNULL5 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __DECAF_255_H__ */ diff --git a/include/decaf_255.hxx b/include/decaf_255.hxx new file mode 100644 index 0000000..22d8e70 --- /dev/null +++ b/include/decaf_255.hxx @@ -0,0 +1,738 @@ +/** + * @file decaf_255.hxx + * @author Mike Hamburg + * + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * + * @brief A group of prime order p, C++ wrapper. + * + * The Decaf library implements cryptographic operations on a an elliptic curve + * group of prime order p. It accomplishes this by using a twisted Edwards + * curve (isogenous to Ed255-Goldilocks) and wiping out the cofactor. + * + * The formulas are all complete and have no special cases, except that + * decaf_255_decode can fail because not every sequence of bytes is a valid group + * element. + * + * The formulas contain no data-dependent branches, timing or memory accesses, + * except for decaf_255_base_double_scalarmul_non_secret. + */ +#ifndef __DECAF_255_HXX__ +#define __DECAF_255_HXX__ 1 + +/** This code uses posix_memalign. */ +#define _XOPEN_SOURCE 600 +#include +#include /* for memcpy */ + +#include "decaf.h" +#include +#include +#include + +/* TODO: This is incomplete */ +/* TODO: attribute nonnull */ + +/** @cond internal */ +#if __cplusplus >= 201103L +#define NOEXCEPT noexcept +#define EXPLICIT_CON explicit +#define GET_DATA(str) ((const unsigned char *)&(str)[0]) +#else +#define NOEXCEPT throw() +#define EXPLICIT_CON +#define GET_DATA(str) ((const unsigned char *)((str).data())) +#endif +/** @endcond */ + +namespace decaf { + +/** @brief An exception for when crypto (ie point decode) has failed. */ +class CryptoException : public std::exception { +public: + /** @return "CryptoException" */ + virtual const char * what() const NOEXCEPT { return "CryptoException"; } +}; + +/** @brief An exception for when crypto (ie point decode) has failed. */ +class LengthException : public std::exception { +public: + /** @return "CryptoException" */ + virtual const char * what() const NOEXCEPT { return "LengthException"; } +}; + +/** + * Securely erase contents of memory. + */ +static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); } + +/** Block object */ +class Block { +protected: + unsigned char *data_; + size_t size_; + +public: + /** Empty init */ + inline Block() NOEXCEPT : data_(NULL), size_(0) {} + + /** Init from C string */ + inline Block(const char *data) NOEXCEPT : data_((unsigned char *)data), size_(strlen(data)) {} + + /** Unowned init */ + inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {} + + /** Block from std::string */ + inline Block(const std::string &s) : data_((unsigned char *)GET_DATA(s)), size_(s.size()) {} + + /** Get const data */ + inline const unsigned char *data() const NOEXCEPT { return data_; } + + /** Get the size */ + inline size_t size() const NOEXCEPT { return size_; } + + /** Autocast to const unsigned char * */ + inline operator const unsigned char*() const NOEXCEPT { return data_; } + + /** Convert to C++ string */ + inline std::string get_string() const { + return std::string((const char *)data_,size_); + } + + /** Slice the buffer*/ + inline Block slice(size_t off, size_t length) const throw(LengthException) { + if (off > size() || length > size() - off) + throw LengthException(); + return Block(data()+off, length); + } + + /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */ + inline virtual ~Block() {}; +}; + +class TmpBuffer; + +class Buffer : public Block { +public: + /** Null init */ + inline Buffer() NOEXCEPT : Block() {} + + /** Unowned init */ + inline Buffer(unsigned char *data, size_t size) NOEXCEPT : Block(data,size) {} + + /** Get unconst data */ + inline unsigned char *data() NOEXCEPT { return data_; } + + /** Get const data */ + inline const unsigned char *data() const NOEXCEPT { return data_; } + + /** Autocast to const unsigned char * */ + inline operator const unsigned char*() const NOEXCEPT { return data_; } + + /** Autocast to unsigned char */ + inline operator unsigned char*() NOEXCEPT { return data_; } + + /** Slice the buffer*/ + inline TmpBuffer slice(size_t off, size_t length) throw(LengthException); +}; + +class TmpBuffer : public Buffer { +public: + /** Unowned init */ + inline TmpBuffer(unsigned char *data, size_t size) NOEXCEPT : Buffer(data,size) {} +}; + +TmpBuffer Buffer::slice(size_t off, size_t length) throw(LengthException) { + if (off > size() || length > size() - off) throw LengthException(); + return TmpBuffer(data()+off, length); +} + +/** A self-erasing block of data */ +class SecureBuffer : public Buffer { +public: + /** Null secure block */ + inline SecureBuffer() NOEXCEPT : Buffer() {} + + /** Construct empty from size */ + inline SecureBuffer(size_t size) { + data_ = new unsigned char[size_ = size]; + memset(data_,0,size); + } + + /** Construct from data */ + inline SecureBuffer(const unsigned char *data, size_t size){ + data_ = new unsigned char[size_ = size]; + memcpy(data_, data, size); + } + + /** Copy constructor */ + inline SecureBuffer(const Block ©) : Buffer() { *this = copy; } + + /** Copy-assign constructor */ + inline SecureBuffer& operator=(const Block ©) throw(std::bad_alloc) { + if (© == this) return *this; + clear(); + data_ = new unsigned char[size_ = copy.size()]; + memcpy(data_,copy.data(),size_); + return *this; + } + + /** Copy-assign constructor */ + inline SecureBuffer& operator=(const SecureBuffer ©) throw(std::bad_alloc) { + if (© == this) return *this; + clear(); + data_ = new unsigned char[size_ = copy.size()]; + memcpy(data_,copy.data(),size_); + return *this; + } + + /** Destructor erases data */ + ~SecureBuffer() NOEXCEPT { clear(); } + + /** Clear data */ + inline void clear() NOEXCEPT { + if (data_ == NULL) return; + really_bzero(data_,size_); + delete[] data_; + data_ = NULL; + size_ = 0; + } + +#if __cplusplus >= 201103L + /** Move constructor */ + inline SecureBuffer(SecureBuffer &&move) { *this = move; } + + /** Move non-constructor */ + inline SecureBuffer(Block &&move) { *this = (Block &)move; } + + /** Move-assign constructor. TODO: check that this actually gets used.*/ + inline SecureBuffer& operator=(SecureBuffer &&move) { + clear(); + data_ = move.data_; move.data_ = NULL; + size_ = move.size_; move.size_ = 0; + return *this; + } + + /** C++11-only explicit cast */ + inline explicit operator std::string() const { return get_string(); } +#endif +}; + + +/** @brief Passed to constructors to avoid (conservative) initialization */ +struct NOINIT {}; + +/**@cond internal*/ +/** Forward-declare sponge RNG object */ +class SpongeRng; +/**@endcond*/ + + +/** + * @brief Ed255-Goldilocks/Decaf instantiation of group. + */ +struct Ed255 { + +/** @cond internal */ +class Point; +class Precomputed; +/** @endcond */ + +/** + * @brief A scalar modulo the curve order. + * Supports the usual arithmetic operations, all in constant time. + */ +class Scalar { +public: + /** @brief Size of a serialized element */ + static const size_t SER_BYTES = DECAF_255_SCALAR_BYTES; + + /** @brief access to the underlying scalar object */ + decaf_255_scalar_t s; + + /** @brief Don't initialize. */ + inline Scalar(const NOINIT &) NOEXCEPT {} + + /** @brief Set to an unsigned word */ + inline Scalar(const decaf_word_t w) NOEXCEPT { *this = w; } + + /** @brief Set to a signed word */ + inline Scalar(const int w) NOEXCEPT { *this = w; } + + /** @brief Construct from RNG */ + inline explicit Scalar(SpongeRng &rng) NOEXCEPT; + + /** @brief Construct from decaf_scalar_t object. */ + inline Scalar(const decaf_255_scalar_t &t = decaf_255_scalar_zero) NOEXCEPT { decaf_255_scalar_copy(s,t); } + + /** @brief Copy constructor. */ + inline Scalar(const Scalar &x) NOEXCEPT { *this = x; } + + /** @brief Construct from arbitrary-length little-endian byte sequence. */ + inline Scalar(const Block &buffer) NOEXCEPT { *this = buffer; } + + /** @brief Assignment. */ + inline Scalar& operator=(const Scalar &x) NOEXCEPT { decaf_255_scalar_copy(s,x.s); return *this; } + + /** @brief Assign from unsigned word. */ + inline Scalar& operator=(decaf_word_t w) NOEXCEPT { decaf_255_scalar_set(s,w); return *this; } + + /** @brief Assign from signed int. */ + inline Scalar& operator=(int w) NOEXCEPT { + Scalar t(-(decaf_word_t)INT_MIN); + decaf_255_scalar_set(s,(decaf_word_t)w - (decaf_word_t)INT_MIN); + *this -= t; + return *this; + } + + /** Destructor securely erases the scalar. */ + inline ~Scalar() NOEXCEPT { decaf_255_scalar_destroy(s); } + + /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ + inline Scalar &operator=(const Block &bl) NOEXCEPT { + decaf_255_scalar_decode_long(s,bl.data(),bl.size()); return *this; + } + + /** + * @brief Decode from correct-length little-endian byte sequence. + * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Scalar &sc, const unsigned char buffer[SER_BYTES] + ) NOEXCEPT { + return decaf_255_scalar_decode(sc.s,buffer); + } + + /** @brief Decode from correct-length little-endian byte sequence in C++ string. */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Scalar &sc, const Block &buffer + ) NOEXCEPT { + if (buffer.size() != SER_BYTES) return DECAF_FAILURE; + return decaf_255_scalar_decode(sc.s,buffer); + } + + /** @brief Encode to fixed-length string */ + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buf(SER_BYTES); decaf_255_scalar_encode(buf,s); return buf; + } + + /** @brief Encode to fixed-length buffer */ + inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ + decaf_255_scalar_encode(buffer, s); + } + + /** Add. */ + inline Scalar operator+ (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_255_scalar_add(r.s,s,q.s); return r; } + + /** Add to this. */ + inline Scalar &operator+=(const Scalar &q) NOEXCEPT { decaf_255_scalar_add(s,s,q.s); return *this; } + + /** Subtract. */ + inline Scalar operator- (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_255_scalar_sub(r.s,s,q.s); return r; } + + /** Subtract from this. */ + inline Scalar &operator-=(const Scalar &q) NOEXCEPT { decaf_255_scalar_sub(s,s,q.s); return *this; } + + /** Multiply */ + inline Scalar operator* (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_255_scalar_mul(r.s,s,q.s); return r; } + + /** Multiply into this. */ + inline Scalar &operator*=(const Scalar &q) NOEXCEPT { decaf_255_scalar_mul(s,s,q.s); return *this; } + + /** Negate */ + inline Scalar operator- () const NOEXCEPT { Scalar r((NOINIT())); decaf_255_scalar_sub(r.s,decaf_255_scalar_zero,s); return r; } + + /** @brief Invert with Fermat's Little Theorem (slow!). If *this == 0, return 0. */ + inline Scalar inverse() const NOEXCEPT { Scalar r; decaf_255_scalar_invert(r.s,s); return r; } + + /** @brief Divide by inverting q. If q == 0, return 0. */ + inline Scalar operator/ (const Scalar &q) const NOEXCEPT { return *this * q.inverse(); } + + /** @brief Divide by inverting q. If q == 0, return 0. */ + inline Scalar &operator/=(const Scalar &q) NOEXCEPT { return *this *= q.inverse(); } + + /** @brief Compare in constant time */ + inline bool operator!=(const Scalar &q) const NOEXCEPT { return !(*this == q); } + + /** @brief Compare in constant time */ + inline bool operator==(const Scalar &q) const NOEXCEPT { return !!decaf_255_scalar_eq(s,q.s); } + + /** @brief Scalarmul with scalar on left. */ + inline Point operator* (const Point &q) const NOEXCEPT { return q * (*this); } + + /** @brief Scalarmul-precomputed with scalar on left. */ + inline Point operator* (const Precomputed &q) const NOEXCEPT { return q * (*this); } + + /** @brief Direct scalar multiplication. */ + inline SecureBuffer direct_scalarmul( + const Block &in, + decaf_bool_t allow_identity=DECAF_FALSE, + decaf_bool_t short_circuit=DECAF_TRUE + ) const throw(CryptoException) { + SecureBuffer out(/*FIXME Point::*/SER_BYTES); + if (!decaf_255_direct_scalarmul(out, in.data(), s, allow_identity, short_circuit)) + throw CryptoException(); + return out; + } +}; + +/** + * @brief Element of prime-order group. + */ +class Point { +public: + /** @brief Size of a serialized element */ + static const size_t SER_BYTES = DECAF_255_SER_BYTES; + + /** @brief Size of a stegged element */ + static const size_t STEG_BYTES = DECAF_255_SER_BYTES + 8; + + /** @brief Bytes required for hash */ + static const size_t HASH_BYTES = DECAF_255_SER_BYTES; + + /** The c-level object. */ + decaf_255_point_t p; + + /** @brief Don't initialize. */ + inline Point(const NOINIT &) NOEXCEPT {} + + /** @brief Constructor sets to identity by default. */ + inline Point(const decaf_255_point_t &q = decaf_255_point_identity) NOEXCEPT { decaf_255_point_copy(p,q); } + + /** @brief Copy constructor. */ + inline Point(const Point &q) NOEXCEPT { decaf_255_point_copy(p,q.p); } + + /** @brief Assignment. */ + inline Point& operator=(const Point &q) NOEXCEPT { decaf_255_point_copy(p,q.p); return *this; } + + /** @brief Destructor securely erases the point. */ + inline ~Point() NOEXCEPT { decaf_255_point_destroy(p); } + + /** @brief Construct from RNG */ + inline explicit Point(SpongeRng &rng, bool uniform = true) NOEXCEPT; + + /** + * @brief Initialize from C++ fixed-length byte string. + * The all-zero string maps to the identity. + * + * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. + */ + inline explicit Point(const Block &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { + if (!decode(*this,s,allow_identity)) throw CryptoException(); + } + + /** + * @brief Initialize from C fixed-length byte string. + * The all-zero string maps to the identity. + * + * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. + */ + inline explicit Point(const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE) + throw(CryptoException) { if (!decode(*this,buffer,allow_identity)) throw CryptoException(); } + + /** + * @brief Initialize from C fixed-length byte string. + * The all-zero string maps to the identity. + * + * @retval DECAF_SUCCESS the string was successfully decoded. + * @return DECAF_FAILURE the string wasn't the encoding of a point, or was the identity + * and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Point &p, const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE + ) NOEXCEPT { + return decaf_255_point_decode(p.p,buffer,allow_identity); + } + + /** + * @brief Initialize from C++ fixed-length byte string. + * The all-zero string maps to the identity. + * + * @retval DECAF_SUCCESS the string was successfully decoded. + * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Point &p, const Block &buffer, decaf_bool_t allow_identity=DECAF_TRUE + ) NOEXCEPT { + if (buffer.size() != SER_BYTES) return DECAF_FAILURE; + return decaf_255_point_decode(p.p,buffer.data(),allow_identity); + } + + /** + * @brief Map uniformly to the curve from a hash buffer. + * The empty or all-zero string maps to the identity, as does the string "\x01". + * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, + * but the buffer will be zero-padded on the right. + */ + static inline Point from_hash ( const Block &s ) NOEXCEPT { + Point p((NOINIT())); p.set_to_hash(s); return p; + } + + /** + * @brief Map to the curve from a hash buffer. + * The empty or all-zero string maps to the identity, as does the string "\x01". + * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, + * but the buffer will be zero-padded on the right. + */ + inline unsigned char set_to_hash( const Block &s ) NOEXCEPT { + if (s.size() < HASH_BYTES) { + SecureBuffer b(HASH_BYTES); + memcpy(b.data(), s.data(), s.size()); + return decaf_255_point_from_hash_nonuniform(p,b); + } else if (s.size() == HASH_BYTES) { + return decaf_255_point_from_hash_nonuniform(p,s); + } else if (s.size() < 2*HASH_BYTES) { + SecureBuffer b(2*HASH_BYTES); + memcpy(b.data(), s.data(), s.size()); + return decaf_255_point_from_hash_uniform(p,b); + } else { + return decaf_255_point_from_hash_uniform(p,s); + } + } + + /** + * @brief Encode to string. The identity encodes to the all-zero string. + */ + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buffer(SER_BYTES); + decaf_255_point_encode(buffer, p); + return buffer; + } + + /** + * @brief Encode to a C buffer. The identity encodes to all zeros. + */ + inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ + decaf_255_point_encode(buffer, p); + } + + /** @brief Point add. */ + inline Point operator+ (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_255_point_add(r.p,p,q.p); return r; } + + /** @brief Point add. */ + inline Point &operator+=(const Point &q) NOEXCEPT { decaf_255_point_add(p,p,q.p); return *this; } + + /** @brief Point subtract. */ + inline Point operator- (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_255_point_sub(r.p,p,q.p); return r; } + + /** @brief Point subtract. */ + inline Point &operator-=(const Point &q) NOEXCEPT { decaf_255_point_sub(p,p,q.p); return *this; } + + /** @brief Point negate. */ + inline Point operator- () const NOEXCEPT { Point r((NOINIT())); decaf_255_point_negate(r.p,p); return r; } + + /** @brief Double the point out of place. */ + inline Point times_two () const NOEXCEPT { Point r((NOINIT())); decaf_255_point_double(r.p,p); return r; } + + /** @brief Double the point in place. */ + inline Point &double_in_place() NOEXCEPT { decaf_255_point_double(p,p); return *this; } + + /** @brief Constant-time compare. */ + inline bool operator!=(const Point &q) const NOEXCEPT { return ! decaf_255_point_eq(p,q.p); } + + /** @brief Constant-time compare. */ + inline bool operator==(const Point &q) const NOEXCEPT { return !!decaf_255_point_eq(p,q.p); } + + /** @brief Scalar multiply. */ + inline Point operator* (const Scalar &s) const NOEXCEPT { Point r((NOINIT())); decaf_255_point_scalarmul(r.p,p,s.s); return r; } + + /** @brief Scalar multiply in place. */ + inline Point &operator*=(const Scalar &s) NOEXCEPT { decaf_255_point_scalarmul(p,p,s.s); return *this; } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point &operator/=(const Scalar &s) NOEXCEPT { return (*this) *= s.inverse(); } + + /** @brief Validate / sanity check */ + inline bool validate() const NOEXCEPT { return !!decaf_255_point_valid(p); } + + /** @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. */ + static inline Point double_scalarmul ( + const Point &q, const Scalar &qs, const Point &r, const Scalar &rs + ) NOEXCEPT { + Point p((NOINIT())); decaf_255_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; + } + + /** + * @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. + * For those who like their scalars before the point. + */ + static inline Point double_scalarmul ( + const Scalar &qs, const Point &q, const Scalar &rs, const Point &r + ) NOEXCEPT { + Point p((NOINIT())); decaf_255_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; + } + + /** + * @brief Double-scalar multiply: this point by the first scalar and base by the second scalar. + * @warning This function takes variable time, and may leak the scalars (or points, but currently + * it doesn't). + */ + inline Point non_secret_combo_with_base(const Scalar &s, const Scalar &s_base) NOEXCEPT { + Point r((NOINIT())); decaf_255_base_double_scalarmul_non_secret(r.p,s_base.s,p,s.s); return r; + } + + inline Point& debugging_torque_in_place() { + decaf_255_point_debugging_2torque(p,p); + return *this; + } + + inline bool invert_elligator ( + Buffer &buf, unsigned char hint + ) const NOEXCEPT { + unsigned char buf2[2*HASH_BYTES]; + memset(buf2,0,sizeof(buf2)); + memcpy(buf2,buf,(buf.size() > 2*HASH_BYTES) ? 2*HASH_BYTES : buf.size()); + decaf_bool_t ret; + if (buf.size() > HASH_BYTES) { + ret = decaf_255_invert_elligator_uniform(buf2, p, hint); + } else { + ret = decaf_255_invert_elligator_nonuniform(buf2, p, hint); + } + if (buf.size() < HASH_BYTES) { + ret &= decaf_memeq(&buf2[buf.size()], &buf2[HASH_BYTES], HASH_BYTES - buf.size()); + } + memcpy(buf,buf2,(buf.size() < HASH_BYTES) ? buf.size() : HASH_BYTES); + decaf_bzero(buf2,sizeof(buf2)); + return !!ret; + } + + /** @brief Steganographically encode this */ + inline SecureBuffer steg_encode(SpongeRng &rng) const NOEXCEPT; + + /** @brief Return the base point */ + static inline const Point base() NOEXCEPT { return Point(decaf_255_point_base); } + + /** @brief Return the identity point */ + static inline const Point identity() NOEXCEPT { return Point(decaf_255_point_identity); } +}; + +/** + * @brief Precomputed table of points. + * Minor difficulties arise here because the decaf API doesn't expose, as a constant, how big such an object is. + * Therefore we have to call malloc() or friends, but that's probably for the best, because you don't want to + * stack-allocate a 15kiB object anyway. + */ +class Precomputed { +private: + /** @cond internal */ + union { + decaf_255_precomputed_s *mine; + const decaf_255_precomputed_s *yours; + } ours; + bool isMine; + + inline void clear() NOEXCEPT { + if (isMine) { + decaf_255_precomputed_destroy(ours.mine); + free(ours.mine); + ours.yours = decaf_255_precomputed_base; + isMine = false; + } + } + inline void alloc() throw(std::bad_alloc) { + if (isMine) return; + int ret = posix_memalign((void**)&ours.mine, alignof_decaf_255_precomputed_s,sizeof_decaf_255_precomputed_s); + if (ret || !ours.mine) { + isMine = false; + throw std::bad_alloc(); + } + isMine = true; + } + inline const decaf_255_precomputed_s *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } + /** @endcond */ +public: + /** Destructor securely erases the memory. */ + inline ~Precomputed() NOEXCEPT { clear(); } + + /** + * @brief Initialize from underlying type, declared as a reference to prevent + * it from being called with 0, thereby breaking override. + * + * The underlying object must remain valid throughout the lifetime of this one. + * + * By default, initializes to the table for the base point. + * + * @warning The empty initializer makes this equal to base, unlike the empty + * initializer for points which makes this equal to the identity. + */ + inline Precomputed( + const decaf_255_precomputed_s &yours = *decaf_255_precomputed_base + ) NOEXCEPT { + ours.yours = &yours; + isMine = false; + } + + /** + * @brief Assign. This may require an allocation and memcpy. + */ + inline Precomputed &operator=(const Precomputed &it) throw(std::bad_alloc) { + if (this == &it) return *this; + if (it.isMine) { + alloc(); + memcpy(ours.mine,it.ours.mine,sizeof_decaf_255_precomputed_s); + } else { + clear(); + ours.yours = it.ours.yours; + } + isMine = it.isMine; + return *this; + } + + /** + * @brief Initilaize from point. Must allocate memory, and may throw. + */ + inline Precomputed &operator=(const Point &it) throw(std::bad_alloc) { + alloc(); + decaf_255_precompute(ours.mine,it.p); + return *this; + } + + /** + * @brief Copy constructor. + */ + inline Precomputed(const Precomputed &it) throw(std::bad_alloc) : isMine(false) { *this = it; } + + /** + * @brief Constructor which initializes from point. + */ + inline explicit Precomputed(const Point &it) throw(std::bad_alloc) : isMine(false) { *this = it; } + +#if __cplusplus >= 201103L + inline Precomputed &operator=(Precomputed &&it) NOEXCEPT { + if (this == &it) return *this; + clear(); + ours = it.ours; + isMine = it.isMine; + it.isMine = false; + it.ours.yours = decaf_255_precomputed_base; + return *this; + } + inline Precomputed(Precomputed &&it) NOEXCEPT : isMine(false) { *this = it; } +#endif + + /** @brief Fixed base scalarmul. */ + inline Point operator* (const Scalar &s) const NOEXCEPT { Point r; decaf_255_precomputed_scalarmul(r.p,get(),s.s); return r; } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } + + /** @brief Return the table for the base point. */ + static inline const Precomputed base() NOEXCEPT { return Precomputed(*decaf_255_precomputed_base); } +}; + +}; /* struct Decaf255 */ + +#undef NOEXCEPT +#undef EXPLICIT_CON +#undef GET_DATA +} /* namespace decaf */ + +#endif /* __DECAF_255_HXX__ */ diff --git a/include/decaf_448.h b/include/decaf_448.h new file mode 100644 index 0000000..b9143a8 --- /dev/null +++ b/include/decaf_448.h @@ -0,0 +1,651 @@ +/** + * @file decaf_448.h + * @author Mike Hamburg + * + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * + * @brief A group of prime order p. + * + * The Decaf library implements cryptographic operations on a an elliptic curve + * group of prime order p. It accomplishes this by using a twisted Edwards + * curve (isogenous to Ed448-Goldilocks) and wiping out the cofactor. + * + * The formulas are all complete and have no special cases, except that + * decaf_448_decode can fail because not every sequence of bytes is a valid group + * element. + * + * The formulas contain no data-dependent branches, timing or memory accesses, + * except for decaf_448_base_double_scalarmul_non_secret. + * + * This library may support multiple curves eventually. The Ed448-Goldilocks + * specific identifiers are prefixed with DECAF_448 or decaf_448. + */ +#ifndef __DECAF_448_H__ +#define __DECAF_448_H__ 1 + +#include +#include + +/* Goldilocks' build flags default to hidden and stripping executables. */ +/** @cond internal */ +#if defined(DOXYGEN) && !defined(__attribute__) +#define __attribute__((x)) +#endif +#define API_VIS __attribute__((visibility("default"))) +#define NOINLINE __attribute__((noinline)) +#define WARN_UNUSED __attribute__((warn_unused_result)) +#define NONNULL1 __attribute__((nonnull(1))) +#define NONNULL2 __attribute__((nonnull(1,2))) +#define NONNULL3 __attribute__((nonnull(1,2,3))) +#define NONNULL4 __attribute__((nonnull(1,2,3,4))) +#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) + +/* Internal word types */ +#if (defined(__ILP64__) || defined(__amd64__) || defined(__x86_64__) || (((__UINT_FAST32_MAX__)>>30)>>30)) \ + && !defined(DECAF_FORCE_32_BIT) +#define DECAF_WORD_BITS 64 +typedef uint64_t decaf_word_t, decaf_bool_t; +typedef __uint128_t decaf_dword_t; +#else +#define DECAF_WORD_BITS 32 +typedef uint32_t decaf_word_t, decaf_bool_t; +typedef uint64_t decaf_dword_t; +#endif + +#define DECAF_448_LIMBS (512/DECAF_WORD_BITS) +#define DECAF_448_SCALAR_BITS 446 +#define DECAF_448_SCALAR_LIMBS (448/DECAF_WORD_BITS) + +/** Galois field element internal structure */ +typedef struct gf_s { + decaf_word_t limb[DECAF_448_LIMBS]; +} __attribute__((aligned(32))) gf_s, gf[1]; +/** @endcond */ + +/** Number of bytes in a serialized point. */ +#define DECAF_448_SER_BYTES 56 + +/** Number of bytes in a serialized scalar. */ +#define DECAF_448_SCALAR_BYTES 56 + +/** Twisted Edwards (-1,d-1) extended homogeneous coordinates */ +typedef struct decaf_448_point_s { /**@cond internal*/gf x,y,z,t;/**@endcond*/ } decaf_448_point_t[1]; + +/** Precomputed table based on a point. Can be trivial implementation. */ +struct decaf_448_precomputed_s; + +/** Precomputed table based on a point. Can be trivial implementation. */ +typedef struct decaf_448_precomputed_s decaf_448_precomputed_s; + +/** Size and alignment of precomputed point tables. */ +extern const size_t sizeof_decaf_448_precomputed_s API_VIS, alignof_decaf_448_precomputed_s API_VIS; + +/** Scalar is stored packed, because we don't need the speed. */ +typedef struct decaf_448_scalar_s { + /** @cond internal */ + decaf_word_t limb[DECAF_448_SCALAR_LIMBS]; + /** @endcond */ +} decaf_448_scalar_t[1]; + +/** DECAF_TRUE = -1 so that DECAF_TRUE & x = x */ +static const decaf_bool_t DECAF_TRUE = -(decaf_bool_t)1, DECAF_FALSE = 0; + +/** NB Success is -1, failure is 0. TODO: see if people would rather the reverse. */ +static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, + DECAF_FAILURE = 0 /*DECAF_FALSE*/; + +/** A scalar equal to 1. */ +extern const decaf_448_scalar_t decaf_448_scalar_one API_VIS; + +/** A scalar equal to 0. */ +extern const decaf_448_scalar_t decaf_448_scalar_zero API_VIS; + +/** The identity point on the curve. */ +extern const decaf_448_point_t decaf_448_point_identity API_VIS; + +/** + * An arbitrarily chosen base point on the curve. + * Equal to Ed448-Goldilocks base point defined by DJB, except of course that + * it's on the twist in this case. TODO: choose a base point with nice encoding? + */ +extern const decaf_448_point_t decaf_448_point_base API_VIS; + +/** Precomputed table for the base point on the curve. */ +extern const struct decaf_448_precomputed_s *decaf_448_precomputed_base API_VIS; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Read a scalar from wire format or from bytes. + * + * @param [in] ser Serialized form of a scalar. + * @param [out] out Deserialized form. + * + * @retval DECAF_SUCCESS The scalar was correctly encoded. + * @retval DECAF_FAILURE The scalar was greater than the modulus, + * and has been reduced modulo that modulus. + */ +decaf_bool_t decaf_448_scalar_decode ( + decaf_448_scalar_t out, + const unsigned char ser[DECAF_448_SCALAR_BYTES] +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Read a scalar from wire format or from bytes. Reduces mod + * scalar prime. + * + * @param [in] ser Serialized form of a scalar. + * @param [in] ser_len Length of serialized form. + * @param [out] out Deserialized form. + */ +void decaf_448_scalar_decode_long ( + decaf_448_scalar_t out, + const unsigned char *ser, + size_t ser_len +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Serialize a scalar to wire format. + * + * @param [out] ser Serialized form of a scalar. + * @param [in] s Deserialized scalar. + */ +void decaf_448_scalar_encode ( + unsigned char ser[DECAF_448_SCALAR_BYTES], + const decaf_448_scalar_t s +) API_VIS NONNULL2 NOINLINE NOINLINE; + +/** + * @brief Add two scalars. The scalars may use the same memory. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @param [out] out a+b. + */ +void decaf_448_scalar_add ( + decaf_448_scalar_t out, + const decaf_448_scalar_t a, + const decaf_448_scalar_t b +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Compare two scalars. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @retval DECAF_TRUE The scalars are equal. + * @retval DECAF_FALSE The scalars are not equal. + */ +decaf_bool_t decaf_448_scalar_eq ( + const decaf_448_scalar_t a, + const decaf_448_scalar_t b +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Subtract two scalars. The scalars may use the same memory. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @param [out] out a-b. + */ +void decaf_448_scalar_sub ( + decaf_448_scalar_t out, + const decaf_448_scalar_t a, + const decaf_448_scalar_t b +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Multiply two scalars. The scalars may use the same memory. + * @param [in] a One scalar. + * @param [in] b Another scalar. + * @param [out] out a*b. + */ +void decaf_448_scalar_mul ( + decaf_448_scalar_t out, + const decaf_448_scalar_t a, + const decaf_448_scalar_t b +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Invert a scalar. When passed zero, return 0. The input and output may alias. + * @param [in] a A scalar. + * @param [out] out 1/a. + * @return DECAF_TRUE The input is nonzero. + */ +decaf_bool_t decaf_448_scalar_invert ( + decaf_448_scalar_t out, + const decaf_448_scalar_t a +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Copy a scalar. The scalars may use the same memory, in which + * case this function does nothing. + * @param [in] a A scalar. + * @param [out] out Will become a copy of a. + */ +static inline void NONNULL2 decaf_448_scalar_copy ( + decaf_448_scalar_t out, + const decaf_448_scalar_t a +) { + *out = *a; +} + +/** + * @brief Set a scalar to an integer. + * @param [in] a An integer. + * @param [out] out Will become equal to a. + * @todo Make inline? + */ +void decaf_448_scalar_set( + decaf_448_scalar_t out, + decaf_word_t a +) API_VIS NONNULL1; + +/** + * @brief Encode a point as a sequence of bytes. + * + * @param [out] ser The byte representation of the point. + * @param [in] pt The point to encode. + */ +void decaf_448_point_encode ( + uint8_t ser[DECAF_448_SER_BYTES], + const decaf_448_point_t pt +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Decode a point from a sequence of bytes. + * + * Every point has a unique encoding, so not every + * sequence of bytes is a valid encoding. If an invalid + * encoding is given, the output is undefined. + * + * @param [out] pt The decoded point. + * @param [in] ser The serialized version of the point. + * @param [in] allow_identity DECAF_TRUE if the identity is a legal input. + * @retval DECAF_SUCCESS The decoding succeeded. + * @retval DECAF_FAILURE The decoding didn't succeed, because + * ser does not represent a point. + */ +decaf_bool_t decaf_448_point_decode ( + decaf_448_point_t pt, + const uint8_t ser[DECAF_448_SER_BYTES], + decaf_bool_t allow_identity +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Copy a point. The input and output may alias, + * in which case this function does nothing. + * + * @param [out] a A copy of the point. + * @param [in] b Any point. + */ +static inline void NONNULL2 decaf_448_point_copy ( + decaf_448_point_t a, + const decaf_448_point_t b +) { + *a=*b; +} + +/** + * @brief Test whether two points are equal. If yes, return + * DECAF_TRUE, else return DECAF_FALSE. + * + * @param [in] a A point. + * @param [in] b Another point. + * @retval DECAF_TRUE The points are equal. + * @retval DECAF_FALSE The points are not equal. + */ +decaf_bool_t decaf_448_point_eq ( + const decaf_448_point_t a, + const decaf_448_point_t b +) API_VIS WARN_UNUSED NONNULL2 NOINLINE; + +/** + * @brief Add two points to produce a third point. The + * input points and output point can be pointers to the same + * memory. + * + * @param [out] sum The sum a+b. + * @param [in] a An addend. + * @param [in] b An addend. + */ +void decaf_448_point_add ( + decaf_448_point_t sum, + const decaf_448_point_t a, + const decaf_448_point_t b +) API_VIS NONNULL3; + +/** + * @brief Double a point. Equivalent to + * decaf_448_point_add(two_a,a,a), but potentially faster. + * + * @param [out] two_a The sum a+a. + * @param [in] a A point. + */ +void decaf_448_point_double ( + decaf_448_point_t two_a, + const decaf_448_point_t a +) API_VIS NONNULL2; + +/** + * @brief Subtract two points to produce a third point. The + * input points and output point can be pointers to the same + * memory. + * + * @param [out] diff The difference a-b. + * @param [in] a The minuend. + * @param [in] b The subtrahend. + */ +void decaf_448_point_sub ( + decaf_448_point_t diff, + const decaf_448_point_t a, + const decaf_448_point_t b +) API_VIS NONNULL3; + +/** + * @brief Negate a point to produce another point. The input + * and output points can use the same memory. + * + * @param [out] nega The negated input point + * @param [in] a The input point. + */ +void decaf_448_point_negate ( + decaf_448_point_t nega, + const decaf_448_point_t a +) API_VIS NONNULL2; + +/** + * @brief Multiply a base point by a scalar: scaled = scalar*base. + * + * @param [out] scaled The scaled point base*scalar + * @param [in] base The point to be scaled. + * @param [in] scalar The scalar to multiply by. + */ +void decaf_448_point_scalarmul ( + decaf_448_point_t scaled, + const decaf_448_point_t base, + const decaf_448_scalar_t scalar +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Multiply a base point by a scalar: scaled = scalar*base. + * This function operates directly on serialized forms. + * + * @warning This function is experimental. It may not be supported + * long-term. + * + * @param [out] scaled The scaled point base*scalar + * @param [in] base The point to be scaled. + * @param [in] scalar The scalar to multiply by. + * @param [in] allow_identity Allow the input to be the identity. + * @param [in] short_circuit Allow a fast return if the input is illegal. + * + * @retval DECAF_SUCCESS The scalarmul succeeded. + * @retval DECAF_FAILURE The scalarmul didn't succeed, because + * base does not represent a point. + */ +decaf_bool_t decaf_448_direct_scalarmul ( + uint8_t scaled[DECAF_448_SER_BYTES], + const uint8_t base[DECAF_448_SER_BYTES], + const decaf_448_scalar_t scalar, + decaf_bool_t allow_identity, + decaf_bool_t short_circuit +) API_VIS NONNULL3 WARN_UNUSED NOINLINE; + +/** + * @brief Precompute a table for fast scalar multiplication. + * Some implementations do not include precomputed points; for + * those implementations, this implementation simply copies the + * point. + * + * @param [out] a A precomputed table of multiples of the point. + * @param [in] b Any point. + */ +void decaf_448_precompute ( + decaf_448_precomputed_s *a, + const decaf_448_point_t b +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Multiply a precomputed base point by a scalar: + * scaled = scalar*base. + * Some implementations do not include precomputed points; for + * those implementations, this function is the same as + * decaf_448_point_scalarmul + * + * @param [out] scaled The scaled point base*scalar + * @param [in] base The point to be scaled. + * @param [in] scalar The scalar to multiply by. + * + * @todo precomputed dsmul? const or variable time? + */ +void decaf_448_precomputed_scalarmul ( + decaf_448_point_t scaled, + const decaf_448_precomputed_s *base, + const decaf_448_scalar_t scalar +) API_VIS NONNULL3 NOINLINE; + +/** + * @brief Multiply two base points by two scalars: + * scaled = scalar1*base1 + scalar2*base2. + * + * Equivalent to two calls to decaf_448_point_scalarmul, but may be + * faster. + * + * @param [out] combo The linear combination scalar1*base1 + scalar2*base2. + * @param [in] base1 A first point to be scaled. + * @param [in] scalar1 A first scalar to multiply by. + * @param [in] base2 A second point to be scaled. + * @param [in] scalar2 A second scalar to multiply by. + */ +void decaf_448_point_double_scalarmul ( + decaf_448_point_t combo, + const decaf_448_point_t base1, + const decaf_448_scalar_t scalar1, + const decaf_448_point_t base2, + const decaf_448_scalar_t scalar2 +) API_VIS NONNULL5 NOINLINE; + +/** + * @brief Multiply two base points by two scalars: + * scaled = scalar1*decaf_448_point_base + scalar2*base2. + * + * Otherwise equivalent to decaf_448_point_double_scalarmul, but may be + * faster at the expense of being variable time. + * + * @param [out] combo The linear combination scalar1*base + scalar2*base2. + * @param [in] scalar1 A first scalar to multiply by. + * @param [in] base2 A second point to be scaled. + * @param [in] scalar2 A second scalar to multiply by. + * + * @warning: This function takes variable time, and may leak the scalars + * used. It is designed for signature verification. + */ +void decaf_448_base_double_scalarmul_non_secret ( + decaf_448_point_t combo, + const decaf_448_scalar_t scalar1, + const decaf_448_point_t base2, + const decaf_448_scalar_t scalar2 +) API_VIS NONNULL4 NOINLINE; + +/** + * @brief Test that a point is valid, for debugging purposes. + * + * @param [in] toTest The point to test. + * @retval DECAF_TRUE The point is valid. + * @retval DECAF_FALSE The point is invalid. + */ +decaf_bool_t decaf_448_point_valid ( + const decaf_448_point_t toTest +) API_VIS WARN_UNUSED NONNULL1 NOINLINE; + +/** + * @brief 2-torque a point, for debugging purposes. + * + * @param [out] q The point to torque. + * @param [in] p The point to torque. + */ +void decaf_448_point_debugging_2torque ( + decaf_448_point_t q, + const decaf_448_point_t p +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Almost-Elligator-like hash to curve. + * + * Call this function with the output of a hash to make a hash to the curve. + * + * This function runs Elligator2 on the decaf_448 Jacobi quartic model. It then + * uses the isogeny to put the result in twisted Edwards form. As a result, + * it is safe (cannot produce points of order 4), and would be compatible with + * hypothetical other implementations of Decaf using a Montgomery or untwisted + * Edwards model. + * + * Unlike Elligator, this function may be up to 4:1 on [0,(p-1)/2]: + * A factor of 2 due to the isogeny. + * A factor of 2 because we quotient out the 2-torsion. + * + * This makes it about 8:1 overall. + * + * Negating the input (mod q) results in the same point. Inverting the input + * (mod q) results in the negative point. This is the same as Elligator. + * + * This function isn't quite indifferentiable from a random oracle. + * However, it is suitable for many protocols, including SPEKE and SPAKE2 EE. + * Furthermore, calling it twice with independent seeds and adding the results + * is indifferentiable from a random oracle. + * + * @param [in] hashed_data Output of some hash function. + * @param [out] pt The data hashed to the curve. + * @return A "hint" value which can be used to help invert the encoding. + */ +unsigned char +decaf_448_point_from_hash_nonuniform ( + decaf_448_point_t pt, + const unsigned char hashed_data[DECAF_448_SER_BYTES] +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Inverse of elligator-like hash to curve. + * + * This function writes to the buffer, to make it so that + * decaf_448_point_from_hash_nonuniform(buffer) = pt,hint + * if possible. + * + * @param [out] recovered_hash Encoded data. + * @param [in] pt The point to encode. + * @param [in] hint The hint value returned from + * decaf_448_point_from_hash_nonuniform. + * + * @retval DECAF_SUCCESS The inverse succeeded. + * @retval DECAF_FAILURE The pt isn't the image of + * decaf_448_point_from_hash_nonuniform with the given hint. + * + * @warning The hinting system is subject to change, especially in corner cases. + * @warning FIXME The hinting system doesn't work for certain inputs which have many 0xFF. + */ +decaf_bool_t +decaf_448_invert_elligator_nonuniform ( + unsigned char recovered_hash[DECAF_448_SER_BYTES], + const decaf_448_point_t pt, + unsigned char hint +) API_VIS NONNULL2 NOINLINE WARN_UNUSED; + +/** + * @brief Inverse of elligator-like hash to curve, uniform. + * + * This function modifies the first DECAF_448_SER_BYTES of the + * buffer, to make it so that + * decaf_448_point_from_hash_uniform(buffer) = pt,hint + * if possible. + * + * @param [out] recovered_hash Encoded data. + * @param [in] pt The point to encode. + * @param [in] hint The hint value returned from + * decaf_448_point_from_hash_nonuniform. + * + * @retval DECAF_SUCCESS The inverse succeeded. + * @retval DECAF_FAILURE The pt isn't the image of + * decaf_448_point_from_hash_uniform with the given hint. + * + * @warning The hinting system is subject to change, especially in corner cases. + * @warning FIXME The hinting system doesn't work for certain inputs which have many 0xFF. + */ +decaf_bool_t +decaf_448_invert_elligator_uniform ( + unsigned char recovered_hash[2*DECAF_448_SER_BYTES], + const decaf_448_point_t pt, + unsigned char hint +) API_VIS NONNULL2 NOINLINE WARN_UNUSED; + +/** + * @brief Indifferentiable hash function encoding to curve. + * + * Equivalent to calling decaf_448_point_from_hash_nonuniform twice and adding. + * + * @param [in] hashed_data Output of some hash function. + * @param [out] pt The data hashed to the curve. + * @return A "hint" value which can be used to help invert the encoding. + */ +unsigned char decaf_448_point_from_hash_uniform ( + decaf_448_point_t pt, + const unsigned char hashed_data[2*DECAF_448_SER_BYTES] +) API_VIS NONNULL2 NOINLINE; + +/** + * @brief Overwrite data with zeros. Uses memset_s if available. + */ +void decaf_bzero ( + void *data, + size_t size +) NONNULL1 API_VIS NOINLINE; + +/** + * @brief Compare two buffers, returning DECAF_TRUE if they are equal. + */ +decaf_bool_t decaf_memeq ( + const void *data1, + const void *data2, + size_t size +) NONNULL2 WARN_UNUSED API_VIS NOINLINE; + +/** + * @brief Overwrite scalar with zeros. + */ +void decaf_448_scalar_destroy ( + decaf_448_scalar_t scalar +) NONNULL1 API_VIS; + +/** + * @brief Overwrite point with zeros. + * @todo Use this internally. + */ +void decaf_448_point_destroy ( + decaf_448_point_t point +) NONNULL1 API_VIS; + +/** + * @brief Overwrite point with zeros. + * @todo Use this internally. + */ +void decaf_448_precomputed_destroy ( + decaf_448_precomputed_s *pre +) NONNULL1 API_VIS; + +/* TODO: functions to invert point_from_hash?? */ + +#undef API_VIS +#undef WARN_UNUSED +#undef NOINLINE +#undef NONNULL1 +#undef NONNULL2 +#undef NONNULL3 +#undef NONNULL4 +#undef NONNULL5 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __DECAF_448_H__ */ diff --git a/include/decaf_448.hxx b/include/decaf_448.hxx new file mode 100644 index 0000000..4fc7008 --- /dev/null +++ b/include/decaf_448.hxx @@ -0,0 +1,738 @@ +/** + * @file decaf_448.hxx + * @author Mike Hamburg + * + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * + * @brief A group of prime order p, C++ wrapper. + * + * The Decaf library implements cryptographic operations on a an elliptic curve + * group of prime order p. It accomplishes this by using a twisted Edwards + * curve (isogenous to Ed448-Goldilocks) and wiping out the cofactor. + * + * The formulas are all complete and have no special cases, except that + * decaf_448_decode can fail because not every sequence of bytes is a valid group + * element. + * + * The formulas contain no data-dependent branches, timing or memory accesses, + * except for decaf_448_base_double_scalarmul_non_secret. + */ +#ifndef __DECAF_448_HXX__ +#define __DECAF_448_HXX__ 1 + +/** This code uses posix_memalign. */ +#define _XOPEN_SOURCE 600 +#include +#include /* for memcpy */ + +#include "decaf.h" +#include +#include +#include + +/* TODO: This is incomplete */ +/* TODO: attribute nonnull */ + +/** @cond internal */ +#if __cplusplus >= 201103L +#define NOEXCEPT noexcept +#define EXPLICIT_CON explicit +#define GET_DATA(str) ((const unsigned char *)&(str)[0]) +#else +#define NOEXCEPT throw() +#define EXPLICIT_CON +#define GET_DATA(str) ((const unsigned char *)((str).data())) +#endif +/** @endcond */ + +namespace decaf { + +/** @brief An exception for when crypto (ie point decode) has failed. */ +class CryptoException : public std::exception { +public: + /** @return "CryptoException" */ + virtual const char * what() const NOEXCEPT { return "CryptoException"; } +}; + +/** @brief An exception for when crypto (ie point decode) has failed. */ +class LengthException : public std::exception { +public: + /** @return "CryptoException" */ + virtual const char * what() const NOEXCEPT { return "LengthException"; } +}; + +/** + * Securely erase contents of memory. + */ +static inline void really_bzero(void *data, size_t size) { decaf_bzero(data,size); } + +/** Block object */ +class Block { +protected: + unsigned char *data_; + size_t size_; + +public: + /** Empty init */ + inline Block() NOEXCEPT : data_(NULL), size_(0) {} + + /** Init from C string */ + inline Block(const char *data) NOEXCEPT : data_((unsigned char *)data), size_(strlen(data)) {} + + /** Unowned init */ + inline Block(const unsigned char *data, size_t size) NOEXCEPT : data_((unsigned char *)data), size_(size) {} + + /** Block from std::string */ + inline Block(const std::string &s) : data_((unsigned char *)GET_DATA(s)), size_(s.size()) {} + + /** Get const data */ + inline const unsigned char *data() const NOEXCEPT { return data_; } + + /** Get the size */ + inline size_t size() const NOEXCEPT { return size_; } + + /** Autocast to const unsigned char * */ + inline operator const unsigned char*() const NOEXCEPT { return data_; } + + /** Convert to C++ string */ + inline std::string get_string() const { + return std::string((const char *)data_,size_); + } + + /** Slice the buffer*/ + inline Block slice(size_t off, size_t length) const throw(LengthException) { + if (off > size() || length > size() - off) + throw LengthException(); + return Block(data()+off, length); + } + + /** Virtual destructor for SecureBlock. TODO: probably means vtable? Make bool? */ + inline virtual ~Block() {}; +}; + +class TmpBuffer; + +class Buffer : public Block { +public: + /** Null init */ + inline Buffer() NOEXCEPT : Block() {} + + /** Unowned init */ + inline Buffer(unsigned char *data, size_t size) NOEXCEPT : Block(data,size) {} + + /** Get unconst data */ + inline unsigned char *data() NOEXCEPT { return data_; } + + /** Get const data */ + inline const unsigned char *data() const NOEXCEPT { return data_; } + + /** Autocast to const unsigned char * */ + inline operator const unsigned char*() const NOEXCEPT { return data_; } + + /** Autocast to unsigned char */ + inline operator unsigned char*() NOEXCEPT { return data_; } + + /** Slice the buffer*/ + inline TmpBuffer slice(size_t off, size_t length) throw(LengthException); +}; + +class TmpBuffer : public Buffer { +public: + /** Unowned init */ + inline TmpBuffer(unsigned char *data, size_t size) NOEXCEPT : Buffer(data,size) {} +}; + +TmpBuffer Buffer::slice(size_t off, size_t length) throw(LengthException) { + if (off > size() || length > size() - off) throw LengthException(); + return TmpBuffer(data()+off, length); +} + +/** A self-erasing block of data */ +class SecureBuffer : public Buffer { +public: + /** Null secure block */ + inline SecureBuffer() NOEXCEPT : Buffer() {} + + /** Construct empty from size */ + inline SecureBuffer(size_t size) { + data_ = new unsigned char[size_ = size]; + memset(data_,0,size); + } + + /** Construct from data */ + inline SecureBuffer(const unsigned char *data, size_t size){ + data_ = new unsigned char[size_ = size]; + memcpy(data_, data, size); + } + + /** Copy constructor */ + inline SecureBuffer(const Block ©) : Buffer() { *this = copy; } + + /** Copy-assign constructor */ + inline SecureBuffer& operator=(const Block ©) throw(std::bad_alloc) { + if (© == this) return *this; + clear(); + data_ = new unsigned char[size_ = copy.size()]; + memcpy(data_,copy.data(),size_); + return *this; + } + + /** Copy-assign constructor */ + inline SecureBuffer& operator=(const SecureBuffer ©) throw(std::bad_alloc) { + if (© == this) return *this; + clear(); + data_ = new unsigned char[size_ = copy.size()]; + memcpy(data_,copy.data(),size_); + return *this; + } + + /** Destructor erases data */ + ~SecureBuffer() NOEXCEPT { clear(); } + + /** Clear data */ + inline void clear() NOEXCEPT { + if (data_ == NULL) return; + really_bzero(data_,size_); + delete[] data_; + data_ = NULL; + size_ = 0; + } + +#if __cplusplus >= 201103L + /** Move constructor */ + inline SecureBuffer(SecureBuffer &&move) { *this = move; } + + /** Move non-constructor */ + inline SecureBuffer(Block &&move) { *this = (Block &)move; } + + /** Move-assign constructor. TODO: check that this actually gets used.*/ + inline SecureBuffer& operator=(SecureBuffer &&move) { + clear(); + data_ = move.data_; move.data_ = NULL; + size_ = move.size_; move.size_ = 0; + return *this; + } + + /** C++11-only explicit cast */ + inline explicit operator std::string() const { return get_string(); } +#endif +}; + + +/** @brief Passed to constructors to avoid (conservative) initialization */ +struct NOINIT {}; + +/**@cond internal*/ +/** Forward-declare sponge RNG object */ +class SpongeRng; +/**@endcond*/ + + +/** + * @brief Ed448-Goldilocks/Decaf instantiation of group. + */ +struct Ed448 { + +/** @cond internal */ +class Point; +class Precomputed; +/** @endcond */ + +/** + * @brief A scalar modulo the curve order. + * Supports the usual arithmetic operations, all in constant time. + */ +class Scalar { +public: + /** @brief Size of a serialized element */ + static const size_t SER_BYTES = DECAF_448_SCALAR_BYTES; + + /** @brief access to the underlying scalar object */ + decaf_448_scalar_t s; + + /** @brief Don't initialize. */ + inline Scalar(const NOINIT &) NOEXCEPT {} + + /** @brief Set to an unsigned word */ + inline Scalar(const decaf_word_t w) NOEXCEPT { *this = w; } + + /** @brief Set to a signed word */ + inline Scalar(const int w) NOEXCEPT { *this = w; } + + /** @brief Construct from RNG */ + inline explicit Scalar(SpongeRng &rng) NOEXCEPT; + + /** @brief Construct from decaf_scalar_t object. */ + inline Scalar(const decaf_448_scalar_t &t = decaf_448_scalar_zero) NOEXCEPT { decaf_448_scalar_copy(s,t); } + + /** @brief Copy constructor. */ + inline Scalar(const Scalar &x) NOEXCEPT { *this = x; } + + /** @brief Construct from arbitrary-length little-endian byte sequence. */ + inline Scalar(const Block &buffer) NOEXCEPT { *this = buffer; } + + /** @brief Assignment. */ + inline Scalar& operator=(const Scalar &x) NOEXCEPT { decaf_448_scalar_copy(s,x.s); return *this; } + + /** @brief Assign from unsigned word. */ + inline Scalar& operator=(decaf_word_t w) NOEXCEPT { decaf_448_scalar_set(s,w); return *this; } + + /** @brief Assign from signed int. */ + inline Scalar& operator=(int w) NOEXCEPT { + Scalar t(-(decaf_word_t)INT_MIN); + decaf_448_scalar_set(s,(decaf_word_t)w - (decaf_word_t)INT_MIN); + *this -= t; + return *this; + } + + /** Destructor securely erases the scalar. */ + inline ~Scalar() NOEXCEPT { decaf_448_scalar_destroy(s); } + + /** @brief Assign from arbitrary-length little-endian byte sequence in a Block. */ + inline Scalar &operator=(const Block &bl) NOEXCEPT { + decaf_448_scalar_decode_long(s,bl.data(),bl.size()); return *this; + } + + /** + * @brief Decode from correct-length little-endian byte sequence. + * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Scalar &sc, const unsigned char buffer[SER_BYTES] + ) NOEXCEPT { + return decaf_448_scalar_decode(sc.s,buffer); + } + + /** @brief Decode from correct-length little-endian byte sequence in C++ string. */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Scalar &sc, const Block &buffer + ) NOEXCEPT { + if (buffer.size() != SER_BYTES) return DECAF_FAILURE; + return decaf_448_scalar_decode(sc.s,buffer); + } + + /** @brief Encode to fixed-length string */ + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buf(SER_BYTES); decaf_448_scalar_encode(buf,s); return buf; + } + + /** @brief Encode to fixed-length buffer */ + inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ + decaf_448_scalar_encode(buffer, s); + } + + /** Add. */ + inline Scalar operator+ (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_add(r.s,s,q.s); return r; } + + /** Add to this. */ + inline Scalar &operator+=(const Scalar &q) NOEXCEPT { decaf_448_scalar_add(s,s,q.s); return *this; } + + /** Subtract. */ + inline Scalar operator- (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_sub(r.s,s,q.s); return r; } + + /** Subtract from this. */ + inline Scalar &operator-=(const Scalar &q) NOEXCEPT { decaf_448_scalar_sub(s,s,q.s); return *this; } + + /** Multiply */ + inline Scalar operator* (const Scalar &q) const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_mul(r.s,s,q.s); return r; } + + /** Multiply into this. */ + inline Scalar &operator*=(const Scalar &q) NOEXCEPT { decaf_448_scalar_mul(s,s,q.s); return *this; } + + /** Negate */ + inline Scalar operator- () const NOEXCEPT { Scalar r((NOINIT())); decaf_448_scalar_sub(r.s,decaf_448_scalar_zero,s); return r; } + + /** @brief Invert with Fermat's Little Theorem (slow!). If *this == 0, return 0. */ + inline Scalar inverse() const NOEXCEPT { Scalar r; decaf_448_scalar_invert(r.s,s); return r; } + + /** @brief Divide by inverting q. If q == 0, return 0. */ + inline Scalar operator/ (const Scalar &q) const NOEXCEPT { return *this * q.inverse(); } + + /** @brief Divide by inverting q. If q == 0, return 0. */ + inline Scalar &operator/=(const Scalar &q) NOEXCEPT { return *this *= q.inverse(); } + + /** @brief Compare in constant time */ + inline bool operator!=(const Scalar &q) const NOEXCEPT { return !(*this == q); } + + /** @brief Compare in constant time */ + inline bool operator==(const Scalar &q) const NOEXCEPT { return !!decaf_448_scalar_eq(s,q.s); } + + /** @brief Scalarmul with scalar on left. */ + inline Point operator* (const Point &q) const NOEXCEPT { return q * (*this); } + + /** @brief Scalarmul-precomputed with scalar on left. */ + inline Point operator* (const Precomputed &q) const NOEXCEPT { return q * (*this); } + + /** @brief Direct scalar multiplication. */ + inline SecureBuffer direct_scalarmul( + const Block &in, + decaf_bool_t allow_identity=DECAF_FALSE, + decaf_bool_t short_circuit=DECAF_TRUE + ) const throw(CryptoException) { + SecureBuffer out(/*FIXME Point::*/SER_BYTES); + if (!decaf_448_direct_scalarmul(out, in.data(), s, allow_identity, short_circuit)) + throw CryptoException(); + return out; + } +}; + +/** + * @brief Element of prime-order group. + */ +class Point { +public: + /** @brief Size of a serialized element */ + static const size_t SER_BYTES = DECAF_448_SER_BYTES; + + /** @brief Size of a stegged element */ + static const size_t STEG_BYTES = DECAF_448_SER_BYTES + 8; + + /** @brief Bytes required for hash */ + static const size_t HASH_BYTES = DECAF_448_SER_BYTES; + + /** The c-level object. */ + decaf_448_point_t p; + + /** @brief Don't initialize. */ + inline Point(const NOINIT &) NOEXCEPT {} + + /** @brief Constructor sets to identity by default. */ + inline Point(const decaf_448_point_t &q = decaf_448_point_identity) NOEXCEPT { decaf_448_point_copy(p,q); } + + /** @brief Copy constructor. */ + inline Point(const Point &q) NOEXCEPT { decaf_448_point_copy(p,q.p); } + + /** @brief Assignment. */ + inline Point& operator=(const Point &q) NOEXCEPT { decaf_448_point_copy(p,q.p); return *this; } + + /** @brief Destructor securely erases the point. */ + inline ~Point() NOEXCEPT { decaf_448_point_destroy(p); } + + /** @brief Construct from RNG */ + inline explicit Point(SpongeRng &rng, bool uniform = true) NOEXCEPT; + + /** + * @brief Initialize from C++ fixed-length byte string. + * The all-zero string maps to the identity. + * + * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. + */ + inline explicit Point(const Block &s, decaf_bool_t allow_identity=DECAF_TRUE) throw(CryptoException) { + if (!decode(*this,s,allow_identity)) throw CryptoException(); + } + + /** + * @brief Initialize from C fixed-length byte string. + * The all-zero string maps to the identity. + * + * @throw CryptoException the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. + */ + inline explicit Point(const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE) + throw(CryptoException) { if (!decode(*this,buffer,allow_identity)) throw CryptoException(); } + + /** + * @brief Initialize from C fixed-length byte string. + * The all-zero string maps to the identity. + * + * @retval DECAF_SUCCESS the string was successfully decoded. + * @return DECAF_FAILURE the string wasn't the encoding of a point, or was the identity + * and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Point &p, const unsigned char buffer[SER_BYTES], decaf_bool_t allow_identity=DECAF_TRUE + ) NOEXCEPT { + return decaf_448_point_decode(p.p,buffer,allow_identity); + } + + /** + * @brief Initialize from C++ fixed-length byte string. + * The all-zero string maps to the identity. + * + * @retval DECAF_SUCCESS the string was successfully decoded. + * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, + * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. + */ + static inline decaf_bool_t __attribute__((warn_unused_result)) decode ( + Point &p, const Block &buffer, decaf_bool_t allow_identity=DECAF_TRUE + ) NOEXCEPT { + if (buffer.size() != SER_BYTES) return DECAF_FAILURE; + return decaf_448_point_decode(p.p,buffer.data(),allow_identity); + } + + /** + * @brief Map uniformly to the curve from a hash buffer. + * The empty or all-zero string maps to the identity, as does the string "\x01". + * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, + * but the buffer will be zero-padded on the right. + */ + static inline Point from_hash ( const Block &s ) NOEXCEPT { + Point p((NOINIT())); p.set_to_hash(s); return p; + } + + /** + * @brief Map to the curve from a hash buffer. + * The empty or all-zero string maps to the identity, as does the string "\x01". + * If the buffer is shorter than 2*HASH_BYTES, well, it won't be as uniform, + * but the buffer will be zero-padded on the right. + */ + inline unsigned char set_to_hash( const Block &s ) NOEXCEPT { + if (s.size() < HASH_BYTES) { + SecureBuffer b(HASH_BYTES); + memcpy(b.data(), s.data(), s.size()); + return decaf_448_point_from_hash_nonuniform(p,b); + } else if (s.size() == HASH_BYTES) { + return decaf_448_point_from_hash_nonuniform(p,s); + } else if (s.size() < 2*HASH_BYTES) { + SecureBuffer b(2*HASH_BYTES); + memcpy(b.data(), s.data(), s.size()); + return decaf_448_point_from_hash_uniform(p,b); + } else { + return decaf_448_point_from_hash_uniform(p,s); + } + } + + /** + * @brief Encode to string. The identity encodes to the all-zero string. + */ + inline EXPLICIT_CON operator SecureBuffer() const NOEXCEPT { + SecureBuffer buffer(SER_BYTES); + decaf_448_point_encode(buffer, p); + return buffer; + } + + /** + * @brief Encode to a C buffer. The identity encodes to all zeros. + */ + inline void encode(unsigned char buffer[SER_BYTES]) const NOEXCEPT{ + decaf_448_point_encode(buffer, p); + } + + /** @brief Point add. */ + inline Point operator+ (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_add(r.p,p,q.p); return r; } + + /** @brief Point add. */ + inline Point &operator+=(const Point &q) NOEXCEPT { decaf_448_point_add(p,p,q.p); return *this; } + + /** @brief Point subtract. */ + inline Point operator- (const Point &q) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_sub(r.p,p,q.p); return r; } + + /** @brief Point subtract. */ + inline Point &operator-=(const Point &q) NOEXCEPT { decaf_448_point_sub(p,p,q.p); return *this; } + + /** @brief Point negate. */ + inline Point operator- () const NOEXCEPT { Point r((NOINIT())); decaf_448_point_negate(r.p,p); return r; } + + /** @brief Double the point out of place. */ + inline Point times_two () const NOEXCEPT { Point r((NOINIT())); decaf_448_point_double(r.p,p); return r; } + + /** @brief Double the point in place. */ + inline Point &double_in_place() NOEXCEPT { decaf_448_point_double(p,p); return *this; } + + /** @brief Constant-time compare. */ + inline bool operator!=(const Point &q) const NOEXCEPT { return ! decaf_448_point_eq(p,q.p); } + + /** @brief Constant-time compare. */ + inline bool operator==(const Point &q) const NOEXCEPT { return !!decaf_448_point_eq(p,q.p); } + + /** @brief Scalar multiply. */ + inline Point operator* (const Scalar &s) const NOEXCEPT { Point r((NOINIT())); decaf_448_point_scalarmul(r.p,p,s.s); return r; } + + /** @brief Scalar multiply in place. */ + inline Point &operator*=(const Scalar &s) NOEXCEPT { decaf_448_point_scalarmul(p,p,s.s); return *this; } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point &operator/=(const Scalar &s) NOEXCEPT { return (*this) *= s.inverse(); } + + /** @brief Validate / sanity check */ + inline bool validate() const NOEXCEPT { return !!decaf_448_point_valid(p); } + + /** @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. */ + static inline Point double_scalarmul ( + const Point &q, const Scalar &qs, const Point &r, const Scalar &rs + ) NOEXCEPT { + Point p((NOINIT())); decaf_448_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; + } + + /** + * @brief Double-scalar multiply, equivalent to q*qs + r*rs but faster. + * For those who like their scalars before the point. + */ + static inline Point double_scalarmul ( + const Scalar &qs, const Point &q, const Scalar &rs, const Point &r + ) NOEXCEPT { + Point p((NOINIT())); decaf_448_point_double_scalarmul(p.p,q.p,qs.s,r.p,rs.s); return p; + } + + /** + * @brief Double-scalar multiply: this point by the first scalar and base by the second scalar. + * @warning This function takes variable time, and may leak the scalars (or points, but currently + * it doesn't). + */ + inline Point non_secret_combo_with_base(const Scalar &s, const Scalar &s_base) NOEXCEPT { + Point r((NOINIT())); decaf_448_base_double_scalarmul_non_secret(r.p,s_base.s,p,s.s); return r; + } + + inline Point& debugging_torque_in_place() { + decaf_448_point_debugging_2torque(p,p); + return *this; + } + + inline bool invert_elligator ( + Buffer &buf, unsigned char hint + ) const NOEXCEPT { + unsigned char buf2[2*HASH_BYTES]; + memset(buf2,0,sizeof(buf2)); + memcpy(buf2,buf,(buf.size() > 2*HASH_BYTES) ? 2*HASH_BYTES : buf.size()); + decaf_bool_t ret; + if (buf.size() > HASH_BYTES) { + ret = decaf_448_invert_elligator_uniform(buf2, p, hint); + } else { + ret = decaf_448_invert_elligator_nonuniform(buf2, p, hint); + } + if (buf.size() < HASH_BYTES) { + ret &= decaf_memeq(&buf2[buf.size()], &buf2[HASH_BYTES], HASH_BYTES - buf.size()); + } + memcpy(buf,buf2,(buf.size() < HASH_BYTES) ? buf.size() : HASH_BYTES); + decaf_bzero(buf2,sizeof(buf2)); + return !!ret; + } + + /** @brief Steganographically encode this */ + inline SecureBuffer steg_encode(SpongeRng &rng) const NOEXCEPT; + + /** @brief Return the base point */ + static inline const Point base() NOEXCEPT { return Point(decaf_448_point_base); } + + /** @brief Return the identity point */ + static inline const Point identity() NOEXCEPT { return Point(decaf_448_point_identity); } +}; + +/** + * @brief Precomputed table of points. + * Minor difficulties arise here because the decaf API doesn't expose, as a constant, how big such an object is. + * Therefore we have to call malloc() or friends, but that's probably for the best, because you don't want to + * stack-allocate a 15kiB object anyway. + */ +class Precomputed { +private: + /** @cond internal */ + union { + decaf_448_precomputed_s *mine; + const decaf_448_precomputed_s *yours; + } ours; + bool isMine; + + inline void clear() NOEXCEPT { + if (isMine) { + decaf_448_precomputed_destroy(ours.mine); + free(ours.mine); + ours.yours = decaf_448_precomputed_base; + isMine = false; + } + } + inline void alloc() throw(std::bad_alloc) { + if (isMine) return; + int ret = posix_memalign((void**)&ours.mine, alignof_decaf_448_precomputed_s,sizeof_decaf_448_precomputed_s); + if (ret || !ours.mine) { + isMine = false; + throw std::bad_alloc(); + } + isMine = true; + } + inline const decaf_448_precomputed_s *get() const NOEXCEPT { return isMine ? ours.mine : ours.yours; } + /** @endcond */ +public: + /** Destructor securely erases the memory. */ + inline ~Precomputed() NOEXCEPT { clear(); } + + /** + * @brief Initialize from underlying type, declared as a reference to prevent + * it from being called with 0, thereby breaking override. + * + * The underlying object must remain valid throughout the lifetime of this one. + * + * By default, initializes to the table for the base point. + * + * @warning The empty initializer makes this equal to base, unlike the empty + * initializer for points which makes this equal to the identity. + */ + inline Precomputed( + const decaf_448_precomputed_s &yours = *decaf_448_precomputed_base + ) NOEXCEPT { + ours.yours = &yours; + isMine = false; + } + + /** + * @brief Assign. This may require an allocation and memcpy. + */ + inline Precomputed &operator=(const Precomputed &it) throw(std::bad_alloc) { + if (this == &it) return *this; + if (it.isMine) { + alloc(); + memcpy(ours.mine,it.ours.mine,sizeof_decaf_448_precomputed_s); + } else { + clear(); + ours.yours = it.ours.yours; + } + isMine = it.isMine; + return *this; + } + + /** + * @brief Initilaize from point. Must allocate memory, and may throw. + */ + inline Precomputed &operator=(const Point &it) throw(std::bad_alloc) { + alloc(); + decaf_448_precompute(ours.mine,it.p); + return *this; + } + + /** + * @brief Copy constructor. + */ + inline Precomputed(const Precomputed &it) throw(std::bad_alloc) : isMine(false) { *this = it; } + + /** + * @brief Constructor which initializes from point. + */ + inline explicit Precomputed(const Point &it) throw(std::bad_alloc) : isMine(false) { *this = it; } + +#if __cplusplus >= 201103L + inline Precomputed &operator=(Precomputed &&it) NOEXCEPT { + if (this == &it) return *this; + clear(); + ours = it.ours; + isMine = it.isMine; + it.isMine = false; + it.ours.yours = decaf_448_precomputed_base; + return *this; + } + inline Precomputed(Precomputed &&it) NOEXCEPT : isMine(false) { *this = it; } +#endif + + /** @brief Fixed base scalarmul. */ + inline Point operator* (const Scalar &s) const NOEXCEPT { Point r; decaf_448_precomputed_scalarmul(r.p,get(),s.s); return r; } + + /** @brief Multiply by s.inverse(). If s=0, maps to the identity. */ + inline Point operator/ (const Scalar &s) const NOEXCEPT { return (*this) * s.inverse(); } + + /** @brief Return the table for the base point. */ + static inline const Precomputed base() NOEXCEPT { return Precomputed(*decaf_448_precomputed_base); } +}; + +}; /* struct Decaf448 */ + +#undef NOEXCEPT +#undef EXPLICIT_CON +#undef GET_DATA +} /* namespace decaf */ + +#endif /* __DECAF_448_HXX__ */ diff --git a/src/decaf_crypto.c b/src/decaf_crypto.c index 4ec136e..1527370 100644 --- a/src/decaf_crypto.c +++ b/src/decaf_crypto.c @@ -11,60 +11,60 @@ #include "decaf_crypto.h" #include -static const unsigned int DECAF_448_SCALAR_OVERKILL_BYTES = DECAF_448_SCALAR_BYTES + 8; +static const unsigned int DECAF_255_SCALAR_OVERKILL_BYTES = DECAF_255_SCALAR_BYTES + 8; -void decaf_448_derive_private_key ( - decaf_448_private_key_t priv, - const decaf_448_symmetric_key_t proto +void decaf_255_derive_private_key ( + decaf_255_private_key_t priv, + const decaf_255_symmetric_key_t proto ) { - const char *magic = "decaf_448_derive_private_key"; - uint8_t encoded_scalar[DECAF_448_SCALAR_OVERKILL_BYTES]; - decaf_448_point_t pub; + const char *magic = "decaf_255_derive_private_key"; + uint8_t encoded_scalar[DECAF_255_SCALAR_OVERKILL_BYTES]; + decaf_255_point_t pub; keccak_sponge_t sponge; shake256_init(sponge); - shake256_update(sponge, proto, sizeof(decaf_448_symmetric_key_t)); + shake256_update(sponge, proto, sizeof(decaf_255_symmetric_key_t)); shake256_update(sponge, (const unsigned char *)magic, strlen(magic)); shake256_final(sponge, encoded_scalar, sizeof(encoded_scalar)); shake256_destroy(sponge); - memcpy(priv->sym, proto, sizeof(decaf_448_symmetric_key_t)); - decaf_448_scalar_decode_long(priv->secret_scalar, encoded_scalar, sizeof(encoded_scalar)); + memcpy(priv->sym, proto, sizeof(decaf_255_symmetric_key_t)); + decaf_255_scalar_decode_long(priv->secret_scalar, encoded_scalar, sizeof(encoded_scalar)); - decaf_448_precomputed_scalarmul(pub, decaf_448_precomputed_base, priv->secret_scalar); - decaf_448_point_encode(priv->pub, pub); + decaf_255_precomputed_scalarmul(pub, decaf_255_precomputed_base, priv->secret_scalar); + decaf_255_point_encode(priv->pub, pub); decaf_bzero(encoded_scalar, sizeof(encoded_scalar)); } void -decaf_448_destroy_private_key ( - decaf_448_private_key_t priv +decaf_255_destroy_private_key ( + decaf_255_private_key_t priv ) { - decaf_bzero((void*)priv, sizeof(decaf_448_private_key_t)); + decaf_bzero((void*)priv, sizeof(decaf_255_private_key_t)); } -void decaf_448_private_to_public ( - decaf_448_public_key_t pub, - const decaf_448_private_key_t priv +void decaf_255_private_to_public ( + decaf_255_public_key_t pub, + const decaf_255_private_key_t priv ) { - memcpy(pub, priv->pub, sizeof(decaf_448_public_key_t)); + memcpy(pub, priv->pub, sizeof(decaf_255_public_key_t)); } decaf_bool_t -decaf_448_shared_secret ( +decaf_255_shared_secret ( uint8_t *shared, size_t shared_bytes, - const decaf_448_private_key_t my_privkey, - const decaf_448_public_key_t your_pubkey + const decaf_255_private_key_t my_privkey, + const decaf_255_public_key_t your_pubkey ) { - uint8_t ss_ser[DECAF_448_SER_BYTES]; - const char *nope = "decaf_448_ss_invalid"; + uint8_t ss_ser[DECAF_255_SER_BYTES]; + const char *nope = "decaf_255_ss_invalid"; unsigned i; /* Lexsort keys. Less will be -1 if mine is less, and 0 otherwise. */ uint16_t less = 0; - for (i=0; ipub[i]; delta -= your_pubkey[i]; /* Case: @@ -92,7 +92,7 @@ decaf_448_shared_secret ( } shake256_update(sponge, ss_ser, sizeof(ss_ser)); - decaf_bool_t ret = decaf_448_direct_scalarmul(ss_ser, your_pubkey, my_privkey->secret_scalar, DECAF_FALSE, DECAF_TRUE); + decaf_bool_t ret = decaf_255_direct_scalarmul(ss_ser, your_pubkey, my_privkey->secret_scalar, DECAF_FALSE, DECAF_TRUE); /* If invalid, then replace ... */ for (i=0; isecret_scalar); - decaf_448_scalar_sub(nonce, nonce, challenge); + decaf_255_scalar_mul(challenge, challenge, priv->secret_scalar); + decaf_255_scalar_sub(nonce, nonce, challenge); /* Save results */ memcpy(sig, encoded, sizeof(encoded)); - decaf_448_scalar_encode(&sig[sizeof(encoded)], nonce); + decaf_255_scalar_encode(&sig[sizeof(encoded)], nonce); /* Clean up */ - decaf_448_scalar_destroy(nonce); - decaf_448_scalar_destroy(challenge); + decaf_255_scalar_destroy(nonce); + decaf_255_scalar_destroy(challenge); decaf_bzero(overkill,sizeof(overkill)); decaf_bzero(encoded,sizeof(encoded)); } decaf_bool_t -decaf_448_verify_shake ( - const decaf_448_signature_t sig, - const decaf_448_public_key_t pub, +decaf_255_verify_shake ( + const decaf_255_signature_t sig, + const decaf_255_public_key_t pub, const keccak_sponge_t shake ) { decaf_bool_t ret; - uint8_t overkill[DECAF_448_SCALAR_OVERKILL_BYTES]; - decaf_448_point_t point, pubpoint; - decaf_448_scalar_t challenge, response; + uint8_t overkill[DECAF_255_SCALAR_OVERKILL_BYTES]; + decaf_255_point_t point, pubpoint; + decaf_255_scalar_t challenge, response; /* Derive challenge */ keccak_sponge_t ctx; memcpy(ctx, shake, sizeof(ctx)); - shake256_update(ctx, pub, sizeof(decaf_448_public_key_t)); - shake256_update(ctx, sig, DECAF_448_SER_BYTES); + shake256_update(ctx, pub, sizeof(decaf_255_public_key_t)); + shake256_update(ctx, sig, DECAF_255_SER_BYTES); shake256_final(ctx, overkill, sizeof(overkill)); shake256_destroy(ctx); - decaf_448_scalar_decode_long(challenge, overkill, sizeof(overkill)); + decaf_255_scalar_decode_long(challenge, overkill, sizeof(overkill)); /* Decode points. */ - ret = decaf_448_point_decode(point, sig, DECAF_TRUE); - ret &= decaf_448_point_decode(pubpoint, pub, DECAF_FALSE); - ret &= decaf_448_scalar_decode(response, &sig[DECAF_448_SER_BYTES]); + ret = decaf_255_point_decode(point, sig, DECAF_TRUE); + ret &= decaf_255_point_decode(pubpoint, pub, DECAF_FALSE); + ret &= decaf_255_scalar_decode(response, &sig[DECAF_255_SER_BYTES]); - decaf_448_base_double_scalarmul_non_secret ( + decaf_255_base_double_scalarmul_non_secret ( pubpoint, response, pubpoint, challenge ); - ret &= decaf_448_point_eq(pubpoint, point); + ret &= decaf_255_point_eq(pubpoint, point); return ret; } void -decaf_448_sign ( - decaf_448_signature_t sig, - const decaf_448_private_key_t priv, +decaf_255_sign ( + decaf_255_signature_t sig, + const decaf_255_private_key_t priv, const unsigned char *message, size_t message_len ) { keccak_sponge_t ctx; shake256_init(ctx); shake256_update(ctx, message, message_len); - decaf_448_sign_shake(sig, priv, ctx); + decaf_255_sign_shake(sig, priv, ctx); shake256_destroy(ctx); } decaf_bool_t -decaf_448_verify ( - const decaf_448_signature_t sig, - const decaf_448_public_key_t pub, +decaf_255_verify ( + const decaf_255_signature_t sig, + const decaf_255_public_key_t pub, const unsigned char *message, size_t message_len ) { keccak_sponge_t ctx; shake256_init(ctx); shake256_update(ctx, message, message_len); - decaf_bool_t ret = decaf_448_verify_shake(sig, pub, ctx); + decaf_bool_t ret = decaf_255_verify_shake(sig, pub, ctx); shake256_destroy(ctx); return ret; } diff --git a/src/decaf_fast.c b/src/decaf_fast.c index a5385be..7a404b9 100644 --- a/src/decaf_fast.c +++ b/src/decaf_fast.c @@ -13,20 +13,20 @@ #include "decaf.h" #include #include "field.h" -#include "decaf_448_config.h" +#include "decaf_255_config.h" #define WBITS DECAF_WORD_BITS /* Rename table for eventual factoring into .c.inc, MSR ECC style */ -#define SCALAR_LIMBS DECAF_448_SCALAR_LIMBS -#define SCALAR_BITS DECAF_448_SCALAR_BITS -#define NLIMBS DECAF_448_LIMBS -#define API_NS(_id) decaf_448_##_id -#define API_NS2(_pref,_id) _pref##_decaf_448_##_id -#define scalar_t decaf_448_scalar_t -#define point_t decaf_448_point_t -#define precomputed_s decaf_448_precomputed_s -#define SER_BYTES DECAF_448_SER_BYTES +#define SCALAR_LIMBS DECAF_255_SCALAR_LIMBS +#define SCALAR_BITS DECAF_255_SCALAR_BITS +#define NLIMBS DECAF_255_LIMBS +#define API_NS(_id) decaf_255_##_id +#define API_NS2(_pref,_id) _pref##_decaf_255_##_id +#define scalar_t decaf_255_scalar_t +#define point_t decaf_255_point_t +#define precomputed_s decaf_255_precomputed_s +#define SER_BYTES DECAF_255_SER_BYTES #if WBITS == 64 typedef __int128_t decaf_sdword_t; @@ -45,25 +45,23 @@ typedef int64_t decaf_sdword_t; #define siv static inline void __attribute__((always_inline)) static const gf ZERO = {{{0}}}, ONE = {{{1}}}, TWO = {{{2}}}; -static const int EDWARDS_D = -39081; +static const int EDWARDS_D = 121665; static const scalar_t sc_p = {{{ - SC_LIMB(0x2378c292ab5844f3), - SC_LIMB(0x216cc2728dc58f55), - SC_LIMB(0xc44edb49aed63690), - SC_LIMB(0xffffffff7cca23e9), - SC_LIMB(0xffffffffffffffff), - SC_LIMB(0xffffffffffffffff), - SC_LIMB(0x3fffffffffffffff) + SC_LIMB(0x5812631a5cf5d3ed), + SC_LIMB(0x14def9dea2f79cd6), + SC_LIMB(0), + SC_LIMB(0), + SC_LIMB(0x1000000000000000) }}}; const scalar_t API_NS(scalar_one) = {{{1}}}, API_NS(scalar_zero) = {{{0}}}; extern const scalar_t sc_r2; extern const decaf_word_t MONTGOMERY_FACTOR; -/* sqrt(5) = 2phi-1 from the curve spec. Not exported, but used by pregen tool. */ +/* sqrt(9) = 3 from the curve spec. Not exported, but used by pregen tool. */ const unsigned char base_point_ser_for_pregen[SER_BYTES] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1 + 3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; extern const point_t API_NS(point_base); @@ -324,7 +322,8 @@ decaf_bool_t API_NS(scalar_invert) ( scalar_t out, const scalar_t a ) { - /* FIELD MAGIC */ +#if 0 + /* FIELD MAGIC. FIXME: not updated for 25519 */ scalar_t chain[7], tmp; sc_montmul(chain[0],a,sc_r2); @@ -371,6 +370,11 @@ decaf_bool_t API_NS(scalar_invert) ( API_NS(scalar_destroy)(chain[i]); } return ~API_NS(scalar_eq)(out,API_NS(scalar_zero)); +#else + (void)out; + (void)a; + return 0; +#endif } void API_NS(scalar_sub) ( @@ -1067,7 +1071,7 @@ unsigned char API_NS(point_from_hash_nonuniform) ( decaf_bool_t API_NS(invert_elligator_nonuniform) ( - unsigned char recovered_hash[DECAF_448_SER_BYTES], + unsigned char recovered_hash[DECAF_255_SER_BYTES], const point_t p, unsigned char hint ) { diff --git a/src/decaf_gen_tables.c b/src/decaf_gen_tables.c index 0ae5707..78bc046 100644 --- a/src/decaf_gen_tables.c +++ b/src/decaf_gen_tables.c @@ -12,11 +12,11 @@ #include #include #include "decaf.h" -#include "decaf_448_config.h" /* MAGIC */ +#include "decaf_255_config.h" /* MAGIC */ #include "field.h" -#define API_NS(_id) decaf_448_##_id -#define API_NS2(_pref,_id) _pref##_decaf_448_##_id +#define API_NS(_id) decaf_255_##_id +#define API_NS2(_pref,_id) _pref##_decaf_255_##_id /* To satisfy linker. */ const field_t API_NS(precomputed_base_as_fe)[1]; @@ -24,7 +24,7 @@ const API_NS(scalar_t) API_NS(precomputed_scalarmul_adjustment); const API_NS(scalar_t) API_NS(point_scalarmul_adjustment); const API_NS(scalar_t) sc_r2 = {{{0}}}; const decaf_word_t MONTGOMERY_FACTOR = 0; -const unsigned char base_point_ser_for_pregen[DECAF_448_SER_BYTES]; +const unsigned char base_point_ser_for_pregen[DECAF_255_SER_BYTES]; const API_NS(point_t) API_NS(point_base); @@ -94,8 +94,8 @@ int main(int argc, char **argv) { printf("/** @warning: this file was automatically generated. */\n"); printf("#include \"field.h\"\n\n"); printf("#include \"decaf.h\"\n\n"); - printf("#define API_NS(_id) decaf_448_##_id\n"); - printf("#define API_NS2(_pref,_id) _pref##_decaf_448_##_id\n"); + printf("#define API_NS(_id) decaf_255_##_id\n"); + printf("#define API_NS2(_pref,_id) _pref##_decaf_255_##_id\n"); output = (const field_t *)real_point_base; printf("const API_NS(point_t) API_NS(point_base) = {{\n"); @@ -138,8 +138,8 @@ int main(int argc, char **argv) { scalar_print("API_NS(precomputed_scalarmul_adjustment)", smadj); API_NS(scalar_copy)(smadj,API_NS(scalar_one)); - for (i=0; i>64; +} + +void +p255_mul ( + p255_t *__restrict__ cs, + const p255_t *as, + const p255_t *bs +) { + const uint64_t *a = as->limb, *b = bs->limb; + uint64_t *c = cs->limb; + + __uint128_t accum0 = 0, accum1 = 0, accum2; + uint64_t mask = (1ull<<51) - 1; + + uint64_t aa[4], bb[4], bbb[4]; + + unsigned int i; + for (i=0; i<4; i++) { + aa[i] = a[i] + a[i+4]; + bb[i] = b[i] + b[i+4]; + bbb[i] = bb[i] + b[i+4]; + } + + int I_HATE_UNROLLED_LOOPS = 0; + + if (I_HATE_UNROLLED_LOOPS) { + /* The compiler probably won't unroll this, + * so it's like 80% slower. + */ + for (i=0; i<4; i++) { + accum2 = 0; + + unsigned int j; + for (j=0; j<=i; j++) { + accum2 += widemul(a[j], b[i-j]); + accum1 += widemul(aa[j], bb[i-j]); + accum0 += widemul(a[j+4], b[i-j+4]); + } + for (; j<4; j++) { + accum2 += widemul(a[j], b[i-j+8]); + accum1 += widemul(aa[j], bbb[i-j+4]); + accum0 += widemul(a[j+4], bb[i-j+4]); + } + + accum1 -= accum2; + accum0 += accum2; + + c[i] = ((uint64_t)(accum0)) & mask; + c[i+4] = ((uint64_t)(accum1)) & mask; + + accum0 >>= 56; + accum1 >>= 56; + } + } else { + accum2 = widemul(a[0], b[0]); + accum1 += widemul(aa[0], bb[0]); + accum0 += widemul(a[4], b[4]); + + accum2 += widemul(a[1], b[7]); + accum1 += widemul(aa[1], bbb[3]); + accum0 += widemul(a[5], bb[3]); + + accum2 += widemul(a[2], b[6]); + accum1 += widemul(aa[2], bbb[2]); + accum0 += widemul(a[6], bb[2]); + + accum2 += widemul(a[3], b[5]); + accum1 += widemul(aa[3], bbb[1]); + accum0 += widemul(a[7], bb[1]); + + accum1 -= accum2; + accum0 += accum2; + + c[0] = ((uint64_t)(accum0)) & mask; + c[4] = ((uint64_t)(accum1)) & mask; + + accum0 >>= 56; + accum1 >>= 56; + + accum2 = widemul(a[0], b[1]); + accum1 += widemul(aa[0], bb[1]); + accum0 += widemul(a[4], b[5]); + + accum2 += widemul(a[1], b[0]); + accum1 += widemul(aa[1], bb[0]); + accum0 += widemul(a[5], b[4]); + + accum2 += widemul(a[2], b[7]); + accum1 += widemul(aa[2], bbb[3]); + accum0 += widemul(a[6], bb[3]); + + accum2 += widemul(a[3], b[6]); + accum1 += widemul(aa[3], bbb[2]); + accum0 += widemul(a[7], bb[2]); + + accum1 -= accum2; + accum0 += accum2; + + c[1] = ((uint64_t)(accum0)) & mask; + c[5] = ((uint64_t)(accum1)) & mask; + + accum0 >>= 56; + accum1 >>= 56; + + accum2 = widemul(a[0], b[2]); + accum1 += widemul(aa[0], bb[2]); + accum0 += widemul(a[4], b[6]); + + accum2 += widemul(a[1], b[1]); + accum1 += widemul(aa[1], bb[1]); + accum0 += widemul(a[5], b[5]); + + accum2 += widemul(a[2], b[0]); + accum1 += widemul(aa[2], bb[0]); + accum0 += widemul(a[6], b[4]); + + accum2 += widemul(a[3], b[7]); + accum1 += widemul(aa[3], bbb[3]); + accum0 += widemul(a[7], bb[3]); + + accum1 -= accum2; + accum0 += accum2; + + c[2] = ((uint64_t)(accum0)) & mask; + c[6] = ((uint64_t)(accum1)) & mask; + + accum0 >>= 56; + accum1 >>= 56; + + accum2 = widemul(a[0], b[3]); + accum1 += widemul(aa[0], bb[3]); + accum0 += widemul(a[4], b[7]); + + accum2 += widemul(a[1], b[2]); + accum1 += widemul(aa[1], bb[2]); + accum0 += widemul(a[5], b[6]); + + accum2 += widemul(a[2], b[1]); + accum1 += widemul(aa[2], bb[1]); + accum0 += widemul(a[6], b[5]); + + accum2 += widemul(a[3], b[0]); + accum1 += widemul(aa[3], bb[0]); + accum0 += widemul(a[7], b[4]); + + accum1 -= accum2; + accum0 += accum2; + + c[3] = ((uint64_t)(accum0)) & mask; + c[7] = ((uint64_t)(accum1)) & mask; + + accum0 >>= 56; + accum1 >>= 56; + } /* !I_HATE_UNROLLED_LOOPS */ + + accum0 += accum1; + accum0 += c[4]; + accum1 += c[0]; + c[4] = ((uint64_t)(accum0)) & mask; + c[0] = ((uint64_t)(accum1)) & mask; + + accum0 >>= 56; + accum1 >>= 56; + + c[5] += ((uint64_t)(accum0)); + c[1] += ((uint64_t)(accum1)); +} + +void +p255_mulw ( + p255_t *__restrict__ cs, + const p255_t *as, + uint64_t b +) { + const uint64_t *a = as->limb; + uint64_t *c = cs->limb; + + __uint128_t accum0 = 0, accum4 = 0; + uint64_t mask = (1ull<<56) - 1; + + int i; + for (i=0; i<4; i++) { + accum0 += widemul(b, a[i]); + accum4 += widemul(b, a[i+4]); + c[i] = accum0 & mask; accum0 >>= 56; + c[i+4] = accum4 & mask; accum4 >>= 56; + } + + accum0 += accum4 + c[4]; + c[4] = accum0 & mask; + c[5] += accum0 >> 56; + + accum4 += c[0]; + c[0] = accum4 & mask; + c[1] += accum4 >> 56; +} + +void +p255_sqr ( + p255_t *__restrict__ cs, + const p255_t *as +) { + p255_mul(cs,as,as); // TODO +} + +void +p255_strong_reduce ( + p255_t *a +) { + uint64_t mask = (1ull<<56)-1; + + /* first, clear high */ + a->limb[4] += a->limb[7]>>56; + a->limb[0] += a->limb[7]>>56; + a->limb[7] &= mask; + + /* now the total is less than 2^255 - 2^(255-56) + 2^(255-56+8) < 2p */ + + /* compute total_value - p. No need to reduce mod p. */ + + __int128_t scarry = 0; + int i; + for (i=0; i<8; i++) { + scarry = scarry + a->limb[i] - ((i==4)?mask-1:mask); + a->limb[i] = scarry & mask; + scarry >>= 56; + } + + /* uncommon case: it was >= p, so now scarry = 0 and this = x + * common case: it was < p, so now scarry = -1 and this = x - p + 2^255 + * so let's add back in p. will carry back off the top for 2^255. + */ + + assert(is_zero(scarry) | is_zero(scarry+1)); + + uint64_t scarry_mask = scarry & mask; + __uint128_t carry = 0; + + /* add it back */ + for (i=0; i<8; i++) { + carry = carry + a->limb[i] + ((i==4)?(scarry_mask&~1):scarry_mask); + a->limb[i] = carry & mask; + carry >>= 56; + } + + assert(is_zero(carry + scarry)); +} + +void +p255_serialize ( + uint8_t serial[32], + const struct p255_t *x +) { + int i,j; + p255_t red; + p255_copy(&red, x); + p255_strong_reduce(&red); + for (i=0; i<8; i++) { + for (j=0; j<7; j++) { + serial[7*i+j] = red.limb[i]; + red.limb[i] >>= 8; + } + assert(red.limb[i] == 0); + } +} + +mask_t +p255_deserialize ( + p255_t *x, + const uint8_t serial[32] +) { + int i,j; + for (i=0; i<8; i++) { + uint64_t out = 0; + for (j=0; j<7; j++) { + out |= ((uint64_t)serial[7*i+j])<<(8*j); + } + x->limb[i] = out; + } + + /* Check for reduction. + * + * The idea is to create a variable ge which is all ones (rather, 56 ones) + * if and only if the low $i$ words of $x$ are >= those of p. + * + * Remember p = little_endian(1111,1111,1111,1111,1110,1111,1111,1111) + */ + uint64_t ge = -1, mask = (1ull<<56)-1; + for (i=0; i<4; i++) { + ge &= x->limb[i]; + } + + /* At this point, ge = 1111 iff bottom are all 1111. Now propagate if 1110, or set if 1111 */ + ge = (ge & (x->limb[4] + 1)) | is_zero(x->limb[4] ^ mask); + + /* Propagate the rest */ + for (i=5; i<8; i++) { + ge &= x->limb[i]; + } + + return ~is_zero(ge ^ mask); +} diff --git a/src/p25519/arch_ref64/p25519.h b/src/p25519/arch_ref64/p25519.h new file mode 100644 index 0000000..d291222 --- /dev/null +++ b/src/p25519/arch_ref64/p25519.h @@ -0,0 +1,155 @@ +/* Copyright (c) 2014 Cryptography Research, Inc. + * Released under the MIT License. See LICENSE.txt for license information. + */ +#ifndef __P255_H__ +#define __P255_H__ 1 + +#include +#include +#include + +#include "word.h" + +typedef struct p255_t { + uint64_t limb[5]; +} p255_t; + +#define LBITS 51 +#define FIELD_LITERAL(a,b,c,d,e) {{a,b,c,d,e}} + +#ifdef __cplusplus +extern "C" { +#endif + +static __inline__ void +p255_add_RAW ( + p255_t *out, + const p255_t *a, + const p255_t *b +) __attribute__((unused)); + +static __inline__ void +p255_sub_RAW ( + p255_t *out, + const p255_t *a, + const p255_t *b +) __attribute__((unused)); + +static __inline__ void +p255_copy ( + p255_t *out, + const p255_t *a +) __attribute__((unused)); + +static __inline__ void +p255_weak_reduce ( + p255_t *inout +) __attribute__((unused)); + +void +p255_strong_reduce ( + p255_t *inout +); + +static __inline__ void +p255_bias ( + p255_t *inout, + int amount +) __attribute__((unused)); + +void +p255_mul ( + p255_t *__restrict__ out, + const p255_t *a, + const p255_t *b +); + +void +p255_mulw ( + p255_t *__restrict__ out, + const p255_t *a, + uint64_t b +); + +void +p255_sqr ( + p255_t *__restrict__ out, + const p255_t *a +); + +void +p255_serialize ( + uint8_t serial[32], + const struct p255_t *x +); + +mask_t +p255_deserialize ( + p255_t *x, + const uint8_t serial[32] +); + +/* -------------- Inline functions begin here -------------- */ + +void +p255_add_RAW ( + p255_t *out, + const p255_t *a, + const p255_t *b +) { + unsigned int i; + for (i=0; i<5; i++) { + out->limb[i] = a->limb[i] + b->limb[i]; + } + p255_weak_reduce(out); +} + +void +p255_sub_RAW ( + p255_t *out, + const p255_t *a, + const p255_t *b +) { + unsigned int i; + uint64_t co1 = ((1ull<<51)-1)*2, co2 = co1-36; + for (i=0; i<5; i++) { + out->limb[i] = a->limb[i] - b->limb[i] + ((i==0) ? co2 : co1); + } + p255_weak_reduce(out); +} + +void +p255_copy ( + p255_t *out, + const p255_t *a +) { + memcpy(out,a,sizeof(*a)); +} + +void +p255_bias ( + p255_t *a, + int amt +) { + (void) a; + (void) amt; +} + +void +p255_weak_reduce ( + p255_t *a +) { + uint64_t mask = (1ull<<51) - 1; + uint64_t tmp = a->limb[5] >> 51; + int i; + for (i=7; i>0; i--) { + a->limb[i] = (a->limb[i] & mask) + (a->limb[i-1]>>51); + } + a->limb[0] = (a->limb[0] & mask) + tmp*19; +} + +#ifdef __cplusplus +}; /* extern "C" */ +#endif + +#endif /* __P255_H__ */ diff --git a/src/p25519/f_arithmetic.c b/src/p25519/f_arithmetic.c new file mode 100644 index 0000000..07c140b --- /dev/null +++ b/src/p25519/f_arithmetic.c @@ -0,0 +1,67 @@ +/** + * @cond internal + * @file f_arithmetic.c + * @copyright + * Copyright (c) 2014 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * @author Mike Hamburg + * @brief Field-specific arithmetic. + */ + +#include "field.h" + +extern field_a_t ONE; // TODO + +static const field_a_t SQRT_MINUS_ONE = FIELD_LITERAL( // FIXME goes elsewhere? + 0x61b274a0ea0b0, + 0x0d5a5fc8f189d, + 0x7ef5e9cbd0c60, + 0x78595a6804c9e, + 0x2b8324804fc1d +); + +void +field_isr ( + field_a_t a, + const field_a_t x +) { + field_a_t st[3], tmp1, tmp2; + const struct { unsigned char sh, idx } ops[] = { + {1,2},{1,2},{3,1},{6,0},{1,2},{12,1},{25,1},{25,1},{50,0},{125,0},{2,2},{1,2} + }; + field_cpy(st[0],x); + field_cpy(st[1],x); + field_cpy(st[2],x); + int i; + for (i=0; i + +#include "p25519.h" +#define FIELD_LIT_LIMB_BITS 51 +#define FIELD_BITS 255 +#define field_t p255_t +#define field_mul p255_mul +#define field_sqr p255_sqr +#define field_add_RAW p255_add_RAW +#define field_sub_RAW p255_sub_RAW +#define field_mulw p255_mulw +#define field_bias p255_bias +#define field_isr p255_isr +#define field_inverse p255_inverse +#define field_weak_reduce p255_weak_reduce +#define field_strong_reduce p255_strong_reduce +#define field_serialize p255_serialize +#define field_deserialize p255_deserialize + +#endif /* __F_FIELD_H__ */ diff --git a/test/bench_decaf.cxx b/test/bench_decaf.cxx index 14a56ba..9c868d1 100644 --- a/test/bench_decaf.cxx +++ b/test/bench_decaf.cxx @@ -21,9 +21,9 @@ #include using namespace decaf; -typedef Ed448::Scalar Scalar; -typedef Ed448::Point Point; -typedef Ed448::Precomputed Precomputed; +typedef Ed255::Scalar Scalar; +typedef Ed255::Point Point; +typedef Ed255::Precomputed Precomputed; static __inline__ void __attribute__((unused)) ignore_result ( int result ) { (void)result; } @@ -281,10 +281,10 @@ int main(int argc, char **argv) { if (argc >= 2 && !strcmp(argv[1], "--micro")) micro = true; - decaf_448_public_key_t p1,p2; - decaf_448_private_key_t s1,s2; - decaf_448_symmetric_key_t r1,r2; - decaf_448_signature_t sig1; + decaf_255_public_key_t p1,p2; + decaf_255_private_key_t s1,s2; + decaf_255_symmetric_key_t r1,r2; + decaf_255_signature_t sig1; unsigned char ss[32]; memset(r1,1,sizeof(r1)); @@ -348,25 +348,25 @@ int main(int argc, char **argv) { printf("\nMacro-benchmarks:\n"); for (Benchmark b("Keygen"); b.iter(); ) { - decaf_448_derive_private_key(s1,r1); + decaf_255_derive_private_key(s1,r1); } - decaf_448_private_to_public(p1,s1); - decaf_448_derive_private_key(s2,r2); - decaf_448_private_to_public(p2,s2); + decaf_255_private_to_public(p1,s1); + decaf_255_derive_private_key(s2,r2); + decaf_255_private_to_public(p2,s2); for (Benchmark b("Shared secret"); b.iter(); ) { - decaf_bool_t ret = decaf_448_shared_secret(ss,sizeof(ss),s1,p2); + decaf_bool_t ret = decaf_255_shared_secret(ss,sizeof(ss),s1,p2); ignore_result(ret); assert(ret); } for (Benchmark b("Sign"); b.iter(); ) { - decaf_448_sign(sig1,s1,umessage,lmessage); + decaf_255_sign(sig1,s1,umessage,lmessage); } for (Benchmark b("Verify"); b.iter(); ) { - decaf_bool_t ret = decaf_448_verify(sig1,p1,umessage,lmessage); + decaf_bool_t ret = decaf_255_verify(sig1,p1,umessage,lmessage); umessage[0]++; umessage[1]^=umessage[0]; ignore_result(ret); diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index cdd1099..b6b8f08 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -127,7 +127,7 @@ static void test_arithmetic() { for (int i=0; i::test_arithmetic(); - Tests::test_elligator(); - Tests::test_ec(); + Tests::test_arithmetic(); + Tests::test_elligator(); + Tests::test_ec(); test_decaf(); if (passing) printf("Passed all tests.\n");