class HDetEc::Key

Attributes

chain_code[R]
depth[R]
fingerprint[R]
index[R]
key[R]
parent_fingerprint[R]
public_or_private[R]

Public Class Methods

generate_master_key(seed, key = "Bitcoin seed") click to toggle source

@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
import(serialized_key_string) click to toggle source

@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
new(extended_k, public_or_private, depth, index, parent_fingerprint) click to toggle source
# 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

derive(path) click to toggle source

@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
private?() click to toggle source
# File lib/hdet-ec-key/key.rb, line 96
def private?
  public_or_private == :private
end
public?() click to toggle source
# File lib/hdet-ec-key/key.rb, line 92
def public?
  public_or_private == :public
end
public_key() click to toggle source
# 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
Also aliased as: public_key_from_private
public_key_from_private()
Alias for: public_key
serialize()
serialize_extended_key() click to toggle source

@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
Also aliased as: serialize

Private Instance Methods

key_fingerprint(ex_k) click to toggle source
# 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
private_key_derivation(path) click to toggle source
# 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
public_key_derivation(path) click to toggle source
# 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