openssl-pkey-ec-ies 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7e357d7daaae0ec18393048a55006ec136786c9d
4
+ data.tar.gz: 452ec98344bb5d933bb09fd7256a87cf1290ad73
5
+ SHA512:
6
+ metadata.gz: c15155f099791b242ee0d95fe7ea8e5227e88f532d6e150c40a33d299ffb302d4525796f80135d53b4fd3853dd5e5a15f1a5abd93f20c40abb58b96cce64349a
7
+ data.tar.gz: 3fd6b9c90b847f875736f5502411109dbaab91445d6b3babb6ed3502ef398a3b9d9b8a636867578211549799b2548409e624810d645a610ec78c82dc426871d2
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ /ext/ies/Makefile
24
+ /ext/ies/extconf.h
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in openssl-pkey-ec-ies.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 webpay
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,42 @@
1
+ # OpenSSL::PKey::EC::IES
2
+
3
+ IES implementation following ECIES-KEM specification in [ISO 18033-2](http://www.shoup.net/iso/).
4
+
5
+ This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'openssl-pkey-ec-ies'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install openssl-pkey-ec-ies
20
+
21
+ ## Usage
22
+
23
+ Prepare secret key using OpenSSL.
24
+
25
+ ```
26
+ openssl ecparam -genkey -out ec_key.pem -name prime192v1
27
+ ```
28
+
29
+ ```ruby
30
+ ec = OpenSSL::PKey::EC::IES.new(test_key, "placeholder")
31
+ source = 'my secret'
32
+ cryptogram = ec.public_encrypt(source) # => cryptogram in string
33
+ result = ec.private_decrypt(cryptogram) # => 'my secret'
34
+ ```
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it ( https://github.com/webpay/openssl-pkey-ec-ies/fork )
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create a new Pull Request
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/extensiontask'
3
+ require 'rake/testtask'
4
+
5
+ Rake::ExtensionTask.new 'ies' do |ext|
6
+ ext.lib_dir = 'lib/openssl/pkey/ec'
7
+ end
8
+
9
+ Rake::TestTask.new do |t|
10
+ t.libs << 'test'
11
+ t.test_files = FileList['test/test*.rb']
12
+ t.verbose = true
13
+ end
14
+
15
+ Rake::Task[:test].prerequisites << :compile
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @file /cryptron/secure.c
3
+ *
4
+ * @brief Functions for handling the secure data type.
5
+ *
6
+ * $Author: Ladar Levison $
7
+ * $Website: http://lavabit.com $
8
+ *
9
+ */
10
+
11
+ #include "ies.h"
12
+ #define HEADSIZE (sizeof(cryptogram_head_t))
13
+
14
+ size_t cryptogram_key_length(const cryptogram_t *cryptogram) {
15
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
16
+ return head->length.key;
17
+ }
18
+
19
+ size_t cryptogram_mac_length(const cryptogram_t *cryptogram) {
20
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
21
+ return head->length.mac;
22
+ }
23
+
24
+ size_t cryptogram_body_length(const cryptogram_t *cryptogram) {
25
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
26
+ return head->length.body;
27
+ }
28
+
29
+ size_t cryptogram_data_sum_length(const cryptogram_t *cryptogram) {
30
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
31
+ return (head->length.key + head->length.mac + head->length.body);
32
+ }
33
+
34
+ size_t cryptogram_total_length(const cryptogram_t *cryptogram) {
35
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
36
+ return HEADSIZE + (head->length.key + head->length.mac + head->length.body);
37
+ }
38
+
39
+ unsigned char * cryptogram_key_data(const cryptogram_t *cryptogram) {
40
+ return (unsigned char *)cryptogram + HEADSIZE;
41
+ }
42
+
43
+ unsigned char * cryptogram_mac_data(const cryptogram_t *cryptogram) {
44
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
45
+ return (unsigned char *)cryptogram + (HEADSIZE + head->length.key + head->length.body);
46
+ }
47
+
48
+ unsigned char * cryptogram_body_data(const cryptogram_t *cryptogram) {
49
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
50
+ return (unsigned char *)cryptogram + (HEADSIZE + head->length.key);
51
+ }
52
+
53
+ cryptogram_t * cryptogram_alloc(size_t key, size_t mac, size_t body) {
54
+ cryptogram_t *cryptogram = malloc(HEADSIZE + key + mac + body);
55
+ cryptogram_head_t *head = (cryptogram_head_t *)cryptogram;
56
+ head->length.key = key;
57
+ head->length.mac = mac;
58
+ head->length.body = body;
59
+ return cryptogram;
60
+ }
61
+
62
+ void cryptogram_free(cryptogram_t *cryptogram) {
63
+ free(cryptogram);
64
+ return;
65
+ }
@@ -0,0 +1,555 @@
1
+ /**
2
+ * @file /cryptron/ecies.c
3
+ *
4
+ * @brief ECIES encryption/decryption functions.
5
+ *
6
+ * $Author: Ladar Levison $
7
+ * $Website: http://lavabit.com $
8
+ *
9
+ */
10
+
11
+ #include "ies.h"
12
+ #include <openssl/ecdh.h>
13
+
14
+ #define SET_ERROR(string) \
15
+ sprintf(error, "%s %s:%d", (string), __FILE__, __LINE__)
16
+ #define SET_OSSL_ERROR(string) \
17
+ sprintf(error, "%s {error = %s} %s:%d", (string), ERR_error_string(ERR_get_error(), NULL), __FILE__, __LINE__)
18
+
19
+ /* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
20
+ * Taken from openssl/crypto/ecdh/ech_kdf.c in github:openssl/openssl
21
+ * ffa08b3242e0f10f1fef3c93ef3f0b51de8c27a9 */
22
+
23
+ /* Key derivation function from X9.62/SECG */
24
+ /* Way more than we will ever need */
25
+ #define ECDH_KDF_MAX (1 << 30)
26
+ int ECDH_KDF_X9_62(unsigned char *out, size_t outlen,
27
+ const unsigned char *Z, size_t Zlen,
28
+ const unsigned char *sinfo, size_t sinfolen,
29
+ const EVP_MD *md)
30
+ {
31
+ EVP_MD_CTX mctx;
32
+ int rv = 0;
33
+ unsigned int i;
34
+ size_t mdlen;
35
+ unsigned char ctr[4];
36
+ if (sinfolen > ECDH_KDF_MAX || outlen > ECDH_KDF_MAX || Zlen > ECDH_KDF_MAX)
37
+ return 0;
38
+ mdlen = EVP_MD_size(md);
39
+ EVP_MD_CTX_init(&mctx);
40
+ for (i = 1;;i++)
41
+ {
42
+ unsigned char mtmp[EVP_MAX_MD_SIZE];
43
+ EVP_DigestInit_ex(&mctx, md, NULL);
44
+ ctr[3] = i & 0xFF;
45
+ ctr[2] = (i >> 8) & 0xFF;
46
+ ctr[1] = (i >> 16) & 0xFF;
47
+ ctr[0] = (i >> 24) & 0xFF;
48
+ if (!EVP_DigestUpdate(&mctx, Z, Zlen))
49
+ goto err;
50
+ if (!EVP_DigestUpdate(&mctx, ctr, sizeof(ctr)))
51
+ goto err;
52
+ if (!EVP_DigestUpdate(&mctx, sinfo, sinfolen))
53
+ goto err;
54
+ if (outlen >= mdlen)
55
+ {
56
+ if (!EVP_DigestFinal(&mctx, out, NULL))
57
+ goto err;
58
+ outlen -= mdlen;
59
+ if (outlen == 0)
60
+ break;
61
+ out += mdlen;
62
+ }
63
+ else
64
+ {
65
+ if (!EVP_DigestFinal(&mctx, mtmp, NULL))
66
+ goto err;
67
+ memcpy(out, mtmp, outlen);
68
+ OPENSSL_cleanse(mtmp, mdlen);
69
+ break;
70
+ }
71
+ }
72
+ rv = 1;
73
+ err:
74
+ EVP_MD_CTX_cleanup(&mctx);
75
+ return rv;
76
+ }
77
+
78
+ static size_t envelope_key_len(const ies_ctx_t *ctx)
79
+ {
80
+ return EVP_CIPHER_key_length(ctx->cipher) + EVP_MD_size(ctx->md);
81
+ }
82
+
83
+ static EC_KEY * ecies_key_create(const EC_KEY *user, char *error) {
84
+
85
+ const EC_GROUP *group;
86
+ EC_KEY *key = NULL;
87
+
88
+ if (!(key = EC_KEY_new())) {
89
+ SET_OSSL_ERROR("EC_KEY_new failed");
90
+ return NULL;
91
+ }
92
+
93
+ if (!(group = EC_KEY_get0_group(user))) {
94
+ SET_ERROR("The user key does not have group");
95
+ EC_KEY_free(key);
96
+ return NULL;
97
+ }
98
+
99
+ if (EC_KEY_set_group(key, group) != 1) {
100
+ SET_OSSL_ERROR("EC_KEY_set_group failed");
101
+ EC_KEY_free(key);
102
+ return NULL;
103
+ }
104
+
105
+ if (EC_KEY_generate_key(key) != 1) {
106
+ SET_OSSL_ERROR("EC_KEY_generate_key failed");
107
+ EC_KEY_free(key);
108
+ return NULL;
109
+ }
110
+
111
+ return key;
112
+ }
113
+
114
+ static unsigned char *prepare_envelope_key(const ies_ctx_t *ctx, cryptogram_t *cryptogram, char *error)
115
+ {
116
+
117
+ const size_t key_buf_len = envelope_key_len(ctx);
118
+ const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8;
119
+ unsigned char *envelope_key = NULL, *ktmp = NULL;
120
+ EC_KEY *ephemeral = NULL;
121
+ size_t written_length;
122
+
123
+ /* High-level ECDH via EVP does not allow use of arbitrary KDF function.
124
+ * We should use low-level API for KDF2
125
+ * c.f. openssl/crypto/ec/ec_pmeth.c */
126
+ if ((envelope_key = OPENSSL_malloc(key_buf_len)) == NULL) {
127
+ SET_ERROR("Failed to allocate memory for envelope_key");
128
+ goto err;
129
+ }
130
+
131
+ if (!(ephemeral = ecies_key_create(ctx->user_key, error))) {
132
+ goto err;
133
+ }
134
+
135
+ /* key agreement and KDF
136
+ * reference: openssl/crypto/ec/ec_pmeth.c */
137
+ ktmp = OPENSSL_malloc(ecdh_key_len);
138
+ if (ktmp == NULL) {
139
+ SET_ERROR("No memory for ECDH temporary key");
140
+ goto err;
141
+ }
142
+
143
+ if (ECDH_compute_key(ktmp, ecdh_key_len, EC_KEY_get0_public_key(ctx->user_key), ephemeral, NULL)
144
+ != (int)ecdh_key_len) {
145
+ SET_OSSL_ERROR("An error occurred while ECDH_compute_key");
146
+ goto err;
147
+ }
148
+
149
+ /* equals to ISO 18033-2 KDF2 */
150
+ if (!ECDH_KDF_X9_62(envelope_key, key_buf_len, ktmp, ecdh_key_len, 0, 0, ctx->kdf_md)) {
151
+ SET_OSSL_ERROR("Failed to stretch with KDF2");
152
+ goto err;
153
+ }
154
+
155
+ /* Store the public key portion of the ephemeral key. */
156
+ written_length = EC_POINT_point2oct(
157
+ EC_KEY_get0_group(ephemeral),
158
+ EC_KEY_get0_public_key(ephemeral),
159
+ POINT_CONVERSION_COMPRESSED,
160
+ (void *)cryptogram_key_data(cryptogram),
161
+ ctx->stored_key_length,
162
+ NULL);
163
+ if (written_length == 0) {
164
+ SET_OSSL_ERROR("Error while recording the public portion of the envelope key");
165
+ goto err;
166
+ }
167
+ if (written_length != ctx->stored_key_length) {
168
+ SET_ERROR("Written envelope key length does not match with expected");
169
+ goto err;
170
+ }
171
+
172
+ EC_KEY_free(ephemeral);
173
+ OPENSSL_cleanse(ktmp, ecdh_key_len);
174
+ OPENSSL_free(ktmp);
175
+
176
+ return envelope_key;
177
+
178
+ err:
179
+ if (ephemeral)
180
+ EC_KEY_free(ephemeral);
181
+ if (envelope_key) {
182
+ OPENSSL_cleanse(envelope_key, key_buf_len);
183
+ OPENSSL_free(envelope_key);
184
+ }
185
+ if (ktmp) {
186
+ OPENSSL_cleanse(ktmp, ecdh_key_len);
187
+ OPENSSL_free(ktmp);
188
+ }
189
+ return NULL;
190
+ }
191
+
192
+ static int store_cipher_body(
193
+ const ies_ctx_t *ctx,
194
+ const unsigned char *envelope_key,
195
+ const unsigned char *data,
196
+ size_t length,
197
+ cryptogram_t *cryptogram,
198
+ char *error)
199
+ {
200
+ int out_len, len_sum = 0;
201
+ size_t expected_len = cryptogram_body_length(cryptogram);
202
+ unsigned char iv[EVP_MAX_IV_LENGTH];
203
+ EVP_CIPHER_CTX cipher;
204
+ unsigned char *body;
205
+
206
+ /* For now we use an empty initialization vector. */
207
+ memset(iv, 0, EVP_MAX_IV_LENGTH);
208
+
209
+ EVP_CIPHER_CTX_init(&cipher);
210
+ body = cryptogram_body_data(cryptogram);
211
+
212
+ if (EVP_EncryptInit_ex(&cipher, ctx->cipher, NULL, envelope_key, iv) != 1
213
+ || EVP_EncryptUpdate(&cipher, body, &out_len, data, length) != 1) {
214
+ SET_OSSL_ERROR("Error while trying to secure the data using the symmetric cipher");
215
+ EVP_CIPHER_CTX_cleanup(&cipher);
216
+ return 0;
217
+ }
218
+
219
+ if (expected_len < (size_t)out_len) {
220
+ SET_ERROR("The symmetric cipher overflowed");
221
+ EVP_CIPHER_CTX_cleanup(&cipher);
222
+ return 0;
223
+ }
224
+
225
+ body += out_len;
226
+ len_sum += out_len;
227
+ if (EVP_EncryptFinal_ex(&cipher, body, &out_len) != 1) {
228
+ SET_OSSL_ERROR("Error while finalizing the data using the symmetric cipher");
229
+ EVP_CIPHER_CTX_cleanup(&cipher);
230
+ cryptogram_free(cryptogram);
231
+ return 0;
232
+ }
233
+
234
+ EVP_CIPHER_CTX_cleanup(&cipher);
235
+
236
+ if (expected_len < (size_t)len_sum) {
237
+ SET_ERROR("The symmetric cipher overflowed");
238
+ return 0;
239
+ }
240
+
241
+ return 1;
242
+ }
243
+
244
+ static int store_mac_tag(const ies_ctx_t *ctx, const unsigned char *envelope_key, cryptogram_t *cryptogram, char *error) {
245
+ const size_t key_offset = EVP_CIPHER_key_length(ctx->cipher);
246
+ const size_t key_length = EVP_MD_size(ctx->md);
247
+ const size_t mac_length = cryptogram_mac_length(cryptogram);
248
+ unsigned int out_len;
249
+ HMAC_CTX hmac;
250
+
251
+ HMAC_CTX_init(&hmac);
252
+
253
+ /* Generate hash tag using encrypted data */
254
+ if (HMAC_Init_ex(&hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1
255
+ || HMAC_Update(&hmac, cryptogram_body_data(cryptogram), cryptogram_body_length(cryptogram)) != 1
256
+ || HMAC_Final(&hmac, cryptogram_mac_data(cryptogram), &out_len) != 1) {
257
+ SET_OSSL_ERROR("Unable to generate tag");
258
+ HMAC_CTX_cleanup(&hmac);
259
+ return 0;
260
+ }
261
+
262
+ HMAC_CTX_cleanup(&hmac);
263
+
264
+ if (out_len != mac_length) {
265
+ SET_ERROR("MAC length expectation does not meet");
266
+ return 0;
267
+ }
268
+
269
+ return 1;
270
+ }
271
+
272
+ cryptogram_t * ecies_encrypt(const ies_ctx_t *ctx, const unsigned char *data, size_t length, char *error) {
273
+
274
+ const size_t block_length = EVP_CIPHER_block_size(ctx->cipher);
275
+ const size_t mac_length = EVP_MD_size(ctx->md);
276
+ cryptogram_t *cryptogram = NULL;
277
+ unsigned char *envelope_key = NULL;
278
+
279
+ if (!ctx || !data || !length) {
280
+ SET_ERROR("Invalid arguments");
281
+ return NULL;
282
+ }
283
+
284
+ if (block_length == 0 || block_length > EVP_MAX_BLOCK_LENGTH) {
285
+ SET_ERROR("Derived block size is incorrect");
286
+ return NULL;
287
+ }
288
+
289
+ cryptogram = cryptogram_alloc(ctx->stored_key_length,
290
+ mac_length,
291
+ length + (length % block_length ? (block_length - (length % block_length)) : 0));
292
+ if (!cryptogram) {
293
+ SET_ERROR("Unable to allocate a cryptogram_t buffer to hold the encrypted result.");
294
+ goto err;
295
+ }
296
+
297
+ if ((envelope_key = prepare_envelope_key(ctx, cryptogram, error)) == NULL) {
298
+ goto err;
299
+ }
300
+
301
+ if (!store_cipher_body(ctx, envelope_key, data, length, cryptogram, error)) {
302
+ goto err;
303
+ }
304
+
305
+ if (!store_mac_tag(ctx, envelope_key, cryptogram, error)) {
306
+ goto err;
307
+ }
308
+
309
+ OPENSSL_cleanse(envelope_key, envelope_key_len(ctx));
310
+ OPENSSL_free(envelope_key);
311
+
312
+ return cryptogram;
313
+
314
+ err:
315
+ if (cryptogram)
316
+ cryptogram_free(cryptogram);
317
+ if (envelope_key) {
318
+ OPENSSL_cleanse(envelope_key, envelope_key_len(ctx));
319
+ OPENSSL_free(envelope_key);
320
+ }
321
+ return NULL;
322
+ }
323
+
324
+ static EC_KEY *ecies_key_create_public_octets(EC_KEY *user, unsigned char *octets, size_t length, char *error) {
325
+
326
+ EC_KEY *key = NULL;
327
+ EC_POINT *point = NULL;
328
+ const EC_GROUP *group = NULL;
329
+
330
+ if (!(key = EC_KEY_new())) {
331
+ SET_OSSL_ERROR("Cannot create instance for ephemeral key");
332
+ return NULL;
333
+ }
334
+
335
+ if (!(group = EC_KEY_get0_group(user))) {
336
+ SET_ERROR("Cannot get group from user key");
337
+ EC_KEY_free(key);
338
+ return NULL;
339
+ }
340
+
341
+ if (EC_KEY_set_group(key, group) != 1) {
342
+ SET_OSSL_ERROR("EC_KEY_set_group failed");
343
+ EC_KEY_free(key);
344
+ return NULL;
345
+ }
346
+
347
+ if (!(point = EC_POINT_new(group))) {
348
+ SET_OSSL_ERROR("EC_POINT_new failed");
349
+ EC_KEY_free(key);
350
+ return NULL;
351
+ }
352
+
353
+ if (EC_POINT_oct2point(group, point, octets, length, NULL) != 1) {
354
+ SET_OSSL_ERROR("EC_POINT_oct2point failed");
355
+ EC_KEY_free(key);
356
+ return NULL;
357
+ }
358
+
359
+ if (EC_KEY_set_public_key(key, point) != 1) {
360
+ SET_OSSL_ERROR("EC_KEY_set_public_key failed");
361
+ EC_POINT_free(point);
362
+ EC_KEY_free(key);
363
+ return NULL;
364
+ }
365
+
366
+ EC_POINT_free(point);
367
+
368
+ if (EC_KEY_check_key(key) != 1) {
369
+ SET_OSSL_ERROR("EC_KEY_check_key failed");
370
+ EC_KEY_free(key);
371
+ return NULL;
372
+ }
373
+
374
+ return key;
375
+ }
376
+
377
+ unsigned char *restore_envelope_key(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, char *error)
378
+ {
379
+
380
+ const size_t key_buf_len = envelope_key_len(ctx);
381
+ const size_t ecdh_key_len = (EC_GROUP_get_degree(EC_KEY_get0_group(ctx->user_key)) + 7) / 8;
382
+ EC_KEY *ephemeral = NULL, *user_copy = NULL;
383
+ unsigned char *envelope_key = NULL, *ktmp = NULL;
384
+
385
+ if ((envelope_key = OPENSSL_malloc(key_buf_len)) == NULL) {
386
+ SET_ERROR("Failed to allocate memory for envelope_key");
387
+ goto err;
388
+ }
389
+
390
+ if (!(user_copy = EC_KEY_new())) {
391
+ SET_OSSL_ERROR("Failed to create instance for user key copy");
392
+ goto err;
393
+ }
394
+
395
+ if (!(EC_KEY_copy(user_copy, ctx->user_key))) {
396
+ SET_OSSL_ERROR("Failed to copy user key");
397
+ goto err;
398
+ }
399
+
400
+ if (!(ephemeral = ecies_key_create_public_octets(user_copy, cryptogram_key_data(cryptogram), cryptogram_key_length(cryptogram), error))) {
401
+ goto err;
402
+ }
403
+
404
+ /* key agreement and KDF
405
+ * reference: openssl/crypto/ec/ec_pmeth.c */
406
+ ktmp = OPENSSL_malloc(ecdh_key_len);
407
+ if (ktmp == NULL) {
408
+ SET_ERROR("No memory for ECDH temporary key");
409
+ goto err;
410
+ }
411
+
412
+ if (ECDH_compute_key(ktmp, ecdh_key_len, EC_KEY_get0_public_key(ephemeral), user_copy, NULL)
413
+ != (int)ecdh_key_len) {
414
+ SET_OSSL_ERROR("An error occurred while ECDH_compute_key");
415
+ goto err;
416
+ }
417
+
418
+ /* equals to ISO 18033-2 KDF2 */
419
+ if (!ECDH_KDF_X9_62(envelope_key, key_buf_len, ktmp, ecdh_key_len, 0, 0, ctx->kdf_md)) {
420
+ SET_OSSL_ERROR("Failed to stretch with KDF2");
421
+ goto err;
422
+ }
423
+
424
+ EC_KEY_free(user_copy);
425
+ EC_KEY_free(ephemeral);
426
+ OPENSSL_cleanse(ktmp, ecdh_key_len);
427
+ OPENSSL_free(ktmp);
428
+
429
+ return envelope_key;
430
+
431
+ err:
432
+ if (ephemeral)
433
+ EC_KEY_free(ephemeral);
434
+ if (user_copy)
435
+ EC_KEY_free(user_copy);
436
+ if (envelope_key) {
437
+ OPENSSL_cleanse(envelope_key, key_buf_len);
438
+ OPENSSL_free(envelope_key);
439
+ }
440
+ if (ktmp) {
441
+ OPENSSL_cleanse(ktmp, ecdh_key_len);
442
+ OPENSSL_free(ktmp);
443
+ }
444
+ return NULL;
445
+ }
446
+
447
+ static int verify_mac(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, const unsigned char * envelope_key, char *error)
448
+ {
449
+ const size_t key_offset = EVP_CIPHER_key_length(ctx->cipher);
450
+ const size_t key_length = EVP_MD_size(ctx->md);
451
+ const size_t mac_length = cryptogram_mac_length(cryptogram);
452
+ unsigned int out_len;
453
+ HMAC_CTX hmac;
454
+ unsigned char md[EVP_MAX_MD_SIZE];
455
+
456
+ HMAC_CTX_init(&hmac);
457
+
458
+ /* Generate hash tag using encrypted data */
459
+ if (HMAC_Init_ex(&hmac, envelope_key + key_offset, key_length, ctx->md, NULL) != 1
460
+ || HMAC_Update(&hmac, cryptogram_body_data(cryptogram), cryptogram_body_length(cryptogram)) != 1
461
+ || HMAC_Final(&hmac, md, &out_len) != 1) {
462
+ SET_OSSL_ERROR("Unable to generate tag");
463
+ HMAC_CTX_cleanup(&hmac);
464
+ return 0;
465
+ }
466
+
467
+ HMAC_CTX_cleanup(&hmac);
468
+
469
+ if (out_len != mac_length) {
470
+ SET_ERROR("MAC length expectation does not meet");
471
+ return 0;
472
+ }
473
+
474
+ if (memcmp(md, cryptogram_mac_data(cryptogram), mac_length) != 0) {
475
+ SET_ERROR("MAC tag verification failed");
476
+ return 0;
477
+ }
478
+
479
+ return 1;
480
+ }
481
+
482
+ unsigned char *decrypt_body(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, const unsigned char *envelope_key, size_t *length, char *error)
483
+ {
484
+ int out_len;
485
+ size_t output_sum;
486
+ const size_t body_length = cryptogram_body_length(cryptogram);
487
+ unsigned char iv[EVP_MAX_IV_LENGTH], *block, *output;
488
+ EVP_CIPHER_CTX cipher;
489
+
490
+ if (!(output = malloc(body_length + 1))) {
491
+ SET_ERROR("Failed to allocate memory for clear text");
492
+ return NULL;
493
+ }
494
+
495
+ /* For now we use an empty initialization vector */
496
+ memset(iv, 0, EVP_MAX_IV_LENGTH);
497
+ memset(output, 0, body_length + 1);
498
+
499
+ EVP_CIPHER_CTX_init(&cipher);
500
+
501
+ block = output;
502
+ if (EVP_DecryptInit_ex(&cipher, ctx->cipher, NULL, envelope_key, iv) != 1
503
+ || EVP_DecryptUpdate(&cipher, block, &out_len, cryptogram_body_data(cryptogram), body_length) != 1) {
504
+ SET_OSSL_ERROR("Unable to decrypt");
505
+ EVP_CIPHER_CTX_cleanup(&cipher);
506
+ free(output);
507
+ return NULL;
508
+ }
509
+ output_sum = out_len;
510
+
511
+ block += output_sum;
512
+ if (EVP_DecryptFinal_ex(&cipher, block, &out_len) != 1) {
513
+ printf("Unable to decrypt the data using the chosen symmetric cipher. {error = %s}\n", ERR_error_string(ERR_get_error(), NULL));
514
+ EVP_CIPHER_CTX_cleanup(&cipher);
515
+ free(output);
516
+ return NULL;
517
+ }
518
+ output_sum += out_len;
519
+
520
+ EVP_CIPHER_CTX_cleanup(&cipher);
521
+
522
+ *length = output_sum;
523
+
524
+ return output;
525
+ }
526
+
527
+ unsigned char * ecies_decrypt(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, size_t *length, char *error)
528
+ {
529
+
530
+ unsigned char *envelope_key = NULL, *output = NULL;
531
+
532
+ if (!ctx || !cryptogram || !length || !error) {
533
+ SET_ERROR("Invalid argument");
534
+ goto err;
535
+ }
536
+
537
+ envelope_key = restore_envelope_key(ctx, cryptogram, error);
538
+ if (envelope_key == NULL) {
539
+ goto err;
540
+ }
541
+
542
+ if (!verify_mac(ctx, cryptogram, envelope_key, error)) {
543
+ goto err;
544
+ }
545
+
546
+ if ((output = decrypt_body(ctx, cryptogram, envelope_key, length, error)) == NULL) {
547
+ goto err;
548
+ }
549
+
550
+ err:
551
+ OPENSSL_cleanse(envelope_key, envelope_key_len(ctx));
552
+ OPENSSL_free(envelope_key);
553
+
554
+ return output;
555
+ }
@@ -0,0 +1,34 @@
1
+ require "mkmf"
2
+
3
+ Logging::message "=== OpenSSL ECIES extension configurator ===\n"
4
+
5
+ ##
6
+ # Adds -DOSSL_DEBUG for compilation and some more targets when GCC is used
7
+ # To turn it on, use: --with-debug or --enable-debug
8
+ #
9
+ if with_config("debug") or enable_config("debug")
10
+ $defs.push("-DOSSL_ECIES_DEBUG") unless $defs.include? "-DOSSL_ECIES_DEBUG"
11
+ end
12
+
13
+ result = pkg_config("openssl") && have_header("openssl/ssl.h")
14
+
15
+ unless result
16
+ result = have_header("openssl/ssl.h")
17
+ result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "OpenSSL_add_all_digests")}
18
+ result &&= %w[ssl ssleay32].any? {|lib| have_library(lib, "SSL_library_init")}
19
+ unless result
20
+ Logging::message "=== Checking for required stuff failed. ===\n"
21
+ Logging::message "Makefile wasn't created. Fix the errors above.\n"
22
+ exit 1
23
+ end
24
+ end
25
+
26
+ unless have_header("openssl/conf_api.h")
27
+ raise "OpenSSL 0.9.6 or later required."
28
+ end
29
+
30
+ create_header
31
+ create_makefile("openssl/pkey/ec/ies") {|conf|
32
+ conf << "THREAD_MODEL = #{CONFIG["THREAD_MODEL"]}\n"
33
+ }
34
+ Logging::message "Done.\n"
@@ -0,0 +1,159 @@
1
+ #include "ies.h"
2
+
3
+ static VALUE eIESError;
4
+
5
+ static EC_KEY *require_ec_key(VALUE self)
6
+ {
7
+ const EVP_PKEY *pkey;
8
+ const EC_KEY *ec;
9
+ Data_Get_Struct(self, EVP_PKEY, pkey);
10
+ if (!pkey) {
11
+ rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");
12
+ }
13
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) {
14
+ rb_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!");
15
+ }
16
+ ec = pkey->pkey.ec;
17
+ if (ec == NULL)
18
+ rb_raise(eIESError, "EC_KEY is not initialized");
19
+ return ec;
20
+ }
21
+
22
+ static ies_ctx_t *create_context(VALUE self)
23
+ {
24
+ ies_ctx_t* ctx = malloc(sizeof(ies_ctx_t));
25
+ ctx->cipher = EVP_aes_128_cbc();
26
+ ctx->md = EVP_sha1();
27
+ ctx->kdf_md = EVP_sha1();
28
+ ctx->stored_key_length = 25;
29
+ ctx->user_key = require_ec_key(self);
30
+
31
+ return ctx;
32
+ }
33
+
34
+ static VALUE ies_cryptogram_to_rb_string(const ies_ctx_t *ctx,const cryptogram_t *cryptogram)
35
+ {
36
+ return rb_str_new((char *)cryptogram_key_data(cryptogram), cryptogram_data_sum_length(cryptogram));
37
+ }
38
+
39
+ static cryptogram_t *ies_rb_string_to_cryptogram(const ies_ctx_t *ctx, const VALUE string)
40
+ {
41
+ size_t data_len = RSTRING_LEN(string);
42
+ const char * data = RSTRING_PTR(string);
43
+
44
+ size_t key_length = ctx->stored_key_length;
45
+ size_t mac_length = EVP_MD_size(ctx->md);
46
+ const cryptogram_t *cryptogram = cryptogram_alloc(key_length, mac_length, data_len - key_length - mac_length);
47
+
48
+ memcpy(cryptogram_key_data(cryptogram), data, data_len);
49
+
50
+ return cryptogram;
51
+ }
52
+
53
+ /*
54
+ * call-seq:
55
+ * OpenSSL::PKey::EC::IES.new(key, algorithm_spec)
56
+ *
57
+ * Algorithm spec is currently ignored.
58
+ */
59
+ static VALUE ies_initialize(VALUE self, VALUE key, VALUE algo)
60
+ {
61
+ VALUE args[1];
62
+
63
+ rb_iv_set(self, "@algorithm", algo);
64
+
65
+ args[0] = key;
66
+ return rb_call_super(1, args);
67
+ }
68
+
69
+ /*
70
+ * call-seq:
71
+ * ecies.public_encrypt(plaintext) => String
72
+ *
73
+ * The pem_string given in init must contain public key.
74
+ */
75
+ static VALUE ies_public_encrypt(VALUE self, VALUE clear_text)
76
+ {
77
+ ies_ctx_t *ctx;
78
+ char error[1024] = "Unknown error";
79
+ VALUE cipher_text;
80
+ cryptogram_t *cryptogram;
81
+
82
+ StringValue(clear_text);
83
+
84
+ ctx = create_context(self);
85
+ if (!EC_KEY_get0_public_key(ctx->user_key))
86
+ rb_raise(eIESError, "Given EC key is not public key");
87
+
88
+ cryptogram = ecies_encrypt(ctx, (unsigned char*)RSTRING_PTR(clear_text), RSTRING_LEN(clear_text), error);
89
+ if (cryptogram == NULL) {
90
+ free(ctx);
91
+ ctx = NULL;
92
+ rb_raise(eIESError, "Error in encryption: %s", error);
93
+ }
94
+ cipher_text = ies_cryptogram_to_rb_string(ctx, cryptogram);
95
+ cryptogram_free(cryptogram);
96
+ free(ctx);
97
+ return cipher_text;
98
+ }
99
+
100
+ /*
101
+ * call-seq:
102
+ * ecies.private_decrypt(plaintext) => String
103
+ *
104
+ * The pem_string given in init must contain private key.
105
+ */
106
+ static VALUE ies_private_decrypt(VALUE self, VALUE cipher_text)
107
+ {
108
+ ies_ctx_t *ctx;
109
+ char error[1024] = "Unknown error";
110
+ VALUE clear_text;
111
+ cryptogram_t *cryptogram;
112
+ size_t length;
113
+ unsigned char *data;
114
+
115
+ StringValue(cipher_text);
116
+
117
+ ctx = create_context(self);
118
+ if (!EC_KEY_get0_private_key(ctx->user_key))
119
+ rb_raise(eIESError, "Given EC key is not private key");
120
+
121
+ cryptogram = ies_rb_string_to_cryptogram(ctx, cipher_text);
122
+ data = ecies_decrypt(ctx, cryptogram, &length, error);
123
+ cryptogram_free(cryptogram);
124
+ free(ctx);
125
+
126
+ if (data == NULL) {
127
+ rb_raise(eIESError, "Error in decryption: %s", error);
128
+ }
129
+
130
+ clear_text = rb_str_new((char *)data, length);
131
+ free(data);
132
+
133
+ return clear_text;
134
+ }
135
+
136
+ /*
137
+ * INIT
138
+ */
139
+ void
140
+ Init_ies(void)
141
+ {
142
+ static VALUE cIES;
143
+ VALUE cEC;
144
+
145
+ rb_require("openssl");
146
+ cEC = rb_path2class("OpenSSL::PKey::EC");
147
+
148
+ /* Document-class: OpenSSL::PKey::EC::IES
149
+ *
150
+ * An implementation of ECIES cryptography.
151
+ */
152
+ cIES = rb_define_class_under(cEC, "IES", cEC);
153
+
154
+ rb_define_method(cIES, "initialize", ies_initialize, 2);
155
+ rb_define_method(cIES, "public_encrypt", ies_public_encrypt, 1);
156
+ rb_define_method(cIES, "private_decrypt", ies_private_decrypt, 1);
157
+
158
+ eIESError = rb_define_class_under(cIES, "IESError", rb_eRuntimeError);
159
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * reference: ecies.h
3
+ */
4
+
5
+ #ifndef _IES_H_
6
+ #define _IES_H_
7
+
8
+ #include <openssl/ssl.h>
9
+ #include <openssl/crypto.h>
10
+ #include <openssl/err.h>
11
+
12
+ #include <ruby.h>
13
+
14
+ typedef struct {
15
+ const EVP_CIPHER *cipher;
16
+ const EVP_MD *md; /* for mac tag */
17
+ const EVP_MD *kdf_md; /* for KDF */
18
+ size_t stored_key_length;
19
+ const EC_KEY *user_key;
20
+ } ies_ctx_t;
21
+
22
+ typedef struct {
23
+ struct {
24
+ size_t key;
25
+ size_t mac;
26
+ size_t body;
27
+ } length;
28
+ } cryptogram_head_t;
29
+
30
+ typedef unsigned char * cryptogram_t;
31
+
32
+ void cryptogram_free(cryptogram_t *cryptogram);
33
+ unsigned char * cryptogram_key_data(const cryptogram_t *cryptogram);
34
+ unsigned char * cryptogram_mac_data(const cryptogram_t *cryptogram);
35
+ unsigned char * cryptogram_body_data(const cryptogram_t *cryptogram);
36
+ size_t cryptogram_key_length(const cryptogram_t *cryptogram);
37
+ size_t cryptogram_mac_length(const cryptogram_t *cryptogram);
38
+ size_t cryptogram_body_length(const cryptogram_t *cryptogram);
39
+ size_t cryptogram_data_sum_length(const cryptogram_t *cryptogram);
40
+ size_t cryptogram_total_length(const cryptogram_t *cryptogram);
41
+ cryptogram_t * cryptogram_alloc(size_t key, size_t mac, size_t body);
42
+
43
+ cryptogram_t * ecies_encrypt(const ies_ctx_t *ctx, const unsigned char *data, size_t length, char *error);
44
+ unsigned char * ecies_decrypt(const ies_ctx_t *ctx, const cryptogram_t *cryptogram, size_t *length, char *error);
45
+
46
+ #endif /* _IES_H_ */
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'openssl-pkey-ec-ies'
7
+ spec.version = '0.0.1'
8
+ spec.authors = ['webpay', 'tomykaira']
9
+ spec.email = ['administrators@webpay.jp']
10
+ spec.summary = %q{ECIES implementation}
11
+ spec.description = %q{IES implementation following ECIES-KEM specification in ISO 18033-2}
12
+ spec.homepage = ''
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+ spec.extensions = %w[ext/ies/extconf.rb]
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.6'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rake-compiler', '~> 0.9.3'
24
+ end
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest/unit'
3
+ require 'openssl/pkey/ec/ies'
4
+
5
+ Minitest::Unit.autorun
6
+
7
+ class TestIES < Minitest::Unit::TestCase
8
+ def setup
9
+ test_key = File.read(File.expand_path(File.join(__FILE__, '..', 'test_key.pem')))
10
+ @ec = OpenSSL::PKey::EC::IES.new(test_key, "placeholder")
11
+ end
12
+
13
+ def test_ec_has_private_and_public_keys
14
+ assert @ec.private_key?
15
+ assert @ec.public_key?
16
+ end
17
+
18
+ def test_encrypt_then_decrypt_get_the_source_text
19
+ source = 'いろはにほへと ちるぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせすん'
20
+ cryptogram = @ec.public_encrypt(source)
21
+ result = @ec.private_decrypt(cryptogram)
22
+ assert_equal source, result.force_encoding('UTF-8')
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ -----BEGIN EC PARAMETERS-----
2
+ BggqhkjOPQMBAQ==
3
+ -----END EC PARAMETERS-----
4
+ -----BEGIN EC PRIVATE KEY-----
5
+ MF8CAQEEGN4sOUixZfhhfyR6/9IESuEgMy692DmuJqAKBggqhkjOPQMBAaE0AzIA
6
+ BCpR0j5VZmqU2R/PFE9dttlCHJMZEYY7XmYRU77U3trXSo0QPrYgDzCwFqVLEfqQ
7
+ hQ==
8
+ -----END EC PRIVATE KEY-----
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openssl-pkey-ec-ies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - webpay
8
+ - tomykaira
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-11-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.6'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake-compiler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 0.9.3
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 0.9.3
56
+ description: IES implementation following ECIES-KEM specification in ISO 18033-2
57
+ email:
58
+ - administrators@webpay.jp
59
+ executables: []
60
+ extensions:
61
+ - ext/ies/extconf.rb
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - ext/ies/cryptogram.c
70
+ - ext/ies/ecies.c
71
+ - ext/ies/extconf.rb
72
+ - ext/ies/ies.c
73
+ - ext/ies/ies.h
74
+ - openssl-pkey-ec-ies.gemspec
75
+ - test/test_ies.rb
76
+ - test/test_key.pem
77
+ homepage: ''
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.2.2
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: ECIES implementation
101
+ test_files:
102
+ - test/test_ies.rb
103
+ - test/test_key.pem