x25519 0.0.0 → 0.1.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +3 -0
  4. data/CHANGES.md +3 -0
  5. data/Gemfile +3 -2
  6. data/README.md +205 -14
  7. data/Rakefile +9 -1
  8. data/ext/x25519/cputest.c +68 -0
  9. data/ext/x25519/extconf.rb +31 -0
  10. data/ext/x25519/ref10/api.h +2 -0
  11. data/ext/x25519/ref10/base.c +12 -0
  12. data/ext/x25519/ref10/fe.h +44 -0
  13. data/ext/x25519/ref10/fe_0.c +19 -0
  14. data/ext/x25519/ref10/fe_1.c +19 -0
  15. data/ext/x25519/ref10/fe_add.c +57 -0
  16. data/ext/x25519/ref10/fe_copy.c +29 -0
  17. data/ext/x25519/ref10/fe_cswap.c +73 -0
  18. data/ext/x25519/ref10/fe_frombytes.c +67 -0
  19. data/ext/x25519/ref10/fe_invert.c +14 -0
  20. data/ext/x25519/ref10/fe_mul.c +252 -0
  21. data/ext/x25519/ref10/fe_mul121666.c +69 -0
  22. data/ext/x25519/ref10/fe_sq.c +148 -0
  23. data/ext/x25519/ref10/fe_sub.c +57 -0
  24. data/ext/x25519/ref10/fe_tobytes.c +119 -0
  25. data/ext/x25519/ref10/montgomery.h +140 -0
  26. data/ext/x25519/ref10/pow225521.h +160 -0
  27. data/ext/x25519/ref10/scalarmult.c +46 -0
  28. data/ext/x25519/{fp25519_x64.c → rfc7748_precomputed/fp25519_x64.c} +14 -16
  29. data/ext/x25519/{fp25519_x64.h → rfc7748_precomputed/fp25519_x64.h} +6 -10
  30. data/ext/x25519/{bytes.h → rfc7748_precomputed/rfc7748_precomputed.h} +13 -5
  31. data/ext/x25519/{table_ladder_x25519.h → rfc7748_precomputed/table_ladder_x25519.h} +0 -0
  32. data/ext/x25519/{x25519_x64.c → rfc7748_precomputed/x25519_x64.c} +16 -29
  33. data/ext/x25519/x25519.c +325 -0
  34. data/ext/x25519/x25519.h +24 -0
  35. data/x25519.gemspec +3 -6
  36. metadata +32 -15
  37. data/ext/x25519/bytes.c +0 -42
  38. data/ext/x25519/random.c +0 -51
  39. data/ext/x25519/random.h +0 -24
  40. data/ext/x25519/rfc7748_precompted.h +0 -49
  41. data/ext/x25519/rfc7748_precomputed.c +0 -20
  42. data/lib/x25519.rb +0 -7
  43. data/lib/x25519/version.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 28376f61ba4f900dcd58030544eb00afebde6123
4
- data.tar.gz: 6888c8dfa66915c4b467cc92bf0eb89a0347487e
3
+ metadata.gz: 15e4b54930cad9efae470089ed69b14837394163
4
+ data.tar.gz: b64d42b7a3d9f39ebc2668ce489799ba2f849bef
5
5
  SHA512:
6
- metadata.gz: 00f25c71faeabe6e09dd6fed15d96a605c82d101a1c248d546bc3043f9f52c781e375e4d7d5f9a99555e04e5fc21ff10cf82fa08e92b20da2e56a32de6e23571
7
- data.tar.gz: bcb6b64f41f196bcdeb3e59053732a8fb825619c03e958cb2bbc530d0be1971a8c82cfc7df1f525d10689ecb8b15b7f85b493ed6f48fab70d9974b91f697ad71
6
+ metadata.gz: f1ccbe8cf45e64fa096399e1814059078a52c0148f51e299abf3b6faeeea88b81c5e913c668a467df999393bbe99ba5dbb67914094b6eda2634bb52690d4421d
7
+ data.tar.gz: 910e1e2d8a354c4d19a0c8b4bba50f4e452c64addcf7119f9359786eb640d94f73abfd1916ebaeb48fef8c77e034798afb337b1a10c64ce74587880232f0d548
data/.gitignore CHANGED
@@ -7,6 +7,9 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.o
11
+ *.so
12
+ *.bundle
10
13
 
