diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index 9def6bf..b9294a9 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -83,6 +83,17 @@ def derive_private_key(i = 0) return child_private_key, child_chain_code end + def derive_parent_node(parent_key) + message = parent_key.public_key.to_bytes << i_as_bytes(index) + hash = hmac_sha512 hex_to_bytes(parent_key.chain_code_hex), message + priv = (private_key.to_i - left_from_hash(hash)) % MoneyTree::Key::ORDER + MoneyTree::Node.new( depth: parent_key.depth, + index: parent_key.index, + private_key: MoneyTree::PrivateKey.new(key: priv), + public_key: parent_key.public_key, + chain_code: parent_key.chain_code) + end + def derive_public_key(i = 0) raise PrivatePublicMismatch if i >= 0x80000000 message = public_derivation_message(i) diff --git a/spec/lib/money-tree/node_spec.rb b/spec/lib/money-tree/node_spec.rb index 24a2928..01189a8 100644 --- a/spec/lib/money-tree/node_spec.rb +++ b/spec/lib/money-tree/node_spec.rb @@ -803,5 +803,34 @@ end end end + + describe "deriving a parent node" do + before do + @master = MoneyTree::Master.new seed_hex: "000102030405060708090a0b0c0d0e0f" + @node = @master.node_for_path('m/101p') + @subnode = @node.node_for_path('1') + end + context "m/101'/1 -> m/101'" do + it "correctly derives from a subnode with priv key to a node knowing it's public key" do + node_priv_hex = @node.private_key.to_hex + @node.strip_private_info! + expect(@subnode.derive_parent_node(@node).private_key.to_hex).to eq(node_priv_hex) + end + end + context "m/101' -> master" do + it "unable to derive from a hardened node with priv key to a master node knowing it's public key" do + master_priv_hex = @master.private_key.to_hex + @master.strip_private_info! + expect(@node.derive_parent_node(@master).private_key.to_hex).to_not eq(master_priv_hex) + end + end + context 'm/101 -> master' do + it "correctly derives from a non-hardened node with priv key to a master node knowing it's public key" do + @node = @master.node_for_path('m/101') + master_priv_hex = @master.private_key.to_hex + expect(@node.derive_parent_node(@master).private_key.to_hex).to eq(master_priv_hex) + end + end + end end end