1 package org.bouncycastle.crypto.encodings; 2 3 import java.security.AccessController; 4 import java.security.PrivilegedAction; 5 import java.security.SecureRandom; 6 7 import org.bouncycastle.crypto.AsymmetricBlockCipher; 8 import org.bouncycastle.crypto.CipherParameters; 9 import org.bouncycastle.crypto.InvalidCipherTextException; 10 import org.bouncycastle.crypto.params.AsymmetricKeyParameter; 11 import org.bouncycastle.crypto.params.ParametersWithRandom; 12 import org.bouncycastle.util.Arrays; 13 14 /** 15 * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this 16 * depends on your application - see PKCS1 Version 2 for details. 17 */ 18 public class PKCS1Encoding 19 implements AsymmetricBlockCipher 20 { 21 /** 22 * @deprecated use NOT_STRICT_LENGTH_ENABLED_PROPERTY 23 */ 24 public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict"; 25 26 /** 27 * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to 28 * work with one of these set the system property org.bouncycastle.pkcs1.not_strict to true. 29 * <p> 30 * The system property is checked during construction of the encoding object, it is set to 31 * false by default. 32 * </p> 33 */ 34 public static final String NOT_STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.not_strict"; 35 36 private static final int HEADER_LENGTH = 10; 37 38 private SecureRandom random; 39 private AsymmetricBlockCipher engine; 40 private boolean forEncryption; 41 private boolean forPrivateKey; 42 private boolean useStrictLength; 43 private int pLen = -1; 44 private byte[] fallback = null; 45 private byte[] blockBuffer; 46 47 /** 48 * Basic constructor. 49 * 50 * @param cipher 51 */ PKCS1Encoding( AsymmetricBlockCipher cipher)52 public PKCS1Encoding( 53 AsymmetricBlockCipher cipher) 54 { 55 this.engine = cipher; 56 this.useStrictLength = useStrict(); 57 } 58 59 /** 60 * Constructor for decryption with a fixed plaintext length. 61 * 62 * @param cipher The cipher to use for cryptographic operation. 63 * @param pLen Length of the expected plaintext. 64 */ PKCS1Encoding( AsymmetricBlockCipher cipher, int pLen)65 public PKCS1Encoding( 66 AsymmetricBlockCipher cipher, 67 int pLen) 68 { 69 this.engine = cipher; 70 this.useStrictLength = useStrict(); 71 this.pLen = pLen; 72 } 73 74 /** 75 * Constructor for decryption with a fixed plaintext length and a fallback 76 * value that is returned, if the padding is incorrect. 77 * 78 * @param cipher The cipher to use for cryptographic operation. 79 * @param fallback The fallback value, we don't do an arraycopy here. 80 */ PKCS1Encoding( AsymmetricBlockCipher cipher, byte[] fallback)81 public PKCS1Encoding( 82 AsymmetricBlockCipher cipher, 83 byte[] fallback) 84 { 85 this.engine = cipher; 86 this.useStrictLength = useStrict(); 87 this.fallback = fallback; 88 this.pLen = fallback.length; 89 } 90 91 92 // 93 // for J2ME compatibility 94 // useStrict()95 private boolean useStrict() 96 { 97 // required if security manager has been installed. 98 String strict = (String)AccessController.doPrivileged(new PrivilegedAction() 99 { 100 public Object run() 101 { 102 return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY); 103 } 104 }); 105 String notStrict = (String)AccessController.doPrivileged(new PrivilegedAction() 106 { 107 public Object run() 108 { 109 return System.getProperty(NOT_STRICT_LENGTH_ENABLED_PROPERTY); 110 } 111 }); 112 113 if (notStrict != null) 114 { 115 return !notStrict.equals("true"); 116 } 117 118 return strict == null || strict.equals("true"); 119 } 120 getUnderlyingCipher()121 public AsymmetricBlockCipher getUnderlyingCipher() 122 { 123 return engine; 124 } 125 init( boolean forEncryption, CipherParameters param)126 public void init( 127 boolean forEncryption, 128 CipherParameters param) 129 { 130 AsymmetricKeyParameter kParam; 131 132 if (param instanceof ParametersWithRandom) 133 { 134 ParametersWithRandom rParam = (ParametersWithRandom)param; 135 136 this.random = rParam.getRandom(); 137 kParam = (AsymmetricKeyParameter)rParam.getParameters(); 138 } 139 else 140 { 141 kParam = (AsymmetricKeyParameter)param; 142 if (!kParam.isPrivate() && forEncryption) 143 { 144 this.random = new SecureRandom(); 145 } 146 } 147 148 engine.init(forEncryption, param); 149 150 this.forPrivateKey = kParam.isPrivate(); 151 this.forEncryption = forEncryption; 152 this.blockBuffer = new byte[engine.getOutputBlockSize()]; 153 154 if (pLen > 0 && fallback == null && random == null) 155 { 156 throw new IllegalArgumentException("encoder requires random"); 157 } 158 } 159 getInputBlockSize()160 public int getInputBlockSize() 161 { 162 int baseBlockSize = engine.getInputBlockSize(); 163 164 if (forEncryption) 165 { 166 return baseBlockSize - HEADER_LENGTH; 167 } 168 else 169 { 170 return baseBlockSize; 171 } 172 } 173 getOutputBlockSize()174 public int getOutputBlockSize() 175 { 176 int baseBlockSize = engine.getOutputBlockSize(); 177 178 if (forEncryption) 179 { 180 return baseBlockSize; 181 } 182 else 183 { 184 return baseBlockSize - HEADER_LENGTH; 185 } 186 } 187 processBlock( byte[] in, int inOff, int inLen)188 public byte[] processBlock( 189 byte[] in, 190 int inOff, 191 int inLen) 192 throws InvalidCipherTextException 193 { 194 if (forEncryption) 195 { 196 return encodeBlock(in, inOff, inLen); 197 } 198 else 199 { 200 return decodeBlock(in, inOff, inLen); 201 } 202 } 203 encodeBlock( byte[] in, int inOff, int inLen)204 private byte[] encodeBlock( 205 byte[] in, 206 int inOff, 207 int inLen) 208 throws InvalidCipherTextException 209 { 210 if (inLen > getInputBlockSize()) 211 { 212 throw new IllegalArgumentException("input data too large"); 213 } 214 215 byte[] block = new byte[engine.getInputBlockSize()]; 216 217 if (forPrivateKey) 218 { 219 block[0] = 0x01; // type code 1 220 221 for (int i = 1; i != block.length - inLen - 1; i++) 222 { 223 block[i] = (byte)0xFF; 224 } 225 } 226 else 227 { 228 random.nextBytes(block); // random fill 229 230 block[0] = 0x02; // type code 2 231 232 // 233 // a zero byte marks the end of the padding, so all 234 // the pad bytes must be non-zero. 235 // 236 for (int i = 1; i != block.length - inLen - 1; i++) 237 { 238 while (block[i] == 0) 239 { 240 block[i] = (byte)random.nextInt(); 241 } 242 } 243 } 244 245 block[block.length - inLen - 1] = 0x00; // mark the end of the padding 246 System.arraycopy(in, inOff, block, block.length - inLen, inLen); 247 248 return engine.processBlock(block, 0, block.length); 249 } 250 251 /** 252 * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext 253 * for encryption. 254 * 255 * @param encoded The Plaintext. 256 * @param pLen Expected length of the plaintext. 257 * @return Either 0, if the encoding is correct, or -1, if it is incorrect. 258 */ checkPkcs1Encoding(byte[] encoded, int pLen)259 private static int checkPkcs1Encoding(byte[] encoded, int pLen) 260 { 261 int correct = 0; 262 /* 263 * Check if the first two bytes are 0 2 264 */ 265 correct |= (encoded[0] ^ 2); 266 267 /* 268 * Now the padding check, check for no 0 byte in the padding 269 */ 270 int plen = encoded.length - ( 271 pLen /* Lenght of the PMS */ 272 + 1 /* Final 0-byte before PMS */ 273 ); 274 275 for (int i = 1; i < plen; i++) 276 { 277 int tmp = encoded[i]; 278 tmp |= tmp >> 1; 279 tmp |= tmp >> 2; 280 tmp |= tmp >> 4; 281 correct |= (tmp & 1) - 1; 282 } 283 284 /* 285 * Make sure the padding ends with a 0 byte. 286 */ 287 correct |= encoded[encoded.length - (pLen + 1)]; 288 289 /* 290 * Return 0 or 1, depending on the result. 291 */ 292 correct |= correct >> 1; 293 correct |= correct >> 2; 294 correct |= correct >> 4; 295 return ~((correct & 1) - 1); 296 } 297 298 299 /** 300 * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct. 301 * 302 * @param in The encrypted block. 303 * @param inOff Offset in the encrypted block. 304 * @param inLen Length of the encrypted block. 305 * //@param pLen Length of the desired output. 306 * @return The plaintext without padding, or a random value if the padding was incorrect. 307 * @throws InvalidCipherTextException 308 */ decodeBlockOrRandom(byte[] in, int inOff, int inLen)309 private byte[] decodeBlockOrRandom(byte[] in, int inOff, int inLen) 310 throws InvalidCipherTextException 311 { 312 if (!forPrivateKey) 313 { 314 throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing"); 315 } 316 317 byte[] block = engine.processBlock(in, inOff, inLen); 318 byte[] random; 319 if (this.fallback == null) 320 { 321 random = new byte[this.pLen]; 322 this.random.nextBytes(random); 323 } 324 else 325 { 326 random = fallback; 327 } 328 329 byte[] data = (useStrictLength & (block.length != engine.getOutputBlockSize())) ? blockBuffer : block; 330 331 /* 332 * Check the padding. 333 */ 334 int correct = PKCS1Encoding.checkPkcs1Encoding(data, this.pLen); 335 336 /* 337 * Now, to a constant time constant memory copy of the decrypted value 338 * or the random value, depending on the validity of the padding. 339 */ 340 byte[] result = new byte[this.pLen]; 341 for (int i = 0; i < this.pLen; i++) 342 { 343 result[i] = (byte)((data[i + (data.length - pLen)] & (~correct)) | (random[i] & correct)); 344 } 345 346 Arrays.fill(data, (byte)0); 347 348 return result; 349 } 350 351 /** 352 * @throws InvalidCipherTextException if the decrypted block is not in PKCS1 format. 353 */ decodeBlock( byte[] in, int inOff, int inLen)354 private byte[] decodeBlock( 355 byte[] in, 356 int inOff, 357 int inLen) 358 throws InvalidCipherTextException 359 { 360 /* 361 * If the length of the expected plaintext is known, we use a constant-time decryption. 362 * If the decryption fails, we return a random value. 363 */ 364 if (this.pLen != -1) 365 { 366 return this.decodeBlockOrRandom(in, inOff, inLen); 367 } 368 369 byte[] block = engine.processBlock(in, inOff, inLen); 370 boolean incorrectLength = (useStrictLength & (block.length != engine.getOutputBlockSize())); 371 372 byte[] data; 373 if (block.length < getOutputBlockSize()) 374 { 375 data = blockBuffer; 376 } 377 else 378 { 379 data = block; 380 } 381 382 byte type = data[0]; 383 384 boolean badType; 385 if (forPrivateKey) 386 { 387 badType = (type != 2); 388 } 389 else 390 { 391 badType = (type != 1); 392 } 393 // BEGIN android-added 394 if ((type == 1 && forPrivateKey) || (type == 2 && !forPrivateKey)) 395 { 396 throw new InvalidCipherTextException("invalid block type " + type); 397 } 398 // END android-added 399 400 // 401 // find and extract the message block. 402 // 403 int start = findStart(type, data); 404 405 start++; // data should start at the next byte 406 407 if (badType | start < HEADER_LENGTH) 408 { 409 Arrays.fill(data, (byte)0); 410 throw new InvalidCipherTextException("block incorrect"); 411 } 412 413 // if we get this far, it's likely to be a genuine encoding error 414 if (incorrectLength) 415 { 416 Arrays.fill(data, (byte)0); 417 throw new InvalidCipherTextException("block incorrect size"); 418 } 419 420 byte[] result = new byte[data.length - start]; 421 422 System.arraycopy(data, start, result, 0, result.length); 423 424 return result; 425 } 426 findStart(byte type, byte[] block)427 private int findStart(byte type, byte[] block) 428 throws InvalidCipherTextException 429 { 430 int start = -1; 431 boolean padErr = false; 432 433 for (int i = 1; i != block.length; i++) 434 { 435 byte pad = block[i]; 436 437 if (pad == 0 & start < 0) 438 { 439 start = i; 440 } 441 padErr |= (type == 1 & start < 0 & pad != (byte)0xff); 442 } 443 444 if (padErr) 445 { 446 return -1; 447 } 448 449 return start; 450 } 451 } 452