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.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +15 -0
- data/ext/ies/cryptogram.c +65 -0
- data/ext/ies/ecies.c +555 -0
- data/ext/ies/extconf.rb +34 -0
- data/ext/ies/ies.c +159 -0
- data/ext/ies/ies.h +46 -0
- data/openssl-pkey-ec-ies.gemspec +24 -0
- data/test/test_ies.rb +24 -0
- data/test/test_key.pem +8 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
@@ -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
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|
+
}
|
data/ext/ies/ecies.c
ADDED
@@ -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
|
+
}
|
data/ext/ies/extconf.rb
ADDED
@@ -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"
|
data/ext/ies/ies.c
ADDED
@@ -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
|
+
}
|
data/ext/ies/ies.h
ADDED
@@ -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
|
data/test/test_ies.rb
ADDED
@@ -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
|
data/test/test_key.pem
ADDED
@@ -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
|