11
14
  # rspec failure tracking
12
15
  .rspec_status
data/.rubocop.yml CHANGED
@@ -22,6 +22,9 @@ Metrics/CyclomaticComplexity:
22
22
  Metrics/PerceivedComplexity:
23
23
  Enabled: false
24
24
 
25
+ Metrics/BlockLength:
26
+ Max: 100
27
+
25
28
  Metrics/ClassLength:
26
29
  Max: 100
27
30
 
data/CHANGES.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.1.0 (2017-12-11)
2
+
3
+ * Initial release
data/Gemfile CHANGED
@@ -5,7 +5,8 @@ source "https://rubygems.org"
5
5
  gemspec
6
6
 
7
7
  group :development, :test do
8
- gem "rake"
9
- gem "rspec", "~> 3.7", require: false
8
+ gem "rake", require: false
9
+ gem "rake-compiler", "~> 1.0", require: false
10
+ gem "rspec", "~> 3.7", require: false
10
11
  gem "rubocop", "0.51.0", require: false
11
12
  end
data/README.md CHANGED
@@ -1,23 +1,35 @@
1
- # x25519.rb
1
+ # x25519.rb [![Latest Version][gem-shield]][gem-link] [![Build Status][build-image]][build-link] [![License: LGPL v3][license-image]][license-link]
2
+
3
+ [gem-shield]: https://badge.fury.io/rb/x25519.svg
4
+ [gem-link]: https://rubygems.org/gems/x25519
5
+ [build-image]: https://travis-ci.org/cryptosphere/x25519.svg?branch=master
6
+ [build-link]: https://travis-ci.org/cryptosphere/x25519
7
+ [license-image]: https://img.shields.io/badge/License-LGPL%20v3-blue.svg
8
+ [license-link]: https://www.gnu.org/licenses/lgpl-3.0
2
9
 
3
10
  An efficient public key cryptography library for Ruby providing key
4
11
  exchange/agreement.
5
12
 
6
13
  This gem implements X25519 (a.k.a. Curve25519) Elliptic Curve Diffie-Hellman
7
14
  function as described in [RFC7748] as a C extension using the
8
- high performance [rfc7748-precomputed] implementation based on the paper
9
- [How to (pre-)compute a ladder].
15
+ high performance [rfc7748_precomputed] implementation based on the paper
16
+ [How to (pre-)compute a ladder]
17
+ (with fallback to the ref10 C implementation).
10
18
 
11
19
  [RFC7748]: https://tools.ietf.org/html/rfc7748
12
20
  [How to (pre-)compute a ladder]: https://eprint.iacr.org/2017/264
13
21
  [rfc7748_precomputed]: https://github.com/armfazh/rfc7748_precomputed
14
22
 
23
+ ## Requirements
24
+
25
+ * MRI 2.2+
26
+
15
27
  ## Installation
16
28
 
17
29
  Add this line to your application's Gemfile:
18
30
 
19
31
  ```ruby
20
- gem 'x25519'
32
+ gem "x25519"
21
33
  ```
22
34
 
23
35
  And then execute:
@@ -30,7 +42,165 @@ Or install it yourself as:
30
42
 
31
43
  ## Usage
32
44
 
