1package org.bouncycastle.crypto.engines; 2 3import java.math.BigInteger; 4 5import org.bouncycastle.crypto.BasicAgreement; 6import org.bouncycastle.crypto.BufferedBlockCipher; 7import org.bouncycastle.crypto.CipherParameters; 8import org.bouncycastle.crypto.DerivationFunction; 9import org.bouncycastle.crypto.InvalidCipherTextException; 10import org.bouncycastle.crypto.Mac; 11import org.bouncycastle.crypto.params.IESParameters; 12import org.bouncycastle.crypto.params.IESWithCipherParameters; 13import org.bouncycastle.crypto.params.KDFParameters; 14import org.bouncycastle.crypto.params.KeyParameter; 15 16/** 17 * support class for constructing intergrated encryption ciphers 18 * for doing basic message exchanges on top of key agreement ciphers 19 */ 20public class IESEngine 21{ 22 BasicAgreement agree; 23 DerivationFunction kdf; 24 Mac mac; 25 BufferedBlockCipher cipher; 26 byte[] macBuf; 27 28 boolean forEncryption; 29 CipherParameters privParam, pubParam; 30 IESParameters param; 31 32 /** 33 * set up for use with stream mode, where the key derivation function 34 * is used to provide a stream of bytes to xor with the message. 35 * 36 * @param agree the key agreement used as the basis for the encryption 37 * @param kdf the key derivation function used for byte generation 38 * @param mac the message authentication code generator for the message 39 */ 40 public IESEngine( 41 BasicAgreement agree, 42 DerivationFunction kdf, 43 Mac mac) 44 { 45 this.agree = agree; 46 this.kdf = kdf; 47 this.mac = mac; 48 this.macBuf = new byte[mac.getMacSize()]; 49 this.cipher = null; 50 } 51 52 /** 53 * set up for use in conjunction with a block cipher to handle the 54 * message. 55 * 56 * @param agree the key agreement used as the basis for the encryption 57 * @param kdf the key derivation function used for byte generation 58 * @param mac the message authentication code generator for the message 59 * @param cipher the cipher to used for encrypting the message 60 */ 61 public IESEngine( 62 BasicAgreement agree, 63 DerivationFunction kdf, 64 Mac mac, 65 BufferedBlockCipher cipher) 66 { 67 this.agree = agree; 68 this.kdf = kdf; 69 this.mac = mac; 70 this.macBuf = new byte[mac.getMacSize()]; 71 this.cipher = cipher; 72 } 73 74 /** 75 * Initialise the encryptor. 76 * 77 * @param forEncryption whether or not this is encryption/decryption. 78 * @param privParam our private key parameters 79 * @param pubParam the recipient's/sender's public key parameters 80 * @param param encoding and derivation parameters. 81 */ 82 public void init( 83 boolean forEncryption, 84 CipherParameters privParam, 85 CipherParameters pubParam, 86 CipherParameters param) 87 { 88 this.forEncryption = forEncryption; 89 this.privParam = privParam; 90 this.pubParam = pubParam; 91 this.param = (IESParameters)param; 92 } 93 94 private byte[] decryptBlock( 95 byte[] in_enc, 96 int inOff, 97 int inLen, 98 byte[] z) 99 throws InvalidCipherTextException 100 { 101 byte[] M = null; 102 KeyParameter macKey = null; 103 KDFParameters kParam = new KDFParameters(z, param.getDerivationV()); 104 int macKeySize = param.getMacKeySize(); 105 106 kdf.init(kParam); 107 108 inLen -= mac.getMacSize(); 109 110 if (cipher == null) // stream mode 111 { 112 byte[] buf = new byte[inLen + (macKeySize / 8)]; 113 114 M = new byte[inLen]; 115 116 kdf.generateBytes(buf, 0, buf.length); 117 118 for (int i = 0; i != inLen; i++) 119 { 120 M[i] = (byte)(in_enc[inOff + i] ^ buf[i]); 121 } 122 123 macKey = new KeyParameter(buf, inLen, (macKeySize / 8)); 124 } 125 else 126 { 127 int cipherKeySize = ((IESWithCipherParameters)param).getCipherKeySize(); 128 byte[] buf = new byte[(cipherKeySize / 8) + (macKeySize / 8)]; 129 130 cipher.init(false, new KeyParameter(buf, 0, (cipherKeySize / 8))); 131 132 byte[] tmp = new byte[cipher.getOutputSize(inLen)]; 133 134 int off = cipher.processBytes(in_enc, inOff, inLen, tmp, 0); 135 136 off += cipher.doFinal(tmp, off); 137 138 M = new byte[off]; 139 140 System.arraycopy(tmp, 0, M, 0, off); 141 142 macKey = new KeyParameter(buf, (cipherKeySize / 8), (macKeySize / 8)); 143 } 144 145 byte[] macIV = param.getEncodingV(); 146 147 mac.init(macKey); 148 mac.update(in_enc, inOff, inLen); 149 mac.update(macIV, 0, macIV.length); 150 mac.doFinal(macBuf, 0); 151 152 inOff += inLen; 153 154 for (int t = 0; t < macBuf.length; t++) 155 { 156 if (macBuf[t] != in_enc[inOff + t]) 157 { 158 throw (new InvalidCipherTextException("Mac codes failed to equal.")); 159 } 160 } 161 162 return M; 163 } 164 165 private byte[] encryptBlock( 166 byte[] in, 167 int inOff, 168 int inLen, 169 byte[] z) 170 throws InvalidCipherTextException 171 { 172 byte[] C = null; 173 KeyParameter macKey = null; 174 KDFParameters kParam = new KDFParameters(z, param.getDerivationV()); 175 int c_text_length = 0; 176 int macKeySize = param.getMacKeySize(); 177 178 kdf.init(kParam); 179 180 if (cipher == null) // stream mode 181 { 182 byte[] buf = new byte[inLen + (macKeySize / 8)]; 183 184 C = new byte[inLen + mac.getMacSize()]; 185 c_text_length = inLen; 186 187 kdf.generateBytes(buf, 0, buf.length); 188 189 for (int i = 0; i != inLen; i++) 190 { 191 C[i] = (byte)(in[inOff + i] ^ buf[i]); 192 } 193 194 macKey = new KeyParameter(buf, inLen, (macKeySize / 8)); 195 } 196 else 197 { 198 int cipherKeySize = ((IESWithCipherParameters)param).getCipherKeySize(); 199 byte[] buf = new byte[(cipherKeySize / 8) + (macKeySize / 8)]; 200 201 cipher.init(true, new KeyParameter(buf, 0, (cipherKeySize / 8))); 202 203 c_text_length = cipher.getOutputSize(inLen); 204 205 C = new byte[c_text_length + mac.getMacSize()]; 206 207 int off = cipher.processBytes(in, inOff, inLen, C, 0); 208 209 cipher.doFinal(C, off); 210 211 macKey = new KeyParameter(buf, (cipherKeySize / 8), (macKeySize / 8)); 212 } 213 214 byte[] macIV = param.getEncodingV(); 215 216 mac.init(macKey); 217 mac.update(C, 0, c_text_length); 218 mac.update(macIV, 0, macIV.length); 219 // 220 // return the message and it's MAC 221 // 222 mac.doFinal(C, c_text_length); 223 return C; 224 } 225 226 public byte[] processBlock( 227 byte[] in, 228 int inOff, 229 int inLen) 230 throws InvalidCipherTextException 231 { 232 agree.init(privParam); 233 234 BigInteger z = agree.calculateAgreement(pubParam); 235 236 if (forEncryption) 237 { 238 return encryptBlock(in, inOff, inLen, z.toByteArray()); 239 } 240 else 241 { 242 return decryptBlock(in, inOff, inLen, z.toByteArray()); 243 } 244 } 245} 246