rbnacl 1.0.0.pre

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