| package org.bouncycastle.crypto.engines; |
| |
| import java.math.BigInteger; |
| |
| import org.bouncycastle.crypto.BasicAgreement; |
| import org.bouncycastle.crypto.BufferedBlockCipher; |
| import org.bouncycastle.crypto.CipherParameters; |
| import org.bouncycastle.crypto.DerivationFunction; |
| import org.bouncycastle.crypto.InvalidCipherTextException; |
| import org.bouncycastle.crypto.Mac; |
| import org.bouncycastle.crypto.params.IESParameters; |
| import org.bouncycastle.crypto.params.IESWithCipherParameters; |
| import org.bouncycastle.crypto.params.KDFParameters; |
| import org.bouncycastle.crypto.params.KeyParameter; |
| |
| /** |
| * support class for constructing intergrated encryption ciphers |
| * for doing basic message exchanges on top of key agreement ciphers |
| */ |
| public class IESEngine |
| { |
| BasicAgreement agree; |
| DerivationFunction kdf; |
| Mac mac; |
| BufferedBlockCipher cipher; |
| byte[] macBuf; |
| |
| boolean forEncryption; |
| CipherParameters privParam, pubParam; |
| IESParameters param; |
| |
| /** |
| * set up for use with stream mode, where the key derivation function |
| * is used to provide a stream of bytes to xor with the message. |
| * |
| * @param agree the key agreement used as the basis for the encryption |
| * @param kdf the key derivation function used for byte generation |
| * @param mac the message authentication code generator for the message |
| */ |
| public IESEngine( |
| BasicAgreement agree, |
| DerivationFunction kdf, |
| Mac mac) |
| { |
| this.agree = agree; |
| this.kdf = kdf; |
| this.mac = mac; |
| this.macBuf = new byte[mac.getMacSize()]; |
| this.cipher = null; |
| } |
| |
| /** |
| * set up for use in conjunction with a block cipher to handle the |
| * message. |
| * |
| * @param agree the key agreement used as the basis for the encryption |
| * @param kdf the key derivation function used for byte generation |
| * @param mac the message authentication code generator for the message |
| * @param cipher the cipher to used for encrypting the message |
| */ |
| public IESEngine( |
| BasicAgreement agree, |
| DerivationFunction kdf, |
| Mac mac, |
| BufferedBlockCipher cipher) |
| { |
| this.agree = agree; |
| this.kdf = kdf; |
| this.mac = mac; |
| this.macBuf = new byte[mac.getMacSize()]; |
| this.cipher = cipher; |
| } |
| |
| /** |
| * Initialise the encryptor. |
| * |
| * @param forEncryption whether or not this is encryption/decryption. |
| * @param privParam our private key parameters |
| * @param pubParam the recipient's/sender's public key parameters |
| * @param param encoding and derivation parameters. |
| */ |
| public void init( |
| boolean forEncryption, |
| CipherParameters privParam, |
| CipherParameters pubParam, |
| CipherParameters param) |
| { |
| this.forEncryption = forEncryption; |
| this.privParam = privParam; |
| this.pubParam = pubParam; |
| this.param = (IESParameters)param; |
| } |
| |
| private byte[] decryptBlock( |
| byte[] in_enc, |
| int inOff, |
| int inLen, |
| byte[] z) |
| throws InvalidCipherTextException |
| { |
| byte[] M = null; |
| KeyParameter macKey = null; |
| KDFParameters kParam = new KDFParameters(z, param.getDerivationV()); |
| int macKeySize = param.getMacKeySize(); |
| |
| kdf.init(kParam); |
| |
| inLen -= mac.getMacSize(); |
| |
| if (cipher == null) // stream mode |
| { |
| byte[] buf = new byte[inLen + (macKeySize / 8)]; |
| |
| M = new byte[inLen]; |
| |
| kdf.generateBytes(buf, 0, buf.length); |
| |
| for (int i = 0; i != inLen; i++) |
| { |
| M[i] = (byte)(in_enc[inOff + i] ^ buf[i]); |
| } |
| |
| macKey = new KeyParameter(buf, inLen, (macKeySize / 8)); |
| } |
| else |
| { |
| int cipherKeySize = ((IESWithCipherParameters)param).getCipherKeySize(); |
| byte[] buf = new byte[(cipherKeySize / 8) + (macKeySize / 8)]; |
| |
| cipher.init(false, new KeyParameter(buf, 0, (cipherKeySize / 8))); |
| |
| byte[] tmp = new byte[cipher.getOutputSize(inLen)]; |
| |
| int off = cipher.processBytes(in_enc, inOff, inLen, tmp, 0); |
| |
| off += cipher.doFinal(tmp, off); |
| |
| M = new byte[off]; |
| |
| System.arraycopy(tmp, 0, M, 0, off); |
| |
| macKey = new KeyParameter(buf, (cipherKeySize / 8), (macKeySize / 8)); |
| } |
| |
| byte[] macIV = param.getEncodingV(); |
| |
| mac.init(macKey); |
| mac.update(in_enc, inOff, inLen); |
| mac.update(macIV, 0, macIV.length); |
| mac.doFinal(macBuf, 0); |
| |
| inOff += inLen; |
| |
| for (int t = 0; t < macBuf.length; t++) |
| { |
| if (macBuf[t] != in_enc[inOff + t]) |
| { |
| throw (new InvalidCipherTextException("Mac codes failed to equal.")); |
| } |
| } |
| |
| return M; |
| } |
| |
| private byte[] encryptBlock( |
| byte[] in, |
| int inOff, |
| int inLen, |
| byte[] z) |
| throws InvalidCipherTextException |
| { |
| byte[] C = null; |
| KeyParameter macKey = null; |
| KDFParameters kParam = new KDFParameters(z, param.getDerivationV()); |
| int c_text_length = 0; |
| int macKeySize = param.getMacKeySize(); |
| |
| kdf.init(kParam); |
| |
| if (cipher == null) // stream mode |
| { |
| byte[] buf = new byte[inLen + (macKeySize / 8)]; |
| |
| C = new byte[inLen + mac.getMacSize()]; |
| c_text_length = inLen; |
| |
| kdf.generateBytes(buf, 0, buf.length); |
| |
| for (int i = 0; i != inLen; i++) |
| { |
| C[i] = (byte)(in[inOff + i] ^ buf[i]); |
| } |
| |
| macKey = new KeyParameter(buf, inLen, (macKeySize / 8)); |
| } |
| else |
| { |
| int cipherKeySize = ((IESWithCipherParameters)param).getCipherKeySize(); |
| byte[] buf = new byte[(cipherKeySize / 8) + (macKeySize / 8)]; |
| |
| cipher.init(true, new KeyParameter(buf, 0, (cipherKeySize / 8))); |
| |
| c_text_length = cipher.getOutputSize(inLen); |
| |
| C = new byte[c_text_length + mac.getMacSize()]; |
| |
| int off = cipher.processBytes(in, inOff, inLen, C, 0); |
| |
| cipher.doFinal(C, off); |
| |
| macKey = new KeyParameter(buf, (cipherKeySize / 8), (macKeySize / 8)); |
| } |
| |
| byte[] macIV = param.getEncodingV(); |
| |
| mac.init(macKey); |
| mac.update(C, 0, c_text_length); |
| mac.update(macIV, 0, macIV.length); |
| // |
| // return the message and it's MAC |
| // |
| mac.doFinal(C, c_text_length); |
| return C; |
| } |
| |
| public byte[] processBlock( |
| byte[] in, |
| int inOff, |
| int inLen) |
| throws InvalidCipherTextException |
| { |
| agree.init(privParam); |
| |
| BigInteger z = agree.calculateAgreement(pubParam); |
| |
| if (forEncryption) |
| { |
| return encryptBlock(in, inOff, inLen, z.toByteArray()); |
| } |
| else |
| { |
| return decryptBlock(in, inOff, inLen, z.toByteArray()); |
| } |
| } |
| } |