a native library implementing secp256k1 purely for the crystal language
  • v0.3.5 - February 9, 2021
  • v0.3.4 - October 23, 2020
  • v0.3.3 - June 19, 2020
  • v0.3.2 - April 11, 2020
  • v0.3.1 - February 17, 2020

Build Status Documentation Release Language License

a native library implementing secp256k1 purely for the crystal language. secp256k1 is the elliptic curve used in the public-private-key cryptography required by bitcoin and ethereum.

this library allows for key generation of:

  • private keys (from secure random within the elliptic curve field size)
  • mini private keys (short 30-char base-56 keys)
  • wallet import format (checksummed base-58 private keys)
  • public keys, prefixed, compressed (from private)
  • public keys, unprefixed and prefixed, uncompressed (from private)
  • conversion between the different public key formats

this library allows for address generation of:

  • bitcoin address, compressed and uncompressed (from private or public key)
  • any other bitcoin-based address by passing a version byte
  • ethereum address, checksummed and unchecksummed (from private or public key)
  • any other ethereum-based address

furthermore, this library allows for:

  • signing (r, s) and verification of arbitrary messages and message-hashes (with key pairs)
  • managing enode addresses as per devp2p specification for ethereum nodes


add the secp256k1 library to your shard.yml

    github: q9f/
    version: "~> 0.3"


tl;dr, check out crystal run ./!

# import secp256k1
require "secp256k1"

this library exposes the following modules (in logical order):

  • Secp256k1: necessary constants and data structures, including:
    • Secp256k1::Keypair: for managing private-public key-pairs
    • Secp256k1::ECPoint: for handling of secp256k1 elliptic curve points (public keys)
    • Secp256k1::ECDSASignature: for secp256k1 ecdsa signatures
  • Secp256k1::Core: the entire core mathematics behind the elliptic curve cryptography
  • Secp256k1::Util: all tools for the handling of private-public key-pairs
  • Secp256k1::Hash: implementation of various hashing algorithms for convenience
  • Secp256k1::Signature: allows for signing messages and verifying signatures
  • Secp256k1::Bitcoin: for the generation of bitcoin addresses, including:
    • Secp256k1::Bitcoin::Account: for bitcoin account management
  • Secp256k1::Ethereum: for the generation of ethereum addresses, including
    • Secp256k1::Ethereum::Account: for ethereum account management
    • Secp256k1::Ethereum::Enode: for devp2p enode address management

basic usage:

# generates a new keypair
key =
# => #<Secp256k1::Keypair:0x7f8be5611d80>

# gets the private key
# => "53d77137b39427a35d8c4b187f532d3912e1e7135985e730633e1e3c1b87ce97"

# gets the compressed public key with prefix
compressed = Secp256k1::Util.public_key_compressed_prefix key.public_key
# => "03e097fc69f0b92f711620511c07fefdd648e469df46b1e4385a00a1786f6bc55b"

generate a compressed bitcoin mainnet address:

# generates a new keypair
key =
# => #<Secp256k1::Keypair:0x7f8be5611d80>

# generates a compressed bitcoin account from the keypair
btc = key, "00", true
# => #<Secp256k1::Bitcoin::Account:0x7f81ef21ab80>

# gets the wallet-import format (checksummed private key)
# => "Kz2grUzxEAxNopiREbNpVbjoitAGQVXnUZY4n8pNdmWdVqub99qu"

# gets the compressed bitcoin addresss
# => "1Q1zbmPZtS2chwxpviqz6qHgoM8UUuviGN"

generate a checksummed ethereum address:

# generates a new keypair
key =
# => #<Secp256k1::Keypair:0x7f81ef21ad00>

# generates an ethereum account from the keypair
eth = key
# => #<Secp256k1::Ethereum::Account:0x7f81ef1faac0>

# gets the private key
# => "53d77137b39427a35d8c4b187f532d3912e1e7135985e730633e1e3c1b87ce97"

# gets the ethereum addresss
# => "0x224008a0F3d3cB989c807F568c7f99Bf451328A6"


the full library documentation can be found here:

generate a local copy with:

crystal docs


the library is entirely specified through tests in ./spec; run:

crystal spec --verbose


private keys are just scalars and public keys are points with x and y coordinates.

bitcoin public keys can be uncompressed #{p}#{x}#{y} or compressed #{p}#{x}. both come with a prefix p which is useless for uncompressed keys but necessary for compressed keys to recover the y coordinate on the secp256k1 elliptic curve.

ethereum public keys are uncompressed #{x}#{y} without any prefix. the last 20 bytes slice of the y coordinate is actually used as address without any checksum. a checksum was later added in eip-55 using a keccak256 hash and indicating character capitalization.

neither bitcoin nor ethereum allow for recovering public keys from an address unless there exists a transaction with a valid signature on the blockchain.

known issues

note: this library should not be used in production without proper auditing.

  • this library is not constant time and might be subject to side-channel attacks. (#4)
  • this library does unnecessary big-integer math and should someday rather correctly implement the secp256k1 prime field (#5)

found another issue? report it:


create a pull request, and make sure tests and linter passes.

this pure crystal implementation is based on the python implementation wobine/blackboard101 which is also used as reference to write tests against. it's a complete rewrite of the abandoned packetzero/bitcoinutils for educational purposes.

honerable mention for the bitcoin wiki and the ethereum stackexchange for providing so many in-depth resources that supported this project in reimplementing everything.

license: apache license v2.0

contributors: @q9f, @cserb, MrSorcus

github statistic
  • 23
  • 4
  • 7
  • 7
  • 5 months ago
  • December 20, 2019

Apache License 2.0

Synced at

Fri, 23 Jul 2021 12:26:12 GMT