crypto-lite 0.1.0 → 0.3.0

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.
data/Rakefile CHANGED
@@ -19,6 +19,10 @@ Hoe.spec 'crypto-lite' do
19
19
  self.history_file = 'CHANGELOG.md'
20
20
 
21
21
  self.extra_deps = [
22
+ ['digest-sha3-patched'],
23
+ ['base32-alphabets'],
24
+ ['base58-alphabets'],
25
+ ['elliptic'],
22
26
  ]
23
27
 
24
28
  self.licenses = ['Public Domain']
@@ -3,112 +3,145 @@ require 'digest'
3
3
  require 'base64'
4
4
  require 'openssl'
5
5
 
6
+ ## 3rd party gems
7
+ require 'digest/sha3' # e.g. keccak (original submission/proposal NOT official sha3)
8
+ ## see https://rubygems.org/gems/digest-sha3-patched
9
+ ## https://github.com/teamhedge/digest-sha3-ruby
10
+
11
+ ## our own 3rd party (2nd party?)
12
+ require 'base32-alphabets'
13
+ require 'base58-alphabets'
14
+ require 'elliptic'
15
+
16
+
6
17
 
7
18
  ## our own code
8
19
  require 'crypto-lite/version' # note: let version always go first
20
+ require 'crypto-lite/config'
21
+ require 'crypto-lite/metal'
22
+
9
23
 
10
24
 
11
25
 
12
26
  module Crypto
13
27
 
14
- def self.sha256bin( input, engine=nil ) ## todo/check: add alias sha256b or such to - why? why not?
15
- input_type = if input.is_a?( String )
16
- "#{input.class.name}/#{input.encoding}"
17
- else
18
- input.class.name
19
- end
20
- puts " input: #{input} (#{input_type})"
21
-
22
- message = if input.is_a?( Integer ) ## assume byte if single (unsigned) integer
23
- raise ArgumentError, "expected unsigned byte (0-255) - got #{input} (0x#{input.to_s(16)}) - can't pack negative number; sorry" if input < 0
24
- ## note: pack - H (String) => hex string (high nibble first)
25
- ## todo/check: is there a better way to convert integer number to (binary) string!!!
26
- [input.to_s(16)].pack('H*')
27
- else ## assume (binary) string
28
- input
29
- end
30
-
31
-
32
- bytes = message.bytes
33
- bin = bytes.map {|byte| byte.to_s(2).rjust(8, "0")}.join( ' ' )
34
- hex = bytes.map {|byte| byte.to_s(16).rjust(2, "0")}.join( ' ' )
35
- puts " #{pluralize( bytes.size, 'byte')}: #{bytes.inspect}"
36
- puts " binary: #{bin}"
37
- puts " hex: #{hex}"
38
-
39
- if engine && ['openssl'].include?( engine.to_s.downcase )
40
- puts " engine: #{engine}"
41
- digest = OpenSSL::Digest::SHA256.new
42
- digest.update( message )
43
- digest.digest
44
- else ## use "built-in" hash function from digest module
45
- Digest::SHA256.digest( message )
46
- end
28
+ def self.base58( *args, **kwargs )
29
+ input = args_to_input( args, kwargs )
30
+ Metal.base58bin( input )
47
31
  end
48
32
 
49
- def self.sha256( input, engine=nil )
50
- sha256bin( input, engine ).unpack( 'H*' )[0]
33
+ def self.base58check( *args, **kwargs )
34
+ input = args_to_input( args, kwargs )
35
+ Metal.base58bin_check( input )
51
36
  end
52
37
 
53
38
 
54
- def self.sha256hex( input, engine=nil )
55
- ## convenience helper - lets you pass in hex string
39
+ ########################
40
+ # (secure) hash functions
41
+
42
+ def self.keccak256( *args, **kwargs )
43
+ input = args_to_input( args, kwargs )
44
+ Metal.keccak256bin( input ).unpack( 'H*' )[0]
45
+ end
56
46
 
57
- ## check if input starts with 0x or 0X if yes - (auto-)cut off!!!!!
58
- if input.start_with?( '0x') || input.start_with?( '0X' )
59
- input = input[2..-1]
60
- end
61
47
 
