orlp / ed25519 Goto Github PK
View Code? Open in Web Editor NEWPortable C implementation of Ed25519, a high-speed high-security public-key signature system.
License: zlib License
Portable C implementation of Ed25519, a high-speed high-security public-key signature system.
License: zlib License
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:
Verify a combined signature of two or more public keys, for instance ๐ด+๐ต as mentioned in the answer on Stack Exchange.
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!
I created a vcpkg port for you.
The dependent library that uses your project needs some stuff from fe.h
, fixedint.h
, and ge.h
. Any chance these can be put under the ed25519 namespace, as #include <ed25519/fe.h>
&etc.?
Thanks
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?
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?
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.
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).
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
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?
According to RFC 8032, ed25519 private key should be 32 bytes long. Does the implement comply with the standard?
The implementation seems not comply with the test vector provided by RFC 8032. Please refer to https://asecuritysite.com/ecc/eddsa5 for the test vector. Thanks.
I have ed25519 pubkeys.
I would like to check there validity.
The RFC say how to do that by decoding/decompressing to an x,y point : https://tools.ietf.org/html/rfc8032#page-11
Do y plan to add this to this lib ?
Hi,
Why key exchange is not working after any pair is derived from ed25519_add_scalar? Or it should not and can be used only for signing?
Is there any difference between ge_add and ge_madd functions except for the fact that they accept different types of points representation?
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 ๐
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 :)
Used this library to sign software for my auto-updater. As long as you use this implementation stand-alone everything seems to be ok. Signing and verification works.
But: The private keys generated with this library are incompatible with other implementations (e.g. libsodium which is used by PHP).
See jedisct1/libsodium#1213 for more details.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.