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,75 @@
1
+ require 'sodium'
2
+
3
+ class Sodium::Sign
4
+ include Sodium::Delegate
5
+
6
+ def self.keypair
7
+ public_key = Sodium::Buffer.empty self.implementation[:PUBLICKEYBYTES]
8
+ secret_key = Sodium::Buffer.empty self.implementation[:SECRETKEYBYTES]
9
+
10
+ self.implementation.nacl_keypair(
11
+ public_key.to_str,
12
+ secret_key.to_str
13
+ ) or raise Sodium::CryptoError, 'failed to generate a keypair'
14
+
15
+ return secret_key, public_key
16
+ end
17
+
18
+ def self.verify(key, message, signature)
19
+ key = self._public_key(key)
20
+ signature = self._signature(message, signature)
21
+ message = Sodium::Buffer.empty(signature.bytesize)
22
+ mlen = FFI::MemoryPointer.new(:ulong_long, 1, true)
23
+
24
+ self.implementation.nacl_open(
25
+ message.to_str,
26
+ mlen,
27
+ signature.to_str,
28
+ signature.to_str.bytesize,
29
+ key.to_str
30
+ )
31
+ end
32
+
33
+ def initialize(key)
34
+ @key = self.class._secret_key(key)
35
+ end
36
+
37
+ def sign(message)
38
+ message = self.class._message(message)
39
+ signature = Sodium::Buffer.empty(message.bytesize + self.implementation[:BYTES])
40
+ slen = FFI::MemoryPointer.new(:ulong_long, 1, true)
41
+
42
+ self.implementation.nacl(
43
+ signature.to_str,
44
+ slen,
45
+ message.to_str,
46
+ message.to_str.bytesize,
47
+ @key.to_str
48
+ ) or raise Sodium::CryptoError, 'failed to generate signature'
49
+
50
+ # signatures actually encode the message itself at the end, so we
51
+ # slice off only the signature bytes
52
+ signature.byteslice(
53
+ 0,
54
+ slen.read_ulong_long - message.to_str.bytesize
55
+ )
56
+ end
57
+
58
+ private
59
+
60
+ def self._public_key(k)
61
+ Sodium::Buffer.new k, self.implementation[:PUBLICKEYBYTES]
62
+ end
63
+
64
+ def self._secret_key(k)
65
+ Sodium::Buffer.new k, self.implementation[:SECRETKEYBYTES]
66
+ end
67
+
68
+ def self._message(m)
69
+ Sodium::Buffer.new m
70
+ end
71
+
72
+ def self._signature(m, s)
73
+ Sodium::Buffer.new(s) + m
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ require 'version'
2
+
3
+ module Sodium
4
+ is_versioned
5
+ end
data/sodium.gemspec CHANGED
@@ -1,17 +1,23 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'sodium'
3
- gem.version = '0.0.0'
3
+ gem.version = File.read('VERSION') rescue '0.0.0'
4
4
 
5
5
  gem.author = 'Stephen Touset'
6
6
  gem.email = 'stephen@touset.org'
7
7
 
8
8
  gem.homepage = 'https://github.com/stouset/sodium'
9
- gem.summary = %{TBD}
10
- gem.description = %{TBD}
9
+ gem.summary = 'A Ruby binding to the easy-to-use high-speed crypto library libsodium'
10
+ gem.description = 'A library for performing cryptography based on modern ciphers and protocols'
11
11
 
12
12
  gem.bindir = 'bin'
13
13
  gem.files = `git ls-files` .split("\n")
14
14
  gem.extensions = `git ls-files -- ext/*.rb`.split("\n")
15
15
  gem.executables = `git ls-files -- bin/*` .split("\n").map {|e| File.basename(e) }
16
16
  gem.test_files = `git ls-files -- spec/*` .split("\n")
17
+
18
+ gem.add_dependency 'ffi', '~> 1'
19
+
20
+ gem.add_development_dependency 'rake', '~> 10'
21
+ gem.add_development_dependency 'minitest', '~> 5'
22
+ gem.add_development_dependency 'version', '~> 1'
17
23
  end
