Home | History | Annotate | Download | only in encodings
      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      */
     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      */
     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      */
     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     //
     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 
    121     public AsymmetricBlockCipher getUnderlyingCipher()
    122     {
    123         return engine;
    124     }
    125 
    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 
    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 
    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 
    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 
    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      */
    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      */
    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      */
    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 
    394         //
    395         // find and extract the message block.
    396         //
    397         int start = findStart(type, data);
    398 
    399         start++;           // data should start at the next byte
    400 
    401         if (badType | start < HEADER_LENGTH)
    402         {
    403             Arrays.fill(data, (byte)0);
    404             throw new InvalidCipherTextException("block incorrect");
    405         }
    406 
    407         // if we get this far, it's likely to be a genuine encoding error
    408         if (incorrectLength)
    409         {
    410             Arrays.fill(data, (byte)0);
    411             throw new InvalidCipherTextException("block incorrect size");
    412         }
    413 
    414         byte[] result = new byte[data.length - start];
    415 
    416         System.arraycopy(data, start, result, 0, result.length);
    417 
    418         return result;
    419     }
    420 
    421     private int findStart(byte type, byte[] block)
    422         throws InvalidCipherTextException
    423     {
    424         int start = -1;
    425         boolean padErr = false;
    426 
    427         for (int i = 1; i != block.length; i++)
    428         {
    429             byte pad = block[i];
    430 
    431             if (pad == 0 & start < 0)
    432             {
    433                 start = i;
    434             }
    435             padErr |= (type == 1 & start < 0 & pad != (byte)0xff);
    436         }
    437 
    438         if (padErr)
    439         {
    440             return -1;
    441         }
    442 
    443         return start;
    444     }
    445 }
    446