• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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