x25519 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +3 -0
- data/CHANGES.md +3 -0
- data/Gemfile +3 -2
- data/README.md +205 -14
- data/Rakefile +9 -1
- data/ext/x25519/cputest.c +68 -0
- data/ext/x25519/extconf.rb +31 -0
- data/ext/x25519/ref10/api.h +2 -0
- data/ext/x25519/ref10/base.c +12 -0
- data/ext/x25519/ref10/fe.h +44 -0
- data/ext/x25519/ref10/fe_0.c +19 -0
- data/ext/x25519/ref10/fe_1.c +19 -0
- data/ext/x25519/ref10/fe_add.c +57 -0
- data/ext/x25519/ref10/fe_copy.c +29 -0
- data/ext/x25519/ref10/fe_cswap.c +73 -0
- data/ext/x25519/ref10/fe_frombytes.c +67 -0
- data/ext/x25519/ref10/fe_invert.c +14 -0
- data/ext/x25519/ref10/fe_mul.c +252 -0
- data/ext/x25519/ref10/fe_mul121666.c +69 -0
- data/ext/x25519/ref10/fe_sq.c +148 -0
- data/ext/x25519/ref10/fe_sub.c +57 -0
- data/ext/x25519/ref10/fe_tobytes.c +119 -0
- data/ext/x25519/ref10/montgomery.h +140 -0
- data/ext/x25519/ref10/pow225521.h +160 -0
- data/ext/x25519/ref10/scalarmult.c +46 -0
- data/ext/x25519/{fp25519_x64.c → rfc7748_precomputed/fp25519_x64.c} +14 -16
- data/ext/x25519/{fp25519_x64.h → rfc7748_precomputed/fp25519_x64.h} +6 -10
- data/ext/x25519/{bytes.h → rfc7748_precomputed/rfc7748_precomputed.h} +13 -5
- data/ext/x25519/{table_ladder_x25519.h → rfc7748_precomputed/table_ladder_x25519.h} +0 -0
- data/ext/x25519/{x25519_x64.c → rfc7748_precomputed/x25519_x64.c} +16 -29
- data/ext/x25519/x25519.c +325 -0
- data/ext/x25519/x25519.h +24 -0
- data/x25519.gemspec +3 -6
- metadata +32 -15
- data/ext/x25519/bytes.c +0 -42
- data/ext/x25519/random.c +0 -51
- data/ext/x25519/random.h +0 -24
- data/ext/x25519/rfc7748_precompted.h +0 -49
- data/ext/x25519/rfc7748_precomputed.c +0 -20
- data/lib/x25519.rb +0 -7
- data/lib/x25519/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15e4b54930cad9efae470089ed69b14837394163
|
4
|
+
data.tar.gz: b64d42b7a3d9f39ebc2668ce489799ba2f849bef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1ccbe8cf45e64fa096399e1814059078a52c0148f51e299abf3b6faeeea88b81c5e913c668a467df999393bbe99ba5dbb67914094b6eda2634bb52690d4421d
|
7
|
+
data.tar.gz: 910e1e2d8a354c4d19a0c8b4bba50f4e452c64addcf7119f9359786eb640d94f73abfd1916ebaeb48fef8c77e034798afb337b1a10c64ce74587880232f0d548
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGES.md
ADDED
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 "
|
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 [
|
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
|
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
|
-
|
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
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
248
|
+
Copyright (c) 2017 Armando Faz, Tony Arcieri
|
58
249
|
|
59
|
-
|
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
|
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,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
|
+
}
|