diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,8 @@
-## 0.15.1
+## 0.16
 
-* Add error check on salt length in bcrypt
-* Fix buffer length in scrypt (Luke Taylor)
-* build fixes for i686 and arm related to rdrand
 * Fix basepoint for Ed448
+
+* Enable 64-bit Curve25519 implementation
 
 ## 0.15
 
diff --git a/Crypto/Cipher/Blowfish/Primitive.hs b/Crypto/Cipher/Blowfish/Primitive.hs
--- a/Crypto/Cipher/Blowfish/Primitive.hs
+++ b/Crypto/Cipher/Blowfish/Primitive.hs
@@ -79,9 +79,7 @@
 -- Cost must be between 4 and 31 inclusive
 -- See <https://www.usenix.org/conference/1999-usenix-annual-technical-conference/future-adaptable-password-scheme>
 eksBlowfish :: (ByteArrayAccess salt, ByteArrayAccess password) => Int -> salt -> password -> Context
-eksBlowfish cost salt key
-    | B.length salt /= 16 = error "bcrypt salt must be 16 bytes"
-    | otherwise           = makeKeySchedule key (Just (salt, cost))
+eksBlowfish cost salt key = makeKeySchedule key (Just (salt, cost))
 
 coreCrypto :: Context -> Word64 -> Word64
 coreCrypto (BF p s0 s1 s2 s3) input = doRound input 0
diff --git a/Crypto/KDF/Scrypt.hs b/Crypto/KDF/Scrypt.hs
--- a/Crypto/KDF/Scrypt.hs
+++ b/Crypto/KDF/Scrypt.hs
@@ -53,7 +53,7 @@
         let b = PBKDF2.generate prf (PBKDF2.Parameters 1 intLen) password salt :: B.Bytes
         newSalt <- B.copy b $ \bPtr ->
             allocaBytesAligned (128*(fromIntegral $ n params)*(r params)) 8 $ \v ->
-            allocaBytesAligned (256*r params + 64) 8 $ \xy -> do
+            allocaBytesAligned (256*r params) 8 $ \xy -> do
                 forM_ [0..(p params-1)] $ \i ->
                     ccryptonite_scrypt_smix (bPtr `plusPtr` (i * 128 * (r params)))
                                             (fromIntegral $ r params) (n params) v xy
diff --git a/Crypto/Random/EntropyPool.hs b/Crypto/Random/EntropyPool.hs
--- a/Crypto/Random/EntropyPool.hs
+++ b/Crypto/Random/EntropyPool.hs
@@ -21,8 +21,8 @@
 import           Foreign.Marshal.Utils (copyBytes)
 import           Foreign.Ptr (plusPtr, Ptr)
 
--- | Pool of Entropy. contains a self mutating pool of entropy,
--- that is always guarantee to contains data.
+-- | Pool of Entropy. Contains a self-mutating pool of entropy,
+-- that is always guaranteed to contain data.
 data EntropyPool = EntropyPool [EntropyBackend] (MVar Int) ScrubbedBytes
 
 -- size of entropy pool by default
@@ -31,7 +31,8 @@
 
 -- | Create a new entropy pool of a specific size
 --
--- While you can create as many entropy pool as you want, the pool can be shared between multiples RNGs.
+-- While you can create as many entropy pools as you want,
+-- the pool can be shared between multiples RNGs.
 createEntropyPoolWith :: Int -> [EntropyBackend] -> IO EntropyPool
 createEntropyPoolWith poolSize backends = do
     m  <- newMVar 0
@@ -40,7 +41,8 @@
 
 -- | Create a new entropy pool with a default size.
 --
