sodium 0.0.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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