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