Curvy (Curvy v0.3.0) View Source

Curvy

License

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.0"}
  ]
end

Usage

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::binery-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", compact: true)
<<sig::binary-size(65)>>

iex> sig = Curvy.sign("hello", 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

Link to this section Summary

Functions

Creates a new random ESCDA keypair.

Computes an ECDH shared secret from the first given key's private key and the second's public key.

Recovers the public key from the signature and signed message.

Signs the message with the given private key.

Verifies the signature against the given message and public key.

Link to this section Functions

Specs

generate_key() :: Curvy.Key.t()

Creates a new random ESCDA keypair.

Link to this function

get_shared_secret(privkey, pubkey, opts \\ [])

View Source

Computes an ECDH shared secret from the first given key's private key and the second's public key.

Returns a 32 byte binary.

Accepted options

  • :encoding - Optionally encode the returned secret as :base64 or :hex.
Link to this function

recover_key(sig, message, opts \\ [])

View Source

Specs

recover_key(Curvy.Signature.t() | binary(), binary(), keyword()) ::
  Curvy.Key.t() | :error

Recovers the public key from the signature and signed message.

Returns an ECDSA Keypair struct, without the privkey value.

If recovering fom a DER encoded signature, the Recovery ID returned from Curvy.sign(msg, key, recovery: true) must be passed as an option. If recovering from a compact signature the recovery ID is already encoded in the signature.

Accepted options

  • :encoding - Optionally decode the given signature as :base64 or :hex.
  • :hash - Digest algorithm to hash the message with. Default is :sha256.
  • :recovery_id - The signature Recovery ID.
Link to this function

sign(message, privkey, opts \\ [])

View Source

Specs

sign(binary(), Curvy.Key.t() | binary(), keyword()) :: binary()

Signs the message with the given private key.

Returns a DER encoded or compact signature binary.

Accepted options

  • :hash - Digest algorithm to hash the message with. Default is :sha256.
  • :normalize - Normalize the signature by enforcing low-S. Default is true.
  • :compact - Return a compact 65 byte signature. Default is false.
  • :encoding - Optionally encode the returned signature as :base64 or :hex.
  • :recovery - Return the signature in a tuple paired with a recovery ID. Default is false.
  • :k - Optionally provide a signing secret K value, as a 256 bit integer or binary.
Link to this function

verify(sig, message, pubkey, opts \\ [])

View Source

Specs

verify(
  Curvy.Signature.t() | binary(),
  binary(),
  Curvy.Key.t() | binary(),
  keyword()
) :: boolean() | :error

Verifies the signature against the given message and public key.

Returns a boolean.

Accepted options

  • :encoding - Optionally decode the given signature as :base64 or :hex.
  • :hash - Digest algorithm to hash the message with. Default is :sha256.