62
- raise ArgumentError, "expected hex string (0-9a-f) - got >#{input}< - can't pack string; sorry" if input.downcase =~ /[^0-9a-f]/
63
- sha256( [input].pack( 'H*' ), engine )
48
+ def self.rmd160( *args, **kwargs )
49
+ input = args_to_input( args, kwargs )
50
+ Metal.rmd160bin( input ).unpack( 'H*' )[0]
64
51
  end
65
52
 
53
+ ## add alias RIPEMD160 - why? why not?
54
+ class << self
55
+ alias_method :ripemd160, :rmd160
56
+ end
66
57
 
67
58
 
68
- def self.pluralize( count, noun )
69
- count == 1 ? "#{count} #{noun}" : "#{count} #{noun}s"
59
+ def self.sha256( *args, **kwargs )
60
+ input = args_to_input( args, kwargs )
61
+ engine = kwargs[:engine]
62
+ Metal.sha256bin( input, engine ).unpack( 'H*' )[0]
63
+ end
64
+
65
+ def self.sha3_256( *args, **kwargs )
66
+ input = args_to_input( args, kwargs )
67
+ Metal.sha3_256bin( input ).unpack( 'H*' )[0]
70
68
  end
71
69
 
72
70
 
73
71
 
74
- module RSA
75
- def self.generate_keys ## todo/check: add a generate alias - why? why not?
76
- key_pair = OpenSSL::PKey::RSA.new( 2048 )
77
- private_key = key_pair.export
78
- public_key = key_pair.public_key.export
72
+ def self.hash160( *args, **kwargs )
73
+ input = args_to_input( args, kwargs )
74
+ Metal.hash160bin( input ).unpack( 'H*' )[0]
75
+ end
79
76
 
80
- [private_key, public_key]
77
+ def self.hash256( *args, **kwargs )
78
+ input = args_to_input( args, kwargs )
79
+ Metal.hash256bin( input ).unpack( 'H*' )[0]
81
80
  end
82
81
 
83
82
 
84
- def self.sign( plaintext, private_key )
85
- private_key = OpenSSL::PKey::RSA.new( private_key ) ## note: convert/wrap into to obj from exported text format
86
- Base64.encode64( private_key.private_encrypt( plaintext ))
83
+
84
+ ########
85
+ # more helpers
86
+ ## check if it is a hex (string)
87
+ ## - allow optiona 0x or 0X and allow abcdef and ABCDEF
88
+ HEX_RE = /\A(?:0x)?[0-9a-f]+\z/i
89
+
90
+ def self.args_to_input( args, kwargs )
91
+ if kwargs[:hex]
92
+ hex = kwargs[:hex]
93
+ raise ArgumentError, "expected hex string (0-9a-f) - got >#{hex}< - can't pack string; sorry" unless hex =~ HEX_RE
94
+
95
+ hex = strip0x( hex ) ## check if input starts with 0x or 0X if yes - (auto-)cut off!!!!!
96
+ [hex].pack( 'H*' )
97
+ else ## assume single input arg for now
98
+ input = args[0]
99
+ input = hex_to_bin_automagic( input ) ## add automagic hex (string) to bin (string) check - why? why not?
100
+ input
101
+ end
87
102
  end
88
103
 
89
- def self.decrypt( ciphertext, public_key )
90
- public_key = OpenSSL::PKey::RSA.new( public_key ) ## note: convert/wrap into to obj from exported text format
91
- public_key.public_decrypt( Base64.decode64( ciphertext ))
104
+ def self.hex_to_bin_automagic( input )
105
+ ## todo/check/fix: add configure setting to turn off automagic - why? why not?
106
+ if input.is_a?( String ) && input =~ HEX_RE
107
+ if input[0,2] == '0x' || input[0,2] == '0X'
108
+ ## starting with 0x or 0X always assume hex string for now - why? why not?
109
+ input = input[2..-1]
110
+ [input].pack( 'H*' )
111
+ elsif input.size >= 10
112
+ ## note: hex heuristic!!
113
+ ## for now assumes string MUST have more than 10 digits to qualify!!!
114
+ [input].pack( 'H*' )
115
+ else
116
+ input ## pass through as is!!! (e.g. a, abc, etc.)
117
+ end
118
+ else
119
+ input ## pass through as is
120
+ end
92
121
  end
93
122
 
94
123
 
