rbnacl 1.0.0.pre

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 (68) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +21 -0
  5. data/.yardopts +1 -0
  6. data/CHANGES.md +4 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE.txt +23 -0
  9. data/README.md +179 -0
  10. data/Rakefile +5 -0
  11. data/images/dragons.png +0 -0
  12. data/images/ed25519.png +0 -0
  13. data/images/logo.png +0 -0
  14. data/lib/rbnacl.rb +46 -0
  15. data/lib/rbnacl/auth.rb +78 -0
  16. data/lib/rbnacl/auth/one_time.rb +38 -0
  17. data/lib/rbnacl/box.rb +141 -0
  18. data/lib/rbnacl/encoder.rb +44 -0
  19. data/lib/rbnacl/encoders/base32.rb +33 -0
  20. data/lib/rbnacl/encoders/base64.rb +30 -0
  21. data/lib/rbnacl/encoders/hex.rb +30 -0
  22. data/lib/rbnacl/encoders/raw.rb +12 -0
  23. data/lib/rbnacl/hash.rb +48 -0
  24. data/lib/rbnacl/hmac/sha256.rb +32 -0
  25. data/lib/rbnacl/hmac/sha512256.rb +35 -0
  26. data/lib/rbnacl/keys/key_comparator.rb +59 -0
  27. data/lib/rbnacl/keys/private_key.rb +62 -0
  28. data/lib/rbnacl/keys/public_key.rb +38 -0
  29. data/lib/rbnacl/keys/signing_key.rb +74 -0
  30. data/lib/rbnacl/keys/verify_key.rb +76 -0
  31. data/lib/rbnacl/nacl.rb +132 -0
  32. data/lib/rbnacl/point.rb +67 -0
  33. data/lib/rbnacl/rake_tasks.rb +56 -0
  34. data/lib/rbnacl/random.rb +19 -0
  35. data/lib/rbnacl/random_nonce_box.rb +109 -0
  36. data/lib/rbnacl/secret_box.rb +86 -0
  37. data/lib/rbnacl/self_test.rb +118 -0
  38. data/lib/rbnacl/serializable.rb +23 -0
  39. data/lib/rbnacl/test_vectors.rb +69 -0
  40. data/lib/rbnacl/util.rb +137 -0
  41. data/lib/rbnacl/version.rb +5 -0
  42. data/rbnacl.gemspec +28 -0
  43. data/rbnacl.gpg +30 -0
  44. data/spec/rbnacl/auth/one_time_spec.rb +8 -0
  45. data/spec/rbnacl/box_spec.rb +42 -0
  46. data/spec/rbnacl/encoder_spec.rb +14 -0
  47. data/spec/rbnacl/encoders/base32_spec.rb +16 -0
  48. data/spec/rbnacl/encoders/base64_spec.rb +15 -0
  49. data/spec/rbnacl/encoders/hex_spec.rb +15 -0
  50. data/spec/rbnacl/hash_spec.rb +52 -0
  51. data/spec/rbnacl/hmac/sha256_spec.rb +8 -0
  52. data/spec/rbnacl/hmac/sha512256_spec.rb +8 -0
  53. data/spec/rbnacl/keys/private_key_spec.rb +68 -0
  54. data/spec/rbnacl/keys/public_key_spec.rb +45 -0
  55. data/spec/rbnacl/keys/signing_key_spec.rb +40 -0
  56. data/spec/rbnacl/keys/verify_key_spec.rb +51 -0
  57. data/spec/rbnacl/point_spec.rb +29 -0
  58. data/spec/rbnacl/random_nonce_box_spec.rb +78 -0
  59. data/spec/rbnacl/random_spec.rb +9 -0
  60. data/spec/rbnacl/secret_box_spec.rb +24 -0
  61. data/spec/rbnacl/util_spec.rb +119 -0
  62. data/spec/shared/authenticator.rb +114 -0
  63. data/spec/shared/box.rb +51 -0
  64. data/spec/shared/key_equality.rb +26 -0
  65. data/spec/spec_helper.rb +14 -0
  66. data/tasks/ci.rake +11 -0
  67. data/tasks/rspec.rake +7 -0
  68. metadata +187 -0
