aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2014-12-27 17:50:57 +0000
committerlloyd <[email protected]>2014-12-27 17:50:57 +0000
commitd0daf875978848c3edf65c7b3683a21605f72e64 (patch)
tree46690afadfb5e9acb766468f7f7481bb1244049d
parent675c2e324268ebce7e2c665389ebd57d38083200 (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.rst4
-rw-r--r--doc/license.rst1
-rw-r--r--doc/relnotes/1_11_12.rst4
-rw-r--r--src/cmd/keygen.cpp9
-rw-r--r--src/cmd/speed_pk.cpp56
-rw-r--r--src/lib/engine/core_engine/def_pk_ops.cpp9
-rw-r--r--src/lib/pubkey/curve25519/curve25519.cpp115
-rw-r--r--src/lib/pubkey/curve25519/curve25519.h94
-rw-r--r--src/lib/pubkey/curve25519/donna.cpp456
-rw-r--r--src/lib/pubkey/curve25519/donna128.h116
-rw-r--r--src/lib/pubkey/curve25519/info.txt9
-rw-r--r--src/lib/pubkey/pk_algs.cpp14
-rw-r--r--src/tests/data/pubkey/c25519_scalar.vec79
-rw-r--r--src/tests/test_c25519.cpp56
-rw-r--r--src/tests/tests.cpp1
-rw-r--r--src/tests/tests.h1
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
+ 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