1 package org.bouncycastle.crypto.encodings; 2 3 import org.bouncycastle.crypto.AsymmetricBlockCipher; 4 import org.bouncycastle.crypto.CipherParameters; 5 import org.bouncycastle.crypto.Digest; 6 import org.bouncycastle.crypto.InvalidCipherTextException; 7 import org.bouncycastle.crypto.digests.SHA1Digest; 8 import org.bouncycastle.crypto.params.ParametersWithRandom; 9 10 import java.security.SecureRandom; 11 12 /** 13 * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2. 14 */ 15 public class OAEPEncoding 16 implements AsymmetricBlockCipher 17 { 18 private byte[] defHash; 19 private Digest hash; 20 private Digest mgf1Hash; 21 22 private AsymmetricBlockCipher engine; 23 private SecureRandom random; 24 private boolean forEncryption; 25 26 public OAEPEncoding( 27 AsymmetricBlockCipher cipher) 28 { 29 this(cipher, new SHA1Digest(), null); 30 } 31 32 public OAEPEncoding( 33 AsymmetricBlockCipher cipher, 34 Digest hash) 35 { 36 this(cipher, hash, null); 37 } 38 39 public OAEPEncoding( 40 AsymmetricBlockCipher cipher, 41 Digest hash, 42 byte[] encodingParams) 43 { 44 this(cipher, hash, hash, encodingParams); 45 } 46 47 public OAEPEncoding( 48 AsymmetricBlockCipher cipher, 49 Digest hash, 50 Digest mgf1Hash, 51 byte[] encodingParams) 52 { 53 this.engine = cipher; 54 this.hash = hash; 55 this.mgf1Hash = mgf1Hash; 56 this.defHash = new byte[hash.getDigestSize()]; 57 58 if (encodingParams != null) 59 { 60 hash.update(encodingParams, 0, encodingParams.length); 61 } 62 63 hash.doFinal(defHash, 0); 64 } 65 66 public AsymmetricBlockCipher getUnderlyingCipher() 67 { 68 return engine; 69 } 70 71 public void init( 72 boolean forEncryption, 73 CipherParameters param) 74 { 75 if (param instanceof ParametersWithRandom) 76 { 77 ParametersWithRandom rParam = (ParametersWithRandom)param; 78 79 this.random = rParam.getRandom(); 80 } 81 else 82 { 83 this.random = new SecureRandom(); 84 } 85 86 engine.init(forEncryption, param); 87 88 this.forEncryption = forEncryption; 89 } 90 91 public int getInputBlockSize() 92 { 93 int baseBlockSize = engine.getInputBlockSize(); 94 95 if (forEncryption) 96 { 97 return baseBlockSize - 1 - 2 * defHash.length; 98 } 99 else 100 { 101 return baseBlockSize; 102 } 103 } 104 105 public int getOutputBlockSize() 106 { 107 int baseBlockSize = engine.getOutputBlockSize(); 108 109 if (forEncryption) 110 { 111 return baseBlockSize; 112 } 113 else 114 { 115 return baseBlockSize - 1 - 2 * defHash.length; 116 } 117 } 118 119 public byte[] processBlock( 120 byte[] in, 121 int inOff, 122 int inLen) 123 throws InvalidCipherTextException 124 { 125 if (forEncryption) 126 { 127 return encodeBlock(in, inOff, inLen); 128 } 129 else 130 { 131 return decodeBlock(in, inOff, inLen); 132 } 133 } 134 135 public byte[] encodeBlock( 136 byte[] in, 137 int inOff, 138 int inLen) 139 throws InvalidCipherTextException 140 { 141 byte[] block = new byte[getInputBlockSize() + 1 + 2 * defHash.length]; 142 143 // 144 // copy in the message 145 // 146 System.arraycopy(in, inOff, block, block.length - inLen, inLen); 147 148 // 149 // add sentinel 150 // 151 block[block.length - inLen - 1] = 0x01; 152 153 // 154 // as the block is already zeroed - there's no need to add PS (the >= 0 pad of 0) 155 // 156 157 // 158 // add the hash of the encoding params. 159 // 160 System.arraycopy(defHash, 0, block, defHash.length, defHash.length); 161 162 // 163 // generate the seed. 164 // 165 byte[] seed = new byte[defHash.length]; 166 167 random.nextBytes(seed); 168 169 // 170 // mask the message block. 171 // 172 byte[] mask = maskGeneratorFunction1(seed, 0, seed.length, block.length - defHash.length); 173 174 for (int i = defHash.length; i != block.length; i++) 175 { 176 block[i] ^= mask[i - defHash.length]; 177 } 178 179 // 180 // add in the seed 181 // 182 System.arraycopy(seed, 0, block, 0, defHash.length); 183 184 // 185 // mask the seed. 186 // 187 mask = maskGeneratorFunction1( 188 block, defHash.length, block.length - defHash.length, defHash.length); 189 190 for (int i = 0; i != defHash.length; i++) 191 { 192 block[i] ^= mask[i]; 193 } 194 195 return engine.processBlock(block, 0, block.length); 196 } 197 198 /** 199 * @exception InvalidCipherTextException if the decrypted block turns out to 200 * be badly formatted. 201 */ 202 public byte[] decodeBlock( 203 byte[] in, 204 int inOff, 205 int inLen) 206 throws InvalidCipherTextException 207 { 208 byte[] data = engine.processBlock(in, inOff, inLen); 209 byte[] block; 210 211 // 212 // as we may have zeros in our leading bytes for the block we produced 213 // on encryption, we need to make sure our decrypted block comes back 214 // the same size. 215 // 216 if (data.length < engine.getOutputBlockSize()) 217 { 218 block = new byte[engine.getOutputBlockSize()]; 219 220 System.arraycopy(data, 0, block, block.length - data.length, data.length); 221 } 222 else 223 { 224 block = data; 225 } 226 227 if (block.length < (2 * defHash.length) + 1) 228 { 229 throw new InvalidCipherTextException("data too short"); 230 } 231 232 // 233 // unmask the seed. 234 // 235 byte[] mask = maskGeneratorFunction1( 236 block, defHash.length, block.length - defHash.length, defHash.length); 237 238 for (int i = 0; i != defHash.length; i++) 239 { 240 block[i] ^= mask[i]; 241 } 242 243 // 244 // unmask the message block. 245 // 246 mask = maskGeneratorFunction1(block, 0, defHash.length, block.length - defHash.length); 247 248 for (int i = defHash.length; i != block.length; i++) 249 { 250 block[i] ^= mask[i - defHash.length]; 251 } 252 253 // 254 // check the hash of the encoding params. 255 // 256 for (int i = 0; i != defHash.length; i++) 257 { 258 if (defHash[i] != block[defHash.length + i]) 259 { 260 throw new InvalidCipherTextException("data hash wrong"); 261 } 262 } 263 264 // 265 // find the data block 266 // 267 int start; 268 269 for (start = 2 * defHash.length; start != block.length; start++) 270 { 271 if (block[start] != 0) 272 { 273 break; 274 } 275 } 276 277 if (start >= (block.length - 1) || block[start] != 1) 278 { 279 throw new InvalidCipherTextException("data start wrong " + start); 280 } 281 282 start++; 283 284 // 285 // extract the data block 286 // 287 byte[] output = new byte[block.length - start]; 288 289 System.arraycopy(block, start, output, 0, output.length); 290 291 return output; 292 } 293 294 /** 295 * int to octet string. 296 */ 297 private void ItoOSP( 298 int i, 299 byte[] sp) 300 { 301 sp[0] = (byte)(i >>> 24); 302 sp[1] = (byte)(i >>> 16); 303 sp[2] = (byte)(i >>> 8); 304 sp[3] = (byte)(i >>> 0); 305 } 306 307 /** 308 * mask generator function, as described in PKCS1v2. 309 */ 310 private byte[] maskGeneratorFunction1( 311 byte[] Z, 312 int zOff, 313 int zLen, 314 int length) 315 { 316 byte[] mask = new byte[length]; 317 byte[] hashBuf = new byte[mgf1Hash.getDigestSize()]; 318 byte[] C = new byte[4]; 319 int counter = 0; 320 321 hash.reset(); 322 323 do 324 { 325 ItoOSP(counter, C); 326 327 mgf1Hash.update(Z, zOff, zLen); 328 mgf1Hash.update(C, 0, C.length); 329 mgf1Hash.doFinal(hashBuf, 0); 330 331 System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length); 332 } 333 while (++counter < (length / hashBuf.length)); 334 335 if ((counter * hashBuf.length) < length) 336 { 337 ItoOSP(counter, C); 338 339 mgf1Hash.update(Z, zOff, zLen); 340 mgf1Hash.update(C, 0, C.length); 341 mgf1Hash.doFinal(hashBuf, 0); 342 343 System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length)); 344 } 345 346 return mask; 347 } 348 } 349