--- While you can create as many entropy pool as you want, the pool can be shared between multiples RNGs.
+-- While you can create as many entropy pools as you want,
+-- the pool can be shared between multiples RNGs.
 createEntropyPool :: IO EntropyPool
 createEntropyPool = do
     backends <- catMaybes `fmap` sequence supportedBackends
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,27 +2,25 @@
 ==========
 
 [![Join the chat at https://gitter.im/vincenthz/cryptonite](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/vincenthz/cryptonite?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[![Build Status](https://travis-ci.org/vincenthz/cryptonite.png?branch=master)](https://travis-ci.org/vincenthz/cryptonite)
+[![Build Status](https://travis-ci.org/haskell-crypto/cryptonite.png?branch=master)](https://travis-ci.org/haskell-crypto/cryptonite)
 [![BSD](http://b.repl.ca/v1/license-BSD-blue.png)](http://en.wikipedia.org/wiki/BSD_licenses)
 [![Haskell](http://b.repl.ca/v1/language-haskell-lightgrey.png)](http://haskell.org)
 
 Cryptonite is a haskell repository of cryptographic primitives. Each crypto
-algorithm have specificities, that are hard to wrap in common APIs and types,
-so instead of trying to provide a common ground for algorithms that wouldn't
-allow to provide all different usage or a really complicated system, this just
-provide a non-consistant low-level API.
+algorithm has specificities that are hard to wrap in common APIs and types,
+so instead of trying to provide a common ground for algorithms, this package
+provides a non-consistent low-level API.
 
-If you have no idea what're you doing, please do not use this directly, rely on
-higher level protocols or higher level implementation.
+If you have no idea what you're doing, please do not use this directly.
+Instead, rely on higher level protocols or implementations.
 
 Documentation: [cryptonite on hackage](http://hackage.haskell.org/package/cryptonite)
 
 Versioning
 ----------
 
-Development versions are an incremental number prefixed by 0.
-No specific meaning is associated with the versions, specially
-no API stability.
+Development versions are an incremental number prefixed by 0. There is no
+API stability between development versions.
 
 Production versions : TBD
 
@@ -35,7 +33,7 @@
 Support
 -------
 
-cryptonite supports the following platform:
+cryptonite supports the following platforms:
 
 * Windows >= 8
 * OSX >= 10.8
@@ -55,21 +53,22 @@
 * GHC 7.8.x
 * GHC 7.10.x
 
-Further platforms and architectures probably works too, but until maintainer(s) don't have regular
-access to them, we can't commit for further support
+Further platforms and architectures probably work too, but since the
+maintainer(s) don't have regular access to them, we can't commit to
+further support.
 
 Known Building Issues
 ---------------------
 
-on OSX <= 10.7, the system compiler doesn't understand the '-maes' option, and
+On OSX <= 10.7, the system compiler doesn't understand the '-maes' option, and
 with the lack of autodetection feature builtin in .cabal file, it is left on
 the user to disable the aesni. See the [Disabling AESNI] section
 
 Disabling AESNI
 ---------------
 
-It may be useful to disable AESNI (for building, testing or runtime purpose), and one can do that with the
-*support_aesni* flag.
+It may be useful to disable AESNI for building, testing or runtime purposes.
+This is achieved with the *support_aesni* flag.
 
 As part of configure of cryptonite:
 
diff --git a/cbits/cryptonite_rdrand.c b/cbits/cryptonite_rdrand.c
--- a/cbits/cryptonite_rdrand.c
+++ b/cbits/cryptonite_rdrand.c
@@ -37,53 +37,16 @@
 int cryptonite_cpu_has_rdrand()
 {
 	uint32_t ax,bx,cx,dx,func=1;
-#if defined(__PIC__) && defined(__i386__)
-	__asm__ volatile ("mov %%ebx, %%edi;" "cpuid;" "xchgl %%ebx, %%edi;"
-		: "=a" (ax), "=D" (bx), "=c" (cx), "=d" (dx) : "a" (func));
-#else
 	__asm__ volatile ("cpuid": "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));
-#endif
 	return (cx & 0x40000000);
 }
 
-/* inline encoding of 'rdrand %rax' to cover old binutils
- * - no inputs
- * - 'cc' to the clobber list as we modify condition code.
- * - output of rdrand in rax and have a 8 bit error condition
- */
-#define inline_rdrand_rax(val, err) \
-	asm(".byte 0x48,0x0f,0xc7,0xf0; setc %1" \
-	   : "=a" (val), "=q" (err) \
-	   : \
-	   : "cc")
-
-/* inline encoding of 'rdrand %eax' to cover old binutils
- * - no inputs
- * - 'cc' to the clobber list as we modify condition code.
- * - output of rdrand in eax and have a 8 bit error condition
- */
-#define inline_rdrand_eax(val, err) \
-	asm(".byte 0x0f,0xc7,0xf0; setc %1" \
-	   : "=a" (val), "=q" (err) \
-	   : \
-	   : "cc")
-
-#ifdef __x86_64__
-# define RDRAND_SZ 8
-# define RDRAND_T  uint64_t
-#define inline_rdrand(val, err) err = cryptonite_rdrand_step(&val)
-#else
-# define RDRAND_SZ 4
-# define RDRAND_T  uint32_t
-#define inline_rdrand(val, err) err = cryptonite_rdrand_step(&val)
-#endif
-
 /* sadly many people are still using an old binutils,
  * leading to report that instruction is not recognized.
  */
-#if 1
+#if 0
 /* Returns 1 on success */
-static inline int cryptonite_rdrand_step(RDRAND_T *buffer)
+static inline int crypto_random_rdrand64_step(uint64_t *buffer)
 {
 	unsigned char err;
 	asm volatile ("rdrand %0; setc %1" : "=r" (*buffer), "=qm" (err));
@@ -91,17 +54,28 @@
 }
 #endif
 
+/* inline encoding of 'rdrand %rax' to cover old binutils
+ * - no inputs
+ * - 'cc' to the clobber list as we modify condition code.
+ * - output of rdrand in rax and have a 8 bit error condition
+ */
+#define inline_rdrand_rax(val, err) \
+	asm(".byte 0x48,0x0f,0xc7,0xf0; setc %1" \
+	   : "=a" (val), "=q" (err) \
+	   : \
+	   : "cc")
+
 /* Returns the number of bytes succesfully generated */
 int cryptonite_get_rand_bytes(uint8_t *buffer, size_t len)
 {
-	RDRAND_T tmp;
-	int aligned = (intptr_t) buffer % RDRAND_SZ;
+	uint64_t tmp;
+	int aligned = (intptr_t) buffer % 8;
 	int orig_len = len;
-	int to_alignment = RDRAND_SZ - aligned;
+	int to_alignment = 8 - aligned;
 	uint8_t ok;
 
 	if (aligned != 0) {
-		inline_rdrand(tmp, ok);
+		inline_rdrand_rax(tmp, ok);
 		if (!ok)
 			return 0;
 		memcpy(buffer, (uint8_t *) &tmp, to_alignment);
@@ -109,15 +83,15 @@
 		len -= to_alignment;
 	}
 
-	for (; len >= RDRAND_SZ; buffer += RDRAND_SZ, len -= RDRAND_SZ) {
-		inline_rdrand(tmp, ok);
+	for (; len >= 8; buffer += 8, len -= 8) {
+		inline_rdrand_rax(tmp, ok);
 		if (!ok)
 			return (orig_len - len);
-		*((RDRAND_T *) buffer) = tmp;
+		*((uint64_t *) buffer) = tmp;
 	}
 
 	if (len > 0) {
-		inline_rdrand(tmp, ok);
+		inline_rdrand_rax(tmp, ok);
 		if (!ok)
 			return (orig_len - len);
 		memcpy(buffer, (uint8_t *) &tmp, len);
diff --git a/cbits/curve25519/curve25519-donna-c64.c b/cbits/curve25519/curve25519-donna-c64.c
new file mode 100644
--- /dev/null
+++ b/cbits/curve25519/curve25519-donna-c64.c
@@ -0,0 +1,447 @@
+/* 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 <agl@imperialviolet.org>
+ *
+ * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
+ *
+ * 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 <string.h>
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint64_t limb;
+typedef limb felem[5];
+// This is a special gcc mode for 128-bit integers. It's implemented on 64-bit
+// platforms only as far as I know.
+typedef unsigned uint128_t __attribute__((mode(TI)));
+
+#undef force_inline
+#define force_inline __attribute__((always_inline))
+
+/* Sum two numbers: output += in */
+static inline void force_inline
+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 force_inline
+fdifference_backwards(felem out, const felem in) {
+  /* 152 is 19 << 3 */
+  static const limb two54m152 = (((limb)1) << 54) - 152;
+  static const limb two54m8 = (((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 force_inline
+fscalar_product(felem output, const felem in, const limb scalar) {
+  uint128_t a;
+
+  a = ((uint128_t) in[0]) * scalar;
+  output[0] = ((limb)a) & 0x7ffffffffffff;
+
+  a = ((uint128_t) in[1]) * scalar + ((limb) (a >> 51));
+  output[1] = ((limb)a) & 0x7ffffffffffff;
+
+  a = ((uint128_t) in[2]) * scalar + ((limb) (a >> 51));
+  output[2] = ((limb)a) & 0x7ffffffffffff;
+
+  a = ((uint128_t) in[3]) * scalar + ((limb) (a >> 51));
+  output[3] = ((limb)a) & 0x7ffffffffffff;
+
+  a = ((uint128_t) in[4]) * scalar + ((limb) (a >> 51));
+  output[4] = ((limb)a) & 0x7ffffffffffff;
+
+  output[0] += (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 force_inline
+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 = (limb)t[0] & 0x7ffffffffffff; c = (limb)(t[0] >> 51);
+  t[1] += c;      r1 = (limb)t[1] & 0x7ffffffffffff; c = (limb)(t[1] >> 51);
+  t[2] += c;      r2 = (limb)t[2] & 0x7ffffffffffff; c = (limb)(t[2] >> 51);
+  t[3] += c;      r3 = (limb)t[3] & 0x7ffffffffffff; c = (limb)(t[3] >> 51);
+  t[4] += c;      r4 = (limb)t[4] & 0x7ffffffffffff; c = (limb)(t[4] >> 51);
+  r0 +=   c * 19; c = r0 >> 51; r0 = r0 & 0x7ffffffffffff;
+  r1 +=   c;      c = 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 force_inline
+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 = (limb)t[0] & 0x7ffffffffffff; c = (limb)(t[0] >> 51);
+    t[1] += c;      r1 = (limb)t[1] & 0x7ffffffffffff; c = (limb)(t[1] >> 51);
+    t[2] += c;      r2 = (limb)t[2] & 0x7ffffffffffff; c = (limb)(t[2] >> 51);
+    t[3] += c;      r3 = (limb)t[3] & 0x7ffffffffffff; c = (limb)(t[3] >> 51);
+    t[4] += c;      r4 = (limb)t[4] & 0x7ffffffffffff; c = (limb)(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
+    ((limb)in[0]) |
+    (((limb)in[1]) << 8) |
+    (((limb)in[2]) << 16) |
+    (((limb)in[3]) << 24) |
+    (((limb)in[4]) << 32) |
+    (((limb)in[5]) << 40) |
+    (((limb)in[6]) << 48) |
+    (((limb)in[7]) << 56);
+}
+
+static void
+store_limb(u8 *out, limb in) {
+  out[0] = in & 0xff;
+  out[1] = (in >> 8) & 0xff;
+  out[2] = (in >> 16) & 0xff;
+  out[3] = (in >> 24) & 0xff;
+  out[4] = (in >> 32) & 0xff;
+  out[5] = (in >> 40) & 0xff;
+  out[6] = (in >> 48) & 0xff;
+  out[7] = (in >> 56) & 0xff;
+}
+
+/* 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] += 19 * (t[4] >> 51); 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] += 19 * (t[4] >> 51); 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] += 19 * (t[4] >> 51); 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,    t[0] | (t[1] << 51));
+  store_limb(output+8,  (t[1] >> 13) | (t[2] << 38));
+  store_limb(output+16, (t[2] >> 26) | (t[3] << 25));
+  store_limb(output+24, (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
+cryptonite_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/cryptonite.cabal b/cryptonite.cabal
--- a/cryptonite.cabal
+++ b/cryptonite.cabal
@@ -1,5 +1,5 @@
 Name:                cryptonite
-Version:             0.15.1
+Version:             0.16
 Synopsis:            Cryptography Primitives sink
 Description:
     A repository of cryptographic primitives.
@@ -10,7 +10,7 @@
     .
     * MAC: HMAC, Poly1305
     .
-    * Assymmetric crypto: DSA, RSA, DH, ECDH, ECDSA, ECC, Curve25519, Ed25519
+    * Asymmetric crypto: DSA, RSA, DH, ECDH, ECDSA, ECC, Curve25519, Ed25519, Ed448
     .
     * Key Derivation Function: PBKDF2, Scrypt, HKDF
     .
@@ -41,9 +41,7 @@
                      cbits/ed448/*.h
                      cbits/p256/*.h
                      cbits/blake2/ref/*.h
-                     cbits/blake2/ref/*.c
                      cbits/blake2/sse/*.h
-                     cbits/blake2/sse/*.c
                      cbits/aes/x86ni_impl.c
                      tests/*.hs
 
@@ -201,7 +199,6 @@
                    , cbits/cryptonite_salsa.c
                    , cbits/cryptonite_rc4.c
                    , cbits/cryptonite_cpu.c
-                   , cbits/curve25519/curve25519-donna.c
                    , cbits/ed25519/ed25519.c
                    , cbits/ed448/x448.c
                    , cbits/p256/p256.c
@@ -227,6 +224,11 @@
                    , cbits/cryptonite_scrypt.c
   include-dirs:  cbits cbits/ed25519
 
+  if arch(x86_64)
+    C-sources: cbits/curve25519/curve25519-donna-c64.c
+  else
+    C-sources: cbits/curve25519/curve25519-donna.c
+
   -- FIXME armel or mispel is also little endian.
   -- might be a good idea to also add a runtime autodetect mode.
   -- ARCH_ENDIAN_UNKNOWN
@@ -239,7 +241,7 @@
   if arch(x86_64)
     CPP-options: -DARCH_X86_64
 
-  if flag(support_rdrand) && (arch(i386) || arch(x86_64))
+  if flag(support_rdrand)
     CPP-options:    -DSUPPORT_RDRAND
     Other-modules:  Crypto.Random.Entropy.RDRand
     c-sources:      cbits/cryptonite_rdrand.c
diff --git a/tests/BCrypt.hs b/tests/BCrypt.hs
--- a/tests/BCrypt.hs
+++ b/tests/BCrypt.hs
@@ -74,5 +74,4 @@
 
 tests = testGroup "bcrypt"
     [ testGroup "KATs" makeKATs
-    , testCase "Invalid hash length" (assertEqual "" (Left "Invalid hash format") (validatePasswordEither B.empty ("$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s" :: B.ByteString)))
     ]
