x25519 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +7 -0
  3. data/Rakefile +4 -2
  4. data/ext/{x25519 → x25519_precomputed}/cputest.c +0 -0
  5. data/ext/x25519_precomputed/extconf.rb +9 -0
  6. data/ext/{x25519/rfc7748_precomputed → x25519_precomputed}/fp25519_x64.c +0 -0
  7. data/ext/{x25519/rfc7748_precomputed → x25519_precomputed}/fp25519_x64.h +0 -0
  8. data/ext/{x25519/rfc7748_precomputed → x25519_precomputed}/table_ladder_x25519.h +0 -0
  9. data/ext/x25519_precomputed/x25519_precomputed.c +86 -0
  10. data/ext/{x25519/rfc7748_precomputed/rfc7748_precomputed.h → x25519_precomputed/x25519_precomputed.h} +4 -0
  11. data/ext/{x25519/rfc7748_precomputed → x25519_precomputed}/x25519_x64.c +3 -3
  12. data/ext/{x25519/ref10 → x25519_ref10}/api.h +0 -0
  13. data/ext/{x25519/ref10 → x25519_ref10}/base.c +1 -4
  14. data/ext/x25519_ref10/extconf.rb +9 -0
  15. data/ext/{x25519/ref10 → x25519_ref10}/fe.h +0 -0
  16. data/ext/{x25519/ref10 → x25519_ref10}/fe_0.c +0 -0
  17. data/ext/{x25519/ref10 → x25519_ref10}/fe_1.c +0 -0
  18. data/ext/{x25519/ref10 → x25519_ref10}/fe_add.c +0 -0
  19. data/ext/{x25519/ref10 → x25519_ref10}/fe_copy.c +0 -0
  20. data/ext/{x25519/ref10 → x25519_ref10}/fe_cswap.c +0 -0
  21. data/ext/{x25519/ref10 → x25519_ref10}/fe_frombytes.c +0 -0
  22. data/ext/{x25519/ref10 → x25519_ref10}/fe_invert.c +0 -0
  23. data/ext/{x25519/ref10 → x25519_ref10}/fe_mul.c +0 -0
  24. data/ext/{x25519/ref10 → x25519_ref10}/fe_mul121666.c +0 -0
  25. data/ext/{x25519/ref10 → x25519_ref10}/fe_sq.c +0 -0
  26. data/ext/{x25519/ref10 → x25519_ref10}/fe_sub.c +0 -0
  27. data/ext/{x25519/ref10 → x25519_ref10}/fe_tobytes.c +0 -0
  28. data/ext/{x25519/ref10 → x25519_ref10}/montgomery.h +0 -0
  29. data/ext/{x25519/ref10 → x25519_ref10}/pow225521.h +0 -0
  30. data/ext/{x25519/ref10 → x25519_ref10}/scalarmult.c +1 -0
  31. data/ext/x25519_ref10/x25519_ref10.c +80 -0
  32. data/ext/x25519_ref10/x25519_ref10.h +15 -0
  33. data/lib/x25519.rb +54 -0
  34. data/lib/x25519/montgomery_u.rb +29 -0
  35. data/lib/x25519/scalar.rb +56 -0
  36. data/lib/x25519/version.rb +5 -0
  37. data/x25519.gemspec +7 -3
  38. metadata +38 -31
  39. data/ext/x25519/extconf.rb +0 -31
  40. data/ext/x25519/x25519.c +0 -325
  41. data/ext/x25519/x25519.h +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 15e4b54930cad9efae470089ed69b14837394163
4
- data.tar.gz: b64d42b7a3d9f39ebc2668ce489799ba2f849bef
3
+ metadata.gz: 25356788bb752f943f14d47eb09477c80e8831f3
4
+ data.tar.gz: c90ad445482d6d376bb957187ba1c01a4ade1c7d
5
5
  SHA512:
