digibyte-cigs 0.0.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
+ 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