x25519 0.0.0 → 0.1.0

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