sodium 0.0.0 → 0.5.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.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/.travis.yml +27 -0
  3. data/Gemfile +9 -0
  4. data/README.md +111 -0
  5. data/Rakefile +3 -0
  6. data/VERSION +1 -0
  7. data/config/nacl_ffi.yml +90 -0
  8. data/lib/sodium.rb +24 -0
  9. data/lib/sodium/auth.rb +52 -0
  10. data/lib/sodium/box.rb +127 -0
  11. data/lib/sodium/buffer.rb +141 -0
  12. data/lib/sodium/delegate.rb +58 -0
  13. data/lib/sodium/ffi.rb +9 -0
  14. data/lib/sodium/ffi/crypto.rb +106 -0
  15. data/lib/sodium/ffi/lib_c.rb +9 -0
  16. data/lib/sodium/ffi/random.rb +11 -0
  17. data/lib/sodium/hash.rb +23 -0
  18. data/lib/sodium/one_time_auth.rb +52 -0
  19. data/lib/sodium/random.rb +16 -0
  20. data/lib/sodium/secret_box.rb +65 -0
  21. data/lib/sodium/sign.rb +75 -0
  22. data/lib/sodium/version.rb +5 -0
  23. data/sodium.gemspec +9 -3
  24. data/sodium.pub.gpg +37 -0
  25. data/tasks/libsodium.rake +70 -0
  26. data/tasks/test.rake +6 -0
  27. data/tasks/version.rake +3 -0
  28. data/test/sodium/auth/hmacsha256_test.rb +54 -0
  29. data/test/sodium/auth/hmacsha512256_test.rb +53 -0
  30. data/test/sodium/auth_test.rb +49 -0
  31. data/test/sodium/box/curve25519xsalsa20poly1305_test.rb +79 -0
  32. data/test/sodium/box_test.rb +109 -0
  33. data/test/sodium/buffer_test.rb +120 -0
  34. data/test/sodium/delegate_test.rb +44 -0
  35. data/test/sodium/hash/sha256_test.rb +31 -0
  36. data/test/sodium/hash/sha512_test.rb +35 -0
  37. data/test/sodium/hash_test.rb +35 -0
  38. data/test/sodium/one_time_auth/poly1305_test.rb +54 -0
  39. data/test/sodium/one_time_auth_test.rb +49 -0
  40. data/test/sodium/random_test.rb +25 -0
  41. data/test/sodium/secret_box/xsalsa20poly1305_test.rb +50 -0
  42. data/test/sodium/secret_box_test.rb +56 -0
  43. data/test/sodium/sign/ed25519_test.rb +52 -0
  44. data/test/sodium/sign_test.rb +58 -0
  45. data/test/test_helper.rb +44 -0
  46. metadata +117 -8
  47. checksums.yaml +0 -7
