ccrypto-ruby 0.1.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +64 -0
  5. data/README.md +149 -0
  6. data/Rakefile +10 -0
  7. data/bin/console +15 -0
  8. data/bin/setup +8 -0
  9. data/ccrypto-ruby.gemspec +45 -0
  10. data/lib/ccrypto/provider.rb +175 -0
  11. data/lib/ccrypto/ruby/data_conversion.rb +68 -0
  12. data/lib/ccrypto/ruby/engines/asn1_engine.rb +110 -0
  13. data/lib/ccrypto/ruby/engines/asn1_object.rb +19 -0
  14. data/lib/ccrypto/ruby/engines/cipher_engine.rb +170 -0
  15. data/lib/ccrypto/ruby/engines/compression_engine.rb +61 -0
  16. data/lib/ccrypto/ruby/engines/data_conversion_engine.rb +9 -0
  17. data/lib/ccrypto/ruby/engines/decompression_engine.rb +70 -0
  18. data/lib/ccrypto/ruby/engines/digest_engine.rb +127 -0
  19. data/lib/ccrypto/ruby/engines/ecc_engine.rb +218 -0
  20. data/lib/ccrypto/ruby/engines/hkdf_engine.rb +54 -0
  21. data/lib/ccrypto/ruby/engines/hmac_engine.rb +53 -0
  22. data/lib/ccrypto/ruby/engines/pbkdf2_engine.rb +69 -0
  23. data/lib/ccrypto/ruby/engines/pkcs7_engine.rb +179 -0
  24. data/lib/ccrypto/ruby/engines/rsa_engine.rb +300 -0
  25. data/lib/ccrypto/ruby/engines/scrypt_engine.rb +34 -0
  26. data/lib/ccrypto/ruby/engines/secret_key_engine.rb +18 -0
  27. data/lib/ccrypto/ruby/engines/secret_sharing_engine.rb +331 -0
  28. data/lib/ccrypto/ruby/engines/secure_random_engine.rb +34 -0
  29. data/lib/ccrypto/ruby/engines/x509_engine.rb +213 -0
  30. data/lib/ccrypto/ruby/ext/secret_key.rb +24 -0
  31. data/lib/ccrypto/ruby/ext/x509_cert.rb +24 -0
  32. data/lib/ccrypto/ruby/keybundle_store/pem_store.rb +73 -0
  33. data/lib/ccrypto/ruby/keybundle_store/pkcs12.rb +111 -0
  34. data/lib/ccrypto/ruby/utils/comparator.rb +15 -0
  35. data/lib/ccrypto/ruby/utils/memory_buffer.rb +63 -0
  36. data/lib/ccrypto/ruby/utils/native_helper.rb +17 -0
  37. data/lib/ccrypto/ruby/version.rb +7 -0
  38. data/lib/ccrypto/ruby.rb +25 -0
  39. metadata +136 -0
