Giter Club home page Giter Club logo

ed25519's Introduction

Ed25519

This is a portable implementation of Ed25519 based 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 licensed under the permissive zlib license.

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 256 bit (32 byte) cryptographic random number generator).

Performance

On a Windows machine with an Intel Pentium B970 @ 2.3GHz I got the following speeds (running on only one a single core):

Seed generation: 64us (15625 per second)
Key generation: 88us (11364 per second)
Message signing (short message): 87us (11494 per second)
Message verifying (short message): 228us (4386 per second)
Scalar addition: 100us (10000 per second)
Key exchange: 220us (4545 per second)

The speeds on other machines may vary. Sign/verify times will be higher with longer messages. The implementation significantly benefits from 64 bit architectures, if possible compile as 64 bit.

Usage

Simply add all .c and .h files in the src/ folder to your project and include ed25519.h in any file you want to use the API. If you prefer to use a shared 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, shared secrets or signatures. Instead simple unsigned char buffers are used with the following sizes:

unsigned char seed[32];
unsigned char signature[64];
unsigned char public_key[32];
unsigned char private_key[64];
unsigned char scalar[32];
unsigned char shared_secret[32];

Note: this library stores private keys in a different format than some other libraries, notably libsodium. They tend to store the concatenation of the seed and public_key as their private key representation. If you wish to be compatible with these libraries you must keep the seed around.

API

int ed25519_create_seed(unsigned char *seed);

Creates a 32 byte random seed in seed for key generation. seed must be a writable 32 byte buffer. Returns 0 on success, and nonzero on failure.

void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key,
                            const unsigned char *seed);

Creates a new key pair from the given seed. public_key must be a writable 32 byte buffer, private_key must be a writable 64 byte buffer and seed must be a 32 byte buffer.

void ed25519_sign(unsigned char *signature,
                  const unsigned char *message, size_t message_len,
                  const unsigned char *public_key, const unsigned char *private_key);

Creates a signature of the given message with the given key pair. signature must be a writable 64 byte buffer. message must have at least message_len bytes to be read.

int ed25519_verify(const unsigned char *signature,
                   const unsigned char *message, size_t message_len,
                   const unsigned char *public_key);

Verifies the signature on the given message using public_key. signature must be a readable 64 byte buffer. message must have at least message_len bytes to be read. Returns 1 if the signature matches, 0 otherwise.

void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key,
                        const unsigned char *scalar);

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 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.

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

unsigned char seed[32], public_key[32], private_key[64], signature[64];
unsigned char other_public_key[32], other_private_key[64], shared_secret[32];
const unsigned char message[] = "TEST MESSAGE";

/* create a random seed, and a key pair out of that seed */
if (ed25519_create_seed(seed)) {
    printf("error while generating seed\n");
    exit(1);
}

ed25519_create_keypair(public_key, private_key, seed);

/* create signature on the message with the key pair */
ed25519_sign(signature, message, strlen(message), public_key, private_key);

/* verify the signature */
if (ed25519_verify(signature, message, strlen(message), public_key)) {
    printf("valid signature\n");
} else {
    printf("invalid signature\n");
}

/* create a dummy keypair to use for a key exchange, normally you'd only have
the public key and receive it through some communication channel */
if (ed25519_create_seed(seed)) {
    printf("error while generating seed\n");
    exit(1);
}

ed25519_create_keypair(other_public_key, other_private_key, seed);

/* do a key exchange with other_public_key */
ed25519_key_exchange(shared_secret, other_public_key, private_key);

/* 
    the magic here is that ed25519_key_exchange(shared_secret, public_key,
    other_private_key); would result in the same shared_secret
*/

License

All code is released under the zlib license. See license.txt for details.

ed25519's People

Contributors

orlp avatar radii avatar vszakats avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ed25519's Issues

Why private_key 64 bits?

Soory sir:

i have a confuse that why private key is 64 bits? the order of G is 2^255 +277....493, so privacy_key shouldnt be one number less order? So why 64 bits?

