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 }