hdet-ec-key 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fcb033ee0743d2afde945e3751dcd25cdad97aa555b96eb1fe5019b8cc209005
4
+ data.tar.gz: 00e91fabee3f2e82741a7e51ed2906c2952aa03e5bf949e6693cd728470052a4
5
+ SHA512:
6
+ metadata.gz: 9ded3bcc9aee7b1c52519ee9da5650b26ddcc10e05d1c3dc1c4316c36491dfe7295bfd5034a971fed7ce8ecbae51852791859c2dda8e175a229f942476a1057a
7
+ data.tar.gz: dda56d995c45047886960bb2424e1fa6fea72ec8a42a45fae2b6f05a20d89e73f18be9cecfcc9e9564a8319ae94bbdcae8b2646fbee9cadda4938add23aae673
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Stéphane Clérambault
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.adoc ADDED
@@ -0,0 +1,88 @@
1
+ = HDetEcKey
2
+
3
+ HDetEcKey is a Ruby implement of https://en.bitcoin.it/wiki/Deterministic_Wallet[Hierachical Deterministic wallets] according to the specification https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki[BIP0032].
4
+
5
+ == What is use for ?
6
+
7
+
8
+ == How to get it !
9
+
10
+ [source, bash]
11
+ ----
12
+ git clone https://gitlab.com/elionne/hdet-ec-key.git
13
+ ----
14
+
15
+ Or in a neer future:
16
+
17
+ [source, bash]
18
+ ----
19
+ gem install hdet-ec-key
20
+ ----
21
+
22
+ === Prerequistes
23
+
24
+ - Ruby 2.5.0
25
+ - Openssl (ruby included)
26
+
27
+ Support of other version of Ruby should be possible.
28
+
29
+ == Usage
30
+
31
+ For more details about how Herarchical Deterministic Wallet works, please see the https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki[BIP0032] specifications.
32
+
33
+ === Generate master key
34
+
35
+ First you need to generate the first key.
36
+ The point where all keys starts.
37
+ The BIP32 standard specify simple method with `HMAC512`.
38
+ The default key is _Bitcoin seed_ and the seed is choosen by the user.
39
+
40
+ To generate a master key:
41
+
42
+ [source, ruby]
43
+ ----
44
+ seed = ["000102030405060708090a0b0c0d0e0f"].pack("H*")
45
+ master_key = HDetEc::Key.generate_master_key(seed)
46
+ ----
47
+
48
+ No convertion of the given string is operated, the `seed` is used _as is_.
49
+ The `key` can be change by setting the second parameter:
50
+
51
+ [source, ruby]
52
+ ----
53
+ master_key = HDetEc::Key.generate_master_key(seed, "my special key")
54
+ ----
55
+
56
+ === Public key, private and public derivation
57
+
58
+ To get the public key from the a key:
59
+
60
+ [source, ruby]
61
+ ----
62
+ pub = master_key.public_key
63
+
64
+ # or a more verbose version
65
+ pub = master.public_key_from_private
66
+ ----
67
+
68
+ Then you can derive, ether public or private key:
69
+
70
+ [source, ruby]
71
+ ----
72
+ pub.derive([0, 1, 2000])
73
+ master_key.derive(0, 1.h, 2000)
74
+ ----
75
+
76
+ The _derivation path_ is determined by an array of index.
77
+ the _#h_ function for interger means *harderned* index, only available for private key derivation.
78
+
79
+ === Serialization
80
+
81
+ Each keys can be serialized in BIP32 specified form:
82
+
83
+ [source,ruby]
84
+ ----
85
+ pub.serialize # xpub.....
86
+ master_key.serialize # xprv.....
87
+ ----
88
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0
@@ -0,0 +1,18 @@
1
+ require 'openssl'
2
+ require 'digest'
3
+ require 'base58'
4
+
5
+ # @abstract Use for hardened index
6
+ #
7
+ # @return the self plus 2^31
8
+ class Integer
9
+ def h
10
+ 2**31 + self
11
+ end
12
+ end
13
+
14
+ module HDetEc
15
+ autoload :DataManipulation, 'hdet-ec-key/data_manipulation'
16
+ autoload :ECManipulation, 'hdet-ec-key/ec_manipulation'
17
+ autoload :Key, 'hdet-ec-key/key'
18
+ end
@@ -0,0 +1,60 @@
1
+
2
+ module HDetEc
3
+ module DataManipulation
4
+
5
+ # Serialize a 32-bit unsigned integer i as a 4-byte sequence, most
6
+ # significant byte first.
7
+ def ser32(i)
8
+ [i].pack("l>")
9
+ end
10
+
11
+ def to_binary(num, byte_length=1)
12
+ num = num.to_i if num.kind_of? OpenSSL::BN
13
+
14
+ hex_num = num.to_s(16).rjust(byte_length*2, "0")
15
+ hex_num = "0" + hex_num if hex_num.size.odd?
16
+
17
+ bin = [hex_num].pack("H*")
18
+ end
19
+
20
+ def to_number(bin)
21
+ bin.unpack1("H*").to_i 16
22
+ end
23
+
24
+ # Serializes the integer p as a 32-byte sequence, most significant byte first.
25
+ def ser256(p)
26
+ raise ArgumentError, "overflow" if p.bit_length > 256
27
+ to_binary(p, 256/8)
28
+ end
29
+
30
+ def left_hash(h)
31
+ h[0..31]
32
+ end
33
+
34
+ def right_hash(h)
35
+ h[32..63]
36
+ end
37
+
38
+ def split_hash(h)
39
+ [left_hash(h), right_hash(h)]
40
+ end
41
+
42
+ def rmd160_sha256(data)
43
+ Digest::RMD160.digest Digest::SHA256.digest(data)
44
+ end
45
+
46
+ alias :hash160 :rmd160_sha256
47
+
48
+ def double_sha256(data)
49
+ Digest::SHA256.digest Digest::SHA256.digest(data)
50
+ end
51
+
52
+ alias :hash256 :double_sha256
53
+
54
+
55
+ # Interprets a 32-byte sequence as a 256-bit number, most significant byte
56
+ # first.
57
+ alias :parse256 :to_number
58
+
59
+ end
60
+ end
@@ -0,0 +1,133 @@
1
+ require 'openssl'
2
+
3
+ module HDetEc
4
+ module ECManipulation
5
+
6
+ # The default group is the same as used by Bitcoin network, "secp256k1". But
7
+ # it can be change, just provide the a OpenSSL group string.
8
+ #
9
+ # example:
10
+ # HDetEc::Key.group = "secp256r1"
11
+ #
12
+ def group=(openssl_group)
13
+ @@group = OpenSSL::PKey::EC.new(openssl_group).group
14
+ @@group.point_conversion_form = :compressed
15
+ @@group
16
+ end
17
+
18
+ def group
19
+ @@group ||= OpenSSL::PKey::EC.new("secp256k1").group
20
+ @@group.point_conversion_form = :compressed
21
+ @@group
22
+ end
23
+
24
+ # @abstract Generate public key from the private one
25
+ #
26
+ # The input parameter {#private_key} a binary string representation of the
27
+ # private key
28
+ #
29
+ # @return [String] The output is a binary representation of the public key
30
+ # in the compressed form.
31
+ def generate_public_key_from_private(private_key)
32
+ bn_private_key = OpenSSL::BN.new private_key, 2
33
+
34
+ public_key = group.generator.mul(bn_private_key)
35
+ public_key.to_bn.to_s(2)
36
+ end
37
+
38
+ alias_method :point, :generate_public_key_from_private
39
+
40
+ # @abstract Generic derivation method for hardened keys
41
+ #
42
+ # Alogrithm to derive only hardened private key. That means no {#index}
43
+ # below 2^31 are allowed.
44
+ def child_key_derivation_hardened(extended_key, index)
45
+ k, c = extended_key
46
+ key = c
47
+ data = "\x00" + k + ser32(index)
48
+ OpenSSL::HMAC.digest('SHA512', key, data)
49
+ end
50
+
51
+ alias_method :CKDh, :child_key_derivation_hardened
52
+
53
+ # @abstract Generic derivation method for non-hardened keys
54
+ #
55
+ # Alogrithm to derive public and private key if it not hardened key. That
56
+ # means no {#index} above 2^31 - 1 are allowed.
57
+ def child_key_derivation(extended_key, index)
58
+ k, c = extended_key
59
+ key = c
60
+ data = serp(k) + ser32(index)
61
+ OpenSSL::HMAC.digest('SHA512', key, data)
62
+ end
63
+
64
+ alias_method :CKD, :child_key_derivation
65
+
66
+ # @abstract Derive a public key according to BIP32 specifications
67
+ #
68
+ # Public parent key → public child key.
69
+ #
70
+ # @param extended_key [Array] extended_key
71
+ # @param index [Integer]
72
+ #
73
+ # @note
74
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#private-parent-key--private-child-key
75
+ def child_key_derivation_private(extended_key, index)
76
+ k, c = extended_key
77
+
78
+ if index >= 2**31
79
+ inter = child_key_derivation_hardened(extended_key, index)
80
+ else
81
+ inter = child_key_derivation([point(k), c], index)
82
+ end
83
+
84
+ iL, iR = split_hash(inter)
85
+
86
+ bn = parse256(iL) + parse256(k)
87
+ k_child = OpenSSL::BN.new(bn) % group.order
88
+
89
+ [k_child.to_s(2), iR]
90
+ end
91
+
92
+ alias_method :CKDpriv, :child_key_derivation_private
93
+
94
+
95
+ # @abstract Derive a public key according to BIP32 specifications
96
+ #
97
+ # Public parent key → public child key.
98
+ #
99
+ # @param extended_key [Array] extended_key
100
+ # @param index [Integer]
101
+ #
102
+ # @note
103
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#public-parent-key--public-child-key
104
+ def child_key_derivation_public(extended_key, index)
105
+ k, c = extended_key
106
+ if index >= 2**31
107
+ raise "No hardened public key derivation possible"
108
+ end
109
+
110
+ inter = child_key_derivation(extended_key, index)
111
+
112
+ iL, iR = split_hash(inter)
113
+
114
+ bn = OpenSSL::BN.new(iL, 2)
115
+ pub_key = OpenSSL::PKey::EC::Point.new(group, k).mul(1, bn)
116
+ [pub_key.to_octet_string(:compressed), iR]
117
+ end
118
+
119
+ alias_method :CKDpub, :child_key_derivation_public
120
+
121
+ # Serializes the coordinate pair P = (x,y) as a byte sequence using SEC1's
122
+ # compressed form: (0x02 or 0x03) || ser256(x), where the header byte
123
+ # depends on the parity of the omitted y coordinate.
124
+ # input kp: public key
125
+ def serp(kp)
126
+ # ensure kp is in compressed form
127
+ point = OpenSSL::PKey::EC::Point.new(group, kp)
128
+ point.to_octet_string(:compressed)
129
+ end
130
+
131
+ end
132
+ end
133
+
@@ -0,0 +1,198 @@
1
+
2
+ module HDetEc
3
+
4
+ class Key
5
+
6
+ class << self
7
+ include DataManipulation
8
+ include ECManipulation
9
+
10
+ # @abstract Generate a master extended key from {#seed} and {#key}
11
+ #
12
+ # According to BIP32 specification, the master key is generated from a
13
+ # HMAC512 and splited in two vector of 256 bits each. The first is the
14
+ # master key and the later is the chain code.
15
+ #
16
+ # You just have to provide the {#seed}. The standard {#key} is
17
+ # "Bitcoin seed" but it can be overrided.
18
+ #
19
+ # The {#seed} could be any kind of string. No convertion is performed (idem
20
+ # for {#key}), so if you want a binary seed represented by the hex form you
21
+ # have to convert it like:
22
+ #
23
+ # @example: Binary seed with hex form
24
+ # seed = ["000102030405060708090a0b0c0d0e0f"].pack("H*")
25
+ #
26
+ #
27
+ # @return [Key] A new master key.
28
+ # @note
29
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation
30
+ def generate_master_key(seed, key = "Bitcoin seed")
31
+ inter = OpenSSL::HMAC.digest('SHA512', key, seed)
32
+ m = split_hash(inter)
33
+ Key.new(m, :private, 0, 0, "\x00" * 4)
34
+ end
35
+
36
+
37
+ # @abstract Import a serialized key in as describe in BIP32 specification
38
+ #
39
+ # Conforming to the BIP32 specification, the key should start with 'xpub'
40
+ # or 'xprv'. Other version of testnet is not supported.
41
+ #
42
+ # Because a checksum is stored in the key, this function check the
43
+ # integrety of imported data, if check sum could not be verified it raise
44
+ # an ArgumentError "Wrong checksum".
45
+ def import(serialized_key_string)
46
+ data = Base58.base58_to_binary(serialized_key_string, :bitcoin)
47
+
48
+ unpacked = data.unpack("L>Ca4L>a32a33a4")
49
+ version, depth, parent_fingerprint, index, c, k, checksum = unpacked
50
+
51
+ # Remove checksum from data
52
+ data.slice!(-4..-1)
53
+
54
+ raise ArgumentError, "Wrong checksum" unless hash256(data)[0..3] == checksum
55
+
56
+ case version
57
+ when 0x0488ADE4
58
+ k.slice!(0)
59
+ Key.new([k, c], :private, depth, index, parent_fingerprint)
60
+ when 0x0488B21E
61
+ Key.new([k, c], :public, depth, index, parent_fingerprint)
62
+ else raise "version not supported #{version.to_s(16)}"
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+ include DataManipulation
69
+
70
+ attr_reader :key, :depth, :index, :public_or_private, :chain_code
71
+ attr_reader :parent_fingerprint, :fingerprint
72
+
73
+ def initialize(extended_k, public_or_private, depth, index, parent_fingerprint)
74
+ @depth = depth
75
+ @index = index
76
+ @key, @chain_code = extended_k
77
+ @public_or_private = public_or_private
78
+ @parent_fingerprint = parent_fingerprint
79
+ end
80
+
81
+ def public_key
82
+ case public_or_private
83
+ when :public
84
+ self
85
+ when :private
86
+ Key.new([Key.point(key), chain_code], :public, depth, index, parent_fingerprint)
87
+ end
88
+ end
89
+
90
+ alias_method :public_key_from_private, :public_key
91
+
92
+ def public?
93
+ public_or_private == :public
94
+ end
95
+
96
+ def private?
97
+ public_or_private == :private
98
+ end
99
+
100
+
101
+ # @abstract serialize key according to BIP32 specifications
102
+ #
103
+ # The serialization result is a string in Base58 that represents the public
104
+ # or the private extented key.
105
+ #
106
+ # @note
107
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format
108
+ def serialize_extended_key
109
+ k, c = key, chain_code
110
+
111
+ case public_or_private
112
+ when :private
113
+ data = ser32(0x0488ADE4) + to_binary(depth) + parent_fingerprint + ser32(index) + c + "\x00" + k
114
+ when :public then
115
+ data = ser32(0x0488B21E) + to_binary(depth) + parent_fingerprint + ser32(index) + c + Key.serp(public_key.key)
116
+ else raise "invalid property #{public_or_private}"
117
+ end
118
+
119
+ Base58.binary_to_base58(data + hash256(data)[0..3], :bitcoin)
120
+ end
121
+
122
+ alias_method :serialize, :serialize_extended_key
123
+
124
+ # @abstract Derive key by specifying derivation path
125
+ #
126
+ # Derive a private or a public key by applying multiple derivation definined
127
+ # by the derivation path.
128
+ #
129
+ # The {#path} is an Array of multiple indexes. For example to derive a key
130
+ # like m/0H/1/2H/2, use indexes [0.h, 1, 2.h, 2].
131
+ #
132
+ # @return [HDetEc] The returned object is an instance of the new derivated
133
+ # key.
134
+ #
135
+ # @note
136
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#the-key-tree
137
+ def derive(path)
138
+ return self if path.nil? || path.empty?
139
+
140
+ case public_or_private
141
+ when :public then public_key_derivation(path)
142
+ when :private then private_key_derivation(path)
143
+ else
144
+ raise "Wrong key"
145
+ end
146
+ end
147
+
148
+ # @abstract Compute the key fingerprint
149
+ #
150
+ # @note
151
+ # https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#key-identifiers
152
+ def fingerprint
153
+ key_fingerprint([key, chain_code])
154
+ end
155
+
156
+ def public_key_derivation(path)
157
+ ex_k = [key, chain_code]
158
+
159
+ ex_k_parent = ex_k
160
+ path.each do |i|
161
+ raise "No hardened derivation for public key" if i >= 2**31
162
+ ex_k_parent = ex_k
163
+ ex_k = HDetEc::Key.CKDpub(ex_k_parent, i)
164
+ end
165
+
166
+ HDetEc::Key.new(ex_k, :public, path.size, path.last, key_fingerprint(ex_k_parent))
167
+ end
168
+
169
+ private :public_key_derivation
170
+
171
+ def private_key_derivation(path)
172
+ ex_k = [key, chain_code]
173
+
174
+ ex_k_parent = ex_k
175
+ path.each do |i|
176
+ ex_k_parent = ex_k
177
+ ex_k = HDetEc::Key.CKDpriv(ex_k_parent, i)
178
+ end
179
+
180
+ HDetEc::Key.new(ex_k, :private, path.size, path.last, key_fingerprint(ex_k_parent))
181
+ end
182
+
183
+ private :private_key_derivation
184
+
185
+ def key_fingerprint(ex_k)
186
+ k, c = ex_k
187
+ case public_or_private
188
+ when :private then hash160(Key::point(k))[0..3]
189
+ when :public then hash160(k)[0..3]
190
+ end
191
+ end
192
+
193
+ private :key_fingerprint
194
+
195
+ end
196
+
197
+
198
+ end
@@ -0,0 +1,39 @@
1
+ require 'hdet-ec-key'
2
+
3
+ require_relative 'test_vectors.rb'
4
+
5
+ shared_examples "public and private key derivation" do |vector|
6
+ m = HDetEc::Key.generate_master_key([vector[:seed]].pack("H*"))
7
+ vector[:data].each do |data|
8
+
9
+ it data[:chain] + " private key" do
10
+ expect(m.derive(data[:path]).serialize).to eq(data[:priv_key])
11
+ end
12
+
13
+ it data[:chain] + " public key" do
14
+ expect(m.derive(data[:path]).public_key.serialize).to eq(data[:pub_key])
15
+ end
16
+
17
+ if data[:path].all? {|i| i < 2**31 }
18
+ it data[:chain] + " public key derivation" do
19
+ expect(m.public_key.derive(data[:path]).serialize).to eq(data[:pub_key])
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ describe "derivation key seed = 000102030405060708090a0b0c0d0e0f" do
27
+ vector = test_vector[0]
28
+ include_examples("public and private key derivation", vector)
29
+ end
30
+
31
+ describe "derivation key seed = fffcf9f6f3f0edeae7e4e1...484542" do
32
+ vector = test_vector[1]
33
+ include_examples("public and private key derivation", vector)
34
+ end
35
+
36
+ describe "derivation key seed = 4b381541583be4...3235be" do
37
+ vector = test_vector[2]
38
+ include_examples("public and private key derivation", vector)
39
+ end
@@ -0,0 +1,78 @@
1
+ require 'hdet-ec-key/data_manipulation'
2
+ require 'openssl'
3
+
4
+ describe HDetEc::DataManipulation do
5
+ let(:dm) {Class.new {include HDetEc::DataManipulation}.new }
6
+
7
+ context "#ser32" do
8
+ it "serialize 4 bytes length" do
9
+ expect(dm.ser32(0)).to eq("\x00\x00\x00\x00".b)
10
+ end
11
+
12
+ it "select least significant bytes when overflow" do
13
+ expect(dm.ser32(2**32+2)).to eq("\x00\x00\x00\x02".b)
14
+ end
15
+
16
+ it "serialize in big-endian" do
17
+ expect(dm.ser32(0xabcd)).to eq("\x00\x00\xab\xcd".b)
18
+ expect(dm.ser32(0xabcd00ef)).to eq("\xab\xcd\x00\xef".b)
19
+ end
20
+ end
21
+
22
+ context "#to_binary" do
23
+ it "autodetect bytes length" do
24
+ i2_38_33 = dm.to_binary(2**39 + 2**33)
25
+ expect(i2_38_33.size).to be 5
26
+ expect(i2_38_33).to eq("\x82\x00\x00\x00\x00".b)
27
+
28
+ i2_15 = dm.to_binary(2**15)
29
+ expect(i2_15.size).to be 2
30
+ expect(i2_15).to eq("\x80\x00".b)
31
+ end
32
+
33
+ it "left pad with 0" do
34
+ i2_33 = dm.to_binary(2**33)
35
+ expect(i2_33).to eq("\x02\x00\x00\x00\x00".b)
36
+
37
+ i2_127 = dm.to_binary(2**128)
38
+ expect(i2_127).to eq("\x01".b + "\x00".b * 16)
39
+ end
40
+
41
+ it "serialize in big-endian" do
42
+ expect(dm.to_binary(0x0123abcdef)).to eq("\x01\x23\xab\xcd\xef".b)
43
+ expect(dm.to_binary(0xabcd)).to eq("\xab\xcd".b)
44
+ end
45
+
46
+ it "could specify serialize byte size" do
47
+ expect(dm.to_binary(0, 8).size).to eq 8
48
+ expect(dm.to_binary(2**128-1, 2).size).to eq 16
49
+ end
50
+ end
51
+
52
+ context "#to_number" do
53
+ it "could convert number" do
54
+ expect(dm.to_number("\x01".b + "\x00".b * 16)).to eq(2**128)
55
+ end
56
+
57
+ it "deserialize in big-endian" do
58
+ expect(dm.to_number("\x01\x23\xab\xcd\xef")).to eq(0x0123abcdef)
59
+ expect(dm.to_number("\xab\xcd")).to eq(0xabcd)
60
+ end
61
+ end
62
+
63
+ context "#ser256" do
64
+ it "always 256 bits long (32 bytes)" do
65
+ expect(dm.ser256(1).size).to eq(32)
66
+ expect(dm.ser256(73927273964184847435527778358743529074551849469743120849858998083583072341153).size).to eq(32)
67
+ end
68
+
69
+ it "raise ArgumentError exception when overflow" do
70
+ expect{dm.ser256(2**256)}.to raise_error(ArgumentError)
71
+ end
72
+
73
+ it "serialize in big-endian" do
74
+ expect(dm.ser256(0x0123abcdef)).to eq( "\x00".b * 27 + "\x01\x23\xab\xcd\xef".b)
75
+ end
76
+ end
77
+
78
+ end
data/spec/key_spec.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'hdet-ec-key'
2
+
3
+ describe HDetEc::Key do
4
+ context "#import" do
5
+ it "can import private key from BIP32 string" do
6
+ bip32_key = "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"
7
+ key = HDetEc::Key.import(bip32_key)
8
+
9
+ expect(key.serialize).to eq(bip32_key)
10
+ end
11
+
12
+ it "can import public key from BIP32 string" do
13
+ bip32_key = "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"
14
+ key = HDetEc::Key.import(bip32_key)
15
+
16
+ expect(key.serialize).to eq(bip32_key)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,82 @@
1
+ def test_vector
2
+ [{
3
+ seed: "000102030405060708090a0b0c0d0e0f",
4
+ data: [{
5
+ chain: "Chain m",
6
+ path: [],
7
+ pub_key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
8
+ priv_key: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
9
+ }, {
10
+ chain: "Chain m/0h",
11
+ path: [0.h],
12
+ pub_key: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
13
+ priv_key: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
14
+ },{
15
+ chain: "Chain m/0h/1",
16
+ path: [0.h, 1],
17
+ pub_key: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
18
+ priv_key: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
19
+ }, {
20
+ chain: "Chain m/0h/1/2h",
21
+ path: [0.h, 1, 2.h],
22
+ pub_key: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
23
+ priv_key: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
24
+ }, {
25
+ chain: "Chain m/0h/1/2h/2",
26
+ path: [0.h, 1, 2.h, 2],
27
+ pub_key: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
28
+ priv_key: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
29
+ }, {
30
+ chain: "Chain m/0h/1/2h/2/1000000000",
31
+ path: [0.h, 1, 2.h, 2, 1000000000],
32
+ pub_key: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",
33
+ priv_key: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
34
+ }]
35
+ }, {
36
+ seed: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
37
+ data: [{
38
+ chain: "Chain m",
39
+ path: [],
40
+ pub_key: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
41
+ priv_key: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
42
+ }, {
43
+ chain: "Chain m/0",
44
+ path: [0],
45
+ pub_key: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
46
+ priv_key: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
47
+ }, {
48
+ chain: "Chain m/0/2147483647h",
49
+ path: [0, 2147483647.h],
50
+ pub_key: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",
51
+ priv_key: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
52
+ }, {
53
+ chain: "Chain m/0/2147483647h/1",
54
+ path: [0, 2147483647.h, 1],
55
+ pub_key: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",
56
+ priv_key: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
57
+ }, {
58
+ chain: "Chain m/0/2147483647h/1/2147483646h",
59
+ path: [0, 2147483647.h, 1, 2147483646.h],
60
+ pub_key: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
61
+ priv_key: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
62
+ }, {
63
+ chain: "Chain m/0/2147483647h/1/2147483646h/2",
64
+ path: [0, 2147483647.h, 1, 2147483646.h, 2],
65
+ pub_key: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",
66
+ priv_key: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
67
+ }]
68
+ }, {
69
+ seed: "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be",
70
+ data: [{
71
+ chain: "Chain m",
72
+ path: [],
73
+ pub_key: "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13",
74
+ priv_key: "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
75
+ }, {
76
+ chain: "Chain m/0h",
77
+ path: [0.h],
78
+ pub_key: "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y",
79
+ priv_key: "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
80
+ }]
81
+ }]
82
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hdet-ec-key
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Stéphane Clérambault
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base58
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: |-
42
+ Provide an easy implementation of the BIP32
43
+ specification. This implementation is not related to
44
+ bitcoin and can be use for any propose.
45
+ email:
46
+ - stephane@clerambault.fr
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files:
50
+ - README.adoc
51
+ files:
52
+ - LICENSE
53
+ - README.adoc
54
+ - VERSION
55
+ - lib/hdet-ec-key.rb
56
+ - lib/hdet-ec-key/data_manipulation.rb
57
+ - lib/hdet-ec-key/ec_manipulation.rb
58
+ - lib/hdet-ec-key/key.rb
59
+ - spec/bip32_vector_spec.rb
60
+ - spec/data_manipulation_spec.rb
61
+ - spec/key_spec.rb
62
+ - spec/test_vectors.rb
63
+ homepage: https://gitlab.com/elionne/hdet-ec-key
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.5.0
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.7.6
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Implements Herarchical Deterministic Key wallet.
87
+ test_files: []