sodium 0.6.2 → 0.7.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.
- 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
|