@@ -0,0 +1,170 @@
1
+
2
+ require_relative '../data_conversion'
3
+
4
+ module Ccrypto
5
+ module Ruby
6
+ class CipherEngine
7
+ include TR::CondUtils
8
+ include DataConversion
9
+
10
+ include TeLogger::TeLogHelper
11
+
12
+ teLogger_tag :r_cipher_eng
13
+
14
+ def self.supported_ciphers
15
+ if @sCipher.nil?
16
+ @sCipher = OpenSSL::Cipher.ciphers
17
+ end
18
+
19
+ @sCipher
20
+
21
+ end
22
+
23
+ def self.is_supported_cipher?(c)
24
+ case c
25
+ when String
26
+ supported_ciphers.include?(c)
27
+ when Hash
28
+ spec = to_openssl_spec(c)
29
+ begin
30
+ OpenSSL::Cipher.new(spec)
31
+ true
32
+ rescue Exception => ex
33
+ false
34
+ end
35
+ else
36
+ raise Ccrypto::CipherEngineException, "Unsupported input #{c} to check supported cipher"
37
+ end
38
+ end
39
+
40
+ def self.to_openssl_spec(spec)
41
+ res = []
42
+
43
+ teLogger.debug "to_openssl_spec #{spec}"
44
+ case spec.algo
45
+ when :blowfish
46
+ res << "bf"
47
+ else
48
+ res << spec.algo
49
+ end
50
+
51
+ res << spec.keysize if not_empty?(spec.keysize) and spec.keysize.to_i > 0 and not spec.is_algo?(:chacha20) and not spec.is_algo?(:seed) and not spec.is_algo?(:sm4) and not spec.is_algo?(:blowfish)
52
+
53
+ res << spec.mode
54
+
55
+ teLogger.debug "to_openssl_spec #{res}"
56
+
57
+ res.join("-")
58
+
59
+ end
60
+
61
+ def initialize(*args, &block)
62
+ @spec = args.first
63
+
64
+ #teLogger = TteLogger.new
65
+ teLogger.debug "Cipher spec : #{@spec}"
66
+
67
+ begin
68
+ case @spec
69
+ #when String
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
81
+
82
+ case @spec.cipherOps
83
+ when :encrypt, :enc
84
+ teLogger.debug "Operation encrypt"
85
+ @cipher.encrypt
86
+ when :decrypt, :dec
87
+ teLogger.debug "Operation decrypt"
88
+ @cipher.decrypt
89
+ else
90
+ raise Ccrypto::CipherEngineException, "Cipher operation (encrypt/decrypt) must be given"
91
+ end
92
+
93
+
94
+ if @spec.has_iv?
95
+ teLogger.debug "IV from spec"
96
+ @cipher.iv = @spec.iv
97
+ teLogger.debug "IV : #{to_hex(@spec.iv)}"
98
+ else
99
+ teLogger.debug "Generate random IV"
100
+ @spec.iv = @cipher.random_iv
101
+ teLogger.debug "IV : #{to_hex(@spec.iv)}"
102
+ end
103
+
104
+
105
+ if @spec.has_key?
106
+ teLogger.debug "Key from spec"
107
+ case @spec.key
108
+ when Ccrypto::SecretKey
109
+ @cipher.key = @spec.key.to_bin
110
+ when String
111
+ @cipher.key = @spec.key
112
+ else
113
+ raise Ccrypto::CipherEngineException, "Unknown key type for processing #{@spec.key}"
114
+ end
115
+ else
116
+ teLogger.debug "Generate random Key"
117
+ @spec.key = @cipher.random_key
118
+ end
119
+
120
+
121
+ if @spec.is_mode?(:gcm)
122
+
123
+ if not_empty?(@spec.auth_data)
124
+ teLogger.debug "Setting auth data"
125
+ @cipher.auth_data = @spec.auth_data
126
+ end
127
+
128
+ if not_empty?(@spec.auth_tag)
129
+ raise CipherEngineException, "Tag length of 16 bytes is expected" if @spec.auth_tag.bytesize != 16
130
+ teLogger.debug "Setting auth tag"
131
+ @cipher.auth_tag = @spec.auth_tag
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ def update(val)
139
+ @cipher.update(val)
140
+ end
141
+
142
+ def final(val = nil)
143
+ res = []
144
+
145
+ begin
146
+
147
+ if not_empty?(val)
148
+ res << @cipher.update(val)
149
+ end
150
+
151
+ res << @cipher.final
152
+
153
+ rescue Exception => ex
154
+ raise CipherEngineException, ex
155
+ end
156
+
157
+ if @spec.is_mode?(:gcm) and @spec.is_encrypt_cipher_mode?
158
+ @spec.auth_tag = @cipher.auth_tag
159
+ end
160
+
161
+ res.join
162
+ end
163
+
164
+ def reset
165
+ @cipher.reset
166
+ end
167
+
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,61 @@
1
+
2
+
3
+ module Ccrypto
4
+ module Ruby
5
+ class Compression
6
+
7
+ include TeLogger::TeLogHelper
8
+
9
+ teLogger_tag :r_compression
10
+
11
+ def initialize(*args, &block)
12
+
13
+ @config = args.first
14
+ raise CompressionError, "Compress Config is expected. Given #{@config}" if not @config.is_a?(Ccrypto::CompressionConfig)
15
+
16
+ if block
17
+
18
+ outPath = block.call(:out_path)
19
+ if is_empty?(outPath)
20
+ outFile = block.call(:out_file)
21
+ raise CompressionError, "Given out_file required to support write() call" if not outFile.respond_to?(:write)
22
+ @out = outFile
23
+ else
24
+ @out = Tempfile.new(SecureRandom.hex(24))
25
+ end
26
+
27
+ @intBufSize = block.call(:int_buf_size) || 102400
28
+
29
+ else
30
+ @intBufSize = 102400
31
+
32
+ end
33
+
34
+ case @config.level
35
+ when :best_compression
36
+ teLogger.debug "Best compression"
37
+ @eng = Zlib::Deflate.new(Zlib::BEST_COMPRESSION)
38
+ when :best_speed
39
+ teLogger.debug "Best compression"
40
+ @eng = Zlib::Deflate.new(Zlib::BEST_SPEED)
41
+ when :no_compression
42
+ teLogger.debug "No compression"
43
+ @eng = Zlib::Deflate.new(Zlib::NO_COMPRESSION)
44
+ else
45
+ teLogger.debug "Default compression"
46
+ @eng = Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION)
47
+ end
48
+
49
+ end
50
+
51
+ def update(val)
52
+ @eng.deflate(val, Zlib::SYNC_FLUSH)
53
+ end
54
+
55
+ def final
56
+
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+
2
+
3
+ module Ccrypto
4
+ module Ruby
5
+ class DataConversionEngine
6
+ extend DataConversion
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,70 @@
1
+
2
+
3
+ module Ccrypto
4
+ module Ruby
5
+ class Decompression
6
+
7
+ def initialize(*args, &block)
8
+ if block
9
+
10
+ outPath = block.call(:out_path)
11
+ if is_empty?(outPath)
12
+ outFile = block.call(:out_file)
13
+ raise CompressionError, "Given out_file required to support write() call" if not outFile.respond_to?(:write)
14
+ @out = outFile
15
+ else
16
+ @out = Tempfile.new(SecureRandom.hex(16))
17
+ end
18
+
19
+ @intBufSize = block.call(:int_buf_size) || 102400
20
+
21
+ else
22
+ @intBufSize = 102400
23
+
24
+ end
25
+
26
+ @eng = Zlib::Inflate.new
27
+
28
+ #@in = Tempfile.new(SecureRandom.hex(16))
29
+ end
30
+
31
+ def update(val)
32
+ begin
33
+ @eng.inflate(val)
34
+ rescue Zlib::DataError
35
+ end
36
+ end
37
+
38
+ def final
39
+
40
+ #eng = Zlib::Inflate.new
41
+
42
+ #@in.seek(0)
43
+
44
+ #intBuf = false
45
+ #if @out.nil?
46
+ # @out = StringIO.new
47
+ # intBuf = true
48
+ #end
49
+
50
+ #chunk = 102400
51
+ #loop do
52
+ # compressed = @in.read(chunk)
53
+ # res = eng.inflate(compressed) #, Zlib::SYNC_FLUSH)
54
+ # @out.write(res)
55
+
56
+ # break if @in.eof?
57
+ #end
58
+
59
+ #if intBuf
60
+ # @out.string
61
+ #else
62
+ # @out
63
+ #end
64
+
65
+ end
66
+
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,127 @@
1
+
2
+ require_relative '../data_conversion'
3
+
4
+ module Ccrypto
5
+ module Ruby
6
+ class DigestEngine
7
+ include Ccrypto::Ruby::DataConversion
8
+ include TR::CondUtils
9
+
10
+ include TeLogger::TeLogHelper
11
+
12
+ teLogger_tag :r_digest
13
+
14
+ SupportedDigest = [
15
+ Ccrypto::SHA1.provider_info("sha1"),
16
+ Ccrypto::SHA224.provider_info("sha224"),
17
+ Ccrypto::SHA256.provider_info("sha256"),
18
+ Ccrypto::SHA384.provider_info("sha384"),
19
+ Ccrypto::SHA512.provider_info("sha512"),
20
+ Ccrypto::SHA512_224.provider_info("sha512-224"),
21
+ Ccrypto::SHA512_256.provider_info("sha512-256"),
22
+ Ccrypto::SHA3_224.provider_info("sha3-224"),
23
+ Ccrypto::SHA3_256.provider_info("sha3-256"),
24
+ Ccrypto::SHA3_384.provider_info("sha3-384"),
25
+ Ccrypto::SHA3_512.provider_info("sha3-512"),
26
+ Ccrypto::SHAKE128.provider_info("shake128"),
27
+ Ccrypto::SHAKE256.provider_info("shake256"),
28
+ Ccrypto::BLAKE2b512.provider_info("BLAKE2b512"),
29
+ Ccrypto::BLAKE2s256.provider_info("BLAKE2s256"),
30
+ Ccrypto::SM3.provider_info("SM3"),
31
+ Ccrypto::RIPEMD160.provider_info("RIPEMD160"),
32
+ Ccrypto::WHIRLPOOL.provider_info("whirlpool")
33
+ ]
34
+
35
+
36
+ def self.supported
37
+ SupportedDigest
38
+ end
39
+
40
+ def self.is_supported?(eng)
41
+ res = supported.include?(eng)
42
+ begin
43
+ res = digest(eng) if not res
44
+ rescue DigestEngineException => ex
45
+ res = false
46
+ end
47
+
48
+ res
49
+ end
50
+
51
+ def self.instance(*args, &block)
52
+ conf = args.first
53
+ if not_empty?(conf.provider_config)
54
+ teLogger.debug "Creating digest engine #{conf.provider_config}"
55
+ DigestEngine.new(OpenSSL::Digest.new(conf.provider_config))
56
+ else
57
+ raise DigestEngineException, "Given digest config #{conf.algo} does not have provider key mapping. Most likely this config is not supported by provider #{Ccrypto::Ruby::Provider.provider_name}"
58
+ end
59
+ end
60
+
61
+ def self.digest(key)
62
+
63
+ res = engineKeys[key]
64
+ if is_empty?(res)
65
+ teLogger.debug "No digest available for #{key}"
66
+ raise DigestEngineException, "Not supported digest engine #{key}"
67
+ else
68
+ teLogger.debug "Found digest #{key.to_sym}"
69
+ DigestEngine.new(OpenSSL::Digest.new(res.provider_config))
70
+ end
71
+
72
+ end
73
+
74
+ def self.engineKeys
75
+ if @engineKeys.nil?
76
+ @engineKeys = {}
77
+ supported.map do |e|
78
+ @engineKeys[e.algo.to_sym] = e
79
+ end
80
+ end
81
+ @engineKeys
82
+ end
83
+
84
+ def initialize(inst)
85
+ @inst = inst
86
+ end
87
+
88
+ def native_digest_engine
89
+ @inst
90
+ end
91
+ alias_method :native_instance, :native_digest_engine
92
+
93
+ def digest(val, output = :binary)
94
+ digest_update(val)
95
+ digest_final(output)
96
+ end
97
+
98
+ def digest_update(val)
99
+ case val
100
+ when MemoryBuffer
101
+ @inst.update(val.bytes)
102
+ else
103
+ @inst.update(val)
104
+ end
105
+ end
106
+
107
+ def digest_final(output = :binary)
108
+
109
+ res = @inst.digest
110
+ @inst.reset
111
+ case output
112
+ when :hex
113
+ to_hex(res)
114
+ when :b64
115
+ to_b64(res)
116
+ else
117
+ res
118
+ end
119
+ end
120
+
121
+ def reset
122
+ @inst.reset
123
+ end
124
+
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,218 @@
1
+
2
+ require 'openssl'
3
+ module PKeyPatch
4
+ def to_pem; public_key.to_pem end
5
+ def to_der; public_key.to_der end
6
+
7
+ #private
8
+ def public_key
9
+ key = ::OpenSSL::PKey::EC.new group
10
+ key.public_key = self
11
+ key
12
+ end
13
+ end
14
+ OpenSSL::PKey::EC::Point.prepend PKeyPatch
15
+
16
+ require_relative '../keybundle_store/pkcs12'
17
+ require_relative '../keybundle_store/pem_store'
18
+
19
+ module Ccrypto
20
+ module Ruby
21
+
22
+ class ECCPublicKey < Ccrypto::ECCPublicKey
23
+
24
+ def to_bin
25
+ @native_pubKey.to_der
26
+ end
27
+
28
+ def self.to_key(bin)
29
+ ek = OpenSSL::PKey::EC.new(bin)
30
+ ECCPublicKey.new(ek)
31
+ end
32
+
33
+ end
34
+
35
+ class ECCKeyBundle
36
+ include Ccrypto::ECCKeyBundle
37
+ include TR::CondUtils
38
+
39
+ include PKCS12Store
40
+ include PEMStore
41
+
42
+ include TeLogger::TeLogHelper
43
+
44
+ teLogger_tag :r_ecc_keybundle
45
+
46
+ def initialize(keypair)
47
+ @nativeKeypair = keypair
48
+ end
49
+
50
+ def public_key
51
+ if @pubKey.nil?
52
+ @pubKey = ECCPublicKey.new(@nativeKeypair.public_key)
53
+ end
54
+ @pubKey
55
+ end
56
+
57
+ def private_key
58
+ ECCPrivateKey.new(@nativeKeypair)
59
+ end
60
+
61
+ def derive_dh_shared_secret(pubKey)
62
+
63
+ case pubKey
64
+ when OpenSSL::PKey::EC::Point
65
+ tkey = pubKey
66
+ when Ccrypto::ECCPublicKey
67
+ tkey = pubKey.native_pubKey
68
+ tkey = tkey.public_key if not tkey.is_a?(OpenSSL::PKey::EC::Point)
69
+ else
70
+ raise KeypairEngineException, "Unknown public key type #{pubKey.class}"
71
+ end
72
+
73
+ raise KeypairEngineException, "OpenSSL::PKey::EC::Point is required. Given #{tkey.inspect}" if not tkey.is_a?(OpenSSL::PKey::EC::Point)
74
+ @nativeKeypair.dh_compute_key(tkey)
75
+ end
76
+
77
+ def is_public_key_equal?(pubKey)
78
+
79
+ case pubKey
80
+ when OpenSSL::PKey::EC
81
+ targetKey = pubKey
82
+ when ECCKeyBundle
83
+ targetKey = pubKey.public_key
84
+ when ECCPublicKey
85
+ targetKey = pubKey.native_pubKey
86
+ else
87
+ raise KeypairEngineException, "Unknown public key type #{pubKey.class}"
88
+ end
89
+
90
+ public_key.to_bin == targetKey.to_der
91
+ end
92
+
93
+ def to_storage(format, &block)
94
+ case format
95
+ when :pkcs12, :p12
96
+ to_pkcs12 do |key|
97
+ case key
98
+ when :keypair
99
+ @nativeKeypair
100
+ else
101
+ block.call(key) if block
102
+ end
103
+ end
104
+
105
+ when :pem
106
+ to_pem do |key|
107
+ case key
108
+ when :keypair
109
+ @nativeKeypair
110
+ else
111
+ block.call(key) if block
112
+ end
113
+ end
114
+
115
+ else
116
+ raise KeyBundleStorageException, "Unknown storage format #{format}"
117
+ end
118
+ end
119
+
120
+ def self.from_storage(bin, &block)
121
+ raise KeypairEngineException, "Given data to load is empty" if is_empty?(bin)
122
+
123
+ case bin
124
+ when String
125
+ begin
126
+ teLogger.debug "Given String to load from storage"
127
+ if is_pem?(bin)
128
+ self.from_pem(bin, &block)
129
+ else
130
+ # binary buffer
131
+ teLogger.debug "Given binary to load from storage"
132
+ self.from_pkcs12(bin,&block)
133
+ end
134
+ rescue Ccrypto::Ruby::PKCS12Store::PKCS12StoreException => ex
135
+ raise KeyBundleStorageException, ex
136
+ end
137
+ else
138
+ raise KeyBundleStorageException, "Unsupported input type #{bin}"
139
+ end
140
+
141
+ end
142
+
143
+ def equal?(kp)
144
+ if kp.respond_to?(:to_der)
145
+ @nativeKeypair.to_der == kp.to_der
146
+ else
147
+ @nativeKeypair == kp
148
+ #false
149
+ end
150
+ end
151
+
152
+ def method_missing(mtd, *args, &block)
153
+ if @nativeKeypair.respond_to?(mtd)
154
+ teLogger.debug "Sending to nativeKeypair #{mtd}"
155
+ @nativeKeypair.send(mtd,*args, &block)
156
+ else
157
+ super
158
+ end
159
+ end
160
+
161
+ def respond_to_missing?(mtd, *args, &block)
162
+ @nativeKeypair.respond_to?(mtd)
163
+ end
164
+
165
+ end
166
+
167
+ class ECCEngine
168
+ include TR::CondUtils
169
+
170
+ include TeLogger::TeLogHelper
171
+
172
+ teLogger_tag :r_ecc
173
+
174
+ def self.supported_curves
175
+ if @curves.nil?
176
+ @curves = OpenSSL::PKey::EC.builtin_curves.map { |c| Ccrypto::ECCConfig.new(c[0]) }
177
+ end
178
+ @curves
179
+ end
180
+
181
+ def initialize(*args, &block)
182
+ @config = args.first
183
+ raise KeypairEngineException, "1st parameter must be a #{Ccrypto::KeypairConfig.class} object" if not @config.is_a?(Ccrypto::KeypairConfig)
184
+ teLogger.debug "Config #{@config}"
185
+ end
186
+
187
+ def generate_keypair(&block)
188
+ teLogger.debug "Generating keypair of curve #{@config.curve}"
189
+ kp = OpenSSL::PKey::EC.generate(@config.curve.to_s)
190
+ #teLogger.debug "Generated keypair #{kp.inspect}"
191
+ ECCKeyBundle.new(kp)
192
+ end
193
+
194
+ def sign(val)
195
+ raise KeypairEngineException, "Keypair is required" if @config.keypair.nil?
196
+ raise KeypairEngineException, "ECC keypair is required" if not @config.keypair.is_a?(ECCKeyBundle)
197
+ kp = @config.keypair
198
+
199
+ res = kp.nativeKeypair.dsa_sign_asn1(val)
200
+ teLogger.debug "Data of length #{val.length} signed "
201
+
202
+ res
203
+ end
204
+
205
+ def self.verify(pubKey, val, sign)
206
+ uPubKey = pubKey.native_pubKey
207
+ if pubKey.native_pubKey.is_a?(OpenSSL::PKey::EC::Point)
208
+ uPubKey = OpenSSL::PKey::EC.new(uPubKey.group)
209
+ uPubKey.public_key = pubKey.native_pubKey
210
+ end
211
+
212
+ res = uPubKey.dsa_verify_asn1(val, sign)
213
+ res
214
+ end
215
+
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,54 @@
1
+
2
+ require_relative '../data_conversion'
3
+
4
+ module Ccrypto
5
+ module Ruby
6
+ class HKDFEngine
7
+ include DataConversion
8
+ include TR::CondUtils
9
+
10
+ def initialize(*args, &block)
11
+ @config = args.first
12
+
13
+ raise KDFEngineException, "KDF config is expected" if not @config.is_a?(Ccrypto::KDFConfig)
14
+ raise KDFEngineException, "Output bit length (outBitLength) value is not given or not a positive value (#{@config.outBitLength})" if is_empty?(@config.outBitLength) or @config.outBitLength <= 0
15
+
16
+
17
+ @config.salt = SecureRandom.random_bytes(16) if is_empty?(@config.salt)
18
+ end
19
+
20
+ def derive(input, output = :binary)
21
+
22
+ digest = init_digest(@config.digest)
23
+
24
+ @config.info = "" if @config.info.nil?
25
+
26
+ res = OpenSSL::KDF.hkdf(input, salt: @config.salt, info: @config.info, length: @config.outBitLength/8, hash: digest)
27
+
28
+ case output
29
+ when :hex
30
+ to_hex(res)
31
+ when :b64
32
+ to_b64(res)
33
+ else
34
+ res
35
+ end
36
+ end
37
+
38
+ private
39
+ def init_digest(algo)
40
+ if DigestEngine.is_supported?(algo)
41
+ conf = DigestEngine.engineKeys[algo]
42
+ if not_empty?(conf)
43
+ OpenSSL::Digest.new(conf.provider_config)
44
+ else
45
+ raise DigestEngineException, "Algo config '#{algo}' not found"
46
+ end
47
+ else
48
+ raise DigestEngineException, "Digest algo '#{algo}' is not supported"
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end