1 package org.bouncycastle.crypto.engines; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.IOException; 5 import java.math.BigInteger; 6 7 import org.bouncycastle.crypto.BasicAgreement; 8 import org.bouncycastle.crypto.BufferedBlockCipher; 9 import org.bouncycastle.crypto.CipherParameters; 10 import org.bouncycastle.crypto.DerivationFunction; 11 import org.bouncycastle.crypto.EphemeralKeyPair; 12 import org.bouncycastle.crypto.InvalidCipherTextException; 13 import org.bouncycastle.crypto.KeyParser; 14 import org.bouncycastle.crypto.Mac; 15 import org.bouncycastle.crypto.generators.EphemeralKeyPairGenerator; 16 import org.bouncycastle.crypto.params.AsymmetricKeyParameter; 17 import org.bouncycastle.crypto.params.IESParameters; 18 import org.bouncycastle.crypto.params.IESWithCipherParameters; 19 import org.bouncycastle.crypto.params.KDFParameters; 20 import org.bouncycastle.crypto.params.KeyParameter; 21 import org.bouncycastle.crypto.params.ParametersWithIV; 22 import org.bouncycastle.util.Arrays; 23 import org.bouncycastle.util.BigIntegers; 24 import org.bouncycastle.util.Pack; 25 26 /** 27 * Support class for constructing integrated encryption ciphers 28 * for doing basic message exchanges on top of key agreement ciphers. 29 * Follows the description given in IEEE Std 1363a. 30 */ 31 public class IESEngine 32 { 33 BasicAgreement agree; 34 DerivationFunction kdf; 35 Mac mac; 36 BufferedBlockCipher cipher; 37 byte[] macBuf; 38 39 boolean forEncryption; 40 CipherParameters privParam, pubParam; 41 IESParameters param; 42 43 byte[] V; 44 private EphemeralKeyPairGenerator keyPairGenerator; 45 private KeyParser keyParser; 46 private byte[] IV; 47 48 /** 49 * Set up for use with stream mode, where the key derivation function 50 * is used to provide a stream of bytes to xor with the message. 51 * 52 * @param agree the key agreement used as the basis for the encryption 53 * @param kdf the key derivation function used for byte generation 54 * @param mac the message authentication code generator for the message 55 */ IESEngine( BasicAgreement agree, DerivationFunction kdf, Mac mac)56 public IESEngine( 57 BasicAgreement agree, 58 DerivationFunction kdf, 59 Mac mac) 60 { 61 this.agree = agree; 62 this.kdf = kdf; 63 this.mac = mac; 64 this.macBuf = new byte[mac.getMacSize()]; 65 this.cipher = null; 66 } 67 68 69 /** 70 * Set up for use in conjunction with a block cipher to handle the 71 * message. It is <b>strongly</b> recommended that the cipher is not in ECB mode. 72 * 73 * @param agree the key agreement used as the basis for the encryption 74 * @param kdf the key derivation function used for byte generation 75 * @param mac the message authentication code generator for the message 76 * @param cipher the cipher to used for encrypting the message 77 */ IESEngine( BasicAgreement agree, DerivationFunction kdf, Mac mac, BufferedBlockCipher cipher)78 public IESEngine( 79 BasicAgreement agree, 80 DerivationFunction kdf, 81 Mac mac, 82 BufferedBlockCipher cipher) 83 { 84 this.agree = agree; 85 this.kdf = kdf; 86 this.mac = mac; 87 this.macBuf = new byte[mac.getMacSize()]; 88 this.cipher = cipher; 89 } 90 91 /** 92 * Initialise the encryptor. 93 * 94 * @param forEncryption whether or not this is encryption/decryption. 95 * @param privParam our private key parameters 96 * @param pubParam the recipient's/sender's public key parameters 97 * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher. 98 */ init( boolean forEncryption, CipherParameters privParam, CipherParameters pubParam, CipherParameters params)99 public void init( 100 boolean forEncryption, 101 CipherParameters privParam, 102 CipherParameters pubParam, 103 CipherParameters params) 104 { 105 this.forEncryption = forEncryption; 106 this.privParam = privParam; 107 this.pubParam = pubParam; 108 this.V = new byte[0]; 109 110 extractParams(params); 111 } 112 113 /** 114 * Initialise the decryptor. 115 * 116 * @param publicKey the recipient's/sender's public key parameters 117 * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher. 118 * @param ephemeralKeyPairGenerator the ephemeral key pair generator to use. 119 */ init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator)120 public void init(AsymmetricKeyParameter publicKey, CipherParameters params, EphemeralKeyPairGenerator ephemeralKeyPairGenerator) 121 { 122 this.forEncryption = true; 123 this.pubParam = publicKey; 124 this.keyPairGenerator = ephemeralKeyPairGenerator; 125 126 extractParams(params); 127 } 128 129 /** 130 * Initialise the encryptor. 131 * 132 * @param privateKey the recipient's private key. 133 * @param params encoding and derivation parameters, may be wrapped to include an IV for an underlying block cipher. 134 * @param publicKeyParser the parser for reading the ephemeral public key. 135 */ init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser)136 public void init(AsymmetricKeyParameter privateKey, CipherParameters params, KeyParser publicKeyParser) 137 { 138 this.forEncryption = false; 139 this.privParam = privateKey; 140 this.keyParser = publicKeyParser; 141 142 extractParams(params); 143 } 144 extractParams(CipherParameters params)145 private void extractParams(CipherParameters params) 146 { 147 if (params instanceof ParametersWithIV) 148 { 149 this.IV = ((ParametersWithIV)params).getIV(); 150 this.param = (IESParameters)((ParametersWithIV)params).getParameters(); 151 } 152 else 153 { 154 this.IV = null; 155 this.param = (IESParameters)params; 156 } 157 } 158 getCipher()159 public BufferedBlockCipher getCipher() 160 { 161 return cipher; 162 } 163 getMac()164 public Mac getMac() 165 { 166 return mac; 167 } 168 encryptBlock( byte[] in, int inOff, int inLen)169 private byte[] encryptBlock( 170 byte[] in, 171 int inOff, 172 int inLen) 173 throws InvalidCipherTextException 174 { 175 byte[] C = null, K = null, K1 = null, K2 = null; 176 int len; 177 178 if (cipher == null) 179 { 180 // Streaming mode. 181 K1 = new byte[inLen]; 182 K2 = new byte[param.getMacKeySize() / 8]; 183 K = new byte[K1.length + K2.length]; 184 185 kdf.generateBytes(K, 0, K.length); 186 187 if (V.length != 0) 188 { 189 System.arraycopy(K, 0, K2, 0, K2.length); 190 System.arraycopy(K, K2.length, K1, 0, K1.length); 191 } 192 else 193 { 194 System.arraycopy(K, 0, K1, 0, K1.length); 195 System.arraycopy(K, inLen, K2, 0, K2.length); 196 } 197 198 C = new byte[inLen]; 199 200 for (int i = 0; i != inLen; i++) 201 { 202 C[i] = (byte)(in[inOff + i] ^ K1[i]); 203 } 204 len = inLen; 205 } 206 else 207 { 208 // Block cipher mode. 209 K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8]; 210 K2 = new byte[param.getMacKeySize() / 8]; 211 K = new byte[K1.length + K2.length]; 212 213 kdf.generateBytes(K, 0, K.length); 214 System.arraycopy(K, 0, K1, 0, K1.length); 215 System.arraycopy(K, K1.length, K2, 0, K2.length); 216 217 // If iv provided use it to initialise the cipher 218 if (IV != null) 219 { 220 cipher.init(true, new ParametersWithIV(new KeyParameter(K1), IV)); 221 } 222 else 223 { 224 cipher.init(true, new KeyParameter(K1)); 225 } 226 227 C = new byte[cipher.getOutputSize(inLen)]; 228 len = cipher.processBytes(in, inOff, inLen, C, 0); 229 len += cipher.doFinal(C, len); 230 } 231 232 233 // Convert the length of the encoding vector into a byte array. 234 byte[] P2 = param.getEncodingV(); 235 byte[] L2 = null; 236 if (V.length != 0) 237 { 238 L2 = getLengthTag(P2); 239 } 240 241 242 // Apply the MAC. 243 byte[] T = new byte[mac.getMacSize()]; 244 245 mac.init(new KeyParameter(K2)); 246 mac.update(C, 0, C.length); 247 if (P2 != null) 248 { 249 mac.update(P2, 0, P2.length); 250 } 251 if (V.length != 0) 252 { 253 mac.update(L2, 0, L2.length); 254 } 255 mac.doFinal(T, 0); 256 257 258 // Output the triple (V,C,T). 259 byte[] Output = new byte[V.length + len + T.length]; 260 System.arraycopy(V, 0, Output, 0, V.length); 261 System.arraycopy(C, 0, Output, V.length, len); 262 System.arraycopy(T, 0, Output, V.length + len, T.length); 263 return Output; 264 } 265 decryptBlock( byte[] in_enc, int inOff, int inLen)266 private byte[] decryptBlock( 267 byte[] in_enc, 268 int inOff, 269 int inLen) 270 throws InvalidCipherTextException 271 { 272 byte[] M, K, K1, K2; 273 int len = 0; 274 275 // Ensure that the length of the input is greater than the MAC in bytes 276 if (inLen < V.length + mac.getMacSize()) 277 { 278 throw new InvalidCipherTextException("Length of input must be greater than the MAC and V combined"); 279 } 280 281 // note order is important: set up keys, do simple encryptions, check mac, do final encryption. 282 if (cipher == null) 283 { 284 // Streaming mode. 285 K1 = new byte[inLen - V.length - mac.getMacSize()]; 286 K2 = new byte[param.getMacKeySize() / 8]; 287 K = new byte[K1.length + K2.length]; 288 289 kdf.generateBytes(K, 0, K.length); 290 291 if (V.length != 0) 292 { 293 System.arraycopy(K, 0, K2, 0, K2.length); 294 System.arraycopy(K, K2.length, K1, 0, K1.length); 295 } 296 else 297 { 298 System.arraycopy(K, 0, K1, 0, K1.length); 299 System.arraycopy(K, K1.length, K2, 0, K2.length); 300 } 301 302 // process the message 303 M = new byte[K1.length]; 304 305 for (int i = 0; i != K1.length; i++) 306 { 307 M[i] = (byte)(in_enc[inOff + V.length + i] ^ K1[i]); 308 } 309 } 310 else 311 { 312 // Block cipher mode. 313 K1 = new byte[((IESWithCipherParameters)param).getCipherKeySize() / 8]; 314 K2 = new byte[param.getMacKeySize() / 8]; 315 K = new byte[K1.length + K2.length]; 316 317 kdf.generateBytes(K, 0, K.length); 318 System.arraycopy(K, 0, K1, 0, K1.length); 319 System.arraycopy(K, K1.length, K2, 0, K2.length); 320 321 CipherParameters cp = new KeyParameter(K1); 322 323 // If IV provide use it to initialize the cipher 324 if (IV != null) 325 { 326 cp = new ParametersWithIV(cp, IV); 327 } 328 329 cipher.init(false, cp); 330 331 M = new byte[cipher.getOutputSize(inLen - V.length - mac.getMacSize())]; 332 333 // do initial processing 334 len = cipher.processBytes(in_enc, inOff + V.length, inLen - V.length - mac.getMacSize(), M, 0); 335 } 336 337 // Convert the length of the encoding vector into a byte array. 338 byte[] P2 = param.getEncodingV(); 339 byte[] L2 = null; 340 if (V.length != 0) 341 { 342 L2 = getLengthTag(P2); 343 } 344 345 // Verify the MAC. 346 int end = inOff + inLen; 347 byte[] T1 = Arrays.copyOfRange(in_enc, end - mac.getMacSize(), end); 348 349 byte[] T2 = new byte[T1.length]; 350 mac.init(new KeyParameter(K2)); 351 mac.update(in_enc, inOff + V.length, inLen - V.length - T2.length); 352 353 if (P2 != null) 354 { 355 mac.update(P2, 0, P2.length); 356 } 357 if (V.length != 0) 358 { 359 mac.update(L2, 0, L2.length); 360 } 361 mac.doFinal(T2, 0); 362 363 if (!Arrays.constantTimeAreEqual(T1, T2)) 364 { 365 throw new InvalidCipherTextException("invalid MAC"); 366 } 367 368 if (cipher == null) 369 { 370 return M; 371 } 372 else 373 { 374 len += cipher.doFinal(M, len); 375 376 return Arrays.copyOfRange(M, 0, len); 377 } 378 } 379 380 processBlock( byte[] in, int inOff, int inLen)381 public byte[] processBlock( 382 byte[] in, 383 int inOff, 384 int inLen) 385 throws InvalidCipherTextException 386 { 387 if (forEncryption) 388 { 389 if (keyPairGenerator != null) 390 { 391 EphemeralKeyPair ephKeyPair = keyPairGenerator.generate(); 392 393 this.privParam = ephKeyPair.getKeyPair().getPrivate(); 394 this.V = ephKeyPair.getEncodedPublicKey(); 395 } 396 } 397 else 398 { 399 if (keyParser != null) 400 { 401 ByteArrayInputStream bIn = new ByteArrayInputStream(in, inOff, inLen); 402 403 try 404 { 405 this.pubParam = keyParser.readKey(bIn); 406 } 407 catch (IOException e) 408 { 409 throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e); 410 } 411 catch (IllegalArgumentException e) 412 { 413 throw new InvalidCipherTextException("unable to recover ephemeral public key: " + e.getMessage(), e); 414 } 415 416 int encLength = (inLen - bIn.available()); 417 this.V = Arrays.copyOfRange(in, inOff, inOff + encLength); 418 } 419 } 420 421 // Compute the common value and convert to byte array. 422 agree.init(privParam); 423 BigInteger z = agree.calculateAgreement(pubParam); 424 byte[] Z = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), z); 425 426 // Create input to KDF. 427 if (V.length != 0) 428 { 429 byte[] VZ = Arrays.concatenate(V, Z); 430 Arrays.fill(Z, (byte)0); 431 Z = VZ; 432 } 433 434 try 435 { 436 // Initialise the KDF. 437 KDFParameters kdfParam = new KDFParameters(Z, param.getDerivationV()); 438 kdf.init(kdfParam); 439 440 return forEncryption 441 ? encryptBlock(in, inOff, inLen) 442 : decryptBlock(in, inOff, inLen); 443 } 444 finally 445 { 446 Arrays.fill(Z, (byte)0); 447 } 448 } 449 450 // as described in Shroup's paper and P1363a getLengthTag(byte[] p2)451 protected byte[] getLengthTag(byte[] p2) 452 { 453 byte[] L2 = new byte[8]; 454 if (p2 != null) 455 { 456 Pack.longToBigEndian(p2.length * 8L, L2, 0); 457 } 458 return L2; 459 } 460 } 461