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.
- data/.coveralls.yml +1 -0
- data/.gitignore +19 -0
- data/.rspec +4 -0
- data/.travis.yml +21 -0
- data/.yardopts +1 -0
- data/CHANGES.md +4 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +23 -0
- data/README.md +179 -0
- data/Rakefile +5 -0
- data/images/dragons.png +0 -0
- data/images/ed25519.png +0 -0
- data/images/logo.png +0 -0
- data/lib/rbnacl.rb +46 -0
- data/lib/rbnacl/auth.rb +78 -0
- data/lib/rbnacl/auth/one_time.rb +38 -0
- data/lib/rbnacl/box.rb +141 -0
- data/lib/rbnacl/encoder.rb +44 -0
- data/lib/rbnacl/encoders/base32.rb +33 -0
- data/lib/rbnacl/encoders/base64.rb +30 -0
- data/lib/rbnacl/encoders/hex.rb +30 -0
- data/lib/rbnacl/encoders/raw.rb +12 -0
- data/lib/rbnacl/hash.rb +48 -0
- data/lib/rbnacl/hmac/sha256.rb +32 -0
- data/lib/rbnacl/hmac/sha512256.rb +35 -0
- data/lib/rbnacl/keys/key_comparator.rb +59 -0
- data/lib/rbnacl/keys/private_key.rb +62 -0
- data/lib/rbnacl/keys/public_key.rb +38 -0
- data/lib/rbnacl/keys/signing_key.rb +74 -0
- data/lib/rbnacl/keys/verify_key.rb +76 -0
- data/lib/rbnacl/nacl.rb +132 -0
- data/lib/rbnacl/point.rb +67 -0
- data/lib/rbnacl/rake_tasks.rb +56 -0
- data/lib/rbnacl/random.rb +19 -0
- data/lib/rbnacl/random_nonce_box.rb +109 -0
- data/lib/rbnacl/secret_box.rb +86 -0
- data/lib/rbnacl/self_test.rb +118 -0
- data/lib/rbnacl/serializable.rb +23 -0
- data/lib/rbnacl/test_vectors.rb +69 -0
- data/lib/rbnacl/util.rb +137 -0
- data/lib/rbnacl/version.rb +5 -0
- data/rbnacl.gemspec +28 -0
- data/rbnacl.gpg +30 -0
- data/spec/rbnacl/auth/one_time_spec.rb +8 -0
- data/spec/rbnacl/box_spec.rb +42 -0
- data/spec/rbnacl/encoder_spec.rb +14 -0
- data/spec/rbnacl/encoders/base32_spec.rb +16 -0
- data/spec/rbnacl/encoders/base64_spec.rb +15 -0
- data/spec/rbnacl/encoders/hex_spec.rb +15 -0
- data/spec/rbnacl/hash_spec.rb +52 -0
- data/spec/rbnacl/hmac/sha256_spec.rb +8 -0
- data/spec/rbnacl/hmac/sha512256_spec.rb +8 -0
- data/spec/rbnacl/keys/private_key_spec.rb +68 -0
- data/spec/rbnacl/keys/public_key_spec.rb +45 -0
- data/spec/rbnacl/keys/signing_key_spec.rb +40 -0
- data/spec/rbnacl/keys/verify_key_spec.rb +51 -0
- data/spec/rbnacl/point_spec.rb +29 -0
- data/spec/rbnacl/random_nonce_box_spec.rb +78 -0
- data/spec/rbnacl/random_spec.rb +9 -0
- data/spec/rbnacl/secret_box_spec.rb +24 -0
- data/spec/rbnacl/util_spec.rb +119 -0
- data/spec/shared/authenticator.rb +114 -0
- data/spec/shared/box.rb +51 -0
- data/spec/shared/key_equality.rb +26 -0
- data/spec/spec_helper.rb +14 -0
- data/tasks/ci.rake +11 -0
- data/tasks/rspec.rake +7 -0
- 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
|
data/lib/rbnacl/nacl.rb
ADDED
@@ -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
|
data/lib/rbnacl/point.rb
ADDED
@@ -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
|
+
|