@@ -0,0 +1,109 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Box do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(*self.keypair) }
7
+ let(:klass) { Sodium::Box }
8
+ let(:keypair) { self.klass.keypair }
9
+
10
+ it 'must default to the Curve22519XSalsa20Poly1305 implementation' do
11
+ self.klass.implementation.
12
+ must_equal Sodium::Box::Curve25519XSalsa20Poly1305
13
+ end
14
+
15
+ it 'must allow access to alternate implementations' do
16
+ self.klass.implementation(:xyz).
17
+ must_equal nil
18
+ end
19
+
20
+ it 'must instantiate the default implementation' do
21
+ self.subject.
22
+ must_be_kind_of Sodium::Box::Curve25519XSalsa20Poly1305
23
+ end
24
+
25
+ it 'must mint keypairs from the default implementation' do
26
+ sodium_mock_default(self.klass) do |klass, mock|
27
+ mock.expect :nacl_keypair, true, [ '', '' ]
28
+ mock.expect :[], 0, [:PUBLICKEYBYTES]
29
+ mock.expect :[], 0, [:SECRETKEYBYTES]
30
+
31
+ sk, pk = klass.keypair
32
+
33
+ sk.to_str.must_equal ''
34
+ pk.to_str.must_equal ''
35
+ end
36
+ end
37
+
38
+ it 'must raise when instantiating with an invalid keypair' do
39
+ secret_key, public_key = self.keypair
40
+
41
+ lambda { self.klass.new(secret_key.to_str[0..-2], public_key) }.
42
+ must_raise Sodium::LengthError
43
+
44
+ lambda { self.klass.new(secret_key, public_key.to_str[0..-2]) }.
45
+ must_raise Sodium::LengthError
46
+ end
47
+
48
+ it 'must raise when receiving an invalid nonce' do
49
+ lambda { self.subject.box('message', self.subject.nonce.to_str[0..-2]) }.
50
+ must_raise Sodium::LengthError
51
+ end
52
+
53
+ it 'must raise when receiving an invalid shared key' do
54
+ lambda { self.klass.afternm('key', 'message', self.subject.nonce) }.
55
+ must_raise Sodium::LengthError
56
+ end
57
+
58
+ it 'must raise when failing to generate keypairs' do
59
+ sodium_stub_failure(self.klass, :nacl_keypair) do
60
+ lambda { self.keypair }.
61
+ must_raise Sodium::CryptoError
62
+ end
63
+ end
64
+
65
+ it 'must raise when failing to close a box' do
66
+ sodium_stub_failure(self.klass, :nacl) do
67
+ lambda { self.subject.box('message', self.subject.nonce) }.
68
+ must_raise Sodium::CryptoError
69
+ end
70
+ end
71
+
72
+ it 'must raise when failing to open a box' do
73
+ sodium_stub_failure(self.klass, :nacl_open) do
74
+ lambda { self.subject.open('ciphertext', self.subject.nonce) }.
75
+ must_raise Sodium::CryptoError
76
+ end
77
+ end
78
+
79
+ it 'must raise when failing to generate a shared key' do
80
+ sodium_stub_failure(self.klass, :nacl_beforenm) do
81
+ lambda { self.subject.beforenm }.
82
+ must_raise Sodium::CryptoError
83
+ end
84
+ end
85
+
86
+ it 'must raise when failing to close a box with a shared key' do
87
+ sodium_stub_failure(self.klass, :nacl_afternm) do
88
+ lambda do
89
+ key = self.subject.beforenm
90
+ nonce = self.subject.nonce
91
+ message = 'message'
92
+
93
+ self.klass.afternm(key, message, nonce)
94
+ end.must_raise Sodium::CryptoError
95
+ end
96
+ end
97
+
98
+ it 'must raise when failing to open a box with a shared key' do
99
+ sodium_stub_failure(self.klass, :nacl_open_afternm) do
100
+ lambda do
101
+ key = self.subject.beforenm
102
+ nonce = self.subject.nonce
103
+ ciphertext = 'ciphertext'
104
+
105
+ self.klass.open_afternm(key, ciphertext, nonce)
106
+ end.must_raise Sodium::CryptoError
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,120 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Buffer do
4
+ subject { Sodium::Buffer }
5
+
6
+ it '::key must securely generate random keys of specified length' do
7
+ Sodium::Random.stub(:bytes, lambda {|l| ' ' * l }) do
8
+ subject.key( 7).to_str.must_equal(' ' * 7)
9
+ subject.key( 8).to_str.must_equal(' ' * 8)
10
+ subject.key(16).to_str.must_equal(' ' * 16)
11
+ subject.key(32).to_str.must_equal(' ' * 32)
12
+ subject.key(64).to_str.must_equal(' ' * 64)
13
+ end
14
+ end
15
+
16
+ it '::nonce must securely generate random nonces of specified length' do
17
+ Sodium::Random.stub(:bytes, lambda {|l| ' ' * l }) do
18
+ subject.nonce( 7).to_str.must_equal(' ' * 7)
19
+ subject.nonce( 8).to_str.must_equal(' ' * 8)
20
+ subject.nonce(16).to_str.must_equal(' ' * 16)
21
+ subject.nonce(32).to_str.must_equal(' ' * 32)
22
+ subject.nonce(64).to_str.must_equal(' ' * 64)
23
+ end
24
+ end
25
+
26
+ it '::empty must generate an empty buffer of specified length' do
27
+ subject.empty(32).to_str.must_equal("\0" * 32)
28
+ subject.empty(40).to_str.must_equal("\0" * 40)
29
+ end
30
+
31
+ it '::empty must yield to a block when given' do
32
+ mock = MiniTest::Mock.new
33
+ mock.expect :flag, nil
34
+
35
+ subject.empty(5) {|buffer| mock.flag }
36
+
37
+ mock.verify
38
+ end
39
+
40
+ it '::new must create a buffer containing the specified string' do
41
+ subject.new('xyz' ).to_str.must_equal('xyz')
42
+ subject.new('xyz' * 50).to_str.must_equal('xyz' * 50)
43
+ end
44
+
45
+ it '::new must do optional length checking' do
46
+ lambda { subject.new('xyz', 4).to_str }.
47
+ must_raise Sodium::LengthError
48
+ end
49
+
50
+ it '#initialize must freeze its bytes' do
51
+ subject.new('s').to_str.must_be :frozen?
52
+ end
53
+
54
+ it '#initialize must wipe the memory from the original string' do
55
+ 'test'.tap do |s|
56
+ subject.new(s)
57
+ end.must_equal("\0" * 4)
58
+ end
59
+
60
+ it '#initialize must wipe the buffer during finalization'
61
+ it '#initialize must prevent the string from being paged to disk'
62
+
63
+ it '#pad bytes onto the front' do
64
+ subject.new('s').pad(3).to_str.must_equal "\0\0\0s"
65
+ end
66
+
67
+ it '#unpad bytes from the front' do
68
+ subject.new("\0\0\0s").unpad(3).to_str.must_equal 's'
69
+ end
70
+
71
+ it '#byteslice must accept an indivdual byte offset to return' do
72
+ subject.new('xyz').tap do |buffer|
73
+ buffer.byteslice(-4).to_str.must_equal ''
74
+ buffer.byteslice(-3).to_str.must_equal 'x'
75
+ buffer.byteslice(-2).to_str.must_equal 'y'
76
+ buffer.byteslice(-1).to_str.must_equal 'z'
77
+ buffer.byteslice( 0).to_str.must_equal 'x'
78
+ buffer.byteslice( 1).to_str.must_equal 'y'
79
+ buffer.byteslice( 2).to_str.must_equal 'z'
80
+ buffer.byteslice( 3).to_str.must_equal ''
81
+ end
82
+ end
83
+
84
+ it '#byteslice must accept ranges of bytes to return' do
85
+ subject.new('xyz').tap do |buffer|
86
+ buffer.byteslice( 0.. 0).to_str.must_equal 'x'
87
+ buffer.byteslice( 0.. 1).to_str.must_equal 'xy'
88
+ buffer.byteslice( 0.. 2).to_str.must_equal 'xyz'
89
+ buffer.byteslice( 0.. 3).to_str.must_equal 'xyz'
90
+ buffer.byteslice( 1..-1).to_str.must_equal 'yz'
91
+ buffer.byteslice( 2..-2).to_str.must_equal ''
92
+ buffer.byteslice(-3..-1).to_str.must_equal 'xyz'
93
+ buffer.byteslice(-4.. 1).to_str.must_equal ''
94
+ end
95
+ end
96
+
97
+ it '#byteslice must accept an offset and number of bytes to return' do
98
+ subject.new('xyz').tap do |buffer|
99
+ buffer.byteslice( 0, 0).to_str.must_equal ''
100
+ buffer.byteslice( 0, 1).to_str.must_equal 'x'
101
+ buffer.byteslice( 0, 3).to_str.must_equal 'xyz'
102
+ buffer.byteslice( 2, 4).to_str.must_equal 'z'
103
+ buffer.byteslice( 2, 1).to_str.must_equal 'z'
104
+ buffer.byteslice(-2, 1).to_str.must_equal 'y'
105
+ buffer.byteslice( 0, -1).to_str.must_equal ''
106
+ end
107
+ end
108
+
109
+ it '#bytesize must return its length' do
110
+ subject.new('testing').bytesize.must_equal 7
111
+ end
112
+
113
+ it '#inspect must not reveal its instance variables' do
114
+ subject.new('blah').inspect.wont_include 'blah'
115
+ end
116
+
117
+ it '#to_str must return its internal bytes' do
118
+ subject.new('xyz').to_str.must_equal('xyz')
119
+ end
120
+ end
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+
3
+ class DelegateTest
4
+ include Sodium::Delegate
5
+
6
+ def self.[](key)
7
+ self.const_get(key)
8
+ end
9
+
10
+ class Subclass1 < self; PRIMITIVE = :subclass1; end
11
+ class Subclass2 < self; PRIMITIVE = :subclass2; end
12
+
13
+ DEFAULT = :subclass1
14
+ end
15
+
16
+ describe Sodium::Delegate do
17
+ subject { self.klass.new }
18
+ let(:klass) { DelegateTest }
19
+ let(:subclass) { DelegateTest::Subclass1 }
20
+
21
+ it '::implementation must be the default' do
22
+ self.klass.implementation.must_equal self.subclass
23
+ end
24
+
25
+ it 'must allow access to constants through indexing' do
26
+ self.klass.implementation[:PRIMITIVE].must_equal :subclass1
27
+ end
28
+
29
+ it 'must allow access to arbitrary implementations' do
30
+ self.klass.implementation(:subclass2).must_equal DelegateTest::Subclass2
31
+ end
32
+
33
+ it 'must instantiate the default implementation' do
34
+ self.klass.new.class.must_equal self.subclass
35
+ end
36
+
37
+ it 'must allow instance access to the instantiated primitive' do
38
+ self.subject.primitive.must_equal self.subclass::PRIMITIVE
39
+ end
40
+
41
+ it 'must allow class access to the instantiated primitive' do
42
+ self.subject.class.primitive.must_equal self.subclass::PRIMITIVE
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Hash::SHA256 do
4
+ include SodiumTestHelpers
5
+
6
+ let (:klass) { Sodium::Hash::SHA256 }
7
+ let (:primitive) { :sha256 }
8
+
9
+ let :constants do
10
+ { :BYTES => 32 }
11
+ end
12
+
13
+ let_64(:hash) { 'q1MKE+RZFJgrefm34/uplM/R8/si9xzqGvvwK0YMbR0=' }
14
+ let_64(:plaintext) { 'bWVzc2FnZQ==' }
15
+
16
+ it '::primitive must be correct' do
17
+ self.klass.primitive.must_equal self.primitive
18
+ end
19
+
20
+ it 'must have correct values for its constants' do
21
+ self.constants.each_pair do |name, value|
22
+ self.klass[name].must_equal value
23
+ end
24
+ end
25
+
26
+ it 'must generate hashes' do
27
+ self.klass.hash(
28
+ self.plaintext
29
+ ).to_str.must_equal self.hash
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Hash::SHA512 do
4
+ include SodiumTestHelpers
5
+
6
+ let (:klass) { Sodium::Hash::SHA512 }
7
+ let (:primitive) { :sha512 }
8
+
9
+ let :constants do
10
+ { :BYTES => 64 }
11
+ end
12
+
13
+ let_64(:hash) do
14
+ %{ +Nr1ejNHzE1rnVdbMf5gd+LLSH9gqWIzwIy0edvzFTjMkV7G1IvbqpbdwaFt
15
+ tPT5bzcnbPyzUQuCRiQXcNWVLA== }
16
+ end
17
+
18
+ let_64(:plaintext) { 'bWVzc2FnZQ==' }
19
+
20
+ it '::primitive must be correct' do
21
+ self.klass.primitive.must_equal self.primitive
22
+ end
23
+
24
+ it 'must have correct values for its constants' do
25
+ self.constants.each_pair do |name, value|
26
+ self.klass[name].must_equal value
27
+ end
28
+ end
29
+
30
+ it 'must generate hashes' do
31
+ self.klass.hash(
32
+ self.plaintext
33
+ ).to_str.must_equal self.hash
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Hash do
4
+ include SodiumTestHelpers
5
+
6
+ let(:klass) { Sodium::Hash }
7
+
8
+ let_64(:plaintext) { 'bWVzc2FnZQ==' }
9
+
10
+ it 'must default to the SHA512 implementation' do
11
+ self.klass.implementation.
12
+ must_equal Sodium::Hash::SHA512
13
+ end
14
+
15
+ it 'must allow access to alternate implementations' do
16
+ self.klass.implementation(:sha256).
17
+ must_equal Sodium::Hash::SHA256
18
+ end
19
+
20
+ it 'must hash from the default implementation' do
21
+ sodium_mock_default(self.klass) do |klass, mock|
22
+ mock.expect :[], 0, [ :BYTES ]
23
+ mock.expect :nacl, '', [ String, self.plaintext, self.plaintext.bytesize ]
24
+
25
+ klass.hash(self.plaintext).to_str.must_equal ''
26
+ end
27
+ end
28
+
29
+ it 'must raise when failing to generate a hash' do
30
+ sodium_stub_failure(self.klass, :nacl) do
31
+ lambda { self.klass.hash(self.plaintext) }.
32
+ must_raise Sodium::CryptoError
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::OneTimeAuth::Poly1305 do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(self.key) }
7
+
8
+ let(:klass) { Sodium::OneTimeAuth::Poly1305 }
9
+ let(:primitive) { :poly1305 }
10
+
11
+ let :constants do
12
+ { :BYTES => 16,
13
+ :KEYBYTES => 32, }
14
+ end
15
+
16
+ let_64(:key) { 'tZUeTVtSHOfgOei4DUwCt10xqrIYhALpO08NIDMWFB0=' }
17
+ let_64(:authenticator) { 'n+6StqC6SLRuLT8YZoQoFw==' }
18
+ let_64(:plaintext) { 'bWVzc2FnZQ==' }
19
+
20
+ it '::primitive must be correct' do
21
+ self.klass.primitive.must_equal self.primitive
22
+ end
23
+
24
+ it 'must have correct values for its constants' do
25
+ self.constants.each_pair do |name, value|
26
+ self.klass[name].must_equal value
27
+ end
28
+ end
29
+
30
+ it 'must mint keys' do
31
+ self.klass.key.bytesize.
32
+ must_equal self.klass::KEYBYTES
33
+ end
34
+
35
+ it 'must generate authenticators' do
36
+ self.subject.one_time_auth(
37
+ self.plaintext
38
+ ).to_str.must_equal self.authenticator
39
+ end
40
+
41
+ it 'must verify authenticators' do
42
+ self.subject.verify(
43
+ self.plaintext,
44
+ self.authenticator
45
+ ).must_equal true
46
+ end
47
+
48
+ it 'must not verify forged authenticators' do
49
+ self.subject.verify(
50
+ self.plaintext,
51
+ self.authenticator.succ
52
+ ).must_equal false
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::OneTimeAuth do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(self.key) }
7
+ let(:klass) { Sodium::OneTimeAuth }
8
+ let(:key) { self.klass.key }
9
+
10
+ it 'must default to the Poly1305 implementation' do
11
+ self.klass.implementation.
12
+ must_equal Sodium::OneTimeAuth::Poly1305
13
+ end
14
+
15
+ it 'must allow access to alternate implementations' do
16
+ self.klass.implementation(:foo).
17
+ must_equal nil
18
+ end
19
+
20
+ it 'must instantiate the default implementation' do
21
+ self.subject.
22
+ must_be_kind_of Sodium::OneTimeAuth::Poly1305
23
+ end
24
+
25
+ it 'must mint keys from the default implementation' do
26
+ sodium_mock_default(self.klass) do |klass, mock|
27
+ mock.expect :[], 0, [:KEYBYTES]
28
+
29
+ klass.key.to_str.must_equal ''
30
+ end
31
+ end
32
+
33
+ it 'must raise when instantiating with an invalid key' do
34
+ lambda { self.klass.new(self.key.to_str[0..-2]) }.
35
+ must_raise Sodium::LengthError
36
+ end
37
+
38
+ it 'must raise when verifying an invalid authenticator' do
39
+ lambda { self.subject.verify('message', 'blaah') }.
40
+ must_raise Sodium::LengthError
41
+ end
42
+
43
+ it 'must raise when failing to generate an authenticator' do
44
+ sodium_stub_failure(self.klass, :nacl) do
45
+ lambda { self.subject.one_time_auth('message') }.
46
+ must_raise Sodium::CryptoError
47
+ end
48
+ end
49
+ end