33
- Coming soon!
45
+ The example below shows how to perform a full Diffie-Hellman key exchange:
46
+
47
+ ```ruby
48
+ require "x25519"
49
+
50
+ # Alice generates random scalar (private key)
51
+ alice_sk = X25519::Scalar.generate
52
+
53
+ # Alice obtains public key for her private key/scalar
54
+ alice_pk = alice_sk.public_key
55
+
56
+ # Bob generates random scalar (private key)
57
+ # Ostensibly this would be on a different computer somewhere
58
+ bob_sk = X25519::Scalar.generate
59
+ bob_pk = bob_sk.public_key
60
+
61
+ # Alice can perform Diffie-Hellman with Bob's public key
62
+ alice_secret = alice_sk.diffie_hellman(bob_pk).to_bytes
63
+
64
+ # Bob can perform Diffie-Hellman with Alice's public key
65
+ bob_secret = bob_sk.diffie_hellman(alice_pk).to_bytes
66
+
67
+ # The resulting secrets should be the same
68
+ alice_secret == bob_secret # true
69
+ ```
70
+
71
+ ## API
72
+
73
+ ### `X25519::Scalar`: private keys
74
+
75
+ The `X25519::Scalar` class represents secret integers used as X25519 private
76
+ keys. These secret integers are multiplied by a well-known base point to
77
+ obtain X25519 public keys (`X25519::MontgomeryU`).
78
+
79
+ #### `X25519::Scalar.generate()`: make a random private key
80
+
81
+ Generate a random private scalar (using `SecureRandom`)
82
+
83
+ ##### Example
84
+
85
+ ```ruby
86
+ secret_key = X25519::Scalar.generate
87
+ ```
88
+
89
+ #### `X25519::Scalar.new(bytes)`: load existing private key
90
+
91
+ * `bytes`: a 32-byte `String` value containing the private key
92
+
93
+ ##### Example
94
+
95
+ ```ruby
96
+ secret_key = X25519::Scalar.new(File.read("alice.key"))
97
+ ```
98
+
99
+ #### `X25519::Scalar#public_key()`: obtain public key for this scalar
100
+
101
+ NOTE: The `#multiply_base` method is an alias of this one.
102
+
103
+ Performs fixed-base scalar multiplication (i.e. calculates public key)
104
+
105
+ ##### Return Value
106
+
107
+ Returns a `X25519::MontgomeryU` object which represents the public key for this private key/scalar.
108
+
109
+ ##### Example
110
+
111
+ ```ruby
112
+ secret_key = X25519::Scalar.generate
113
+ public_key = secret_key.public_key
114
+ ```
115
+
116
+ #### `X25519::Scalar#diffie_hellman(other_public_key)`: obtain public key for this scalar
117
+
118
+ NOTE: The `#multiply` method is an alias of this one.
119
+
120
+ Performs variable-base scalar multiplication, computing a shared secret between
121
+ our private scalar and someone else's public key/point.
122
+
123
+ ##### Return Value
124
+
125
+ Returns a `X25519::MontgomeryU` object which represents the shared secret.
126
+
127
+ ##### Arguments
128
+
129
+ * `other_public_key`: a `X25519::MontgomeryU` object containing the public key
130
+ with which we'd like to compute a shared secret.
131
+
132
+ ##### Example
133
+
134
+ ```ruby
135
+ secret_key = X25519::Scalar.generate
136
+ public_key = X25519::MontgomeryU.new(File.read("bob.pub"))
137
+
138
+ # Returns an X25519::MontgomeryU
139
+ shared_secret = secret_key.multiply(public_key)
140
+
141
+ # Obtain the shared secret as a serialized byte representation
142
+ shared_secret_bytes = shared_secret.to_bytes
143
+ ```
144
+
145
+ #### `X25519::Scalar#to_bytes`: serialize a scalar as a `String`
146
+
147
+ ##### Return Value
148
+
149
+ Returns a `String` containing a byte representation of this scalar:
150
+
151
+ ##### Example
152
+
153
+ ```ruby
154
+ secret_key = X25519::Scalar.new(...)
155
+ File.write("alice.key", secret_key.to_bytes)
156
+ ```
157
+
158
+ ### `X25519::MontgomeryU`: public keys and shared secrets
159
+
160
+ The `X25519::MontgomeryU` class represents a coordinate (specifically a
161
+ Montgomery-u coordinate) on the elliptic curve. In the X25519 Diffie-Hellman
162
+ function, these serve both as public keys and as shared secrets.
163
+
164
+ #### `X25519::MontgomeryU.new(bytes)`: load existing public key
165
+
166
+ ##### Arguments
167
+
168
+ * `bytes`: a 32-byte `String` value containing the public key
169
+
170
+ ##### Example
171
+
172
+ ```ruby
173
+ public_key = X25519::MontgomeryU.new(File.read("bob.pub"))
174
+ ```
175
+
176
+ #### `X25519::MontgomeryU#to_bytes`: serialize a Montgomery-u coordinate as a `String`
177
+
178
+ ##### Return Value
179
+
180
+ Returns a `String` containing a byte representation of a compressed Montgomery-u coordinate:
181
+
182
+ ##### Example
183
+
184
+ ```ruby
185
+ public_key = X25519::MontgomeryU..new(...)
186
+ File.write("bob.pub", public_key.to_bytes)
187
+ ```
188
+
189
+ ### `X25519`: module-level functionality
190
+
191
+ #### `X25519.diffie_hellman(secret_key, public_key)`: shorthand `String`-oriented API
192
+
193
+ If you'd like to avoid the object-oriented API, you can use a simplified API which
194
+ acts entirely on bytestrings.
195
+
196
+ ##### Arguments
197
+
198
+ * `secret_key`: a 32-byte `String` containing a private scalar
199
+ * `public_key`: a 32-byte `String` containing a compressed Montgomery-u coordinate
200
+
201
+ ##### Return Value
202
+
203
+ Returns a `String` containing a 32-byte compressed Montgomery-u coordinate
34
204
 