Thank you!
Wang

why private_key + 32 in sign.c

Maybe it's me being dumb, but why adding 32 to the private_key address in the call to sha512_update in sign.c? Doesn't that make it point to a random value in memory?

Multisig

Hello! This is a question or a feature request.

I find this portable library very useful, but has come to a situation where multisig functionality is required. The solution is outlined in the answer to this question:
https://crypto.stackexchange.com/questions/50448/schnorr-signatures-multisignature-support

Considering my specific use cases, the desired functions are:

  1. Verify a combined signature of two or more public keys, for instance 𝐴+𝐡 as mentioned in the answer on Stack Exchange.

  2. Create a combined signature for one message using multiple key pairs, where all seeds or private keys are known to the signer. (This is mostly required to test the verification in 1. Later on other implementations will create such signatures cooperatively or on their own.)

Maybe this can already be achieved by using the provided functions in the lib in various ways? Any ideas are welcome.

Thank you!

key recovery from protocols using ed25519_add_scalar

Consider the following protocol:

Alice has a private key a with corresponding public key A.
Alice and Bob perform some protocol to derive a scalar t, from which they derive a new keypair (a', A') using ed25519_add_scalar.
Now Bob gets Alice to sign the same message twice - once using A and once using A'. Denote (R, S) and (R', S') as the respective signatures.

Simple enough, right? However, note that ed25519_add_scalar does not modify the second half of the key (the one used to generate R), so R=R'. Bob can therefore recover Alice's private key using:

a = (S - S' + t*H(R,A',M)) / (H(R,A,M)-H(R,A',M))

I have implemented a proof-of-concept that confirms it works in practice.

My recommendation is that ed25519_add_scalar should replace private_key[32:64] with Hash(private_key[32:64], scalar).

Get public key (and seed) from private key

Hi,

as far as I know it should be possible to get the public key from a private one.
Unfortunatly I'm not feeling confident enough to code this by myself.

So maybe someone could do this? Consider this a feature request πŸ˜„

ge_madd and ge_add difference

Is there any difference between ge_add and ge_madd functions except for the fact that they accept different types of points representation?

Possible issues regarding signed to unsigned conversions.

Compiling with the -Wsign-conversion option gives the following warnings.

src/ge.c:334:24: warning: implicit conversion changes signedness: 'signed char' to 'unsigned char' [-Wsign-conversion]
    unsigned char ub = b;
                  ~~   ^
src/ge.c:335:24: warning: implicit conversion changes signedness: 'signed char' to 'unsigned char' [-Wsign-conversion]
    unsigned char uc = c;
                  ~~   ^
src/ge.c:344:18: warning: implicit conversion changes signedness: 'signed char' to 'uint64_t' (aka 'unsigned long') [-Wsign-conversion]
    uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
             ~   ^
src/ge.c:363:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][0], equal(babs, 1));
                           ~~~~~ ^~~~
src/ge.c:364:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][1], equal(babs, 2));
                           ~~~~~ ^~~~
src/ge.c:365:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][2], equal(babs, 3));
                           ~~~~~ ^~~~
src/ge.c:366:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][3], equal(babs, 4));
                           ~~~~~ ^~~~
src/ge.c:367:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][4], equal(babs, 5));
                           ~~~~~ ^~~~
src/ge.c:368:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][5], equal(babs, 6));
                           ~~~~~ ^~~~
src/ge.c:369:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][6], equal(babs, 7));
                           ~~~~~ ^~~~
src/ge.c:370:34: warning: implicit conversion changes signedness: 'unsigned char' to 'signed char' [-Wsign-conversion]
    cmov(t, &base[pos][7], equal(babs, 8));

