ccrypto-ruby 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.release_history.yml +4 -0
- data/Dockerfile.dockerun +29 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +57 -24
- data/ccrypto-ruby.gemspec +4 -1
- data/gen_ecc_const.rb +13 -0
- data/lib/ccrypto/provider.rb +52 -44
- data/lib/ccrypto/ruby/ecc_const.rb +82 -0
- data/lib/ccrypto/ruby/engines/cipher_engine.rb +214 -55
- data/lib/ccrypto/ruby/engines/digest_engine.rb +39 -17
- data/lib/ccrypto/ruby/engines/ecc_engine.rb +89 -14
- data/lib/ccrypto/ruby/engines/ed25519_engine.rb +84 -0
- data/lib/ccrypto/ruby/engines/pkcs7_engine.rb +1 -0
- data/lib/ccrypto/ruby/engines/rsa_engine.rb +9 -0
- data/lib/ccrypto/ruby/engines/x25519_engine.rb +65 -0
- data/lib/ccrypto/ruby/engines/x509_csr_engine.rb +128 -0
- data/lib/ccrypto/ruby/engines/x509_engine.rb +208 -8
- data/lib/ccrypto/ruby/ext/x509_csr.rb +153 -0
- data/lib/ccrypto/ruby/keybundle_store/pem_store.rb +3 -3
- data/lib/ccrypto/ruby/keybundle_store/pkcs12.rb +6 -5
- data/lib/ccrypto/ruby/version.rb +1 -1
- data/lib/ccrypto/ruby.rb +1 -0
- metadata +26 -4
@@ -11,73 +11,194 @@ module Ccrypto
|
|
11
11
|
|
12
12
|
teLogger_tag :r_cipher_eng
|
13
13
|
|
14
|
+
NotAbleToFigureOutOnOpenSSLv2 = [
|
15
|
+
"aes-128-cbc-hmac-sha1",
|
16
|
+
"aes-256-cbc-hmac-sha1",
|
17
|
+
"aes-128-cbc-hmac-sha256",
|
18
|
+
"aes-256-cbc-hmac-sha256",
|
19
|
+
"aes-128-ccm",
|
20
|
+
"aes-192-ccm",
|
21
|
+
"aes-256-ccm",
|
22
|
+
"aria-128-ccm",
|
23
|
+
"aria-192-ccm",
|
24
|
+
"aria-256-ccm",
|
25
|
+
"rc4-hmac-md5"
|
26
|
+
]
|
27
|
+
|
28
|
+
#CherryPick = [
|
29
|
+
# "aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
|
30
|
+
# "aes-128-gcm", "aes-192-gcm", "aes-256-gcm",
|
31
|
+
# "aria-128-cbc","aria-192-cbc", "aria-256-cbc",
|
32
|
+
# "aria-128-gcm","aria-192-gcm", "aria-256-gcm",
|
33
|
+
# "camellia-128-cbc","camellia-192-cbc", "camellia-256-cbc",
|
34
|
+
# "camellia-128-ctr","camellia-192-ctr", "camellia-256-ctr",
|
35
|
+
# "chacha20-poly1305"
|
36
|
+
#]
|
37
|
+
|
14
38
|
def self.supported_ciphers
|
15
39
|
if @sCipher.nil?
|
16
|
-
@
|
40
|
+
@sCipherStr = []
|
41
|
+
@sCipher = []
|
42
|
+
|
43
|
+
OpenSSL::Cipher.ciphers.each do |c|
|
44
|
+
|
45
|
+
teLogger.debug "found cipher : #{c}"
|
46
|
+
next if c =~ /^id-\w*/
|
47
|
+
teLogger.debug "loading cipher : #{c}"
|
48
|
+
|
49
|
+
if OpenSSL::VERSION < "3.0.0" and NotAbleToFigureOutOnOpenSSLv2.include?(c)
|
50
|
+
teLogger.debug "Running on OpenSSL < v3. Skipping cipher #{c}"
|
51
|
+
next
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
co = OpenSSL::Cipher.new(c)
|
56
|
+
#teLogger.debug "Algo #{c} : authenticated? #{co.authenticated?}"
|
57
|
+
#p co.methods.sort
|
58
|
+
cc = c.split("-")
|
59
|
+
@sCipherStr << c
|
60
|
+
|
61
|
+
algo = cc.first
|
62
|
+
exclusion = ["chacha20","sm4"]
|
63
|
+
# to find string like aes128, aes256 from openssl
|
64
|
+
if not exclusion.include?(algo) and (algo =~ /[0-9]/) != nil
|
65
|
+
# match the one before the numbers
|
66
|
+
algo = $`
|
67
|
+
end
|
68
|
+
|
69
|
+
native = { algo_str: c }
|
70
|
+
keysize = co.key_len*8
|
71
|
+
opts = { keysize: keysize, ivLength: co.iv_len, authMode: co.authenticated? }
|
72
|
+
|
73
|
+
if c.downcase =~ /\w*(gcm|ecb|cbc|cfb|cfb1|cfb8|ofb|ctr|ccm|xts|wrap|poly1305)/
|
74
|
+
teLogger.debug "Mode extraction : #{$&} / #{$'}"
|
75
|
+
# after the match
|
76
|
+
if not_empty?($')
|
77
|
+
opts[:mode] = "#{$&}#{$'}"
|
78
|
+
else
|
79
|
+
# the match
|
80
|
+
opts[:mode] = $&.downcase
|
81
|
+
end
|
82
|
+
else
|
83
|
+
# no mode defined. Seems defaulted to cbc according to document
|
84
|
+
if co.authenticated?
|
85
|
+
opts[:mode] = "gcm"
|
86
|
+
else
|
87
|
+
opts[:mode] = "cbc"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
teLogger.debug "Mode : #{opts[:mode]}"
|
92
|
+
if opts[:mode] == :xts.to_s
|
93
|
+
opts[:min_input_length] = 16
|
94
|
+
elsif opts[:mode] == "cbc-hmac-sha1"
|
95
|
+
opts[:min_input_length] = 16
|
96
|
+
opts[:mandatory_block_size] = 16
|
97
|
+
elsif opts[:mode] == "cbc-hmac-sha256"
|
98
|
+
opts[:min_input_length] = 32
|
99
|
+
opts[:mandatory_block_size] = 32
|
100
|
+
elsif opts[:mode] == "wrap"
|
101
|
+
case algo
|
102
|
+
when "aes"
|
103
|
+
case keysize
|
104
|
+
when 128
|
105
|
+
opts[:min_input_length] = 16
|
106
|
+
opts[:mandatory_block_size] = 8
|
107
|
+
when 192
|
108
|
+
opts[:min_input_length] = 24
|
109
|
+
opts[:mandatory_block_size] = 8
|
110
|
+
when 256
|
111
|
+
opts[:min_input_length] = 32
|
112
|
+
opts[:mandatory_block_size] = 8
|
113
|
+
end
|
114
|
+
when "des3", "des"
|
115
|
+
opts[:min_input_length] = 8
|
116
|
+
opts[:mandatory_block_size] = 8
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
conf = Ccrypto::CipherConfig.new(algo, opts)
|
121
|
+
conf.native_config = native
|
122
|
+
|
123
|
+
Ccrypto::SupportedCipherList.instance.register(conf)
|
124
|
+
|
125
|
+
@sCipher << conf
|
126
|
+
rescue OpenSSL::Cipher::CipherError => ex
|
127
|
+
teLogger.debug "Algo '#{c}' hit error : #{ex.message}"
|
128
|
+
end
|
129
|
+
end
|
17
130
|
end
|
18
131
|
|
19
132
|
@sCipher
|
20
133
|
|
21
134
|
end
|
22
135
|
|
136
|
+
def self.get_cipher(algo, keysize = nil, mode = nil)
|
137
|
+
supported_ciphers if Ccrypto::SupportedCipherList.instance.algo_count == 0
|
138
|
+
|
139
|
+
if is_empty?(algo)
|
140
|
+
[]
|
141
|
+
elsif is_empty?(keysize) and is_empty?(mode)
|
142
|
+
teLogger.debug "get_cipher algo #{algo} only"
|
143
|
+
Ccrypto::SupportedCipherList.instance.find_algo(algo)
|
144
|
+
elsif is_empty?(mode) and not_empty?(keysize)
|
145
|
+
teLogger.debug "get_cipher algo #{algo} keysize #{keysize}"
|
146
|
+
Ccrypto::SupportedCipherList.instance.find_algo_keysize(algo, keysize)
|
147
|
+
elsif not_empty?(mode) and is_empty?(keysize)
|
148
|
+
teLogger.debug "get_cipher algo #{algo} mode #{mode}"
|
149
|
+
Ccrypto::SupportedCipherList.instance.find_algo_mode(algo, mode)
|
150
|
+
elsif not_empty?(keysize) and not_empty?(mode)
|
151
|
+
teLogger.debug "get_cipher #{algo}/#{keysize}/#{mode}"
|
152
|
+
Ccrypto::SupportedCipherList.instance.find_algo_keysize_mode(algo, keysize, mode)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
class << self
|
156
|
+
alias_method :get_cipher_config, :get_cipher
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.supported_cipher_list
|
160
|
+
supported_ciphers if Ccrypto::SupportedCipherList.instance.algo_count == 0
|
161
|
+
Ccrypto::SupportedCipherList.instance
|
162
|
+
end
|
163
|
+
|
23
164
|
def self.is_supported_cipher?(c)
|
24
165
|
case c
|
25
166
|
when String
|
26
|
-
supported_ciphers.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
true
|
32
|
-
rescue Exception => ex
|
167
|
+
supported_ciphers if @sCipherStr.nil?
|
168
|
+
|
169
|
+
if not @sCipherStr.nil?
|
170
|
+
@sCipherStr.include?(C)
|
171
|
+
else
|
33
172
|
false
|
34
173
|
end
|
174
|
+
when Ccrypto::CipherConfig
|
175
|
+
@sCipher.include?(c)
|
35
176
|
else
|
36
177
|
raise Ccrypto::CipherEngineException, "Unsupported input #{c} to check supported cipher"
|
37
178
|
end
|
38
179
|
end
|
39
180
|
|
40
181
|
def self.to_openssl_spec(spec)
|
41
|
-
res = []
|
42
182
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
183
|
+
case spec
|
184
|
+
when Ccrypto::CipherConfig
|
185
|
+
spec.native_config[:algo_str]
|
186
|
+
#@sCipher[spec]
|
47
187
|
else
|
48
|
-
|
188
|
+
raise Ccrypto::Error, "Unknown spec #{spec.inspect}"
|
49
189
|
end
|
50
190
|
|
51
|
-
|
52
|
-
|
53
|
-
res << spec.mode
|
54
|
-
|
55
|
-
teLogger.debug "to_openssl_spec #{res}"
|
56
|
-
|
57
|
-
res.join("-")
|
58
|
-
|
59
|
-
end
|
191
|
+
end # self.to_openssl_spec(spec)
|
60
192
|
|
61
193
|
def initialize(*args, &block)
|
194
|
+
|
62
195
|
@spec = args.first
|
63
196
|
|
64
|
-
#
|
65
|
-
teLogger.debug "Cipher spec : #{@spec}"
|
197
|
+
raise Ccrypto::CipherEngineException, "Not supported cipher spec #{@spec.class}" if not @spec.is_a?(Ccrypto::CipherConfig)
|
66
198
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
# @cipher = OpenSSL::Cipher.new(@spec)
|
71
|
-
when Ccrypto::CipherEngineConfig
|
72
|
-
@cipher = OpenSSL::Cipher.new(@spec.provider_config)
|
73
|
-
when Ccrypto::DirectCipherConfig
|
74
|
-
@cipher = OpenSSL::Cipher.new(self.class.to_openssl_spec(@spec))
|
75
|
-
else
|
76
|
-
raise Ccrypto::CipherEngineException, "Not supported cipher init type #{@spec.class}"
|
77
|
-
end
|
78
|
-
rescue OpenSSL::Cipher::CipherError, RuntimeError => ex
|
79
|
-
raise Ccrypto::CipherEngineException, ex
|
80
|
-
end
|
199
|
+
teLogger.debug "Cipher spec : #{@spec} (Native algo : #{@spec.native_config[:algo_str]})"
|
200
|
+
|
201
|
+
@cipher = OpenSSL::Cipher.new(@spec.native_config[:algo_str])
|
81
202
|
|
82
203
|
case @spec.cipherOps
|
83
204
|
when :encrypt, :enc
|
@@ -94,11 +215,11 @@ module Ccrypto
|
|
94
215
|
if @spec.has_iv?
|
95
216
|
teLogger.debug "IV from spec"
|
96
217
|
@cipher.iv = @spec.iv
|
97
|
-
teLogger.debug "IV : #{to_hex(@spec.iv)}"
|
218
|
+
teLogger.debug "IV : #{to_hex(@spec.iv)} / #{@spec.iv.length}"
|
98
219
|
else
|
99
220
|
teLogger.debug "Generate random IV"
|
100
221
|
@spec.iv = @cipher.random_iv
|
101
|
-
teLogger.debug "IV : #{to_hex(@spec.iv)}"
|
222
|
+
teLogger.debug "IV : #{to_hex(@spec.iv)} / #{@spec.iv.length}"
|
102
223
|
end
|
103
224
|
|
104
225
|
|
@@ -118,25 +239,50 @@ module Ccrypto
|
|
118
239
|
end
|
119
240
|
|
120
241
|
|
121
|
-
if @spec.is_mode?(:
|
122
|
-
|
123
|
-
|
124
|
-
|
242
|
+
if @spec.is_mode?(:ccm)
|
243
|
+
#if not_empty?(@spec.auth_data)
|
244
|
+
if @spec.is_encrypt_cipher_mode?
|
245
|
+
teLogger.debug "Setting ccm plaintext data length (ccm mode) [#{@spec.plaintext_length}]"
|
246
|
+
@cipher.ccm_data_len = @spec.plaintext_length
|
247
|
+
teLogger.debug "Setting auth data (ccm mode)"
|
248
|
+
@cipher.auth_data = @spec.auth_data.nil? ? "" : @spec.auth_data
|
249
|
+
teLogger.debug "Setting auth tag len (ccm mode)"
|
250
|
+
@cipher.auth_tag_len = 12
|
251
|
+
elsif @spec.is_decrypt_cipher_mode?
|
252
|
+
teLogger.debug "Setting ccm cipher data length (ccm mode) #{@spec.ciphertext_length}"
|
253
|
+
@cipher.ccm_data_len = @spec.ciphertext_length
|
254
|
+
teLogger.debug "Setting auth data (ccm mode)"
|
255
|
+
@cipher.auth_data = @spec.auth_data.nil? ? "" : @spec.auth_data
|
256
|
+
teLogger.debug "Setting auth tag (ccm mode)"
|
257
|
+
@cipher.auth_tag = @spec.auth_tag
|
258
|
+
end
|
259
|
+
#end
|
260
|
+
|
261
|
+
|
262
|
+
elsif @spec.is_auth_mode_cipher?
|
263
|
+
|
264
|
+
if not_empty?(@spec.auth_data) and not ((@spec.is_mode?("cbc-hmac-sha1") or @spec.is_mode?("cbc-hmac-sha256")))
|
265
|
+
teLogger.debug "Setting auth data (generic mode)"
|
125
266
|
@cipher.auth_data = @spec.auth_data
|
126
|
-
end
|
127
267
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
268
|
+
if @spec.is_decrypt_cipher_mode?
|
269
|
+
teLogger.debug "Setting auth tag (generic mode)"
|
270
|
+
raise CipherEngineException, "Tag length of 16 bytes is expected" if @spec.auth_tag.bytesize != 16 and @spec.is_mode?(:gcm)
|
271
|
+
@cipher.auth_tag = @spec.auth_tag
|
272
|
+
end
|
132
273
|
end
|
133
274
|
|
134
275
|
end
|
135
276
|
|
136
|
-
|
277
|
+
|
278
|
+
end # initialize
|
137
279
|
|
138
280
|
def update(val)
|
139
|
-
|
281
|
+
if not_empty?(val)
|
282
|
+
res = @cipher.update(val)
|
283
|
+
teLogger.debug "(update) Written #{val.length} bytes"
|
284
|
+
res
|
285
|
+
end
|
140
286
|
end
|
141
287
|
|
142
288
|
def final(val = nil)
|
@@ -145,17 +291,22 @@ module Ccrypto
|
|
145
291
|
begin
|
146
292
|
|
147
293
|
if not_empty?(val)
|
148
|
-
|
294
|
+
teLogger.debug "(final) Written #{val.length} bytes"
|
295
|
+
res << @cipher.update(val)
|
149
296
|
end
|
150
297
|
|
151
298
|
res << @cipher.final
|
299
|
+
teLogger.debug "(final) cipher finalized"
|
152
300
|
|
153
301
|
rescue Exception => ex
|
302
|
+
teLogger.error self
|
303
|
+
teLogger.error ex.backtrace.join("\n")
|
154
304
|
raise CipherEngineException, ex
|
155
305
|
end
|
156
306
|
|
157
|
-
if @spec.is_mode?(:gcm)
|
158
|
-
|
307
|
+
#if @spec.is_mode?(:gcm)
|
308
|
+
if @spec.is_auth_mode_cipher? and @spec.is_encrypt_cipher_mode?
|
309
|
+
@spec.auth_tag = @cipher.auth_tag
|
159
310
|
end
|
160
311
|
|
161
312
|
res.join
|
@@ -165,6 +316,14 @@ module Ccrypto
|
|
165
316
|
@cipher.reset
|
166
317
|
end
|
167
318
|
|
319
|
+
def method_missing(mtd, *args, &block)
|
320
|
+
if not @cipher.nil?
|
321
|
+
@cipher.send(mtd, *args, &block)
|
322
|
+
else
|
323
|
+
super
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
168
327
|
end
|
169
328
|
end
|
170
329
|
end
|
@@ -27,9 +27,10 @@ module Ccrypto
|
|
27
27
|
Ccrypto::SHAKE256.provider_info("shake256"),
|
28
28
|
Ccrypto::BLAKE2b512.provider_info("BLAKE2b512"),
|
29
29
|
Ccrypto::BLAKE2s256.provider_info("BLAKE2s256"),
|
30
|
-
Ccrypto::SM3.provider_info("SM3")
|
31
|
-
|
32
|
-
Ccrypto::
|
30
|
+
Ccrypto::SM3.provider_info("SM3")
|
31
|
+
# deprecated starting OpenSSL v3.0
|
32
|
+
#Ccrypto::RIPEMD160.provider_info("RIPEMD160"),
|
33
|
+
#Ccrypto::WHIRLPOOL.provider_info("whirlpool")
|
33
34
|
]
|
34
35
|
|
35
36
|
|
@@ -38,14 +39,21 @@ module Ccrypto
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def self.is_supported?(eng)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
|
43
|
+
case eng
|
44
|
+
when Ccrypto::DigestConfig
|
45
|
+
SupportedDigest.include?(eng)
|
46
|
+
when String, Symbol
|
47
|
+
if eng.is_a?(Symbol)
|
48
|
+
engineKeys.include?(eng)
|
49
|
+
else
|
50
|
+
e = eng.gsub("-","_")
|
51
|
+
engineKeys.include?(e.to_sym)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
raise DigestEngineException, "Unknown engine '#{eng}'"
|
46
55
|
end
|
47
56
|
|
48
|
-
res
|
49
57
|
end
|
50
58
|
|
51
59
|
def self.instance(*args, &block)
|
@@ -59,16 +67,30 @@ module Ccrypto
|
|
59
67
|
end
|
60
68
|
|
61
69
|
def self.digest(key)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
70
|
+
|
71
|
+
case key
|
72
|
+
when Ccrypto::DigestConfig
|
73
|
+
DigestEngine.new(OpenSSL::Digest.new(key.provider_config))
|
74
|
+
when String, Symbol
|
75
|
+
if key.is_a?(Symbol)
|
76
|
+
res = engineKeys[key]
|
77
|
+
else
|
78
|
+
k = key.gsub("-","_")
|
79
|
+
res = engineKeys[k.to_sym]
|
80
|
+
end
|
81
|
+
|
82
|
+
if is_empty?(res)
|
83
|
+
teLogger.debug "No digest available for #{key}"
|
84
|
+
raise DigestEngineException, "Not supported digest engine #{key}"
|
85
|
+
else
|
86
|
+
teLogger.debug "Found digest #{key.to_sym}"
|
87
|
+
DigestEngine.new(OpenSSL::Digest.new(res.provider_config))
|
88
|
+
end
|
89
|
+
|
67
90
|
else
|
68
|
-
|
69
|
-
DigestEngine.new(OpenSSL::Digest.new(res.provider_config))
|
91
|
+
raise DigestEngineException, "Not supported digest engine #{key}"
|
70
92
|
end
|
71
|
-
|
93
|
+
|
72
94
|
end
|
73
95
|
|
74
96
|
def self.engineKeys
|
@@ -1,5 +1,7 @@
|
|
1
1
|
|
2
2
|
require 'openssl'
|
3
|
+
if OpenSSL::VERSION < "3.0.0"
|
4
|
+
|
3
5
|
module PKeyPatch
|
4
6
|
def to_pem; public_key.to_pem end
|
5
7
|
def to_der; public_key.to_der end
|
@@ -13,20 +15,51 @@ module PKeyPatch
|
|
13
15
|
end
|
14
16
|
OpenSSL::PKey::EC::Point.prepend PKeyPatch
|
15
17
|
|
18
|
+
end
|
19
|
+
|
16
20
|
require_relative '../keybundle_store/pkcs12'
|
17
21
|
require_relative '../keybundle_store/pem_store'
|
22
|
+
require_relative '../ecc_const'
|
18
23
|
|
19
24
|
module Ccrypto
|
20
25
|
module Ruby
|
21
26
|
|
22
27
|
class ECCPublicKey < Ccrypto::ECCPublicKey
|
23
|
-
|
28
|
+
|
24
29
|
def to_bin
|
25
|
-
|
30
|
+
if OpenSSL::VERSION < "3.0.0"
|
31
|
+
@native_pubKey.to_der
|
32
|
+
else
|
33
|
+
const = ECCConst[@native_pubKey.group.curve_name]
|
34
|
+
# At 01 April 2023
|
35
|
+
# at Ruby 3.2.1/OpenSSL gem 3.1.0/OpenSSL 3.0.2
|
36
|
+
# The gem has bug that the encoding is incorrect
|
37
|
+
OpenSSL::ASN1::Sequence.new([
|
38
|
+
OpenSSL::ASN1::ObjectId.new("2.8.8.128.0"),
|
39
|
+
OpenSSL::ASN1::Integer.new(0x0100),
|
40
|
+
OpenSSL::ASN1::Integer.new(const),
|
41
|
+
OpenSSL::ASN1::BitString.new(@native_pubKey.to_bn)
|
42
|
+
]).to_der
|
43
|
+
end
|
26
44
|
end
|
27
45
|
|
28
46
|
def self.to_key(bin)
|
29
|
-
|
47
|
+
if OpenSSL::VERSION > "3.0.0"
|
48
|
+
ek = OpenSSL::PKey::EC.new(bin)
|
49
|
+
else
|
50
|
+
seq = OpenSSL::ASN1Object.decode(bin).value
|
51
|
+
envp = ASN1Object.decode(seq[0]).value
|
52
|
+
raise KeypairEngineException, "Not ECC public key" if envp != "2.8.8.8.128.0"
|
53
|
+
ver = ASN1Object.decode(seq[1]).value
|
54
|
+
raise KeypairEngineException, "Unsupported version" if ver != 0x0100
|
55
|
+
cv = ASN1Object.decode(seq[2]).value
|
56
|
+
curve = ECCConst.invert[cv]
|
57
|
+
raise KeypairEngineException, "Unknown curve '#{curve}'" if curve.nil?
|
58
|
+
kv = ASN1Object.decode(seq[3]).value
|
59
|
+
|
60
|
+
ek = OpenSSL::PKey::EC::Point.new(OpenSSL::PKey::EC::Group.new(curve), kv)
|
61
|
+
end
|
62
|
+
|
30
63
|
ECCPublicKey.new(ek)
|
31
64
|
end
|
32
65
|
|
@@ -49,7 +82,11 @@ module Ccrypto
|
|
49
82
|
|
50
83
|
def public_key
|
51
84
|
if @pubKey.nil?
|
52
|
-
|
85
|
+
#if OpenSSL::VERSION < "3.0.0"
|
86
|
+
@pubKey = ECCPublicKey.new(@nativeKeypair.public_key)
|
87
|
+
#else
|
88
|
+
# @pubKey = ECCPublicKey.new(@nativeKeypair.public_key.to_bn)
|
89
|
+
#end
|
53
90
|
end
|
54
91
|
@pubKey
|
55
92
|
end
|
@@ -65,12 +102,21 @@ module Ccrypto
|
|
65
102
|
tkey = pubKey
|
66
103
|
when Ccrypto::ECCPublicKey
|
67
104
|
tkey = pubKey.native_pubKey
|
68
|
-
|
105
|
+
if OpenSSL::VERSION < "3.0.0"
|
106
|
+
tkey = tkey.public_key if not tkey.is_a?(OpenSSL::PKey::EC::Point)
|
107
|
+
else
|
108
|
+
tkey = OpenSSL::PKey::EC.new(tkey)
|
109
|
+
end
|
69
110
|
else
|
70
111
|
raise KeypairEngineException, "Unknown public key type #{pubKey.class}"
|
71
112
|
end
|
72
113
|
|
73
|
-
|
114
|
+
if OpenSSL::VERSION < "3.0.0"
|
115
|
+
raise KeypairEngineException, "OpenSSL::PKey::EC::Point is required. Given #{tkey.inspect}" if not tkey.is_a?(OpenSSL::PKey::EC::Point)
|
116
|
+
else
|
117
|
+
raise KeypairEngineException, "OpenSSL::PKey::EC is required. Given #{tkey.inspect}" if not tkey.is_a?(OpenSSL::PKey::EC)
|
118
|
+
end
|
119
|
+
|
74
120
|
@nativeKeypair.dh_compute_key(tkey)
|
75
121
|
end
|
76
122
|
|
@@ -171,9 +217,24 @@ module Ccrypto
|
|
171
217
|
|
172
218
|
teLogger_tag :r_ecc
|
173
219
|
|
220
|
+
NotAbleToFigureOutOnOpenSSLv2 = [
|
221
|
+
"Oakley-EC2N-3",
|
222
|
+
"Oakley-EC2N-4"
|
223
|
+
]
|
224
|
+
|
174
225
|
def self.supported_curves
|
175
226
|
if @curves.nil?
|
176
|
-
@curves =
|
227
|
+
@curves = []
|
228
|
+
OpenSSL::PKey::EC.builtin_curves.sort.map { |c|
|
229
|
+
|
230
|
+
next if c[0] =~ /^wap/ or NotAbleToFigureOutOnOpenSSLv2.include?(c[0])
|
231
|
+
|
232
|
+
if c[0] == "prime256v1"
|
233
|
+
@curves << Ccrypto::ECCConfig.new(c[0], Ccrypto::KeypairConfig::Algo_Active, true)
|
234
|
+
else
|
235
|
+
@curves << Ccrypto::ECCConfig.new(c[0])
|
236
|
+
end
|
237
|
+
}
|
177
238
|
end
|
178
239
|
@curves
|
179
240
|
end
|
@@ -203,14 +264,28 @@ module Ccrypto
|
|
203
264
|
end
|
204
265
|
|
205
266
|
def self.verify(pubKey, val, sign)
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
uPubKey
|
210
|
-
|
267
|
+
if OpenSSL::VERSION > "3.0.0"
|
268
|
+
p pubKey.native_pubKey
|
269
|
+
p pubKey.native_pubKey.methods.sort
|
270
|
+
uPubKey = pubKey.native_pubKey
|
271
|
+
if uPubKey.is_a?(OpenSSL::PKey::EC::Point)
|
272
|
+
res = uPubKey.dsa_verify_asn1(val, sign)
|
273
|
+
res
|
274
|
+
else
|
275
|
+
raise KeypairEngineException, "Unsupported public key type '#{uPubKey.class}'"
|
276
|
+
end
|
211
277
|
|
212
|
-
|
213
|
-
|
278
|
+
else
|
279
|
+
# OpenSSL v2 - Ruby 2.x
|
280
|
+
uPubKey = pubKey.native_pubKey
|
281
|
+
if pubKey.native_pubKey.is_a?(OpenSSL::PKey::EC::Point)
|
282
|
+
uPubKey = OpenSSL::PKey::EC.new(uPubKey.group)
|
283
|
+
uPubKey.public_key = pubKey.native_pubKey
|
284
|
+
end
|
285
|
+
|
286
|
+
res = uPubKey.dsa_verify_asn1(val, sign)
|
287
|
+
res
|
288
|
+
end
|
214
289
|
end
|
215
290
|
|
216
291
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
require 'ed25519'
|
3
|
+
|
4
|
+
module Ccrypto
|
5
|
+
module Ruby
|
6
|
+
|
7
|
+
class ED25519Exception < StandardError; end
|
8
|
+
|
9
|
+
class ED25519PublicKey < Ccrypto::ED25519PublicKey
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class ED25519KeyBundle
|
14
|
+
include Ccrypto::ED25519KeyBundle
|
15
|
+
|
16
|
+
include TR::CondUtils
|
17
|
+
|
18
|
+
include TeLogger::TeLogHelper
|
19
|
+
teLogger_tag :ed25519_kb
|
20
|
+
|
21
|
+
def initialize(kp)
|
22
|
+
@nativeKeypair = kp
|
23
|
+
end
|
24
|
+
|
25
|
+
def public_key
|
26
|
+
if @pubKey.nil?
|
27
|
+
@pubKey = ED25519PublicKey.new(@nativeKeypair.verify_key)
|
28
|
+
end
|
29
|
+
@pubKey
|
30
|
+
end
|
31
|
+
|
32
|
+
def private_key
|
33
|
+
ED25519PrivateKey.new(@nativeKeypair)
|
34
|
+
end
|
35
|
+
|
36
|
+
end # ED25519KeyBundle
|
37
|
+
|
38
|
+
class ED25519Engine
|
39
|
+
include TeLogger::TeLogHelper
|
40
|
+
teLogger_tag :ed25519_eng
|
41
|
+
|
42
|
+
def initialize(*args, &block)
|
43
|
+
@config = args.first
|
44
|
+
teLogger.debug "Config : #{@config}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_keypair(&block)
|
48
|
+
teLogger.debug "Generating ED25519 keypair"
|
49
|
+
ED25519KeyBundle.new(Ed25519::SigningKey.generate)
|
50
|
+
end
|
51
|
+
|
52
|
+
def sign(val)
|
53
|
+
|
54
|
+
raise KeypairEngineException, "Keypair is required" if @config.keypair.nil?
|
55
|
+
raise KeypairEngineException, "ED25519 keypair is required" if not @config.keypair.is_a?(ED25519KeyBundle)
|
56
|
+
|
57
|
+
kp = @config.keypair
|
58
|
+
|
59
|
+
res = kp.nativeKeypair.sign(val)
|
60
|
+
teLogger.debug "Data of length #{val.length} signed using ED25519"
|
61
|
+
|
62
|
+
res
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.verify(pubKey, val, sign)
|
67
|
+
case pubKey
|
68
|
+
when Ccrypto::ED25519PublicKey
|
69
|
+
uPubKey = pubKey
|
70
|
+
else
|
71
|
+
raise KeypairEngineException, "Unsupported public key '#{pubKey.class}' for ED25519 operation"
|
72
|
+
end
|
73
|
+
|
74
|
+
begin
|
75
|
+
uPubKey.verify(sign, val)
|
76
|
+
rescue Ed25519::VerifyError => ex
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|