diff options
author | lloyd <[email protected]> | 2014-12-27 17:50:57 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-12-27 17:50:57 +0000 |
commit | d0daf875978848c3edf65c7b3683a21605f72e64 (patch) | |
tree | 46690afadfb5e9acb766468f7f7481bb1244049d | |
parent | 675c2e324268ebce7e2c665389ebd57d38083200 (diff) |
Add Curve25519 based on curve25519-donna by Adam Langley.
This uses only the c64 version from curve25519-donna; on systems that
don't have a native uint128_t type, a donna128 type stands in for just
enough 128-bit operations to satisfy donna.cpp
-rw-r--r-- | doc/credits.rst | 4 | ||||
-rw-r--r-- | doc/license.rst | 1 | ||||
-rw-r--r-- | doc/relnotes/1_11_12.rst | 4 | ||||
-rw-r--r-- | src/cmd/keygen.cpp | 9 | ||||
-rw-r--r-- | src/cmd/speed_pk.cpp | 56 | ||||
-rw-r--r-- | src/lib/engine/core_engine/def_pk_ops.cpp | 9 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/curve25519.cpp | 115 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/curve25519.h | 94 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/donna.cpp | 456 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/donna128.h | 116 | ||||
-rw-r--r-- | src/lib/pubkey/curve25519/info.txt | 9 | ||||
-rw-r--r-- | src/lib/pubkey/pk_algs.cpp | 14 | ||||
-rw-r--r-- | src/tests/data/pubkey/c25519_scalar.vec | 79 | ||||
-rw-r--r-- | src/tests/test_c25519.cpp | 56 | ||||
-rw-r--r-- | src/tests/tests.cpp | 1 | ||||
-rw-r--r-- | src/tests/tests.h | 1 |
16 files changed, 1024 insertions, 0 deletions
diff --git a/doc/credits.rst b/doc/credits.rst index 633d69f79..eda4bcb64 100644 --- a/doc/credits.rst +++ b/doc/credits.rst @@ -55,6 +55,10 @@ snail-mail address (S), and Bitcoin address (B). D: LZMA compression module S: Czech Republic + N: Adam Langley + E: [email protected] + D: Curve25519 + N: Jack Lloyd W: http://www.randombit.net/ diff --git a/doc/license.rst b/doc/license.rst index a8335f7ea..0bf7226fc 100644 --- a/doc/license.rst +++ b/doc/license.rst @@ -24,6 +24,7 @@ Botan (http://botan.randombit.net/) is distributed under these terms:: 2007 Patrick Sona 2008 Copyright Projet SECRET, INRIA, Rocquencourt 2008 Bhaskar Biswas and Nicolas Sendrier + 2008 Google Inc. 2010 Olivier de Gaalon 2012 Vojtech Kral 2012-2014 Markus Wanner diff --git a/doc/relnotes/1_11_12.rst b/doc/relnotes/1_11_12.rst index 9a2a5dad7..344dc95dc 100644 --- a/doc/relnotes/1_11_12.rst +++ b/doc/relnotes/1_11_12.rst @@ -1,3 +1,7 @@ Version 1.11.12, Not Yet Released ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* Add Curve25519. The implementation is based on curve25519-donna-c64.c + by Adam Langley. New (completely non-standard) OIDs and + formats for encrypting Curve25519 keys under PKCS #8 and including + them in certificates and CRLs have been defined. diff --git a/src/cmd/keygen.cpp b/src/cmd/keygen.cpp index 3054b98ee..ae23ce45e 100644 --- a/src/cmd/keygen.cpp +++ b/src/cmd/keygen.cpp @@ -17,6 +17,10 @@ #include <botan/ecdsa.h> #endif +#if defined(BOTAN_HAS_CURVE_25519) +#include <botan/curve25519.h> +#endif + using namespace Botan; namespace { @@ -55,6 +59,11 @@ Private_Key* gen_key(RandomNumberGenerator& rng, const std::string& algo, size_t } #endif +#if defined(BOTAN_HAS_CURVE_25519) + if(algo == "curve25519") + return new Curve25519_PrivateKey(rng); +#endif + throw std::runtime_error("Unknown algorithm " + algo); } diff --git a/src/cmd/speed_pk.cpp b/src/cmd/speed_pk.cpp index 6d20065cc..d978cde9d 100644 --- a/src/cmd/speed_pk.cpp +++ b/src/cmd/speed_pk.cpp @@ -32,6 +32,10 @@ #include <botan/dh.h> #endif +#if defined(BOTAN_HAS_CURVE_25519) + #include <botan/curve25519.h> +#endif + #if defined(BOTAN_HAS_NYBERG_RUEPPEL) #include <botan/nr.h> #endif @@ -507,6 +511,53 @@ void benchmark_dsa_nr(RandomNumberGenerator& rng, #endif } +#if defined(BOTAN_HAS_CURVE_25519) +void benchmark_curve25519(RandomNumberGenerator& rng, + double seconds, + Benchmark_Report& report) + { + Timer keygen_timer("keygen"); + Timer kex_timer("key exchange"); + + while(kex_timer.seconds() < seconds) + { + keygen_timer.start(); + Curve25519_PrivateKey key1(rng); + keygen_timer.stop(); + + keygen_timer.start(); + Curve25519_PrivateKey key2(rng); + keygen_timer.stop(); + + PK_Key_Agreement ka1(key1, "KDF2(SHA-256)"); + PK_Key_Agreement ka2(key2, "KDF2(SHA-256)"); + + SymmetricKey secret1, secret2; + + for(size_t i = 0; i != 1000; ++i) + { + if(kex_timer.seconds() > seconds) + break; + + kex_timer.start(); + secret1 = ka1.derive_key(32, key2.public_value()); + kex_timer.stop(); + + kex_timer.start(); + secret2 = ka2.derive_key(32, key1.public_value()); + kex_timer.stop(); + + if(secret1 != secret2) + std::cerr << "Curve25519 secrets did not match\n"; + } + } + + const std::string nm = "Curve25519"; + report.report(nm, keygen_timer); + report.report(nm, kex_timer); + } +#endif + #ifdef BOTAN_HAS_DIFFIE_HELLMAN void benchmark_dh(RandomNumberGenerator& rng, double seconds, @@ -793,6 +844,11 @@ void bench_pk(RandomNumberGenerator& rng, benchmark_gost_3410(rng, seconds, report); #endif +#if defined(BOTAN_HAS_CURVE_25519) + if(algo == "All" || algo == "Curve25519") + benchmark_curve25519(rng, seconds, report); +#endif + #if defined(BOTAN_HAS_DIFFIE_HELLMAN) if(algo == "All" || algo == "DH") benchmark_dh(rng, seconds, report); diff --git a/src/lib/engine/core_engine/def_pk_ops.cpp b/src/lib/engine/core_engine/def_pk_ops.cpp index b21877b16..cbe7d27fc 100644 --- a/src/lib/engine/core_engine/def_pk_ops.cpp +++ b/src/lib/engine/core_engine/def_pk_ops.cpp @@ -43,6 +43,10 @@ #include <botan/ecdh.h> #endif +#if defined(BOTAN_HAS_CURVE_25519) + #include <botan/curve25519.h> +#endif + namespace Botan { PK_Ops::Encryption* @@ -90,6 +94,11 @@ Core_Engine::get_key_agreement_op(const Private_Key& key, RandomNumberGenerator& return new ECDH_KA_Operation(*ecdh); #endif +#if defined(BOTAN_HAS_CURVE_25519) + if(const Curve25519_PrivateKey* c25519 = dynamic_cast<const Curve25519_PrivateKey*>(&key)) + return new Curve25519_KA_Operation(*c25519); +#endif + return nullptr; } diff --git a/src/lib/pubkey/curve25519/curve25519.cpp b/src/lib/pubkey/curve25519/curve25519.cpp new file mode 100644 index 000000000..fe30e37d9 --- /dev/null +++ b/src/lib/pubkey/curve25519/curve25519.cpp @@ -0,0 +1,115 @@ +/* +* Curve25519 +* (C) 2014 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/curve25519.h> +#include <botan/ber_dec.h> +#include <botan/der_enc.h> + +namespace Botan { + +namespace { + +void size_check(size_t size, const char* thing) + { + if(size != 32) + throw Decoding_Error("Invalid size " + std::to_string(size) + " for Curve25519 " + thing); + } + +secure_vector<byte> curve25519(const secure_vector<byte>& secret, + const byte pubval[32]) + { + secure_vector<byte> out(32); + const int rc = curve25519_donna(&out[0], &secret[0], &pubval[0]); + BOTAN_ASSERT_EQUAL(rc, 0, "Return value of curve25519_donna is ok"); + return out; + } + +secure_vector<byte> curve25519_basepoint(const secure_vector<byte>& secret) + { + const byte basepoint[32] = { 9 }; + return curve25519(secret, basepoint); + } + +} + +AlgorithmIdentifier Curve25519_PublicKey::algorithm_identifier() const + { + return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_NULL_PARAM); + } + +bool Curve25519_PublicKey::check_key(RandomNumberGenerator&, bool) const + { + return true; // no tests possible? + } + +Curve25519_PublicKey::Curve25519_PublicKey(const AlgorithmIdentifier&, + const secure_vector<byte>& key_bits) + { + BER_Decoder(key_bits) + .start_cons(SEQUENCE) + .decode(m_public, OCTET_STRING) + .verify_end() + .end_cons(); + + size_check(m_public.size(), "public key"); + } + +std::vector<byte> Curve25519_PublicKey::x509_subject_public_key() const + { + return DER_Encoder() + .start_cons(SEQUENCE) + .encode(m_public, OCTET_STRING) + .end_cons() + .get_contents_unlocked(); + } + +Curve25519_PrivateKey::Curve25519_PrivateKey(RandomNumberGenerator& rng) + { + m_private = rng.random_vec(32); + m_public = curve25519_basepoint(m_private); + } + +Curve25519_PrivateKey::Curve25519_PrivateKey(const AlgorithmIdentifier&, + const secure_vector<byte>& key_bits, + RandomNumberGenerator& rng) + { + BER_Decoder(key_bits) + .start_cons(SEQUENCE) + .decode(m_public, OCTET_STRING) + .decode(m_private, OCTET_STRING) + .verify_end() + .end_cons(); + + size_check(m_public.size(), "public key"); + size_check(m_private.size(), "private key"); + + load_check(rng); + } + +secure_vector<byte> Curve25519_PrivateKey::pkcs8_private_key() const + { + return DER_Encoder() + .start_cons(SEQUENCE) + .encode(m_public, OCTET_STRING) + .encode(m_private, OCTET_STRING) + .end_cons() + .get_contents(); + } + +bool Curve25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const + { + return curve25519_basepoint(m_private) == m_public; + } + +secure_vector<byte> Curve25519_PrivateKey::agree(const byte w[], size_t w_len) const + { + size_check(w_len, "public value"); + + return curve25519(m_private, w); + } + +} diff --git a/src/lib/pubkey/curve25519/curve25519.h b/src/lib/pubkey/curve25519/curve25519.h new file mode 100644 index 000000000..718ebf05d --- /dev/null +++ b/src/lib/pubkey/curve25519/curve25519.h @@ -0,0 +1,94 @@ +/* +* Curve25519 +* (C) 2014 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CURVE_25519_H__ +#define BOTAN_CURVE_25519_H__ + +#include <botan/pk_keys.h> +#include <botan/pk_ops.h> + +namespace Botan { + +class BOTAN_DLL Curve25519_PublicKey : public virtual Public_Key + { + public: + std::string algo_name() const override { return "Curve25519"; } + + size_t estimated_strength() const override { return 128; } + + size_t max_input_bits() const { return 256; } + + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + + AlgorithmIdentifier algorithm_identifier() const override; + + std::vector<byte> x509_subject_public_key() const override; + + std::vector<byte> public_value() const { return unlock(m_public); } + + Curve25519_PublicKey(const AlgorithmIdentifier& alg_id, + const secure_vector<byte>& key_bits); + + Curve25519_PublicKey(const secure_vector<byte>& pub) : m_public(pub) {} + protected: + Curve25519_PublicKey() {} + secure_vector<byte> m_public; + }; + +class BOTAN_DLL Curve25519_PrivateKey : public Curve25519_PublicKey, + public virtual Private_Key, + public virtual PK_Key_Agreement_Key + { + public: + Curve25519_PrivateKey(const AlgorithmIdentifier& alg_id, + const secure_vector<byte>& key_bits, + RandomNumberGenerator& rng); + + Curve25519_PrivateKey(RandomNumberGenerator& rng); + + Curve25519_PrivateKey(const secure_vector<byte>& secret_key); + + std::vector<byte> public_value() const override { return Curve25519_PublicKey::public_value(); } + + secure_vector<byte> agree(const byte w[], size_t w_len) const; + + const secure_vector<byte>& get_x() const { return m_private; } + + secure_vector<byte> pkcs8_private_key() const override; + + bool check_key(RandomNumberGenerator& rng, bool strong) const override; + private: + secure_vector<byte> m_private; + }; + +/** +* Curve25519 operation +*/ +class BOTAN_DLL Curve25519_KA_Operation : public PK_Ops::Key_Agreement + { + public: + Curve25519_KA_Operation(const Curve25519_PrivateKey& key) : m_key(key) {} + + secure_vector<byte> agree(const byte w[], size_t w_len) + { + return m_key.agree(w, w_len); + } + private: + const Curve25519_PrivateKey& m_key; + }; + +/* +* The types above are just wrappers for curve25519_donna, plus defining +* encodings for public and private keys. +*/ +int BOTAN_DLL curve25519_donna(uint8_t mypublic[32], + const uint8_t secret[32], + const uint8_t basepoint[32]); + +} + +#endif diff --git a/src/lib/pubkey/curve25519/donna.cpp b/src/lib/pubkey/curve25519/donna.cpp new file mode 100644 index 000000000..29d40ce13 --- /dev/null +++ b/src/lib/pubkey/curve25519/donna.cpp @@ -0,0 +1,456 @@ +/* +* curve25519-donna-c64.c from github.com/agl/curve25519-donna +* revision 80ad9b9930c9baef5829dd2a235b6b7646d32a8e +*/ + +/* Copyright 2008, Google Inc. + * All rights reserved. + * + * Code released into the public domain. + * + * curve25519-donna: Curve25519 elliptic curve, public key function + * + * http://code.google.com/p/curve25519-donna/ + * + * Adam Langley <[email protected]> + * + * Derived from public domain C code by Daniel J. Bernstein <[email protected]> + * + * More information about curve25519 can be found here + * http://cr.yp.to/ecdh.html + * + * djb's sample implementation of curve25519 is written in a special assembly + * language called qhasm and uses the floating point registers. + * + * This is, almost, a clean room reimplementation from the curve25519 paper. It + * uses many of the tricks described therein. Only the crecip function is taken + * from the sample implementation. + */ + +#include <botan/curve25519.h> +#include <botan/mul128.h> +#include <botan/loadstor.h> +#include <string.h> + +#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) + #include <botan/internal/donna128.h> +#endif + +namespace Botan { + +typedef byte u8; +typedef u64bit limb; +typedef limb felem[5]; + +#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128) +typedef donna128 uint128_t; + +#else + +inline u64bit carry_shift(const uint128_t a, size_t shift) + { + return static_cast<u64bit>(a >> shift); + } + +inline u64bit combine_lower(const uint128_t a, size_t s1, + const uint128_t b, size_t s2) + { + return static_cast<u64bit>((a >> s1) | (b << s2)); + } + +#endif + +/* Sum two numbers: output += in */ +static inline void +fsum(limb *output, const limb *in) { + output[0] += in[0]; + output[1] += in[1]; + output[2] += in[2]; + output[3] += in[3]; + output[4] += in[4]; +} + +/* Find the difference of two numbers: output = in - output + * (note the order of the arguments!) + * + * Assumes that out[i] < 2**52 + * On return, out[i] < 2**55 + */ +static inline void +fdifference_backwards(felem out, const felem in) { + /* 152 is 19 << 3 */ + static const limb two54m152 = (static_cast<limb>(1) << 54) - 152; + static const limb two54m8 = (static_cast<limb>(1) << 54) - 8; + + out[0] = in[0] + two54m152 - out[0]; + out[1] = in[1] + two54m8 - out[1]; + out[2] = in[2] + two54m8 - out[2]; + out[3] = in[3] + two54m8 - out[3]; + out[4] = in[4] + two54m8 - out[4]; +} + +/* Multiply a number by a scalar: output = in * scalar */ +static inline void +fscalar_product(felem output, const felem in, const limb scalar) { + uint128_t a = uint128_t(in[0]) * scalar; + output[0] = a & 0x7ffffffffffff; + + a = uint128_t(in[1]) * scalar + carry_shift(a, 51); + output[1] = a & 0x7ffffffffffff; + + a = uint128_t(in[2]) * scalar + carry_shift(a, 51); + output[2] = a & 0x7ffffffffffff; + + a = uint128_t(in[3]) * scalar + carry_shift(a, 51); + output[3] = a & 0x7ffffffffffff; + + a = uint128_t(in[4]) * scalar + carry_shift(a, 51); + output[4] = a & 0x7ffffffffffff; + + output[0] += carry_shift(a, 51) * 19; +} + +/* Multiply two numbers: output = in2 * in + * + * output must be distinct to both inputs. The inputs are reduced coefficient + * form, the output is not. + * + * Assumes that in[i] < 2**55 and likewise for in2. + * On return, output[i] < 2**52 + */ +static inline void +fmul(felem output, const felem in2, const felem in) { + uint128_t t[5]; + limb r0,r1,r2,r3,r4,s0,s1,s2,s3,s4,c; + + r0 = in[0]; + r1 = in[1]; + r2 = in[2]; + r3 = in[3]; + r4 = in[4]; + + s0 = in2[0]; + s1 = in2[1]; + s2 = in2[2]; + s3 = in2[3]; + s4 = in2[4]; + + t[0] = uint128_t(r0) * s0; + t[1] = uint128_t(r0) * s1 + uint128_t(r1) * s0; + t[2] = uint128_t(r0) * s2 + uint128_t(r2) * s0 + uint128_t(r1) * s1; + t[3] = uint128_t(r0) * s3 + uint128_t(r3) * s0 + uint128_t(r1) * s2 + uint128_t(r2) * s1; + t[4] = uint128_t(r0) * s4 + uint128_t(r4) * s0 + uint128_t(r3) * s1 + uint128_t(r1) * s3 + uint128_t(r2) * s2; + + r4 *= 19; + r1 *= 19; + r2 *= 19; + r3 *= 19; + + t[0] += uint128_t(r4) * s1 + uint128_t(r1) * s4 + uint128_t(r2) * s3 + uint128_t(r3) * s2; + t[1] += uint128_t(r4) * s2 + uint128_t(r2) * s4 + uint128_t(r3) * s3; + t[2] += uint128_t(r4) * s3 + uint128_t(r3) * s4; + t[3] += uint128_t(r4) * s4; + + r0 = t[0] & 0x7ffffffffffff; c = carry_shift(t[0], 51); + t[1] += c; r1 = t[1] & 0x7ffffffffffff; c = carry_shift(t[1], 51); + t[2] += c; r2 = t[2] & 0x7ffffffffffff; c = carry_shift(t[2], 51); + t[3] += c; r3 = t[3] & 0x7ffffffffffff; c = carry_shift(t[3], 51); + t[4] += c; r4 = t[4] & 0x7ffffffffffff; c = carry_shift(t[4], 51); + r0 += c * 19; c = carry_shift(r0, 51); r0 = r0 & 0x7ffffffffffff; + r1 += c; c = carry_shift(r1, 51); r1 = r1 & 0x7ffffffffffff; + r2 += c; + + output[0] = r0; + output[1] = r1; + output[2] = r2; + output[3] = r3; + output[4] = r4; +} + +static inline void fsquare_times(felem output, const felem in, limb count) { + uint128_t t[5]; + limb r0,r1,r2,r3,r4,c; + limb d0,d1,d2,d4,d419; + + r0 = in[0]; + r1 = in[1]; + r2 = in[2]; + r3 = in[3]; + r4 = in[4]; + + do { + d0 = r0 * 2; + d1 = r1 * 2; + d2 = r2 * 2 * 19; + d419 = r4 * 19; + d4 = d419 * 2; + + t[0] = uint128_t(r0) * r0 + uint128_t(d4) * r1 + uint128_t(d2) * (r3 ); + t[1] = uint128_t(d0) * r1 + uint128_t(d4) * r2 + uint128_t(r3) * (r3 * 19); + t[2] = uint128_t(d0) * r2 + uint128_t(r1) * r1 + uint128_t(d4) * (r3 ); + t[3] = uint128_t(d0) * r3 + uint128_t(d1) * r2 + uint128_t(r4) * (d419 ); + t[4] = uint128_t(d0) * r4 + uint128_t(d1) * r3 + uint128_t(r2) * (r2 ); + + r0 = t[0] & 0x7ffffffffffff; c = carry_shift(t[0], 51); + t[1] += c; r1 = t[1] & 0x7ffffffffffff; c = carry_shift(t[1], 51); + t[2] += c; r2 = t[2] & 0x7ffffffffffff; c = carry_shift(t[2], 51); + t[3] += c; r3 = t[3] & 0x7ffffffffffff; c = carry_shift(t[3], 51); + t[4] += c; r4 = t[4] & 0x7ffffffffffff; c = carry_shift(t[4], 51); + r0 += c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff; + r1 += c; c = r1 >> 51; r1 = r1 & 0x7ffffffffffff; + r2 += c; + } while(--count); + + output[0] = r0; + output[1] = r1; + output[2] = r2; + output[3] = r3; + output[4] = r4; +} + +/* Load a little-endian 64-bit number */ +static limb +load_limb(const u8 *in) { + return load_le<u64bit>(in, 0); +} + +static void +store_limb(u8 *out, limb in) { + store_le(in, out); +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +static void +fexpand(limb *output, const u8 *in) { + output[0] = load_limb(in) & 0x7ffffffffffff; + output[1] = (load_limb(in+6) >> 3) & 0x7ffffffffffff; + output[2] = (load_limb(in+12) >> 6) & 0x7ffffffffffff; + output[3] = (load_limb(in+19) >> 1) & 0x7ffffffffffff; + output[4] = (load_limb(in+24) >> 12) & 0x7ffffffffffff; +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +static void +fcontract(u8 *output, const felem input) { + uint128_t t[5]; + + t[0] = input[0]; + t[1] = input[1]; + t[2] = input[2]; + t[3] = input[3]; + t[4] = input[4]; + + t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; + t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; + t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; + t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; + t[0] += (t[4] >> 51) * 19; t[4] &= 0x7ffffffffffff; + + t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; + t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; + t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; + t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; + t[0] += (t[4] >> 51) * 19; t[4] &= 0x7ffffffffffff; + + /* now t is between 0 and 2^255-1, properly carried. */ + /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */ + + t[0] += 19; + + t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; + t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; + t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; + t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; + t[0] += (t[4] >> 51) * 19; t[4] &= 0x7ffffffffffff; + + /* now between 19 and 2^255-1 in both cases, and offset by 19. */ + + t[0] += 0x8000000000000 - 19; + t[1] += 0x8000000000000 - 1; + t[2] += 0x8000000000000 - 1; + t[3] += 0x8000000000000 - 1; + t[4] += 0x8000000000000 - 1; + + /* now between 2^255 and 2^256-20, and offset by 2^255. */ + + t[1] += t[0] >> 51; t[0] &= 0x7ffffffffffff; + t[2] += t[1] >> 51; t[1] &= 0x7ffffffffffff; + t[3] += t[2] >> 51; t[2] &= 0x7ffffffffffff; + t[4] += t[3] >> 51; t[3] &= 0x7ffffffffffff; + t[4] &= 0x7ffffffffffff; + + store_limb(output, combine_lower(t[0], 0, t[1], 51)); + store_limb(output+8, combine_lower(t[1], 13, t[2], 38)); + store_limb(output+16, combine_lower(t[2], 26, t[3], 25)); + store_limb(output+24, combine_lower(t[3], 39, t[4], 12)); +} + +/* Input: Q, Q', Q-Q' + * Output: 2Q, Q+Q' + * + * x2 z3: long form + * x3 z3: long form + * x z: short form, destroyed + * xprime zprime: short form, destroyed + * qmqp: short form, preserved + */ +static void +fmonty(limb *x2, limb *z2, /* output 2Q */ + limb *x3, limb *z3, /* output Q + Q' */ + limb *x, limb *z, /* input Q */ + limb *xprime, limb *zprime, /* input Q' */ + const limb *qmqp /* input Q - Q' */) { + limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5], + zzprime[5], zzzprime[5]; + + memcpy(origx, x, 5 * sizeof(limb)); + fsum(x, z); + fdifference_backwards(z, origx); // does x - z + + memcpy(origxprime, xprime, sizeof(limb) * 5); + fsum(xprime, zprime); + fdifference_backwards(zprime, origxprime); + fmul(xxprime, xprime, z); + fmul(zzprime, x, zprime); + memcpy(origxprime, xxprime, sizeof(limb) * 5); + fsum(xxprime, zzprime); + fdifference_backwards(zzprime, origxprime); + fsquare_times(x3, xxprime, 1); + fsquare_times(zzzprime, zzprime, 1); + fmul(z3, zzzprime, qmqp); + + fsquare_times(xx, x, 1); + fsquare_times(zz, z, 1); + fmul(x2, xx, zz); + fdifference_backwards(zz, xx); // does zz = xx - zz + fscalar_product(zzz, zz, 121665); + fsum(zzz, xx); + fmul(z2, zz, zzz); +} + +// ----------------------------------------------------------------------------- +// Maybe swap the contents of two limb arrays (@a and @b), each @len elements +// long. Perform the swap iff @swap is non-zero. +// +// This function performs the swap without leaking any side-channel +// information. +// ----------------------------------------------------------------------------- +static void +swap_conditional(limb a[5], limb b[5], limb iswap) { + unsigned i; + const limb swap = -iswap; + + for (i = 0; i < 5; ++i) { + const limb x = swap & (a[i] ^ b[i]); + a[i] ^= x; + b[i] ^= x; + } +} + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * resultx/resultz: the x coordinate of the resulting curve point (short form) + * n: a little endian, 32-byte number + * q: a point of the curve (short form) + */ +static void +cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { + limb a[5] = {0}, b[5] = {1}, c[5] = {1}, d[5] = {0}; + limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; + limb e[5] = {0}, f[5] = {1}, g[5] = {0}, h[5] = {1}; + limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; + + unsigned i, j; + + memcpy(nqpqx, q, sizeof(limb) * 5); + + for (i = 0; i < 32; ++i) { + u8 byte = n[31 - i]; + for (j = 0; j < 8; ++j) { + const limb bit = byte >> 7; + + swap_conditional(nqx, nqpqx, bit); + swap_conditional(nqz, nqpqz, bit); + fmonty(nqx2, nqz2, + nqpqx2, nqpqz2, + nqx, nqz, + nqpqx, nqpqz, + q); + swap_conditional(nqx2, nqpqx2, bit); + swap_conditional(nqz2, nqpqz2, bit); + + t = nqx; + nqx = nqx2; + nqx2 = t; + t = nqz; + nqz = nqz2; + nqz2 = t; + t = nqpqx; + nqpqx = nqpqx2; + nqpqx2 = t; + t = nqpqz; + nqpqz = nqpqz2; + nqpqz2 = t; + + byte <<= 1; + } + } + + memcpy(resultx, nqx, sizeof(limb) * 5); + memcpy(resultz, nqz, sizeof(limb) * 5); +} + + +// ----------------------------------------------------------------------------- +// Shamelessly copied from djb's code, tightened a little +// ----------------------------------------------------------------------------- +static void +crecip(felem out, const felem z) { + felem a,t0,b,c; + + /* 2 */ fsquare_times(a, z, 1); // a = 2 + /* 8 */ fsquare_times(t0, a, 2); + /* 9 */ fmul(b, t0, z); // b = 9 + /* 11 */ fmul(a, b, a); // a = 11 + /* 22 */ fsquare_times(t0, a, 1); + /* 2^5 - 2^0 = 31 */ fmul(b, t0, b); + /* 2^10 - 2^5 */ fsquare_times(t0, b, 5); + /* 2^10 - 2^0 */ fmul(b, t0, b); + /* 2^20 - 2^10 */ fsquare_times(t0, b, 10); + /* 2^20 - 2^0 */ fmul(c, t0, b); + /* 2^40 - 2^20 */ fsquare_times(t0, c, 20); + /* 2^40 - 2^0 */ fmul(t0, t0, c); + /* 2^50 - 2^10 */ fsquare_times(t0, t0, 10); + /* 2^50 - 2^0 */ fmul(b, t0, b); + /* 2^100 - 2^50 */ fsquare_times(t0, b, 50); + /* 2^100 - 2^0 */ fmul(c, t0, b); + /* 2^200 - 2^100 */ fsquare_times(t0, c, 100); + /* 2^200 - 2^0 */ fmul(t0, t0, c); + /* 2^250 - 2^50 */ fsquare_times(t0, t0, 50); + /* 2^250 - 2^0 */ fmul(t0, t0, b); + /* 2^255 - 2^5 */ fsquare_times(t0, t0, 5); + /* 2^255 - 21 */ fmul(out, t0, a); +} + +int +curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { + limb bp[5], x[5], z[5], zmone[5]; + uint8_t e[32]; + int i; + + for (i = 0;i < 32;++i) e[i] = secret[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + + fexpand(bp, basepoint); + cmult(x, z, e, bp); + crecip(zmone, z); + fmul(z, x, zmone); + fcontract(mypublic, z); + return 0; +} + +} diff --git a/src/lib/pubkey/curve25519/donna128.h b/src/lib/pubkey/curve25519/donna128.h new file mode 100644 index 000000000..f2b2d88ea --- /dev/null +++ b/src/lib/pubkey/curve25519/donna128.h @@ -0,0 +1,116 @@ +/* +* A minimal 128-bit integer type for curve25519-donna +* (C) 2014 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CURVE25519_DONNA128_H__ +#define BOTAN_CURVE25519_DONNA128_H__ + +#include <botan/mul128.h> + +namespace Botan { + +class donna128 + { + public: + donna128(u64bit ll = 0, u64bit hh = 0) { l = ll; h = hh; } + + donna128(const donna128&) = default; + donna128& operator=(const donna128&) = default; + + friend donna128 operator>>(const donna128& x, size_t shift) + { + donna128 z = x; + const u64bit carry = z.h << (64 - shift); + z.h = (z.h >> shift); + z.l = (z.l >> shift) | carry; + return z; + } + + friend donna128 operator<<(const donna128& x, size_t shift) + { + donna128 z = x; + const u64bit carry = z.l >> (64 - shift); + z.l = (z.l << shift); + z.h = (z.h << shift) | carry; + return z; + } + + friend u64bit operator&(const donna128& x, u64bit mask) + { + return x.l & mask; + } + + u64bit operator&=(u64bit mask) + { + h = 0; + l &= mask; + return l; + } + + donna128& operator+=(const donna128& x) + { + l += x.l; + h += (l < x.l); + h += x.h; + return *this; + } + + donna128& operator+=(u64bit x) + { + l += x; + h += (l < x); + return *this; + } + + u64bit lo() const { return l; } + u64bit hi() const { return h; } + private: + u64bit h = 0, l = 0; + }; + +inline donna128 operator*(const donna128& x, u64bit y) + { + BOTAN_ASSERT(x.hi() == 0, "High 64 bits of donna128 set to zero during multiply"); + + u64bit lo = 0, hi = 0; + mul64x64_128(x.lo(), y, &lo, &hi); + return donna128(lo, hi); + } + +inline donna128 operator+(const donna128& x, const donna128& y) + { + donna128 z = x; + z += y; + return z; + } + +inline donna128 operator+(const donna128& x, u64bit y) + { + donna128 z = x; + z += y; + return z; + } + +inline donna128 operator|(const donna128& x, const donna128& y) + { + return donna128(x.lo() | y.lo(), x.hi() | y.hi()); + } + +inline u64bit carry_shift(const donna128& a, size_t shift) + { + return (a >> shift).lo(); + } + +inline u64bit combine_lower(const donna128 a, size_t s1, + const donna128 b, size_t s2) + { + donna128 z = (a >> s1) | (b << s2); + return z.lo(); + } + +} + +#endif diff --git a/src/lib/pubkey/curve25519/info.txt b/src/lib/pubkey/curve25519/info.txt new file mode 100644 index 000000000..68c417056 --- /dev/null +++ b/src/lib/pubkey/curve25519/info.txt @@ -0,0 +1,9 @@ +define CURVE_25519 20141227 + +<header:public> +curve25519.h +</header:public> + +<header:internal> +donna128.h +</header:internal> diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp index 9673199e0..e1784dd9f 100644 --- a/src/lib/pubkey/pk_algs.cpp +++ b/src/lib/pubkey/pk_algs.cpp @@ -44,6 +44,10 @@ #include <botan/ecdh.h> #endif +#if defined(BOTAN_HAS_CURVE_25519) + #include <botan/curve25519.h> +#endif + namespace Botan { Public_Key* make_public_key(const AlgorithmIdentifier& alg_id, @@ -98,6 +102,11 @@ Public_Key* make_public_key(const AlgorithmIdentifier& alg_id, return new ECDH_PublicKey(alg_id, key_bits); #endif +#if defined(BOTAN_HAS_CURVE_25519) + if(alg_name == "Curve25519") + return new Curve25519_PublicKey(alg_id, key_bits); +#endif + return nullptr; } @@ -154,6 +163,11 @@ Private_Key* make_private_key(const AlgorithmIdentifier& alg_id, return new ECDH_PrivateKey(alg_id, key_bits); #endif +#if defined(BOTAN_HAS_CURVE_25519) + if(alg_name == "Curve25519") + return new Curve25519_PrivateKey(alg_id, key_bits, rng); +#endif + return nullptr; } diff --git a/src/tests/data/pubkey/c25519_scalar.vec b/src/tests/data/pubkey/c25519_scalar.vec new file mode 100644 index 000000000..0b8f7e72b --- /dev/null +++ b/src/tests/data/pubkey/c25519_scalar.vec @@ -0,0 +1,79 @@ + +# scalarmult1 from libsodium +Secret = 77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = 8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A + +# scalarmult2 +Secret = 5DAB087E624A8A4B79E17F8B83800EE66F3BB1292618B6FD1C2F8B27FF88E0EB +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B4F + +# scalarmult5 +Secret = 77076D0A7318A57D3C16C17251B26645DF4C2F87EBC0992AB177FBA51DB92C2A +Basepoint = DE9EDB7D7B7DC1B4D35B61C2ECE435373F8343C85B78674DADFC7E146F882B4F +Out = 4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742 + +# scalarmult6 +Secret = 5DAB087E624A8A4B79E17F8B83800EE66F3BB1292618B6FD1C2F8B27FF88E0EB +Basepoint = 8520F0098930A754748B7DDCB43EF75A0DBF3A0D26381AF4EBA4A98EAA9B4E6A +Out = 4A5D9D5BA4CE2DE1728E3BF480350F25E07E21C947D19E3376F09B3C1E161742 + +# test-noncanon from donna +Secret = 0100000000000000000000000000000000000000000000000000000000000000 +Basepoint = 2500000000000000000000000000000000000000000000000000000000000000 +Out = 3C7777CAF997B264416077665B4E229D0B9548DC0CD81998DDCDC5C8533C797F + +Secret = 0100000000000000000000000000000000000000000000000000000000000000 +Basepoint = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +Out = B32D1362C248D62FE62619CFF04DD43DB73FFC1B6308EDE30B78D87380F1E834 + +# Following values generated at random by curve25519-donna + +Secret = D55FF01DA1262795A4E50E607F87B80DCCD447A6EE0F6CD8D25177F79575744D +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = D5BDC7055ABA7855692CC861009E3AE6B6339329826B11F8B92E5ADAEB85335E + +Secret = 0224D9367436089D81B1150DFC748EC851F9A41389E21C8C1181E01BA1760C23 +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = AD7E1E41C6AC0544F949EB76D71C75120ADD345C44384BDD830974D2DD329644 + +Secret = D55FF01DA1262795A4E50E607F87B80DCCD447A6EE0F6CD8D25177F79575744D +Basepoint = AD7E1E41C6AC0544F949EB76D71C75120ADD345C44384BDD830974D2DD329644 +Out = FC343E1965225D8666F4AE8E70E04039D21C603F7CE7F17C0CC8440C62C03575 + +Secret = 0224D9367436089D81B1150DFC748EC851F9A41389E21C8C1181E01BA1760C23 +Basepoint = D5BDC7055ABA7855692CC861009E3AE6B6339329826B11F8B92E5ADAEB85335E +Out = FC343E1965225D8666F4AE8E70E04039D21C603F7CE7F17C0CC8440C62C03575 + +Secret = 65A06F749B010EA5738CDEBE3EF65F38C17A13F8CCC5B0AE51B5091D845C6DEB +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = B84AEDDA6C2CD17CCB62D94E3238E7093BA77D15BE4C563D1B11EA3C2EECA87F + +Secret = 92B158501611EF8521C101E28629130427B42D2DA65E9EC9387B94BC2F08B806 +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = 06479EC3C771D635F54787DEA334149E07E20957127C816E3059258428C17970 + +Secret = 65A06F749B010EA5738CDEBE3EF65F38C17A13F8CCC5B0AE51B5091D845C6DEB +Basepoint = 06479EC3C771D635F54787DEA334149E07E20957127C816E3059258428C17970 +Out = 47C69389596EA49AEE14F4259250808385C37F4EBBD6AA176779BFEB8042D834 + +Secret = 92B158501611EF8521C101E28629130427B42D2DA65E9EC9387B94BC2F08B806 +Basepoint = B84AEDDA6C2CD17CCB62D94E3238E7093BA77D15BE4C563D1B11EA3C2EECA87F +Out = 47C69389596EA49AEE14F4259250808385C37F4EBBD6AA176779BFEB8042D834 + +Secret = 2D7B885AA77351153994425725F35AFB84D9729DECA3D9D832570569C973566E +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = 93928FCC20DFEA2AF6CA8B1268192B68D87FDA744FD67FCFAEF84165C504597C + +Secret = E3B07D96E4B5F3D265ACFE950707B62B053F2FD5CCF20D662F62AB09ED2AC325 +Basepoint = 0900000000000000000000000000000000000000000000000000000000000000 +Out = 68B7894FF03386C8C1907847866CD771519163D002FA7C3360650186A22F7263 + +Secret = 2D7B885AA77351153994425725F35AFB84D9729DECA3D9D832570569C973566E +Basepoint = 68B7894FF03386C8C1907847866CD771519163D002FA7C3360650186A22F7263 +Out = 0BE4BB615F362A21A28A404631F6832DD1019A7145C020031614D8B6F697983A + +Secret = E3B07D96E4B5F3D265ACFE950707B62B053F2FD5CCF20D662F62AB09ED2AC325 +Basepoint = 93928FCC20DFEA2AF6CA8B1268192B68D87FDA744FD67FCFAEF84165C504597C +Out = 0BE4BB615F362A21A28A404631F6832DD1019A7145C020031614D8B6F697983A diff --git a/src/tests/test_c25519.cpp b/src/tests/test_c25519.cpp new file mode 100644 index 000000000..cb03ebf69 --- /dev/null +++ b/src/tests/test_c25519.cpp @@ -0,0 +1,56 @@ +#include "tests.h" +#include "test_pubkey.h" + +#if defined(BOTAN_HAS_CURVE_25519) +#include <botan/curve25519.h> +#include <botan/hex.h> +#include <iostream> +#include <fstream> +#endif + +using namespace Botan; + +#if defined(BOTAN_HAS_CURVE_25519) + +namespace { + +size_t curve25519_scalar_kat(const std::string& secret_h, + const std::string& basepoint_h, + const std::string& out_h) + { + const std::vector<byte> secret = hex_decode(secret_h); + const std::vector<byte> basepoint = hex_decode(basepoint_h); + const std::vector<byte> out = hex_decode(out_h); + + std::vector<byte> got(32); + curve25519_donna(&got[0], &secret[0], &basepoint[0]); + + if(got != out) + { + std::cout << "Got " << hex_encode(got) << " exp " << hex_encode(out) << "\n"; + return 1; + } + + return 0; + } + +} +#endif + +size_t test_curve25519() + { + size_t fails = 0; + +#if defined(BOTAN_HAS_CURVE_25519) + std::ifstream c25519_scalar(PK_TEST_DATA_DIR "/c25519_scalar.vec"); + + fails += run_tests_bb(c25519_scalar, "Curve25519 ScalarMult", "Out", true, + [](std::map<std::string, std::string> m) -> size_t + { + return curve25519_scalar_kat(m["Secret"], m["Basepoint"], m["Out"]); + }); +#endif + + return fails; + } + diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 3ec904c02..99a4d8e3a 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -261,6 +261,7 @@ int main(int argc, char* argv[]) DEF_TEST(ecc_pointmul); DEF_TEST(ecdsa); DEF_TEST(gost_3410); + DEF_TEST(curve25519); DEF_TEST(mceliece); DEF_TEST(ecc_unit); diff --git a/src/tests/tests.h b/src/tests/tests.h index d4ccf91c1..61cc405a0 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -67,6 +67,7 @@ size_t test_elgamal(); size_t test_ecc_pointmul(); size_t test_ecdsa(); size_t test_gost_3410(); +size_t test_curve25519(); size_t test_mceliece(); // One off tests |