money-tree-openssl 1.1

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: a68f4b7caaea388fb6e6627224aae66f94ff7d4071bc9e46cf07a08ae2eb4ad6
4
+ data.tar.gz: 3860531c005b0674818e7fbe305ed6831546b896234683d2e0f7226f64dcfac8
5
+ SHA512:
6
+ metadata.gz: 53d0354dae66e31d5311369152cfe2568c8f20646e072f0a69b006f15e4ddfa6c4766e1aa4f0c6a2e4dc7d458ba8e40e0e17ad1cdb885491bee3bce0451c3d79
7
+ data.tar.gz: bc26829bf8ee4171e7e042304223ae5eb5800fc4c04cafb50e64da0e8cca1b4da457bd713f0d1d2b2202f1fb6adeaf528d7a8eab6e688d5093d17622407f14fe
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format documentation
data/.simplecov ADDED
@@ -0,0 +1,7 @@
1
+ require 'coveralls'
2
+
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ])
7
+ SimpleCov.start
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in money-tree.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Micah Winkelspecht
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,258 @@
1
+ [![Build Status](https://travis-ci.org/GemHQ/money-tree.png)](https://travis-ci.org/GemHQ/money-tree) [![Coverage Status](https://img.shields.io/coveralls/GemHQ/money-tree.svg)](https://coveralls.io/r/GemHQ/money-tree?branch=master) [![Code Climate](https://codeclimate.com/github/GemHQ/money-tree.png)](https://codeclimate.com/github/GemHQ/money-tree) [![Gem Version](https://badge.fury.io/rb/money-tree.png)](http://badge.fury.io/rb/money-tree)
2
+ # MoneyTree
3
+ ### RSpec tested. Big Brother removed.
4
+
5
+ MoneyTree is a Ruby implementation of Bitcoin Wallets. Specifically, it supports [Hierachical Deterministic wallets](https://en.bitcoin.it/wiki/Deterministic_Wallet) according to the protocol specified in [BIP0032](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki).
6
+
7
+ ___
8
+ If you find this helpful, please consider a small Bitcoin donation to 1nj2kie1hATcFbAaD7dEY53QaxNgt4KBp
9
+
10
+ ![Donate BTC](https://raw.github.com/wink/money-tree/master/donation_btc_qr_code.gif)
11
+ ___
12
+
13
+ ## Why would I want an HD Wallet?
14
+ Hierarchical Deterministic (HD) Bitcoin Wallets offer several advantages over traditional Bitcoin wallets.
15
+
16
+ One of the problems with traditional Bitcoin wallets is that the wallet may hold a whole bunch of keypairs, each with Bitcoins attached to them. When you want to back up your wallet, you backup all of the current keys that you control in that wallet. However, if you later generate a new key, you need to make a brand new back up of your wallet. In fact, you need to back up your wallet every time you generate a new key.
17
+
18
+ ### Easy backups
19
+
20
+ HD wallets allow you to create a huge number of Bitcoin keys (keypairs) that all derive from a parent master key. This means that if you control the master key, you can generate the entire tree of children keys. So instead of needing to make repeated backups of your wallet, you can create a single backup when you create the wallet, and from then on to the end of time, you will never need to make a new backup, because you can just recreate ALL of the child keys from your master key.
21
+
22
+ ### Safely store your private keys offline
23
+ Additionally, HD wallets introduce cool new features to wallets, like being able to derive the entire tree of public keys from a parent public key without needing ANY private keys. For instance, let's say you have your master private key backed up on a paper wallet and stored offline in a safe somewhere, but you have the master public key available. Using just this public key, you can generate an entire tree of receive-only child public keys.
24
+
25
+ For instance, let's say you wanted to open a Bitcoin ecommerce website. With HD wallets, you can keep your master private key offline, and only put your public key onto the public webserver. Your website can then use that key to generate a receiving address for each and every product on your site, a unique address for each one of your customers, or even a key unique to each customer/product combo. (The uses are left up to your imagination.) And since the private key is stored offline, nobody will ever be able to hack your site and steal your Bitcoins.
26
+
27
+ ### Access controls
28
+ One bonus feature of HD Wallets is that they give you a lot of control over who in your organization has access to which keys. Like an organizational chart for a business, HD wallets are arranged in a tree formation. You could create whole branches of keypairs for each department in your organization, and by giving each department only the private key at the top of their department branch, each department will only be able to spend the coins on their branch. However, since you hold the master key, you can watch and spend ALL coins in the entire tree.
29
+
30
+ ### Accounting
31
+ Want to give your accountant access to view all transactions, but you don't want to give her access to spend any of your coins? No problem. You can simply give her the public key at any level in the tree that you desire, and she will be able to view transactions below that key in the tree, but won't be able to spend any of the coins.
32
+
33
+ ## Where can I learn more?
34
+ - [A quick primer on deterministic wallets](https://en.bitcoin.it/wiki/Deterministic_wallet)
35
+ - [The official HD Wallet spec on the Bitcoin wiki](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
36
+ - [An awesome talk by Pieter Wuille at Bitcoin 2013 Conference](http://youtu.be/cfkCs4NdNss)
37
+
38
+ ## Installation
39
+
40
+ Add this line to your application's Gemfile:
41
+
42
+ gem 'money-tree'
43
+
44
+ And then execute:
45
+
46
+ $ bundle
47
+
48
+ Or install it yourself as:
49
+
50
+ $ gem install money-tree
51
+
52
+ ## Prerequisites
53
+ MoneyTree will only work with Ruby 2.0.0 and greater. This is because the version of OpenSSL included with previous versions of Ruby did not include an OpenSSL::PKey::EC::Point#mul (point multiplication) method, which is required in order to calculate a Bitcoin public key from a private key.
54
+
55
+ If you have a serious problem with this and REALLY need it to work on previous versions of Ruby, bring it up in the Issues section on Github, and I'll try to get to it. Or better yet, submit a pull request with matching spec! (Hint: you'll need to use FFI and the OpenSSL c library directly)
56
+
57
+ ## Usage
58
+
59
+ These instructions assume you have a decent understanding of how Bitcoin wallets operate and a cursory knowledge of how a [Hierarchical Deterministic Bitcoin Wallet (HD Wallet)](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) works.
60
+
61
+ ### Create a Master Node (seed)
62
+
63
+ To create a new HD Wallet, we're going to create a tree structure of private/public keypairs (nodes). You'll first want to start with a master node. This master node should be seeded with at least 16 random bytes but preferably 32 random bytes from a cryptographically secure PRNG (pseudo-random number generator).
64
+
65
+ DO NOT use a user generated password. Keep in mind that whoever controls the seed controls ALL coins in the entire tree, so it should not be left up to a human brain, because humans tend to follow patterns and patterns are subject to brute force attacks. Luckily, MoneyTree includes the seed generation by default so you don't need to create this on your own.
66
+
67
+ ```ruby
68
+ # Create a new master node (with automatic seed generation)
69
+ @master = MoneyTree::Master.new
70
+ => MoneyTree::Master instance
71
+
72
+ @master.seed
73
+ => "N\xC5\x9DD\xAA\xCC\x80a\a\x96%8\xC8\x86\x81\x90\t\x82&\xE4\x97Ay\xECs\xD8\xB1M\xEA\xE6|\xEF"
74
+
75
+ # Or import an existing seed
76
+ @master = MoneyTree::Master.new seed_hex: "000102030405060708090a0b0c0d0e0f"
77
+ => MoneyTree::Master instance
78
+
79
+ @master.seed
80
+ => "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0E\x0F"
81
+ ```
82
+
83
+ ### Get info from a Node
84
+ `MoneyTree::Master` inherits from `MoneyTree::Node`, and you can do a lot of fun stuff with a node.
85
+
86
+ ```ruby
87
+ # Here are some things you can do with a node.
88
+ @master.index # The index is a sequential identifier in relation to its parent node. (i.e. the nth child of its parent)
89
+ => 0
90
+
91
+ @master.depth # How many steps down the tree this node is. (The master node is at depth 0, its direct child is at depth 1, and so on...)
92
+ => 0
93
+
94
+ @master.to_identifier
95
+ => "3442193e1bb70916e914552172cd4e2dbc9df811"
96
+
97
+ @master.to_fingerprint
98
+ => "3442193e"
99
+
100
+ @master.to_address
101
+ => "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"
102
+
103
+ @master.private_key.to_hex
104
+ => "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"
105
+
106
+ @master.private_key.to_wif
107
+ => "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW"
108
+
109
+ @master.public_key.to_hex
110
+ => "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"
111
+
112
+ @master.chain_code_hex
113
+ => "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508" # Look up chain codes in the BIP0032 spec
114
+
115
+ @master.to_serialized_hex(:private)
116
+ => "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"
117
+
118
+ @master.to_bip32(:private)
119
+ => "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
120
+
121
+ @master.to_serialized_hex
122
+ => "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"
123
+
124
+ @master.to_bip32
125
+ => "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
126
+ ```
127
+
128
+ ### Generate a child Node
129
+ In HD Wallets, we refer to children nodes by their path in relation to the master node. This is determined using a slash-delimited string where each part of the delimited string represents a node in increasing depth. For instance, the path "m/0/3" walks down the tree starting with "m" the master key. The first part "m" represents the master key at depth 0. The next part "0" (i=0) represents the first child (sequentially) of "m" (depth 1). The last part "3" (i=3) represents the fourth child node of the previous node (depth 2), and so on down the line. You can create as many depths of nodes as you like.
130
+
131
+ To generate a child node from a given path:
132
+
133
+ ```ruby
134
+ @node = @master.node_for_path "m/0/3"
135
+ => MoneyTree::Node instance
136
+
137
+ @node.index
138
+ => 3
139
+
140
+ @node.depth
141
+ => 2
142
+
143
+ @node.to_bip32(:private)
144
+ => "xprv9ww7sMFLzJN15m7zX5JEBXQrQq8h4fU8PVqd929Hjy3xNSMzeBf163idMNBSq47DdCakyZTK7KcC2nbz3jqUkpJj8ZR4FqrijcFcFmcoBAe"
145
+
146
+ @node.to_bip32
147
+ => "xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr"
148
+ ```
149
+
150
+ #### Chain codes
151
+ In HD wallets, chain codes are the mathematical glue that binds a parent node to its child node. We use chain codes in order to create a mathematical relationship between a parent and its child. You don't necessarily need to understand how chain codes work because this library abstracts it for you, but you do at least need to know that for any given node, if you'd like to calculate its child node, you'll need three pieces of information. The parent node's key (either private or public), the sequential index value of i for the child and the parent node's chain code.
152
+
153
+ You don't need to worry about chain codes if you are creating or importing from a Master key (it's always the same for all HD wallet master keys), however if you are trying to import a derived child key at some lower depth in the tree, you'll need the chain code. Luckily, whenever we export a node to a wallet file, we encode it in a special format that includes all of the relevant info (including chain code) that we need to reconstruct the node in a single convenient serialized address.
154
+
155
+ #### Serialized Addresses
156
+ Because we need multiple pieces of info to reconstruct nodes in a tree, when we're dealing with HD wallets, we pass around a serialized address format that encodes both the key and the chain code. It looks like this:
157
+
158
+ ```ruby
159
+ # private key
160
+ "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
161
+
162
+ # public key
163
+ "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
164
+ ```
165
+
166
+ In addition to the key and the chain code, this encoding also includes info about the depth and index of the key, along with a fingerprint of its parent key (which I presume is for quickly sorting a big pile of keys into a tree).
167
+
168
+ These are the addresses that you should use to represent each node in the tree structure, however these are NOT the bitcoin addresses you should pass around for receiving money. These are more for storing inside a wallet file so that you can reconstruct the tree.
169
+
170
+ To export a node to a serialized address, you can do:
171
+
172
+ ```ruby
173
+ @node.to_bip32(:private) # for private keys
174
+ => "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
175
+
176
+ @node.to_bip32
177
+ => "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
178
+ ```
179
+
180
+ To import from a serialized address: (either public or private)
181
+ ```ruby
182
+ @node = MoneyTree::Node.from_bip32 "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
183
+ => MoneyTree::Node instance
184
+ ```
185
+
186
+ #### Private derivation vs public derivation
187
+ You'll recall that HD Wallets allow us to generate an entire tree of private/public keypairs with a single parent private key. When we wish to generate child keypairs (that is, we want both the child private key and the child public key), we MUST have access to the parent private key. Using what's called "private derivation", we take the parent private key and its associated chain code along with a given index value (i.e. 0 = 1st child, 1 = 2nd child, (i-1) = ith child...), and we cryptomash™ them together to form a child private key and a new child chain code. This child key can then be used to generate its associated public key (in the same way we normally create Bitcoin public keys from private keys). The new chain code can be used to derive children of this child key and this process can keep repeating itself down the tree.
188
+
189
+ However, an added benefit of HD Wallets is that with JUST a public key, we can generate ALL public keys below that key. But how do we do this, since we don't have any private keys? We usually just put our private key in the Cryptomatic 2000 and out comes a public key. We accomplish this by using a second type of derivation called "public derivation". Using the power of a lot of math and elliptic curve formulae that look like it's straight out of _Good Will Hunting_, we can calculate the child public key directly from a parent public key. However, we cannot calculate the child private key. Therefore, if you only have a public key, you will only be able to derive other public keys. (That's a feature, not a bug.)
190
+
191
+ #### Export a public-key only node
192
+ Sometimes you don't want a node to contain any private key information. You can request that MoneyTree return you a node that is stripped of its private information by using a special notation in the path.
193
+
194
+ Either using a capital "M" instead of a lowercase "m", or by appending ".pub" to the end of the path, you will receive a node that is stripped of its private key.
195
+
196
+ For example:
197
+
198
+ ```ruby
199
+ @node = @master.node_for_path("M/0/3") # or "m/0/3.pub" or "M/0/3.pub"...these are equivalent
200
+
201
+ @node.to_bip32
202
+ => "xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr"
203
+
204
+ @node.to_bip32(:private)
205
+ -> raises MoneyTree::Node::PrivatePublicMismatch error
206
+ ```
207
+
208
+ #### Import a public-key only node
209
+ You can also import a node using only a public key. Keep in mind that this node will only be able to generate other public-key only nodes. You will not be able to derive child private keys using this node.
210
+
211
+ ```ruby
212
+ @node = MoneyTree::Node.from_bip32("xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr")
213
+ => MoneyTree::Node instance
214
+
215
+ @node.to_bip32
216
+ => "xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr"
217
+
218
+ @node.to_bip32(:private)
219
+ -> raises MoneyTree::Node::PrivatePublicMismatch error
220
+ ```
221
+
222
+ #### Values of i and i-prime
223
+ Earlier we discussed the use of an index value `i` to represent the sequential ordinal index of a child in relation to its parent. That is, `i=0` would be the first child, `i=1` would be the second child, and so forth up to `(i-1)` as the i'th child. This index value is very important in the child key derivation process because it allows us to create a whole bunch of subnodes (child keys) for a given node, just by incrementing the i value. In fact, i is a 32-bit unsigned integer which gives us the ability to create up to 4,294,967,296 addresses for one node.
224
+
225
+ When choosing `i` values, the BIP0032 spec calls for using a reserved set of `i` values to denote a node that should use private derivation and another reserved set of `i` values to denote public derivation.
226
+
227
+ The way this breaks down is:
228
+
229
+ 0 through 2,147,483,647 (i < 0x80000000) should use public derivation
230
+
231
+ 2,147,483,648 through 4,294,967,295 (i >= 0x80000000) should use private derivation
232
+
233
+ Yikes, that's a lot of detail to remember. Luckily, there is a simple notation in the path strings that allow us to easily tell the difference between a node that uses public derivation and a node that uses private derivation. We use either " ' " (prime symbol) or "p" at the end of a node path part to denote private derivation.
234
+
235
+ For instance:
236
+ `"m/0'/1"` and `"m/0p/1"` are equivalent and they translate to the "the second publicly derived child `1` of the first privately derived child `0 prime` of the master key `m`". But since we know that the "first privately derived child" is in the `i` value range above `0x80000000`, the very first possible `i` value in this range is actually `0x80000000`, or 2,147,483,648. Therefore when we say `"m/0p/1"`, what we really mean is `"m/2147483648/1"`. Thus:
237
+
238
+ ```ruby
239
+ "m/0'/1" == "m/0p/1" == "m/2147483648/1"
240
+ ```
241
+
242
+ They are all equivalent ways of saying the same thing, but the first two are just a more human readable shorthand notation. You are free to use whichever notation you prefer. This gem will parse it.
243
+
244
+ To add just a little more confusion to the mix, some wallets will use negative `i` values to also denote private derivation. Any `i` value that is negative will be processed using private derivation by this library. e.g. `"m/-1/1"`. (NOTE: known issue, see below)
245
+
246
+
247
+ ## Contributing
248
+
249
+ 1. Fork it
250
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
251
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
252
+ 4. Push to the branch (`git push origin my-new-feature`)
253
+ 5. Create new Pull Request
254
+
255
+ ## Known Issues (PRs welcome)
256
+
257
+ - Segwit ([BIP49](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki)) address generation is not supported
258
+ - Use of negative integers in paths does _not_ produce the correct hardened derivation.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEHDCCAoSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFtYXR0
3
+ L0RDPWdlbS9EQz1jbzAeFw0xODA1MzAyMTQwNDZaFw0xOTA1MzAyMTQwNDZaMBwx
4
+ GjAYBgNVBAMMEW1hdHQvREM9Z2VtL0RDPWNvMIIBojANBgkqhkiG9w0BAQEFAAOC
5
+ AY8AMIIBigKCAYEAxfbjMHFlxA2P+4YWPagKoGAMi4078imgXdFbD3Rloe6cGfYp
6
+ IMUQitiHrKi6fhSE0UjXmoP3qnYFddm1enN9zUAFRhHWv7xpINqSqss4PYAb5Anl
7
+ RYZu3jromop5aVodi15HUfu5z27MvBm4rAaN/dDRfh/rT2hDbTTh0HmvEaPUDfX6
8
+ TyflAttfabFvtY4qsD+ao8tks0DytqyuEWZ0tvQ6upOgHRNNuYDwDZB1T9v2dq2w
9
+ 3goJFmOKBMMn7UH8WMjD3HiOuRD4tWhq5xWLjBqjzFlVPlZPgdCNyXeMMnLXER98
10
+ NY35cVWFFuqG+kZwy4MFKdE9WFTocLZxLFo0VVTNSpPara9HirbHtIo9jZNuop4S
11
+ g4JTf1F8dIWYii3sXoAYZfkl6rHVRP0G/OV5LcTfSS3QkmI5hNltz5FZzc+qI6S1
12
+ rTR1ZwTy1rRI3coFY7vDRaFWBoMbbo/DytgCE3+rfbVDxQrJa4aZ0iYDhu8LXEA1
13
+ VTtpf1EWYCOsYE1TAgMBAAGjaTBnMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0G
14
+ A1UdDgQWBBQ6QoDNre7LFgOukH2Cv+RqZyfUzjAWBgNVHREEDzANgQttYXR0QGdl
15
+ bS5jbzAWBgNVHRIEDzANgQttYXR0QGdlbS5jbzANBgkqhkiG9w0BAQsFAAOCAYEA
16
+ kOxYnOsB+NwHwLc2lHEZ8ubxanq2qIZDhvVQ4M31gwmba43xO7vq0ktFxYRvozs4
17
+ 74dQ6bmY2e7njoFgeutyJwxulA+BC71mDQA1s4WsZo7Z2TRgB0GViVqHrzq+jY+M
18
+ p9mTHQqKH+2j0P9T4DXSzq4qOaBA3YROAwAzYI9N8MObeWkRt2pZ4zYQrAniP2nd
19
+ wzXs/G5lWbbntVcvQOfAAXBipSJ3X5P2EGpUytP9ZpGdezY5HZzuiJFcmCf1CM3t
20
+ VX4NZjbJak9gOY0AFD0Aw497sYenm0VBExclOmeRuZLffpWteTTL//utpG3bbFPl
21
+ jQ78uzsrexYTYW5IshjfSIf3TZxm50Z45pyOTow5EOP1Nd7OmKOcI8hrLGv5+AlD
22
+ hCnomUTUNsM4Rjwl5rzQiIn3ezv6+0tlg4rWJmVTuOGwcHk/oj1In2sPjCqm0pgx
23
+ TLnMa8gr6aUpuHR5s2N4ZH0Q2YIsaD6cv7DYXt+G4MRut3njOYHfkqsSVykO6hvr
24
+ -----END CERTIFICATE-----
@@ -0,0 +1 @@
1
+ ce3c7dc0fe6817aee65f990c7a97f89fd36c94380ac804c7579554d665a934df99d4e72ee9b2e467efcc76799a20c4d1de4c950bbba3512d42260c38a46e54b9
Binary file
@@ -0,0 +1,16 @@
1
+ module MoneyTree
2
+ class Address
3
+ attr_reader :private_key, :public_key
4
+
5
+ def initialize(opts = {})
6
+ private_key = opts.delete(:private_key)
7
+ @private_key = MoneyTree::PrivateKey.new({ key: private_key }.merge(opts))
8
+ @public_key = MoneyTree::PublicKey.new(@private_key, opts)
9
+ end
10
+
11
+ def to_s(network: :bitcoin)
12
+ public_key.to_s(network: network)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,265 @@
1
+ # encoding ascii-8bit
2
+
3
+ require 'openssl'
4
+
5
+ module MoneyTree
6
+ class Key
7
+ include OpenSSL
8
+ include Support
9
+ extend Support
10
+ class KeyInvalid < StandardError; end
11
+ class KeyGenerationFailure < StandardError; end
12
+ class KeyImportFailure < StandardError; end
13
+ class KeyFormatNotFound < StandardError; end
14
+ class InvalidWIFFormat < StandardError; end
15
+ class InvalidBase64Format < StandardError; end
16
+
17
+ attr_reader :options, :key, :raw_key
18
+ attr_accessor :ec_key
19
+
20
+ GROUP_NAME = 'secp256k1'
21
+ ORDER = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".to_i(16)
22
+
23
+ def valid?(eckey = nil)
24
+ eckey ||= ec_key
25
+ eckey.nil? ? false : eckey.check_key
26
+ end
27
+
28
+ def to_bytes
29
+ hex_to_bytes to_hex
30
+ end
31
+
32
+ def to_i
33
+ bytes_to_int to_bytes
34
+ end
35
+ end
36
+
37
+ class PrivateKey < Key
38
+
39
+ def initialize(opts = {})
40
+ @options = opts
41
+ @ec_key = PKey::EC.new GROUP_NAME
42
+ if @options[:key]
43
+ @raw_key = @options[:key]
44
+ @key = parse_raw_key
45
+ import
46
+ else
47
+ generate
48
+ @key = to_hex
49
+ end
50
+ end
51
+
52
+ def generate
53
+ ec_key.generate_key
54
+ end
55
+
56
+ def import
57
+ ec_key.private_key = BN.new(key, 16)
58
+ set_public_key
59
+ end
60
+
61
+ def calculate_public_key(opts = {})
62
+ opts[:compressed] = true unless opts[:compressed] == false
63
+ group = ec_key.group
64
+ group.point_conversion_form = opts[:compressed] ? :compressed : :uncompressed
65
+ point = group.generator.mul ec_key.private_key
66
+ end
67
+
68
+ def set_public_key(opts = {})
69
+ ec_key.public_key = calculate_public_key(opts)
70
+ end
71
+
72
+ def parse_raw_key
73
+ result = if raw_key.is_a?(Integer) then from_integer
74
+ elsif hex_format? then from_hex
75
+ elsif base64_format? then from_base64
76
+ elsif compressed_wif_format? then from_wif
77
+ elsif uncompressed_wif_format? then from_wif
78
+ else
79
+ raise KeyFormatNotFound
80
+ end
81
+ result.downcase
82
+ end
83
+
84
+ def from_integer(bignum = raw_key)
85
+ # TODO: does this need a byte size specification?
86
+ int_to_hex(bignum)
87
+ end
88
+
89
+ def from_hex(hex = raw_key)
90
+ hex
91
+ end
92
+
93
+ def from_wif(wif = raw_key)
94
+ compressed = wif.length == 52
95
+ validate_wif(wif)
96
+ hex = decode_base58(wif)
97
+ last_char = compressed ? -11 : -9
98
+ hex.slice(2..last_char)
99
+ end
100
+
101
+ def from_base64(base64_key = raw_key)
102
+ raise InvalidBase64Format unless base64_format?(base64_key)
103
+ decode_base64(base64_key)
104
+ end
105
+
106
+ def compressed_wif_format?
107
+ wif_format?(:compressed)
108
+ end
109
+
110
+ def uncompressed_wif_format?
111
+ wif_format?(:uncompressed)
112
+ end
113
+
114
+ def wif_format?(compression)
115
+ length = compression == :compressed ? 52 : 51
116
+ wif_prefixes = MoneyTree::NETWORKS.map {|k, v| v["#{compression}_wif_chars".to_sym]}.flatten
117
+ raw_key.length == length && wif_prefixes.include?(raw_key.slice(0))
118
+ end
119
+
120
+ def base64_format?(base64_key = raw_key)
121
+ base64_key.length == 44 && base64_key =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
122
+ end
123
+
124
+ def hex_format?
125
+ raw_key.length == 64 && !raw_key[/\H/]
126
+ end
127
+
128
+ def to_hex
129
+ int_to_hex @ec_key.private_key, 64
130
+ end
131
+
132
+ def to_wif(compressed: true, network: :bitcoin)
133
+ source = NETWORKS[network][:privkey_version] + to_hex
134
+ source += NETWORKS[network][:privkey_compression_flag] if compressed
135
+ hash = sha256(source)
136
+ hash = sha256(hash)
137
+ checksum = hash.slice(0..7)
138
+ source_with_checksum = source + checksum
139
+ encode_base58(source_with_checksum)
140
+ end
141
+
142
+ def wif_valid?(wif)
143
+ hex = decode_base58(wif)
144
+ checksum = hex.chars.to_a.pop(8).join
145
+ source = hex.slice(0..-9)
146
+ hash = sha256(source)
147
+ hash = sha256(hash)
148
+ hash_checksum = hash.slice(0..7)
149
+ checksum == hash_checksum
150
+ end
151
+
152
+ def validate_wif(wif)
153
+ raise InvalidWIFFormat unless wif_valid?(wif)
154
+ end
155
+
156
+ def to_base64
157
+ encode_base64(to_hex)
158
+ end
159
+
160
+ def to_s(network: :bitcoin)
161
+ to_wif(network: network)
162
+ end
163
+
164
+ end
165
+
166
+ class PublicKey < Key
167
+ attr_reader :private_key, :point, :group, :key_int
168
+
169
+ def initialize(p_key, opts = {})
170
+ @options = opts
171
+ @options[:compressed] = true if @options[:compressed].nil?
172
+ if p_key.is_a?(PrivateKey)
173
+ @private_key = p_key
174
+ @point = @private_key.calculate_public_key(@options)
175
+ @group = @point.group
176
+ @key = @raw_key = to_hex
177
+ else
178
+ @raw_key = p_key
179
+ @group = PKey::EC::Group.new GROUP_NAME
180
+ @key = parse_raw_key
181
+ end
182
+
183
+ raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil?
184
+ end
185
+
186
+ def compression
187
+ @group.point_conversion_form
188
+ end
189
+
190
+ def compression=(compression_type = :compressed)
191
+ @group.point_conversion_form = compression_type
192
+ end
193
+
194
+ def compressed
195
+ compressed_key = self.class.new raw_key, options # deep clone
196
+ compressed_key.set_point to_i, compressed: true
197
+ compressed_key
198
+ end
199
+
200
+ def uncompressed
201
+ uncompressed_key = self.class.new raw_key, options # deep clone
202
+ uncompressed_key.set_point to_i, compressed: false
203
+ uncompressed_key
204
+ end
205
+
206
+ def set_point(int = to_i, opts = {})
207
+ opts = options.merge(opts)
208
+ opts[:compressed] = true if opts[:compressed].nil?
209
+ self.compression = opts[:compressed] ? :compressed : :uncompressed
210
+ bn = BN.new int_to_hex(int), 16
211
+ @point = PKey::EC::Point.new group, bn
212
+ raise KeyInvalid, 'point is not on the curve' unless @point.on_curve?
213
+ end
214
+
215
+ def parse_raw_key
216
+ result = if raw_key.is_a?(Integer)
217
+ set_point raw_key
218
+ elsif hex_format?
219
+ set_point hex_to_int(raw_key), compressed: false
220
+ elsif compressed_hex_format?
221
+ set_point hex_to_int(raw_key), compressed: true
222
+ else
223
+ raise KeyFormatNotFound
224
+ end
225
+ to_hex
226
+ end
227
+
228
+ def hex_format?
229
+ raw_key.length == 130 && !raw_key[/\H/]
230
+ end
231
+
232
+ def compressed_hex_format?
233
+ raw_key.length == 66 && !raw_key[/\H/]
234
+ end
235
+
236
+ def to_hex
237
+ int_to_hex to_i, 66
238
+ end
239
+
240
+ def to_i
241
+ point.to_bn.to_i
242
+ end
243
+
244
+ def to_ripemd160
245
+ hash = sha256 to_hex
246
+ ripemd160 hash
247
+ end
248
+
249
+ def to_address(network: :bitcoin)
250
+ hash = to_ripemd160
251
+ address = NETWORKS[network][:address_version] + hash
252
+ to_serialized_base58 address
253
+ end
254
+ alias :to_s :to_address
255
+
256
+ def to_fingerprint
257
+ hash = to_ripemd160
258
+ hash.slice(0..7)
259
+ end
260
+
261
+ def to_bytes
262
+ int_to_bytes to_i
263
+ end
264
+ end
265
+ end