ccipher_box 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
1
+
2
+ require 'tempfile'
3
+
4
+ module CcipherBox
5
+ #
6
+ # Meant to encrypt large data
7
+ #
8
+ class EncryptionEngine
9
+ include TR::CondUtils
10
+
11
+ def initialize(*args)
12
+ @keys = args
13
+ end
14
+
15
+ def init(output)
16
+ raise CcipherBox::Error, "Output is mandatory" if output.nil?
17
+
18
+ @output = output
19
+ @baseMat = SecureRandom.random_bytes(256/8)
20
+ @sk = CcipherFactory::SymKeyGenerator.derive(:aes, 256) do |ops|
21
+ case ops
22
+ when :password
23
+ @baseMat
24
+ end
25
+ end
26
+
27
+ @intOut = Tempfile.new
28
+
29
+ @cipher = CcipherFactory::SymKeyCipher.encryptor
30
+ @cipher.output(@intOut)
31
+ @cipher.key = @sk
32
+ @cipher.encrypt_init
33
+ end
34
+
35
+ def update(data)
36
+ @cipher.encrypt_update(data)
37
+ end
38
+
39
+ def final(&block)
40
+ header = @cipher.encrypt_final
41
+
42
+ st = BinStruct.instance.struct(:ccipherbox_cipher)
43
+ st.keyConfig = @sk.encoded
44
+
45
+ encBaseMat = []
46
+ @keys.each do |k|
47
+ #logger.debug "Encrypt with key #{k.name}"
48
+ encBaseMat << k.encrypt(@baseMat)
49
+ end
50
+ st.baseMaterial = encBaseMat
51
+
52
+ st.cipherConfig = header
53
+ aheader = st.encoded
54
+
55
+ @output.write(aheader)
56
+
57
+ @intOut.rewind
58
+ while not @intOut.eof?
59
+ @output.write(@intOut.read)
60
+ end
61
+
62
+ @intOut.close
63
+ @intOut.delete
64
+
65
+ aheader
66
+ end
67
+
68
+ def logger
69
+ if @logger.nil?
70
+ @logger = TeLogger::Tlogger.new
71
+ @logger.tag = :enc_eng
72
+ end
73
+ @logger
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,147 @@
1
+
2
+ require 'ccipher_factory'
3
+
4
+ module CcipherBox
5
+
6
+ # Abstraction of deriving a key from a given base key (baseMat)
7
+ # and the Key Derivation Function (KDF).
8
+ # With the two inputs, a derive key would be provided via dkey() API
9
+ class Keybox
10
+ include TR::CondUtils
11
+
12
+ attr_accessor :outBitLength, :kdf, :baseMat
13
+
14
+ def initialize(kdfEngine = nil)
15
+ raise KeyboxException, "Instance of KDF engine (CcipherFactory::KDF::KDFEngine) required" if not_empty?(kdfEngine) and not kdfEngine.is_a?(CcipherFactory::KDF::KDFEngine)
16
+
17
+ @kdfEng = kdfEngine
18
+ if not @kdfEng.nil?
19
+ @kdfOut = MemBuf.new
20
+ @kdfEng.output(@kdfOut)
21
+ end
22
+ end
23
+
24
+ def self.from_encoded(bin)
25
+
26
+ raise KeyboxException, "Given binary is empty" if is_empty?(bin)
27
+
28
+ st = BinStruct.instance.struct_from_bin(bin)
29
+ kdfEng = CcipherFactory::KDF.from_encoded(st.kdfConfig)
30
+ Keybox.new(kdfEng)
31
+
32
+ end
33
+
34
+ def baseMat=(val)
35
+
36
+ case val
37
+ when CcipherFactory::SymKey
38
+ # taking the key value only will still can get
39
+ # derived key correctly even if the key type and size
40
+ # has changed. For example change from Blowfish to AES
41
+ # will not resulted in a different value
42
+ #
43
+ # Decided to take only the raw key value to allow
44
+ # flexibility at upper application to change from a
45
+ # symmetric algo to another, since the objective of this
46
+ # class is just to get a derived raw key, not SymKey object
47
+ @baseMat = val.key
48
+
49
+ # taking the encoded value however from symkey
50
+ # will make the base key sensitive to meta data changes,
51
+ # from key type, size, KDF config changes all will affect
52
+ # final result
53
+ #@baseMat = val.encoded
54
+ #when String
55
+ # test for String will means need a mechanism to test for Java::byte[]
56
+ # which is not yet there
57
+ # @baseMat = val
58
+ else
59
+ @baseMat = val
60
+ #raise KeyboxException, "Unsupported base material type '#{val}'"
61
+ end
62
+
63
+ @dkeyVal = nil
64
+ end
65
+
66
+ def dkey
67
+
68
+ if @dkeyVal.nil?
69
+
70
+ raise KeyboxException, "BaseMat not given" if is_empty?(@baseMat)
71
+
72
+ kdfEng.derive_update(@baseMat)
73
+
74
+ @kdfConfig = kdfEng.derive_final
75
+
76
+ @dkeyVal = @kdfOut.bytes.clone
77
+ @kdfOut.dispose
78
+
79
+ end
80
+
81
+ @dkeyVal
82
+
83
+ end
84
+
85
+ def encoded
86
+ st = BinStruct.instance.struct(:keybox)
87
+ st.kdfConfig = @kdfConfig
88
+ st.encoded
89
+ end
90
+
91
+ private
92
+ def kdfEng
93
+
94
+ if @kdfEng.nil?
95
+
96
+ raise KeyboxException, "OutBitLength is not given" if is_empty?(@outBitLength)
97
+
98
+ if @kdf.nil?
99
+
100
+ # random KDF
101
+ rEng = [:scrypt, :hkdf, :pbkdf2].sample
102
+ logger.debug "Random KDF : #{rEng}"
103
+ @kdfEng = CcipherFactory::KDF.instance(rEng)
104
+ @kdfEng.outByteLength = @outBitLength/8
105
+
106
+ @kdfOut = MemBuf.new
107
+ @kdfEng.output(@kdfOut)
108
+ @kdfEng.derive_init
109
+
110
+ else
111
+
112
+ case @kdf
113
+ when :scrypt, :hkdf, :pbkdf2
114
+ logger.debug "Given KDF : #{@kdf}"
115
+ @kdfEng = CcipherFactory::KDF.instance(@kdf)
116
+ @kdfEng.outByteLength = @outBitLength/8
117
+ else
118
+ raise KeyboxException, "Unknown KDF engine '#{@kdf}' requested"
119
+ end
120
+
121
+ @kdfOut = MemBuf.new
122
+ @kdfEng.output(@kdfOut)
123
+ @kdfEng.derive_init
124
+
125
+ end # @kdf.nil?
126
+
127
+ end # kdfEng.nil?
128
+
129
+ @kdfEng
130
+
131
+ end
132
+
133
+ def self.logger
134
+ if @logger.nil?
135
+ @logger = TeLogger::Tlogger.new
136
+ @logger.tag = :keybox
137
+ end
138
+ @logger
139
+ end
140
+
141
+ def logger
142
+ self.class.logger
143
+ end
144
+
145
+ end
146
+
147
+ end
@@ -0,0 +1,394 @@
1
+
2
+
3
+ module CcipherBox
4
+
5
+ #
6
+ # Actual data encryption key that is encrypted in memory
7
+ # This class represents a single encryption key
8
+ #
9
+ class MemKey
10
+ include TR::CondUtils
11
+
12
+ class MemKeyException < Exception; end
13
+
14
+ attr_accessor :name, :ring
15
+
16
+ def initialize(ring, key, name = nil)
17
+ @dataConv = Ccrypto::UtilFactory.instance(:data_conversion)
18
+ @dataComp = Ccrypto::UtilFactory.instance(:comparator)
19
+ self.ring = ring
20
+ self.key = key
21
+ self.name = name
22
+
23
+ @activateCount = 0
24
+ end
25
+
26
+ def key=(val)
27
+ generateKeyID(val)
28
+ @key = mem_seal(val)
29
+ end
30
+
31
+ def keyID
32
+ @keyID
33
+ end
34
+
35
+ # keyID is stored as binary in the structure
36
+ # but was used as Hex value at application level
37
+ def self.to_KeyID(val)
38
+ Ccrypto::UtilFactory.instance(:data_conversion).to_hex(val)
39
+ end
40
+
41
+ #
42
+ # Encrypt by this key is generally consider key wrap
43
+ # therefore data is not expected to be very big.
44
+ #
45
+ # This is not really going to encrypt the actual data
46
+ # since there might have multiple key involved during
47
+ # data protection
48
+ #
49
+ def encrypt(data, &block)
50
+
51
+ if block
52
+ algo = block.call(:symkey_algo)
53
+ keysize = block.call(:symkey_length)
54
+ mode = block.call(:symkey_mode)
55
+ output = block.call(:output)
56
+ end
57
+
58
+ algo = :aes if is_empty?(algo)
59
+ keysize = 256 if is_empty?(keysize)
60
+ mode = :gcm if is_empty?(mode)
61
+ output = :binary if is_empty?(output)
62
+
63
+ key = mem_unseal(@key)
64
+ case key
65
+ when CcipherFactory::SymKey
66
+ sk = key
67
+ else
68
+ sk = CcipherFactory::SymKeyGenerator.derive(algo, keysize) do |k|
69
+ case k
70
+ when :password
71
+ key
72
+ end
73
+ end
74
+ end
75
+
76
+ @activateCount += 1
77
+ # refresh the in-memory encryption
78
+ if @activateCount > 5
79
+ logger.debug "Seal refresh during encrypt ops"
80
+ @key = mem_seal(@key)
81
+ end
82
+
83
+
84
+ membuf = MemBuf.new
85
+ enc = CcipherFactory::SymKeyCipher.att_encryptor
86
+ enc.key = sk
87
+ enc.mode = mode
88
+
89
+ enc.output(membuf)
90
+ enc.att_encrypt_init
91
+ enc.att_encrypt_update(data)
92
+ enc.att_encrypt_final
93
+
94
+ encOut = membuf.bytes.clone
95
+ membuf.dispose
96
+
97
+ st = BinStruct.instance.struct(:ccipherbox_keywrap)
98
+ st.keyid = @dataConv.from_hex(self.keyID)
99
+ sk.attach_mode
100
+ st.keyConfig = sk.encoded
101
+ st.cipher = encOut
102
+ res = st.encoded
103
+
104
+ case output
105
+ when :hex
106
+ @dataConv.to_hex(res)
107
+ when :b64
108
+ @dataConv.to_b64(res)
109
+ else
110
+ res
111
+ end
112
+
113
+ end
114
+
115
+ def decrypt(bin, &block)
116
+
117
+ st = BinStruct.instance.struct_from_bin(bin)
118
+ st.keyid = @dataConv.to_hex(st.keyid)
119
+
120
+ raise MemKeyException, "Given data to decrypt is not cipher envelope" if CBTag.value_constant(st.oid) != :ccipherbox_keywrap
121
+ raise MemKeyException, "Give cipher envelope is not meant for this key. Current key ID '#{self.keyID}' and key ID inside envelope '#{st.keyid}'" if not @dataComp.is_equals?(self.keyID, st.keyid)
122
+
123
+ case @key
124
+ when CcipherFactory::SymKey
125
+ sk = @key
126
+ else
127
+ sk = CcipherFactory::SymKey.from_encoded(st.keyConfig) do |k|
128
+ case k
129
+ when :password
130
+ mem_unseal(@key)
131
+ end
132
+ end
133
+ end
134
+
135
+ @activateCount += 1
136
+ # refresh the in-memory protection
137
+ if @activateCount > 5
138
+ logger.debug "Seal refresh during decrypt ops"
139
+ @key = mem_seal(@key)
140
+ end
141
+
142
+ membuf = MemBuf.new
143
+ dec = CcipherFactory::SymKeyCipher.att_decryptor
144
+ dec.key = sk
145
+
146
+ dec.output(membuf)
147
+ dec.att_decrypt_init
148
+ dec.att_decrypt_update(st.cipher)
149
+ dec.att_decrypt_final
150
+
151
+ plain = membuf.bytes.clone
152
+ membuf.dispose
153
+
154
+ if block
155
+ output = block.call(:output)
156
+ end
157
+ output = :binary if is_empty?(output)
158
+
159
+ case output
160
+ when :hex
161
+ @dataConv.to_hex(plain)
162
+ when :b64
163
+ @dataConv.to_b64(plain)
164
+ else
165
+ plain
166
+ end
167
+
168
+ end
169
+
170
+ #def derive(algo = :script)
171
+ # kb = Keybox.new(algo)
172
+ # kb.baseMat = mem_unseal(@key)
173
+ # kb
174
+ #end
175
+
176
+ #def encoded
177
+ # st = BinStruct.instance.struct(:mem_key)
178
+ # st.value = mem_unseal(@key)
179
+ # st.encoded
180
+ #end
181
+
182
+ def plain_key
183
+ mem_unseal(@key)
184
+ end
185
+
186
+ private
187
+ # Encrypt the actual key resides in memory
188
+ def mem_seal(key)
189
+
190
+ supported = CcipherFactory::SymKeyGenerator.supported_symkey
191
+ symAlgo = supported.keys
192
+ # randomly select which algo to start for in memory protection
193
+ startIndx = rand(0..symAlgo.length-1)
194
+ loopCnt = 0
195
+
196
+ logger.debug "Total supported symkey algo : #{symAlgo.length}"
197
+
198
+ indx = startIndx
199
+
200
+ #case key
201
+ #when CcipherFactory::SoftSymKey
202
+ # logger.debug "Given key to seal is Soft SymKey"
203
+ # payload = key.key.key
204
+ #when CcipherFactory::DerivedSymKey
205
+ # logger.debug "Given key to seal is Derived SymKey"
206
+ # payload = key.key
207
+ #else
208
+ # logger.debug "Given key to seal is native key"
209
+ # payload = key
210
+ #end
211
+
212
+ case key
213
+ when CcipherFactory::SymKey
214
+ logger.debug "Given key to seal is SymKey #{key.class}"
215
+ payload = key.raw_key
216
+ else
217
+ logger.debug "Given key to seal is native key"
218
+ payload = key
219
+ end
220
+
221
+ loop do
222
+
223
+ algo = symAlgo[indx]
224
+ ks = supported[algo][:keysize][-1]
225
+ mode = supported[algo][:mode][-1]
226
+
227
+ logger.debug "Utilizing symkey at index : #{indx} - #{algo}/#{ks}/#{mode}"
228
+ sk = CcipherFactory::SymKeyGenerator.generate(algo, ks)
229
+
230
+ #logger.debug "Generated key #{loopCnt} : #{sk.inspect}"
231
+
232
+ enc = CcipherFactory::SymKeyCipher.att_encryptor
233
+ enc.key = sk
234
+ enc.mode = mode
235
+
236
+ membuf = MemBuf.new
237
+ enc.output(membuf)
238
+
239
+ enc.att_encrypt_init
240
+ enc.att_encrypt_update(payload)
241
+ enc.att_encrypt_final
242
+
243
+ sk.attach_mode
244
+ encRes = membuf.bytes.clone
245
+ membuf.dispose
246
+
247
+ #logger.debug "Encrypted #{loopCnt} : #{encRes.inspect}"
248
+
249
+ st = BinStruct.instance.struct(:mem_key_layer)
250
+ st.material = sk.encoded
251
+ st.payload = encRes
252
+ payload = st.encoded
253
+
254
+ indx = ((indx+1) % symAlgo.length)
255
+
256
+ loopCnt += 1
257
+ break if loopCnt >= symAlgo.length
258
+
259
+ end
260
+
261
+ kkey = CcipherFactory::SymKeyGenerator.derive(:aes, 256) do |k|
262
+ case k
263
+ when :password
264
+ case key
265
+ when CcipherFactory::SoftSymKey
266
+ # SymKey -> Ccrypto::SecretKey -> raw key
267
+ key.key.key
268
+ when CcipherFactory::DerivedSymKey
269
+ key.key
270
+ else
271
+ key
272
+ end
273
+ end
274
+ end
275
+
276
+ st = BinStruct.instance.struct(:mem_key_envp)
277
+
278
+ kcv = CcipherFactory::KCV.new
279
+ kcv.key = kkey
280
+
281
+ st.kcv = kcv.encoded
282
+ st.kcvconfig = kkey.encoded
283
+ st.layer = payload
284
+ st.encoded
285
+
286
+ end
287
+
288
+ # Decrypt the in-memory protected key for operational
289
+ def mem_unseal(env)
290
+
291
+ begin
292
+
293
+ sti = BinStruct.instance.struct_from_bin(env)
294
+
295
+ payload = sti.layer
296
+
297
+ loop do
298
+
299
+ st = BinStruct.instance.struct_from_bin(payload)
300
+
301
+ break if st.nil?
302
+
303
+ key = CcipherFactory::SymKey.from_encoded(st.material)
304
+ #logger.debug "Unseal found : #{key.inspect}"
305
+
306
+ encData = st.payload
307
+
308
+ dec = CcipherFactory::SymKeyCipher.att_decryptor
309
+
310
+ membuf = MemBuf.new
311
+ dec.output(membuf)
312
+
313
+ dec.key = key
314
+ dec.att_decrypt_init
315
+ dec.att_decrypt_update(encData)
316
+ dec.att_decrypt_final
317
+
318
+ payload = membuf.bytes.clone
319
+ membuf.dispose
320
+
321
+ #logger.debug "Decrypted payload : #{payload.inspect}"
322
+
323
+ end
324
+ rescue Binenc::BinencDecodingError => ex
325
+ rescue Binenc::BinencEngineException => ex
326
+ #STDERR.puts ex.message
327
+ end
328
+
329
+ #begin
330
+ # dkey = CcipherFactory::SymKey.from_encoded(payload)
331
+ #rescue Binenc::BinencDecodingError => ex
332
+ payload
333
+ #end
334
+
335
+ end
336
+
337
+ def generateKeyID(key)
338
+
339
+ if @keyID.nil?
340
+
341
+ if key.is_a?(CcipherFactory::SymKey)
342
+ sk = key
343
+ else
344
+ sk = CcipherFactory::SymKeyGenerator.derive(:aes, 256) do |k|
345
+ case k
346
+ when :password
347
+ key
348
+ when :kdf
349
+ :scrypt
350
+ when :kdf_scrypt_cost
351
+ 65536
352
+ when :kdf_scrypt_parallel
353
+ 1
354
+ when :kdf_scrypt_blocksize
355
+ 8
356
+ when :kdf_scrypt_salt
357
+ @dataConv.from_hex("FEDCBA0123456789")
358
+ when :kdf_scrypt_digestAlgo
359
+ :sha3_256
360
+ end
361
+ end
362
+ end
363
+
364
+ kcv = CcipherFactory::KCV.new
365
+ kcv.key = sk
366
+ kcv.nonce = "8"*32
367
+ kcvBin = kcv.encoded do |k|
368
+ case k
369
+ when :kcv_cipher_iv
370
+ # given key (sk) is AES key with GCM mode (default if mode not given)
371
+ # therefore the IV size has to be 12
372
+ # Other algo this must be adjusted accordingly
373
+ "1A2B3C4D5E6F"
374
+ end
375
+ end
376
+
377
+ @keyID = @dataConv.to_hex(kcvBin)
378
+ end
379
+
380
+ @keyID
381
+
382
+ end
383
+
384
+
385
+ def logger
386
+ if @logger.nil?
387
+ @logger = TeLogger::Tlogger.new
388
+ @logger.tag = :mem_key
389
+ end
390
+ @logger
391
+ end
392
+
393
+ end
394
+ end
@@ -0,0 +1,93 @@
1
+
2
+ require_relative 'bin_struct'
3
+
4
+ require_relative 'mem_key'
5
+
6
+ module CcipherBox
7
+
8
+ # Collection of crypto keys in a vault to be used by application.
9
+ # Application can register a key that tie with a name here
10
+ # and later use the name to encrypt a data
11
+ # This only store keys that will be kept in memory
12
+ # Not meant to be persistance
13
+ class MemVault
14
+ include TR::CondUtils
15
+
16
+ class MemVaultException < Exception; end
17
+
18
+ def initialize(ringName)
19
+ @ringName = ringName
20
+ end
21
+
22
+ def register(name, key)
23
+ @dataConv = Ccrypto::UtilFactory.instance(:data_conversion)
24
+ vault[name] = MemKey.new(@ringName, key, name)
25
+ self
26
+ end
27
+
28
+ def deregister(name)
29
+ vault.delete(name)
30
+ self
31
+ end
32
+
33
+ def is_registered?(name)
34
+ vault.keys.include?(name)
35
+ end
36
+
37
+ def encrypt(name, data, &block)
38
+ key = vault[name]
39
+ key.encrypt(data, &block)
40
+ end
41
+
42
+ def decrypt(cipher)
43
+ keyID = BinStruct.instance.struct_fields_from_bin(cipher, 2)[0]
44
+ keyID = MemKey.to_KeyID(keyID)
45
+
46
+ foundKey = nil
47
+ vault.values.each do |k|
48
+ if k.keyID == keyID
49
+ foundKey = k
50
+ break
51
+ end
52
+ end
53
+
54
+ if not_empty?(foundKey)
55
+ logger.debug "Found decryption key with label #{vault.invert[foundKey]}"
56
+ foundKey.decrypt(cipher)
57
+ else
58
+ raise KeyNotRegistered, "Encryption key for this cipher not registered (KeyID : #{keyID})"
59
+ end
60
+
61
+ end
62
+
63
+ def derive(name)
64
+ vault[name].derive
65
+ end
66
+
67
+ def get_key(name)
68
+ vault[name]
69
+ end
70
+
71
+ private
72
+ # internal structure to bind the application given name
73
+ # to a key.
74
+ # This allow application to select which key to be used for
75
+ # data protection
76
+ def vault
77
+ if @vault.nil?
78
+ @vault = { }
79
+ end
80
+ @vault
81
+ end
82
+
83
+ def logger
84
+ if @logger.nil?
85
+ @logger = TeLogger::Tlogger.new
86
+ @logger.tag = :mem_vault
87
+ end
88
+ @logger
89
+ end
90
+
91
+
92
+ end
93
+ end