openssl-pkey-ec-ies 0.0.1

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.
@@ -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