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