I'm also confused as to why there are parameters that are explicitly signed and then immediately assigned to unsigned variables.

                                                                                                               
static unsigned char equal(signed char b, signed char c) {                                                    
    unsigned char ub = b;                                                                                     
    unsigned char uc = c;                                                                                     
    unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */                                                       
    uint64_t y = x; /* 0: yes; 1..255: no */                                                                  
    y -= 1; /* large: yes; 0..254: no */                                                                      
    y >>= 63; /* 1: yes; 0: no */                                                                             
    return (unsigned char) y;                                                                                 
}                                                                                                             
                                                                                                              
static unsigned char negative(signed char b) {                                                                
    uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */                         
    x >>= 63; /* 1: yes; 0: no */                                                                             
    return (unsigned char) x;                                                                                 
}

equal appears to only be used in this file and is called with unsigned values. negative seems to just be testing if b is in the range [-128.-1]. why not return b < 0 or return b >> (sizeof(signed char) - 1)?

Also, the comment 0..255: no is wrong since b will never be in [128,255].

Also, in equal, why use the 64-bit intermediate?

ed25519_sign() is significantly different from current libsodium/SUPERCOP ref10 implementations (and generates incompatible output)

In working with your implementation, I was doing some testing against other code I have that uses libsodium, and noted that the ed25519 signatures produced by each were entirely different. After some digging, I noted that the implementation of ed25519_sign() you provide does not match the current libsodium/SUPERCOP ref10 (20141124) implementations.

I have an updated version that is interoperable that I'd be happy to submit if you'd like.

Inconsistent signature results

Note: this library stores private keys in a different format than some other libraries, notably libsodium. They tend to store the concatenation of the seed and public_key as their private key representation. If you wish to be compatible with these libraries you must keep the seed around.

Can you expand on this? In Go, I'm generating a key pair, and using it for signing:

        // pubKey = 32 bytes, privKey = 64 bytes (which is seed + publicKey)
	pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)

	goSignature := ed25519.Sign(privKey, exampleBytes)

When I do something similar with the same key material and ed25519_sign, I get a different signature:

	var pubBuf, privBuf, messageBuf, signatureBuf bytes.Buffer
	messageBuf.Write(exampleBytes)
	pubBuf.Write(pubKey)
	privBuf.Write(privKey)

	for i := 0; i != 64; i++ {
		signatureBuf.WriteByte(0)
	}
	C.ed25519_sign(
		(*C.uchar)(&signatureBuf.Bytes()[0]),
		(*C.uchar)(&messageBuf.Bytes()[0]),
		(C.ulong)(len(exampleBytes)),
		(*C.uchar)(&pubBuf.Bytes()[0]),
		(*C.uchar)(&privBuf.Bytes()[0]))

I'm guessing this is related to the comment above, but I'm not quite sure?

2nd hexadecimal number of private key

Hello
The private key is supposed to be the SHA512 hash of the seed but for exemple with the seed 4cd4110833f1c3716c57dc34786702d30e0fa6c4ab06b78faf42005e467249e3
The hash is e6d51e244199e61b8bd5ea48a627938966763a08a1dbb5401afb43152c4cae073cc29453ce07327d832ae261a216e507526a3903bd2720e12719b1b4512afe2f
while the private key is e0d51e244199e61b8bd5ea48a627938966763a08a1dbb5401afb43152c4cae473cc29453ce07327d832ae261a216e507526a3903bd2720e12719b1b4512afe2f
The only difference is the first byte that is e0 in the library but that should be e6.
Do you know why there is this difference ?

Secret key generation

Hi! This is not an issue as much as it is a comment; I was reviewing the code for compatibility with libsodium and apart from the way that private keys are encoded, I noticed that line for line the bit clearing was also different, despite both being based on the ref10 implementation. It was the pk[31] &= 63 that was different from most other implementations, which do pk[31] &= 127. However, this doesn't matter as the next line is pk[31] |= 64. It can be seen from the below notation:

  0b11111111
& 0b01111111 // 127
| 0b01000000 // 64

is equivalent to

  0b11111111
& 0b00111111 // 63
| 0b01000000 // 64

I just want to leave this here for anyone else who finds this in the future :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.