95
- def self.valid_signature?( plaintext, ciphertext, public_key )
96
- plaintext == decrypt( ciphertext, public_key )
124
+ def self.strip0x( str ) ## todo/check: add alias e.g. strip_hex_prefix or such - why? why not?
125
+ (str[0,2] == '0x' || str[0,2] == '0X') ? str[2..-1] : str
97
126
  end
98
- end # module RSA
99
- end # module Crypto
100
127
 
128
+ # def self.hex_to_bin( str )
129
+ # str = strip0x( str ) ## check if input starts with 0x or 0X if yes - (auto-)cut off!!!!!
130
+ # [str].pack( 'H*' )
131
+ # end
101
132
 
133
+ end # module Crypto
102
134
 
103
135
 
104
- ## add convenience "top-level" helpers
105
- def sha256( input, engine=nil ) Crypto.sha256( input, engine ); end
106
- def sha256hex( input, engine=nil ) Crypto.sha256hex( input, engine ); end
107
136
 
108
- RSA = Crypto::RSA
109
137
 
138
+ require 'crypto-lite/helper'
139
+ include CryptoHelper # add convenience "top-level" / global helpers
110
140
 
111
141
 
142
+ require 'crypto-lite/sign_rsa'
143
+ RSA = Crypto::RSA
144
+
112
145
 
113
146
 
114
147
 
@@ -0,0 +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
+
@@ -0,0 +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
+
@@ -0,0 +1,128 @@
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::SHA3.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
+ def self.sha3_256bin( input )
60
+ message = message( input ) ## "normalize" / convert to (binary) string
61
+
62
+ digest = OpenSSL::Digest.new( 'SHA3-256' )
63
+ digest.update( message )
64
+ digest.digest
65
+ end
66
+
67
+ ####
68
+ ## helper
69
+ # def hash160( pubkey )
70
+ # binary = [pubkey].pack( "H*" ) # Convert to binary first before hashing
71
+ # sha256 = Digest::SHA256.digest( binary )
72
+ # ripemd160 = Digest::RMD160.digest( sha256 )
73
+ # ripemd160.unpack( "H*" )[0] # Convert back to hex
74
+ # end
75
+
76
+ def self.hash160bin( input )
77
+ message = message( input ) ## "normalize" / convert to (binary) string
78
+
79
+ rmd160bin(sha256bin( message ))
80
+ end
81
+
82
+
83
+ def self.hash256bin( input )
84
+ message = message( input ) ## "normalize" / convert to (binary) string
85
+
86
+ sha256bin(sha256bin( message ))
87
+ end
88
+
89
+
90
+ ##############################
91
+ ## helpers
92
+ def self.message( input ) ## convert input to (binary) string
93
+ if debug?
94
+ input_type = if input.is_a?( String )
95
+ "#{input.class.name}/#{input.encoding}"
96
+ else
97
+ input.class.name
98
+ end
99
+ puts " input: #{input} (#{input_type})"
100
+ end
101
+
102
+ message = if input.is_a?( Integer ) ## assume byte if single (unsigned) integer
103
+ raise ArgumentError, "expected unsigned byte (0-255) - got #{input} (0x#{input.to_s(16)}) - can't pack negative number; sorry" if input < 0
104
+ ## note: pack - H (String) => hex string (high nibble first)
105
+ ## todo/check: is there a better way to convert integer number to (binary) string!!!
106
+ [input.to_s(16)].pack('H*')
107
+ else ## assume (binary) string
108
+ input
109
+ end
110
+
111
+ if debug?
112
+ bytes = message.bytes
113
+ bin = bytes.map {|byte| byte.to_s(2).rjust(8, "0")}.join( ' ' )
114
+ hex = bytes.map {|byte| byte.to_s(16).rjust(2, "0")}.join( ' ' )
115
+ puts " #{pluralize( bytes.size, 'byte')}: #{bytes.inspect}"
116
+ puts " binary: #{bin}"
117
+ puts " hex: #{hex}"
118
+ end
119
+
120
+ message
121
+ end
122
+
123
+ def self.pluralize( count, noun )
124
+ count == 1 ? "#{count} #{noun}" : "#{count} #{noun}s"
125
+ end
126
+
127
+ end # module Metal
128
+ end # module Crypto
@@ -0,0 +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
@@ -2,7 +2,7 @@
2
2
  module CryptoLite
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 1
5
+ MINOR = 3
6
6
  PATCH = 0
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8