Installation

To use this shard, start by adding it to your shard.yml like so:

dependencies:
  ecdsa:
    github: sikoba/ECDSA.cr
    version: ~> 0.1

This requires Crystal 0.36 or higher. An older version, which works with Crystal 0.35 and uses SHA256 for hashing, is available as branch "legacy-crystal-0.35"

Usage Examples

SHA3

We have implemented SHA3_256 in this shard.

res = Digest::SHA3.hexdigest "https://www.sikoba.com"
puts res #=> 93adc6708e6c5d53c6dcab13ffd31d695b5bfd49282cf457d4ed4f323a83c751

Initialising a group

Here is how to initialise the group secp256k1, which is the one used by Bitcoin, Ethereum and many other cryptocurrencies. The list of predefined groups is provided in "curves.cr". Note that it is possible to use any curve as long as h = 1.

g = ECDSA.get_group :secp256k1
puts "g.name: #{g.name}" #=> secp256k1
puts "g.p: #{g.p}" #=> 115792089237316195423570985008687907853269984665640564039457584007908834671663
puts "g.a: #{g.a}" #=> 0
puts "g.b: #{g.b}" #=> 7
puts "g.gx: #{g.gx}" #=> 55066263022277343669578718895168534326250603453777594175500187360389116729240
puts "g.gy: #{g.gy}" #=> 32670510020758816978083085130507043184471273380659243275938904335757337482424
puts "g.n: #{g.n}" #=> 115792089237316195423570985008687907852837564279074904382605163141518161494337
puts "g.d: #{g.d}" #=> 256

Creating a key pair

Here is how to create a new key pair for a given group g:

key_pair = g1.create_key_pair()
puts "Secret key: #{key_pair.[:secret_key]}"
puts "Public key (x): #{key_pair.[:public_key].x}"
puts "Public key (y): #{key_pair.[:public_key].y}"

Computing a public key

Here is how to get the public key from a private key "sec":

sec = BigInt.new("181819889614099500139968055079267329034062173137940193777846146779387277", base: 10)

key_pair = g.create_key_pair(sec)
puts "Public key (x): #{key_pair.[:public_key].x}"
#=> 85178987611776079650687100736630225395836133315679241448696142401730235065445
puts "Public key (y): #{key_pair.[:public_key].y}"
#=> 42655463775677901349476176253478345062189292709218709770749313858929229563957

Signing

The default signature start by hasing the message using SHA3, then signs using a random integer:

signature = g.sign(sec, message)
puts signature.r #=> 41299063418692046109627559638328395711492145286793497389846134068804323840081
puts signature.s #=> 75335275323898069941217967781306232505574825015382438464958146008911188316568

You can also use your own random number:

k = BigInt.new("5846704604701277839882806211944760658860225489638225087703968849823566991145", base: 10)
signature = g.sign(sec, message, k)
puts signature.r #=> 46936881718680924751941056637981176854079153858678292484057701054143224621739
puts signature.s #=> 18442110116601975958734127083110648061233993479485730263351699466754248595366

You can also use another hashing sunction, in which case you will need to sign a number. See "group.cr" for more ways to sign.

Verify signature

verify = g.verify(key_pair[:public_key], message, signature)
puts "Result of verification: #{verify}" #=> true

Precomputed curves

To speed up signing (significantly) and verification (a bit), you can use the curves :secp256k1_PRE and :secp256r1_PRE, for which values of g * 2^n are precomputed.

To Do

Current benchmark (secp256k1) using SHA3

                           user     system      total        real
key-pair generation:   0.022889   0.000033   0.022922 (  0.019881)
sign:                  0.023829   0.000049   0.023878 (  0.021340)
verify:                0.064223   0.008755   0.072978 (  0.065146)

If turn of 1 param check in verify method:

                           user     system      total        real
key-pair generation:   0.050000   0.000000   0.050000 (  0.049207)
sign:                  0.050000   0.000000   0.050000 (  0.042136)
verify:                0.090000   0.010000   0.100000 (  0.084240)

Ruby ecdsa implementation:

                          user     system      total        real
key-pair generation:  0.110000   0.000000   0.110000 (  0.105881)
sign:                 0.110000   0.000000   0.110000 (  0.107868)
verify:               0.210000   0.000000   0.210000 (  0.214049)

Impact of using precomputed values

Using the precomputed curve :secp256k1_PRE is faster than :secp256k1, especially for key-pair generation and signing:

    key-pair generation:  46.90  ( 21.32ms) (± 4.85%)  10.4MB/op
                   sign:  47.35  ( 21.12ms) (± 4.41%)  10.3MB/op
                 verify:  14.83  ( 67.42ms) (± 6.27%)  32.5MB/op

key-pair generation PRE: 142.43  (  7.02ms) (± 5.30%)  3.44MB/op
               sign_PRE: 141.27  (  7.08ms) (± 4.90%)  3.47MB/op
             verify_PRE:  18.67  ( 53.55ms) (± 5.53%)  25.9MB/op