digibyte-cigs 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df993226b86b93297a57ad07fa01304a1d6702f5
4
+ data.tar.gz: 752c471cbcd1c6af8ebd8466284415ad480c101d
5
+ SHA512:
6
+ metadata.gz: 8632c3d2e520af4e00d5c384eac7ef90b0f8ad7ef378ecefd248b4e4e06f10f81fa03cb6c86498808e7038f467fdae5af7e03b790c3b8c0bf2ef02507e042c8d
7
+ data.tar.gz: 062e367f0e1211e9ad2ec181025aaf1f672c5327f6723fd63277fa87109cb95ddf491d4c46652785de8a536905b7903e019325f3312c55405a33e7955f35820e
data/.gitignore ADDED
@@ -0,0 +1,46 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+ *.gem
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+
31
+ .DS_Store
32
+
33
+ # For TextMate
34
+ *.tmproj
35
+ tmtags
36
+ .tmtags
37
+
38
+ # For emacs:
39
+ *~
40
+ \#*
41
+ .\#*
42
+
43
+ # For vim:
44
+ *.swp
45
+
46
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ digibyte-cigs (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.4)
10
+ rake (10.0.4)
11
+ rspec (2.13.0)
12
+ rspec-core (~> 2.13.0)
13
+ rspec-expectations (~> 2.13.0)
14
+ rspec-mocks (~> 2.13.0)
15
+ rspec-core (2.13.1)
16
+ rspec-expectations (2.13.0)
17
+ diff-lcs (>= 1.1.3, < 2.0)
18
+ rspec-mocks (2.13.1)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ digibyte-cigs!
25
+ rake (~> 10.0.4)
26
+ rspec (~> 2.13.0)
27
+
28
+ BUNDLED WITH
29
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Michael Pearce
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # DigiByte Cigs - Smokin' Hot DigiByte Signatures
2
+
3
+ ## Installation
4
+
5
+ ```sh
6
+ ~$ gem install digibyte-cigs
7
+ ```
8
+
9
+ ## Command Line
10
+
11
+ Usage:
12
+ ```sh
13
+ ~$ digibyte-cigs
14
+ Usage: digibyte-cigs command [arguments ...] [options ...]
15
+
16
+ Commands
17
+ verify digibyte-address signature [message-file]
18
+ sign private-key [message-file]
19
+
20
+ Options
21
+ -m, --message MESSAGE Message can also be read from STDIN
22
+ -S, --no-strip Do not strip leading and trailing whitespace from message (stripped by default)
23
+ ```
24
+
25
+ Examples:
26
+ ```sh
27
+ ~$ # Sign with -m message parameter
28
+ ~$ digibyte-cigs sign 5JFZuDkLgbEXK4CUEiXyyz4fUqzAsQ5QUqufdJy8MoLA9S1RdNX -m 'this is a message'
29
+ HIBYi2g3yFimzD/YSD9j+PYwtsdCuHR2xwIQ6n0AN6RPUVDGttgOmlnsiwx90ZSjmaWrH1/HwrINJbaP7eMA6V4=
30
+ ~$
31
+ ~$ # Verify with message from STDIN
32
+ ~$ echo 'this is a message' | digibyte-cigs verify 11o51X3ciSjoLWFN3sbg3yzCM8RSuD2q9 HIBYi2g3yFimzD/YSD9j+PYwtsdCuHR2xwIQ6n0AN6RPUVDGttgOmlnsiwx90ZSjmaWrH1/HwrINJbaP7eMA6V4=
33
+ ~$
34
+ ~$ # Verify with message from file
35
+ ~$ echo 'this is a message' > message.txt
36
+ ~$ digibyte-cigs verify 11o51X3ciSjoLWFN3sbg3yzCM8RSuD2q9 HIBYi2g3yFimzD/YSD9j+PYwtsdCuHR2xwIQ6n0AN6RPUVDGttgOmlnsiwx90ZSjmaWrH1/HwrINJbaP7eMA6V4= message.txt
37
+ ~$
38
+ ```
39
+
40
+ ## Ruby API
41
+
42
+ Sign a message:
43
+ ```ruby
44
+ require 'rubygems'
45
+ require 'digibyte-cigs'
46
+
47
+ # Support for Wallet Import Format, Compressed WIF, Mini Format, Hex and Base64 wallets
48
+ wallet_key = "5JFZuDkLgbEXK4CUEiXyyz4fUqzAsQ5QUqufdJy8MoLA9S1RdNX"
49
+ message = "this is a message"
50
+
51
+ puts "The signature is: #{DigiByteCigs.sign_message!(wallet_key, message)}"
52
+ ```
53
+
54
+ Verify a message signature:
55
+ ```ruby
56
+ require 'rubygems'
57
+ require 'digibyte-cigs'
58
+
59
+ address = "11o51X3ciSjoLWFN3sbg3yzCM8RSuD2q9"
60
+ signature = "HIBYi2g3yFimzD/YSD9j+PYwtsdCuHR2xwIQ6n0AN6RPUVDGttgOmlnsiwx90ZSjmaWrH1/HwrINJbaP7eMA6V4="
61
+ message = "this is a message"
62
+
63
+ if DigiByteCigs.verify_message(address, signature, message)
64
+ puts "It looks like you own address #{address}!"
65
+ end
66
+ ```
67
+
68
+ # Credits
69
+
70
+ Thanks to jackjack for pointing me to Armory's implementation of message signatures:
71
+ https://github.com/jackjack-jj/jasvet
72
+
73
+ [DigiByte Cigs](https://github.com/michaelgpearce/digibyte-cigs) is maintained by [Michael Pearce](https://github.com/michaelgpearce).
74
+
75
+ # Donation
76
+
77
+ If you find this software useful and wish to donate, you can do so here:
78
+ ```
79
+ 1Cyd1wG4hCXK5aRCJQu3KnnhSrrfgs7NeM
80
+ ```
81
+
82
+ # Copyright
83
+
84
+ Copyright (c) 2013 Michael Pearce. See LICENSE.txt for further details.
85
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Run Specs'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
data/bin/digibyte-cigs ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'digibyte-cigs'
5
+
6
+ @options = {}
7
+
8
+ opt_parser = OptionParser.new do |opt|
9
+ opt.banner = "Usage: digibyte-cigs command [arguments ...] [options ...]"
10
+ opt.separator ""
11
+ opt.separator "Commands"
12
+ opt.separator " verify digibyte-address signature [message-file]"
13
+ opt.separator " sign private-key [message-file]"
14
+ opt.separator ""
15
+ opt.separator "Options"
16
+ opt.on("-m", "--message MESSAGE", "Message can also be read from STDIN") do |message|
17
+ @options[:message] = message
18
+ end
19
+ opt.on("-S", "--no-strip", "Do not strip leading and trailing whitespace from message (stripped by default)") do
20
+ @options[:no_strip] = true
21
+ end
22
+ end
23
+
24
+ opt_parser.parse!
25
+
26
+ def message
27
+ message = @options[:message] || ARGF.read
28
+ message.strip! unless @options[:no_strip]
29
+
30
+ message
31
+ end
32
+
33
+ begin
34
+ case ARGV.shift
35
+ when "verify"
36
+ address = ARGV.shift or raise ArgumentError
37
+ signature = ARGV.shift or raise ArgumentError
38
+ ::DigiByteCigs.verify_message!(address, signature, message)
39
+ when "sign"
40
+ private_key = ARGV.shift or raise ArgumentError
41
+ puts ::DigiByteCigs.sign_message!(private_key, message)
42
+ else
43
+ raise ArgumentError
44
+ end
45
+ exit 0
46
+ rescue ::DigiByteCigs::Error => e
47
+ puts "Error: #{e}"
48
+ exit 2
49
+ rescue ArgumentError
50
+ puts opt_parser
51
+ exit 1
52
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "digibyte_cigs/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "digibyte-cigs"
8
+ spec.version = DigiByteCigs::VERSION
9
+ spec.authors = ["Vertbase"]
10
+ spec.email = ["dev@vertbase.com"]
11
+ spec.summary = "Create and Verify DigiByte Signatures"
12
+ spec.homepage = "https://github.com/vertbase/digibyte-cigs"
13
+
14
+ spec.rubyforge_project = "digibyte-cigs"
15
+
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ spec.require_paths = ["lib"]
19
+ spec.executables = ['digibyte-cigs']
20
+
21
+ spec.add_development_dependency 'rspec', '~> 2.13.0'
22
+ spec.add_development_dependency 'rake', '~> 10.0.4'
23
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'digibyte_cigs.rb')
@@ -0,0 +1,278 @@
1
+ %w(error crypto_helper compact_int base_58 curve_fp point public_key private_key signature ec_key).each do |f|
2
+ require File.join(File.dirname(__FILE__), 'digibyte_cigs', f)
3
+ end
4
+
5
+ module DigiByteCigs
6
+ PRIVATE_KEY_PREFIX = {
7
+ :mainnet => 0x80,
8
+ :testnet => 0xEF
9
+ }
10
+ NETWORK_VERSION = {
11
+ :mainnet => 0x00,
12
+ :testnet => 0x6F
13
+ }
14
+
15
+ P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
16
+ R = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
17
+ B = 0x0000000000000000000000000000000000000000000000000000000000000007
18
+ A = 0x0000000000000000000000000000000000000000000000000000000000000000
19
+ Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
20
+ Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
21
+
22
+ CURVE_SECP256K1 = ::DigiByteCigs::CurveFp.new(P, A, B)
23
+ GENERATOR_SECP256K1 = ::DigiByteCigs::Point.new(CURVE_SECP256K1, Gx, Gy, R)
24
+
25
+ class << self
26
+ include ::DigiByteCigs::CryptoHelper
27
+
28
+ def verify_message(address, signature, message, options = {:network => :mainnet})
29
+ begin
30
+ verify_message!(address, signature, message, options)
31
+ true
32
+ rescue ::DigiByteCigs::Error
33
+ false
34
+ end
35
+ end
36
+
37
+ def verify_message!(address, signature, message, options = {:network => :mainnet})
38
+
39
+ decoded_address = decode58(address)
40
+ raise ::DigiByteCigs::Error.new("Incorrect address or message for signature.") if decoded_address.nil?
41
+ # network_version = str_to_num(decoded_address) >> (8 * 24)
42
+
43
+ addr = get_signature_address!(signature, message, options)
44
+
45
+ raise ::DigiByteCigs::Error.new("Incorrect address or message for signature.") if address != addr
46
+
47
+ nil
48
+ end
49
+
50
+ def get_signature_address(signature, message, options = {:network => :mainnet})
51
+ begin
52
+ get_signature_address!(signature, message, options)
53
+ rescue ::DigiByteCigs::Error
54
+ false
55
+ end
56
+ end
57
+
58
+ def get_signature_address!(signature, message, options = {:network => :mainnet})
59
+
60
+ message = calculate_hash(format_message_to_sign(message))
61
+
62
+ curve = CURVE_SECP256K1
63
+ g = GENERATOR_SECP256K1
64
+ a, b, p = curve.a, curve.b, curve.p
65
+
66
+ order = g.order
67
+
68
+ sig = decode64(signature)
69
+ raise ::DigiByteCigs::Error.new("Bad signature length") if sig.size != 65
70
+ raise ::DigiByteCigs::Error.new("Bad characters in signature") if signature != encode64(sig)
71
+
72
+ hb = sig[0].ord
73
+ r, s = [sig[1...33], sig[33...65]].collect { |s| str_to_num(s) }
74
+
75
+
76
+ raise ::DigiByteCigs::Error.new("Bad signature first byte") if hb < 27 || hb >= 35
77
+
78
+ compressed = false
79
+ if hb >= 31
80
+ compressed = true
81
+ hb -= 4
82
+ end
83
+
84
+ recid = hb - 27
85
+ x = (r + (recid / 2) * order) % p
86
+ y2 = ((x ** 3 % p) + a * x + b) % p
87
+ yomy = sqrt_mod(y2, p)
88
+ if (yomy - recid) % 2 == 0
89
+ y = yomy
90
+ else
91
+ y = p - yomy
92
+ end
93
+
94
+ r_point = ::DigiByteCigs::Point.new(curve, x, y, order)
95
+ e = str_to_num(message)
96
+ minus_e = -e % order
97
+
98
+ inv_r = inverse_mod(r, order)
99
+ q = (r_point * s + g * minus_e) * inv_r
100
+
101
+
102
+ public_key = ::DigiByteCigs::PublicKey.new(g, q, compressed)
103
+
104
+ public_key_to_bc_address(public_key.ser(), NETWORK_VERSION[options[:network]])
105
+ end
106
+
107
+ def sign_message(wallet_key, message, options = {:network => :mainnet})
108
+ begin
109
+ sign_message!(wallet_key, message, options)
110
+ rescue ::DigiByteCigs::Error
111
+ nil
112
+ end
113
+ end
114
+
115
+ def sign_message!(wallet_key, message, options = {:network => :mainnet})
116
+ private_key = convert_wallet_format_to_bytes!(wallet_key, options[:network])
117
+
118
+ msg_hash = sha256(sha256(format_message_to_sign(message)))
119
+
120
+ ec_key = ::DigiByteCigs::EcKey.new(str_to_num(private_key))
121
+ private_key = ec_key.private_key
122
+ public_key = ec_key.public_key
123
+ addr = public_key_to_bc_address(get_pub_key(ec_key, ec_key.public_key.compressed), NETWORK_VERSION[options[:network]])
124
+
125
+ sig = private_key.sign(msg_hash, random_k)
126
+ raise ::DigiByteCigs::Error.new("Unable to sign message") unless public_key.verify(msg_hash, sig)
127
+
128
+ 4.times do |i|
129
+ hb = 27 + i
130
+
131
+ sign = "#{hb.chr}#{sig.ser}"
132
+ sign_64 = encode64(sign)
133
+
134
+ begin
135
+ verify_message!(addr, sign_64, message, options)
136
+ return sign_64
137
+ rescue ::DigiByteCigs::Error
138
+ next
139
+ end
140
+ end
141
+
142
+ raise ::DigiByteCigs::Error, "Unable to construct recoverable key"
143
+ end
144
+
145
+ def convert_wallet_format_to_bytes!(input, network)
146
+ bytes = if is_wallet_import_format?(input, network)
147
+ decode_wallet_import_format(input, network)
148
+ elsif is_compressed_wallet_import_format?(input, network)
149
+ decode_compressed_wallet_import_format(input, network)
150
+ elsif is_mini_format?(input)
151
+ sha256(input)
152
+ elsif is_hex_format?(input)
153
+ decode_hex(input)
154
+ elsif is_base_64_format?(input)
155
+ decode64(input)
156
+ else
157
+ raise ::DigiByteCigs::Error.new("Unknown Wallet Format")
158
+ end
159
+
160
+ bytes
161
+ end
162
+
163
+ private
164
+
165
+ def format_message_to_sign(message)
166
+ "\x18DigiByte Signed Message:\n#{::DigiByteCigs::CompactInt.new(message.size).encode}#{message}"
167
+ end
168
+
169
+ def random_k
170
+ k = 0
171
+ 8.times do |i|
172
+ k |= (rand * 0xffffffff).to_i << (32 * i)
173
+ end
174
+
175
+ k
176
+ end
177
+
178
+ def get_pub_key(public_key, compressed)
179
+ i2o_ec_public_key(public_key, compressed)
180
+ end
181
+
182
+ def i2o_ec_public_key(public_key, compressed)
183
+ key = if compressed
184
+ "#{public_key.public_key.point.y & 1 > 0 ? '03' : '02'}%064x" % public_key.public_key.point.x
185
+ else
186
+ "04%064x%064x" % [public_key.public_key.point.x, public_key.public_key.point.y]
187
+ end
188
+
189
+ decode_hex(key)
190
+ end
191
+
192
+ def decode_wallet_import_format(input, network)
193
+ bytes = decode58(input)#[1..-1]
194
+ #puts "ASDF #{bytes.unpack('H*')}"
195
+ #puts bytes.bytes.collect {|e| e.to_i}.join(" ")
196
+ hash = bytes[0..32]
197
+
198
+ checksum = sha256(sha256(hash))
199
+ raise ::DigiByteCigs::Error.new("Wallet checksum invalid") if bytes[33..37] != checksum[0..3]
200
+
201
+ version, hash = hash[0], hash[1..-1]
202
+ raise ::DigiByteCigs::Error.new("Wallet Version #{version} not supported") if version.ord != PRIVATE_KEY_PREFIX[network]
203
+
204
+ hash
205
+ end
206
+
207
+ def decode_compressed_wallet_import_format(input, network)
208
+ bytes = decode58(input)
209
+ hash = bytes[0...34]
210
+
211
+ checksum = sha256(sha256(hash))
212
+ raise ::DigiByteCigs::Error.new("Wallet checksum invalid") if bytes[34..37] != checksum[0..3]
213
+
214
+ version, hash = hash[0], hash[1..32]
215
+ raise ::DigiByteCigs::Error.new("Wallet Version #{version} not supported") if version.ord != PRIVATE_KEY_PREFIX[network]
216
+
217
+ hash
218
+ end
219
+
220
+ # 64 characters [0-9A-F]
221
+ def is_hex_format?(key)
222
+ /^[A-Fa-f0-9]{64}$/ =~ key
223
+ end
224
+
225
+ # 51 characters base58 starting with 5
226
+ def is_wallet_import_format?(key, network)
227
+ /^#{network == :mainnet ? '5' : '9'}[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/ =~ key
228
+ end
229
+
230
+ # 52 characters base58 starting with L or K
231
+ def is_compressed_wallet_import_format?(key, network)
232
+ /^[network == :mainnet ? 'LK' : 'c'][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/ =~ key
233
+ end
234
+
235
+ # 44 characters
236
+ def is_base_64_format?(key)
237
+ /^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=+\/]{44}$/ =~ key
238
+ end
239
+
240
+ # 22, 26 or 30 characters, always starts with an 'S'
241
+ def is_mini_format?(key)
242
+ validChars22 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21}$/ =~ key
243
+ validChars26 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{25}$/ =~ key
244
+ validChars30 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{29}$/ =~ key
245
+
246
+ bytes = sha256("#{key}?")
247
+
248
+ (bytes[0].ord === 0x00 || bytes[0].ord === 0x01) && (validChars22 || validChars26 || validChars30)
249
+ end
250
+
251
+ def debug_bytes(s)
252
+ s.chars.collect(&:ord).join(', ')
253
+ end
254
+
255
+ def calculate_hash(d)
256
+ sha256(sha256(d))
257
+ end
258
+
259
+ def public_key_to_bc_address(public_key, network_version)
260
+ h160 = hash_160(public_key)
261
+
262
+ hash_160_to_bc_address(h160, network_version)
263
+ end
264
+
265
+ def hash_160_to_bc_address(h160, address_type)
266
+ vh160 = address_type.chr + h160
267
+ h = calculate_hash(vh160)
268
+ addr = vh160 + h[0...4]
269
+
270
+ encode58(addr)
271
+ end
272
+
273
+ def hash_160(public_key)
274
+ ripemd160(sha256(public_key))
275
+ end
276
+
277
+ end
278
+ end