@@ -0,0 +1,62 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # Crypto::Box private key. Keep it safe
4
+ #
5
+ # This class generates and stores NaCL private keys, as well as providing a
6
+ # reference to the public key associated with this private key, if that's
7
+ # provided.
8
+ #
9
+ # Note that the documentation for NaCl refers to this as a secret key, but in
10
+ # this library its a private key, to avoid confusing the issue with the
11
+ # SecretBox, which does symmetric encryption.
12
+ class PrivateKey
13
+ include KeyComparator
14
+ include Serializable
15
+
16
+ # The size of the key, in bytes
17
+ BYTES = Crypto::NaCl::SECRETKEYBYTES
18
+
19
+ # Initializes a new PrivateKey for key operations.
20
+ #
21
+ # Takes the (optionally encoded) private key bytes. This class can then be
22
+ # used for various key operations, including deriving the corresponding
23
+ # PublicKey
24
+ #
25
+ # @param private_key [String] The private key
26
+ # @param key_encoding [Symbol] The encoding of the key
27
+ #
28
+ # @raise [Crypto::LengthError] If the key is not valid after decoding.
29
+ #
30
+ # @return A new PrivateKey
31
+ def initialize(private_key, key_encoding = :raw)
32
+ @private_key = Crypto::Encoder[key_encoding].decode(private_key)
33
+ Util.check_length(@private_key, BYTES, "Private key")
34
+ end
35
+
36
+ # Generates a new keypair
37
+ #
38
+ # @raise [Crypto::CryptoError] if key generation fails, due to insufficient randomness.
39
+ #
40
+ # @return [Crypto::PrivateKey] A new private key, with the associated public key also set.
41
+ def self.generate
42
+ pk = Util.zeros(NaCl::PUBLICKEYBYTES)
43
+ sk = Util.zeros(NaCl::SECRETKEYBYTES)
44
+ NaCl.crypto_box_keypair(pk, sk) || raise(CryptoError, "Failed to generate a key pair")
45
+ new(sk)
46
+ end
47
+
48
+ # The raw bytes of the key
49
+ #
50
+ # @return [String] the raw bytes.
51
+ def to_bytes
52
+ @private_key
53
+ end
54
+
55
+ # the public key associated with this private key
56
+ #
57
+ # @return [PublicKey] the key
58
+ def public_key
59
+ @public_key ||= PublicKey.new(Point.base.mult(to_bytes))
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # Crypto::Box public key. Send to your friends.
4
+ #
5
+ # This class stores the NaCL public key, and provides some convience
6
+ # functions for working with it.
7
+ class PublicKey
8
+ include KeyComparator
9
+ include Serializable
10
+
11
+ # The size of the key, in bytes
12
+ BYTES = Crypto::NaCl::PUBLICKEYBYTES
13
+
14
+ # Initializes a new PublicKey for key operations.
15
+ #
16
+ # Takes the (optionally encoded) public key bytes. This can be shared with
17
+ # many people and used to establish key pairs with their private key, for
18
+ # the exchanging of messages using a Crypto::Box
19
+ #
20
+ # @param public_key [String] The public key
21
+ # @param key_encoding [Symbol] The encoding of the key
22
+ #
23
+ # @raise [Crypto::LengthError] If the key is not valid after decoding.
24
+ #
25
+ # @return A new PublicKey
26
+ def initialize(public_key, key_encoding = :raw)
27
+ @public_key = Crypto::Encoder[key_encoding].decode(public_key)
28
+ Util.check_length(@public_key, BYTES, "Public key")
29
+ end
30
+
31
+ # The raw bytes of the key
32
+ #
33
+ # @return [String] the raw bytes.
34
+ def to_bytes
35
+ @public_key
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # Private key for producing digital signatures using the Ed25519 algorithm.
4
+ # Ed25519 provides a 128-bit security level, that is to say, all known attacks
5
+ # take at least 2^128 operations, providing the same security level as
6
+ # AES-128, NIST P-256, and RSA-3072.
7
+ #
8
+ # Signing keys are produced from a 32-byte (256-bit) random seed value.
9
+ # This value can be passed into the SigningKey constructoras a String
10
+ # whose bytesize is 32.
11
+ #
12
+ # The public VerifyKey can be computed from the private 32-byte seed value
13
+ # as well, eliminating the need to store a "keypair".
14
+ #
15
+ # SigningKey produces 64-byte (512-bit) signatures. The signatures are
16
+ # deterministic: signing the same message will always produce the same
17
+ # signature. This prevents "entropy failure" seen in other signature
18
+ # algorithms like DSA and ECDSA, where poor random number generators can
19
+ # leak enough information to recover the private key.
20
+ class SigningKey
21
+ include KeyComparator
22
+ include Serializable
23
+
24
+ attr_reader :verify_key
25
+
26
+ # Generate a random SigningKey
27
+ #
28
+ # @return [Crypto::SigningKey] Freshly-generated random SigningKey
29
+ def self.generate
30
+ new Crypto::Random.random_bytes(NaCl::SECRETKEYBYTES)
31
+ end
32
+
33
+ # Create a SigningKey from a seed value
34
+ #
35
+ # @param seed [String] Random 32-byte value (i.e. private key)
36
+ # @param encoding [Symbol] Parse seed from the given encoding
37
+ #
38
+ # @return [Crypto::SigningKey] Key which can sign messages
39
+ def initialize(seed, encoding = :raw)
40
+ seed = Encoder[encoding].decode(seed)
41
+
42
+ Util.check_length(seed, NaCl::SECRETKEYBYTES, "seed")
43
+
44
+ pk = Util.zeros(NaCl::PUBLICKEYBYTES)
45
+ sk = Util.zeros(NaCl::SECRETKEYBYTES * 2)
46
+
47
+ NaCl.crypto_sign_seed_keypair(pk, sk, seed) || raise(CryptoError, "Failed to generate a key pair")
48
+
49
+ @seed, @signing_key = seed, sk
50
+ @verify_key = VerifyKey.new(pk)
51
+ end
52
+
53
+ # Sign a message using this key
54
+ #
55
+ # @param message [String] Message to be signed by this key
56
+ # @param encoding [Symbol] Encode signature in the given format
57
+ #
58
+ # @return [String] Signature as bytes
59
+ def sign(message, encoding = :raw)
60
+ buffer = Util.prepend_zeros(NaCl::SIGNATUREBYTES, message)
61
+ buffer_len = Util.zeros(FFI::Type::LONG_LONG.size)
62
+
63
+ NaCl.crypto_sign(buffer, buffer_len, message, message.bytesize, @signing_key)
64
+
65
+ signature = buffer[0, NaCl::SIGNATUREBYTES]
66
+ Encoder[encoding].encode(signature)
67
+ end
68
+
69
+ # Return the raw seed value of this key
70
+ #
71
+ # @return [String] seed used to create this key
72
+ def to_bytes; @seed; end
73
+ end
74
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # The signature was forged or otherwise corrupt
4
+ class BadSignatureError < CryptoError; end
5
+
6
+ # The public key counterpart to an Ed25519 SigningKey for producing digital
7
+ # signatures. Like the name says, VerifyKeys can be used to verify that a
8
+ # given digital signature is authentic.
9
+ #
10
+ # For more information on the Ed25519 digital signature system, please see
11
+ # the SigningKey documentation.
12
+ class VerifyKey
13
+ include KeyComparator
14
+ include Serializable
15
+
16
+ # Create a new VerifyKey object from a serialized public key. The key can
17
+ # be decoded from any serialization format supported by the
18
+ # Crypto::Encoding system.
19
+ #
20
+ # @param key [String] Serialized Ed25519 public key
21
+ # @param encoding [Symbol] Parse key from the given encoding
22
+ #
23
+ # @return [Crypto::SigningKey] Key which can sign messages
24
+ def initialize(key, encoding = :raw)
25
+ key = Encoder[encoding].decode(key)
26
+ Util.check_length(key, NaCl::PUBLICKEYBYTES, "key")
27
+
28
+ @key = key
29
+ end
30
+
31
+ # Create a new VerifyKey object from a serialized public key. The key can
32
+ # be decoded from any serialization format supported by the
33
+ # Crypto::Encoding system.
34
+ #
35
+ # You can remember the argument ordering by "verify message with signature"
36
+ # It's like a legal document, with the signature at the end.
37
+ #
38
+ # @param message [String] Message to be authenticated
39
+ # @param signature [String] Alleged signature to be checked
40
+ # @param signature_encoding [Symbol] Parse signature from the given encoding
41
+ #
42
+ # @return [Boolean] was the signature authentic?
43
+ def verify(message, signature, signature_encoding = :raw)
44
+ signature = Encoder[signature_encoding].decode(signature)
45
+ Util.check_length(signature, NaCl::SIGNATUREBYTES, "signature")
46
+
47
+ sig_and_msg = signature + message
48
+ buffer = Util.zeros(sig_and_msg.bytesize)
49
+ buffer_len = Util.zeros(FFI::Type::LONG_LONG.size)
50
+
51
+ NaCl.crypto_sign_open(buffer, buffer_len, sig_and_msg, sig_and_msg.bytesize, @key)
52
+ end
53
+
54
+ # "Dangerous" (but probably safer) verify that raises an exception if a
55
+ # signature check fails. This is probably less likely to go unnoticed than
56
+ # an improperly checked verify, as you are forced to deal with the
57
+ # exception explicitly (and failing signature checks are certainly an
58
+ # exceptional condition!)
59
+ #
60
+ # The arguments are otherwise the same as the verify method.
61
+ #
62
+ # @param message [String] Message to be authenticated
63
+ # @param signature [String] Alleged signature to be checked
64
+ # @param signature_encoding [Symbol] Parse signature from the given encoding
65
+ #
66
+ # @return [true] Will raise BadSignatureError if signature check fails
67
+ def verify!(message, signature, signature_encoding = :raw)
68
+ verify(message, signature, signature_encoding) or raise BadSignatureError, "signature was forged/corrupt"
69
+ end
70
+
71
+ # Return the raw key in byte format
72
+ #
73
+ # @return [String] raw key as bytes
74
+ def to_bytes; @key; end
75
+ end
76
+ end
@@ -0,0 +1,132 @@
1
+ # encoding: binary
2
+ require 'ffi'
3
+ module Crypto
4
+ # This module has all the FFI code hanging off it
5
+ #
6
+ # And that's all it does, really.
7
+ #
8
+ # @private
9
+ module NaCl
10
+ extend FFI::Library
11
+ ffi_lib 'sodium'
12
+
13
+ # Wraps an NaCl function so it returns a sane value
14
+ #
15
+ # The NaCl functions generally have an integer return value which is 0 in
16
+ # the case of success and below 0 if they failed. This is a bit
17
+ # inconvinient in ruby, where 0 is a truthy value, so this makes them
18
+ # return true/false based on success.
19
+ #
20
+ # @param [Symbol] name Function name that will return true/false
21
+ # @param [Symbol] function Function to attach
22
+ # @param [Array<Symbol>] arguments Array of arguments to the function
23
+ def self.wrap_nacl_function(name, function, arguments)
24
+ module_eval <<-eos, __FILE__, __LINE__ + 1
25
+ attach_function #{function.inspect}, #{arguments.inspect}, :int
26
+ def self.#{name}(*args)
27
+ ret = #{function}(*args)
28
+ ret == 0
29
+ end
30
+ eos
31
+ end
32
+
33
+ SHA256BYTES = 32
34
+ wrap_nacl_function :crypto_hash_sha256,
35
+ :crypto_hash_sha256_ref,
36
+ [:pointer, :string, :long_long]
37
+
38
+ SHA512BYTES = 64
39
+ wrap_nacl_function :crypto_hash_sha512,
40
+ :crypto_hash_sha512_ref,
41
+ [:pointer, :string, :long_long]
42
+
43
+ PUBLICKEYBYTES = 32
44
+ SECRETKEYBYTES = 32
45
+ wrap_nacl_function :crypto_box_keypair,
46
+ :crypto_box_curve25519xsalsa20poly1305_ref_keypair,
47
+ [:pointer, :pointer]
48
+
49
+ NONCEBYTES = 24
50
+ ZEROBYTES = 32
51
+ BOXZEROBYTES = 16
52
+ BEFORENMBYTES = 32
53
+
54
+ wrap_nacl_function :crypto_box_beforenm,
55
+ :crypto_box_curve25519xsalsa20poly1305_ref_beforenm,
56
+ [:pointer, :pointer, :pointer]
57
+
58
+ wrap_nacl_function :crypto_box_afternm,
59
+ :crypto_box_curve25519xsalsa20poly1305_ref_afternm,
60
+ [:pointer, :pointer, :long_long, :pointer, :pointer]
61
+
62
+ wrap_nacl_function :crypto_box_open_afternm,
63
+ :crypto_box_curve25519xsalsa20poly1305_ref_open_afternm,
64
+ [:pointer, :pointer, :long_long, :pointer, :pointer]
65
+
66
+ SECRETBOX_KEYBYTES = 32
67
+ wrap_nacl_function :crypto_secretbox,
68
+ :crypto_secretbox_xsalsa20poly1305_ref,
69
+ [:pointer, :pointer, :long_long, :pointer, :pointer]
70
+
71
+ wrap_nacl_function :crypto_secretbox_open,
72
+ :crypto_secretbox_xsalsa20poly1305_ref_open,
73
+ [:pointer, :pointer, :long_long, :pointer, :pointer]
74
+
75
+ HMACSHA512256_KEYBYTES = 32
76
+ HMACSHA512256_BYTES = 32
77
+ wrap_nacl_function :crypto_auth_hmacsha512256,
78
+ :crypto_auth_hmacsha512256_ref,
79
+ [:pointer, :pointer, :long_long, :pointer]
80
+ wrap_nacl_function :crypto_auth_hmacsha512256_verify,
81
+ :crypto_auth_hmacsha512256_ref_verify,
82
+ [:pointer, :pointer, :long_long, :pointer]
83
+
84
+ HMACSHA256_KEYBYTES = 32
85
+ HMACSHA256_BYTES = 32
86
+ wrap_nacl_function :crypto_auth_hmacsha256,
87
+ :crypto_auth_hmacsha256_ref,
88
+ [:pointer, :pointer, :long_long, :pointer]
89
+ wrap_nacl_function :crypto_auth_hmacsha256_verify,
90
+ :crypto_auth_hmacsha256_ref_verify,
91
+ [:pointer, :pointer, :long_long, :pointer]
92
+
93
+ ONETIME_KEYBYTES = 32
94
+ ONETIME_BYTES = 16
95
+ wrap_nacl_function :crypto_auth_onetime,
96
+ :crypto_onetimeauth_poly1305_ref,
97
+ [:pointer, :pointer, :long_long, :pointer]
98
+ wrap_nacl_function :crypto_auth_onetime_verify,
99
+ :crypto_onetimeauth_poly1305_ref_verify,
100
+ [:pointer, :pointer, :long_long, :pointer]
101
+
102
+ wrap_nacl_function :random_bytes,
103
+ :randombytes,
104
+ [:pointer, :long_long]
105
+
106
+ wrap_nacl_function :crypto_verify_32,
107
+ :crypto_verify_32_ref,
108
+ [:pointer, :pointer]
109
+ wrap_nacl_function :crypto_verify_16,
110
+ :crypto_verify_16_ref,
111
+ [:pointer, :pointer]
112
+
113
+ SIGNATUREBYTES = 64
114
+ wrap_nacl_function :crypto_sign_seed_keypair,
115
+ :crypto_sign_ed25519_ref_seed_keypair,
116
+ [:pointer, :pointer, :pointer]
117
+
118
+ wrap_nacl_function :crypto_sign,
119
+ :crypto_sign_ed25519_ref,
120
+ [:pointer, :pointer, :pointer, :long_long, :pointer]
121
+
122
+ wrap_nacl_function :crypto_sign_open,
123
+ :crypto_sign_ed25519_ref_open,
124
+ [:pointer, :pointer, :pointer, :long_long, :pointer]
125
+
126
+ SCALARBYTES = 32
127
+
128
+ wrap_nacl_function :crypto_scalarmult,
129
+ :crypto_scalarmult_curve25519_ref,
130
+ [:pointer, :pointer, :pointer]
131
+ end
132
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # NaCl's base point (a.k.a. standard group element), serialized as hex
4
+ STANDARD_GROUP_ELEMENT = "0900000000000000000000000000000000000000000000000000000000000000".freeze
5
+
6
+ # Order of the standard group
7
+ STANDARD_GROUP_ORDER = 2**252 + 27742317777372353535851937790883648493
8
+
9
+ # Points provide the interface to NaCl's Curve25519 high-speed elliptic
10
+ # curve cryptography, which can be used for implementing Diffie-Hellman
11
+ # and other forms of public key cryptography (e.g. Crypto::Box)
12
+ #
13
+ # Objects of the Point class represent points on Edwards curves. NaCl
14
+ # defines a base point (the "standard group element") which we can
15
+ # multiply by an arbitrary integer. This is how NaCl computes public
16
+ # keys from private keys.
17
+ class Point
18
+ include KeyComparator
19
+ include Serializable
20
+
21
+ # Creates a new Point from the given serialization
22
+ #
23
+ # @param value [String] 32-byte value representing a group element
24
+ # @param encoding [Symbol] The encoding format of the group element
25
+ #
26
+ # @return [Crypto::Point] New Crypto::Point object
27
+ def initialize(value, encoding = :raw)
28
+ @point = Encoder[encoding].decode(value)
29
+
30
+ # FIXME: really should have a separate constant here for group element size
31
+ # Group elements and scalars are both 32-bits, but that's for convenience
32
+ Util.check_length(@point, NaCl::SCALARBYTES, "group element")
33
+ end
34
+
35
+ # Multiply the given integer by this point
36
+ # This ordering is a bit confusing because traditionally the point
37
+ # would be the right-hand operand.
38
+ #
39
+ # @param integer [String] 32-byte integer value
40
+ # @param encoding [Symbol] The encoding format of the integer
41
+ #
42
+ # @return [Crypto::Point] Result as a Point object
43
+ def mult(integer, encoding = :raw)
44
+ integer = Encoder[encoding].decode(integer)
45
+ Util.check_length(integer, NaCl::SCALARBYTES, "integer")
46
+
47
+ result = Util.zeros(NaCl::SCALARBYTES)
48
+ NaCl.crypto_scalarmult(result, integer, @point)
49
+
50
+ self.class.new(result)
51
+ end
52
+
53
+ # Return the point serialized as bytes
54
+ #
55
+ # @return [String] 32-byte string representing this point
56
+ def to_bytes; @point; end
57
+
58
+ @base_point = Point.new(STANDARD_GROUP_ELEMENT, :hex)
59
+
60
+ # NaCl's standard base point for all Curve25519 public keys
61
+ #
62
+ # @return [Crypto::Point] standard base point (a.k.a. standard group element)
63
+ def self.base; @base_point; end
64
+ def self.base_point; @base_point; end
65
+ end
66
+ end
67
+