Home | History | Annotate | Download | only in engines
      1 package org.bouncycastle.crypto.engines;
      2 
      3 import org.bouncycastle.crypto.BlockCipher;
      4 import org.bouncycastle.crypto.CipherParameters;
      5 import org.bouncycastle.crypto.DataLengthException;
      6 import org.bouncycastle.crypto.InvalidCipherTextException;
      7 import org.bouncycastle.crypto.Wrapper;
      8 import org.bouncycastle.crypto.params.KeyParameter;
      9 import org.bouncycastle.crypto.params.ParametersWithIV;
     10 import org.bouncycastle.crypto.params.ParametersWithRandom;
     11 import org.bouncycastle.util.Arrays;
     12 
     13 /**
     14  * an implementation of the AES Key Wrapper from the NIST Key Wrap
     15  * Specification as described in RFC 3394.
     16  * <p>
     17  * For further details see: <a href="http://www.ietf.org/rfc/rfc3394.txt">http://www.ietf.org/rfc/rfc3394.txt</a>
     18  * and  <a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf">http://csrc.nist.gov/encryption/kms/key-wrap.pdf</a>.
     19  */
     20 public class RFC3394WrapEngine
     21     implements Wrapper
     22 {
     23     private BlockCipher     engine;
     24     private boolean         wrapCipherMode;
     25     private KeyParameter    param;
     26     private boolean         forWrapping;
     27 
     28     private byte[]          iv = {
     29                               (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6,
     30                               (byte)0xa6, (byte)0xa6, (byte)0xa6, (byte)0xa6 };
     31 
     32     /**
     33      * Create a RFC 3394 WrapEngine specifying the encrypt for wrapping, decrypt for unwrapping.
     34      *
     35      * @param engine the block cipher to be used for wrapping.
     36      */
     37     public RFC3394WrapEngine(BlockCipher engine)
     38     {
     39         this(engine, false);
     40     }
     41 
     42     /**
     43      * Create a RFC 3394 WrapEngine specifying the direction for wrapping and unwrapping..
     44      *
     45      * @param engine the block cipher to be used for wrapping.
     46      * @param useReverseDirection true if engine should be used in decryption mode for wrapping, false otherwise.
     47      */
     48     public RFC3394WrapEngine(BlockCipher engine, boolean useReverseDirection)
     49     {
     50         this.engine = engine;
     51         this.wrapCipherMode = (useReverseDirection) ? false : true;
     52     }
     53 
     54     public void init(
     55         boolean             forWrapping,
     56         CipherParameters    param)
     57     {
     58         this.forWrapping = forWrapping;
     59 
     60         if (param instanceof ParametersWithRandom)
     61         {
     62             param = ((ParametersWithRandom) param).getParameters();
     63         }
     64 
     65         if (param instanceof KeyParameter)
     66         {
     67             this.param = (KeyParameter)param;
     68         }
     69         else if (param instanceof ParametersWithIV)
     70         {
     71             this.iv = ((ParametersWithIV)param).getIV();
     72             this.param = (KeyParameter)((ParametersWithIV) param).getParameters();
     73             if (this.iv.length != 8)
     74             {
     75                throw new IllegalArgumentException("IV not equal to 8");
     76             }
     77         }
     78     }
     79 
     80     public String getAlgorithmName()
     81     {
     82         return engine.getAlgorithmName();
     83     }
     84 
     85     public byte[] wrap(
     86         byte[]  in,
     87         int     inOff,
     88         int     inLen)
     89     {
     90         if (!forWrapping)
     91         {
     92             throw new IllegalStateException("not set for wrapping");
     93         }
     94 
     95         int     n = inLen / 8;
     96 
     97         if ((n * 8) != inLen)
     98         {
     99             throw new DataLengthException("wrap data must be a multiple of 8 bytes");
    100         }
    101 
    102         byte[]  block = new byte[inLen + iv.length];
    103         byte[]  buf = new byte[8 + iv.length];
    104 
    105         System.arraycopy(iv, 0, block, 0, iv.length);
    106         System.arraycopy(in, inOff, block, iv.length, inLen);
    107 
    108         engine.init(wrapCipherMode, param);
    109 
    110         for (int j = 0; j != 6; j++)
    111         {
    112             for (int i = 1; i <= n; i++)
    113             {
    114                 System.arraycopy(block, 0, buf, 0, iv.length);
    115                 System.arraycopy(block, 8 * i, buf, iv.length, 8);
    116                 engine.processBlock(buf, 0, buf, 0);
    117 
    118                 int t = n * j + i;
    119                 for (int k = 1; t != 0; k++)
    120                 {
    121                     byte    v = (byte)t;
    122 
    123                     buf[iv.length - k] ^= v;
    124 
    125                     t >>>= 8;
    126                 }
    127 
    128                 System.arraycopy(buf, 0, block, 0, 8);
    129                 System.arraycopy(buf, 8, block, 8 * i, 8);
    130             }
    131         }
    132 
    133         return block;
    134     }
    135 
    136     public byte[] unwrap(
    137         byte[]  in,
    138         int     inOff,
    139         int     inLen)
    140         throws InvalidCipherTextException
    141     {
    142         if (forWrapping)
    143         {
    144             throw new IllegalStateException("not set for unwrapping");
    145         }
    146 
    147         int     n = inLen / 8;
    148 
    149         if ((n * 8) != inLen)
    150         {
    151             throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
    152         }
    153 
    154         byte[]  block = new byte[inLen - iv.length];
    155         byte[]  a = new byte[iv.length];
    156         byte[]  buf = new byte[8 + iv.length];
    157 
    158         System.arraycopy(in, inOff, a, 0, iv.length);
    159         System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
    160 
    161         engine.init(!wrapCipherMode, param);
    162 
    163         n = n - 1;
    164 
    165         for (int j = 5; j >= 0; j--)
    166         {
    167             for (int i = n; i >= 1; i--)
    168             {
    169                 System.arraycopy(a, 0, buf, 0, iv.length);
    170                 System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);
    171 
    172                 int t = n * j + i;
    173                 for (int k = 1; t != 0; k++)
    174                 {
    175                     byte    v = (byte)t;
    176 
    177                     buf[iv.length - k] ^= v;
    178 
    179                     t >>>= 8;
    180                 }
    181 
    182                 engine.processBlock(buf, 0, buf, 0);
    183                 System.arraycopy(buf, 0, a, 0, 8);
    184                 System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
    185             }
    186         }
    187 
    188         if (!Arrays.constantTimeAreEqual(a, iv))
    189         {
    190             throw new InvalidCipherTextException("checksum failed");
    191         }
    192 
    193         return block;
    194     }
    195 }
    196