data/sodium.pub.gpg ADDED
@@ -0,0 +1,37 @@
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+ Version: GnuPG v1.4.13 (Darwin)
3
+
4
+ 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
35
+ 4AA=
36
+ =JlbY
37
+ -----END PGP PUBLIC KEY BLOCK-----
@@ -0,0 +1,70 @@
1
+ require 'rake/clean'
2
+
3
+ require 'rbconfig'
4
+ require 'digest/sha2'
5
+
6
+
7
+ LIBSODIUM_MIRROR = ENV['LIBSODIUM_MIRROR'] || "https://github.com/jedisct1/libsodium/tarball/%s"
8
+ LIBSODIUM_VERSION = ENV['LIBSODIUM_VERSION'] || 'master'
9
+ LIBSODIUM_DIGEST = ENV['LIBSODIUM_DIGEST'] || nil
10
+
11
+ LIBSODIUM_URL = LIBSODIUM_MIRROR % LIBSODIUM_VERSION
12
+ LIBSODIUM_PATH = "libsodium-#{LIBSODIUM_VERSION}"
13
+ LIBSODIUM_TARBALL = "build/#{LIBSODIUM_PATH}.tar.gz"
14
+ LIBSODIUM_BUILD = "build/#{LIBSODIUM_PATH}"
15
+ LIBSODIUM_LIBDIR = "#{LIBSODIUM_BUILD}/src/libsodium/.libs"
16
+ LIBSODIUM_LIB = "libsodium.a"
17
+ LIBSODIUM = "#{LIBSODIUM_LIBDIR}/#{LIBSODIUM_LIB}"
18
+
19
+ namespace :libsodium do
20
+ directory LIBSODIUM_BUILD
21
+
22
+ file LIBSODIUM_TARBALL => LIBSODIUM_BUILD do
23
+ sh %{curl -L -o #{LIBSODIUM_TARBALL} #{LIBSODIUM_URL}}
24
+
25
+ next if LIBSODIUM_DIGEST.nil?
26
+ next if LIBSODIUM_DIGEST == Digest::SHA256.hexdigest(
27
+ File.read(LIBSODIUM_TARBALL)
28
+ )
29
+
30
+ rm LIBSODIUM_TARBALL
31
+ raise "#{LIBSODIUM_TARBALL} failed checksum"
32
+ end
33
+
34
+ file "#{LIBSODIUM_BUILD}/autogen.sh" => [
35
+ LIBSODIUM_BUILD,
36
+ LIBSODIUM_TARBALL,
37
+ ] do
38
+ sh %{tar -C #{LIBSODIUM_BUILD} --strip-components 1 -m -xf #{LIBSODIUM_TARBALL}}
39
+ end
40
+
41
+ file "#{LIBSODIUM_BUILD}/configure" => "#{LIBSODIUM_BUILD}/autogen.sh" do
42
+ sh %{cd #{LIBSODIUM_BUILD} && ./autogen.sh}
43
+ end
44
+
45
+ file "#{LIBSODIUM_BUILD}/Makefile" => "#{LIBSODIUM_BUILD}/configure" do
46
+ sh %{cd #{LIBSODIUM_BUILD} && ./configure}
47
+ end
48
+
49
+ file LIBSODIUM => "#{LIBSODIUM_BUILD}/Makefile" do
50
+ sh %{cd #{LIBSODIUM_BUILD} && make}
51
+ end
52
+
53
+ desc 'Compile a local copy of libsodium'
54
+ task :compile => LIBSODIUM do
55
+ # allow use of the library by the dynamic linker
56
+ ENV['DYLD_LIBRARY_PATH'] = LIBSODIUM_LIBDIR
57
+ ENV[ 'LD_LIBRARY_PATH'] = LIBSODIUM_LIBDIR
58
+ end
59
+
60
+ task :clean do
61
+ sh %{cd #{LIBSODIUM_BUILD} && make mostlyclean}
62
+ end
63
+
64
+ task :clobber do
65
+ CLOBBER.add 'build'
66
+ end
67
+ end
68
+
69
+ task :clean => 'libsodium:clean'
70
+ task :clobber => 'libsodium:clobber'
data/tasks/test.rake ADDED
@@ -0,0 +1,6 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new 'test' => 'libsodium:compile' do |t|
4
+ t.libs << 'test'
5
+ t.pattern = "test/**/*_test.rb"
6
+ end
@@ -0,0 +1,3 @@
1
+ require 'rake/version_task'
2
+
3
+ Rake::VersionTask.new
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Auth::HMACSHA256 do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(self.key) }
7
+
8
+ let(:klass) { Sodium::Auth::HMACSHA256 }
9
+ let(:primitive) { :hmacsha256 }
10
+
11
+ let :constants do
12
+ { :BYTES => 32,
13
+ :KEYBYTES => 32, }
14
+ end
15
+
16
+ let_64(:key) { 'XMfWD8/yrcNDzJyGhxRIwi5tSGKf8D0ul9FyX/djvjg=' }
17
+ let_64(:authenticator) { '6WDKvxKevcZts0Yc1HWGnylNYEpcxPO9tVtApEK8XWc=' }
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.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,53 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Auth::HMACSHA512256 do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(self.key) }
7
+
8
+ let(:klass) { Sodium::Auth::HMACSHA512256 }
9
+ let(:primitive) { :hmacsha512256 }
10
+
11
+ let :constants do
12
+ { :BYTES => 32,
13
+ :KEYBYTES => 32, }
14
+ end
15
+
16
+ let_64(:key) { 'XMfWD8/yrcNDzJyGhxRIwi5tSGKf8D0ul9FyX/djvjg=' }
17
+ let_64(:authenticator) { '6BN5+HNq0F8skQKkta+CLiBJ7mrrJaGw3G2J7jMT2qA=' }
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.must_equal self.klass[:KEYBYTES]
32
+ end
33
+
34
+ it 'must generate authenticators' do
35
+ self.subject.auth(
36
+ self.plaintext
37
+ ).to_str.must_equal self.authenticator
38
+ end
39
+
40
+ it 'must verify authenticators' do
41
+ self.subject.verify(
42
+ self.plaintext,
43
+ self.authenticator
44
+ ).must_equal true
45
+ end
46
+
47
+ it 'must not verify forged authenticators' do
48
+ self.subject.verify(
49
+ self.plaintext,
50
+ self.authenticator.succ
51
+ ).must_equal false
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Auth do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(self.key) }
7
+ let(:klass) { Sodium::Auth }
8
+ let(:key) { self.klass.key }
9
+
10
+ it 'must default to the HMACSHA512256 implementation' do
11
+ self.klass.implementation.
12
+ must_equal Sodium::Auth::HMACSHA512256
13
+ end
14
+
15
+ it 'must allow access to alternate implementations' do
16
+ self.klass.implementation(:hmacsha256).
17
+ must_equal Sodium::Auth::HMACSHA256
18
+ end
19
+
20
+ it 'must instantiate the default implementation' do
21
+ self.subject.
22
+ must_be_kind_of Sodium::Auth::HMACSHA512256
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', 'blaaah') }.
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.auth('message') }.
46
+ must_raise Sodium::CryptoError
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+ describe Sodium::Box::Curve25519XSalsa20Poly1305 do
4
+ include SodiumTestHelpers
5
+
6
+ subject { self.klass.new(self.secret_key, self.public_key) }
7
+
8
+ let(:klass) { Sodium::Box::Curve25519XSalsa20Poly1305 }
9
+ let(:primitive) { :curve25519xsalsa20poly1305 }
10
+
11
+ let :constants do
12
+ { :PUBLICKEYBYTES => 32,
13
+ :SECRETKEYBYTES => 32,
14
+ :BEFORENMBYTES => 32,
15
+ :NONCEBYTES => 24,
16
+ :ZEROBYTES => 32,
17
+ :BOXZEROBYTES => 16,
18
+ :MACBYTES => 16, }
19
+ end
20
+
21
+ let_64(:secret_key) { 'f52WNdyy0r1YA5NCGlcF+vJ5HPG8yfHwzn/HJSJzfQk=' }
22
+ let_64(:public_key) { 'es8h5AH9GGD7PF10D1txeHAFAB2UNc9OZF+JqFWE9y8=' }
23
+ let_64(:shared_key) { 'TTp8bQBhIuiiQ0plVcqS3Cj62i/IdAFnopx4t9di2Kg=' }
24
+ let_64(:nonce) { 'i72xIDJ4tcHCOHGYAzI6PoiVm31PFVgx' }
25
+ let_64(:ciphertext) { 'lHhCSFzopX4z02nlIuInHe3hFwpHFdA=' }
26
+ let_64(:plaintext) { 'bWVzc2FnZQ==' }
27
+
28
+ it '::primitive must be correct' do
29
+ self.klass.primitive.must_equal self.primitive
30
+ end
31
+
32
+ it 'must have correct values for its constants' do
33
+ self.constants.each_pair do |name, value|
34
+ self.klass[name].must_equal value
35
+ end
36
+ end
37
+
38
+ it 'must mint secret keys' do
39
+ self.klass.keypair[0].bytesize.must_equal self.klass[:SECRETKEYBYTES]
40
+ end
41
+
42
+ it 'must mint public keys' do
43
+ self.klass.keypair[1].bytesize.must_equal self.klass[:PUBLICKEYBYTES]
44
+ end
45
+
46
+ it 'must generate closed boxes' do
47
+ self.subject.box(
48
+ self.plaintext,
49
+ self.nonce
50
+ ).to_str.must_equal self.ciphertext
51
+ end
52
+
53
+ it 'must open boxes' do
54
+ self.subject.open(
55
+ self.ciphertext,
56
+ self.nonce
57
+ ).to_str.must_equal self.plaintext
58
+ end
59
+
60
+ it 'must generate shared keys' do
61
+ self.subject.beforenm.to_str.must_equal self.shared_key
62
+ end
63
+
64
+ it 'must generate closed boxes with shared keys' do
65
+ self.klass.afternm(
66
+ self.shared_key,
67
+ self.plaintext,
68
+ self.nonce
69
+ ).to_str.must_equal self.ciphertext
70
+ end
71
+
72
+ it 'must open boxes with shared keys' do
73
+ self.klass.open_afternm(
74
+ self.shared_key,
75
+ self.ciphertext,
76
+ self.nonce
77
+ ).to_str.must_equal self.plaintext
78
+ end
79
+ end