ecies 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -2
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +15 -1
- data/README.md +11 -7
- data/lib/ecies/crypt.rb +54 -30
- data/lib/ecies/version.rb +1 -1
- data/spec/crypt_spec.rb +62 -60
- metadata +2 -2
- metadata.gz.sig +1 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8ed7a562b019fdcd96fabaacd5a7617577b0b640812a4473997b82e0beb5bd2
|
4
|
+
data.tar.gz: bc8b871ae2ea77b8d3288c01dc741b5268ca165a8b154c4cf2ce821ce3abdc5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e2c2db8d8b2323d7854a265217ef03d0f82ae4e57b5c597a73e8e348ae9ac06cf981349c6b6cfc8e6bb7b6dd50ed31eb72e4701650e15ca6e42f2703d560022
|
7
|
+
data.tar.gz: 3a34f70a7d4cabe2712681b3ef1bc446a6ee53d578f7051f3e12b4b529d9ca82188471f7787933f28301e8e3008dacba2ae42cabfbc6a236e796c7d0efee73f7
|
checksums.yaml.gz.sig
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
����a*@hYԍ#F��-��3�?����%@+1��=-j�<X
|
2
|
+
���{�%H,��y�[��Z2\9T���1[V����[<`Y�ړD\��L$)��G^ٛ��x��@��#:�Ҿ��Wwz������Ҳ:RU�iA��c��q�
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -4,9 +4,23 @@ Change log
|
|
4
4
|
This gem follows [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html).
|
5
5
|
All classes and public methods are part of the public API.
|
6
6
|
|
7
|
+
0.3.0
|
8
|
+
---
|
9
|
+
Released on 2018-04-22
|
10
|
+
|
11
|
+
- Prevent benign malleability, as suggested in sec1-v2 page 97.
|
12
|
+
- The ECIES process is modified to prevent benign malleability by including the ephemeral public key as an input to the KDF.
|
13
|
+
- All encrypted output generated with previous versions cannot be decrypted with this version, and older versions cannot decrypt output generated with this version.
|
14
|
+
- The choice was made to simply break compatibility early in this library's life, rather than add an extra configuration parameter that should almost always be unused.
|
15
|
+
- Add `Crypt.public_key_from_hex` method.
|
16
|
+
- Add `Crypt.private_key_from_hex` method.
|
17
|
+
- Remove support for hex-encoded keys in `Crypt#encrypt` and `Crypt#decrypt` methods. The above `*_from_hex` helper methods can be used instead.
|
18
|
+
- Remove `ec_group` option from `Crypt` constructor.
|
19
|
+
- Add `Crypt#to_s` method.
|
20
|
+
|
7
21
|
0.2.0
|
8
22
|
---
|
9
|
-
|
23
|
+
Released on 2018-04-19
|
10
24
|
|
11
25
|
- Add support for hex-encoded keys in `Crypt#encrypt` and `Crypt#decrypt` methods.
|
12
26
|
- Add new option `ec_group` in `Crypt` constructor.
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# ECIES - Elliptical Curve Integrated Encryption System
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/jamoes/ecies.svg?branch=master)](https://travis-ci.org/jamoes/ecies)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/ecies.svg)](https://badge.fury.io/rb/ecies)
|
4
5
|
|
5
6
|
## Description
|
6
7
|
|
@@ -48,17 +49,17 @@ crypt.decrypt(key, encrypted) # => "secret message"
|
|
48
49
|
Bitcoin P2PKH addresses themselves contain only *hashes* of public keys (hence the name, pay-to-public-key-hash). However, any time a P2PKH output is spent, the public key associated with the address is published on the blockchain in the transaction's scriptSig. This allows you to encrypt a message to any bitcoin address that has sent a transaction (or published its public key in other ways). To demonstrate this, we'll encrypt a message to Satoshi's public key from Bitcoin's genesis block:
|
49
50
|
|
50
51
|
```ruby
|
51
|
-
|
52
|
+
public_key = ECIES::Crypt.public_key_from_hex(
|
52
53
|
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb"\
|
53
|
-
"649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"
|
54
|
-
encrypted = ECIES::Crypt.new.encrypt(
|
54
|
+
"649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")
|
55
|
+
encrypted = ECIES::Crypt.new.encrypt(public_key, 'you rock!')
|
55
56
|
```
|
56
57
|
|
57
58
|
To decrypt this message, Satoshi would follow these steps:
|
58
59
|
|
59
60
|
```ruby
|
60
|
-
|
61
|
-
ECIES::Crypt.new.decrypt(
|
61
|
+
private_key = ECIES::Crypt.private_key_from_hex("<satoshi's private key>")
|
62
|
+
ECIES::Crypt.new.decrypt(private_key, encrypted) # => "you rock!"
|
62
63
|
```
|
63
64
|
|
64
65
|
### Default parameters
|
@@ -74,8 +75,7 @@ These defaults work well for encrypting messages to bitcoin keys. This library a
|
|
74
75
|
|
75
76
|
## Compatibility
|
76
77
|
|
77
|
-
The sec1-v2 document allows for
|
78
|
-
|
78
|
+
The sec1-v2 document allows for many combinations of various algorithms for ECIES. This library only supports a subset of the allowable algorithms:
|
79
79
|
- Key Derivation Functions
|
80
80
|
- Supported:
|
81
81
|
- ANSI-X9.63-KDF
|
@@ -119,6 +119,10 @@ The sec1-v2 document allows for a many combinations of various algorithms for EC
|
|
119
119
|
- 3-key TDES in CBC mode
|
120
120
|
- XOR encryption scheme
|
121
121
|
|
122
|
+
In addition, the following options have been chosen:
|
123
|
+
- Elliptical curve points are represented in compressed form.
|
124
|
+
- Benign malleability is prevented by including the ephemeral public key as an input to the KDF (sec1-v2 p97).
|
125
|
+
|
122
126
|
## Supported platforms
|
123
127
|
|
124
128
|
Ruby 2.0 and above.
|
data/lib/ecies/crypt.rb
CHANGED
@@ -27,9 +27,6 @@ module ECIES
|
|
27
27
|
# length will be equal to half the mac_digest's digest_legnth. If
|
28
28
|
# :full, the mac length will be equal to the mac_digest's
|
29
29
|
# digest_length.
|
30
|
-
# @param ec_group [OpenSSL::PKey::EC::Group,String] The elliptical curve
|
31
|
-
# group to use when the key is passed in hex form to `encrypt` or
|
32
|
-
# `decrypt`.
|
33
30
|
# @param kdf_digest [String,OpenSSL::Digest,nil] The digest algorithm to
|
34
31
|
# use for KDF. If not specified, the `digest` argument will be used.
|
35
32
|
# @param mac_digest [String,OpenSSL::Digest,nil] The digest algorithm to
|
@@ -38,9 +35,8 @@ module ECIES
|
|
38
35
|
# info used for KDF, also known as SharedInfo1.
|
39
36
|
# @param mac_shared_info [String] Optional. A string containing the shared
|
40
37
|
# info used for MAC, also known as SharedInfo2.
|
41
|
-
def initialize(cipher: 'AES-256-CTR', digest: 'SHA256', mac_length: :half,
|
38
|
+
def initialize(cipher: 'AES-256-CTR', digest: 'SHA256', mac_length: :half, kdf_digest: nil, mac_digest: nil, kdf_shared_info: '', mac_shared_info: '')
|
42
39
|
@cipher = OpenSSL::Cipher.new(cipher)
|
43
|
-
@ec_group = OpenSSL::PKey::EC::Group.new(ec_group)
|
44
40
|
@mac_digest = OpenSSL::Digest.new(mac_digest || digest)
|
45
41
|
@kdf_digest = OpenSSL::Digest.new(kdf_digest || digest)
|
46
42
|
@kdf_shared_info = kdf_shared_info
|
@@ -57,27 +53,21 @@ module ECIES
|
|
57
53
|
|
58
54
|
# Encrypts a message to a public key using ECIES.
|
59
55
|
#
|
60
|
-
# @param key [OpenSSL::EC:PKey
|
61
|
-
# containing the public key, or a hex-encoded string representing the
|
62
|
-
# public key on this Crypt's `ec_group`.
|
56
|
+
# @param key [OpenSSL::EC:PKey] The public key.
|
63
57
|
# @param message [String] The plain-text message.
|
64
58
|
# @return [String] The octet string of the encrypted message.
|
65
59
|
def encrypt(key, message)
|
66
|
-
if key.is_a?(String)
|
67
|
-
new_key = OpenSSL::PKey::EC.new(@ec_group)
|
68
|
-
new_key.public_key = OpenSSL::PKey::EC::Point.new(@ec_group, OpenSSL::BN.new(key, 16))
|
69
|
-
key = new_key
|
70
|
-
end
|
71
60
|
key.public_key? or raise "Must have public key to encrypt"
|
72
61
|
@cipher.reset
|
73
62
|
|
74
63
|
group_copy = OpenSSL::PKey::EC::Group.new(key.group)
|
75
64
|
group_copy.point_conversion_form = :compressed
|
76
65
|
ephemeral_key = OpenSSL::PKey::EC.new(group_copy).generate_key
|
66
|
+
ephemeral_public_key_octet = ephemeral_key.public_key.to_bn.to_s(2)
|
77
67
|
|
78
68
|
shared_secret = ephemeral_key.dh_compute_key(key.public_key)
|
79
69
|
|
80
|
-
key_pair = kdf(shared_secret, @cipher.key_len + @mac_length)
|
70
|
+
key_pair = kdf(shared_secret, @cipher.key_len + @mac_length, ephemeral_public_key_octet)
|
81
71
|
cipher_key = key_pair.byteslice(0, @cipher.key_len)
|
82
72
|
hmac_key = key_pair.byteslice(-@mac_length, @mac_length)
|
83
73
|
|
@@ -88,23 +78,15 @@ module ECIES
|
|
88
78
|
|
89
79
|
mac = OpenSSL::HMAC.digest(@mac_digest, hmac_key, ciphertext + @mac_shared_info).byteslice(0, @mac_length)
|
90
80
|
|
91
|
-
|
81
|
+
ephemeral_public_key_octet + ciphertext + mac
|
92
82
|
end
|
93
83
|
|
94
84
|
# Decrypts a message with a private key using ECIES.
|
95
85
|
#
|
96
86
|
# @param key [OpenSSL::EC:PKey] The private key.
|
97
|
-
# @param key [OpenSSL::EC:PKey,String] The private key. An OpenSSL::EC:PKey
|
98
|
-
# containing the private key, or a hex-encoded string representing the
|
99
|
-
# private key on this Crypt's `ec_group`.
|
100
87
|
# @param encrypted_message [String] Octet string of the encrypted message.
|
101
88
|
# @return [String] The plain-text message.
|
102
89
|
def decrypt(key, encrypted_message)
|
103
|
-
if key.is_a?(String)
|
104
|
-
new_key = OpenSSL::PKey::EC.new(@ec_group)
|
105
|
-
new_key.private_key = OpenSSL::BN.new(key, 16)
|
106
|
-
key = new_key
|
107
|
-
end
|
108
90
|
key.private_key? or raise "Must have private key to decrypt"
|
109
91
|
@cipher.reset
|
110
92
|
|
@@ -115,15 +97,15 @@ module ECIES
|
|
115
97
|
ciphertext_length = encrypted_message.bytesize - ephemeral_public_key_length - @mac_length
|
116
98
|
ciphertext_length > 0 or raise OpenSSL::PKey::ECError, "Encrypted message too short"
|
117
99
|
|
118
|
-
|
100
|
+
ephemeral_public_key_octet = encrypted_message.byteslice(0, ephemeral_public_key_length)
|
119
101
|
ciphertext = encrypted_message.byteslice(ephemeral_public_key_length, ciphertext_length)
|
120
102
|
mac = encrypted_message.byteslice(-@mac_length, @mac_length)
|
121
103
|
|
122
|
-
ephemeral_public_key = OpenSSL::PKey::EC::Point.new(group_copy, OpenSSL::BN.new(
|
104
|
+
ephemeral_public_key = OpenSSL::PKey::EC::Point.new(group_copy, OpenSSL::BN.new(ephemeral_public_key_octet, 2))
|
123
105
|
|
124
106
|
shared_secret = key.dh_compute_key(ephemeral_public_key)
|
125
107
|
|
126
|
-
key_pair = kdf(shared_secret, @cipher.key_len + @mac_length)
|
108
|
+
key_pair = kdf(shared_secret, @cipher.key_len + @mac_length, ephemeral_public_key_octet)
|
127
109
|
cipher_key = key_pair.byteslice(0, @cipher.key_len)
|
128
110
|
hmac_key = key_pair.byteslice(-@mac_length, @mac_length)
|
129
111
|
|
@@ -139,11 +121,13 @@ module ECIES
|
|
139
121
|
|
140
122
|
# Key-derivation function, compatible with ANSI-X9.63-KDF
|
141
123
|
#
|
142
|
-
# @param shared_secret [String] The shared secret from which the key will
|
143
|
-
# derived.
|
124
|
+
# @param shared_secret [String] The shared secret from which the key will
|
125
|
+
# be derived.
|
144
126
|
# @param length [Integer] The length of the key to generate.
|
127
|
+
# @param shared_info_suffix [String] The suffix to append to the
|
128
|
+
# shared_info.
|
145
129
|
# @return [String] Octet string of the derived key.
|
146
|
-
def kdf(shared_secret, length)
|
130
|
+
def kdf(shared_secret, length, shared_info_suffix)
|
147
131
|
length >=0 or raise "length cannot be negative"
|
148
132
|
return "" if length == 0
|
149
133
|
|
@@ -158,11 +142,51 @@ module ECIES
|
|
158
142
|
counter += 1
|
159
143
|
counter_bytes = [counter].pack('N')
|
160
144
|
|
161
|
-
io << @kdf_digest.digest(shared_secret + counter_bytes + @kdf_shared_info)
|
145
|
+
io << @kdf_digest.digest(shared_secret + counter_bytes + @kdf_shared_info + shared_info_suffix)
|
162
146
|
if io.pos >= length
|
163
147
|
return io.string.byteslice(0, length)
|
164
148
|
end
|
165
149
|
end
|
166
150
|
end
|
151
|
+
|
152
|
+
# @return [String] A string representing this Crypt's parameters.
|
153
|
+
def to_s
|
154
|
+
"KDF-#{@kdf_digest.name}_" +
|
155
|
+
"HMAC-SHA-#{@mac_digest.digest_length * 8}-#{@mac_length * 8}_" +
|
156
|
+
@cipher.name
|
157
|
+
end
|
158
|
+
|
159
|
+
# Converts a hex-encoded public key to an `OpenSSL::PKey::EC`.
|
160
|
+
#
|
161
|
+
# @param hex_string [String] The hex-encoded public key.
|
162
|
+
# @param ec_group [OpenSSL::PKey::EC::Group,String] The elliptical curve
|
163
|
+
# group for this public key.
|
164
|
+
# @return [OpenSSL::PKey::EC] The public key.
|
165
|
+
# @raise [OpenSSL::PKey::EC::Point::Error] If the public key is invalid.
|
166
|
+
def self.public_key_from_hex(hex_string, ec_group = 'secp256k1')
|
167
|
+
ec_group = OpenSSL::PKey::EC::Group.new(ec_group) if ec_group.is_a?(String)
|
168
|
+
key = OpenSSL::PKey::EC.new(ec_group)
|
169
|
+
key.public_key = OpenSSL::PKey::EC::Point.new(ec_group, OpenSSL::BN.new(hex_string, 16))
|
170
|
+
key
|
171
|
+
end
|
172
|
+
|
173
|
+
# Converts a hex-encoded private key to an `OpenSSL::PKey::EC`.
|
174
|
+
#
|
175
|
+
# @param hex_string [String] The hex-encoded private key.
|
176
|
+
# @param ec_group [OpenSSL::PKey::EC::Group,String] The elliptical curve
|
177
|
+
# group for this private key.
|
178
|
+
# @return [OpenSSL::PKey::EC] The private key.
|
179
|
+
# @note The returned key only contains the private component. In order to
|
180
|
+
# populate the public component of the key, you must compute it as
|
181
|
+
# follows: `key.public_key = key.group.generator.mul(key.private_key)`.
|
182
|
+
# @raise [OpenSSL::PKey::ECError] If the private key is invalid.
|
183
|
+
def self.private_key_from_hex(hex_string, ec_group = 'secp256k1')
|
184
|
+
ec_group = OpenSSL::PKey::EC::Group.new(ec_group) if ec_group.is_a?(String)
|
185
|
+
key = OpenSSL::PKey::EC.new(ec_group)
|
186
|
+
key.private_key = OpenSSL::BN.new(hex_string, 16)
|
187
|
+
key.private_key < ec_group.order or raise OpenSSL::PKey::ECError, "Private key greater than group's order"
|
188
|
+
key.private_key > 1 or raise OpenSSL::PKey::ECError, "Private key too small"
|
189
|
+
key
|
190
|
+
end
|
167
191
|
end
|
168
192
|
end
|
data/lib/ecies/version.rb
CHANGED
data/spec/crypt_spec.rb
CHANGED
@@ -10,6 +10,9 @@ describe ECIES::Crypt do
|
|
10
10
|
|
11
11
|
encrypted = crypt.encrypt(key, 'secret')
|
12
12
|
expect(crypt.decrypt(key, encrypted)).to eq 'secret'
|
13
|
+
|
14
|
+
expect{ ECIES::Crypt.new(mac_length: :full).decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
15
|
+
expect{ ECIES::Crypt.new(mac_digest: 'sha512').decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
13
16
|
end
|
14
17
|
|
15
18
|
it 'Supports hex-encoded keys' do
|
@@ -17,73 +20,66 @@ describe ECIES::Crypt do
|
|
17
20
|
public_key_hex = key.public_key.to_bn.to_s(16)
|
18
21
|
private_key_hex = key.private_key.to_s(16)
|
19
22
|
|
20
|
-
|
23
|
+
public_key = ECIES::Crypt.public_key_from_hex(public_key_hex)
|
24
|
+
private_key = ECIES::Crypt.private_key_from_hex(private_key_hex)
|
21
25
|
|
22
|
-
|
23
|
-
expect(
|
26
|
+
expect(public_key.public_key).to eq key.public_key
|
27
|
+
expect(private_key.private_key).to eq key.private_key
|
28
|
+
|
29
|
+
expect{ ECIES::Crypt.public_key_from_hex(public_key_hex, 'secp224k1') }.to raise_error(OpenSSL::PKey::EC::Point::Error)
|
30
|
+
expect{ ECIES::Crypt.private_key_from_hex(private_key_hex, 'secp224k1') }.to raise_error(OpenSSL::PKey::ECError)
|
31
|
+
expect{ ECIES::Crypt.private_key_from_hex("00") }.to raise_error(OpenSSL::PKey::ECError)
|
24
32
|
end
|
25
33
|
|
26
34
|
it 'Supports other EC curves' do
|
27
35
|
key = OpenSSL::PKey::EC.new('secp224k1').generate_key
|
28
|
-
crypt = ECIES::Crypt.new
|
36
|
+
crypt = ECIES::Crypt.new
|
29
37
|
|
30
38
|
encrypted = crypt.encrypt(key, 'secret')
|
31
39
|
expect(crypt.decrypt(key, encrypted)).to eq 'secret'
|
32
|
-
|
33
|
-
encrypted = crypt.encrypt(key.public_key.to_bn.to_s(16), 'secret')
|
34
|
-
expect(crypt.decrypt(key.private_key.to_s(16), encrypted)).to eq 'secret'
|
35
40
|
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
context 'known value' do
|
43
|
+
before(:all) do
|
44
|
+
OpenSSL::PKey::EC.class_eval do
|
45
|
+
# Overwrites `generate_key` for both the key generated below, and the
|
46
|
+
# ephemeral_key generated in the `encrypt` method.
|
47
|
+
def generate_key
|
48
|
+
self.private_key = 2
|
49
|
+
self.public_key = group.generator.mul(private_key)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
end
|
40
53
|
|
41
|
-
|
42
|
-
|
54
|
+
@key = OpenSSL::PKey::EC.new('secp256k1').generate_key
|
55
|
+
end
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
57
|
+
[
|
58
|
+
[ECIES::Crypt.new, "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5C\x9E\xE0\x0FYBZ\xBB\xC8\x95\x93\xC1@\xC6+\xE2/yb\x065\xFF".b],
|
59
|
+
[ECIES::Crypt.new(mac_length: :full), "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5C\x9E\xE0\x0FYB\x03.\x1E\x92,[\rI\xBC\xCC\xFD%\xCD)9\v!]]A\xE0\xADc\xBA[\xA4\xF2\xB1\xB5\xC5)\xA4".b],
|
60
|
+
[ECIES::Crypt.new(digest: 'sha512', mac_length: :full), "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5\xA2Y\x1A\x7F\xB3\xB2\xA7\xDE\x03\xF4\xA6\e\xD1\x9F\xF9\xD5P\x06\x91\x8EiW\xC82\xD9\xBB\xD2\xC92\xE2\x9F\x15F.\x8C]\xE3Y2\xD3L\xE8\xC4\x9F\xBF\xA5S\x98\x9AYy_Y\xF8\x05\xE7\x19\x9E\xDA\vn;Bvm\xA2`i5:".b],
|
61
|
+
[ECIES::Crypt.new(cipher: 'aes-256-cbc', mac_length: :full), "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5\xDF\xCD\x95\xAD!m\xAA/Xv\"\x97\x04\xEE\x9F\xEB^\x1F\xA7\xC9n\xE3\x94l;\xBA\xF2\xBE\xCD\x83\x02+\x02\x9D\x18\x11\x9A\xBEz_\x8A\xDB\xA3\x00\xF7\x8A\x94G".b],
|
62
|
+
[ECIES::Crypt.new(mac_digest: 'sha256', kdf_digest: 'sha512'), "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5\xA2Y\x1A\x7F\xB3\xB2l\x9E|\xC4\xBCE r\xA6\xB1k\x93W\xE5d\xE4".b],
|
63
|
+
].each do |crypt, expected_value|
|
64
|
+
it "matches for #{crypt.to_s}" do
|
65
|
+
encrypted = crypt.encrypt(@key, 'secret')
|
66
|
+
expect(encrypted).to eq expected_value
|
67
|
+
expect(crypt.decrypt(@key, encrypted)).to eq 'secret'
|
52
68
|
end
|
53
69
|
end
|
70
|
+
end
|
54
71
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
expect{ crypt_full.decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
67
|
-
|
68
|
-
encrypted = crypt_full.encrypt(key, 'secret')
|
69
|
-
expect(encrypted).to eq "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5B;\x12:\x17\xCEo\x9B\xA7\x955\x89\x9FR\xDF\x1C\xED\x00\x86<\n\x04\v\xD6(\x9D\xF5\xF9\x13\xC8/\xD7os(ZsF".force_encoding(Encoding::BINARY)
|
70
|
-
expect(crypt_full.decrypt(key, encrypted)).to eq 'secret'
|
71
|
-
expect{ crypt_sha512.decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
72
|
-
|
73
|
-
encrypted = crypt_sha512.encrypt(key, 'secret')
|
74
|
-
expect(encrypted).to eq "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5%\r^\xA9\xD9\xDC\xFB\xD7\x14b\xB4\x00\x84\xABl\xEAh\x0Fc\x805\xAF\x1DT\x05\x87`\xA5\xC4\xB7\xB5r\xF6\x89\xB1U0\x0E \xD4\x1E\x16\x184\xE9:\xE7\x951\xF4\xB3\x93\"A\x85\x1F\x9A\x8E\xAD\xE1(\x1D\xB3\xC4\x15\xD3\xB1\xA8\xFB\x1D".force_encoding(Encoding::BINARY)
|
75
|
-
expect(crypt_sha512.decrypt(key, encrypted)).to eq 'secret'
|
76
|
-
expect{ crypt_full.decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
77
|
-
|
78
|
-
encrypted = crypt_cbc.encrypt(key, 'secret')
|
79
|
-
expect(encrypted).to eq "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5\x1Fj\x04N\eg($\xD4\xFBD\xFFd\xA1\xA3z\x90T#\x1D\x12{3IM\x93!|\xA5\xAF&\xD4+;\e\xA6i.wD\x1F\xCB\xE1\x90{\xB6\x8B\xAF".force_encoding(Encoding::BINARY)
|
80
|
-
expect(crypt_cbc.decrypt(key, encrypted)).to eq 'secret'
|
81
|
-
expect{ crypt_mixed.decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
82
|
-
|
83
|
-
encrypted = crypt_mixed.encrypt(key, 'secret')
|
84
|
-
expect(encrypted).to eq "\x02\xC6\x04\x7F\x94A\xED}m0E@n\x95\xC0|\xD8\\w\x8EK\x8C\xEF<\xA7\xAB\xAC\t\xB9\\p\x9E\xE5%\r^\xA9\xD9\xDC\xF5\\\x9A\x04\xE0T\x91\xEA=\xA6W\x84X\xBB\xCA\xB4".force_encoding(Encoding::BINARY)
|
85
|
-
expect(crypt_mixed.decrypt(key, encrypted)).to eq 'secret'
|
86
|
-
expect{ crypt_full.decrypt(key, encrypted) }.to raise_error(OpenSSL::PKey::ECError)
|
72
|
+
it '#to_s' do
|
73
|
+
[
|
74
|
+
[ECIES::Crypt.new, "KDF-SHA256_HMAC-SHA-256-128_AES-256-CTR"],
|
75
|
+
[ECIES::Crypt.new(mac_length: :full), "KDF-SHA256_HMAC-SHA-256-256_AES-256-CTR"],
|
76
|
+
[ECIES::Crypt.new(digest: 'sha512'), "KDF-SHA512_HMAC-SHA-512-256_AES-256-CTR"],
|
77
|
+
[ECIES::Crypt.new(mac_digest: 'sha512'), "KDF-SHA256_HMAC-SHA-512-256_AES-256-CTR"],
|
78
|
+
[ECIES::Crypt.new(mac_digest: 'sha512', kdf_digest: 'sha224'), "KDF-SHA224_HMAC-SHA-512-256_AES-256-CTR"],
|
79
|
+
[ECIES::Crypt.new(cipher: 'aes-128-cbc'), "KDF-SHA256_HMAC-SHA-256-128_AES-128-CBC"],
|
80
|
+
].each do |crypt, expected_value|
|
81
|
+
expect(crypt.to_s).to eq expected_value
|
82
|
+
end
|
87
83
|
end
|
88
84
|
|
89
85
|
it 'Raises on unknown cipher or digest' do
|
@@ -103,10 +99,8 @@ describe ECIES::Crypt do
|
|
103
99
|
end
|
104
100
|
end
|
105
101
|
|
106
|
-
|
107
|
-
|
108
102
|
describe '#kdf' do
|
109
|
-
it '
|
103
|
+
it 'derives keys correctly' do
|
110
104
|
sha256_test_vectors = [
|
111
105
|
# [shared_secret, shared_info, expected_key]
|
112
106
|
['96c05619d56c328ab95fe84b18264b08725b85e33fd34f08', '', '443024c3dae66b95e6f5670601558f71'],
|
@@ -120,16 +114,24 @@ describe ECIES::Crypt do
|
|
120
114
|
shared_info = [shared_info].pack('H*')
|
121
115
|
expected_key = [expected_key].pack('H*')
|
122
116
|
|
123
|
-
computed_key = ECIES::Crypt.new(kdf_shared_info: shared_info).kdf(shared_secret, expected_key.size)
|
124
|
-
|
117
|
+
computed_key = ECIES::Crypt.new(kdf_shared_info: shared_info).kdf(shared_secret, expected_key.size, '')
|
125
118
|
expect(computed_key).to eq expected_key
|
126
119
|
end
|
127
120
|
end
|
128
121
|
|
122
|
+
it 'concats kdf_shared_info with shared_info_suffix' do
|
123
|
+
shared_secret = ['22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d'].pack('H*')
|
124
|
+
shared_info = ['75eef81aa3041e33'].pack('H*')
|
125
|
+
shared_info_suffix = ['b80971203d2c0c52'].pack('H*')
|
126
|
+
expected_key = ['c498af77161cc59f2962b9a713e2b215152d139766ce34a776df11866a69bf2e52a13d9c7c6fc878c50c5ea0bc7b00e0da2447cfd874f6cf92f30d0097111485500c90c3af8b487872d04685d14c8d1dc8d7fa08beb0ce0ababc11f0bd496269142d43525a78e5bc79a17f59676a5706dc54d54d4d1f0bd7e386128ec26afc21'].pack('H*')
|
127
|
+
|
128
|
+
computed_key = ECIES::Crypt.new(kdf_shared_info: shared_info).kdf(shared_secret, expected_key.size, shared_info_suffix)
|
129
|
+
expect(computed_key).to eq expected_key
|
130
|
+
end
|
131
|
+
|
129
132
|
it 'raises when size is invalid' do
|
130
|
-
expect{ ECIES::Crypt.new.kdf('a', -1) }.to raise_error(RuntimeError)
|
131
|
-
expect{ ECIES::Crypt.new.kdf('a', 32 * 2**32) }.to raise_error(RuntimeError)
|
133
|
+
expect{ ECIES::Crypt.new.kdf('a', -1, '') }.to raise_error(RuntimeError)
|
134
|
+
expect{ ECIES::Crypt.new.kdf('a', 32 * 2**32, '') }.to raise_error(RuntimeError)
|
132
135
|
end
|
133
136
|
end
|
134
|
-
|
135
137
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen McCarthy
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
kts216EGG4oP6dVuZmf2Ii2F4lQTBDdZM/cisW8jCkO7KeEzJAPhIw1JJwHltHya
|
31
31
|
0TpOI3t2Mz/FJ+rudtz9PJ/d8QvhrF7M7+qH4w==
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2018-04-
|
33
|
+
date: 2018-04-22 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: bundler
|
metadata.gz.sig
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
'
|
2
|
-
��x�jGZLۋ��k[r�\Ƙu;*�=�`��\/)
|
1
|
+
Ġ�Jd�cs��?� �-�ܞ���j��������`%4���@���]"�J�|]����v�k�F�Z�*��}�YZ+��EI�o�&|�MϲI�DB��7��QzŖI�8Oě�K�>�Tm�7��x�Ր:��Һ����0�7��i�'�p-װ�����{!0^�c-=赌��-�C"�ѿ�V5�Qs�bj�ꢳ�/e҃Lp����w&�:��/�xo��%��5L�c����(�e˚��F��
|