naoki 1.0.21 → 1.0.22
Sign up to get free protection for your applications and to get access to all the features.
- data/config/data_secure.yml +14 -0
- data/lib/data_secure_wrapper.rb +2 -0
- data/lib/naoki/data_secure.rb +176 -109
- data/naoki.gemspec +1 -1
- data/test/helper.rb +1 -20
- data/test/test_data_secure.rb +53 -0
- metadata +7 -4
- data/test/test_data_secure_wrapper.rb +0 -53
@@ -0,0 +1,14 @@
|
|
1
|
+
data_secure_enabled: false
|
2
|
+
properties_file: IngrianNAE.properties
|
3
|
+
|
4
|
+
credentials:
|
5
|
+
document_store:
|
6
|
+
username: username
|
7
|
+
password: password
|
8
|
+
initialization_vector: "1234567890123456"
|
9
|
+
key_name: stg-new-test-key
|
10
|
+
account_number:
|
11
|
+
username: username
|
12
|
+
password: password
|
13
|
+
initialization_vector: "1234567890123456"
|
14
|
+
key_name: stg-new-test-key
|
data/lib/data_secure_wrapper.rb
CHANGED
data/lib/naoki/data_secure.rb
CHANGED
@@ -3,34 +3,10 @@
|
|
3
3
|
# NB: this class is not thread-safe. Create an instance for each thread.
|
4
4
|
module Naoki
|
5
5
|
class DataSecure
|
6
|
-
@enabled = if File.exist?("config/data_secure.yml")
|
7
|
-
YAML.load(File.read("config/data_secure.yml"))['data_secure_enabled']
|
8
|
-
end
|
9
|
-
|
10
|
-
if !(`uname -s`.strip =~ /Linux/ && @enabled)
|
11
|
-
|
12
|
-
def self.init(file); end
|
13
|
-
def close; end
|
14
|
-
|
15
|
-
def encrypt_stream(input, out)
|
16
|
-
out.write("ENC")
|
17
|
-
count = 0
|
18
|
-
while block = input.read(4096)
|
19
|
-
count += block.size
|
20
|
-
out.write(block)
|
21
|
-
end
|
22
|
-
[count, count + 3]
|
23
|
-
end
|
24
|
-
|
25
|
-
def decrypt_stream(input, out)
|
26
|
-
val = input.read(3)
|
27
|
-
raise ArgumentError, "Invalid encrypted data (#{val})" if val != 'ENC'
|
28
|
-
count = IO.copy_stream(input, out)
|
29
|
-
[count + 3, count]
|
30
|
-
end
|
31
6
|
|
32
|
-
|
7
|
+
@Linux = `uname -s`.strip =~ /linux/i
|
33
8
|
|
9
|
+
if @Linux
|
34
10
|
require 'ffi'
|
35
11
|
extend FFI::Library
|
36
12
|
LIB_ICAPI_FILE = `uname -m`.match(/x86_64/) ? 'libICAPI_64.so' : 'libICAPI_32.so'
|
@@ -60,115 +36,206 @@ module Naoki
|
|
60
36
|
attach_function 'I_C_CryptFinal', [:pointer,:pointer,:pointer,:pointer], :int
|
61
37
|
@blocking = true
|
62
38
|
attach_function 'I_C_CryptUpdate', [:pointer, :pointer, :pointer, :uint, :pointer, :pointer], :int
|
39
|
+
end
|
40
|
+
|
41
|
+
DEFAULTS = {
|
42
|
+
'algorithm' => 'AES/CBC/PKCS5Padding',
|
43
|
+
}
|
44
|
+
|
45
|
+
MAX_SAFENET_BLOCK_SIZE = 31000
|
46
|
+
MAX_OUTPUT_BLOCK_SIZE = 32000
|
63
47
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
}
|
48
|
+
def initialize(credential_to_use, data_secure_yml)
|
49
|
+
options = data_secure_yml
|
50
|
+
credential_to_use = credential_to_use.to_s
|
51
|
+
@live = self.class.init(options['data_secure_enabled'], options['properties_file'])
|
69
52
|
|
70
|
-
|
71
|
-
|
53
|
+
if live?
|
54
|
+
@options = DEFAULTS.merge(data_secure_yml['credentials'][credential_to_use])
|
72
55
|
|
73
|
-
def initialize(key_to_use = :account_number, options={})
|
74
|
-
@options = DEFAULTS.merge(options.symbolize_keys)
|
75
|
-
#check { I_C_Initialize(I_T_Init_File, @options[:properties_file]) }
|
76
56
|
@session_pointer = FFI::MemoryPointer.new :pointer
|
77
|
-
check { I_C_OpenSession(@session_pointer, I_T_Auth_Password, @options[
|
57
|
+
check { I_C_OpenSession(@session_pointer, I_T_Auth_Password, @options['username'], @options['password']) }
|
78
58
|
@cipherspec_pointer = FFI::MemoryPointer.new :pointer
|
79
|
-
check { I_C_CreateCipherSpec(@options[
|
59
|
+
check { I_C_CreateCipherSpec(@options['algorithm'], @options['key_name'], @cipherspec_pointer) }
|
80
60
|
end
|
61
|
+
end
|
81
62
|
|
82
|
-
|
83
|
-
|
84
|
-
|
63
|
+
def self.init(enabled, file)
|
64
|
+
return false unless file && enabled && @Linux
|
65
|
+
return true if @initialized
|
66
|
+
check { I_C_Initialize(I_T_Init_File, file) }
|
67
|
+
@initialized = true
|
68
|
+
true
|
69
|
+
end
|
85
70
|
|
86
|
-
|
87
|
-
|
88
|
-
|
71
|
+
def close
|
72
|
+
return unless live?
|
73
|
+
check { I_C_DeleteCipherSpec(@cipherspec_pointer.get_pointer(0)) } if @cipherspec_pointer
|
74
|
+
@cipherspec_pointer = nil
|
75
|
+
I_C_CloseSession(@session_pointer.get_pointer(0)) if @session_pointer
|
76
|
+
@session_pointer = nil
|
77
|
+
end
|
89
78
|
|
90
|
-
|
91
|
-
|
92
|
-
|
79
|
+
def encrypt_stream(input_io, output_io)
|
80
|
+
return dummy_encrypt_stream(input_io, output_io) unless live?
|
81
|
+
transform_stream(I_T_Operation_Encrypt, input_io, output_io)
|
82
|
+
end
|
93
83
|
|
94
|
-
|
95
|
-
|
96
|
-
|
84
|
+
def decrypt_stream(input_io, output_io)
|
85
|
+
return dummy_decrypt_stream(input_io, output_io) unless live?
|
86
|
+
transform_stream(I_T_Operation_Decrypt, input_io, output_io)
|
87
|
+
end
|
97
88
|
|
98
|
-
|
89
|
+
def encrypt(plain_text)
|
90
|
+
return dummy_encrypt(plain_text) unless live?
|
91
|
+
transform(I_T_Operation_Encrypt, plain_text) do |transform_data_length_pointer|
|
92
|
+
check do
|
93
|
+
I_C_CalculateEncipheredSizeForKey(
|
94
|
+
@session_pointer.get_pointer(0),
|
95
|
+
@cipherspec_pointer.get_pointer(0),
|
96
|
+
I_T_Operation_Encrypt,
|
97
|
+
plain_text.length,
|
98
|
+
transform_data_length_pointer)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
99
102
|
|
100
|
-
|
101
|
-
|
103
|
+
def decrypt(text)
|
104
|
+
return dummy_decrypt(text) unless live?
|
105
|
+
transform(I_T_Operation_Decrypt, text) do |transform_data_length_pointer|
|
102
106
|
check do
|
103
|
-
|
107
|
+
I_C_CalculateOutputSizeForKey(
|
104
108
|
@session_pointer.get_pointer(0),
|
105
109
|
@cipherspec_pointer.get_pointer(0),
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
current_state)
|
110
|
+
I_T_Operation_Decrypt,
|
111
|
+
text.length,
|
112
|
+
transform_data_length_pointer)
|
110
113
|
end
|
114
|
+
end
|
115
|
+
end
|
111
116
|
|
112
|
-
|
113
|
-
read_size = 0
|
114
|
-
wrote_size = 0
|
115
|
-
output_data = FFI::MemoryPointer.new :pointer, MAX_OUTPUT_BLOCK_SIZE
|
116
|
-
output_data_length_pointer = FFI::MemoryPointer.new :pointer
|
117
|
-
|
118
|
-
loop do
|
119
|
-
output_data_length_pointer.write_uint(MAX_OUTPUT_BLOCK_SIZE)
|
120
|
-
|
121
|
-
data = input_io.read(MAX_SAFENET_BLOCK_SIZE)
|
122
|
-
if !data || data.size == 0
|
123
|
-
|
124
|
-
check do
|
125
|
-
I_C_CryptFinal(
|
126
|
-
@session_pointer.get_pointer(0),
|
127
|
-
current_state.get_pointer(0),
|
128
|
-
output_data,
|
129
|
-
output_data_length_pointer)
|
130
|
-
end
|
131
|
-
done = true
|
132
|
-
|
133
|
-
elsif data.size <= MAX_SAFENET_BLOCK_SIZE
|
134
|
-
read_size += data.size
|
135
|
-
check do
|
136
|
-
I_C_CryptUpdate(
|
137
|
-
@session_pointer.get_pointer(0),
|
138
|
-
current_state.get_pointer(0),
|
139
|
-
data,
|
140
|
-
data.size,
|
141
|
-
output_data,
|
142
|
-
output_data_length_pointer)
|
143
|
-
end
|
144
|
-
|
145
|
-
else
|
146
|
-
raise "DataSecure, how did we get here? #{data}"
|
147
|
-
end
|
117
|
+
private
|
148
118
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
119
|
+
def live?
|
120
|
+
@live
|
121
|
+
end
|
122
|
+
|
123
|
+
def dummy_encrypt(input)
|
124
|
+
"encrypted string"
|
125
|
+
end
|
126
|
+
|
127
|
+
def dummy_decrypt(input)
|
128
|
+
"decrypted string"
|
129
|
+
end
|
130
|
+
|
131
|
+
def dummy_encrypt_stream(input, out)
|
132
|
+
out.write("ENC")
|
133
|
+
count = 0
|
134
|
+
while block = input.read(4096)
|
135
|
+
count += block.size
|
136
|
+
out.write(block)
|
137
|
+
end
|
138
|
+
[count, count + 3]
|
139
|
+
end
|
140
|
+
|
141
|
+
def dummy_decrypt_stream(input, out)
|
142
|
+
val = input.read(3)
|
143
|
+
raise ArgumentError, "Invalid encrypted data (#{val})" if val != 'ENC'
|
144
|
+
count = IO.copy_stream(input, out)
|
145
|
+
[count + 3, count]
|
146
|
+
end
|
147
|
+
|
148
|
+
def transform(op, text)
|
149
|
+
yield(transform_data_length_pointer = FFI::MemoryPointer.new(:uint))
|
150
|
+
|
151
|
+
transform_data = FFI::MemoryPointer.new :char, transform_data_length_pointer.read_uint
|
154
152
|
|
155
|
-
|
153
|
+
check { I_C_Crypt(
|
154
|
+
@session_pointer.get_pointer(0),
|
155
|
+
@cipherspec_pointer.get_pointer(0),
|
156
|
+
op,
|
157
|
+
@options['initialization_vector'],
|
158
|
+
@options['initialization_vector'].length,
|
159
|
+
text,
|
160
|
+
text.length,
|
161
|
+
transform_data,
|
162
|
+
transform_data_length_pointer
|
163
|
+
) }
|
164
|
+
|
165
|
+
transform_data.read_string(transform_data_length_pointer.read_uint)
|
166
|
+
end
|
167
|
+
|
168
|
+
def transform_stream(op, input_io, output_io)
|
169
|
+
current_state = FFI::MemoryPointer.new :pointer
|
170
|
+
check do
|
171
|
+
I_C_CryptInit(
|
172
|
+
@session_pointer.get_pointer(0),
|
173
|
+
@cipherspec_pointer.get_pointer(0),
|
174
|
+
op,
|
175
|
+
@options['initialization_vector'],
|
176
|
+
@options['initialization_vector'].length,
|
177
|
+
current_state)
|
156
178
|
end
|
157
179
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
180
|
+
done = false
|
181
|
+
read_size = 0
|
182
|
+
wrote_size = 0
|
183
|
+
output_data = FFI::MemoryPointer.new :pointer, MAX_OUTPUT_BLOCK_SIZE
|
184
|
+
output_data_length_pointer = FFI::MemoryPointer.new :pointer
|
185
|
+
|
186
|
+
loop do
|
187
|
+
output_data_length_pointer.write_uint(MAX_OUTPUT_BLOCK_SIZE)
|
188
|
+
|
189
|
+
data = input_io.read(MAX_SAFENET_BLOCK_SIZE)
|
190
|
+
if !data || data.size == 0
|
191
|
+
|
192
|
+
check do
|
193
|
+
I_C_CryptFinal(
|
194
|
+
@session_pointer.get_pointer(0),
|
195
|
+
current_state.get_pointer(0),
|
196
|
+
output_data,
|
197
|
+
output_data_length_pointer)
|
198
|
+
end
|
199
|
+
done = true
|
200
|
+
|
201
|
+
elsif data.size <= MAX_SAFENET_BLOCK_SIZE
|
202
|
+
read_size += data.size
|
203
|
+
check do
|
204
|
+
I_C_CryptUpdate(
|
205
|
+
@session_pointer.get_pointer(0),
|
206
|
+
current_state.get_pointer(0),
|
207
|
+
data,
|
208
|
+
data.size,
|
209
|
+
output_data,
|
210
|
+
output_data_length_pointer)
|
211
|
+
end
|
212
|
+
|
213
|
+
else
|
214
|
+
raise "DataSecure, how did we get here? #{data}"
|
164
215
|
end
|
216
|
+
|
217
|
+
outsize = output_data_length_pointer.read_uint
|
218
|
+
enc_data = output_data.get_bytes(0, outsize)
|
219
|
+
wrote_size += output_io.write(enc_data)
|
220
|
+
break if done
|
165
221
|
end
|
166
222
|
|
167
|
-
|
168
|
-
|
169
|
-
|
223
|
+
[read_size, wrote_size]
|
224
|
+
end
|
225
|
+
|
226
|
+
def check
|
227
|
+
return_code = yield
|
228
|
+
if return_code != I_E_OK
|
229
|
+
I_C_DeleteCipherSpec(@cipherspec_pointer.get_pointer(0)) if @cipherspec_pointer
|
230
|
+
@cipherspec_pointer = nil
|
231
|
+
raise I_C_GetErrorString(return_code)
|
170
232
|
end
|
233
|
+
end
|
171
234
|
|
235
|
+
def self.check(&block)
|
236
|
+
return_code = yield
|
237
|
+
raise I_C_GetErrorString(return_code) if return_code != I_E_OK
|
172
238
|
end
|
239
|
+
|
173
240
|
end
|
174
241
|
end
|
data/naoki.gemspec
CHANGED
data/test/helper.rb
CHANGED
@@ -1,23 +1,4 @@
|
|
1
|
+
require 'yaml'
|
1
2
|
require 'minitest/unit'
|
2
3
|
require 'naoki'
|
3
|
-
require '~/naoki_config'
|
4
|
-
|
5
|
-
def one_time_setup
|
6
|
-
user_name = SAFENET_CONFIG[:username]
|
7
|
-
password = SAFENET_CONFIG[:password]
|
8
|
-
algorithm = SAFENET_CONFIG[:algorithm]
|
9
|
-
key_name = SAFENET_CONFIG[:key_name] # versioned key
|
10
|
-
initialization_vector = SAFENET_CONFIG[:initialization_vector]
|
11
|
-
|
12
|
-
properties_file = 'IngrianNAE.properties'
|
13
|
-
|
14
|
-
DataSecureWrapper.configure(properties_file)
|
15
|
-
DataSecureWrapper.open(user_name, password)
|
16
|
-
|
17
|
-
at_exit do
|
18
|
-
DataSecureWrapper.close
|
19
|
-
end
|
20
|
-
end
|
21
|
-
one_time_setup
|
22
|
-
|
23
4
|
require 'minitest/autorun'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class TestDataSecure < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@data_secure = Naoki::DataSecure.new(:account_number, config_file)
|
7
|
+
end
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
@data_secure.close
|
11
|
+
@data_secure = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_hello_world
|
15
|
+
expected = 'hello world!'
|
16
|
+
encrypted_data = @data_secure.encrypt(expected)
|
17
|
+
decrypted_data = @data_secure.decrypt(encrypted_data)
|
18
|
+
assert(expected == decrypted_data || "decrypted string" == decrypted_data)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_documents
|
22
|
+
13.times do |idx|
|
23
|
+
str = '01234567'*(1 << idx)
|
24
|
+
encrypt_and_decrypt(str.size, StringIO.new(str))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def encrypt_and_decrypt(size, unencrypted)
|
31
|
+
encrypted = StringIO.new
|
32
|
+
unencrypted_size = unencrypted.size
|
33
|
+
|
34
|
+
(read_bytes, written_bytes) = @data_secure.encrypt_stream(unencrypted, encrypted)
|
35
|
+
|
36
|
+
assert_equal unencrypted_size, read_bytes
|
37
|
+
assert(read_bytes < written_bytes)
|
38
|
+
|
39
|
+
encrypted.rewind
|
40
|
+
|
41
|
+
decrypted = StringIO.new
|
42
|
+
(read_bytes, written_bytes) = @data_secure.decrypt_stream(encrypted, decrypted)
|
43
|
+
|
44
|
+
unencrypted.rewind
|
45
|
+
decrypted.rewind
|
46
|
+
assert(read_bytes > written_bytes)
|
47
|
+
assert_equal(unencrypted.read, decrypted.read)
|
48
|
+
end
|
49
|
+
|
50
|
+
def config_file
|
51
|
+
YAML.load(File.read('config/data_secure.yml'))
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: naoki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 1.0.
|
5
|
+
version: 1.0.22
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Chris Apolzon
|
@@ -11,7 +11,8 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2011-06-21 00:00:00
|
14
|
+
date: 2011-06-21 00:00:00 -07:00
|
15
|
+
default_executable:
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
17
18
|
name: ffi
|
@@ -38,6 +39,7 @@ files:
|
|
38
39
|
- .rvmrc
|
39
40
|
- Gemfile
|
40
41
|
- Rakefile
|
42
|
+
- config/data_secure.yml
|
41
43
|
- lib/data_secure_wrapper.rb
|
42
44
|
- lib/libICAPI_32.so
|
43
45
|
- lib/libICAPI_64.so
|
@@ -46,7 +48,8 @@ files:
|
|
46
48
|
- naoki.gemspec
|
47
49
|
- sample.rb
|
48
50
|
- test/helper.rb
|
49
|
-
- test/
|
51
|
+
- test/test_data_secure.rb
|
52
|
+
has_rdoc: true
|
50
53
|
homepage: ""
|
51
54
|
licenses: []
|
52
55
|
|
@@ -70,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
73
|
requirements: []
|
71
74
|
|
72
75
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.
|
76
|
+
rubygems_version: 1.5.2
|
74
77
|
signing_key:
|
75
78
|
specification_version: 3
|
76
79
|
summary: C bindings for SafeNet DataSecure ICAPI
|
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'stringio'
|
3
|
-
|
4
|
-
class TestDataSecureWrapper < MiniTest::Unit::TestCase
|
5
|
-
|
6
|
-
def test_hello_world
|
7
|
-
expected = 'hello world!'
|
8
|
-
encrypted_data = encrypt(expected)
|
9
|
-
decrypted_data = decrypt(encrypted_data)
|
10
|
-
assert(expected == decrypted_data || "decrypted_string" == decrypted_data)
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_documents
|
14
|
-
ds = Naoki::DataSecure.new(:document_store, SAFENET_CONFIG)
|
15
|
-
|
16
|
-
15.times do |idx|
|
17
|
-
str = '01234567'*(1 << idx)
|
18
|
-
encrypt_and_decrypt(ds, str.size, StringIO.new(str))
|
19
|
-
end
|
20
|
-
ensure
|
21
|
-
ds.close
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def encrypt_and_decrypt(ds, size, unencrypted)
|
27
|
-
encrypted = StringIO.new
|
28
|
-
unencrypted_size = unencrypted.size
|
29
|
-
|
30
|
-
(read_bytes, written_bytes) = ds.encrypt_stream(unencrypted, encrypted)
|
31
|
-
|
32
|
-
assert_equal unencrypted_size, read_bytes
|
33
|
-
assert(read_bytes < written_bytes)
|
34
|
-
|
35
|
-
encrypted.rewind
|
36
|
-
|
37
|
-
decrypted = StringIO.new
|
38
|
-
(read_bytes, written_bytes) = ds.decrypt_stream(encrypted, decrypted)
|
39
|
-
|
40
|
-
unencrypted.rewind
|
41
|
-
decrypted.rewind
|
42
|
-
assert(read_bytes > written_bytes)
|
43
|
-
assert_equal(unencrypted.read, decrypted.read)
|
44
|
-
end
|
45
|
-
|
46
|
-
def decrypt(data)
|
47
|
-
DataSecureWrapper.decrypt(SAFENET_CONFIG[:algorithm], SAFENET_CONFIG[:key_name], SAFENET_CONFIG[:initialization_vector], data)
|
48
|
-
end
|
49
|
-
|
50
|
-
def encrypt(data)
|
51
|
-
DataSecureWrapper.encrypt(SAFENET_CONFIG[:algorithm], SAFENET_CONFIG[:key_name], SAFENET_CONFIG[:initialization_vector], data)
|
52
|
-
end
|
53
|
-
end
|