ccipher_box 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.
@@ -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