35
205
  ## Contributing
36
206
 
@@ -41,27 +211,48 @@ code of conduct.
41
211
 
42
212
  ## Implementation Details
43
213
 
214
+ This gem contains two implementations of X25519: an optimized assembly
215
+ implementation and a portable C implementation. Implementations are selected
216
+ based on available CPU features.
217
+
218
+ ### [rfc7748_precomputed]: optimized assembly implementation
219
+
44
220
  * Prime field arithmetic is optimized for the 4th and 6th generation of Intel Core processors (Haswell and Skylake micro-architectures).
45
221
  * Efficient integer multiplication using MULX instruction.
46
222
  * Integer additions accelerated with ADCX/ADOX instructions.
47
- * Key generation uses a read-only table of 8 KB (25 KB) for X25519 (X448).
223
+ * Key generation uses a read-only table of 8 KB for X25519.
224
+
225
+ ### ref10: portable C implementation
226
+
227
+ * Taken from the [SUPERCOP] cryptographic benchmarking suite (supercop-20171020)
228
+ * Portable C code which should compile on any architecture
229
+
230
+ [SUPERCOP]: https://bench.cr.yp.to/supercop.html
48
231
 
49
232
  ## Designers
50
233
 
51
- Thomaz Oliveira, Computer Science Department, Cinvestav-IPN, Mexico.
52
- Julio López, University of Campinas, Brazil.
53
- Hüseyin Hisil, Yasar University, Turkey.
54
- Armando Faz-Hernández, University of Campinas, Brazil.
55
- Francisco Rodríguez-Henríquez, Computer Science Department, Cinvestav-IPN, Mexico.
234
+ The X25519 Diffie-Hellman function was originally designed by Dan Bernstein:
235
+
236
+ https://cr.yp.to/ecdh.html
237
+
238
+ The optimized [rfc7748_precomputed] implementation was designed by:
239
+
240
+ * Thomaz Oliveira, Computer Science Department, Cinvestav-IPN, Mexico.
241
+ * Julio López, University of Campinas, Brazil.
242
+ * Hüseyin Hisil, Yasar University, Turkey.
243
+ * Armando Faz-Hernández, University of Campinas, Brazil.
244
+ * Francisco Rodríguez-Henríquez, Computer Science Department, Cinvestav-IPN, Mexico.
245
+
246
+ ## Copyright and License
56
247
 
57
- ## License
248
+ Copyright (c) 2017 Armando Faz, Tony Arcieri
58
249
 
