sodium 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.travis.yml +13 -6
- data/CHANGELOG.md +15 -1
- data/VERSION +1 -1
- data/lib/sodium/auth.rb +8 -8
- data/lib/sodium/box.rb +27 -27
- data/lib/sodium/buffer.rb +141 -85
- data/lib/sodium/ffi/lib_c.rb +3 -0
- data/lib/sodium/hash.rb +3 -3
- data/lib/sodium/one_time_auth.rb +8 -8
- data/lib/sodium/random.rb +1 -1
- data/lib/sodium/secret_box.rb +10 -10
- data/lib/sodium/sign.rb +11 -11
- data/tasks/extensions.rake +12 -3
- data/test/sodium/auth/hmacsha256_test.rb +2 -2
- data/test/sodium/auth/hmacsha512256_test.rb +2 -2
- data/test/sodium/auth_test.rb +2 -2
- data/test/sodium/box/curve25519xsalsa20poly1305_test.rb +5 -5
- data/test/sodium/box_test.rb +6 -6
- data/test/sodium/buffer_test.rb +79 -72
- data/test/sodium/hash/sha256_test.rb +1 -1
- data/test/sodium/hash/sha512_test.rb +1 -1
- data/test/sodium/hash_test.rb +3 -2
- data/test/sodium/one_time_auth/poly1305_test.rb +1 -1
- data/test/sodium/one_time_auth_test.rb +2 -2
- data/test/sodium/secret_box/xsalsa20poly1305_test.rb +2 -2
- data/test/sodium/secret_box_test.rb +3 -3
- data/test/sodium/sign/ed25519_test.rb +1 -1
- data/test/sodium/sign_test.rb +4 -4
- metadata +154 -159
- metadata.gz.sig +2 -4
- checksums.yaml +0 -7
- checksums.yaml.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/.travis.yml
CHANGED
@@ -5,12 +5,9 @@ bundler_args: --without tools
|
|
5
5
|
env:
|
6
6
|
matrix:
|
7
7
|
- >
|
8
|
-
LIBSODIUM_MIRROR="
|
9
|
-
LIBSODIUM_VERSION=
|
10
|
-
|
11
|
-
# LIBSODIUM_MIRROR="http://download.dnscrypt.org/libsodium/releases/libsodium-%s.tar.gz"
|
12
|
-
# LIBSODIUM_VERSION=0.4.1
|
13
|
-
# LIBSODIUM_DIGEST=65756c7832950401cc0e6ee0e99b165974244e749f40f33d465f56447bae8ce3
|
8
|
+
LIBSODIUM_MIRROR="http://download.dnscrypt.org/libsodium/releases/libsodium-%s.tar.gz"
|
9
|
+
LIBSODIUM_VERSION=0.4.2
|
10
|
+
LIBSODIUM_DIGEST=1a7901cdd127471724e854a8eb478247dc0ca67be549345c75fc6f2d4e05ed39
|
14
11
|
|
15
12
|
rvm:
|
16
13
|
- 1.8.7
|
@@ -25,6 +22,16 @@ rvm:
|
|
25
22
|
- rbx-19mode
|
26
23
|
|
27
24
|
matrix:
|
25
|
+
include:
|
26
|
+
- rvm: 2.0.0
|
27
|
+
env: >
|
28
|
+
LIBSODIUM_MIRROR="https://github.com/jedisct1/libsodium/tarball/%s"
|
29
|
+
LIBSODIUM_VERSION="master"
|
30
|
+
|
28
31
|
allow_failures:
|
29
32
|
- rvm: ruby-head
|
30
33
|
- rvm: jruby-head
|
34
|
+
- rvm: 2.0.0
|
35
|
+
env: >
|
36
|
+
LIBSODIUM_MIRROR="https://github.com/jedisct1/libsodium/tarball/%s"
|
37
|
+
LIBSODIUM_VERSION="master"
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
### 0.
|
1
|
+
### 0.7.0 (2013-07-18)
|
2
|
+
|
3
|
+
- Additions
|
4
|
+
* Sodium::Buffer#to_ptr added to replace #to_str
|
5
|
+
* Sodium::Buffer#to_s added to replace #to_str
|
6
|
+
|
7
|
+
- Removals
|
8
|
+
* Sodium::Buffer#to_str removed
|
9
|
+
|
10
|
+
- Bug Fixes
|
11
|
+
* Potential data loss bug fixed. Sodium::Buffer can no longer be
|
12
|
+
garbage collected (thus clearing its bytes) while a pointer to its
|
13
|
+
bytes (from #to_ptr) is being held.
|
14
|
+
|
15
|
+
### 0.6.2 (2013-07-09)
|
2
16
|
|
3
17
|
- Additions
|
4
18
|
* now actually distributed with a license! (MIT)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/lib/sodium/auth.rb
CHANGED
@@ -13,10 +13,10 @@ class Sodium::Auth
|
|
13
13
|
|
14
14
|
Sodium::Buffer.empty self.implementation[:BYTES] do |authenticator|
|
15
15
|
self.implementation.nacl(
|
16
|
-
authenticator.
|
17
|
-
message.
|
18
|
-
message.
|
19
|
-
key.
|
16
|
+
authenticator.to_ptr,
|
17
|
+
message .to_ptr,
|
18
|
+
message .bytesize,
|
19
|
+
key .to_ptr
|
20
20
|
) or raise Sodium::CryptoError, 'failed to generate an authenticator'
|
21
21
|
end
|
22
22
|
end
|
@@ -27,10 +27,10 @@ class Sodium::Auth
|
|
27
27
|
authenticator = self._authenticator(authenticator)
|
28
28
|
|
29
29
|
self.implementation.nacl_verify(
|
30
|
-
authenticator.
|
31
|
-
message.
|
32
|
-
message.
|
33
|
-
key.
|
30
|
+
authenticator.to_ptr,
|
31
|
+
message .to_ptr,
|
32
|
+
message .bytesize,
|
33
|
+
key .to_ptr
|
34
34
|
)
|
35
35
|
end
|
36
36
|
|
data/lib/sodium/box.rb
CHANGED
@@ -8,8 +8,8 @@ class Sodium::Box
|
|
8
8
|
secret_key = Sodium::Buffer.empty self.implementation[:SECRETKEYBYTES]
|
9
9
|
|
10
10
|
self.implementation.nacl_keypair(
|
11
|
-
public_key.
|
12
|
-
secret_key.
|
11
|
+
public_key.to_ptr,
|
12
|
+
secret_key.to_ptr
|
13
13
|
) or raise Sodium::CryptoError, 'failed to generate a keypair'
|
14
14
|
|
15
15
|
return secret_key, public_key
|
@@ -22,11 +22,11 @@ class Sodium::Box
|
|
22
22
|
|
23
23
|
Sodium::Buffer.empty(message.bytesize) do |ciphertext|
|
24
24
|
self.implementation.nacl_afternm(
|
25
|
-
ciphertext.
|
26
|
-
message.
|
27
|
-
message.
|
28
|
-
nonce.
|
29
|
-
shared_key.
|
25
|
+
ciphertext.to_ptr,
|
26
|
+
message .to_ptr,
|
27
|
+
message .bytesize,
|
28
|
+
nonce .to_ptr,
|
29
|
+
shared_key.to_ptr
|
30
30
|
) or raise Sodium::CryptoError, 'failed to close the box'
|
31
31
|
end.ldrop self.implementation[:BOXZEROBYTES]
|
32
32
|
end
|
@@ -38,11 +38,11 @@ class Sodium::Box
|
|
38
38
|
|
39
39
|
Sodium::Buffer.empty(ciphertext.bytesize) do |message|
|
40
40
|
self.implementation.nacl_open_afternm(
|
41
|
-
message.
|
42
|
-
ciphertext.
|
43
|
-
ciphertext.
|
44
|
-
nonce.
|
45
|
-
shared_key.
|
41
|
+
message .to_ptr,
|
42
|
+
ciphertext.to_ptr,
|
43
|
+
ciphertext.bytesize,
|
44
|
+
nonce .to_ptr,
|
45
|
+
shared_key.to_ptr
|
46
46
|
) or raise Sodium::CryptoError, 'failed to open the box'
|
47
47
|
end.ldrop self.implementation[:ZEROBYTES]
|
48
48
|
end
|
@@ -63,12 +63,12 @@ class Sodium::Box
|
|
63
63
|
|
64
64
|
Sodium::Buffer.empty(message.bytesize) do |ciphertext|
|
65
65
|
self.implementation.nacl(
|
66
|
-
ciphertext.
|
67
|
-
message.
|
68
|
-
message.
|
69
|
-
nonce.
|
70
|
-
@public_key.
|
71
|
-
@secret_key.
|
66
|
+
ciphertext .to_ptr,
|
67
|
+
message .to_ptr,
|
68
|
+
message .bytesize,
|
69
|
+
nonce .to_ptr,
|
70
|
+
@public_key.to_ptr,
|
71
|
+
@secret_key.to_ptr
|
72
72
|
) or raise Sodium::CryptoError, 'failed to close the box'
|
73
73
|
end.ldrop self.implementation[:BOXZEROBYTES]
|
74
74
|
end
|
@@ -79,12 +79,12 @@ class Sodium::Box
|
|
79
79
|
|
80
80
|
Sodium::Buffer.empty(ciphertext.bytesize) do |message|
|
81
81
|
self.implementation.nacl_open(
|
82
|
-
message.
|
83
|
-
ciphertext.
|
84
|
-
ciphertext.
|
85
|
-
nonce.
|
86
|
-
@public_key.
|
87
|
-
@secret_key.
|
82
|
+
message .to_ptr,
|
83
|
+
ciphertext .to_ptr,
|
84
|
+
ciphertext .bytesize,
|
85
|
+
nonce .to_ptr,
|
86
|
+
@public_key.to_ptr,
|
87
|
+
@secret_key.to_ptr
|
88
88
|
) or raise Sodium::CryptoError, 'failed to open the box'
|
89
89
|
end.ldrop self.implementation[:ZEROBYTES]
|
90
90
|
end
|
@@ -92,9 +92,9 @@ class Sodium::Box
|
|
92
92
|
def beforenm
|
93
93
|
Sodium::Buffer.empty self.implementation[:BEFORENMBYTES] do |shared_key|
|
94
94
|
self.implementation.nacl_beforenm(
|
95
|
-
shared_key.
|
96
|
-
@public_key.
|
97
|
-
@secret_key.
|
95
|
+
shared_key .to_ptr,
|
96
|
+
@public_key.to_ptr,
|
97
|
+
@secret_key.to_ptr
|
98
98
|
) or raise Sodium::CryptoError, 'failed to create a shared key'
|
99
99
|
end
|
100
100
|
end
|
data/lib/sodium/buffer.rb
CHANGED
@@ -48,21 +48,37 @@ class Sodium::Buffer
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def initialize(bytes)
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
51
|
+
# allocate memory for the incoming bytes and copy them in to our
|
52
|
+
# newly-owned memory
|
53
|
+
pointer = Sodium::FFI::LibC.calloc(1, bytes.bytesize)
|
54
|
+
pointer.write_string(bytes)
|
55
|
+
|
56
|
+
# use the ZeroingDelegator finalizer to wipe the memory from our
|
57
|
+
# pointer, and attach our own finalizer to free() the memory
|
58
|
+
ZeroingDelegator._finalize! self, pointer, bytes.bytesize,
|
59
|
+
&self.class._finalizer(pointer, bytes.bytesize)
|
60
|
+
|
61
|
+
# zero out the bytes passed to us, since we can't control their
|
62
|
+
# lifecycle
|
63
|
+
ZeroingDelegator._mwipe!(bytes, bytes.bytesize)
|
64
|
+
|
65
|
+
# WARNING: The following section is critical. Edit with caution!
|
66
|
+
#
|
67
|
+
# We create a new pointer to the bytes allocated earlier and set a
|
68
|
+
# hidden instance variable pointing at ourself. We do the latter
|
69
|
+
# so that there is a cyclic dependency between the buffer and the
|
70
|
+
# pointer; if either is still live in the current scope, it is
|
71
|
+
# enough to prevent the other from being collected. We create the
|
72
|
+
# new pointer since the previous pointer is pointed to by our
|
73
|
+
# finalizer; if we didn't, its existence in the finalizer proc
|
74
|
+
# would keep us from being garbage collected because of its
|
75
|
+
# pointer to us!
|
76
|
+
@bytesize = bytes.bytesize
|
77
|
+
@bytes = FFI::Pointer.new(pointer.address)
|
78
|
+
@bytes.instance_variable_set(:@_sodium_buffer, self)
|
79
|
+
|
80
|
+
@bytes.freeze
|
81
|
+
self .freeze
|
66
82
|
end
|
67
83
|
|
68
84
|
def ==(bytes)
|
@@ -72,8 +88,8 @@ class Sodium::Buffer
|
|
72
88
|
self.bytesize == bytes.bytesize
|
73
89
|
|
74
90
|
Sodium::FFI::Crypto.sodium_memcmp(
|
75
|
-
self.
|
76
|
-
bytes.
|
91
|
+
self .to_ptr,
|
92
|
+
bytes.to_ptr,
|
77
93
|
bytes.bytesize
|
78
94
|
) == 0
|
79
95
|
end
|
@@ -93,10 +109,10 @@ class Sodium::Buffer
|
|
93
109
|
|
94
110
|
Sodium::Buffer.empty(self.bytesize) do |buffer|
|
95
111
|
Sodium::FFI::Memory.sodium_memxor(
|
96
|
-
buffer.
|
97
|
-
self.
|
98
|
-
bytes.
|
99
|
-
bytes.bytesize
|
112
|
+
buffer.to_ptr,
|
113
|
+
self .to_ptr,
|
114
|
+
bytes .to_ptr,
|
115
|
+
bytes .bytesize
|
100
116
|
)
|
101
117
|
end
|
102
118
|
end
|
@@ -112,8 +128,8 @@ class Sodium::Buffer
|
|
112
128
|
bytes = Sodium::Buffer.new(bytes)
|
113
129
|
|
114
130
|
Sodium::FFI::Memory.sodium_memput(
|
115
|
-
self.
|
116
|
-
bytes.
|
131
|
+
self .to_ptr,
|
132
|
+
bytes.to_ptr,
|
117
133
|
offset,
|
118
134
|
size
|
119
135
|
)
|
@@ -121,67 +137,16 @@ class Sodium::Buffer
|
|
121
137
|
true
|
122
138
|
end
|
123
139
|
|
124
|
-
def [](
|
125
|
-
|
126
|
-
@bytes.
|
127
|
-
) if (
|
128
|
-
# Ruby 1.8 doesn't have byteslice
|
129
|
-
@bytes.respond_to?(:byteslice) or
|
130
|
-
|
131
|
-
# JRuby reuses memory regions when calling byteslice, which
|
132
|
-
# results in them getting cleared when the new buffer initializes
|
133
|
-
defined?(RUBY_ENGINE) and RUBY_ENGINE == 'java'
|
140
|
+
def [](offset, size)
|
141
|
+
self.class.new(
|
142
|
+
@bytes.get_bytes(offset, size)
|
134
143
|
)
|
135
|
-
|
136
|
-
raise ArgumentError, 'wrong number of arguments (0 for 1..2)' if
|
137
|
-
args.length < 1 or args.length > 2
|
138
|
-
|
139
|
-
start, finish = case
|
140
|
-
when args[1]
|
141
|
-
# matches: byteslice(start, size)
|
142
|
-
start = args[0].to_i
|
143
|
-
size = args[1].to_i
|
144
|
-
|
145
|
-
# if size is less than 1, finish needs to be small enough that
|
146
|
-
# `finish - start + 1 <= 0` even after finish is wrapped
|
147
|
-
# around to account for negative indices
|
148
|
-
finish = size > 0 ?
|
149
|
-
start + size - 1 :
|
150
|
-
- self.bytesize.succ
|
151
|
-
|
152
|
-
[ start, finish ]
|
153
|
-
when args[0].kind_of?(Range)
|
154
|
-
# matches: byteslice(start .. finish)
|
155
|
-
# matches: byteslice(start ... finish)
|
156
|
-
range = args[0]
|
157
|
-
start = range.begin.to_i
|
158
|
-
finish = range.exclude_end? ? range.end.to_i - 1 : range.end.to_i
|
159
|
-
|
160
|
-
[ start, finish ]
|
161
|
-
else
|
162
|
-
# matches: byteslice(start)
|
163
|
-
[ args[0].to_i, args[0].to_i ]
|
164
|
-
end
|
165
|
-
|
166
|
-
# ensure negative values are wrapped around explicitly
|
167
|
-
start += self.bytesize if start < 0
|
168
|
-
finish += self.bytesize if finish < 0
|
169
|
-
size = finish - start + 1
|
170
|
-
|
171
|
-
# this approach ensures the bytes are copied into new memory, so
|
172
|
-
# instantiating a new buffer doesn't clear the bytes in the
|
173
|
-
# existing buffer
|
174
|
-
bytes = (start >= 0 and size >= 0) ?
|
175
|
-
@bytes.unpack("@#{start}a#{size}").first :
|
176
|
-
''
|
177
|
-
|
178
|
-
self.class.new(bytes)
|
179
144
|
end
|
180
145
|
|
181
146
|
alias byteslice []
|
182
147
|
|
183
148
|
def bytesize
|
184
|
-
@
|
149
|
+
@bytesize
|
185
150
|
end
|
186
151
|
|
187
152
|
def rdrop(size)
|
@@ -198,22 +163,113 @@ class Sodium::Buffer
|
|
198
163
|
"#<%s:0x%x>" % [ self.class.name, self.__id__ * 2 ]
|
199
164
|
end
|
200
165
|
|
166
|
+
def to_s
|
167
|
+
# Pretend to return a string, but really return a Delegator that
|
168
|
+
# wipes the string's memory when it gets garbage collected.
|
169
|
+
#
|
170
|
+
# Since any calls to the methods of the String inside the
|
171
|
+
# delegator by necessity have the delegator's `method_missing` in
|
172
|
+
# their backtrace, there can never be a situation where there is a
|
173
|
+
# live pointer to the string itself but not one to the delegator.
|
174
|
+
ZeroingDelegator.new(
|
175
|
+
@bytes.read_bytes(@bytesize)
|
176
|
+
)
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_ptr
|
180
|
+
@bytes
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def self._finalizer(pointer, size)
|
186
|
+
proc { self._free!(pointer) }
|
187
|
+
end
|
188
|
+
|
189
|
+
def self._free!(pointer)
|
190
|
+
Sodium::FFI::LibC.free(pointer)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class Sodium::Buffer::ZeroingDelegator
|
195
|
+
self.instance_methods.map(&:to_sym).each do |method|
|
196
|
+
undef_method method unless [
|
197
|
+
:__id__,
|
198
|
+
:__send__,
|
199
|
+
:object_id,
|
200
|
+
:equal?,
|
201
|
+
].include?(method)
|
202
|
+
end
|
203
|
+
|
204
|
+
def initialize(string, &finalizer)
|
205
|
+
self.__setobj__(string)
|
206
|
+
|
207
|
+
# specify class name explicitly, since we're letting the `class`
|
208
|
+
# method delegate to the wrapped object
|
209
|
+
Sodium::Buffer::ZeroingDelegator._mlock! string, string.bytesize
|
210
|
+
Sodium::Buffer::ZeroingDelegator._finalize! self, string, string.bytesize,
|
211
|
+
&finalizer
|
212
|
+
|
213
|
+
self.__getobj__.freeze
|
214
|
+
self .freeze
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_s
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
201
221
|
def to_str
|
202
|
-
|
222
|
+
# Okay, fine, you win. I'll give you access to the raw string
|
223
|
+
# we're wrapping. But now I can't wipe the data when you're done
|
224
|
+
# with it.
|
225
|
+
self.__getobj__.
|
226
|
+
tr('', ''). # trick to force the string to be copied
|
227
|
+
freeze
|
228
|
+
end
|
229
|
+
|
230
|
+
alias dup to_s
|
231
|
+
alias clone to_s
|
232
|
+
|
233
|
+
protected
|
234
|
+
|
235
|
+
def method_missing(*args, &block)
|
236
|
+
self.__getobj__.__send__(*args, &block)
|
237
|
+
ensure
|
238
|
+
$@.delete_if do |trace|
|
239
|
+
# delete lines from the backtrace that originate from the
|
240
|
+
# __send__ line above
|
241
|
+
trace =~ %r{ \A #{Regexp.quote(__FILE__)}:#{__LINE__ - 5} : }x
|
242
|
+
end if $@
|
243
|
+
end
|
244
|
+
|
245
|
+
def __getobj__
|
246
|
+
@_obj
|
247
|
+
end
|
248
|
+
|
249
|
+
def __setobj__(string)
|
250
|
+
@_obj = string
|
203
251
|
end
|
204
252
|
|
205
253
|
private
|
206
254
|
|
207
|
-
def self._finalizer(
|
208
|
-
proc {
|
255
|
+
def self._finalizer(pointer, size, &finalizer)
|
256
|
+
proc {
|
257
|
+
self._mwipe! pointer, size
|
258
|
+
finalizer.call pointer, size if finalizer
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
def self._finalize!(delegator, pointer, size, &finalizer)
|
263
|
+
ObjectSpace.define_finalizer delegator,
|
264
|
+
self._finalizer(pointer, size, &finalizer)
|
209
265
|
end
|
210
266
|
|
211
|
-
def self._mwipe!(
|
212
|
-
Sodium::FFI::Crypto.sodium_memzero(
|
267
|
+
def self._mwipe!(pointer, size)
|
268
|
+
Sodium::FFI::Crypto.sodium_memzero(pointer, size)
|
213
269
|
end
|
214
270
|
|
215
|
-
def self._mlock!(
|
216
|
-
Sodium::FFI::LibC.mlock(
|
271
|
+
def self._mlock!(pointer, size)
|
272
|
+
Sodium::FFI::LibC.mlock(pointer, size) or
|
217
273
|
raise Sodium::MemoryError, 'could not mlock(2) secure buffer into memory'
|
218
274
|
end
|
219
275
|
end
|