#blake3 #curve25519 #signature #schnorr-signature

c255b3

Schorr signatures based Curve25519 and Blake3

3 releases

0.0.3 May 27, 2023
0.0.2 Apr 14, 2023
0.0.1 Apr 13, 2023

#1738 in Cryptography

44 downloads per month

Unlicense

21KB
290 lines

c255b3: schnorr signatures using curve25519 and blake3.

This crate provides Schnorr signatures based on Curve25519 and Blake3.

Please note that this particular formulation is deprecated. It lives on as r255b3, based on ristretto255 and blake3 instead, for a cleaner definition and faster public key parsing.

Note: This is not Ed25519, if you want Ed25519, please use the excellent ed25519-dalek crate.

Warning! This is a deprecated cryptographic primitive. It has not been audited. Use at your own risk!

Why?

The initial motivation was preparing for embedded versions of converge. We already use the Blake3 hash function for bulk data, and adding SHA512 just for Ed25519 signatures just isn't necessary.

That said, there are other benefits:

  • proper application-specific domain separation for signatures
  • support for non-deterministic signatures with application-supplied nonces
  • well specified secret and public keys
  • a much faster hash function

How?

For the most part the parameterization is straight-forward. However there are two minor deviations:

  • The keyed version of Blake3 is used to provide domain separation.
  • The nonce k is deterministic by default, a Blake3 hash of the private key and message keyed with the domain.

Key Generation

c255b3 uses 255-bit (less 19 values) secret keys. Contrary to Ed25519 it does not require clamping, though to reuse the keys with X25519 the low three bits must be cleared. The secret key sk may simply be generated as 256 random bits and reduced modulo the order of the base point (slightly more than 2²⁵²).

Signing

The first step is to generate the scalar nonce k. This can be done with a random number generator, or by taking the Blake3 hash (keyed with the domain) of the secret key concatinated with the message.

k = Blake3(domain, sk | message)

Now k multiplies the base point to get the point r, which we will recover later while verifying. r is compressed, concatinated with the message, and fed to Blake2 to used to derive the scalar e.

r = k*B
e = Blake3(domain, r | message)

Finally, we multiply e and the secret key, and subtract their product from k, yielding the scalar s. This produces our signature, which is the tuple of e and s.

s = k - sk * e
sig = (e, s)

Verifying

For verification we need the public key, this is found by multiplying the base point by the private key.

pk = sk*B

Then we take our signature and reconstruct r, calling it rᵥ:

rᵥ = s*B + e*pk = (k - sk*e)*B + (e*sk*B) = k*B - sk*e*B + e*sk*B = k*B

Now that we have rᵥ, we hash the message with Blake3 exactly as was done during signing. This yields another copy of e, which we call eᵥ.

eᵥ = Blake3(domain, rᵥ | message)

Finally we can compare eᵥ with the e from the signature, and if they match we have a valid signature.

License

This project is dedicated to the public domain, see the UNLICENSE for details.

Dependencies

~2.5–3.5MB
~81K SLoC