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,299 @@
1
+
2
+
3
+ module CcipherBox
4
+
5
+
6
+ #
7
+ # SecureBox is a secure container protected by user password
8
+ # which has multiple SecureRings (crypto configurations)
9
+ #
10
+ class SecureBox
11
+ include TR::CondUtils
12
+
13
+ attr_accessor :rings
14
+
15
+ def initialize(rings = nil)
16
+ @rings = { }
17
+ @keyConfigs = []
18
+ if not_empty?(rings)
19
+ rings.each do |r|
20
+ @rings[r.name] = r
21
+ end
22
+ end
23
+ end
24
+
25
+ ##
26
+ # SecureRing management
27
+ #
28
+ # Allow external created ring
29
+ def add_ring(ring)
30
+ @rings[ring.name] = ring
31
+ end
32
+
33
+ def remove_ring(ring_name)
34
+ @rings.delete(ring_name)
35
+ end
36
+
37
+ def rings
38
+ @rings.freeze
39
+ end
40
+
41
+ def init_ring(spec, opts = { })
42
+
43
+ ss = split_encryption_spec(spec)
44
+
45
+ ring = find_ring(ss[0], opts)
46
+ ring.generate_key(ss[1], opts) if not ring.is_key_registered?(ss[1])
47
+ ring
48
+
49
+ end
50
+
51
+ # Implicit SecureRing management
52
+ #
53
+ # Encryption in chunk
54
+ #
55
+ def encryption_session(*specs, &block)
56
+
57
+ opts = block.call(:options) if block
58
+ opts = { } if opts.nil?
59
+
60
+ keys = []
61
+ specs.each do |spec|
62
+ ss = split_encryption_spec(spec)
63
+ ringName = ss[0]
64
+ keyName = ss[1]
65
+ ring = find_ring(ringName, opts)
66
+ ring.generate_key(keyName, opts) if not ring.is_key_registered?(keyName)
67
+ keys << ring.get_key(keyName)
68
+ end
69
+
70
+ #puts "Encryption key : #{keys}"
71
+ EncryptionEngine.new(*keys)
72
+ end
73
+
74
+ #
75
+ # Decryption in chunk
76
+ #
77
+ def decryption_session(ringName)
78
+ ring = find_ring(ringName, { auto_create_ring: false })
79
+ ring.new_decryption_engine
80
+ end
81
+
82
+ #
83
+ # Single line encryption
84
+ #
85
+ def encrypt(data, *specs, &block)
86
+
87
+ opts = block.call(:options) if block
88
+ opts = { } if opts.nil?
89
+
90
+ keys = []
91
+ specs.each do |spec|
92
+ ss = split_encryption_spec(spec)
93
+ ringName = ss[0]
94
+ keyName = ss[1]
95
+ ring = find_ring(ringName, opts)
96
+ if not ring.is_key_registered?(keyName)
97
+ ring.generate_key(keyName, opts)
98
+ block.call(:new_key_generated) if block
99
+ end
100
+ keys << ring.get_key(keyName)
101
+ end
102
+
103
+ eng = EncryptionEngine.new(*keys)
104
+ intBuf = MemBuf.new
105
+ eng.init(intBuf)
106
+ eng.update(data)
107
+ eng.final
108
+
109
+ res = intBuf.bytes.clone
110
+ intBuf.dispose
111
+
112
+ res
113
+
114
+ end
115
+
116
+ #
117
+ # Single line decryption
118
+ #
119
+ def decrypt(bin, &block)
120
+
121
+ raise CcipherBox::Error, "No SecureRing is laoded" if is_empty?(@rings)
122
+
123
+ intBuf = false
124
+ if block
125
+ output = block.call(:output)
126
+ end
127
+
128
+ if output.nil?
129
+ intBuf = true
130
+ output = MemBuf.new
131
+ end
132
+
133
+ res = nil
134
+ lastEx = nil
135
+ @rings.values.each do |v|
136
+ begin
137
+ dec = v.new_decryption_engine
138
+ dec.init(output)
139
+ dec.update(bin)
140
+ dec.final
141
+
142
+ res = output.bytes.clone
143
+ output.dispose
144
+
145
+ break
146
+ rescue KeyNotRegistered => ex
147
+ lastEx = ex
148
+ end
149
+ end
150
+
151
+ if intBuf
152
+ raise KeyNotRegistered, "Decryption failed. #{lastEx.nil? ? "" : "(#{lastEx.message})"}" if res.nil?
153
+ res
154
+ else
155
+ nil
156
+ end
157
+
158
+ end
159
+
160
+ ## end SecureRing management
161
+
162
+
163
+ def to_storage(&block)
164
+
165
+ raise CcipherBox::Error, "Block is required" if not block
166
+
167
+ pass = block.call(:password)
168
+ raise CcipherBox::Error, "Password is required" if is_empty?(pass)
169
+
170
+ deriveLevel = block.call(:derive_level) || 2
171
+
172
+ keyConfigs = []
173
+ payload = pass
174
+ (0..deriveLevel).each do |i|
175
+
176
+ kb = Keybox.new
177
+ kb.baseMat = payload
178
+ kb.outBitLength = 256
179
+ payload = kb.dkey
180
+
181
+ keyConfigs << kb.encoded
182
+ end
183
+
184
+ ringBin = []
185
+ @rings.values.each do |e|
186
+ ringBin << e.encoded
187
+ end
188
+
189
+ cboxes = BinStruct.instance.struct(:secure_rings)
190
+ cboxes.secure_rings = ringBin
191
+
192
+ sk = CcipherFactory::SymKeyGenerator.derive(:aes, payload.length*8) do |ops|
193
+ case ops
194
+ when :password
195
+ payload
196
+ end
197
+ end
198
+
199
+ keyConfigs << sk.encoded
200
+
201
+ enc = CcipherFactory::SymKeyCipher.att_encryptor
202
+ intOut = MemBuf.new
203
+ enc.output(intOut)
204
+ enc.key = sk
205
+ enc.att_encrypt_init
206
+ enc.att_encrypt_update(cboxes.encoded)
207
+ enc.att_encrypt_final
208
+
209
+ st = BinStruct.instance.struct(:securebox)
210
+ st.keyConfigs = keyConfigs
211
+ st.engines = intOut.bytes
212
+ st.encoded
213
+
214
+ end
215
+
216
+ def self.load_storage(bin, &block)
217
+
218
+ raise CcipherBox::Error, "Block is required" if not block
219
+
220
+ pass = block.call(:password)
221
+ raise CcipherBox::Error, "Password is required" if is_empty?(pass)
222
+
223
+ st = BinStruct.instance.struct_from_bin(bin)
224
+ payload = pass
225
+ st.keyConfigs[0..-2].each do |kc|
226
+ kb = Keybox.from_encoded(kc)
227
+ kb.baseMat = payload
228
+ payload = kb.dkey
229
+ end
230
+
231
+ sk = CcipherFactory::SymKey.from_encoded(st.keyConfigs[-1]) do |ops|
232
+ case ops
233
+ when :password
234
+ payload
235
+ end
236
+ end
237
+
238
+ begin
239
+
240
+ dec = CcipherFactory::SymKeyCipher.att_decryptor
241
+ intOut = MemBuf.new
242
+ dec.output(intOut)
243
+ dec.key = sk
244
+ dec.att_decrypt_init
245
+ dec.att_decrypt_update(st.engines)
246
+ dec.att_decrypt_final
247
+
248
+ cboxes = BinStruct.instance.struct_from_bin(intOut.bytes)
249
+ rings = []
250
+ cboxes.secure_rings.each do |cb|
251
+ rings << CcipherBox::SecureRing.from_encoded(cb)
252
+ end
253
+
254
+ SecureBox.new(rings)
255
+
256
+ rescue CcipherFactory::SymKeyDecryptionError => ex
257
+ raise SecureBoxDecryptionError, ex
258
+ end
259
+
260
+ end
261
+
262
+ private
263
+ def find_ring(ringName, opts = { })
264
+
265
+ raise SecureBoxError, "Ring name cannot be empty" if is_empty?(ringName)
266
+
267
+ if not @rings.keys.include?(ringName)
268
+ autoCreate = opts[:auto_create_ring]
269
+ autoCreate = true if is_empty?(autoCreate)
270
+ if autoCreate
271
+ logger.debug "auto_create_ring is true. Creating ring '#{ringName}'."
272
+ ring = SecureRing.new({ name: ringName })
273
+ @rings[ringName] = ring
274
+ else
275
+ logger.debug "auto_create_ring is false"
276
+ raise SecureRingNotExist, "Ring '#{ringName}' does not exist and auto create is not active."
277
+ end
278
+ end
279
+
280
+ @rings[ringName]
281
+
282
+ end
283
+
284
+ def split_encryption_spec(spec)
285
+ ss = spec.split("/")
286
+ raise SecureBoxEncryptionSpecError, "Spec requires to in format ring_name/key_name format" if ss.length != 2
287
+ ss
288
+ end
289
+
290
+ def logger
291
+ if @logger.nil?
292
+ @logger = TeLogger::Tlogger.new
293
+ @logger.tag = :secbox
294
+ end
295
+ @logger
296
+ end
297
+
298
+ end
299
+ end
@@ -0,0 +1,129 @@
1
+
2
+ require_relative 'encryption_engine'
3
+ require_relative 'decryption_engine'
4
+
5
+ require_relative 'enc_key_config'
6
+
7
+ module CcipherBox
8
+
9
+ #
10
+ # SecureRing that carries a unique seed for data encryption and decryption
11
+ # Typically SecureRing contains one key vault for data protection
12
+ #
13
+ # Different between SecureRing and MemVault is the encrypt/decrypt
14
+ # function of MemVault only for limited data however for SecureRing
15
+ # the data size is limited only by the algorithm limitation
16
+ #
17
+ class SecureRing
18
+ include TR::CondUtils
19
+
20
+ attr_accessor :name
21
+ def initialize(opts = { })
22
+
23
+ @name = opts[:name] || "Genesis"
24
+
25
+ @vault = MemVault.new(@name)
26
+
27
+ # seed for data encryption key derivation
28
+ @encSeed = opts[:encSeed] || SecureRandom.random_bytes(64)
29
+
30
+ # keep link between encryption key config with a name
31
+ @encKeyConfig = EncKeyConfig.new
32
+ if not_empty?(opts[:encKeyConfig])
33
+ conf = opts[:encKeyConfig].keyConfigs
34
+ conf.each do |name, kc|
35
+ regenerate_key(name, kc)
36
+ end
37
+ end
38
+ end
39
+
40
+ # generate new operation key for data encryption and decryption
41
+ def generate_key(name, opts = { })
42
+ algo = opts[:algo] || :aes
43
+ keysize = opts[:keysize] || 256
44
+
45
+ sk = CcipherFactory::SymKeyGenerator.derive(algo, keysize) do |ops|
46
+ case ops
47
+ when :password
48
+ @encSeed
49
+ end
50
+ end
51
+
52
+ @vault.register(name, sk)
53
+ @encKeyConfig.register_config(name, sk.encoded)
54
+ end
55
+
56
+ def dispose_key(name)
57
+ @vault.deregister(name)
58
+ self
59
+ end
60
+
61
+ def is_key_registered?(name)
62
+ @vault.is_registered?(name)
63
+ end
64
+
65
+ def registered_keys
66
+ @vault.keys.freeze
67
+ end
68
+
69
+ def get_key(name)
70
+ @vault.get_key(name)
71
+ end
72
+
73
+ def new_encryption_engine(*keyNames)
74
+ names = []
75
+ keyNames.each do |name|
76
+ raise KeyNotRegistered, "Key with name '#{name}' not registered" if not is_key_registered?(name)
77
+ names << @vault.get_key(name)
78
+ end
79
+ EncryptionEngine.new(*names)
80
+ end
81
+
82
+ def new_decryption_engine
83
+ DecryptionEngine.new(@vault)
84
+ end
85
+
86
+ def encoded
87
+ st = BinStruct.instance.struct(:secure_ring)
88
+ st.name = @name
89
+ st.cipherSeed = @encSeed
90
+ st.keyConfigs = @encKeyConfig.encoded
91
+ st.encoded
92
+ end
93
+
94
+ def self.from_encoded(bin)
95
+ st = BinStruct.instance.struct_from_bin(bin)
96
+ encKeyConfig = EncKeyConfig.from_encoded(st.keyConfigs)
97
+ SecureRing.new({ encSeed: st.cipherSeed, encKeyConfig: encKeyConfig, name: st.name })
98
+ end
99
+
100
+ private
101
+ # regenerate key based on config loaded from external
102
+ def regenerate_key(name, config)
103
+
104
+ sk = CcipherFactory::SymKey.from_encoded(config[:config]) do |ops|
105
+ case ops
106
+ when :password
107
+ if @encKeyConfig.is_derived_key?(config)
108
+ else
109
+ @encSeed
110
+ end
111
+ end
112
+ end
113
+
114
+ @vault.register(name, sk)
115
+
116
+ end
117
+
118
+ def logger
119
+ if @logger.nil?
120
+ @logger = TeLogger::Tlogger.new
121
+ @logger.tag = :secure_ring
122
+ end
123
+ @logger
124
+ end
125
+
126
+
127
+ end
128
+
129
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CcipherBox
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ccipher_box/version"
4
+
5
+ require_relative 'ccipher_box/keybox'
6
+ require_relative 'ccipher_box/mem_vault'
7
+
8
+ require_relative 'ccipher_box/secure_box'
9
+ require_relative 'ccipher_box/secure_ring'
10
+
11
+ module CcipherBox
12
+ class Error < StandardError; end
13
+
14
+ class KeyboxException < StandardError; end
15
+ class KeyboxRegisterException < StandardError; end
16
+ class KeyNotRegistered < StandardError; end
17
+
18
+ class InsufficientData < StandardError; end
19
+
20
+
21
+ class SecureBoxError < StandardError; end
22
+ class SecureBoxDecryptionError < StandardError; end
23
+ class SecureRingNotExist < StandardError; end
24
+
25
+ # Your code goes here...
26
+
27
+
28
+ end
data/run_test.rb ADDED
@@ -0,0 +1,27 @@
1
+
2
+
3
+ require 'toolrack'
4
+ require 'fileutils'
5
+
6
+ gemfile = File.join(File.dirname(__FILE__),"Gemfile.lock")
7
+ if TR::RTUtils.on_jruby?
8
+ rtGemFile = File.join(File.dirname(__FILE__),"Gemfile.lock-java")
9
+ else
10
+ rtGemFile = File.join(File.dirname(__FILE__),"Gemfile.lock-ruby")
11
+ end
12
+
13
+ if File.exist?(rtGemFile)
14
+
15
+ FileUtils.rm_f gemfile if File.exist?(gemfile)
16
+
17
+ FileUtils.cp rtGemFile, gemfile
18
+
19
+ cmd = "bundle exec rspec #{ARGV.join(" ")}"
20
+
21
+ system(cmd)
22
+
23
+ else
24
+
25
+ STDERR.puts "No java or ruby Gemfile.lock found. Please create the environment first."
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ccipher_box
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ian
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-08-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: toolrack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: teLogger
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ccrypto
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: binenc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ccipher_factory
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: devops_assist
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: ''
98
+ email:
99
+ - cameronian0@protonmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".rspec"
105
+ - Gemfile
106
+ - Gemfile.lock-java
107
+ - Gemfile.lock-ruby
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - ccipher_box.gemspec
113
+ - lib/ccipher_box.rb
114
+ - lib/ccipher_box/bin_struct.rb
115
+ - lib/ccipher_box/binenc_constant.rb
116
+ - lib/ccipher_box/decryption_engine.rb
117
+ - lib/ccipher_box/enc_key_config.rb
118
+ - lib/ccipher_box/encryption_engine.rb
119
+ - lib/ccipher_box/keybox.rb
120
+ - lib/ccipher_box/mem_key.rb
121
+ - lib/ccipher_box/mem_vault.rb
122
+ - lib/ccipher_box/secure_box.rb
123
+ - lib/ccipher_box/secure_ring.rb
124
+ - lib/ccipher_box/version.rb
125
+ - run_test.rb
126
+ homepage: ''
127
+ licenses: []
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 2.4.0
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubygems_version: 3.2.22
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: ''
148
+ test_files: []