curvy
curvy copied to clipboard
Signatures and Bitcoin flavoured crypto written in pure Elixir.
Curvy

Signatures and Bitcoin flavoured crypto written in pure Elixir. Curvy is an implementation of secp256k1, an elliptic curve that can be used in signature schemes, asymmetric encryption and ECDH shared secrets.
Highlights
- Pure Elixir implementation of
secp256k1- no external dependencies - Fast ECDSA cryptography using Jacobian Point mathematics
- Supports deterministic ECDSA signatures as per RFC 6979
- Securely generate random ECDSA keypairs
- Compute ECDH shared secrets
Installation
The package can be installed by adding curvy to your list of dependencies in mix.exs.
def deps do
[
{:curvy, "~> 0.3"}
]
end
Usage
For further examples, refer to the full documentation.
1. Key generation
Create random ECDSA keypairs.
iex> key = Curvy.generate_key()
%Curvy.Key{
crv: :secp256k1,
point: %Curvy.Point{},
private_key: <<>>
}
ECDSA Keypairs can by converted to public and private key binaries.
iex> Curvy.Key.to_privkey(key)
<<privkey::binary-size(32)>>
iex> Curvy.Key.to_pubkey(key)
<<privkey::binary-size(33)>>
iex> Curvy.Key.to_pubkey(key, compressed: false)
<<privkey::binary-size(65)>>
2. Sign messages
Sign arbitrary messages with a private key. Signatures are deterministic as per RFC 6979.
iex> sig = Curvy.sign("hello", key)
<<sig::binary-size(71)>>
iex> sig = Curvy.sign("hello", key, compact: true)
<<sig::binary-size(65)>>
iex> sig = Curvy.sign("hello", key, compact: true, encoding: :base64)
"IEnXUDXZ3aghwXaq1zu9ax2zJj7N+O4gGREmWBmrldwrIb9B7QuicjwPrrv3ocPpxYO7uCxcw+DR/FcHR9b/YjM="
3. Verify signatures
Verify a signature against the message and a public key.
iex> sig = Curvy.verify(sig, "hello", key)
true
iex> sig = Curvy.verify(sig, "hello", wrongkey)
false
# Returns :error if the signature cannot be decoded
iex> sig = Curvy.verify("notasig", "hello", key)
:error
4. Recover the public key from a signature
It's possible to recover the public key from a compact signature when given with the signed message.
iex> sig = Curvy.sign("hello", key, compact: true)
iex> recovered = Curvy.recover_key(sig, "hello")
iex> recovered.point == key.point
true
The same can be done with DER encoded signatures if the recovery ID is known.
iex> {sig, recovery_id} = Curvy.sign("hello", key, recovery: true)
iex> recovered = Curvy.recover_key(sig, "hello", recovery_id: recovery_id)
iex> recovered.point == key.point
true
5. ECDH shared secrets
ECDH shared secrets are computed by multiplying a public key with a private key. The operation yields the same result in both directions.
iex> s1 = Curvy.get_shared_secret(key1, key2)
iex> s2 = Curvy.get_shared_secret(key2, key1)
iex> s1 == s2
true
For more examples, refer to the full documentation.
Disclaimer
The are many warnings that you should never try to implement well-known (and known to be secure) crypto algorithms yourself. Well guess what, that's exactly what I've done here. I am not a cryptographer nor a mathmetician. Proceed at your own risk. If you're after the most performant and battle tested Bitcoin library, use the libsecp256k1 NIF bindings.
This library offers a simpler and smaller interface for common functionality. Being writting purely in Elixir without any dependencies, it is a lighter-weight option without the compilation complexities NIF bindings may bring.
I am grateful to the authors of the following open source libraries I have referred to extensively in creating this library:
- noble-secp256k1 - JS
- starbank-ecdsa - Elixir
- bsv - JS
License
Curvy is open source and released under the Apache-2 License.
© Copyright 2021 Chronos Labs Ltd.