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,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: []