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.



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



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