diff --git a/Makefile b/Makefile index 40e709c..a1278e4 100644 --- a/Makefile +++ b/Makefile @@ -66,18 +66,20 @@ HEADERS= Makefile $(shell find . -name "*.h") build/timestamp LIBCOMPONENTS= build/goldilocks.o build/barrett_field.o build/crandom.o \ build/$(FIELD).o build/ec_point.o build/scalarmul.o build/sha512.o build/magic.o \ - build/f_arithmetic.o build/arithmetic.o build/decaf.o build/shake.o + build/f_arithmetic.o build/arithmetic.o + +DECAFCOMPONENTS= build/decaf.o build/shake.o build/decaf_crypto.o TESTCOMPONENTS=build/test.o build/test_scalarmul.o build/test_sha512.o \ build/test_pointops.o build/test_arithmetic.o build/test_goldilocks.o build/magic.o \ build/shake.o -BENCHCOMPONENTS=build/bench.o build/shake.o +BENCHCOMPONENTS = build/bench.o build/shake.o BATBASE=ed448goldilocks-bats-$(TODAY) BATNAME=build/$(BATBASE) -all: lib build/test build/bench build/shakesum +all: lib decaf_lib build/test build/bench build/shakesum scan: clean scan-build --use-analyzer=`which clang` \ @@ -85,10 +87,10 @@ scan: clean -enable-checker osx -enable-checker security -enable-checker unix \ make build/bench build/test build/goldilocks.so -build/bench: $(LIBCOMPONENTS) $(BENCHCOMPONENTS) +build/bench: $(LIBCOMPONENTS) $(BENCHCOMPONENTS) $(DECAFCOMPONENTS) $(LD) $(LDFLAGS) -o $@ $^ -build/test: $(LIBCOMPONENTS) $(TESTCOMPONENTS) +build/test: $(LIBCOMPONENTS) $(TESTCOMPONENTS) $(DECAFCOMPONENTS) $(LD) $(LDFLAGS) -o $@ $^ -lgmp build/shakesum: build/shakesum.o build/shake.o @@ -96,6 +98,8 @@ build/shakesum: build/shakesum.o build/shake.o lib: build/goldilocks.so +decaf_lib: build/decaf.so + build/goldilocks.so: $(LIBCOMPONENTS) rm -f $@ ifeq ($(UNAME),Darwin) @@ -107,6 +111,17 @@ else ln -sf `basename $@` build/goldilocks.so.1 endif +build/decaf.so: $(DECAFCOMPONENTS) + rm -f $@ +ifeq ($(UNAME),Darwin) + libtool -macosx_version_min 10.6 -dynamic -dead_strip -lc -x -o $@ \ + $(DECAFCOMPONENTS) +else + $(LD) $(LDFLAGS) -shared -Wl,-soname,goldilocks.so.1 -Wl,--gc-sections -o $@ $(DECAFCOMPONENTS) + strip --discard-all $@ + ln -sf `basename $@` build/goldilocks.so.1 +endif + build/timestamp: mkdir -p build touch $@ diff --git a/include/decaf.h b/include/decaf.h index b7f0ff2..a1c2b0b 100644 --- a/include/decaf.h +++ b/include/decaf.h @@ -1,10 +1,11 @@ -/* Copyright (c) 2015 Cryptography Research, Inc. - * Released under the MIT License. See LICENSE.txt for license information. - */ - /** * @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 @@ -24,6 +25,7 @@ #define __DECAF_448_H__ 1 #include +#include /* Goldilocks' build flags default to hidden and stripping executables. */ /** @cond internal */ @@ -113,18 +115,30 @@ extern "C" { /** * @brief Read a scalar from wire format or from bytes. * - * Return DECAF_SUCCESS if the scalar was in reduced form. This - * function is not WARN_UNUSED because eg challenges in signatures - * may need to be longer. - * - * TODO: create a decode long function, and make this WARN_UNUSED. - * * @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 s, const unsigned char ser[DECAF_448_SCALAR_BYTES] +) API_VIS WARN_UNUSED NONNULL2; + +/** + * @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 s, + const unsigned char *ser, + size_t ser_len ) API_VIS NONNULL2; /** @@ -416,9 +430,24 @@ void decaf_448_point_from_hash_uniform ( decaf_448_point_t pt, const unsigned char hashed_data[2*DECAF_448_SER_BYTES] ) API_VIS NONNULL2; - + +/** + * @brief Overwrite data with zeros. Use memset_s if available. + */ +void decaf_bzero ( + void *data, + size_t size +) NONNULL1 API_VIS; + +/** + * @brief Overwrite scalar with zeros. + */ +void decaf_448_scalar_destroy ( + decaf_448_scalar_t scalar +) NONNULL1 API_VIS; + /* TODO: functions to invert point_from_hash?? */ - + #undef API_VIS #undef WARN_UNUSED #undef NONNULL1 diff --git a/include/decaf_crypto.h b/include/decaf_crypto.h new file mode 100644 index 0000000..a92911c --- /dev/null +++ b/include/decaf_crypto.h @@ -0,0 +1,108 @@ +/** + * @file decaf_crypto.h + * @copyright + * Copyright (c) 2015 Cryptography Research, Inc. \n + * Released under the MIT License. See LICENSE.txt for license information. + * @author Mike Hamburg + * @brief Decaf cyrpto routines. + * @warning Experimental! The names, parameter orders etc are likely to change. + */ + +#ifndef __DECAF_CRYPTO_H__ +#define __DECAF_CRYPTO_H__ 1 + +#include "decaf.h" +#include "shake.h" + +#define DECAF_448_SYMMETRIC_KEY_BYTES 32 +/** @cond internal */ +#define API_VIS __attribute__((visibility("default"))) +#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 NONNULL134 __attribute__((nonnull(1,3,4))) +#define NONNULL5 __attribute__((nonnull(1,2,3,4,5))) +/** @endcond */ + +/** A symmetric key, the compressed point of a private key. */ +typedef unsigned char decaf_448_symmetric_key_t[DECAF_448_SYMMETRIC_KEY_BYTES]; + +/** An encoded public key. */ +typedef unsigned char decaf_448_public_key_t[DECAF_448_SER_BYTES]; + +/** A private key. */ +typedef struct { + decaf_448_symmetric_key_t sym; + decaf_448_scalar_t secret_scalar; + decaf_448_public_key_t pub; +} decaf_448_private_key_t[1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Derive a key from its compressed form. + * @param [out] privkey The derived private key. + * @param [in] proto The compressed or proto-key, which must be 32 random bytes. + */ +void decaf_448_derive_private_key ( + decaf_448_private_key_t priv, + const decaf_448_symmetric_key_t proto +) NONNULL2 API_VIS; + +/** + * @brief Destroy a private key. + */ +void decaf_448_destroy_private_key ( + decaf_448_private_key_t priv +) NONNULL1 API_VIS; + +/** + * @brief Convert a private key to a public one. + * @param [out] pub The extracted private key. + * @param [in] priv The private key. + */ +void decaf_448_private_to_public ( + decaf_448_public_key_t pub, + const decaf_448_private_key_t priv +) NONNULL2 API_VIS; + +/** + * @brief Compute a Diffie-Hellman shared secret. + * + * This is an example routine; real protocols would use something + * protocol-specific. + * + * @param [out] shared A buffer to store the shared secret. + * @param [in] shared_bytes The size of the buffer. + * @param [in] my_privkey My private key. + * @param [in] your_pubkey Your public key. + * + * @retval DECAF_SUCCESS Key exchange was successful. + * @retval DECAF_FAILURE Key exchange failed. + */ +decaf_bool_t +decaf_448_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 +) NONNULL134 WARN_UNUSED API_VIS; + +#undef API_VIS +#undef WARN_UNUSED +#undef NONNULL1 +#undef NONNULL2 +#undef NONNULL3 +#undef NONNULL134 +#undef NONNULL5 + +#ifdef __cplusplus +}; /* extern "C" */ +#endif + +#endif /* __DECAF_CRYPTO_H__ */ + + diff --git a/include/shake.h b/include/shake.h index 86d9817..113f8ab 100644 --- a/include/shake.h +++ b/include/shake.h @@ -13,6 +13,7 @@ #define __SHAKE_H__ #include +#include #define API_VIS __attribute__((visibility("default"))) @@ -98,7 +99,7 @@ void sponge_hash ( sha3_output(sponge, out, outlen); \ sponge_init(sponge, SHAKE##n##_params); \ } \ - static inline void shake##n##_hash(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen ) { \ + static inline void shake##n##_hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { \ sponge_hash(in,inlen,out,outlen,SHAKE##n##_params); \ } \ static inline void shake##n##_destroy( keccak_sponge_t sponge ) { \ @@ -117,7 +118,7 @@ void sponge_hash ( sha3_output(sponge, out, outlen); \ sponge_init(sponge, SHA3_##n##_params); \ } \ - static inline void sha3_##n##_hash(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen ) { \ + static inline void sha3_##n##_hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen) { \ sponge_hash(in,inlen,out,outlen,SHA3_##n##_params); \ } \ static inline void sha3_##n##_destroy( keccak_sponge_t sponge ) { \ diff --git a/src/decaf.c b/src/decaf.c index b763bf9..ae791ba 100644 --- a/src/decaf.c +++ b/src/decaf.c @@ -8,7 +8,9 @@ * @brief Decaf high-level functions. */ +#define __STDC_WANT_LIB_EXT1__ 1 /* for memset_s */ #include "decaf.h" +#include #define WBITS DECAF_WORD_BITS @@ -548,6 +550,65 @@ decaf_bool_t decaf_448_scalar_decode( return accum; } +void decaf_bzero ( + void *s, + size_t size +) { +#ifdef __STDC_LIB_EXT1__ + memset_s(s, size, 0, size); +#else + volatile uint8_t *destroy = (volatile uint8_t *)s; + unsigned i; + for (i=0; i + +void decaf_448_derive_private_key ( + decaf_448_private_key_t priv, + const decaf_448_symmetric_key_t proto +) { + const char *magic = "decaf_448_derive_private_key"; + keccak_sponge_t sponge; + uint8_t encoded_scalar[64]; + decaf_448_point_t pub; + shake256_init(sponge); + shake256_update(sponge, proto, sizeof(decaf_448_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)); + + decaf_448_precomputed_scalarmul(pub, decaf_448_precomputed_base, priv->secret_scalar); + decaf_448_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_bzero((void*)priv, sizeof(decaf_448_private_key_t)); +} + +void decaf_448_private_to_public ( + decaf_448_public_key_t pub, + const decaf_448_private_key_t priv +) { + memcpy(pub, priv->pub, sizeof(decaf_448_public_key_t)); +} + +decaf_bool_t +decaf_448_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 +) { + decaf_448_point_t point; + uint8_t ss_ser[DECAF_448_SER_BYTES]; + const char *nope = "decaf_448_ss_invalid"; + decaf_bool_t ret = decaf_448_point_decode(point, your_pubkey, DECAF_FALSE); + decaf_448_point_scalarmul(point, point, my_privkey->secret_scalar); + + 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: + * = -> delta = 0 -> hi delta-1 = -1, hi delta = 0 + * > -> delta > 0 -> hi delta-1 = 0, hi delta = 0 + * < -> delta < 0 -> hi delta-1 = (doesnt matter), hi delta = -1 + */ + less &= delta-1; + less |= delta; + } + less >>= 8; + + keccak_sponge_t sponge; + shake256_init(sponge); + + /* update the lesser */ + for (i=0; ipub[i] & less) | (your_pubkey[i] & ~less); + } + shake256_update(sponge, ss_ser, sizeof(ss_ser)); + + /* update the greater */ + for (i=0; ipub[i] & ~less) | (your_pubkey[i] & less); + } + shake256_update(sponge, ss_ser, sizeof(ss_ser)); + + /* encode the shared secret but mask with secret key */ + decaf_448_point_encode(ss_ser, point); + + /* If invalid, then replace ... */ + for (i=0; isym)) { + ss_ser[i] |= my_privkey->sym[i] & ~ret; + } else if (i - sizeof(my_privkey->sym) < strlen(nope)) { + ss_ser[i] |= nope[i-sizeof(my_privkey->sym)] & ~ret; + } + } + + shake256_update(sponge, ss_ser, sizeof(ss_ser)); + shake256_final(sponge, shared, shared_bytes); + shake256_destroy(sponge); + + decaf_bzero(ss_ser, sizeof(ss_ser)); + + return ret; +} diff --git a/src/shake.c b/src/shake.c index 8ed0842..ff934ab 100644 --- a/src/shake.c +++ b/src/shake.c @@ -197,6 +197,7 @@ void sha3_output ( } } +/** TODO: unify with decaf_bzero? */ void sponge_destroy ( keccak_sponge_t sponge ) { diff --git a/test/bench.c b/test/bench.c index 346329c..2283fa0 100644 --- a/test/bench.c +++ b/test/bench.c @@ -17,6 +17,7 @@ #include "goldilocks.h" #include "sha512.h" #include "decaf.h" +#include "decaf_crypto.h" #include "shake.h" static __inline__ void @@ -65,15 +66,6 @@ field_print_full ( printf("\n"); } -static void q448_print( const char *descr, const word_t secret[SCALAR_WORDS] ) { - int j; - printf("%s = 0x", descr); - for (j=SCALAR_WORDS-1; j>=0; j--) { - printf(PRIxWORDfull, secret[j]); - } - printf("\n"); -} - #ifndef N_TESTS_BASE #define N_TESTS_BASE 10000 #endif @@ -687,135 +679,37 @@ int main(int argc, char **argv) { when = now() - when; printf("ecdh pre: %5.1fµs\n", when * 1e6 / i); - printf("\nTesting...\n"); + printf("\nDecaf slow:\n"); + decaf_448_symmetric_key_t sym[2] = {{0},{1}}; + decaf_448_private_key_t dpriv[2]; + decaf_448_public_key_t dpub[2]; - int failures=0, successes = 0; - for (i=0; i