6
- metadata.gz: f1ccbe8cf45e64fa096399e1814059078a52c0148f51e299abf3b6faeeea88b81c5e913c668a467df999393bbe99ba5dbb67914094b6eda2634bb52690d4421d
7
- data.tar.gz: 910e1e2d8a354c4d19a0c8b4bba50f4e452c64addcf7119f9359786eb640d94f73abfd1916ebaeb48fef8c77e034798afb337b1a10c64ce74587880232f0d548
6
+ metadata.gz: e16c1bfd49e7dd844676159714e6145d357259002800a1033b7993c7ac4049e238dba7c5b8aa1bdff12dced90dfc5a5e70caaf054a3f401eed6efdcd9da7d12f
7
+ data.tar.gz: 173e2fecb31f6b0bc09287fe9d7b7bba563e2cac1ca8b9877290b86241cd7cbbb0b4c8d20f212c2dfd777327a50ef6ac403966f9babac4ced0c38fddc4bfba60
data/CHANGES.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [0.2.0] (2017-12-12)
2
+
3
+ [0.2.0]: https://github.com/cryptosphere/x25519/compare/v0.1.0...v0.2.0
4
+
5
+ * [#5](https://github.com/cryptosphere/x25519/pull/5)
6
+ Rewrite gem in Ruby with minimal native extensions
7
+
1
8
  # 0.1.0 (2017-12-11)
2
9
 
3
10
  * Initial release
data/Rakefile CHANGED
@@ -6,8 +6,10 @@ require "rake/clean"
6
6
  CLEAN.include("**/*.o", "**/*.so", "**/*.bundle", "pkg", "tmp")
7
7
 
8
8
  require "rake/extensiontask"
9
- Rake::ExtensionTask.new("x25519") do |ext|
10
- ext.ext_dir = "ext/x25519"
9
+ %w[precomputed ref10].each do |provider|
10
+ Rake::ExtensionTask.new("x25519_#{provider}") do |ext|
11
+ ext.ext_dir = "ext/x25519_#{provider}"
12
+ end
11
13
  end
12
14
 
13
15
  require "rspec/core/rake_task"
File without changes
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/GlobalVars
4
+
5
+ require "mkmf"
6
+
7
+ $CFLAGS << " -Wall -O3 -pedantic -std=c99 -mbmi -mbmi2 -march=native -mtune=native"
8
+
9
+ create_makefile "x25519_precomputed"
@@ -0,0 +1,86 @@
1
+ /*
2
+ Ruby C extension providing bindings to the rfc7748_precomputed implementation of
3
+ the X25519 Diffie-Hellman algorithm
4
+ */
5
+
6
+ #include "ruby.h"
7
+ #include "x25519_precomputed.h"
8
+
9
+ static VALUE mX25519 = Qnil;
10
+ static VALUE mX25519_Precomputed = Qnil;
11
+
12
+ static VALUE mX25519_Scalar_multiply(VALUE self, VALUE scalar, VALUE montgomery_u);
13
+ static VALUE mX25519_Scalar_multiply_base(VALUE self, VALUE scalar);
14
+ static VALUE mX25519_is_available(VALUE self);
15
+
16
+ /* Initialize the x25519_precomputed C extension */
17
+ void Init_x25519_precomputed()
18
+ {
19
+ mX25519 = rb_define_module("X25519");
20
+ mX25519_Precomputed = rb_define_module_under(mX25519, "Precomputed");
21
+
22
+ rb_define_singleton_method(mX25519_Precomputed, "multiply", mX25519_Scalar_multiply, 2);
23
+ rb_define_singleton_method(mX25519_Precomputed, "multiply_base", mX25519_Scalar_multiply_base, 1);
24
+ rb_define_singleton_method(mX25519_Precomputed, "available?", mX25519_is_available, 0);
25
+ }
26
+
27
+ /* Variable-base scalar multiplication */
28
+ static VALUE mX25519_Scalar_multiply(VALUE self, VALUE scalar, VALUE montgomery_u)
29
+ {
30
+ /* X25519_KEY ensures inputs are aligned at 32-bytes */
31
+ X25519_KEY raw_scalar, raw_montgomery_u, product;
32
+
33
+ StringValue(scalar);
34
+ if(RSTRING_LEN(scalar) != X25519_KEYSIZE_BYTES) {
35
+ rb_raise(
36
+ rb_eArgError,
37
+ "expected %d-byte scalar, got %ld",
38
+ X25519_KEYSIZE_BYTES,
39
+ RSTRING_LEN(scalar)
40
+ );
41
+ }
42
+
43
+ StringValue(montgomery_u);
44
+ if(RSTRING_LEN(montgomery_u) != X25519_KEYSIZE_BYTES) {
45
+ rb_raise(
46
+ rb_eArgError,
47
+ "expected %d-byte Montgomery-u coordinate, got %ld",
48
+ X25519_KEYSIZE_BYTES,
49
+ RSTRING_LEN(montgomery_u)
50
+ );
51
+ }
52
+
53
+ memcpy(raw_scalar, RSTRING_PTR(scalar), X25519_KEYSIZE_BYTES);
54
+ memcpy(raw_montgomery_u, RSTRING_PTR(montgomery_u), X25519_KEYSIZE_BYTES);
55
+ x25519_precomputed_scalarmult(product, raw_scalar, raw_montgomery_u);
56
+
57
+ return rb_str_new((const char *)product, X25519_KEYSIZE_BYTES);
58
+ }
59
+
60
+ /* Fixed-base scalar multiplication */
61
+ static VALUE mX25519_Scalar_multiply_base(VALUE self, VALUE scalar)
62
+ {
63
+ /* X25519_KEY ensures inputs are aligned at 32-bytes */
64
+ X25519_KEY raw_scalar, product;
65
+
66
+ StringValue(scalar);
67
+ if(RSTRING_LEN(scalar) != X25519_KEYSIZE_BYTES) {
68
+ rb_raise(
69
+ rb_eArgError,
70
+ "expected %d-byte scalar, got %ld",
71
+ X25519_KEYSIZE_BYTES,
72
+ RSTRING_LEN(scalar)
73
+ );
74
+ }
75
+
76
+ memcpy(raw_scalar, RSTRING_PTR(scalar), X25519_KEYSIZE_BYTES);
77
+ x25519_precomputed_scalarmult_base(product, raw_scalar);
78
+
79
+ return rb_str_new((const char *)product, X25519_KEYSIZE_BYTES);
80
+ }
81
+
82
+ /* Is the x25519_precomputed backend supported on this CPU? */
83
+ static VALUE mX25519_is_available(VALUE self)
84
+ {
85
+ return check_4th_gen_intel_core_features() ? Qtrue : Qfalse;
86
+ }
@@ -30,4 +30,8 @@
30
30
  #define X25519_KEYSIZE_BYTES 32
31
31
  typedef ALIGN uint8_t X25519_KEY[X25519_KEYSIZE_BYTES];
32
32
 
33
+ void x25519_precomputed_scalarmult(uint8_t *shared, uint8_t *private_key, uint8_t *session_key);
34
+ void x25519_precomputed_scalarmult_base(uint8_t *session_key, uint8_t *private_key);
35
+ int check_4th_gen_intel_core_features();
36
+
33
37
  #endif /* RFC7748_PRECOMPUTED_H */
@@ -17,7 +17,7 @@
17
17
  */
18
18
  #include "fp25519_x64.h"
19
19
  #include "table_ladder_x25519.h"
20
- #include "rfc7748_precomputed.h"
20
+ #include "x25519_precomputed.h"
21
21
 
22
22
  /****** Implementation of Montgomery Ladder Algorithm ************/
23
23
  static inline void cswap_x64(uint64_t bit, uint64_t *const px, uint64_t *const py)
@@ -32,7 +32,7 @@ static inline void cswap_x64(uint64_t bit, uint64_t *const px, uint64_t *const p
32
32
  }
33
33
  }
34
34
 