59
- The gem is available as open source under the terms of the
250
+ This gem is available as open source under the terms of the
60
251
  GNU Lesser General Public License v3.0 ([LICENSE](https://www.gnu.org/licenses/lgpl-3.0.txt))
61
252
 
62
253
  ## Code of Conduct
63
254
 
64
- Everyone interacting in the X25519 project’s codebases, issue trackers, chat
255
+ Everyone interacting in the x25519.rb project’s codebases, issue trackers, chat
65
256
  rooms and mailing lists is expected to follow the [code of conduct].
66
257
 
67
258
  [code of conduct]: https://github.com/cryptosphere/x25519/blob/master/CODE_OF_CONDUCT.md
data/Rakefile CHANGED
@@ -2,10 +2,18 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
 
5
+ require "rake/clean"
6
+ CLEAN.include("**/*.o", "**/*.so", "**/*.bundle", "pkg", "tmp")
7
+
8
+ require "rake/extensiontask"
9
+ Rake::ExtensionTask.new("x25519") do |ext|
10
+ ext.ext_dir = "ext/x25519"
11
+ end
12
+
5
13
  require "rspec/core/rake_task"
6
14
  RSpec::Core::RakeTask.new
7
15
 
8
16
  require "rubocop/rake_task"
9
17
  RuboCop::RakeTask.new
10
18
 
11
- task default: %w[spec rubocop]
19
+ task default: %w[compile spec rubocop]
@@ -0,0 +1,68 @@
1
+ /*
2
+ Test for 4th generation Intel Core processor family features (e.g. Haswell)
3
+ From https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
4
+ */
5
+
6
+ #include <stdint.h>
7
+ #if defined(_MSC_VER)
8
+ # include <intrin.h>
9
+ #endif
10
+
11
+ static void run_cpuid(uint32_t eax, uint32_t ecx, uint32_t* abcd)
12
+ {
13
+ #if defined(_MSC_VER)
14
+ __cpuidex(abcd, eax, ecx);
15
+ #else
16
+ uint32_t ebx = 0, edx;
17
+ # if defined( __i386__ ) && defined ( __PIC__ )
18
+ /* in case of PIC under 32-bit EBX cannot be clobbered */
19
+ __asm__ ( "movl %%ebx, %%edi \n\t cpuid \n\t xchgl %%ebx, %%edi" : "=D" (ebx),
20
+ # else
21
+ __asm__ ( "cpuid" : "+b" (ebx),
22
+ # endif
23
+ "+a" (eax), "+c" (ecx), "=d" (edx) );
24
+ abcd[0] = eax; abcd[1] = ebx; abcd[2] = ecx; abcd[3] = edx;
25
+ #endif
26
+ }
27
+
28
+ static int check_xcr0_ymm()
29
+ {
30
+ uint32_t xcr0;
31
+ #if defined(_MSC_VER)
32
+ xcr0 = (uint32_t)_xgetbv(0); /* min VS2010 SP1 compiler is required */
33
+ #else
34
+ __asm__ ("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx" );
35
+ #endif
36
+ return ((xcr0 & 6) == 6); /* checking if xmm and ymm state are enabled in XCR0 */
37
+ }
38
+
39
+ int check_4th_gen_intel_core_features()
40
+ {
41
+ uint32_t abcd[4];
42
+ uint32_t fma_movbe_osxsave_mask = ((1 << 12) | (1 << 22) | (1 << 27));
43
+ uint32_t avx2_bmi12_mask = (1 << 5) | (1 << 3) | (1 << 8);
44
+
45
+ /* CPUID.(EAX=01H, ECX=0H):ECX.FMA[bit 12]==1 &&
46
+ CPUID.(EAX=01H, ECX=0H):ECX.MOVBE[bit 22]==1 &&
47
+ CPUID.(EAX=01H, ECX=0H):ECX.OSXSAVE[bit 27]==1 */
48
+ run_cpuid( 1, 0, abcd );
49
+ if ( (abcd[2] & fma_movbe_osxsave_mask) != fma_movbe_osxsave_mask )
50
+ return 0;
51
+
52
+ if ( ! check_xcr0_ymm() )
53
+ return 0;
54
+
55
+ /* CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]==1 &&
56
+ CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]==1 &&
57
+ CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]==1 */
58
+ run_cpuid( 7, 0, abcd );
59
+ if ( (abcd[1] & avx2_bmi12_mask) != avx2_bmi12_mask )
60
+ return 0;
61
+
62
+ /* CPUID.(EAX=80000001H):ECX.LZCNT[bit 5]==1 */
63
+ run_cpuid( 0x80000001, 0, abcd );
64
+ if ( (abcd[2] & (1 << 5)) == 0)
65
+ return 0;
66
+
67
+ return 1;
68
+ }
@@ -0,0 +1,31 @@
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"
@@ -0,0 +1,2 @@
1
+ #define CRYPTO_BYTES 32
2
+ #define CRYPTO_SCALARBYTES 32
@@ -0,0 +1,12 @@
1
+ #include "fe.h"
2
+
3
+ int x25519_ref10_scalarmult(uint8_t *q,
4
+ const uint8_t *n,
5
+ const uint8_t *p);
6
+
7
+ static const uint8_t x25519_basepoint[32] = {9};
8
+
9
+ int x25519_ref10_scalarmult_base(uint8_t *q, const uint8_t *n)
10
+ {
11
+ return x25519_ref10_scalarmult(q,n,x25519_basepoint);
12
+ }