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,141 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # The Box class boxes and unboxes messages between a pair of keys
4
+ #
5
+ # This class uses the given public and secret keys to derive a shared key,
6
+ # which is used with the nonce given to encrypt the given messages and
7
+ # decrypt the given ciphertexts. The same shared key will generated from
8
+ # both pairing of keys, so given two keypairs belonging to alice (pkalice,
9
+ # skalice) and bob(pkbob, skbob), the key derived from (pkalice, skbob) with
10
+ # equal that from (pkbob, skalice). This is how the system works:
11
+ #
12
+ # @example
13
+ # # On bob's system
14
+ # bobkey = Crypto::PrivateKey.generate
15
+ # #=> #<Crypto::PrivateKey ...>
16
+ #
17
+ # # send bobkey.public_key to alice
18
+ # # recieve alice's public key, alicepk
19
+ # # NB: This is actually the hard part of the system. How to do it securely
20
+ # # is left as an exercise to for the reader.
21
+ # alice_pubkey = "..."
22
+ #
23
+ # # make a box
24
+ # alicebob_box = Crypto::Box.new(alice_pubkey, bobkey)
25
+ # #=> #<Crypto::Box ...>
26
+ #
27
+ # # encrypt a message to alice
28
+ # cipher_text = alicebob_box.box("A bad example of a nonce", "Hello, Alice!")
29
+ # #=> "..." # a string of bytes, 29 bytes long
30
+ #
31
+ # # send ["A bad example of a nonce", cipher_text] to alice
32
+ # # note that nonces don't have to be secret
33
+ # # receive [nonce, cipher_text_to_bob] from alice
34
+ #
35
+ # # decrypt the reply
36
+ # # Alice has been a little more sensible than bob, and has a random nonce
37
+ # # that is too fiddly to type here. But there are other choices than just
38
+ # # random
39
+ # plain_text = alicebob_box.open(nonce, cipher_text_to_bob)
40
+ # #=> "Hey there, Bob!"
41
+ #
42
+ # # we have a new message!
43
+ # # But Eve has tampered with this message, by flipping some bytes around!
44
+ # # [nonce2, cipher_text_to_bob_honest_love_eve]
45
+ # alicebob_box.open(nonce2, cipher_text_to_bob_honest_love_eve)
46
+ #
47
+ # # BOOM!
48
+ # # Bob gets a Crypto::CryptoError to deal with!
49
+ #
50
+ # It is VITALLY important that the nonce is a nonce, i.e. it is a number used
51
+ # only once for any given pair of keys. If you fail to do this, you
52
+ # compromise the privacy of the the messages encrypted. Also, bear in mind
53
+ # the property mentioned just above. Give your nonces a different prefix, or
54
+ # have one side use an odd counter and one an even counter. Just make sure
55
+ # they are different.
56
+ #
57
+ # The ciphertexts generated by this class include a 16-byte authenticator which
58
+ # is checked as part of the decryption. An invalid authenticator will cause
59
+ # the unbox function to raise. The authenticator is not a signature. Once
60
+ # you've looked in the box, you've demonstrated the ability to create
61
+ # arbitrary valid messages, so messages you send are repudiatable. For
62
+ # non-repudiatable messages, sign them before or after encryption.
63
+ class Box
64
+
65
+ # Create a new Box
66
+ #
67
+ # Sets up the Box for deriving the shared key and encrypting and
68
+ # decrypting messages.
69
+ #
70
+ # @param public_key [String,Crypto::PublicKey] The public key to encrypt to
71
+ # @param private_key [String,Crypto::PrivateKey] The private key to encrypt with
72
+ # @param encoding [Symbol] Parse keys from the given encoding
73
+ #
74
+ # @raise [Crypto::LengthError] on invalid keys
75
+ #
76
+ # @return [Crypto::Box] The new Box, ready to use
77
+ def initialize(public_key, private_key, encoding = :raw)
78
+ @public_key = Encoder[encoding].decode(public_key) if public_key
79
+ @private_key = Encoder[encoding].decode(private_key) if private_key
80
+ Util.check_length(@public_key, PublicKey::BYTES, "Public key")
81
+ Util.check_length(@private_key, PrivateKey::BYTES, "Private key")
82
+ end
83
+
84
+ # Encrypts a message
85
+ #
86
+ # Encrypts the message with the given nonce to the keypair set up when
87
+ # initializing the class. Make sure the nonce is unique for any given
88
+ # keypair, or you might as well just send plain text.
89
+ #
90
+ # This function takes care of the padding required by the NaCL C API.
91
+ #
92
+ # @param nonce [String] A 24-byte string containing the nonce.
93
+ # @param message [String] The message to be encrypted.
94
+ #
95
+ # @raise [Crypto::LengthError] If the nonce is not valid
96
+ #
97
+ # @return [String] The ciphertext without the nonce prepended (BINARY encoded)
98
+ def box(nonce, message)
99
+ Util.check_length(nonce, Crypto::NaCl::NONCEBYTES, "Nonce")
100
+ msg = Util.prepend_zeros(NaCl::ZEROBYTES, message)
101
+ ct = Util.zeros(msg.bytesize)
102
+
103
+ NaCl.crypto_box_afternm(ct, msg, msg.bytesize, nonce, beforenm) || raise(CryptoError, "Encryption failed")
104
+ Util.remove_zeros(NaCl::BOXZEROBYTES, ct)
105
+ end
106
+ alias encrypt box
107
+
108
+ # Decrypts a ciphertext
109
+ #
110
+ # Decrypts the ciphertext with the given nonce using the keypair setup when
111
+ # initializing the class.
112
+ #
113
+ # This function takes care of the padding required by the NaCL C API.
114
+ #
115
+ # @param nonce [String] A 24-byte string containing the nonce.
116
+ # @param ciphertext [String] The message to be decrypted.
117
+ #
118
+ # @raise [Crypto::LengthError] If the nonce is not valid
119
+ # @raise [Crypto::CryptoError] If the ciphertext cannot be authenticated.
120
+ #
121
+ # @return [String] The decrypted message (BINARY encoded)
122
+ def open(nonce, ciphertext)
123
+ Util.check_length(nonce, Crypto::NaCl::NONCEBYTES, "Nonce")
124
+ ct = Util.prepend_zeros(NaCl::BOXZEROBYTES, ciphertext)
125
+ message = Util.zeros(ct.bytesize)
126
+
127
+ NaCl.crypto_box_open_afternm(message, ct, ct.bytesize, nonce, beforenm) || raise(CryptoError, "Decryption failed. Ciphertext failed verification.")
128
+ Util.remove_zeros(NaCl::ZEROBYTES, message)
129
+ end
130
+ alias decrypt open
131
+
132
+ private
133
+ def beforenm
134
+ @k ||= begin
135
+ k = Util.zeros(NaCl::BEFORENMBYTES)
136
+ NaCl.crypto_box_beforenm(k, @public_key, @private_key) || raise(CryptoError, "Failed to derive shared key")
137
+ k
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # Encoders can be used to serialize or deserialize keys, ciphertexts, hashes,
4
+ # and signatures. To provide an encoder, simply subclass Encoder and call the
5
+ # register class method, then define the encode and decode methods:
6
+ #
7
+ # class CrazysauceEncoder < Crypto::Encoder
8
+ # register :crazysauce
9
+ #
10
+ # def encode(string)
11
+ # ...
12
+ # end
13
+ #
14
+ # def decode(string)
15
+ # ...
16
+ # end
17
+ # end
18
+ #
19
+ # Once an encoder has been registered, an instance of it is available via
20
+ # calling Crypto::Encoder[], e.g. Crypto::Encoder[:hex].encode("foobar")
21
+ #
22
+ class Encoder
23
+ # Hash where encoder objects are stored
24
+ Registry = {}
25
+
26
+ # Register the current class as an encoder
27
+ def self.register(name)
28
+ self[name] = self.new
29
+ end
30
+
31
+ # Look up an encoder by the given name
32
+ def self.[](name)
33
+ Registry[name.to_sym] or raise ArgumentError, "unsupported encoder: #{name}"
34
+ end
35
+
36
+ # Register an encoder object directly
37
+ def self.[]=(name, obj)
38
+ Registry[name.to_sym] = obj
39
+ end
40
+
41
+ def encode(string); raise NotImplementedError, "encoding not implemented"; end
42
+ def decode(string); raise NotImplementedError, "decoding not implemented"; end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: binary
2
+ # Requires the base32 gem
3
+ require 'base32'
4
+
5
+ module Crypto
6
+ module Encoders
7
+ # Base64 encoding provider
8
+ #
9
+ # Accessable as Crypto::Encoder[:base64]
10
+ #
11
+ class Base32 < Crypto::Encoder
12
+ register :base32
13
+
14
+ # Base64 encodes a message
15
+ #
16
+ # @param [String] bytes The bytes to encode
17
+ #
18
+ # @return [String] Lovely, elegant "Zooko-style" Base32
19
+ def encode(bytes)
20
+ ::Base32.encode(bytes.to_s).downcase
21
+ end
22
+
23
+ # Hex decodes a message
24
+ #
25
+ # @param [String] base32 string to decode.
26
+ #
27
+ # @return [String] crisp and clean bytes
28
+ def decode(base32)
29
+ ::Base32.decode(base32.to_s.upcase)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ module Encoders
4
+ # Base64 encoding provider
5
+ #
6
+ # Accessable as Crypto::Encoder[:base64]
7
+ #
8
+ class Base64 < Crypto::Encoder
9
+ register :base64
10
+
11
+ # Base64 encodes a message
12
+ #
13
+ # @param [String] bytes The bytes to encode
14
+ #
15
+ # @return [String] Clunky old base64
16
+ def encode(bytes)
17
+ [bytes.to_s].pack("m").gsub("\n", '')
18
+ end
19
+
20
+ # Hex decodes a message
21
+ #
22
+ # @param [String] base64 string to decode.
23
+ #
24
+ # @return [String] crisp and clean bytes
25
+ def decode(base64)
26
+ base64.to_s.unpack("m").first
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ module Encoders
4
+ # Hex encoding provider
5
+ #
6
+ # Accessable as Crypto::Encoder[:hex]
7
+ #
8
+ class Hex < Crypto::Encoder
9
+ register :hex
10
+
11
+ # Hex encodes a message
12
+ #
13
+ # @param [String] bytes The bytes to encode
14
+ #
15
+ # @return [String] Tasty, tasty hexidecimal
16
+ def encode(bytes)
17
+ bytes.to_s.unpack("H*").first
18
+ end
19
+
20
+ # Hex decodes a message
21
+ #
22
+ # @param [String] hex hex to decode.
23
+ #
24
+ # @return [String] crisp and clean bytes
25
+ def decode(hex)
26
+ [hex.to_s].pack("H*")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ module Encoders
4
+ # Raw encoder which only does a string conversion (if necessary)
5
+ class Raw < Crypto::Encoder
6
+ register :raw
7
+
8
+ def encode(bytes); bytes.to_s; end
9
+ def decode(bytes); bytes.to_s; end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # Cryptographic hash functions
4
+ #
5
+ # Cryptographic hash functions take a variable length message and compute a
6
+ # fixed length string, the message digest. Even a small change in the input
7
+ # data should produce a large change in the digest, and it is 'very difficult'
8
+ # to create two messages with the same digest.
9
+ #
10
+ # A cryptographic hash can be used for checking the integrity of data, but
11
+ # there is no secret involved in the hashing, so anyone can create the hash of
12
+ # a given message.
13
+ #
14
+ # RbNaCl provides the SHA-256 and SHA-512 hash functions.
15
+ module Hash
16
+ # Returns the SHA-256 hash of the given data
17
+ #
18
+ # There's no streaming done, just pass in the data and be done with it.
19
+ #
20
+ # @param [String] data The data, as a collection of bytes
21
+ # @param [#to_sym] encoding Encoding of the returned hash.
22
+ #
23
+ # @raise [CryptoError] If the hashing fails for some reason.
24
+ #
25
+ # @return [String] The SHA-256 hash as raw bytes (Or encoded as per the second argument)
26
+ def self.sha256(data, encoding = :raw)
27
+ hash = Util.zeros(NaCl::SHA256BYTES)
28
+ NaCl.crypto_hash_sha256(hash, data, data.bytesize) || raise(CryptoError, "Hashing failed!")
29
+ Encoder[encoding].encode(hash)
30
+ end
31
+
32
+ # Returns the SHA-512 hash of the given data
33
+ #
34
+ # There's no streaming done, just pass in the data and be done with it.
35
+ #
36
+ # @param [String] data The data, as a collection of bytes
37
+ # @param [#to_sym] encoding Encoding of the returned hash.
38
+ #
39
+ # @raise [CryptoError] If the hashing fails for some reason.
40
+ #
41
+ # @return [String] The SHA-512 hash as raw bytes (Or encoded as per the second argument)
42
+ def self.sha512(data, encoding = :raw)
43
+ hash = Util.zeros(NaCl::SHA512BYTES)
44
+ NaCl.crypto_hash_sha512(hash, data, data.bytesize) || raise(CryptoError, "Hashing failed!")
45
+ Encoder[encoding].encode(hash)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ module HMAC
4
+ # Computes an authenticator as HMAC-SHA-256
5
+ #
6
+ # The authenticator can be used at a later time to verify the provenence of
7
+ # the message by recomputing the HMAC over the message and then comparing it to
8
+ # the provided authenticator. The class provides methods for generating
9
+ # signatures and also has a constant-time implementation for checking them.
10
+ #
11
+ # This is a secret key authenticator, i.e. anyone who can verify signatures
12
+ # can also create them.
13
+ #
14
+ # @see http://nacl.cr.yp.to/auth.html
15
+ class SHA256 < Auth
16
+ # Number of bytes in a valid key
17
+ KEYBYTES = NaCl::HMACSHA256_KEYBYTES
18
+
19
+ # Number of bytes in a valid authenticator
20
+ BYTES = NaCl::HMACSHA256_BYTES
21
+
22
+ private
23
+ def compute_authenticator(message, authenticator)
24
+ NaCl.crypto_auth_hmacsha256(authenticator, message, message.bytesize, key)
25
+ end
26
+
27
+ def verify_message(message, authenticator)
28
+ NaCl.crypto_auth_hmacsha256_verify(authenticator, message, message.bytesize, key)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ module HMAC
4
+ # Computes an authenticator as HMAC-SHA-512 truncated to 256-bits
5
+ #
6
+ # The authenticator can be used at a later time to verify the provenence of
7
+ # the message by recomputing the HMAC over the message and then comparing it to
8
+ # the provided authenticator. The class provides methods for generating
9
+ # signatures and also has a constant-time implementation for checking them.
10
+ #
11
+ # This is a secret key authenticator, i.e. anyone who can verify signatures
12
+ # can also create them.
13
+ #
14
+ # @see http://nacl.cr.yp.to/auth.html
15
+ class SHA512256 < Auth
16
+ # Number of bytes in a valid key
17
+ KEYBYTES = NaCl::HMACSHA512256_KEYBYTES
18
+
19
+ # Number of bytes in a valid authenticator
20
+ BYTES = NaCl::HMACSHA512256_BYTES
21
+
22
+ private
23
+ def compute_authenticator(message, authenticator)
24
+ NaCl.crypto_auth_hmacsha512256(authenticator, message, message.bytesize, key)
25
+ end
26
+
27
+ def verify_message(message, authenticator)
28
+ NaCl.crypto_auth_hmacsha512256_verify(authenticator, message, message.bytesize, key)
29
+ end
30
+ end
31
+
32
+ # TIMTOWTDI!
33
+ SHA512 = SHA512256
34
+ end
35
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: binary
2
+ module Crypto
3
+ # Implements comparisons of keys
4
+ #
5
+ # This permits both timing invariant equality tests, as well as
6
+ # lexicographical sorting.
7
+ module KeyComparator
8
+ include Comparable
9
+ # spaceship operator
10
+ #
11
+ # @param other [KeyComparator,#to_str] The thing to compare
12
+ #
13
+ # @return [0] if the keys are equal
14
+ # @return [1] if the key is larger than the other key
15
+ # @return [-1] if the key is smaller than the other key
16
+ # @return [nil] if comparison doesn't make sense
17
+ def <=>(other)
18
+ if KeyComparator > other.class
19
+ other = other.to_bytes
20
+ elsif other.respond_to?(:to_str)
21
+ other = other.to_str
22
+ else
23
+ return nil
24
+ end
25
+
26
+ if Util.verify32(self.to_bytes, other)
27
+ return 0
28
+ elsif self.to_bytes > other
29
+ return 1
30
+ else
31
+ return -1
32
+ end
33
+ end
34
+
35
+ # equality operator
36
+ #
37
+ # The equality operator is explicity defined, despite including Comparable
38
+ # and having a spaceship operator, so that if equality tests are desired,
39
+ # they can be timing invariant, without any chance that the further
40
+ # comparisons for greater than and less than can leak information. Maybe
41
+ # this is too paranoid, but I don't know how ruby works under the hood with
42
+ # comparable.
43
+ #
44
+ # @param other [KeyComparator,#to_str] The thing to compare
45
+ #
46
+ # @return [true] if the keys are equal
47
+ # @return [false] if they keys are not equal
48
+ def ==(other)
49
+ if KeyComparator > other.class
50
+ other = other.to_bytes
51
+ elsif other.respond_to?(:to_str)
52
+ other = other.to_str
53
+ else
54
+ return false
55
+ end
56
+ Util.verify32(self.to_bytes, other)
57
+ end
58
+ end
59
+ end