diff --git a/ed25519.dll b/ed25519.dll index 3b953f6..2dd29d5 100644 Binary files a/ed25519.dll and b/ed25519.dll differ diff --git a/readme.md b/readme.md index 37c80f2..80cc48b 100644 --- a/readme.md +++ b/readme.md @@ -2,24 +2,29 @@ Ed25519 ======= This is a portable implementation of [Ed25519](http://ed25519.cr.yp.to/) based -on the SUPERCOP "ref10" implementation. All code is in the public domain. +on the SUPERCOP "ref10" implementation. Additionally there is key exchanging +and scalar addition included to further aid building a PKI using Ed25519. All +code is in the public domain. All code is pure ANSI C without any dependencies, except for the random seed -generation which uses standard OS cryptography APIs (`CryptGenRandom` on Windows, `/dev/urandom` on *nix). If you wish to be entirely -portable define `ED25519_NO_SEED`. This disables the `ed25519_create_seed` -function, so if your application requires key generation you must supply your -own seeding function (which is simply a 32 byte cryptographic random number generator). +generation which uses standard OS cryptography APIs (`CryptGenRandom` on +Windows, `/dev/urandom` on nix). If you wish to be entirely portable define +`ED25519_NO_SEED`. This disables the `ed25519_create_seed` function, so if your +application requires key generation you must supply your own seeding function +(which is simply a 256 byte cryptographic random number generator). Performance ----------- -On a machine with an Intel Pentium B970 @ 2.3GHz I got the following speeds (running -on only one a single core): +On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following +speeds (running on only one a single core): - Seed + key generation: 345us - Message signing (short message): 256us - Message verifying (short message): 777us +Seed + key generation: 489us +Message signing (short message): 251us +Message verifying (short message): 772us +Scalar addition: 358us +Key exchange: 724us The speeds on other machines may vary. Sign/verify times will be higher with longer messages. @@ -33,8 +38,9 @@ Simply add all .c and .h files in the `src/` folder to your project and include library, only copy `ed25519.h` and define `ED25519_DLL` before importing. A windows DLL is pre-built. -There are no defined types for seeds, private keys, public keys or signatures. -Instead simple `unsigned char` buffers are used with the following sizes: +There are no defined types for seeds, private keys, public keys, shared secrets +or signatures. Instead simple `unsigned char` buffers are used with the +following sizes: ```c unsigned char seed[32]; @@ -42,6 +48,7 @@ unsigned char signature[64]; unsigned char public_key[32]; unsigned char private_key[64]; unsigned char scalar[32]; +unsigned char shared_secret[32]; ``` API @@ -91,12 +98,24 @@ Adds `scalar` to the given key pair where scalar is a 32 byte buffer (possibly generated with `ed25519_create_seed`), generating a new key pair. You can calculate the public key sum without knowing the private key and vice versa by passing in `NULL` for the key you don't know. This is useful for enforcing -randomness on a key pair while only knowing the public key, among other things. -Warning: the last bit of the scalar is ignored - if comparing scalars make sure -to clear it with `scalar[31] &= 127`. +randomness on a key pair by a third party while only knowing the public key, +among other things. Warning: the last bit of the scalar is ignored - if +comparing scalars make sure to clear it with `scalar[31] &= 127`. + + +```c +void ed25519_key_exchange(unsigned char *shared_secret, + const unsigned char *public_key, const unsigned char *private_key); +``` + +Performs a key exchange on the given public key and private key, producing a +shared secret. It is recommended to hash the shared secret before using it. +`shared_secret` must be a 32 byte writable buffer where the shared secret will +be stored. Example ------- + ```c unsigned char seed[32], public_key[32], private_key[64], signature[64]; const unsigned char message[] = "TEST MESSAGE"; @@ -119,3 +138,7 @@ if (ed25519_verify(signature, message, strlen(message), public_key)) { printf("invalid signature\n"); } ``` + +License +------- +All code is in the public domain. diff --git a/src/ed25519.h b/src/ed25519.h index c1dc0d3..09804f7 100644 --- a/src/ed25519.h +++ b/src/ed25519.h @@ -28,6 +28,7 @@ void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key); int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *private_key); void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar); +void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key); #ifdef __cplusplus diff --git a/src/key_exchange.c b/src/key_exchange.c new file mode 100644 index 0000000..924da79 --- /dev/null +++ b/src/key_exchange.c @@ -0,0 +1,79 @@ +#include "ed25519.h" +#include "fe.h" + +void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) { + unsigned char e[32]; + unsigned int i; + + fe x1; + fe x2; + fe z2; + fe x3; + fe z3; + fe tmp0; + fe tmp1; + + int pos; + unsigned int swap; + unsigned int b; + + /* copy the private key and make sure it's valid */ + for (i = 0; i < 32; ++i) { + e[i] = private_key[i]; + } + + e[0] &= 248; + e[31] &= 63; + e[31] |= 64; + + /* unpack the public key and convert edwards to montgomery */ + /* due to CodesInChaos */ + fe_frombytes(x1, public_key); + fe_1(tmp1); + fe_add(tmp0, x1, tmp1); + fe_sub(tmp1, tmp1, x1); + fe_invert(tmp1, tmp1); + fe_mul(x1, tmp0, tmp1); + + fe_1(x2); + fe_0(z2); + fe_copy(x3, x1); + fe_1(z3); + + swap = 0; + for (pos = 254; pos >= 0; --pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + swap ^= b; + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + swap = b; + + /* from montgomery.h */ + fe_sub(tmp0, x3, z3); + fe_sub(tmp1, x2, z2); + fe_add(x2, x2, z2); + fe_add(z2, x3, z3); + fe_mul(z3, tmp0, x2); + fe_mul(z2, z2, tmp1); + fe_sq(tmp0, tmp1); + fe_sq(tmp1, x2); + fe_add(x3, z3, z2); + fe_sub(z2, z3, z2); + fe_mul(x2, tmp1, tmp0); + fe_sub(tmp1, tmp1, tmp0); + fe_sq(z2, z2); + fe_mul121666(z3, tmp1); + fe_sq(x3, x3); + fe_add(tmp0, tmp0, z3); + fe_mul(z3, x1, z2); + fe_mul(z2, tmp1, tmp0); + } + + fe_cswap(x2, x3, swap); + fe_cswap(z2, z3, swap); + + fe_invert(z2, z2); + fe_mul(x2, x2, z2); + fe_tobytes(shared_secret, x2); +} diff --git a/test.c b/test.c index 3230dc0..ec1eeef 100644 --- a/test.c +++ b/test.c @@ -14,6 +14,8 @@ const char message[] = "Hello, world!"; int main(int argc, char *argv[]) { unsigned char public_key[32], private_key[64], seed[32], scalar[32]; + unsigned char other_public_key[32], other_private_key[64]; + unsigned char shared_secret[32], other_shared_secret[32]; unsigned char signature[64]; clock_t start; @@ -56,6 +58,27 @@ int main(int argc, char *argv[]) { printf("correctly detected signature change\n"); } + /* generate two keypairs for testing key exchange */ + ed25519_create_seed(seed); + ed25519_create_keypair(public_key, private_key, seed); + ed25519_create_seed(seed); + ed25519_create_keypair(other_public_key, other_private_key, seed); + + /* create two shared secrets - from both perspectives - and check if they're equal */ + ed25519_key_exchange(shared_secret, other_public_key, private_key); + ed25519_key_exchange(other_shared_secret, public_key, other_private_key); + + for (i = 0; i < 32; ++i) { + if (shared_secret[i] != other_shared_secret[i]) { + printf("key exchange was incorrect\n"); + break; + } + } + + if (i == 32) { + printf("key exchange was correct\n"); + } + /* test performance */ printf("testing key generation performance: "); start = clock(); @@ -93,6 +116,15 @@ int main(int argc, char *argv[]) { } end = clock(); + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + + printf("testing key exchange performance: "); + start = clock(); + for (i = 0; i < 10000; ++i) { + ed25519_key_exchange(shared_secret, other_public_key, private_key); + } + end = clock(); + printf("%fus per signature\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); return 0;