Module: Bitcoin::Secp256k1::Ruby

Defined in:
lib/bitcoin/secp256k1/ruby.rb

Overview

secp256 module using ecdsa gem github.com/DavidEGrayson/ruby_ecdsa

Class Method Summary collapse

Class Method Details

.generate_key(compressed: true) ⇒ Object

generate bitcoin key object



20
21
22
23
# File 'lib/bitcoin/secp256k1/ruby.rb', line 20

def generate_key(compressed: true)
  privkey, pubkey = generate_key_pair(compressed: compressed)
  Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
end

.generate_key_pair(compressed: true) ⇒ Object

generate ec private key and public key



11
12
13
14
15
16
17
# File 'lib/bitcoin/secp256k1/ruby.rb', line 11

def generate_key_pair(compressed: true)
  private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
  public_key = GROUP.generator.multiply_by_scalar(private_key)
  privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
  pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
  [privkey.bth, pubkey.bth]
end

.generate_rfc6979_nonce(data, privkey) ⇒ Object

generate temporary key k to be used when ECDSA sign. tools.ietf.org/html/rfc6979#section-3.2



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/bitcoin/secp256k1/ruby.rb', line 86

def generate_rfc6979_nonce(data, privkey)
  v = ('01' * 32).htb
  k = ('00' * 32).htb
  # 3.2.d
  k = Bitcoin.hmac_sha256(k, v + '00'.htb + privkey + data)
  # 3.2.e
  v = Bitcoin.hmac_sha256(k, v)
  # 3.2.f
  k = Bitcoin.hmac_sha256(k, v + '01'.htb + privkey + data)
  # 3.2.g
  v = Bitcoin.hmac_sha256(k, v)
  # 3.2.h
  t = ''
  10000.times do
    v = Bitcoin.hmac_sha256(k, v)
    t = (t + v)
    t_num = t.bth.to_i(16)
    return t_num if 1 <= t_num && t_num < GROUP.order
    k = Bitcoin.hmac_sha256(k, v + '00'.htb)
    v = Bitcoin.hmac_sha256(v)
  end
  raise 'A valid nonce was not found.'
end

.repack_pubkey(pubkey) ⇒ Object

if pubkey is hybrid public key format, it convert uncompressed format. lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html



73
74
75
76
77
78
79
80
81
82
# File 'lib/bitcoin/secp256k1/ruby.rb', line 73

def repack_pubkey(pubkey)
  p = pubkey.htb
  case p[0]
    when "\x06", "\x07"
      p[0] = "\x04"
      p
    else
      pubkey.htb
  end
end

.sign_data(data, privkey) ⇒ String

sign data.

Parameters:

  • data (String)

    a data to be signed with binary format

  • privkey (String)

    a private key using sign

Returns:

  • (String)

    signature data with binary format



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/bitcoin/secp256k1/ruby.rb', line 29

def sign_data(data, privkey)
  privkey = privkey.htb
  private_key = ECDSA::Format::IntegerOctetString.decode(privkey)
  nonce = generate_rfc6979_nonce(data, privkey)

  # port form ecdsa gem.
  r_point = GROUP.new_point(nonce)

  point_field = ECDSA::PrimeField.new(GROUP.order)
  r = point_field.mod(r_point.x)
  return nil if r.zero?

  e = ECDSA.normalize_digest(data, GROUP.bit_length)
  s = point_field.mod(point_field.inverse(nonce) * (e + r * private_key))

  if s > (GROUP.order / 2) # convert low-s
    s = GROUP.order - s
  end

  return nil if s.zero?

  signature = ECDSA::Signature.new(r, s).to_der
  public_key = Bitcoin::Key.new(priv_key: privkey.bth).pubkey
  raise 'Creation of signature failed.' unless Bitcoin::Secp256k1::Ruby.verify_sig(data, signature, public_key)
  signature
end

.verify_sig(digest, sig, pubkey) ⇒ Boolean

verify signature using public key

Parameters:

  • digest (String)

    a SHA-256 message digest with binary format

  • sig (String)

    a signature for data with binary format

  • pubkey (String)

    a public key corresponding to the private key used for sign

Returns:

  • (Boolean)

    verify result



61
62
63
64
65
66
67
68
69
# File 'lib/bitcoin/secp256k1/ruby.rb', line 61

def verify_sig(digest, sig, pubkey)
  begin
    k = ECDSA::Format::PointOctetString.decode(repack_pubkey(pubkey), GROUP)
    signature = ECDSA::Format::SignatureDerString.decode(sig)
    ECDSA.valid_signature?(k, digest, signature)
  rescue Exception
    false
  end
end