crypto-lite 0.2.3 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -3
- data/README.md +1023 -501
- data/Rakefile +34 -33
- data/lib/crypto/lite.rb +2 -2
- data/lib/crypto-lite/config.rb +32 -32
- data/lib/crypto-lite/helper.rb +25 -25
- data/lib/crypto-lite/metal.rb +135 -128
- data/lib/crypto-lite/sign_rsa.rb +29 -29
- data/lib/crypto-lite/version.rb +23 -23
- data/lib/crypto-lite.rb +145 -147
- data/lib/crypto.rb +2 -2
- data/test/helper.rb +11 -11
- data/test/test_base58.rb +36 -36
- data/test/test_bitcoin_addr.rb +58 -58
- data/test/test_hash.rb +47 -47
- data/test/test_hash_sha.rb +90 -87
- data/test/test_version.rb +19 -19
- metadata +21 -7
data/Rakefile
CHANGED
@@ -1,33 +1,34 @@
|
|
1
|
-
require 'hoe'
|
2
|
-
require './lib/crypto-lite/version.rb'
|
3
|
-
|
4
|
-
|
5
|
-
Hoe.spec 'crypto-lite' do
|
6
|
-
|
7
|
-
self.version = CryptoLite::VERSION
|
8
|
-
|
9
|
-
self.summary = "crypto-lite - cryptographic secure hash functions and public key signature algorithms made easy"
|
10
|
-
self.description = summary
|
11
|
-
|
12
|
-
self.urls = { home: 'https://github.com/
|
13
|
-
|
14
|
-
self.author = 'Gerald Bauer'
|
15
|
-
self.email = 'wwwmake@googlegroups.com'
|
16
|
-
|
17
|
-
# switch extension to .markdown for gihub formatting
|
18
|
-
self.readme_file = 'README.md'
|
19
|
-
self.history_file = 'CHANGELOG.md'
|
20
|
-
|
21
|
-
self.extra_deps = [
|
22
|
-
['digest-
|
23
|
-
['base32-alphabets'],
|
24
|
-
['base58-alphabets'],
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/crypto-lite/version.rb'
|
3
|
+
|
4
|
+
|
5
|
+
Hoe.spec 'crypto-lite' do
|
6
|
+
|
7
|
+
self.version = CryptoLite::VERSION
|
8
|
+
|
9
|
+
self.summary = "crypto-lite - cryptographic secure hash functions and public key signature algorithms made easy"
|
10
|
+
self.description = summary
|
11
|
+
|
12
|
+
self.urls = { home: 'https://github.com/rubycocos/blockchain' }
|
13
|
+
|
14
|
+
self.author = 'Gerald Bauer'
|
15
|
+
self.email = 'wwwmake@googlegroups.com'
|
16
|
+
|
17
|
+
# switch extension to .markdown for gihub formatting
|
18
|
+
self.readme_file = 'README.md'
|
19
|
+
self.history_file = 'CHANGELOG.md'
|
20
|
+
|
21
|
+
self.extra_deps = [
|
22
|
+
['digest-lite'],
|
23
|
+
['base32-alphabets'],
|
24
|
+
['base58-alphabets'],
|
25
|
+
['elliptic'],
|
26
|
+
]
|
27
|
+
|
28
|
+
self.licenses = ['Public Domain']
|
29
|
+
|
30
|
+
self.spec_extras = {
|
31
|
+
required_ruby_version: '>= 2.3'
|
32
|
+
}
|
33
|
+
|
34
|
+
end
|
data/lib/crypto/lite.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require_relative '../crypto-lite' ## lets you use require 'crypto/lite' too
|
2
|
-
|
1
|
+
require_relative '../crypto-lite' ## lets you use require 'crypto/lite' too
|
2
|
+
|
data/lib/crypto-lite/config.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
|
-
module Crypto
|
2
|
-
|
3
|
-
class Configuration
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@debug = false
|
7
|
-
end
|
8
|
-
|
9
|
-
def debug?() @debug || false; end
|
10
|
-
def debug=(value) @debug = value; end
|
11
|
-
end # class Configuration
|
12
|
-
|
13
|
-
## lets you use
|
14
|
-
## Crypto.configure do |config|
|
15
|
-
## config.debug = true
|
16
|
-
## end
|
17
|
-
|
18
|
-
def self.configuration
|
19
|
-
@configuration ||= Configuration.new
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.configure
|
23
|
-
yield( configuration )
|
24
|
-
end
|
25
|
-
|
26
|
-
## add convenience helper for format
|
27
|
-
def self.debug?() configuration.debug?; end
|
28
|
-
def self.debug=(value) self.configuration.debug = value; end
|
29
|
-
end # module Crypto
|
30
|
-
|
31
|
-
|
32
|
-
|
1
|
+
module Crypto
|
2
|
+
|
3
|
+
class Configuration
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@debug = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def debug?() @debug || false; end
|
10
|
+
def debug=(value) @debug = value; end
|
11
|
+
end # class Configuration
|
12
|
+
|
13
|
+
## lets you use
|
14
|
+
## Crypto.configure do |config|
|
15
|
+
## config.debug = true
|
16
|
+
## end
|
17
|
+
|
18
|
+
def self.configuration
|
19
|
+
@configuration ||= Configuration.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configure
|
23
|
+
yield( configuration )
|
24
|
+
end
|
25
|
+
|
26
|
+
## add convenience helper for format
|
27
|
+
def self.debug?() configuration.debug?; end
|
28
|
+
def self.debug=(value) self.configuration.debug = value; end
|
29
|
+
end # module Crypto
|
30
|
+
|
31
|
+
|
32
|
+
|
data/lib/crypto-lite/helper.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
|
2
|
-
module CryptoHelper
|
3
|
-
### note: use include CryptoHelper
|
4
|
-
## to get "top-level" / global helpers
|
5
|
-
|
6
|
-
## add convenience "top-level" helpers
|
7
|
-
def sha256( *args, **kwargs ) Crypto.sha256( *args, **kwargs ); end
|
8
|
-
def sha3_256( *args, **kwargs ) Crypto.sha3_256( *args, **kwargs ); end
|
9
|
-
|
10
|
-
def keccak256( *args, **kwargs ) Crypto.keccak256( *args, **kwargs ); end
|
11
|
-
|
12
|
-
def rmd160( *args, **kwargs ) Crypto.rmd160( *args, **kwargs ); end
|
13
|
-
## def ripemd160( input ) Crypto.rmd160( input ); end
|
14
|
-
alias_method :ripemd160, :rmd160
|
15
|
-
|
16
|
-
def hash160( *args, **kwargs ) Crypto.hash160( *args, **kwargs ); end
|
17
|
-
|
18
|
-
def hash256( *args, **kwargs ) Crypto.hash256( *args, **kwargs ); end
|
19
|
-
|
20
|
-
|
21
|
-
def base58( *args, **kwargs ) Crypto.base58( *args, **kwargs ); end
|
22
|
-
def base58check( *args, **kwargs ) Crypto.base58check( *args, **kwargs ); end
|
23
|
-
end
|
24
|
-
|
25
|
-
|
1
|
+
|
2
|
+
module CryptoHelper
|
3
|
+
### note: use include CryptoHelper
|
4
|
+
## to get "top-level" / global helpers
|
5
|
+
|
6
|
+
## add convenience "top-level" helpers
|
7
|
+
def sha256( *args, **kwargs ) Crypto.sha256( *args, **kwargs ); end
|
8
|
+
def sha3_256( *args, **kwargs ) Crypto.sha3_256( *args, **kwargs ); end
|
9
|
+
|
10
|
+
def keccak256( *args, **kwargs ) Crypto.keccak256( *args, **kwargs ); end
|
11
|
+
|
12
|
+
def rmd160( *args, **kwargs ) Crypto.rmd160( *args, **kwargs ); end
|
13
|
+
## def ripemd160( input ) Crypto.rmd160( input ); end
|
14
|
+
alias_method :ripemd160, :rmd160
|
15
|
+
|
16
|
+
def hash160( *args, **kwargs ) Crypto.hash160( *args, **kwargs ); end
|
17
|
+
|
18
|
+
def hash256( *args, **kwargs ) Crypto.hash256( *args, **kwargs ); end
|
19
|
+
|
20
|
+
|
21
|
+
def base58( *args, **kwargs ) Crypto.base58( *args, **kwargs ); end
|
22
|
+
def base58check( *args, **kwargs ) Crypto.base58check( *args, **kwargs ); end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
data/lib/crypto-lite/metal.rb
CHANGED
@@ -1,128 +1,135 @@
|
|
1
|
-
module Crypto
|
2
|
-
module Metal
|
3
|
-
|
4
|
-
def self.debug?() Crypto.debug?; end
|
5
|
-
|
6
|
-
########################
|
7
|
-
### to the "metal" crypto primitives
|
8
|
-
## work with binary strings (aka byte arrays) / data
|
9
|
-
|
10
|
-
##
|
11
|
-
## todo/check: use/keep bin-suffix in name - why? why not?
|
12
|
-
|
13
|
-
|
14
|
-
def self.base58bin( input )
|
15
|
-
## todo/check: input must be a (binary) string - why? why not?
|
16
|
-
Base58::Bitcoin.encode_bin( input )
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.base58bin_check( input )
|
20
|
-
## todo/check: input must be a (binary) string - why? why not?
|
21
|
-
hash256 = hash256bin( input )
|
22
|
-
base58bin( input + hash256[0,4] )
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
########################
|
27
|
-
# (secure) hash functions
|
28
|
-
|
29
|
-
def self.keccak256bin( input )
|
30
|
-
message = message( input ) ## "normalize" / convert to (binary) string
|
31
|
-
Digest::
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.rmd160bin( input )
|
35
|
-
message = message( input ) ## "normalize" / convert to (binary) string
|
36
|
-
Digest::RMD160.digest( message )
|
37
|
-
end
|
38
|
-
|
39
|
-
## add alias RIPEMD160 - why? why not?
|
40
|
-
class << self
|
41
|
-
alias_method :ripemd160bin, :rmd160bin
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
def self.sha256bin( input, engine=nil ) ## todo/check: add alias sha256b or such to - why? why not?
|
46
|
-
message = message( input ) ## "normalize" / convert to (binary) string
|
47
|
-
|
48
|
-
if engine && ['openssl'].include?( engine.to_s.downcase )
|
49
|
-
puts " engine: #{engine}" if debug?
|
50
|
-
digest = OpenSSL::Digest::SHA256.new
|
51
|
-
## or use OpenSSL::Digest.new( 'SHA256' )
|
52
|
-
digest.update( message )
|
53
|
-
digest.digest
|
54
|
-
else ## use "built-in" hash function from digest module
|
55
|
-
Digest::SHA256.digest( message )
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
##
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
def self.
|
84
|
-
message = message( input ) ## "normalize" / convert to (binary) string
|
85
|
-
|
86
|
-
|
87
|
-
end
|
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
|
-
end
|
1
|
+
module Crypto
|
2
|
+
module Metal
|
3
|
+
|
4
|
+
def self.debug?() Crypto.debug?; end
|
5
|
+
|
6
|
+
########################
|
7
|
+
### to the "metal" crypto primitives
|
8
|
+
## work with binary strings (aka byte arrays) / data
|
9
|
+
|
10
|
+
##
|
11
|
+
## todo/check: use/keep bin-suffix in name - why? why not?
|
12
|
+
|
13
|
+
|
14
|
+
def self.base58bin( input )
|
15
|
+
## todo/check: input must be a (binary) string - why? why not?
|
16
|
+
Base58::Bitcoin.encode_bin( input )
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.base58bin_check( input )
|
20
|
+
## todo/check: input must be a (binary) string - why? why not?
|
21
|
+
hash256 = hash256bin( input )
|
22
|
+
base58bin( input + hash256[0,4] )
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
########################
|
27
|
+
# (secure) hash functions
|
28
|
+
|
29
|
+
def self.keccak256bin( input )
|
30
|
+
message = message( input ) ## "normalize" / convert to (binary) string
|
31
|
+
Digest::KeccakLite.digest( message, 256 )
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.rmd160bin( input )
|
35
|
+
message = message( input ) ## "normalize" / convert to (binary) string
|
36
|
+
Digest::RMD160.digest( message )
|
37
|
+
end
|
38
|
+
|
39
|
+
## add alias RIPEMD160 - why? why not?
|
40
|
+
class << self
|
41
|
+
alias_method :ripemd160bin, :rmd160bin
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def self.sha256bin( input, engine=nil ) ## todo/check: add alias sha256b or such to - why? why not?
|
46
|
+
message = message( input ) ## "normalize" / convert to (binary) string
|
47
|
+
|
48
|
+
if engine && ['openssl'].include?( engine.to_s.downcase )
|
49
|
+
puts " engine: #{engine}" if debug?
|
50
|
+
digest = OpenSSL::Digest::SHA256.new
|
51
|
+
## or use OpenSSL::Digest.new( 'SHA256' )
|
52
|
+
digest.update( message )
|
53
|
+
digest.digest
|
54
|
+
else ## use "built-in" hash function from digest module
|
55
|
+
Digest::SHA256.digest( message )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def self.sha3_256bin( input, engine=nil )
|
61
|
+
message = message( input ) ## "normalize" / convert to (binary) string
|
62
|
+
|
63
|
+
if engine && ['openssl'].include?( engine.to_s.downcase )
|
64
|
+
puts " engine: #{engine}" if debug?
|
65
|
+
digest = OpenSSL::Digest.new( 'SHA3-256' )
|
66
|
+
digest.update( message )
|
67
|
+
digest.digest
|
68
|
+
else ## use "built-in" hash function from digest module
|
69
|
+
Digest::SHA3Lite.digest( message, 256 )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
####
|
75
|
+
## helper
|
76
|
+
# def hash160( pubkey )
|
77
|
+
# binary = [pubkey].pack( "H*" ) # Convert to binary first before hashing
|
78
|
+
# sha256 = Digest::SHA256.digest( binary )
|
79
|
+
# ripemd160 = Digest::RMD160.digest( sha256 )
|
80
|
+
# ripemd160.unpack( "H*" )[0] # Convert back to hex
|
81
|
+
# end
|
82
|
+
|
83
|
+
def self.hash160bin( input )
|
84
|
+
message = message( input ) ## "normalize" / convert to (binary) string
|
85
|
+
|
86
|
+
rmd160bin(sha256bin( message ))
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def self.hash256bin( input )
|
91
|
+
message = message( input ) ## "normalize" / convert to (binary) string
|
92
|
+
|
93
|
+
sha256bin(sha256bin( message ))
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
##############################
|
98
|
+
## helpers
|
99
|
+
def self.message( input ) ## convert input to (binary) string
|
100
|
+
if debug?
|
101
|
+
input_type = if input.is_a?( String )
|
102
|
+
"#{input.class.name}/#{input.encoding}"
|
103
|
+
else
|
104
|
+
input.class.name
|
105
|
+
end
|
106
|
+
puts " input: #{input} (#{input_type})"
|
107
|
+
end
|
108
|
+
|
109
|
+
message = if input.is_a?( Integer ) ## assume byte if single (unsigned) integer
|
110
|
+
raise ArgumentError, "expected unsigned byte (0-255) - got #{input} (0x#{input.to_s(16)}) - can't pack negative number; sorry" if input < 0
|
111
|
+
## note: pack - H (String) => hex string (high nibble first)
|
112
|
+
## todo/check: is there a better way to convert integer number to (binary) string!!!
|
113
|
+
[input.to_s(16)].pack('H*')
|
114
|
+
else ## assume (binary) string
|
115
|
+
input
|
116
|
+
end
|
117
|
+
|
118
|
+
if debug?
|
119
|
+
bytes = message.bytes
|
120
|
+
bin = bytes.map {|byte| byte.to_s(2).rjust(8, "0")}.join( ' ' )
|
121
|
+
hex = bytes.map {|byte| byte.to_s(16).rjust(2, "0")}.join( ' ' )
|
122
|
+
puts " #{pluralize( bytes.size, 'byte')}: #{bytes.inspect}"
|
123
|
+
puts " binary: #{bin}"
|
124
|
+
puts " hex: #{hex}"
|
125
|
+
end
|
126
|
+
|
127
|
+
message
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.pluralize( count, noun )
|
131
|
+
count == 1 ? "#{count} #{noun}" : "#{count} #{noun}s"
|
132
|
+
end
|
133
|
+
|
134
|
+
end # module Metal
|
135
|
+
end # module Crypto
|
data/lib/crypto-lite/sign_rsa.rb
CHANGED
@@ -1,29 +1,29 @@
|
|
1
|
-
module Crypto
|
2
|
-
|
3
|
-
|
4
|
-
module RSA
|
5
|
-
def self.generate_keys ## todo/check: add a generate alias - why? why not?
|
6
|
-
key_pair = OpenSSL::PKey::RSA.new( 2048 )
|
7
|
-
private_key = key_pair.export
|
8
|
-
public_key = key_pair.public_key.export
|
9
|
-
|
10
|
-
[private_key, public_key]
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
def self.sign( plaintext, private_key )
|
15
|
-
private_key = OpenSSL::PKey::RSA.new( private_key ) ## note: convert/wrap into to obj from exported text format
|
16
|
-
Base64.encode64( private_key.private_encrypt( plaintext ))
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.decrypt( ciphertext, public_key )
|
20
|
-
public_key = OpenSSL::PKey::RSA.new( public_key ) ## note: convert/wrap into to obj from exported text format
|
21
|
-
public_key.public_decrypt( Base64.decode64( ciphertext ))
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
def self.valid_signature?( plaintext, ciphertext, public_key )
|
26
|
-
plaintext == decrypt( ciphertext, public_key )
|
27
|
-
end
|
28
|
-
end # module RSA
|
29
|
-
end # module Crypto
|
1
|
+
module Crypto
|
2
|
+
|
3
|
+
|
4
|
+
module RSA
|
5
|
+
def self.generate_keys ## todo/check: add a generate alias - why? why not?
|
6
|
+
key_pair = OpenSSL::PKey::RSA.new( 2048 )
|
7
|
+
private_key = key_pair.export
|
8
|
+
public_key = key_pair.public_key.export
|
9
|
+
|
10
|
+
[private_key, public_key]
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def self.sign( plaintext, private_key )
|
15
|
+
private_key = OpenSSL::PKey::RSA.new( private_key ) ## note: convert/wrap into to obj from exported text format
|
16
|
+
Base64.encode64( private_key.private_encrypt( plaintext ))
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.decrypt( ciphertext, public_key )
|
20
|
+
public_key = OpenSSL::PKey::RSA.new( public_key ) ## note: convert/wrap into to obj from exported text format
|
21
|
+
public_key.public_decrypt( Base64.decode64( ciphertext ))
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self.valid_signature?( plaintext, ciphertext, public_key )
|
26
|
+
plaintext == decrypt( ciphertext, public_key )
|
27
|
+
end
|
28
|
+
end # module RSA
|
29
|
+
end # module Crypto
|
data/lib/crypto-lite/version.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
|
2
|
-
module CryptoLite
|
3
|
-
|
4
|
-
MAJOR = 0
|
5
|
-
MINOR =
|
6
|
-
PATCH =
|
7
|
-
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
8
|
-
|
9
|
-
def self.version
|
10
|
-
VERSION
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.banner
|
14
|
-
"crypto-lite/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.root
|
18
|
-
File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
|
19
|
-
end
|
20
|
-
|
21
|
-
end # module CryptoLite
|
22
|
-
|
23
|
-
|
1
|
+
|
2
|
+
module CryptoLite
|
3
|
+
|
4
|
+
MAJOR = 0
|
5
|
+
MINOR = 3
|
6
|
+
PATCH = 1
|
7
|
+
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
8
|
+
|
9
|
+
def self.version
|
10
|
+
VERSION
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.banner
|
14
|
+
"crypto-lite/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.root
|
18
|
+
File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
|
19
|
+
end
|
20
|
+
|
21
|
+
end # module CryptoLite
|
22
|
+
|
23
|
+
|