35
- void x25519_rfc7748_precomputed_scalarmult(uint8_t *shared, uint8_t *private_key, uint8_t *session_key)
35
+ void x25519_precomputed_scalarmult(uint8_t *shared, uint8_t *private_key, uint8_t *session_key)
36
36
  {
37
37
  ALIGN uint64_t buffer[4*NUM_WORDS_ELTFP25519_X64];
38
38
  ALIGN uint64_t coordinates[4*NUM_WORDS_ELTFP25519_X64];
@@ -133,7 +133,7 @@ void x25519_rfc7748_precomputed_scalarmult(uint8_t *shared, uint8_t *private_key
133
133
  private_key[0] = (uint8_t)(save & 0xFF);
134
134
  }
135
135
 
136
- void x25519_rfc7748_precomputed_scalarmult_base(uint8_t *session_key, uint8_t *private_key)
136
+ void x25519_precomputed_scalarmult_base(uint8_t *session_key, uint8_t *private_key)
137
137
  {
138
138
  ALIGN uint64_t buffer[4*NUM_WORDS_ELTFP25519_X64];
139
139
  ALIGN uint64_t coordinates[4*NUM_WORDS_ELTFP25519_X64];
File without changes
@@ -1,8 +1,5 @@
1
1
  #include "fe.h"
2
-
3
- int x25519_ref10_scalarmult(uint8_t *q,
4
- const uint8_t *n,
5
- const uint8_t *p);
2
+ #include "x25519_ref10.h"
6
3
 
7
4
  static const uint8_t x25519_basepoint[32] = {9};
8
5
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/GlobalVars
4
+
5
+ require "mkmf"
6
+
7
+ $CFLAGS << " -Wall -O3 -pedantic -std=c99"
8
+
9
+ create_makefile "x25519_ref10"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,4 +1,5 @@
1
1
  #include "fe.h"
2
+ #include "x25519_ref10.h"
2
3
 
3
4
  int x25519_ref10_scalarmult(uint8_t *q, const uint8_t *n, const uint8_t *p)
4
5
  {
@@ -0,0 +1,80 @@
1
+ /*
2
+ Ruby C extension providing bindings to the ref10 implementation of the
3
+ X25519 Diffie-Hellman algorithm
4
+ */
5
+
6
+ #include "ruby.h"
7
+ #include "x25519_ref10.h"
8
+
9
+ static VALUE mX25519 = Qnil;
10
+ static VALUE mX25519_Ref10 = Qnil;
11
+
12
+ static VALUE mX25519_Scalar_multiply(VALUE self, VALUE scalar, VALUE montgomery_u);
13
+ static VALUE mX25519_Scalar_multiply_base(VALUE self, VALUE scalar);
14
+
15
+ /* Initialize the x25519_ref10 C extension */
16
+ void Init_x25519_ref10()
17
+ {
18
+ mX25519 = rb_define_module("X25519");
19
+ mX25519_Ref10 = rb_define_module_under(mX25519, "Ref10");
20
+
21
+ rb_define_singleton_method(mX25519_Ref10, "multiply", mX25519_Scalar_multiply, 2);
22
+ rb_define_singleton_method(mX25519_Ref10, "multiply_base", mX25519_Scalar_multiply_base, 1);
23
+ }
24
+
25
+ /* Variable-base scalar multiplication */
26
+ static VALUE mX25519_Scalar_multiply(VALUE self, VALUE scalar, VALUE montgomery_u)
27
+ {
28
+ X25519_KEY product;
29
+
30
+ StringValue(scalar);
31
+ if(RSTRING_LEN(scalar) != X25519_KEYSIZE_BYTES) {
32
+ rb_raise(
33
+ rb_eArgError,
34
+ "expected %d-byte scalar, got %ld",
35
+ X25519_KEYSIZE_BYTES,
36
+ RSTRING_LEN(scalar)
37
+ );
38
+ }
39
+
40
+ StringValue(montgomery_u);
41
+ if(RSTRING_LEN(montgomery_u) != X25519_KEYSIZE_BYTES) {
42
+ rb_raise(
43
+ rb_eArgError,
44
+ "expected %d-byte Montgomery-u coordinate, got %ld",
45
+ X25519_KEYSIZE_BYTES,
46
+ RSTRING_LEN(montgomery_u)
47
+ );
48
+ }
49
+
50
+ x25519_ref10_scalarmult(
51
+ product,
52
+ (const uint8_t *)RSTRING_PTR(scalar),
53
+ (const uint8_t *)RSTRING_PTR(montgomery_u)
54
+ );
55
+
56
+ return rb_str_new((const char *)product, X25519_KEYSIZE_BYTES);
57
+ }
58
+
59
+ /* Fixed-base scalar multiplication */
60
+ static VALUE mX25519_Scalar_multiply_base(VALUE self, VALUE scalar)
61
+ {
62
+ X25519_KEY product;
63
+
64
+ StringValue(scalar);
65
+ if(RSTRING_LEN(scalar) != X25519_KEYSIZE_BYTES) {
66
+ rb_raise(
67
+ rb_eArgError,
68
+ "expected %d-byte scalar, got %ld",
69
+ X25519_KEYSIZE_BYTES,
70
+ RSTRING_LEN(scalar)
71
+ );
72
+ }
73
+
74
+ x25519_ref10_scalarmult_base(
75
+ product,
76
+ (const uint8_t *)RSTRING_PTR(scalar)
77
+ );
78
+
79
+ return rb_str_new((const char *)product, X25519_KEYSIZE_BYTES);
80
+ }
@@ -0,0 +1,15 @@
1
+ #ifndef RFC7748_REF10_H
2
+ #define RFC7748_REF10_H
3
+
4
+ #include <stdint.h>
5
+
6
+ #define X25519_KEYSIZE_BYTES 32
7
+ typedef uint8_t X25519_KEY[X25519_KEYSIZE_BYTES];
8
+
9
+ /* Fixed-base scalar multiplication */
10
+ int x25519_ref10_scalarmult(uint8_t *q, const uint8_t *n, const uint8_t *p);
11
+
12
+ /* Variable-base scalar multiplication */
13
+ int x25519_ref10_scalarmult_base(uint8_t *q, const uint8_t *n);
14
+
15
+ #endif /* RFC7748_REF10_H */
data/lib/x25519.rb ADDED
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ require "x25519/version"
6
+
7
+ require "x25519/montgomery_u"
8
+ require "x25519/scalar"
9
+
10
+ # Native extension backends
11
+ require "x25519_ref10"
12
+ require "x25519_precomputed"
13
+
14
+ # The X25519 elliptic curve Diffie-Hellman algorithm
15
+ module X25519
16
+ module_function
17
+
18
+ # Size of an X25519 key (public or private) in bytes
19
+ KEY_SIZE = 32
20
+
21
+ # X25519::Precomputed requires a 4th generation Intel Core CPU or newer,
22
+ # so only enable it if we detect we're on a supported platform. Otherwise,
23
+ # fall back to the ref10 portable C implementation.
24
+ @provider = if X25519::Precomputed.available?
25
+ X25519::Precomputed
26
+ else
27
+ X25519::Ref10
28
+ end
29
+
30
+ # Selected provider based on the logic above
31
+ def provider
32
+ @provider
33
+ end
34
+
35
+ # Raw Diffie-Hellman function that acts directly on bytestrings. An
36
+ # alternative to the object-oriented API
37
+ #
38
+ # @param scalar_bytes [String] a serialized private scalar
39
+ # @param montgomery_u_bytes [String] a point we wish to multiply by the scalar
40
+ #
41
+ # @return [String] resulting point, serialized as bytes
42
+ def diffie_hellman(scalar_bytes, montgomery_u_bytes)
43
+ validate_key_bytes(scalar_bytes)
44
+ validate_key_bytes(montgomery_u_bytes)
45
+ X25519.provider.multiply(scalar_bytes, montgomery_u_bytes)
46
+ end
47
+
48
+ # Ensure a serialized key meets the requirements
49
+ def validate_key_bytes(key_bytes)
50
+ raise TypeError, "expected String, got #{key_bytes.class}" unless key_bytes.is_a?(String)
51
+ return true if key_bytes.bytesize == KEY_SIZE
52
+ raise ArgumentError, "expected #{KEY_SIZE}-byte String, got #{key_bytes.bytesize}"
53
+ end
54
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module X25519
4
+ # X25519 public keys and shared secrets
5
+ #
6
+ # Montgomery-u coordinates of points on the elliptic curve used by X25519
7
+ # (a.k.a. Curve25519)
8
+ class MontgomeryU
9
+ # Create an object representing a Montgomery-u coordinate from a bytestring
10
+ #
11
+ # @param bytes [String] 32-byte compressed Montgomery-u coordinate
12
+ def initialize(bytes)
13
+ X25519.validate_key_bytes(bytes)
14
+ @bytes = bytes
15
+ end
16
+
17
+ # Return a compressed Montgomery-u coordinate serialized as a bytestring
18
+ #
19
+ # @return [String] bytestring serialization of a Montgomery-u coordinate
20
+ def to_bytes
21
+ @bytes
22
+ end
23
+
24
+ # Show hex representation of serialized coordinate in string inspection
25
+ def inspect
26
+ "#<#{self.class}:#{@bytes.unpack('H*').first}>"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module X25519
4
+ # X25519 private keys
5
+ #
6
+ # Scalars are the integer component of scalar multiplication, multiplied
7
+ # against an elliptic curve point.
8
+ class Scalar
9
+ # Securely generate a random scalar
10
+ def self.generate
11
+ new(SecureRandom.random_bytes(X25519::KEY_SIZE))
12
+ end
13
+
14
+ # Create an X25519 scalar object from a bytestring
15
+ #
16
+ # @param bytes [String] 32-byte random secret scalar
17
+ def initialize(bytes)
18
+ X25519.validate_key_bytes(bytes)
19
+ @bytes = bytes
20
+ end
21
+
22
+ # Variable-base scalar multiplication a.k.a. Diffie-Hellman
23
+ #
24
+ # This can be used to obtain a shared secret from a public key
25
+ #
26
+ # @param montgomery_u [X25519::MontgomeryU] coordinate of the public key/point to perform D-H with
27
+ #
28
+ # @return [X25519::MontgomeryU] resulting point (i.e. D-H shared secret)
29
+ def multiply(montgomery_u)
30
+ raise TypeError, "expected X25519::MontgomeryU, got #{montgomery_u}" unless montgomery_u.is_a?(MontgomeryU)
31
+ MontgomeryU.new(X25519.provider.multiply(@bytes, montgomery_u.to_bytes))
32
+ end
33
+ alias diffie_hellman multiply
34
+
35
+ # Fixed-base scalar multiplication. Calculates a public key from a
36
+ # private scalar
37
+ #
38
+ # @return [X25519::MontgomeryU] resulting point (i.e. public key)
39
+ def multiply_base
40
+ MontgomeryU.new(X25519.provider.multiply_base(@bytes))
41
+ end
42
+ alias public_key multiply_base
43
+
44
+ # Return a bytestring representation of this scalar
45
+ #
46
+ # @return [String] scalar converted to a bytestring
47
+ def to_bytes
48
+ @bytes
49
+ end
50
+
51
+ # String inspection that does not leak the private scalar
52
+ def inspect
53
+ to_s
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module X25519
4
+ VERSION = "0.2.0"
5
+ end
data/x25519.gemspec CHANGED
@@ -1,15 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "x25519/version"
6
+
3
7
  Gem::Specification.new do |spec|
4
8
  spec.name = "x25519"
5
- spec.version = "0.1.0"
9
+ spec.version = X25519::VERSION
6
10
  spec.authors = ["Tony Arcieri"]
7
11
  spec.email = ["bascule@gmail.com"]
8
12
  spec.summary = "Public key cryptography library providing the X25519 D-H function"
9
13
  spec.description = <<-DESCRIPTION.strip.gsub(/\s+/, " ")
10
14
  An efficient public key cryptography library for Ruby providing key
11
15
  exchange/agreement via the X25519 (a.k.a. Curve25519) Elliptic Curve
12
- Diffie-Hellman function as described in [RFC7748].
16
+ Diffie-Hellman function as described in RFC 7748.
13
17
  DESCRIPTION
14
18
  spec.homepage = "https://github.com/cryptosphere/x25519"
15
19
  spec.license = "MIT"
@@ -18,7 +22,7 @@ Gem::Specification.new do |spec|
18
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
23
  spec.require_paths = ["lib"]
20
24
  spec.platform = Gem::Platform::RUBY
21
- spec.extensions = "ext/x25519/extconf.rb"
25
+ spec.extensions = ["ext/x25519_precomputed/extconf.rb", "ext/x25519_ref10/extconf.rb"]
22
26
 
23
27
  spec.required_ruby_version = ">= 2.2.2"
24
28
  spec.add_development_dependency "bundler", "~> 1.16"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: x25519
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Arcieri
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-11 00:00:00.000000000 Z
11
+ date: 2017-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -26,12 +26,13 @@ dependencies:
26
26
  version: '1.16'
27
27
  description: An efficient public key cryptography library for Ruby providing key exchange/agreement
28
28
  via the X25519 (a.k.a. Curve25519) Elliptic Curve Diffie-Hellman function as described
29
- in [RFC7748].
29
+ in RFC 7748.
30
30
  email:
31
31
  - bascule@gmail.com
32
32
  executables: []
33
33
  extensions:
34
- - ext/x25519/extconf.rb
34
+ - ext/x25519_precomputed/extconf.rb
35
+ - ext/x25519_ref10/extconf.rb
35
36
  extra_rdoc_files: []
36
37
  files:
37
38
  - ".gitignore"
@@ -44,33 +45,39 @@ files:
44
45
  - LICENSE
45
46
  - README.md
46
47
  - Rakefile
47
- - ext/x25519/cputest.c
48
- - ext/x25519/extconf.rb
49
- - ext/x25519/ref10/api.h
50
- - ext/x25519/ref10/base.c
51
- - ext/x25519/ref10/fe.h
52
- - ext/x25519/ref10/fe_0.c
53
- - ext/x25519/ref10/fe_1.c
54
- - ext/x25519/ref10/fe_add.c
55
- - ext/x25519/ref10/fe_copy.c
56
- - ext/x25519/ref10/fe_cswap.c
57
- - ext/x25519/ref10/fe_frombytes.c
58
- - ext/x25519/ref10/fe_invert.c
59
- - ext/x25519/ref10/fe_mul.c
60
- - ext/x25519/ref10/fe_mul121666.c
61
- - ext/x25519/ref10/fe_sq.c
62
- - ext/x25519/ref10/fe_sub.c
63
- - ext/x25519/ref10/fe_tobytes.c
64
- - ext/x25519/ref10/montgomery.h
65
- - ext/x25519/ref10/pow225521.h
66
- - ext/x25519/ref10/scalarmult.c
67
- - ext/x25519/rfc7748_precomputed/fp25519_x64.c
68
- - ext/x25519/rfc7748_precomputed/fp25519_x64.h
69
- - ext/x25519/rfc7748_precomputed/rfc7748_precomputed.h
70
- - ext/x25519/rfc7748_precomputed/table_ladder_x25519.h
71
- - ext/x25519/rfc7748_precomputed/x25519_x64.c
72
- - ext/x25519/x25519.c
73
- - ext/x25519/x25519.h
48
+ - ext/x25519_precomputed/cputest.c
49
+ - ext/x25519_precomputed/extconf.rb
50
+ - ext/x25519_precomputed/fp25519_x64.c
51
+ - ext/x25519_precomputed/fp25519_x64.h
52
+ - ext/x25519_precomputed/table_ladder_x25519.h
53
+ - ext/x25519_precomputed/x25519_precomputed.c
54
+ - ext/x25519_precomputed/x25519_precomputed.h
55
+ - ext/x25519_precomputed/x25519_x64.c
56
+ - ext/x25519_ref10/api.h
57
+ - ext/x25519_ref10/base.c
58
+ - ext/x25519_ref10/extconf.rb
59
+ - ext/x25519_ref10/fe.h
60
+ - ext/x25519_ref10/fe_0.c
61
+ - ext/x25519_ref10/fe_1.c
62
+ - ext/x25519_ref10/fe_add.c
63
+ - ext/x25519_ref10/fe_copy.c
64
+ - ext/x25519_ref10/fe_cswap.c
65
+ - ext/x25519_ref10/fe_frombytes.c
66
+ - ext/x25519_ref10/fe_invert.c
67
+ - ext/x25519_ref10/fe_mul.c
68
+ - ext/x25519_ref10/fe_mul121666.c
69
+ - ext/x25519_ref10/fe_sq.c
70
+ - ext/x25519_ref10/fe_sub.c
71
+ - ext/x25519_ref10/fe_tobytes.c
72
+ - ext/x25519_ref10/montgomery.h
73
+ - ext/x25519_ref10/pow225521.h
74
+ - ext/x25519_ref10/scalarmult.c
75
+ - ext/x25519_ref10/x25519_ref10.c
76
+ - ext/x25519_ref10/x25519_ref10.h
77
+ - lib/x25519.rb
78
+ - lib/x25519/montgomery_u.rb
79
+ - lib/x25519/scalar.rb
80
+ - lib/x25519/version.rb
74
81
  - x25519.gemspec
75
82
  homepage: https://github.com/cryptosphere/x25519
76
83
  licenses:
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Style/GlobalVars
4
-
5
- require "mkmf"
6
-
7
- abort("missing posix_memalign()") unless have_func("posix_memalign", "stdlib.h")
8
-
9
- $INCFLAGS << " -I$(srcdir)/ref10 -I$(srcdir)/rfc7748_precomputed"
10
-
11
- # Check for Intel 4th Gen Core CPU features
12
- # TODO: move this detection completely to runtime
13
- cputest_c = <<SRC
14
- #{File.read(File.expand_path('cputest.c', __dir__))}
15
- int main() {
16
- return check_4th_gen_intel_core_features() != 1;
17
- }
18
- SRC
19
-
20
- if try_run(cputest_c)
21
- $defs.push("-DHAVE_4TH_GEN_INTEL_CORE")
22
- $CFLAGS << " -Wall -O3 -pedantic -std=c99 -mbmi -mbmi2 -march=native -mtune=native"
23
- $srcs = Dir.glob(File.join(__dir__, "**", "*.c"))
24
- else
25
- # Do not include the rfc7748_precomputed sources if we do not have a 4th gen+ Core CPU
26
- $srcs = Dir.glob(File.join(__dir__, "*.c")) + Dir.glob(File.expand_path("ref10/*.c", __dir__))
27
- end
28
-
29
- $objs = $srcs.map { |src| src.sub(/\.c$/, ".o") }
30
-
31
- create_makefile "x25519"
data/ext/x25519/x25519.c DELETED
@@ -1,325 +0,0 @@
1
- /* Ruby C extension providing bindings to the X25519 Diffie-Hellman algorithm */
2
-
3
- #define _POSIX_C_SOURCE 200809L
4
- #include <stdlib.h>
5
-
6
- #include "ruby.h"
7
- #include "x25519.h"
8
-
9
- /* The X25519::VERSION */
10
- #define GEM_VERSION "0.1.0"
11
-
12
- /* X25519 module method prototypes */
13
- static VALUE X25519_backend(VALUE self);
14
- static VALUE X25519_self_test(VALUE self);
15
- static VALUE X25519_diffie_hellman(VALUE self, VALUE public_key, VALUE secret_key);
16
-
17
- /* X25519::Scalar prototypes */
18
- static VALUE cX25519_Scalar_allocate(VALUE klass);
19
- static void cX25519_Scalar_mark(X25519_KEY *scalar);
20
- static void cX25519_Scalar_free(X25519_KEY *scalar);
21
- static VALUE cX25519_Scalar_generate(VALUE self);
22
- static VALUE cX25519_Scalar_initialize(VALUE self, VALUE bytes);
23
- static VALUE cX25519_Scalar_multiply_base(VALUE self);
24
- static VALUE cX25519_Scalar_multiply(VALUE self, VALUE montgomery_u);
25
- static VALUE cX25519_Scalar_to_bytes(VALUE self);
26
-
27
- /* X25519::MontgomeryU prototypes */
28
- static VALUE cX25519_MontgomeryU_allocate(VALUE klass);
29
- static void cX25519_MontgomeryU_mark(X25519_KEY *coord);
30
- static void cX25519_MontgomeryU_free(X25519_KEY *coord);
31
- static VALUE cX25519_MontgomeryU_initialize(VALUE self, VALUE bytes);
32
- static VALUE cX25519_MontgomeryU_to_bytes(VALUE self);
33
-
34
- static VALUE mX25519 = Qnil;
35
- static VALUE cX25519_Scalar = Qnil;
36
- static VALUE cX25519_MontgomeryU = Qnil;
37
-
38
- /* Are we on a 4th gen Intel Core CPU architecture that supports the
39
- rfc7748_precomputed backend? */
40
- static int use_rfc7748_precomputed = 0;
41
-
42
- /* Initialize the Ruby module */
43
- void Init_x25519()
44
- {
45
- /* Test for support for the rfc7748_precomputed backend */
46
- use_rfc7748_precomputed = check_4th_gen_intel_core_features();
47
-
48
- /* Used for key generation */
49
- rb_require("securerandom");
50
-
51
- mX25519 = rb_define_module("X25519");
52
- rb_define_const(mX25519, "VERSION", rb_str_new2(GEM_VERSION));
53
- rb_define_singleton_method(mX25519, "backend", X25519_backend, 0);
54
- rb_define_singleton_method(mX25519, "self_test", X25519_self_test, 0);
55
- rb_define_singleton_method(mX25519, "diffie_hellman", X25519_diffie_hellman, 2);
56
-
57
- cX25519_Scalar = rb_define_class_under(mX25519, "Scalar", rb_cObject);
58
- rb_define_alloc_func(cX25519_Scalar, cX25519_Scalar_allocate);
59
- rb_define_singleton_method(cX25519_Scalar, "generate", cX25519_Scalar_generate, 0);
60
- rb_define_method(cX25519_Scalar, "initialize", cX25519_Scalar_initialize, 1);
61
- rb_define_method(cX25519_Scalar, "multiply_base", cX25519_Scalar_multiply_base, 0);
62
- rb_define_method(cX25519_Scalar, "public_key", cX25519_Scalar_multiply_base, 0);
63
- rb_define_method(cX25519_Scalar, "multiply", cX25519_Scalar_multiply, 1);
64
- rb_define_method(cX25519_Scalar, "diffie_hellman", cX25519_Scalar_multiply, 1);
65
- rb_define_method(cX25519_Scalar, "to_bytes", cX25519_Scalar_to_bytes, 0);
66
- rb_define_method(cX25519_Scalar, "to_str", cX25519_Scalar_to_bytes, 0);
67
-
68
- cX25519_MontgomeryU = rb_define_class_under(mX25519, "MontgomeryU", rb_cObject);
69
- rb_define_alloc_func(cX25519_MontgomeryU, cX25519_MontgomeryU_allocate);
70
- rb_define_method(cX25519_MontgomeryU, "initialize", cX25519_MontgomeryU_initialize, 1);
71
- rb_define_method(cX25519_MontgomeryU, "to_bytes", cX25519_MontgomeryU_to_bytes, 0);
72
- rb_define_method(cX25519_MontgomeryU, "to_str", cX25519_MontgomeryU_to_bytes, 0);
73
-
74
- /* Run the self-test on load to ensure everything is working */
75
- rb_funcall(mX25519, rb_intern("self_test"), 0);
76
- }
77
-
78
- /* Return a symbol identifying the backend in use */
79
- static VALUE X25519_backend(VALUE self)
80
- {
81
- switch(use_rfc7748_precomputed) {
82
- case 1:
83
- return ID2SYM(rb_intern("rfc7748_precomputed"));
84
- case 0:
85
- return ID2SYM(rb_intern("ref10"));
86
- default:
87
- rb_raise(rb_eRuntimeError, "invalid X25519 backend! (%d)", use_rfc7748_precomputed);
88
- }
89
- }
90
-
91
- /* Perform an end-to-end test of the Ruby binding to ensure it's working correctly */
92
- static VALUE X25519_self_test(VALUE self)
93
- {
94
- VALUE sk, pk, shared;
95
-
96
- /* Test vectors from RFC 7748 */
97
- X25519_KEY ietf_cfrg_key0 = {
98
- 0xa5,0x46,0xe3,0x6b,0xf0,0x52,0x7c,0x9d,
99
- 0x3b,0x16,0x15,0x4b,0x82,0x46,0x5e,0xdd,
100
- 0x62,0x14,0x4c,0x0a,0xc1,0xfc,0x5a,0x18,
101
- 0x50,0x6a,0x22,0x44,0xba,0x44,0x9a,0xc4
102
- };
103
-
104
- X25519_KEY ietf_cfrg_input_coord0 = {
105
- 0xe6,0xdb,0x68,0x67,0x58,0x30,0x30,0xdb,
106
- 0x35,0x94,0xc1,0xa4,0x24,0xb1,0x5f,0x7c,
107
- 0x72,0x66,0x24,0xec,0x26,0xb3,0x35,0x3b,
108
- 0x10,0xa9,0x03,0xa6,0xd0,0xab,0x1c,0x4c
109
- };
110
-
111
- X25519_KEY ietf_cfrg_output_coord0 = {
112
- 0xc3,0xda,0x55,0x37,0x9d,0xe9,0xc6,0x90,
113
- 0x8e,0x94,0xea,0x4d,0xf2,0x8d,0x08,0x4f,
114
- 0x32,0xec,0xcf,0x03,0x49,0x1c,0x71,0xf7,
115
- 0x54,0xb4,0x07,0x55,0x77,0xa2,0x85,0x52
116
- };
117
-
118
- sk = rb_str_new((const char *)&ietf_cfrg_key0, X25519_KEYSIZE_BYTES);
119
- pk = rb_str_new((const char *)&ietf_cfrg_input_coord0, X25519_KEYSIZE_BYTES);
120
-
121
- shared = rb_funcall(mX25519, rb_intern("diffie_hellman"), 2, sk, pk);
122
-
123
- if(RSTRING_LEN(shared) != X25519_KEYSIZE_BYTES ||
124
- memcmp(RSTRING_PTR(shared), ietf_cfrg_output_coord0, X25519_KEYSIZE_BYTES) != 0)
125
- {
126
- rb_raise(rb_eRuntimeError, "X25519 self-test failed!");
127
- }
128
-
129
- return Qtrue;
130
- }
131
-
132
- /* Compute Diffie-Hellman for the given key and Montgomery-u coordinate
133
- * (i.e. variable base scalar multiplication) */
134
- static VALUE X25519_diffie_hellman(VALUE self, VALUE secret_key, VALUE public_key)
135
- {
136
- VALUE scalar, coord, shared;
137
-
138
- scalar = rb_class_new_instance(1, &secret_key, cX25519_Scalar);
139
- coord = rb_class_new_instance(1, &public_key, cX25519_MontgomeryU);
140
- shared = rb_funcall(scalar, rb_intern("multiply"), 1, coord);
141
-
142
- return rb_funcall(shared, rb_intern("to_bytes"), 0);
143
- }
144
-
145
- /********************************
146
- * X25519::Scalar: private keys *
147
- ********************************/
148
-
149
- static VALUE cX25519_Scalar_allocate(VALUE klass)
150
- {
151
- X25519_KEY *scalar = NULL;
152
-
153
- /* Ensure allocation with the correct (32-byte) memory alignent */
154
- if(posix_memalign((void **)&scalar, ALIGN_BYTES, X25519_KEYSIZE_BYTES)) {
155
- rb_fatal("x25519: can't allocate memory with posix_memalign()");
156
- }
157
-
158
- /* Avoid using unitialized memory */
159
- memset(scalar, 0, X25519_KEYSIZE_BYTES);
160
-
161
- return Data_Wrap_Struct(klass, cX25519_Scalar_mark, cX25519_Scalar_free, scalar);
162
- }
163
-
164
- static void cX25519_Scalar_mark(X25519_KEY *scalar)
165
- {
166
- }
167
-
168
- static void cX25519_Scalar_free(X25519_KEY *scalar)
169
- {
170
- free(scalar);
171
- }
172
-
173
- /* Generate a random X25519 private scalar */
174
- static VALUE cX25519_Scalar_generate(VALUE self)
175
- {
176
- VALUE rb_mSecureRandom, scalar_bytes;
177
- rb_mSecureRandom = rb_const_get(rb_cObject, rb_intern("SecureRandom"));
178
-
179
- scalar_bytes = rb_funcall(
180
- rb_mSecureRandom,
181
- rb_intern("random_bytes"),
182
- 1,
183
- INT2NUM(X25519_KEYSIZE_BYTES)
184
- );
185
-
186
- return rb_class_new_instance(1, &scalar_bytes, self);
187
- }
188
-
189
- /* Create an X25519::Scalar from a String containing bytes */
190
- static VALUE cX25519_Scalar_initialize(VALUE self, VALUE bytes)
191
- {
192
- X25519_KEY *scalar = NULL;
193
- Data_Get_Struct(self, X25519_KEY, scalar);
194
-
195
- StringValue(bytes);
196
- if(RSTRING_LEN(bytes) != X25519_KEYSIZE_BYTES) {
197
- rb_raise(
198
- rb_eArgError,
199
- "expected %d-byte scalar, got %ld",
200
- X25519_KEYSIZE_BYTES,
201
- RSTRING_LEN(bytes)
202
- );
203
- }
204
-
205
- memcpy(scalar, RSTRING_PTR(bytes), X25519_KEYSIZE_BYTES);
206
-
207
- return self;
208
- }
209
-
210
- /* Obtain a public key for an X25519 private scalar
211
- * (i.e. fixed base scalar multiplication ) */
212
- static VALUE cX25519_Scalar_multiply_base(VALUE self)
213
- {
214
- X25519_KEY *scalar = NULL, public_key;
215
- VALUE public_key_str;
216
-
217
- Data_Get_Struct(self, X25519_KEY, scalar);
218
-
219
- /* Avoid using unitialized memory */
220
- memset(&public_key, 0, X25519_KEYSIZE_BYTES);
221
-
222
- /* Compute public key from private scalar using fixed-base scalar multiplication */
223
- if(use_rfc7748_precomputed) {
224
- x25519_rfc7748_precomputed_scalarmult_base(public_key, *scalar);
225
- } else {
226
- x25519_ref10_scalarmult_base(public_key, *scalar);
227
- }
228
-
229
- public_key_str = rb_str_new((const char *)&public_key, X25519_KEYSIZE_BYTES);
230
- return rb_class_new_instance(1, &public_key_str, cX25519_MontgomeryU);
231
- }
232
-
233
- /* Obtain a public key for an X25519 private scalar
234
- * (i.e. fixed base scalar multiplication ) */
235
- static VALUE cX25519_Scalar_multiply(VALUE self, VALUE montgomery_u)
236
- {
237
- X25519_KEY *scalar = NULL, *coord = NULL, product;
238
- VALUE product_str;
239
-
240
- if(rb_obj_class(montgomery_u) != cX25519_MontgomeryU) {
241
- rb_raise(rb_eTypeError, "wrong argument type (expected X25519::MontgomeryU)");
242
- }
243
-
244
- Data_Get_Struct(self, X25519_KEY, scalar);
245
- Data_Get_Struct(montgomery_u, X25519_KEY, coord);
246
-
247
- /* Avoid using unitialized memory */
248
- memset(&product, 0, X25519_KEYSIZE_BYTES);
249
-
250
- /* Compute the Diffie-Hellman shared secret */
251
- if(use_rfc7748_precomputed) {
252
- x25519_rfc7748_precomputed_scalarmult(product, *scalar, *coord);
253
- } else {
254
- x25519_ref10_scalarmult(product, *scalar, *coord);
255
- }
256
-
257
- product_str = rb_str_new((const char *)&product, X25519_KEYSIZE_BYTES);
258
- return rb_class_new_instance(1, &product_str, cX25519_MontgomeryU);
259
- }
260
-
261
- /* Return a String containing the raw bytes of this scalar */
262
- static VALUE cX25519_Scalar_to_bytes(VALUE self)
263
- {
264
- X25519_KEY *scalar = NULL;
265
- Data_Get_Struct(self, X25519_KEY, scalar);
266
-
267
- return rb_str_new((const char *)scalar, X25519_KEYSIZE_BYTES);;
268
- }
269
-
270
- /************************************
271
- * X25519::MontgomeryU: public keys *
272
- ************************************/
273
-
274
- static VALUE cX25519_MontgomeryU_allocate(VALUE klass)
275
- {
276
- X25519_KEY *coord = NULL;
277
-
278
- /* Ensure allocation with the correct (32-byte) memory alignent */
279
- if(posix_memalign((void **)&coord, ALIGN_BYTES, X25519_KEYSIZE_BYTES)) {
280
- rb_fatal("x25519: can't allocate memory with posix_memalign()");
281
- }
282
-
283
- /* Avoid using unitialized memory */
284
- memset(coord, 0, X25519_KEYSIZE_BYTES);
285
-
286
- return Data_Wrap_Struct(klass, cX25519_MontgomeryU_mark, cX25519_MontgomeryU_free, coord);
287
- }
288
-
289
- static void cX25519_MontgomeryU_mark(X25519_KEY *coord)
290
- {
291
- }
292
-
293
- static void cX25519_MontgomeryU_free(X25519_KEY *coord)
294
- {
295
- free(coord);
296
- }
297
-
298
- static VALUE cX25519_MontgomeryU_initialize(VALUE self, VALUE bytes)
299
- {
300
- X25519_KEY *coord = NULL;
301
- Data_Get_Struct(self, X25519_KEY, coord);
302
-
303
- StringValue(bytes);
304
- if(RSTRING_LEN(bytes) != X25519_KEYSIZE_BYTES) {
305
- rb_raise(
306
- rb_eArgError,
307
- "expected %d-byte scalar, got %ld",
308
- X25519_KEYSIZE_BYTES,
309
- RSTRING_LEN(bytes)
310
- );
311
- }
312
-
313
- memcpy(coord, RSTRING_PTR(bytes), X25519_KEYSIZE_BYTES);
314
-
315
- return self;
316
- }
317
-
318
- /* Return a String containing the raw bytes of this scalar */
319
- static VALUE cX25519_MontgomeryU_to_bytes(VALUE self)
320
- {
321
- X25519_KEY *coord = NULL;
322
- Data_Get_Struct(self, X25519_KEY, coord);
323
-
324
- return rb_str_new((const char *)coord, X25519_KEYSIZE_BYTES);;
325
- }
data/ext/x25519/x25519.h DELETED
@@ -1,24 +0,0 @@
1
- #include "rfc7748_precomputed.h"
2
-
3
- /* Detect support for 4th gen (e.g. Haswell) or newer CPUs */
4
- int check_4th_gen_intel_core_features();
5
-
6
- /**********************************
7
- * rfc7748_precomputed prototypes *
8
- **********************************/
9
-
10
- /* Fixed-base scalar multiplication */
11
- void x25519_rfc7748_precomputed_scalarmult_base(uint8_t *session_key, uint8_t *private_key);
12
-
13
- /* Variable-base scalar multiplication */
14
- void x25519_rfc7748_precomputed_scalarmult(uint8_t *shared, uint8_t *private_key, uint8_t *session_key);
15
-
16
- /********************
17
- * ref10 prototypes *
18
- ********************/
19
-
20
- /* Fixed-base scalar multiplication */
21
- int x25519_ref10_scalarmult_base(uint8_t *q, const uint8_t *n);
22
-
23
- /* Variable-base scalar multiplication */
24
- int x25519_ref10_scalarmult(uint8_t *q, const uint8_t *n, const uint8_t *p);