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