class HDetEc::Key
Attributes
Public Class Methods
@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*")
@return [Key] A new master key. @note
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
# 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
@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”.
# 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
# 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
Public Instance Methods
@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].
@return [HDetEc] The returned object is an instance of the new derivated
key.
@note
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#the-key-tree
# 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
# File lib/hdet-ec-key/key.rb, line 96 def private? public_or_private == :private end
# File lib/hdet-ec-key/key.rb, line 92 def public? public_or_private == :public end
# 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
@abstract serialize key according to BIP32 specifications
The serialization result is a string in Base58 that represents the public or the private extented key.
@note
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format
# 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
Private Instance Methods
# File lib/hdet-ec-key/key.rb, line 185 def key_fingerprint(ex_k) k, c = ex_k case public_or_private when :private then hash160(Key::point(k))[0..3] when :public then hash160(k)[0..3] end end
# File lib/hdet-ec-key/key.rb, line 171 def private_key_derivation(path) ex_k = [key, chain_code] ex_k_parent = ex_k path.each do |i| ex_k_parent = ex_k ex_k = HDetEc::Key.CKDpriv(ex_k_parent, i) end HDetEc::Key.new(ex_k, :private, path.size, path.last, key_fingerprint(ex_k_parent)) end
# File lib/hdet-ec-key/key.rb, line 156 def public_key_derivation(path) ex_k = [key, chain_code] ex_k_parent = ex_k path.each do |i| raise "No hardened derivation for public key" if i >= 2**31 ex_k_parent = ex_k ex_k = HDetEc::Key.CKDpub(ex_k_parent, i) end HDetEc::Key.new(ex_k, :public, path.size, path.last, key_fingerprint(ex_k_parent)) end