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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9b17b5e59affac68ce47a7afe053747c90c287201202fffe9a97550644d6c5c
4
- data.tar.gz: 161fb791e8f8e67252ce565fa06fd1196228629e45b3ccbc07384b5b9f207a4a
3
+ metadata.gz: e8ed7a562b019fdcd96fabaacd5a7617577b0b640812a4473997b82e0beb5bd2
4
+ data.tar.gz: bc8b871ae2ea77b8d3288c01dc741b5268ca165a8b154c4cf2ce821ce3abdc5e
5
5
  SHA512:
6
- metadata.gz: df3185e4b726f977a8834c9718f0cbd9ffedec22f00ff597c7d7e877a1a730283b7d8ef66091a0351c3d5c71939cac60196b613a750c822d0d33e0b7d50b3d90
7
- data.tar.gz: 148151802478d20abde87b33cfe0765bda7bf1714507c0e6c818105beeb57fcaf55f5735625c42f9cd7d8b524a09c2e5550b7d91c205e74830988c2f5718b503
6
+ metadata.gz: 7e2c2db8d8b2323d7854a265217ef03d0f82ae4e57b5c597a73e8e348ae9ac06cf981349c6b6cfc8e6bb7b6dd50ed31eb72e4701650e15ca6e42f2703d560022
7
+ data.tar.gz: 3a34f70a7d4cabe2712681b3ef1bc446a6ee53d578f7051f3e12b4b529d9ca82188471f7787933f28301e8e3008dacba2ae42cabfbc6a236e796c7d0efee73f7
@@ -1,2 +1,2 @@
1
- �=�PX�]
2
- 8� ohQPt0�5F{�����5��wf۟:��uL /"Xs�G�*xR�O�(T~��Z9��Lf�����W�< gW�D��b��@�R�����Q�Yn�^D{I�e��_pd��z�߂$����&`5�(�?��AQ��p��
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
@@ -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
- Release 2018-04-19
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
- public_key_hex =
52
+ public_key = ECIES::Crypt.public_key_from_hex(
52
53
  "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb"\
53
- "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"
54
- encrypted = ECIES::Crypt.new.encrypt(public_key_hex, 'you rock!')
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
- private_key_hex = "<satoshi's private key>"
61
- ECIES::Crypt.new.decrypt(private_key_hex, encrypted) # => "you rock!"
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 a many combinations of various algorithms for ECIES. This library only supports a subset of the allowable algorithms.
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.
@@ -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, ec_group: 'secp256k1', kdf_digest: nil, mac_digest: nil, kdf_shared_info: '', mac_shared_info: '')
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,String] The public key. An 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
- ephemeral_key.public_key.to_bn.to_s(2) + ciphertext + mac
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
- ephemeral_public_key_text = encrypted_message.byteslice(0, ephemeral_public_key_length)
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(ephemeral_public_key_text, 2))
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 be
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
@@ -1,4 +1,4 @@
1
1
  module ECIES
2
2
  # This gem's version.
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
@@ -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
- crypt = ECIES::Crypt.new
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
- encrypted = crypt.encrypt(public_key_hex, 'secret')
23
- expect(crypt.decrypt(private_key_hex, encrypted)).to eq 'secret'
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(ec_group: key.group)
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
- it 'Detects invalid hex-encoded points' do
38
- key = OpenSSL::PKey::EC.new('secp224k1').generate_key
39
- public_key_hex = key.public_key.to_bn.to_s(16)
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
- expect{ ECIES::Crypt.new.encrypt(public_key_hex, 'secret') }.to raise_error(OpenSSL::PKey::EC::Point::Error)
42
- end
54
+ @key = OpenSSL::PKey::EC.new('secp256k1').generate_key
55
+ end
43
56
 
44
- it 'Encrypts to known values' do
45
- OpenSSL::PKey::EC.class_eval do
46
- # Overwrites `generate_key` for both the test code below, and the
47
- # ephemeral_key generated in the `encrypt` method.
48
- def generate_key
49
- self.private_key = 2
50
- self.public_key = group.generator.mul(private_key)
51
- self
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
- key = OpenSSL::PKey::EC.new('secp256k1').generate_key
56
-
57
- crypt = ECIES::Crypt.new
58
- crypt_full = ECIES::Crypt.new(mac_length: :full)
59
- crypt_sha512 = ECIES::Crypt.new(digest: 'sha512', mac_length: :full)
60
- crypt_cbc = ECIES::Crypt.new(cipher: 'aes-256-cbc', mac_length: :full)
61
- crypt_mixed = ECIES::Crypt.new(mac_digest: 'sha256', kdf_digest: 'sha512')
62
-
63
- encrypted = crypt.encrypt(key, 'secret')
64
- 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\xCE\x84\xAB\x9F\x0E%\xCD\x94~\x1E\xBC\x89$\x11\xEE6\xE4".force_encoding(Encoding::BINARY)
65
- expect(crypt.decrypt(key, encrypted)).to eq 'secret'
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 'derivates keys correctly' do
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.2.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-19 00:00:00.000000000 Z
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
- 'tt��M�%Co�;��y/t=W6]\�@�(p�/���%��:d>Ѷ3/�?����T���w�靺���`�*��Pj)�܂��
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"�ѿ�V5Qs�bj�ꢳ�/e҃Lp����w&�:��/�xo��%��5Lc����(�e˚��F��