Coverage Summary for Class: EthereumIESEngine (org.ethereum.crypto)
Class |
Class, %
|
Method, %
|
Line, %
|
EthereumIESEngine |
0%
(0/1)
|
0%
(0/12)
|
0%
(0/161)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.crypto;
21
22 import org.bouncycastle.crypto.*;
23 import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator;
24 import org.bouncycastle.crypto.params.*;
25 import org.bouncycastle.util.Arrays;
26 import org.bouncycastle.util.BigIntegers;
27 import org.bouncycastle.util.Pack;
28
29 import java.io.ByteArrayInputStream;
30 import java.io.IOException;
31 import java.math.BigInteger;
32
33 /**
34 * Support class for constructing integrated encryption cipher
35 * for doing basic message exchanges on top of key agreement ciphers.
36 * Follows the description given in IEEE Std 1363a with a couple of changes
37 * specific to Ethereum:
38 * - Hash the MAC key before use
39 * - Include the encryption iv in the MAC computation
40 */
41 public class EthereumIESEngine
42 {
43 private final Digest hash;
44 BasicAgreement agree;
45 DerivationFunction kdf;
46 Mac mac;
47 BufferedBlockCipher cipher;
48 byte[] macBuf;
49
50 boolean forEncryption;
51 CipherParameters privParam;
52 CipherParameters pubParam;
53
54 IESParameters param;
55
56 byte[] v;
57 private EphemeralKeyPairGenerator keyPairGenerator;
58 private KeyParser keyParser;
59 private byte[] iv;
60 boolean hashK2 = true;
61
62 /**
63 * set up for use with stream mode, where the key derivation function
64 * is used to provide a stream of bytes to xor with the message.
65 * @param agree the key agreement used as the basis for the encryption
66 * @param kdf the key derivation function used for byte generation
67 * @param mac the message authentication code generator for the message
68 * @param hash hash ing function
69 * @param cipher the actual cipher
70 */
71 public EthereumIESEngine(
72 BasicAgreement agree,
73 DerivationFunction kdf,
74 Mac mac, Digest hash, BufferedBlockCipher cipher)
75 {
76 this.agree = agree;
77 this.kdf = kdf;
78 this.mac = mac;
79 this.hash = hash;
80 this.macBuf = new byte[mac.getMacSize()];
81 this.cipher = cipher;
82 }
83
84
85 public void setHashMacKey(boolean hashK2) {
86 this.hashK2 = hashK2;
87 }
88
89 /**
90 * Initialise the encryptor.
91 *
92 * @param forEncryption whether or not this is encryption/decryption.
93 * @param privParam our private key parameters
94 * @param pubParam the recipient's/sender's public key parameters
95 * @param params encoding and derivation parameters, may be wrapped to include an iv for an underlying block cipher.
96 */
97 public void init(
98 boolean forEncryption,
99 CipherParameters privParam,
100 CipherParameters pubParam,
101 CipherParameters params)
102 {
103 this.forEncryption = forEncryption;
104 this.privParam = privParam;
105 this.pubParam = pubParam;
106 this.v = new byte[0];
107
108 extractParams(params);
109 }
110
111
112 /**
113 * Initialise the encryptor.
114 *
115 * @param publicKey the recipient's/sender's public key parameters
116 * @param params encoding and derivation parameters, may be wrapped to include an iv for an underlying block cipher.
117 * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use.
118 */
119 public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator)
120 {
121 this.forEncryption = true;
122 this.pubParam = publicKey;
123 this.keyPairGenerator = ephemeralKeyPairGenerator;
124
125 extractParams(params);
126 }
127
128 /**
129 * Initialise the encryptor.
130 *
131 * @param privateKey the recipient's private key.
132 * @param params encoding and derivation parameters, may be wrapped to include an iv for an underlying block cipher.
133 * @param publicKeyParser the parser for reading the ephemeral public key.
134 */
135 public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser)
136 {
137 this.forEncryption = false;
138 this.privParam = privateKey;
139 this.keyParser = publicKeyParser;
140
141 extractParams(params);
142 }
143
144 private void extractParams(CipherParameters params)
145 {
146 if (params instanceof ParametersWithIV)
147 {
148 this.iv = ((ParametersWithIV)params).getIV();
149 this.param = (IESParameters)((ParametersWithIV)params).getParameters();
150 }
151 else
152 {
153 this.iv = null;
154 this.param = (IESParameters)params;
155 }
156 }
157
158 public BufferedBlockCipher getCipher()
159 {
160 return cipher;
161 }
162
163 public Mac getMac()
164 {
165 return mac;
166 }
167
168 private byte[] encryptBlock(
169 byte[] in,
170 int inOff,
171 int inLen,
172 byte[] macData)
173 throws InvalidCipherTextException
174 {
175 byte[] c = null;
176 byte[] k = null;
177 byte[] k1 = null;
178 byte[] k2 = null;
179
180 int len;
181
182 if (cipher == null)
183 {
184 // Streaming mode.
185 k1 = new byte[inLen];
186 k2 = new byte[param.getMacKeySize() / 8];
187 k = new byte[k1.length + k2.length];
188
189 kdf.generateBytes(k, 0, k.length);
190
191 // if (v.length != 0)
192 // {
193 // System.arraycopy(K, 0, K2, 0, K2.length);
194 // System.arraycopy(K, K2.length, K1, 0, K1.length);
195 // }
196 // else
197 {
198 System.arraycopy(k, 0, k1, 0, k1.length);
199 System.arraycopy(k, inLen, k2, 0, k2.length);
200 }
201
202 c = new byte[inLen];
203
204 for (int i = 0; i != inLen; i++)
205 {
206 c[i] = (byte)(in[inOff + i] ^ k1[i]);
207 }
208 len = inLen;
209 }
210 else
211 {
212 // Block cipher mode.
213 k1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8];
214 k2 = new byte[param.getMacKeySize() / 8];
215 k = new byte[k1.length + k2.length];
216
217 kdf.generateBytes(k, 0, k.length);
218 System.arraycopy(k, 0, k1, 0, k1.length);
219 System.arraycopy(k, k1.length, k2, 0, k2.length);
220
221 // If iv provided use it to initialise the cipher
222 if (iv != null)
223 {
224 cipher.init(true, new ParametersWithIV(new KeyParameter(k1), iv));
225 }
226 else
227 {
228 cipher.init(true, new KeyParameter(k1));
229 }
230
231 c = new byte[cipher.getOutputSize(inLen)];
232 len = cipher.processBytes(in, inOff, inLen, c, 0);
233 len += cipher.doFinal(c, len);
234 }
235
236
237 // Convert the length of the encoding vector into a byte array.
238 byte[] p2 = param.getEncodingV();
239
240 // Apply the MAC.
241 byte[] t = new byte[mac.getMacSize()];
242
243 byte[] k2A;
244 if (hashK2) {
245 k2A = new byte[hash.getDigestSize()];
246 hash.reset();
247 hash.update(k2, 0, k2.length);
248 hash.doFinal(k2A, 0);
249 } else {
250 k2A = k2;
251 }
252 mac.init(new KeyParameter(k2A));
253 mac.update(iv, 0, iv.length);
254 mac.update(c, 0, c.length);
255 if (p2 != null)
256 {
257 mac.update(p2, 0, p2.length);
258 }
259 if (v.length != 0 && p2 != null) {
260 byte[] l2 = new byte[4];
261 Pack.intToBigEndian(p2.length * 8, l2, 0);
262 mac.update(l2, 0, l2.length);
263 }
264
265 if (macData != null) {
266 mac.update(macData, 0, macData.length);
267 }
268
269 mac.doFinal(t, 0);
270
271 // Output the triple (v,C,T).
272 byte[] output = new byte[v.length + len + t.length];
273 System.arraycopy(v, 0, output, 0, v.length);
274 System.arraycopy(c, 0, output, v.length, len);
275 System.arraycopy(t, 0, output, v.length + len, t.length);
276 return output;
277 }
278
279 private byte[] decryptBlock(
280 byte[] inEnc,
281 int inOff,
282 int inLen,
283 byte[] macData)
284 throws InvalidCipherTextException
285 {
286 byte[] m = null;
287 byte[] k = null;
288 byte[] k1 = null;
289 byte[] k2 = null;
290
291 int len;
292
293 // Ensure that the length of the input is greater than the MAC in bytes
294 if (inLen <= (param.getMacKeySize() / 8))
295 {
296 throw new InvalidCipherTextException("Length of input must be greater than the MAC");
297 }
298
299 if (cipher == null)
300 {
301 // Streaming mode.
302 k1 = new byte[inLen - v.length - mac.getMacSize()];
303 k2 = new byte[param.getMacKeySize() / 8];
304 k = new byte[k1.length + k2.length];
305
306 kdf.generateBytes(k, 0, k.length);
307
308 // if (v.length != 0)
309 // {
310 // System.arraycopy(K, 0, K2, 0, K2.length);
311 // System.arraycopy(K, K2.length, K1, 0, K1.length);
312 // }
313 // else
314 {
315 System.arraycopy(k, 0, k1, 0, k1.length);
316 System.arraycopy(k, k1.length, k2, 0, k2.length);
317 }
318
319 m = new byte[k1.length];
320
321 for (int i = 0; i != k1.length; i++)
322 {
323 m[i] = (byte)(inEnc[inOff + v.length + i] ^ k1[i]);
324 }
325
326 len = k1.length;
327 }
328 else
329 {
330 // Block cipher mode.
331 k1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8];
332 k2 = new byte[param.getMacKeySize() / 8];
333 k = new byte[k1.length + k2.length];
334
335 kdf.generateBytes(k, 0, k.length);
336 System.arraycopy(k, 0, k1, 0, k1.length);
337 System.arraycopy(k, k1.length, k2, 0, k2.length);
338
339 // If iv provide use it to initialize the cipher
340 if (iv != null)
341 {
342 cipher.init(false, new ParametersWithIV(new KeyParameter(k1), iv));
343 }
344 else
345 {
346 cipher.init(false, new KeyParameter(k1));
347 }
348
349 m = new byte[cipher.getOutputSize(inLen - v.length - mac.getMacSize())];
350 len = cipher.processBytes(inEnc, inOff + v.length, inLen - v.length - mac.getMacSize(), m, 0);
351 len += cipher.doFinal(m, len);
352 }
353
354
355 // Convert the length of the encoding vector into a byte array.
356 byte[] p2 = param.getEncodingV();
357
358 // Verify the MAC.
359 int end = inOff + inLen;
360 byte[] t1 = Arrays.copyOfRange(inEnc, end - mac.getMacSize(), end);
361
362 byte[] t2 = new byte[t1.length];
363 byte[] k2A;
364 if (hashK2) {
365 k2A = new byte[hash.getDigestSize()];
366 hash.reset();
367 hash.update(k2, 0, k2.length);
368 hash.doFinal(k2A, 0);
369 } else {
370 k2A = k2;
371 }
372 mac.init(new KeyParameter(k2A));
373 mac.update(iv, 0, iv.length);
374 mac.update(inEnc, inOff + v.length, inLen - v.length - t2.length);
375
376 if (p2 != null)
377 {
378 mac.update(p2, 0, p2.length);
379 }
380
381 if (v.length != 0 && p2 != null) {
382 byte[] l2 = new byte[4];
383 Pack.intToBigEndian(p2.length * 8, l2, 0);
384 mac.update(l2, 0, l2.length);
385 }
386
387 if (macData != null) {
388 mac.update(macData, 0, macData.length);
389 }
390
391 mac.doFinal(t2, 0);
392
393 if (!Arrays.constantTimeAreEqual(t1, t2))
394 {
395 throw new InvalidCipherTextException("Invalid MAC.");
396 }
397
398
399 // Output the message.
400 return Arrays.copyOfRange(m, 0, len);
401 }
402
403 public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
404 return processBlock(in, inOff, inLen, null);
405 }
406
407 public byte[] processBlock(
408 byte[] in,
409 int inOff,
410 int inLen,
411 byte[] macData)
412 throws InvalidCipherTextException
413 {
414 if (forEncryption)
415 {
416 if (keyPairGenerator != null)
417 {
418 EphemeralKeyPair ephKeyPair = keyPairGenerator.generate();
419
420 this.privParam = ephKeyPair.getKeyPair().getPrivate();
421 this.v = ephKeyPair.getEncodedPublicKey();
422 }
423 }
424 else
425 {
426 if (keyParser != null)
427 {
428 ByteArrayInputStream bIn = new ByteArrayInputStream(in, inOff, inLen);
429
430 try
431 {
432 this.pubParam = keyParser.readKey(bIn);
433 }
434 catch (IOException e)
435 {
436 throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e);
437 }
438
439 int encLength = (inLen - bIn.available());
440 this.v = Arrays.copyOfRange(in, inOff, inOff + encLength);
441 }
442 }
443
444 // Compute the common value and convert to byte array.
445 agree.init(privParam);
446 BigInteger z = agree.calculateAgreement(pubParam);
447 byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z);
448
449 // Create input to KDF.
450 byte[] vz;
451 // if (v.length != 0)
452 // {
453 // VZ = new byte[v.length + Z.length];
454 // System.arraycopy(v, 0, VZ, 0, v.length);
455 // System.arraycopy(Z, 0, VZ, v.length, Z.length);
456 // }
457 // else
458 {
459 vz = Z;
460 }
461
462 // Initialise the KDF.
463 DerivationParameters kdfParam;
464 if (kdf instanceof MGF1BytesGeneratorExt) {
465 kdfParam = new MGFParameters(vz);
466 } else {
467 kdfParam = new KDFParameters(vz, param.getDerivationV());
468 }
469 kdf.init(kdfParam);
470
471 return forEncryption
472 ? encryptBlock(in, inOff, inLen, macData)
473 : decryptBlock(in, inOff, inLen, macData);
474 }
475 }