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 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��