crypto-lite 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +7 -0
- data/README.md +443 -119
- data/Rakefile +4 -0
- data/lib/crypto-lite.rb +98 -65
- data/lib/crypto-lite/config.rb +32 -0
- data/lib/crypto-lite/helper.rb +25 -0
- data/lib/crypto-lite/metal.rb +128 -0
- data/lib/crypto-lite/sign_rsa.rb +29 -0
- data/lib/crypto-lite/version.rb +1 -1
- data/test/test_base58.rb +36 -0
- data/test/test_bitcoin_addr.rb +58 -0
- data/test/test_hash.rb +24 -55
- data/test/test_hash_sha.rb +87 -0
- metadata +65 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e32db0712ee1ed53c0720f9dee44533ffb8482e3d9ac6f5bd7a7e6a5c716cdc8
|
4
|
+
data.tar.gz: c3e8da15486a23e26d72c97c96bfc4f4c1847c420e7bff833c0ed87efe61303c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6593de7e55dbaa353cef41df3cf25cf04fd17cef78f9e2c62fbdcf0bf234627e4ec0dc2f238a341d5de8fa8b3ae33045cb8cf468c80f5ea8a55f312ff7cc0a0
|
7
|
+
data.tar.gz: 2e54d60bd80f34568ff7a6852fc3da01d3066131d4c177c023c1c15b4497fe18af7ad28851668b9c45223cab4f7eb8089759fc9e51fac714820b4a7d3a509ef7
|
data/Manifest.txt
CHANGED
@@ -3,9 +3,16 @@ Manifest.txt
|
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
5
|
lib/crypto-lite.rb
|
6
|
+
lib/crypto-lite/config.rb
|
7
|
+
lib/crypto-lite/helper.rb
|
8
|
+
lib/crypto-lite/metal.rb
|
9
|
+
lib/crypto-lite/sign_rsa.rb
|
6
10
|
lib/crypto-lite/version.rb
|
7
11
|
lib/crypto.rb
|
8
12
|
lib/crypto/lite.rb
|
9
13
|
test/helper.rb
|
14
|
+
test/test_base58.rb
|
15
|
+
test/test_bitcoin_addr.rb
|
10
16
|
test/test_hash.rb
|
17
|
+
test/test_hash_sha.rb
|
11
18
|
test/test_version.rb
|
data/README.md
CHANGED
@@ -12,31 +12,49 @@
|
|
12
12
|
|
13
13
|
### Secure Hashing / Hash Functions
|
14
14
|
|
15
|
-
SHA256 - Secure Hash Algorithm (SHA) 256-
|
15
|
+
**SHA256 - Secure Hash Algorithm (SHA) 256-Bit (32 Bytes)**
|
16
16
|
|
17
17
|
|
18
18
|
``` ruby
|
19
19
|
require 'crypto' ## or use require 'crypto-lite'
|
20
20
|
|
21
21
|
## try abc
|
22
|
-
sha256( "abc" )
|
23
|
-
sha256( "abc".b )
|
24
|
-
sha256( "\x61\x62\x63" )
|
25
|
-
sha256( 0x616263 )
|
22
|
+
sha256( "abc" ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
23
|
+
sha256( "abc".b ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
24
|
+
sha256( "\x61\x62\x63" ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
25
|
+
sha256( 0x616263 ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
26
|
+
|
27
|
+
sha256( hex: '616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
28
|
+
sha256( hex: '0x616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
29
|
+
sha256( hex: '0X616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
30
|
+
|
31
|
+
# "auto-magic" hex string to binary string conversion heuristic
|
32
|
+
sha256( '0x616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
33
|
+
sha256( '0X616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
34
|
+
```
|
35
|
+
|
36
|
+
|
37
|
+
Bonus Back Stage Tip: How does SHA256 work?
|
38
|
+
|
39
|
+
Try this [amazing animation of the SHA256 hash function in your very own terminal](https://github.com/in3rsha/sha256-animation) by Greg Walker.
|
40
|
+
|
41
|
+
More of a code golfer? See [½ Kilo of SHA256](https://idiosyncratic-ruby.com/51-half-kilo-of-sha256.html) by Jan Lelis - yes, the SHA256 algorithm coded (from scratch) in 500 bytes of ruby.
|
26
42
|
|
27
|
-
pp sha256hex( '616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
28
|
-
pp sha256hex( '0x616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
29
|
-
pp sha256hex( '0X616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
30
43
|
|
44
|
+
Onwards with more sha256 examples:
|
31
45
|
|
46
|
+
``` ruby
|
32
47
|
## try a
|
33
48
|
sha256( "a" ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
34
49
|
sha256( "\x61" ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
35
50
|
sha256( 0b01100001 ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
36
51
|
sha256( 0x61 ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
37
52
|
|
38
|
-
|
39
|
-
|
53
|
+
sha256( hex: '61' ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
54
|
+
sha256( hex: '0x61' ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
55
|
+
|
56
|
+
# "auto-magic" hex string to binary string conversion heuristic
|
57
|
+
sha256( '0x61' ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
40
58
|
|
41
59
|
|
42
60
|
## try some more
|
@@ -44,125 +62,431 @@ sha256( "Hello, Cryptos!" ) #=> "33eedea60b0662c66c289ceba71863a864cf84b00e1000
|
|
44
62
|
```
|
45
63
|
|
46
64
|
|
65
|
+
**SHA3-256 - Secure Hashing Algorthim (SHA) 3, 256-Bit (32 Bytes)**
|
66
|
+
|
67
|
+
``` ruby
|
68
|
+
sha3_256( "Hello, Cryptos!" ) #=> "7dddf4bc9b86352b67e8823e5010ddbd2a90a854469e2517992ca7ca89e5bd58"
|
69
|
+
```
|
70
|
+
|
71
|
+
Note: Yes, SHA256 vs SHA3-256 / SHA-2 vs SHA-3 the hashing functions are
|
72
|
+
different (although the 256-bit hash size output is the same).
|
73
|
+
The sha256 hashing function is part of the Secure Hash Algorithm (SHA) 2 family / standards first published in 2001.
|
74
|
+
The sha3_256 is part of the (newer) Secure Hash Algorithm (SHA) 3 family / standards first published in 2015
|
75
|
+
(and uses the Keccak cryptographic primitive "under the hood").
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
**Keccak 256-Bit**
|
80
|
+
|
81
|
+
``` ruby
|
82
|
+
keccak256( "Hello, Cryptos!" ) #=> "2cf14baa817e931f5cc2dcb63c889619d6b7ae0794fc2223ebadf8e672c776f5"
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
#### Aside - Keccak vs SHA3 / Original vs Official
|
87
|
+
|
88
|
+
In 2004 the U.S. National Institute of Standards and Technology (NIST)
|
89
|
+
changed the padding to `SHA3-256(M) = KECCAK [512] (M || 01, 256)`.
|
90
|
+
This is different from the padding proposed by the Keccak team in
|
91
|
+
the original Keccak SHA-3 submission version 3 (the final, winning version).
|
92
|
+
The difference is the additional `'01'` bits appended to the message.
|
93
|
+
|
94
|
+
To help avoid confusion the "submitted original version 3" SHA-3 Keccak
|
95
|
+
hashing is now called "Keccak"
|
96
|
+
and the finalized NIST SHA-3 standard "SHA3".
|
97
|
+
|
98
|
+
Tip: If you don't know what variant of the hash function you have -
|
99
|
+
original or official? - check your hash:
|
100
|
+
|
101
|
+
For keccak 256-bit:
|
102
|
+
|
103
|
+
``` ruby
|
104
|
+
keccak256( '' ) #=> "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
|
105
|
+
```
|
106
|
+
|
107
|
+
For sha3 256-bit:
|
108
|
+
|
109
|
+
``` ruby
|
110
|
+
sha3_256( '' ) #=> "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"
|
111
|
+
```
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
**RMD / RIPE-MD - RACE¹ Integrity Primitives Evaluation Message Digest 160-Bit**
|
116
|
+
|
117
|
+
¹: Research and development in Advanced Communications technologies in Europe
|
118
|
+
|
119
|
+
|
120
|
+
``` ruby
|
121
|
+
rmd160( "Hello, Cryptos!" ) #=>"4d65f7b740bbade4097e1348e15d2a7d52ac5f53"
|
122
|
+
# or use the alias / alternate name
|
123
|
+
ripemd160( "Hello, Cryptos!" ) #=>"4d65f7b740bbade4097e1348e15d2a7d52ac5f53"
|
124
|
+
```
|
125
|
+
|
126
|
+
|
127
|
+
#### Aside - Hex String `"0x616263"` vs Binary String `"\x61\x62\x63" == "abc"`
|
128
|
+
|
129
|
+
Note: All hash functions operate on binary strings ("byte arrays")
|
130
|
+
and NOT hex strings.
|
131
|
+
|
132
|
+
Note: For hex strings the `0x` or `0X` prefix is optional.
|
133
|
+
Examples of hex strings:
|
134
|
+
|
135
|
+
``` ruby
|
136
|
+
# hex string binary string ("byte array")
|
137
|
+
"61" "\x61" == "a"
|
138
|
+
"0x61" "\x61" == "a"
|
139
|
+
|
140
|
+
"616263" "\x61\x62\x63" == "abc"
|
141
|
+
"0x616263" "\x61\x62\x63" == "abc"
|
142
|
+
"0X616263" "\x61\x62\x63" == "abc"
|
143
|
+
|
144
|
+
# or 160-bit hex string (hash)
|
145
|
+
"93ce48570b55c42c2af816aeaba06cfee1224fae"
|
146
|
+
"0x93ce48570b55c42c2af816aeaba06cfee1224fae"
|
147
|
+
|
148
|
+
# or 256-bit hex string (hash)
|
149
|
+
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
150
|
+
"0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
151
|
+
```
|
152
|
+
|
153
|
+
You can use `[str].pack( 'H*' )`
|
154
|
+
to convert a hex string into a binary string.
|
155
|
+
Note: The standard `Array#pack` conversion
|
156
|
+
will NOT "auto-magically" cut-off the `0x` or `0X` prefix.
|
157
|
+
|
158
|
+
|
159
|
+
If you know you have a hex string use the `hex:` keyword to pass
|
160
|
+
in the arg(ument)
|
161
|
+
to the hash function and that will "automagically"
|
162
|
+
handle the hex-to-bin conversion for you. Example:
|
163
|
+
|
164
|
+
``` ruby
|
165
|
+
sha256( hex: '61' ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
166
|
+
sha256( hex: '0x61' ) #=> "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
|
167
|
+
|
168
|
+
sha256( hex: '616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
169
|
+
sha256( hex: '0x616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
170
|
+
sha256( hex: '0X616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
171
|
+
```
|
172
|
+
|
173
|
+
What about the built-in "auto-magic" hex-to-bin conversion / heuristic?
|
174
|
+
|
175
|
+
Yes, if your passed in string starts with the
|
176
|
+
the `0x` or `0X` prefix the string gets "auto-magically" converted
|
177
|
+
to binary. Or if your passed in string is all hexadecimal characters,
|
178
|
+
that is, `0-9` and `a-f` and has a minimum length of ten characters.
|
179
|
+
Example:
|
180
|
+
|
181
|
+
|
182
|
+
``` ruby
|
183
|
+
# "auto-magic" hex string to binary string conversion heuristic
|
184
|
+
|
185
|
+
sha256( '0x616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
186
|
+
sha256( '0X616263' ) #=> "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
187
|
+
|
188
|
+
# or without 0x or 0X BUT with minimum heuristic length
|
189
|
+
hash160( '02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737' )
|
190
|
+
#=> "93ce48570b55c42c2af816aeaba06cfee1224fae"
|
191
|
+
|
192
|
+
hash256( '6fe6b145a3908a4d6616b13c1109717add8672c900' )
|
193
|
+
#=> "02335f08b8fe4ddad263a50b7a33c5d38ea1cbd8fd2056a1320a3ddece541711"
|
194
|
+
|
195
|
+
# and so on
|
196
|
+
```
|
197
|
+
|
198
|
+
|
199
|
+
#### Hash Function Helpers
|
200
|
+
|
201
|
+
**HASH160 - RMD160(SHA256(X))**
|
202
|
+
|
203
|
+
All-in-one "best-of-both-worlds" helper - first hash with sha256 and than hash with rmd160. Why? Get the higher security of sha256 and the smaller size of rmd160.
|
204
|
+
|
205
|
+
|
206
|
+
``` ruby
|
207
|
+
hash160( '02b9d1cc0b793b03b9f64d022e9c67d5f32670b03f636abf0b3147b34123d13990' )
|
208
|
+
#=> "e6b145a3908a4d6616b13c1109717add8672c900"
|
209
|
+
|
210
|
+
hash160( '02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737' )
|
211
|
+
#=> "93ce48570b55c42c2af816aeaba06cfee1224fae"
|
212
|
+
```
|
213
|
+
|
214
|
+
|
215
|
+
**HASH256 - SHA256(SHA256(X))**
|
216
|
+
|
217
|
+
All-in-one double sha256 hash helper, that is, first hash with sha256 and than hash with sha256 again. Why? Arguably higher security.
|
218
|
+
|
219
|
+
> SHA256(SHA256(X)) was proposed by Ferguson and Schneier in their excellent book "Practical Cryptography"
|
220
|
+
> (later updated by Ferguson, Schneier, and Kohno and renamed "Cryptography Engineering") as a way to make SHA256 invulnerable
|
221
|
+
> to "length-extension" attack. They called it "SHA256D".
|
222
|
+
|
223
|
+
|
224
|
+
``` ruby
|
225
|
+
hash256( '6fe6b145a3908a4d6616b13c1109717add8672c900' )
|
226
|
+
#=> "02335f08b8fe4ddad263a50b7a33c5d38ea1cbd8fd2056a1320a3ddece541711"
|
227
|
+
```
|
228
|
+
|
229
|
+
#### Base58 Encoding / Decoding Helpers
|
230
|
+
|
231
|
+
**BASE58**
|
232
|
+
|
233
|
+
Base58 encoding / decoding with leading zero bytes (in hex or binary strings) getting encoded from `00` to `1` and back:
|
234
|
+
|
235
|
+
``` ruby
|
236
|
+
base58( "516b6fcd0f" ) #=> "ABnLTmg"
|
237
|
+
base58( "00000000000000000000123456789abcdef0" ) #=> "111111111143c9JGph3DZ"
|
238
|
+
# or with optional 0x or 0X prefix
|
239
|
+
base58( "0x516b6fcd0f" ) #=> "ABnLTmg"
|
240
|
+
base58( "0x00000000000000000000123456789abcdef0" ) #=> "111111111143c9JGph3DZ"
|
241
|
+
|
242
|
+
unbase58( "ABnLTmg" ) #=> "516b6fcd0f"
|
243
|
+
unbase58( "111111111143c9JGph3DZ" ) #=> "00000000000000000000123456789abcdef0"
|
244
|
+
```
|
245
|
+
|
246
|
+
|
247
|
+
**BASE58CHECK - BASE58(X || SHA256(SHA256(X))[:4])**
|
248
|
+
|
249
|
+
Base58 encoding with an extra 4-byte secure hash checksum.
|
250
|
+
|
251
|
+
``` ruby
|
252
|
+
base58check( "516b6fcd0f" ) #=> "237LSrY9NUUas"
|
253
|
+
base58check( "00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31" ) #=> "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs"
|
254
|
+
|
255
|
+
unbase58check( "237LSrY9NUUas" ) #=> "516b6fcd0f"
|
256
|
+
unbase58check( "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs" ) #=> "00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31"
|
257
|
+
```
|
258
|
+
|
47
259
|
|
48
260
|
|
49
261
|
### Public Key Signature Algorithms
|
50
262
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
#
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
#=>
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
107
|
-
#
|
108
|
-
|
109
|
-
#
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
#
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# jWrdPwEREA3nwNu/HSpiGRBFr+lu/YgWGNp6HLGPeL7uHGAfmWPyU5WRzGzf
|
148
|
-
# iEs5B6kdJ3S8LSbP0hkOD8AOgZLPeU5rzA4+/Ymt8e/UOVwwka6Gj13yoBua
|
149
|
-
# mSdsVuQfgh2VpySejCz4ykYlMSHK8Kx8QFt+QbyI5QZUy2dFh6HlcnHR+G9A
|
150
|
-
# RMRZ1vAuQhYqtDSsxwRcZCSFsc6uctAvsgFinhqy6ls5VpcXfuKwZhKAw3Di
|
151
|
-
# E2MYUnT7+i38Mq26iWzgmDbpOrVCO5tjlSiHY1731A=="
|
152
|
-
|
153
|
-
RSA.valid_signature?( tx_hash, tx_signature, alice_pub )
|
263
|
+
|
264
|
+
**Elliptic Curve Digital Signature Algorithm (ECDSA)**
|
265
|
+
|
266
|
+
|
267
|
+
Private Key
|
268
|
+
|
269
|
+
An ECDSA (Elliptic Curve Digital Signature Algorithm) private key is a random number between 1 and the order of the elliptic curve group.
|
270
|
+
|
271
|
+
|
272
|
+
``` ruby
|
273
|
+
# Auto-generate (random) private key
|
274
|
+
private_key = EC::PrivateKey.generate # by default uses Secp256k1 curve (used in Bitcoin and Ethereum)
|
275
|
+
|
276
|
+
private_key.to_i
|
277
|
+
#=> 72190737707147846840353520312904745954595478835413056312168022784020322830309
|
278
|
+
```
|
279
|
+
|
280
|
+
|
281
|
+
(Auto-)Calculate the Public Key - Enter Elliptic Curve (EC) Cryptography
|
282
|
+
|
283
|
+
The public key are two numbers (that is, a point with the coordinates x and y) computed by multiplying
|
284
|
+
the generator point (`G`) of the curve with the private key.
|
285
|
+
This is equivalent to adding the generator to itself `private_key` times.
|
286
|
+
Magic?
|
287
|
+
Let's try:
|
288
|
+
|
289
|
+
|
290
|
+
``` ruby
|
291
|
+
# This private key is just an example. It should be much more secure!
|
292
|
+
private_key = EC::PrivateKey.new( 1234 ) # by default uses Secp256k1 curve (used in Bitcoin and Ethereum)
|
293
|
+
|
294
|
+
public_key = private_key.public_key ## the "magic" one-way K=k*G curve multiplication (K=public key,k=private key, G=generator point)
|
295
|
+
point = public_key.point
|
296
|
+
|
297
|
+
point.x
|
298
|
+
#=> 102884003323827292915668239759940053105992008087520207150474896054185180420338
|
299
|
+
point.y
|
300
|
+
#=> 49384988101491619794462775601349526588349137780292274540231125201115197157452
|
301
|
+
|
302
|
+
point.x.to_s(16)
|
303
|
+
#=> "e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2"
|
304
|
+
point.y.to_s(16)
|
305
|
+
#=> "6d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c"
|
306
|
+
```
|
307
|
+
|
308
|
+
|
309
|
+
Sign a transaction with an (elliptic curve) private key:
|
310
|
+
|
311
|
+
``` ruby
|
312
|
+
# Step 1 - Calculate the Transaction (tx) Hash
|
313
|
+
tx = 'from: Alice to: Bob cryptos: 43_000_000_000'
|
314
|
+
txhash = sha256( tx )
|
315
|
+
|
316
|
+
# Step 2 - Get the Signer's Private key
|
317
|
+
private_key = EC::PrivateKey.new( 1234 ) # This private key is just an example. It should be much more secure!
|
318
|
+
|
319
|
+
# Sign!
|
320
|
+
signature = private_key.sign( txhash )
|
321
|
+
# -or-
|
322
|
+
signature = EC.sign( txhash, private_key )
|
323
|
+
|
324
|
+
signature.r
|
325
|
+
#=> 80563021554295584320113598933963644829902821722081604563031030942154621916407
|
326
|
+
signature.s
|
327
|
+
#=> 58316177618967642068351252425530175807242657664855230973164972803783751708604
|
328
|
+
|
329
|
+
signature.r.to_s(16)
|
330
|
+
#=> "3306a2f81ad2b2f62ebe0faec129545bc772babe1ca5e70f6e56556b406464c0"
|
331
|
+
signature.s.to_s(16)
|
332
|
+
#=> "4fe202bb0835758f514cd4a0787986f8f6bf303df629dc98c5b1a438a426f49a"
|
333
|
+
```
|
334
|
+
|
335
|
+
|
336
|
+
Verify a signed transaction with an (elliptic curve) public key:
|
337
|
+
|
338
|
+
``` ruby
|
339
|
+
# Step 1 - Calculate the Transaction (tx) Hash
|
340
|
+
tx = 'from: Alice to: Bob cryptos: 43_000_000_000'
|
341
|
+
txhash = sha256( tx )
|
342
|
+
|
343
|
+
# Step 2 - Get the Signer's Public Key
|
344
|
+
public_key = EC::PublicKey.new(
|
345
|
+
102884003323827292915668239759940053105992008087520207150474896054185180420338,
|
346
|
+
49384988101491619794462775601349526588349137780292274540231125201115197157452
|
347
|
+
)
|
348
|
+
|
349
|
+
# Step 3 - Get the Transaction's Signature
|
350
|
+
signature = EC::Signature.new(
|
351
|
+
80563021554295584320113598933963644829902821722081604563031030942154621916407,
|
352
|
+
58316177618967642068351252425530175807242657664855230973164972803783751708604
|
353
|
+
)
|
354
|
+
|
355
|
+
# Don't Trust - Verify
|
356
|
+
public_key.verify?( txhash, signature )
|
357
|
+
# -or-
|
358
|
+
EC.verify?( txhash, signature, public_key )
|
154
359
|
#=> true
|
155
360
|
|
156
|
-
tx = "from: alice, to: bob, $22"
|
157
|
-
tx_hash = sha256( tx )
|
158
|
-
#=> "e899604bb4c95d2f1a7cfe561ad65941769e2064bdbbcaa79eb64ce0a2832380"
|
159
361
|
|
160
|
-
|
161
|
-
|
362
|
+
# or using hexadecimal numbers
|
363
|
+
|
364
|
+
public_key = EC::PublicKey.new(
|
365
|
+
0xe37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2,
|
366
|
+
0x6d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c
|
367
|
+
)
|
368
|
+
|
369
|
+
signature = EC::Signature.new(
|
370
|
+
0x3306a2f81ad2b2f62ebe0faec129545bc772babe1ca5e70f6e56556b406464c0,
|
371
|
+
0x4fe202bb0835758f514cd4a0787986f8f6bf303df629dc98c5b1a438a426f49a
|
372
|
+
)
|
373
|
+
|
374
|
+
public_key.verify?( txhash, signature )
|
375
|
+
# -or-
|
376
|
+
EC.verify?( txhash, signature, public_key )
|
377
|
+
#=> true
|
162
378
|
```
|
163
379
|
|
164
380
|
|
165
|
-
|
381
|
+
To sum up:
|
382
|
+
|
383
|
+
- The (raw) private key is a 256-bit unsigned integer number
|
384
|
+
- The (raw) public key is a point (x,y), that is, two 256-bit unsigned integer numbers - derived (calculated) from the private key
|
385
|
+
- A (raw) signature is composed of (r,s), that is, two 256-bit unsigned integer numbers
|
386
|
+
|
387
|
+
That's all the magic.
|
388
|
+
|
389
|
+
|
390
|
+
|
391
|
+
|
392
|
+
## Examples
|
393
|
+
|
394
|
+
### Generate the Bitcoin (Base58) Address from the (Elliptic Curve) Public Key
|
395
|
+
|
396
|
+
Let's follow the steps from [How to create Bitcoin Address](https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses#How_to_create_Bitcoin_Address):
|
397
|
+
|
398
|
+
``` ruby
|
399
|
+
# Lets start with the public key ("raw" hex string encoded)
|
400
|
+
pk = "0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352"
|
401
|
+
|
402
|
+
# 1. Perform SHA-256 hashing on the public key
|
403
|
+
step1 = sha256( pk )
|
404
|
+
#=> "0b7c28c9b7290c98d7438e70b3d3f7c848fbd7d1dc194ff83f4f7cc9b1378e98"
|
405
|
+
|
406
|
+
# 2. Perform RIPEMD-160 hashing on the result of SHA-256
|
407
|
+
step2 = ripemd160( step1 )
|
408
|
+
#=> "f54a5851e9372b87810a8e60cdd2e7cfd80b6e31"
|
409
|
+
|
410
|
+
# 3. Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)
|
411
|
+
step3 = "00" + step2
|
412
|
+
#=> "00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31"
|
413
|
+
|
414
|
+
# 4. Perform SHA-256 hash on the extended RIPEMD-160 result
|
415
|
+
step4 = sha256( step3 )
|
416
|
+
#=> "ad3c854da227c7e99c4abfad4ea41d71311160df2e415e713318c70d67c6b41c"
|
417
|
+
|
418
|
+
# 5. Perform SHA-256 hash on the result of the previous SHA-256 hash
|
419
|
+
step5 = sha256( step4 )
|
420
|
+
#=> "c7f18fe8fcbed6396741e58ad259b5cb16b7fd7f041904147ba1dcffabf747fd"
|
421
|
+
|
422
|
+
# 6. Take the first 4 bytes of the second SHA-256 hash. This is the address checksum
|
423
|
+
step6 = step5[0..7] # note: 4 bytes in hex string are 8 digits/chars
|
424
|
+
#=> "c7f18fe8"
|
425
|
+
|
426
|
+
# 7. Add the 4 checksum bytes from step 6 at the end of
|
427
|
+
# extended RIPEMD-160 hash from step 3.
|
428
|
+
# This is the 25-byte binary Bitcoin Address.
|
429
|
+
step7 = step3 + step6
|
430
|
+
#=> "00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31c7f18fe8"
|
431
|
+
|
432
|
+
# 8. Convert the result from a byte string into a base58 string using Base58 encoding.
|
433
|
+
# This is the most commonly used Bitcoin Address format.
|
434
|
+
addr = base58( step7 )
|
435
|
+
#=> "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs"
|
436
|
+
```
|
437
|
+
|
438
|
+
Or let's try again with the shortcut helpers:
|
439
|
+
|
440
|
+
- `HASH160 - RMD160(SHA256(X))`
|
441
|
+
- `BASE58CHECK - BASE58(X || SHA256(SHA256(X))[:4])`
|
442
|
+
|
443
|
+
``` ruby
|
444
|
+
# Lets start with the public key ("raw" hex string encoded)
|
445
|
+
pk = "0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352"
|
446
|
+
|
447
|
+
# 1. Perform HASH-160 hashing on the public key
|
448
|
+
# a) Perform SHA-256 hashing on the public key
|
449
|
+
# b) Perform RIPEMD-160 hashing on the result of SHA-256
|
450
|
+
step1 = hash160( pk )
|
451
|
+
#=> "f54a5851e9372b87810a8e60cdd2e7cfd80b6e31"
|
452
|
+
|
453
|
+
# 2. Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)
|
454
|
+
step2 = "00" + step1
|
455
|
+
#=> "00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31"
|
456
|
+
|
457
|
+
# 3. Encode with BASE58CHECK
|
458
|
+
# a) Perform SHA-256 hash on the extended RIPEMD-160 result
|
459
|
+
# b) Perform SHA-256 hash on the result of the previous SHA-256 hash
|
460
|
+
# c) Take the first 4 bytes of the second SHA-256 hash. This is the address checksum
|
461
|
+
# d) Add the 4 checksum bytes at the end of
|
462
|
+
# extended RIPEMD-160 hash from step 2.
|
463
|
+
# This is the 25-byte binary Bitcoin Address.
|
464
|
+
# e) Convert the result from a byte string into a base58 string
|
465
|
+
# using Base58 encoding.
|
466
|
+
# This is the most commonly used Bitcoin Address format.
|
467
|
+
addr = base58check( step2 )
|
468
|
+
#=> "1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs"
|
469
|
+
```
|
470
|
+
|
471
|
+
|
472
|
+
References
|
473
|
+
|
474
|
+
- [How to create Bitcoin Address](https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses#How_to_create_Bitcoin_Address)
|
475
|
+
- [Ruby Quiz #15 - Generate the Bitcoin (Base58) Address from the (Elliptic Curve) Public Key](https://github.com/planetruby/quiz/tree/master/015)
|
476
|
+
|
477
|
+
Bonus: Bitcon Tip - How to Buy Bitcoin (The CO₂-Friendly Way)
|
478
|
+
|
479
|
+
> 1. Take one $50 bill, five $10 bills, or ten $5 bills (I wouldn't recommend change - stay with paper money).
|
480
|
+
> 2. Go to the bathroom.
|
481
|
+
> 3. Lift the lid of the loo.
|
482
|
+
> 4. Throw money in.
|
483
|
+
> 5. Flush down water.
|
484
|
+
>
|
485
|
+
> Congrats! You just purchased $50 worth of Bitcoin - without fucking the planet!
|
486
|
+
>
|
487
|
+
> -- Trolly McTrollface, Bitcon Greater Fool Court Jester
|
488
|
+
|
489
|
+
Read more [Crypto Quotes »](https://github.com/openblockchains/crypto-quotes)
|
166
490
|
|
167
491
|
|
168
492
|
|