aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/pubkey/curve25519
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 /src/lib/pubkey/curve25519
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
Diffstat (limited to 'src/lib/pubkey/curve25519')
-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
5 files changed, 790 insertions, 0 deletions
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>