ecies 0.2.0 → 0.3.0
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 +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
|
[](https://travis-ci.org/jamoes/ecies)
|
4
|
+
[](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��
|