OAEPEncoding.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
1package org.bouncycastle.crypto.encodings; 2 3import org.bouncycastle.crypto.AsymmetricBlockCipher; 4import org.bouncycastle.crypto.CipherParameters; 5import org.bouncycastle.crypto.Digest; 6import org.bouncycastle.crypto.InvalidCipherTextException; 7// BEGIN android-changed 8import org.bouncycastle.crypto.digests.AndroidDigestFactory; 9// END android-changed 10import org.bouncycastle.crypto.params.ParametersWithRandom; 11 12import java.security.SecureRandom; 13 14/** 15 * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2. 16 */ 17public class OAEPEncoding 18 implements AsymmetricBlockCipher 19{ 20 private byte[] defHash; 21 private Digest hash; 22 private Digest mgf1Hash; 23 24 private AsymmetricBlockCipher engine; 25 private SecureRandom random; 26 private boolean forEncryption; 27 28 public OAEPEncoding( 29 AsymmetricBlockCipher cipher) 30 { 31 // BEGIN android-changed 32 this(cipher, AndroidDigestFactory.getSHA1(), null); 33 // END android-changed 34 } 35 36 public OAEPEncoding( 37 AsymmetricBlockCipher cipher, 38 Digest hash) 39 { 40 this(cipher, hash, null); 41 } 42 43 public OAEPEncoding( 44 AsymmetricBlockCipher cipher, 45 Digest hash, 46 byte[] encodingParams) 47 { 48 this(cipher, hash, hash, encodingParams); 49 } 50 51 public OAEPEncoding( 52 AsymmetricBlockCipher cipher, 53 Digest hash, 54 Digest mgf1Hash, 55 byte[] encodingParams) 56 { 57 this.engine = cipher; 58 this.hash = hash; 59 this.mgf1Hash = mgf1Hash; 60 this.defHash = new byte[hash.getDigestSize()]; 61 62 if (encodingParams != null) 63 { 64 hash.update(encodingParams, 0, encodingParams.length); 65 } 66 67 hash.doFinal(defHash, 0); 68 } 69 70 public AsymmetricBlockCipher getUnderlyingCipher() 71 { 72 return engine; 73 } 74 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 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 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 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 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 */ 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 // 260 for (int i = 0; i != defHash.length; i++) 261 { 262 if (defHash[i] != block[defHash.length + i]) 263 { 264 throw new InvalidCipherTextException("data hash wrong"); 265 } 266 } 267 268 // 269 // find the data block 270 // 271 int start; 272 273 for (start = 2 * defHash.length; start != block.length; start++) 274 { 275 if (block[start] != 0) 276 { 277 break; 278 } 279 } 280 281 if (start >= (block.length - 1) || block[start] != 1) 282 { 283 throw new InvalidCipherTextException("data start wrong " + start); 284 } 285 286 start++; 287 288 // 289 // extract the data block 290 // 291 byte[] output = new byte[block.length - start]; 292 293 System.arraycopy(block, start, output, 0, output.length); 294 295 return output; 296 } 297 298 /** 299 * int to octet string. 300 */ 301 private void ItoOSP( 302 int i, 303 byte[] sp) 304 { 305 sp[0] = (byte)(i >>> 24); 306 sp[1] = (byte)(i >>> 16); 307 sp[2] = (byte)(i >>> 8); 308 sp[3] = (byte)(i >>> 0); 309 } 310 311 /** 312 * mask generator function, as described in PKCS1v2. 313 */ 314 private byte[] maskGeneratorFunction1( 315 byte[] Z, 316 int zOff, 317 int zLen, 318 int length) 319 { 320 byte[] mask = new byte[length]; 321 byte[] hashBuf = new byte[mgf1Hash.getDigestSize()]; 322 byte[] C = new byte[4]; 323 int counter = 0; 324 325 hash.reset(); 326 327 do 328 { 329 ItoOSP(counter, C); 330 331 mgf1Hash.update(Z, zOff, zLen); 332 mgf1Hash.update(C, 0, C.length); 333 mgf1Hash.doFinal(hashBuf, 0); 334 335 System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, hashBuf.length); 336 } 337 while (++counter < (length / hashBuf.length)); 338 339 if ((counter * hashBuf.length) < length) 340 { 341 ItoOSP(counter, C); 342 343 mgf1Hash.update(Z, zOff, zLen); 344 mgf1Hash.update(C, 0, C.length); 345 mgf1Hash.doFinal(hashBuf, 0); 346 347 System.arraycopy(hashBuf, 0, mask, counter * hashBuf.length, mask.length - (counter * hashBuf.length)); 348 } 349 350 return mask; 351 } 352} 353