rbnacl 1.1.0 → 2.0.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.travis.yml +1 -5
- data/CHANGES.md +27 -0
- data/Gemfile +4 -1
- data/Guardfile +8 -0
- data/README.md +52 -3
- data/lib/rbnacl/auth.rb +22 -19
- data/lib/rbnacl/{keys → boxes/curve25519xsalsa20poly1305}/private_key.rb +26 -23
- data/lib/rbnacl/{keys → boxes/curve25519xsalsa20poly1305}/public_key.rb +13 -12
- data/lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb +185 -0
- data/lib/rbnacl/group_elements/curve25519.rb +81 -0
- data/lib/rbnacl/hash/blake2b.rb +64 -0
- data/lib/rbnacl/hash/sha256.rb +15 -0
- data/lib/rbnacl/hash/sha512.rb +15 -0
- data/lib/rbnacl/hash.rb +30 -14
- data/lib/rbnacl/hmac/sha256.rb +19 -17
- data/lib/rbnacl/hmac/sha512256.rb +18 -19
- data/lib/rbnacl/init.rb +10 -0
- data/lib/rbnacl/{keys/key_comparator.rb → key_comparator.rb} +1 -1
- data/lib/rbnacl/{auth/one_time.rb → one_time_auths/poly1305.rb} +21 -19
- data/lib/rbnacl/rake_tasks.rb +7 -6
- data/lib/rbnacl/random.rb +12 -3
- data/lib/rbnacl/random_nonce_box.rb +9 -14
- data/lib/rbnacl/secret_boxes/xsalsa20poly1305.rb +125 -0
- data/lib/rbnacl/self_test.rb +67 -43
- data/lib/rbnacl/serializable.rb +4 -12
- data/lib/rbnacl/signatures/ed25519/signing_key.rb +104 -0
- data/lib/rbnacl/signatures/ed25519/verify_key.rb +78 -0
- data/lib/rbnacl/signatures/ed25519.rb +15 -0
- data/lib/rbnacl/sodium/version.rb +23 -0
- data/lib/rbnacl/sodium.rb +43 -0
- data/lib/rbnacl/test_vectors.rb +34 -1
- data/lib/rbnacl/util.rb +50 -7
- data/lib/rbnacl/version.rb +2 -2
- data/lib/rbnacl.rb +64 -29
- data/rbnacl.gemspec +6 -5
- data/spec/rbnacl/{auth/one_time_spec.rb → authenticators/poly1305_spec.rb} +2 -2
- data/spec/rbnacl/boxes/curve25519xsalsa20poly1305/private_key_spec.rb +65 -0
- data/spec/rbnacl/{keys → boxes/curve25519xsalsa20poly1305}/public_key_spec.rb +10 -13
- data/spec/rbnacl/boxes/curve25519xsalsa20poly1305_spec.rb +39 -0
- data/spec/rbnacl/{point_spec.rb → group_element_spec.rb} +6 -8
- data/spec/rbnacl/hash/blake2b_spec.rb +30 -0
- data/spec/rbnacl/hash_spec.rb +13 -33
- data/spec/rbnacl/hmac/sha256_spec.rb +2 -2
- data/spec/rbnacl/hmac/sha512256_spec.rb +2 -2
- data/spec/rbnacl/random_nonce_box_spec.rb +21 -26
- data/spec/rbnacl/random_spec.rb +3 -3
- data/spec/rbnacl/secret_box_spec.rb +6 -6
- data/spec/rbnacl/signatures/ed25519/signing_key_spec.rb +30 -0
- data/spec/rbnacl/signatures/ed25519/verify_key_spec.rb +39 -0
- data/spec/rbnacl/util_spec.rb +67 -53
- data/spec/shared/authenticator.rb +36 -55
- data/spec/shared/box.rb +10 -10
- data/spec/shared/key_equality.rb +3 -3
- data/spec/shared/serializable.rb +17 -0
- data/spec/spec_helper.rb +15 -16
- data/tasks/rspec.rake +1 -0
- data.tar.gz.sig +2 -3
- metadata +41 -42
- metadata.gz.sig +2 -2
- data/lib/rbnacl/box.rb +0 -171
- data/lib/rbnacl/encoder.rb +0 -44
- data/lib/rbnacl/encoders/base32.rb +0 -33
- data/lib/rbnacl/encoders/base64.rb +0 -30
- data/lib/rbnacl/encoders/hex.rb +0 -30
- data/lib/rbnacl/encoders/raw.rb +0 -12
- data/lib/rbnacl/keys/signing_key.rb +0 -95
- data/lib/rbnacl/keys/verify_key.rb +0 -96
- data/lib/rbnacl/nacl.rb +0 -146
- data/lib/rbnacl/point.rb +0 -70
- data/lib/rbnacl/secret_box.rb +0 -119
- data/spec/rbnacl/box_spec.rb +0 -42
- data/spec/rbnacl/encoder_spec.rb +0 -14
- data/spec/rbnacl/encoders/base32_spec.rb +0 -16
- data/spec/rbnacl/encoders/base64_spec.rb +0 -15
- data/spec/rbnacl/encoders/hex_spec.rb +0 -15
- data/spec/rbnacl/keys/private_key_spec.rb +0 -68
- data/spec/rbnacl/keys/signing_key_spec.rb +0 -39
- data/spec/rbnacl/keys/verify_key_spec.rb +0 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4868c75138fed2d13cc2c2e101961e5149a2f950
|
|
4
|
+
data.tar.gz: 9b2bc9e35ff45cf67715ae49a86f3667dff2609a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6667126547e0562e40b3d653b998058a4cc2f4dab18063d636285ca957d5b3c500eac93966946bcf9989bcad24c312208edbb856b34d53391d6ce7d2eec97932
|
|
7
|
+
data.tar.gz: 4e2d2f6ac34fa25972361afa81e8d0ae1c08f6b344e3fba43cdba92790daf1b00c824b564f4c5a3a64070fb2b7a6f9698e546ede8ec9088295a43355528958cd
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
2.0.0 (2013-11-07)
|
|
2
|
+
------------------
|
|
3
|
+
* Add encrypt/decrypt aliases for Crypto::RandomNonceBox
|
|
4
|
+
* Rename Crypto module to RbNaCl module
|
|
5
|
+
* RbNaCl::VerifyKey#verify operand order was reversed. New operand order is
|
|
6
|
+
signature, message instead of message, signature
|
|
7
|
+
* RbNaCL::SecretBox#open, RbNaCl::Box#open, Auth#verify and VerifyKey#verify
|
|
8
|
+
all now raise a (descendent of) CryptoError if the check fails. This ensures
|
|
9
|
+
failures are handled by the program.
|
|
10
|
+
* RbNaCl::SecretBox, Box, etc. are all now aliases for the real implementations,
|
|
11
|
+
which are named after the primitives they provide
|
|
12
|
+
* Encoders have now gone.
|
|
13
|
+
* Add support for the Blake2b cryptographic hash algorithm.
|
|
14
|
+
* Add checks that we have a sufficiently recent version of libsodium (0.4.3+)
|
|
15
|
+
* Dropped ruby-1.8 support
|
|
16
|
+
* Call the `sodium_init()` function, to select the best algorithms.
|
|
17
|
+
* Fix some typos in the documentation
|
|
18
|
+
* Changes in the low level binding for libsodium and removal of the NaCl module
|
|
19
|
+
* Add a mutex around calls to randombytes in libsodium
|
|
20
|
+
|
|
21
|
+
1.1.0 (2013-04-19)
|
|
22
|
+
------------------
|
|
23
|
+
|
|
24
|
+
* Provide API for querying primitives and details about them, such as key
|
|
25
|
+
lengths, nonce lengths, etc.
|
|
26
|
+
* Fixed bug on passing null bytes to sha256, sha512 functions.
|
|
27
|
+
|
|
1
28
|
1.0.0 (2013-03-08)
|
|
2
29
|
------------------
|
|
3
30
|
* Initial release
|
data/Gemfile
CHANGED
data/Guardfile
ADDED
data/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
======
|
|
3
3
|
[](http://badge.fury.io/rb/rbnacl)
|
|
4
4
|
[](https://travis-ci.org/cryptosphere/rbnacl)
|
|
5
|
-
[](https://gemnasium.com/cryptosphere/rbnacl)
|
|
6
5
|
[](https://codeclimate.com/github/cryptosphere/rbnacl)
|
|
7
6
|
[](https://coveralls.io/r/cryptosphere/rbnacl)
|
|
8
7
|
|
|
@@ -47,6 +46,10 @@ is extremely fast with comparatively small cryptographic keys.
|
|
|
47
46
|
For more information on NaCl's goals, see Dan Bernstein's presentation
|
|
48
47
|
[Blaming the Cryptographic User](http://cr.yp.to/talks/2012.08.08/slides.pdf)
|
|
49
48
|
|
|
49
|
+
### Is it any good?
|
|
50
|
+
|
|
51
|
+
[Yes.](http://news.ycombinator.com/item?id=3067434)
|
|
52
|
+
|
|
50
53
|
## Supported platforms
|
|
51
54
|
|
|
52
55
|
You can use RbNaCl anywhere you can get libsodium installed (see below).
|
|
@@ -54,7 +57,6 @@ RbNaCl is continuously integration tested on the following Ruby VMs:
|
|
|
54
57
|
|
|
55
58
|
* MRI 2.0
|
|
56
59
|
* MRI 1.9 (YARV)
|
|
57
|
-
* MRI 1.8 / REE
|
|
58
60
|
* JRuby 1.7 (in both 1.8/1.9 mode)
|
|
59
61
|
* Rubinius HEAD (in both 1.8/1.9 mode)
|
|
60
62
|
|
|
@@ -200,6 +202,53 @@ Coursera offers from Stanford University Professor Dan Boneh:
|
|
|
200
202
|
|
|
201
203
|
[http://crypto-class.org](http://crypto-class.org)
|
|
202
204
|
|
|
205
|
+
## Important Questions
|
|
206
|
+
|
|
207
|
+
### Is it "Military Grade™"?
|
|
208
|
+
|
|
209
|
+
Only if your military understands twisted Edwards curves
|
|
210
|
+
|
|
211
|
+
### Does it have a lock with a checkmark?
|
|
212
|
+
|
|
213
|
+
Sure, here you go:
|
|
214
|
+
|
|
215
|
+

|
|
216
|
+
|
|
217
|
+
### Is it full of NSA backdoors?
|
|
218
|
+
|
|
219
|
+

|
|
220
|
+
|
|
221
|
+
The design of RbNaCl's primitives is completely free from NIST (and by
|
|
222
|
+
association, NSA) influence, with the following minor exceptions:
|
|
223
|
+
|
|
224
|
+
* The Poly1305 MAC, used for authenticating integrity of ciphertexts, uses AES
|
|
225
|
+
as a replaceable component
|
|
226
|
+
* The Ed25519 digital signature algorithm uses SHA-512 for both key derivation
|
|
227
|
+
and computing message digests
|
|
228
|
+
* APIs are provided to certain NIST hash functions, including SHA-256, SHA-512,
|
|
229
|
+
and their associated HMAC counterparts
|
|
230
|
+
|
|
231
|
+
Otherwise, all of the algorithms in NaCl were designed by Dan Bernstein and his
|
|
232
|
+
collaborators.
|
|
233
|
+
|
|
234
|
+
The design choices in NaCl, particularly in regard to the Curve25519
|
|
235
|
+
Diffie-Hellman function, emphasize security (whereas [NIST curves emphasize
|
|
236
|
+
"performance" at the cost of security][nist-security-dangers]), and "magic
|
|
237
|
+
constants" in NaCl are picked by theorems designed to maximize security.
|
|
238
|
+
The same cannot be said of NIST curves, where the specific origins of certain
|
|
239
|
+
constants are not described by the standards and may be subject to malicious
|
|
240
|
+
influence by the NSA.
|
|
241
|
+
|
|
242
|
+
It is the opinion of this library's authors that Dan Bernstein is unlikely to be
|
|
243
|
+
subject to NSA influence (although we have no way of actually knowing this).
|
|
244
|
+
|
|
245
|
+
Dan Bernstein's designs have been well-scrutinized both as part of the [ESTREAM
|
|
246
|
+
Project](https://en.wikipedia.org/wiki/ESTREAM) and the cryptographic community
|
|
247
|
+
as a whole. And despite the emphasis on higher security, NaCl's primitives are
|
|
248
|
+
faster across-the-board than most implementations of the NIST standards.
|
|
249
|
+
|
|
250
|
+
[nist-security-dangers]: http://www.hyperelliptic.org/tanja/vortraege/20130531.pdf
|
|
251
|
+
|
|
203
252
|
## Contributing
|
|
204
253
|
|
|
205
254
|
* Fork this repository on Github
|
|
@@ -208,5 +257,5 @@ Coursera offers from Stanford University Professor Dan Boneh:
|
|
|
208
257
|
|
|
209
258
|
## License
|
|
210
259
|
|
|
211
|
-
Copyright (c) 2013
|
|
260
|
+
Copyright (c) 2013 Jonathan Stott, Tony Arcieri.
|
|
212
261
|
Distributed under the MIT License. See LICENSE.txt for further details.
|
data/lib/rbnacl/auth.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# encoding: binary
|
|
2
|
-
module
|
|
2
|
+
module RbNaCl
|
|
3
|
+
|
|
3
4
|
# Secret Key Authenticators
|
|
4
5
|
#
|
|
5
6
|
# These provide a means of verifying the integrity of a message, but only
|
|
@@ -18,10 +19,8 @@ module Crypto
|
|
|
18
19
|
# A new authenticator, ready for auth and verification
|
|
19
20
|
#
|
|
20
21
|
# @param [#to_str] key the key used for authenticators, 32 bytes.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@key = Encoder[encoding].decode(key)
|
|
24
|
-
Util.check_length(@key, key_bytes, "#{self.class} key")
|
|
22
|
+
def initialize(key)
|
|
23
|
+
@key = Util.check_string(key, key_bytes, "#{self.class} key")
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
# Compute authenticator for message
|
|
@@ -37,38 +36,42 @@ module Crypto
|
|
|
37
36
|
# Verifies the given authenticator with the message.
|
|
38
37
|
#
|
|
39
38
|
# @param [#to_str] key the key used for the authenticator
|
|
40
|
-
# @param [#to_str] message the message to be authenticated
|
|
41
39
|
# @param [#to_str] authenticator to be checked
|
|
40
|
+
# @param [#to_str] message the message to be authenticated
|
|
41
|
+
#
|
|
42
|
+
# @raise [BadAuthenticatorError] if the tag isn't valid
|
|
43
|
+
# @raise [LengthError] if the tag is of the wrong length
|
|
42
44
|
#
|
|
43
45
|
# @return [Boolean] Was it valid?
|
|
44
|
-
def self.verify(key,
|
|
45
|
-
new(key).verify(
|
|
46
|
+
def self.verify(key, authenticator, message)
|
|
47
|
+
new(key).verify(authenticator, message)
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
# Compute authenticator for message
|
|
49
51
|
#
|
|
50
52
|
# @param [#to_str] message the message to authenticate
|
|
51
|
-
# @param [#to_sym] authenticator_encoding format of the authenticator (default raw)
|
|
52
53
|
#
|
|
53
54
|
# @return [String] The authenticator in the requested encoding (default raw)
|
|
54
|
-
def auth(message
|
|
55
|
+
def auth(message)
|
|
55
56
|
authenticator = Util.zeros(tag_bytes)
|
|
56
57
|
message = message.to_str
|
|
57
|
-
compute_authenticator(
|
|
58
|
-
|
|
58
|
+
compute_authenticator(authenticator, message)
|
|
59
|
+
authenticator
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
# Verifies the given authenticator with the message.
|
|
62
63
|
#
|
|
63
64
|
# @param [#to_str] authenticator to be checked
|
|
64
65
|
# @param [#to_str] message the message to be authenticated
|
|
65
|
-
#
|
|
66
|
+
#
|
|
67
|
+
# @raise [BadAuthenticatorError] if the tag isn't valid
|
|
68
|
+
# @raise [LengthError] if the tag is of the wrong length
|
|
66
69
|
#
|
|
67
70
|
# @return [Boolean] Was it valid?
|
|
68
|
-
def verify(
|
|
69
|
-
auth =
|
|
70
|
-
|
|
71
|
-
verify_message(message,
|
|
71
|
+
def verify(authenticator, message)
|
|
72
|
+
auth = authenticator.to_s
|
|
73
|
+
Util.check_length(auth, tag_bytes, "Provided authenticator")
|
|
74
|
+
verify_message(auth, message) || raise(BadAuthenticatorError, "Invalid authenticator provided, message is corrupt")
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
# The crypto primitive for this authenticator instance
|
|
@@ -99,7 +102,7 @@ module Crypto
|
|
|
99
102
|
def tag_bytes; self.class.tag_bytes; end
|
|
100
103
|
|
|
101
104
|
private
|
|
102
|
-
def compute_authenticator(
|
|
103
|
-
def verify_message(
|
|
105
|
+
def compute_authenticator(authenticator, message); raise NotImplementedError; end
|
|
106
|
+
def verify_message(authenticator, message); raise NotImplementedError; end
|
|
104
107
|
end
|
|
105
108
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# encoding: binary
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
module RbNaCl
|
|
4
|
+
# RbNaCl::Box private key. Keep it safe
|
|
4
5
|
#
|
|
5
6
|
# This class generates and stores NaCL private keys, as well as providing a
|
|
6
7
|
# reference to the public key associated with this private key, if that's
|
|
@@ -9,12 +10,22 @@ module Crypto
|
|
|
9
10
|
# Note that the documentation for NaCl refers to this as a secret key, but in
|
|
10
11
|
# this library its a private key, to avoid confusing the issue with the
|
|
11
12
|
# SecretBox, which does symmetric encryption.
|
|
12
|
-
class PrivateKey
|
|
13
|
+
class Boxes::Curve25519XSalsa20Poly1305::PrivateKey
|
|
14
|
+
|
|
13
15
|
include KeyComparator
|
|
14
16
|
include Serializable
|
|
17
|
+
|
|
18
|
+
extend Sodium
|
|
19
|
+
|
|
20
|
+
sodium_type :box
|
|
21
|
+
sodium_primitive :curve25519xsalsa20poly1305
|
|
22
|
+
|
|
23
|
+
sodium_function :box_curve25519xsalsa20poly1305_keypair,
|
|
24
|
+
:crypto_box_curve25519xsalsa20poly1305_keypair,
|
|
25
|
+
[:pointer, :pointer]
|
|
15
26
|
|
|
16
27
|
# The size of the key, in bytes
|
|
17
|
-
BYTES =
|
|
28
|
+
BYTES = Boxes::Curve25519XSalsa20Poly1305::PRIVATEKEYBYTES
|
|
18
29
|
|
|
19
30
|
# Initializes a new PrivateKey for key operations.
|
|
20
31
|
#
|
|
@@ -25,23 +36,23 @@ module Crypto
|
|
|
25
36
|
# @param private_key [String] The private key
|
|
26
37
|
# @param key_encoding [Symbol] The encoding of the key
|
|
27
38
|
#
|
|
28
|
-
# @raise [
|
|
39
|
+
# @raise [TypeError] If the key is nil
|
|
40
|
+
# @raise [RbNaCl::LengthError] If the key is not valid after decoding.
|
|
29
41
|
#
|
|
30
42
|
# @return A new PrivateKey
|
|
31
|
-
def initialize(private_key
|
|
32
|
-
@private_key =
|
|
33
|
-
Util.check_length(@private_key, BYTES, "Private key")
|
|
43
|
+
def initialize(private_key)
|
|
44
|
+
@private_key = Util.check_string(private_key, BYTES, "Private key")
|
|
34
45
|
end
|
|
35
46
|
|
|
36
47
|
# Generates a new keypair
|
|
37
48
|
#
|
|
38
|
-
# @raise [
|
|
49
|
+
# @raise [RbNaCl::CryptoError] if key generation fails, due to insufficient randomness.
|
|
39
50
|
#
|
|
40
|
-
# @return [
|
|
51
|
+
# @return [RbNaCl::PrivateKey] A new private key, with the associated public key also set.
|
|
41
52
|
def self.generate
|
|
42
|
-
pk = Util.zeros(
|
|
43
|
-
sk = Util.zeros(
|
|
44
|
-
|
|
53
|
+
pk = Util.zeros(Boxes::Curve25519XSalsa20Poly1305::PUBLICKEYBYTES)
|
|
54
|
+
sk = Util.zeros(Boxes::Curve25519XSalsa20Poly1305::PRIVATEKEYBYTES)
|
|
55
|
+
self.box_curve25519xsalsa20poly1305_keypair(pk, sk) || raise(CryptoError, "Failed to generate a key pair")
|
|
45
56
|
new(sk)
|
|
46
57
|
end
|
|
47
58
|
|
|
@@ -56,22 +67,14 @@ module Crypto
|
|
|
56
67
|
#
|
|
57
68
|
# @return [PublicKey] the key
|
|
58
69
|
def public_key
|
|
59
|
-
@public_key ||= PublicKey.new
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# The crypto primitive the PrivateKey class is to be used for
|
|
63
|
-
#
|
|
64
|
-
# @return [Symbol] The primitive
|
|
65
|
-
def self.primitive
|
|
66
|
-
:curve25519_xsalsa20_poly1305
|
|
70
|
+
@public_key ||= PublicKey.new GroupElements::Curve25519.base.mult(to_bytes)
|
|
67
71
|
end
|
|
68
|
-
|
|
72
|
+
|
|
69
73
|
# The crypto primitive this PrivateKey is to be used for.
|
|
70
74
|
#
|
|
71
75
|
# @return [Symbol] The primitive
|
|
72
76
|
def primitive
|
|
73
77
|
self.class.primitive
|
|
74
78
|
end
|
|
75
|
-
|
|
76
79
|
end
|
|
77
80
|
end
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
# encoding: binary
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
module RbNaCl
|
|
4
|
+
# RbNaCl::Box public key. Send it (securely!) to your friends.
|
|
4
5
|
#
|
|
5
|
-
# This class stores the NaCL public key, and provides some
|
|
6
|
+
# This class stores the NaCL public key, and provides some convenience
|
|
6
7
|
# functions for working with it.
|
|
7
|
-
class PublicKey
|
|
8
|
+
class Boxes::Curve25519XSalsa20Poly1305::PublicKey
|
|
9
|
+
|
|
8
10
|
include KeyComparator
|
|
9
11
|
include Serializable
|
|
10
12
|
|
|
11
13
|
# The size of the key, in bytes
|
|
12
|
-
BYTES =
|
|
14
|
+
BYTES = Boxes::Curve25519XSalsa20Poly1305::PUBLICKEYBYTES
|
|
13
15
|
|
|
14
16
|
# Initializes a new PublicKey for key operations.
|
|
15
17
|
#
|
|
16
18
|
# Takes the (optionally encoded) public key bytes. This can be shared with
|
|
17
19
|
# many people and used to establish key pairs with their private key, for
|
|
18
|
-
# the exchanging of messages using a
|
|
20
|
+
# the exchanging of messages using a RbNaCl::Box
|
|
19
21
|
#
|
|
20
22
|
# @param public_key [String] The public key
|
|
21
|
-
# @param key_encoding [Symbol] The encoding of the key
|
|
22
23
|
#
|
|
23
|
-
# @raise [
|
|
24
|
+
# @raise [RbNaCl::LengthError] If the key is not valid after decoding.
|
|
24
25
|
#
|
|
25
26
|
# @return A new PublicKey
|
|
26
|
-
def initialize(public_key
|
|
27
|
-
@public_key =
|
|
28
|
-
Util.check_length(@public_key, BYTES, "Public key")
|
|
27
|
+
def initialize(public_key)
|
|
28
|
+
@public_key = Util.check_string(public_key, BYTES, "Public key")
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# The raw bytes of the key
|
|
@@ -39,7 +39,7 @@ module Crypto
|
|
|
39
39
|
#
|
|
40
40
|
# @return [Symbol] The primitive
|
|
41
41
|
def self.primitive
|
|
42
|
-
:
|
|
42
|
+
:curve25519xsalsa20poly1305
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# The crypto primitive this PublicKey is to be used for.
|
|
@@ -49,4 +49,5 @@ module Crypto
|
|
|
49
49
|
self.class.primitive
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
|
+
|
|
52
53
|
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# encoding: binary
|
|
2
|
+
module RbNaCl
|
|
3
|
+
module Boxes
|
|
4
|
+
# The Box class boxes and unboxes messages between a pair of keys
|
|
5
|
+
#
|
|
6
|
+
# This class uses the given public and secret keys to derive a shared key,
|
|
7
|
+
# which is used with the nonce given to encrypt the given messages and
|
|
8
|
+
# decrypt the given ciphertexts. The same shared key will generated from
|
|
9
|
+
# both pairing of keys, so given two keypairs belonging to alice (pkalice,
|
|
10
|
+
# skalice) and bob(pkbob, skbob), the key derived from (pkalice, skbob) with
|
|
11
|
+
# equal that from (pkbob, skalice). This is how the system works:
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# # On bob's system
|
|
15
|
+
# bobkey = RbNaCl::PrivateKey.generate
|
|
16
|
+
# #=> #<RbNaCl::PrivateKey ...>
|
|
17
|
+
#
|
|
18
|
+
# # send bobkey.public_key to alice
|
|
19
|
+
# # recieve alice's public key, alicepk
|
|
20
|
+
# # NB: This is actually the hard part of the system. How to do it securely
|
|
21
|
+
# # is left as an exercise to for the reader.
|
|
22
|
+
# alice_pubkey = "..."
|
|
23
|
+
#
|
|
24
|
+
# # make a box
|
|
25
|
+
# alicebob_box = RbNaCl::Box.new(alice_pubkey, bobkey)
|
|
26
|
+
# #=> #<RbNaCl::Box ...>
|
|
27
|
+
#
|
|
28
|
+
# # encrypt a message to alice
|
|
29
|
+
# cipher_text = alicebob_box.box("A bad example of a nonce", "Hello, Alice!")
|
|
30
|
+
# #=> "..." # a string of bytes, 29 bytes long
|
|
31
|
+
#
|
|
32
|
+
# # send ["A bad example of a nonce", cipher_text] to alice
|
|
33
|
+
# # note that nonces don't have to be secret
|
|
34
|
+
# # receive [nonce, cipher_text_to_bob] from alice
|
|
35
|
+
#
|
|
36
|
+
# # decrypt the reply
|
|
37
|
+
# # Alice has been a little more sensible than bob, and has a random nonce
|
|
38
|
+
# # that is too fiddly to type here. But there are other choices than just
|
|
39
|
+
# # random
|
|
40
|
+
# plain_text = alicebob_box.open(nonce, cipher_text_to_bob)
|
|
41
|
+
# #=> "Hey there, Bob!"
|
|
42
|
+
#
|
|
43
|
+
# # we have a new message!
|
|
44
|
+
# # But Eve has tampered with this message, by flipping some bytes around!
|
|
45
|
+
# # [nonce2, cipher_text_to_bob_honest_love_eve]
|
|
46
|
+
# alicebob_box.open(nonce2, cipher_text_to_bob_honest_love_eve)
|
|
47
|
+
#
|
|
48
|
+
# # BOOM!
|
|
49
|
+
# # Bob gets a RbNaCl::CryptoError to deal with!
|
|
50
|
+
#
|
|
51
|
+
# It is VITALLY important that the nonce is a nonce, i.e. it is a number used
|
|
52
|
+
# only once for any given pair of keys. If you fail to do this, you
|
|
53
|
+
# compromise the privacy of the the messages encrypted. Also, bear in mind
|
|
54
|
+
# the property mentioned just above. Give your nonces a different prefix, or
|
|
55
|
+
# have one side use an odd counter and one an even counter. Just make sure
|
|
56
|
+
# they are different.
|
|
57
|
+
#
|
|
58
|
+
# The ciphertexts generated by this class include a 16-byte authenticator which
|
|
59
|
+
# is checked as part of the decryption. An invalid authenticator will cause
|
|
60
|
+
# the unbox function to raise. The authenticator is not a signature. Once
|
|
61
|
+
# you've looked in the box, you've demonstrated the ability to create
|
|
62
|
+
# arbitrary valid messages, so messages you send are repudiable. For
|
|
63
|
+
# non-repudiable messages, sign them before or after encryption.
|
|
64
|
+
class Curve25519XSalsa20Poly1305
|
|
65
|
+
extend Sodium
|
|
66
|
+
|
|
67
|
+
sodium_type :box
|
|
68
|
+
sodium_primitive :curve25519xsalsa20poly1305
|
|
69
|
+
sodium_constant :NONCEBYTES
|
|
70
|
+
sodium_constant :ZEROBYTES
|
|
71
|
+
sodium_constant :BOXZEROBYTES
|
|
72
|
+
sodium_constant :BEFORENMBYTES
|
|
73
|
+
sodium_constant :PUBLICKEYBYTES
|
|
74
|
+
sodium_constant :SECRETKEYBYTES, :PRIVATEKEYBYTES
|
|
75
|
+
|
|
76
|
+
sodium_function :box_curve25519xsalsa20poly1305_beforenm,
|
|
77
|
+
:crypto_box_curve25519xsalsa20poly1305_beforenm,
|
|
78
|
+
[:pointer, :pointer, :pointer]
|
|
79
|
+
|
|
80
|
+
sodium_function :box_curve25519xsalsa20poly1305_open_afternm,
|
|
81
|
+
:crypto_box_curve25519xsalsa20poly1305_open_afternm,
|
|
82
|
+
[:pointer, :pointer, :ulong_long, :pointer, :pointer]
|
|
83
|
+
|
|
84
|
+
sodium_function :box_curve25519xsalsa20poly1305_afternm,
|
|
85
|
+
:crypto_box_curve25519xsalsa20poly1305_afternm,
|
|
86
|
+
[:pointer, :pointer, :ulong_long, :pointer, :pointer]
|
|
87
|
+
|
|
88
|
+
# Create a new Box
|
|
89
|
+
#
|
|
90
|
+
# Sets up the Box for deriving the shared key and encrypting and
|
|
91
|
+
# decrypting messages.
|
|
92
|
+
#
|
|
93
|
+
# @param public_key [String,RbNaCl::PublicKey] The public key to encrypt to
|
|
94
|
+
# @param private_key [String,RbNaCl::PrivateKey] The private key to encrypt with
|
|
95
|
+
# @param encoding [Symbol] Parse keys from the given encoding
|
|
96
|
+
#
|
|
97
|
+
# @raise [RbNaCl::LengthError] on invalid keys
|
|
98
|
+
#
|
|
99
|
+
# @return [RbNaCl::Box] The new Box, ready to use
|
|
100
|
+
def initialize(public_key, private_key, encoding = :raw)
|
|
101
|
+
@public_key = PublicKey === public_key ? public_key : PublicKey.new(public_key)
|
|
102
|
+
@private_key = PrivateKey === private_key ? private_key : PrivateKey.new(private_key)
|
|
103
|
+
raise IncorrectPrimitiveError unless @public_key.primitive == primitive && @private_key.primitive == primitive
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Encrypts a message
|
|
107
|
+
#
|
|
108
|
+
# Encrypts the message with the given nonce to the keypair set up when
|
|
109
|
+
# initializing the class. Make sure the nonce is unique for any given
|
|
110
|
+
# keypair, or you might as well just send plain text.
|
|
111
|
+
#
|
|
112
|
+
# This function takes care of the padding required by the NaCL C API.
|
|
113
|
+
#
|
|
114
|
+
# @param nonce [String] A 24-byte string containing the nonce.
|
|
115
|
+
# @param message [String] The message to be encrypted.
|
|
116
|
+
#
|
|
117
|
+
# @raise [RbNaCl::LengthError] If the nonce is not valid
|
|
118
|
+
#
|
|
119
|
+
# @return [String] The ciphertext without the nonce prepended (BINARY encoded)
|
|
120
|
+
def box(nonce, message)
|
|
121
|
+
Util.check_length(nonce, nonce_bytes, "Nonce")
|
|
122
|
+
msg = Util.prepend_zeros(ZEROBYTES, message)
|
|
123
|
+
ct = Util.zeros(msg.bytesize)
|
|
124
|
+
|
|
125
|
+
self.class.box_curve25519xsalsa20poly1305_afternm(ct, msg, msg.bytesize, nonce, beforenm) || raise(CryptoError, "Encryption failed")
|
|
126
|
+
Util.remove_zeros(BOXZEROBYTES, ct)
|
|
127
|
+
end
|
|
128
|
+
alias encrypt box
|
|
129
|
+
|
|
130
|
+
# Decrypts a ciphertext
|
|
131
|
+
#
|
|
132
|
+
# Decrypts the ciphertext with the given nonce using the keypair setup when
|
|
133
|
+
# initializing the class.
|
|
134
|
+
#
|
|
135
|
+
# This function takes care of the padding required by the NaCL C API.
|
|
136
|
+
#
|
|
137
|
+
# @param nonce [String] A 24-byte string containing the nonce.
|
|
138
|
+
# @param ciphertext [String] The message to be decrypted.
|
|
139
|
+
#
|
|
140
|
+
# @raise [RbNaCl::LengthError] If the nonce is not valid
|
|
141
|
+
# @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated.
|
|
142
|
+
#
|
|
143
|
+
# @return [String] The decrypted message (BINARY encoded)
|
|
144
|
+
def open(nonce, ciphertext)
|
|
145
|
+
Util.check_length(nonce, nonce_bytes, "Nonce")
|
|
146
|
+
ct = Util.prepend_zeros(BOXZEROBYTES, ciphertext)
|
|
147
|
+
message = Util.zeros(ct.bytesize)
|
|
148
|
+
|
|
149
|
+
self.class.box_curve25519xsalsa20poly1305_open_afternm(message, ct, ct.bytesize, nonce, beforenm) || raise(CryptoError, "Decryption failed. Ciphertext failed verification.")
|
|
150
|
+
Util.remove_zeros(ZEROBYTES, message)
|
|
151
|
+
end
|
|
152
|
+
alias decrypt open
|
|
153
|
+
|
|
154
|
+
# The crypto primitive for the box class
|
|
155
|
+
#
|
|
156
|
+
# @return [Symbol] The primitive used
|
|
157
|
+
def primitive
|
|
158
|
+
self.class.primitive
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# The nonce bytes for the box class
|
|
162
|
+
#
|
|
163
|
+
# @return [Integer] The number of bytes in a valid nonce
|
|
164
|
+
def self.nonce_bytes
|
|
165
|
+
NONCEBYTES
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# The nonce bytes for the box instance
|
|
169
|
+
#
|
|
170
|
+
# @return [Integer] The number of bytes in a valid nonce
|
|
171
|
+
def nonce_bytes
|
|
172
|
+
NONCEBYTES
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
private
|
|
176
|
+
def beforenm
|
|
177
|
+
@k ||= begin
|
|
178
|
+
k = Util.zeros(BEFORENMBYTES)
|
|
179
|
+
self.class.box_curve25519xsalsa20poly1305_beforenm(k, @public_key.to_s, @private_key.to_s) || raise(CryptoError, "Failed to derive shared key")
|
|
180
|
+
k
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# encoding: binary
|
|
2
|
+
module RbNaCl
|
|
3
|
+
module GroupElements
|
|
4
|
+
# Points provide the interface to NaCl's Curve25519 high-speed elliptic
|
|
5
|
+
# curve cryptography, which can be used for implementing Diffie-Hellman
|
|
6
|
+
# and other forms of public key cryptography (e.g. RbNaCl::Box)
|
|
7
|
+
#
|
|
8
|
+
# Objects of the Point class represent points on Edwards curves. NaCl
|
|
9
|
+
# defines a base point (the "standard group element") which we can
|
|
10
|
+
# multiply by an arbitrary integer. This is how NaCl computes public
|
|
11
|
+
# keys from private keys.
|
|
12
|
+
class Curve25519
|
|
13
|
+
# NaCl's Curve25519 base point (a.k.a. standard group element), serialized as hex
|
|
14
|
+
STANDARD_GROUP_ELEMENT = ["0900000000000000000000000000000000000000000000000000000000000000"].pack("H*").freeze
|
|
15
|
+
|
|
16
|
+
# Order of the standard group
|
|
17
|
+
STANDARD_GROUP_ORDER = 2**252 + 27742317777372353535851937790883648493
|
|
18
|
+
|
|
19
|
+
include KeyComparator
|
|
20
|
+
include Serializable
|
|
21
|
+
|
|
22
|
+
extend Sodium
|
|
23
|
+
|
|
24
|
+
sodium_type :scalarmult
|
|
25
|
+
sodium_primitive :curve25519
|
|
26
|
+
|
|
27
|
+
sodium_function :scalarmult_curve25519,
|
|
28
|
+
:crypto_scalarmult_curve25519,
|
|
29
|
+
[:pointer, :pointer, :pointer]
|
|
30
|
+
|
|
31
|
+
# Number of bytes in a scalar on this curve
|
|
32
|
+
SCALARBYTES = 32
|
|
33
|
+
BYTES = 32
|
|
34
|
+
|
|
35
|
+
# Number of bytes in a scalar on this curve
|
|
36
|
+
|
|
37
|
+
# Creates a new Point from the given serialization
|
|
38
|
+
#
|
|
39
|
+
# @param [String] point location of a group element (32-bytes)
|
|
40
|
+
#
|
|
41
|
+
# @return [RbNaCl::Point] the Point at this location
|
|
42
|
+
def initialize(point)
|
|
43
|
+
@point = point.to_str
|
|
44
|
+
|
|
45
|
+
# FIXME: really should have a separate constant here for group element size
|
|
46
|
+
# Group elements and scalars are both 32-bits, but that's for convenience
|
|
47
|
+
Util.check_length(@point, SCALARBYTES, "group element")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Multiply the given integer by this point
|
|
51
|
+
# This ordering is a bit confusing because traditionally the point
|
|
52
|
+
# would be the right-hand operand.
|
|
53
|
+
#
|
|
54
|
+
# @param [String] integer value to multiply with this Point (32-bytes)
|
|
55
|
+
#
|
|
56
|
+
# @return [RbNaCl::Point] result as a Point object
|
|
57
|
+
def mult(integer, encoding = :raw)
|
|
58
|
+
integer = integer.to_str
|
|
59
|
+
Util.check_length(integer, SCALARBYTES, "integer")
|
|
60
|
+
|
|
61
|
+
result = Util.zeros(SCALARBYTES)
|
|
62
|
+
self.class.scalarmult_curve25519(result, integer, @point)
|
|
63
|
+
|
|
64
|
+
self.class.new(result)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Return the point serialized as bytes
|
|
68
|
+
#
|
|
69
|
+
# @return [String] 32-byte string representing this point
|
|
70
|
+
def to_bytes; @point; end
|
|
71
|
+
|
|
72
|
+
@base_point = new(STANDARD_GROUP_ELEMENT)
|
|
73
|
+
|
|
74
|
+
# NaCl's standard base point for all Curve25519 public keys
|
|
75
|
+
#
|
|
76
|
+
# @return [RbNaCl::Point] standard base point (a.k.a. standard group element)
|
|
77
|
+
def self.base; @base_point; end
|
|
78
|
+
def self.base_point; @base_point; end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|