Class: BlockIo::Key

Inherits:
Object
  • Object
show all
Defined in:
lib/block_io/key.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(privkey = nil, use_low_r = true, compressed = true) ⇒ Key

Returns a new instance of Key.



5
6
7
8
9
10
11
12
13
14
# File 'lib/block_io/key.rb', line 5

def initialize(privkey = nil, use_low_r = true, compressed = true)
  # the privkey must be in hex if at all provided

  @group = ECDSA::Group::Secp256k1
  @private_key = (privkey.nil? ? (1 + SecureRandom.random_number(@group.order - 1)) : privkey.to_i(16))
  @public_key = @group.generator.multiply_by_scalar(@private_key)
  @compressed = compressed
  @use_low_r = use_low_r
  
end

Class Method Details

.from_passphrase(passphrase, use_low_r = true) ⇒ Object

Raises:

  • (Exception)


65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/block_io/key.rb', line 65

def self.from_passphrase(passphrase, use_low_r = true)
  # ATTENTION: use BlockIo::Key.new to generate new private keys. Using passphrases is not recommended due to lack of / low entropy.
  # create a private/public key pair from a given passphrase
  # use a long, random passphrase. your security depends on the passphrase's entropy.
  
  raise Exception.new("Must provide passphrase at least 8 characters long.") if passphrase.nil? or passphrase.length < 8
  
  hashed_key = Helper.sha256([passphrase].pack("H*")) # must pass bytes to sha256
  
  # modding is for backward compatibility with legacy bitcoinjs
  Key.new((hashed_key.to_i(16) % ECDSA::Group::Secp256k1.order).to_s(16), use_low_r)
end

.from_wif(wif, use_low_r = true) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/block_io/key.rb', line 78

def self.from_wif(wif, use_low_r = true)
  # returns a new key extracted from the Wallet Import Format provided
  # TODO check against checksum

  hexkey = Helper.decode_base58(wif)
  actual_key = hexkey[2...66]

  compressed = hexkey[2..hexkey.length].length-8 > 64 and hexkey[2..hexkey.length][64...66] == "01"

  Key.new(actual_key, use_low_r, compressed)

end

Instance Method Details

#private_keyObject



16
17
18
19
# File 'lib/block_io/key.rb', line 16

def private_key
  # returns private key in hex form
  @private_key.to_s(16)
end

#public_keyObject



21
22
23
24
25
# File 'lib/block_io/key.rb', line 21

def public_key
  # returns the compressed form of the public key to save network fees (shorter scripts)
  # hex form
  ECDSA::Format::PointOctetString.encode(@public_key, compression: @compressed).unpack("H*")[0]
end

#sign(data) ⇒ Object



27
28
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
55
56
57
58
59
# File 'lib/block_io/key.rb', line 27

def sign(data)
  # sign the given hexadecimal string

  counter = 0
  signature = nil
  
  loop do

    # first this we get K, it's without extra entropy
    # second time onwards, with extra entropy
    nonce = Key.deterministicGenerateK([data].pack("H*"), @private_key, counter) # RFC6979
    signature = ECDSA.sign(@group, @private_key, data.to_i(16), nonce)
  
    r, s = signature.components

    # BIP0062 -- use lower S values only
    over_two = @group.order >> 1 # half of what it was                     
    s = @group.order - s if (s > over_two)
    
    signature = ECDSA::Signature.new(r, s)

    # DER encode this, and return it in hex form
    signature = ECDSA::Format::SignatureDerString.encode(signature).unpack("H*")[0]

    break if !@use_low_r or Helper.low_r?(signature)

    counter += 1

  end
  
  signature
  
end

#valid_signature?(signature, data) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/block_io/key.rb', line 61

def valid_signature?(signature, data)
  ECDSA.valid_signature?(@public_key, [data].pack("H*"), ECDSA::Format::SignatureDerString.decode([signature].pack("H*")))
end