Class: HDetEc::Key

Inherits:
Object
  • Object
show all
Extended by:
DataManipulation, ECManipulation
Includes:
DataManipulation
Defined in:
lib/hdet-ec-key/key.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ECManipulation

child_key_derivation, child_key_derivation_hardened, child_key_derivation_private, child_key_derivation_public, generate_public_key_from_private, group, group=, serp

Methods included from DataManipulation

double_sha256, left_hash, right_hash, rmd160_sha256, ser256, ser32, split_hash, to_binary, to_number

Constructor Details

#initialize(extended_k, public_or_private, depth, index, parent_fingerprint) ⇒ Key

Returns a new instance of Key.



73
74
75
76
77
78
79
# File 'lib/hdet-ec-key/key.rb', line 73

def initialize(extended_k, public_or_private, depth, index, parent_fingerprint)
  @depth = depth
  @index = index
  @key, @chain_code = extended_k
  @public_or_private = public_or_private
  @parent_fingerprint = parent_fingerprint
end

Instance Attribute Details

#chain_codeObject (readonly)

Returns the value of attribute chain_code.



70
71
72
# File 'lib/hdet-ec-key/key.rb', line 70

def chain_code
  @chain_code
end

#depthObject (readonly)

Returns the value of attribute depth.



70
71
72
# File 'lib/hdet-ec-key/key.rb', line 70

def depth
  @depth
end

#fingerprintObject (readonly)

This method is abstract.

Compute the key fingerprint



152
153
154
# File 'lib/hdet-ec-key/key.rb', line 152

def fingerprint
  @fingerprint
end

#indexObject (readonly)

Returns the value of attribute index.



70
71
72
# File 'lib/hdet-ec-key/key.rb', line 70

def index
  @index
end

#keyObject (readonly)

Returns the value of attribute key.



70
71
72
# File 'lib/hdet-ec-key/key.rb', line 70

def key
  @key
end

#parent_fingerprintObject (readonly)

Returns the value of attribute parent_fingerprint.



71
72
73
# File 'lib/hdet-ec-key/key.rb', line 71

def parent_fingerprint
  @parent_fingerprint
end

#public_or_privateObject (readonly)

Returns the value of attribute public_or_private.



70
71
72
# File 'lib/hdet-ec-key/key.rb', line 70

def public_or_private
  @public_or_private
end

Class Method Details

.generate_master_key(seed, key = "Bitcoin seed") ⇒ Key

This method is abstract.

Generate a master extended key from #seed and #key

According to BIP32 specification, the master key is generated from a HMAC512 and splited in two vector of 256 bits each. The first is the master key and the later is the chain code.

You just have to provide the #seed. The standard #key is “Bitcoin seed” but it can be overrided.

The #seed could be any kind of string. No convertion is performed (idem for #key), so if you want a binary seed represented by the hex form you have to convert it like:

@example: Binary seed with hex form

seed = ["000102030405060708090a0b0c0d0e0f"].pack("H*")

Returns:

  • (Key)

    A new master key.



30
31
32
33
34
# File 'lib/hdet-ec-key/key.rb', line 30

def generate_master_key(seed, key = "Bitcoin seed")
  inter = OpenSSL::HMAC.digest('SHA512', key, seed)
  m = split_hash(inter)
  Key.new(m, :private, 0, 0, "\x00" * 4)
end

.import(serialized_key_string) ⇒ Object

This method is abstract.

Import a serialized key in as describe in BIP32 specification

Conforming to the BIP32 specification, the key should start with ‘xpub’ or ‘xprv’. Other version of testnet is not supported.

Because a checksum is stored in the key, this function check the integrety of imported data, if check sum could not be verified it raise an ArgumentError “Wrong checksum”.

Raises:

  • (ArgumentError)


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/hdet-ec-key/key.rb', line 45

def import(serialized_key_string)
  data = Base58.base58_to_binary(serialized_key_string, :bitcoin)

  unpacked = data.unpack("L>Ca4L>a32a33a4")
  version, depth, parent_fingerprint, index, c, k, checksum = unpacked

  # Remove checksum from data
  data.slice!(-4..-1)

  raise ArgumentError, "Wrong checksum" unless hash256(data)[0..3] == checksum

  case version
  when 0x0488ADE4
    k.slice!(0)
    Key.new([k, c], :private, depth, index, parent_fingerprint)
  when 0x0488B21E
    Key.new([k, c], :public, depth, index, parent_fingerprint)
  else raise "version not supported #{version.to_s(16)}"
  end

end

Instance Method Details

#derive(path) ⇒ HDetEc

This method is abstract.

Derive key by specifying derivation path

Derive a private or a public key by applying multiple derivation definined by the derivation path.

The #path is an Array of multiple indexes. For example to derive a key like m/0H/1/2H/2, use indexes [0.h, 1, 2.h, 2].

Returns:

  • (HDetEc)

    The returned object is an instance of the new derivated key.



137
138
139
140
141
142
143
144
145
146
# File 'lib/hdet-ec-key/key.rb', line 137

def derive(path)
  return self if path.nil? || path.empty?

  case public_or_private
  when :public  then public_key_derivation(path)
  when :private then private_key_derivation(path)
  else
    raise "Wrong key"
  end
end

#private?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/hdet-ec-key/key.rb', line 96

def private?
  public_or_private == :private
end

#public?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/hdet-ec-key/key.rb', line 92

def public?
  public_or_private == :public
end

#public_keyObject Also known as: public_key_from_private



81
82
83
84
85
86
87
88
# File 'lib/hdet-ec-key/key.rb', line 81

def public_key
  case public_or_private
  when :public
    self
  when :private
    Key.new([Key.point(key), chain_code], :public, depth, index, parent_fingerprint)
  end
end

#serialize_extended_keyObject Also known as: serialize

This method is abstract.

serialize key according to BIP32 specifications

The serialization result is a string in Base58 that represents the public or the private extented key.



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/hdet-ec-key/key.rb', line 108

def serialize_extended_key
  k, c = key, chain_code

  case public_or_private
  when :private
    data = ser32(0x0488ADE4) + to_binary(depth) + parent_fingerprint + ser32(index) + c + "\x00" + k
  when :public then
    data = ser32(0x0488B21E) + to_binary(depth) + parent_fingerprint + ser32(index) + c + Key.serp(public_key.key)
  else raise "invalid property #{public_or_private}"
  end

  Base58.binary_to_base58(data + hash256(data)[0..3], :bitcoin)
end