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.InvalidCipherTextException;
      6 import org.bouncycastle.crypto.params.ParametersWithRandom;
      7 import org.bouncycastle.crypto.params.RSAKeyParameters;
      8 
      9 /**
     10  * ISO 9796-1 padding. Note in the light of recent results you should
     11  * only use this with RSA (rather than the "simpler" Rabin keys) and you
     12  * should never use it with anything other than a hash (ie. even if the
     13  * message is small don't sign the message, sign it's hash) or some "random"
     14  * value. See your favorite search engine for details.
     15  */
     16 public class ISO9796d1Encoding
     17     implements AsymmetricBlockCipher
     18 {
     19     private static byte[]    shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf,
     20                                     0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 };
     21     private static byte[]    inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc,
     22                                     0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 };
     23 
     24     private AsymmetricBlockCipher   engine;
     25     private boolean                 forEncryption;
     26     private int                     bitSize;
     27     private int                     padBits = 0;
     28 
     29     public ISO9796d1Encoding(
     30         AsymmetricBlockCipher   cipher)
     31     {
     32         this.engine = cipher;
     33     }
     34 
     35     public AsymmetricBlockCipher getUnderlyingCipher()
     36     {
     37         return engine;
     38     }
     39 
     40     public void init(
     41         boolean             forEncryption,
     42         CipherParameters    param)
     43     {
     44         RSAKeyParameters  kParam = null;
     45 
     46         if (param instanceof ParametersWithRandom)
     47         {
     48             ParametersWithRandom    rParam = (ParametersWithRandom)param;
     49 
     50             kParam = (RSAKeyParameters)rParam.getParameters();
     51         }
     52         else
     53         {
     54             kParam = (RSAKeyParameters)param;
     55         }
     56 
     57         engine.init(forEncryption, param);
     58 
     59         bitSize = kParam.getModulus().bitLength();
     60 
     61         this.forEncryption = forEncryption;
     62     }
     63 
     64     /**
     65      * return the input block size. The largest message we can process
     66      * is (key_size_in_bits + 3)/16, which in our world comes to
     67      * key_size_in_bytes / 2.
     68      */
     69     public int getInputBlockSize()
     70     {
     71         int     baseBlockSize = engine.getInputBlockSize();
     72 
     73         if (forEncryption)
     74         {
     75             return (baseBlockSize + 1) / 2;
     76         }
     77         else
     78         {
     79             return baseBlockSize;
     80         }
     81     }
     82 
     83     /**
     84      * return the maximum possible size for the output.
     85      */
     86     public int getOutputBlockSize()
     87     {
     88         int     baseBlockSize = engine.getOutputBlockSize();
     89 
     90         if (forEncryption)
     91         {
     92             return baseBlockSize;
     93         }
     94         else
     95         {
     96             return (baseBlockSize + 1) / 2;
     97         }
     98     }
     99 
    100     /**
    101      * set the number of bits in the next message to be treated as
    102      * pad bits.
    103      */
    104     public void setPadBits(
    105         int     padBits)
    106     {
    107         if (padBits > 7)
    108         {
    109             throw new IllegalArgumentException("padBits > 7");
    110         }
    111 
    112         this.padBits = padBits;
    113     }
    114 
    115     /**
    116      * retrieve the number of pad bits in the last decoded message.
    117      */
    118     public int getPadBits()
    119     {
    120         return padBits;
    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     private byte[] encodeBlock(
    140         byte[]  in,
    141         int     inOff,
    142         int     inLen)
    143         throws InvalidCipherTextException
    144     {
    145         byte[]  block = new byte[(bitSize + 7) / 8];
    146         int     r = padBits + 1;
    147         int     z = inLen;
    148         int     t = (bitSize + 13) / 16;
    149 
    150         for (int i = 0; i < t; i += z)
    151         {
    152             if (i > t - z)
    153             {
    154                 System.arraycopy(in, inOff + inLen - (t - i),
    155                                     block, block.length - t, t - i);
    156             }
    157             else
    158             {
    159                 System.arraycopy(in, inOff, block, block.length - (i + z), z);
    160             }
    161         }
    162 
    163         for (int i = block.length - 2 * t; i != block.length; i += 2)
    164         {
    165             byte    val = block[block.length - t + i / 2];
    166 
    167             block[i] = (byte)((shadows[(val & 0xff) >>> 4] << 4)
    168                                                 | shadows[val & 0x0f]);
    169             block[i + 1] = val;
    170         }
    171 
    172         block[block.length - 2 * z] ^= r;
    173         block[block.length - 1] = (byte)((block[block.length - 1] << 4) | 0x06);
    174 
    175         int maxBit = (8 - (bitSize - 1) % 8);
    176         int offSet = 0;
    177 
    178         if (maxBit != 8)
    179         {
    180             block[0] &= 0xff >>> maxBit;
    181             block[0] |= 0x80 >>> maxBit;
    182         }
    183         else
    184         {
    185             block[0] = 0x00;
    186             block[1] |= 0x80;
    187             offSet = 1;
    188         }
    189 
    190         return engine.processBlock(block, offSet, block.length - offSet);
    191     }
    192 
    193     /**
    194      * @exception InvalidCipherTextException if the decrypted block is not a valid ISO 9796 bit string
    195      */
    196     private byte[] decodeBlock(
    197         byte[]  in,
    198         int     inOff,
    199         int     inLen)
    200         throws InvalidCipherTextException
    201     {
    202         byte[]  block = engine.processBlock(in, inOff, inLen);
    203         int     r = 1;
    204         int     t = (bitSize + 13) / 16;
    205 
    206         if ((block[block.length - 1] & 0x0f) != 0x6)
    207         {
    208             throw new InvalidCipherTextException("invalid forcing byte in block");
    209         }
    210 
    211         block[block.length - 1] = (byte)(((block[block.length - 1] & 0xff) >>> 4) | ((inverse[(block[block.length - 2] & 0xff) >> 4]) << 4));
    212         block[0] = (byte)((shadows[(block[1] & 0xff) >>> 4] << 4)
    213                                                 | shadows[block[1] & 0x0f]);
    214 
    215         boolean boundaryFound = false;
    216         int     boundary = 0;
    217 
    218         for (int i = block.length - 1; i >= block.length - 2 * t; i -= 2)
    219         {
    220             int val = ((shadows[(block[i] & 0xff) >>> 4] << 4)
    221                                         | shadows[block[i] & 0x0f]);
    222 
    223             if (((block[i - 1] ^ val) & 0xff) != 0)
    224             {
    225                 if (!boundaryFound)
    226                 {
    227                     boundaryFound = true;
    228                     r = (block[i - 1] ^ val) & 0xff;
    229                     boundary = i - 1;
    230                 }
    231                 else
    232                 {
    233                     throw new InvalidCipherTextException("invalid tsums in block");
    234                 }
    235             }
    236         }
    237 
    238         block[boundary] = 0;
    239 
    240         byte[]  nblock = new byte[(block.length - boundary) / 2];
    241 
    242         for (int i = 0; i < nblock.length; i++)
    243         {
    244             nblock[i] = block[2 * i + boundary + 1];
    245         }
    246 
    247         padBits = r - 1;
    248 
    249         return nblock;
    250     }
    251 }
    252