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