hdet-ec-key 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.adoc +88 -0
- data/VERSION +1 -0
- data/lib/hdet-ec-key.rb +18 -0
- data/lib/hdet-ec-key/data_manipulation.rb +60 -0
- data/lib/hdet-ec-key/ec_manipulation.rb +133 -0
- data/lib/hdet-ec-key/key.rb +198 -0
- data/spec/bip32_vector_spec.rb +39 -0
- data/spec/data_manipulation_spec.rb +78 -0
- data/spec/key_spec.rb +19 -0
- data/spec/test_vectors.rb +82 -0
- metadata +87 -0
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
|
data/lib/hdet-ec-key.rb
ADDED
|
@@ -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: []
|