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