2006-08-14 Mark Wielaard <mark@klomp.org>
[official-gcc.git] / libjava / classpath / gnu / javax / crypto / kwa / TripleDESKeyWrap.java
blob71562bd752ba211744535358bd44c4005ad629bd
1 /* TripleDESKeyWrap.java -- FIXME: briefly describe file purpose
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.javax.crypto.kwa;
41 import gnu.java.security.Registry;
42 import gnu.java.security.hash.Sha160;
43 import gnu.javax.crypto.assembly.Assembly;
44 import gnu.javax.crypto.assembly.Cascade;
45 import gnu.javax.crypto.assembly.Direction;
46 import gnu.javax.crypto.assembly.Stage;
47 import gnu.javax.crypto.assembly.Transformer;
48 import gnu.javax.crypto.assembly.TransformerException;
49 import gnu.javax.crypto.cipher.IBlockCipher;
50 import gnu.javax.crypto.cipher.TripleDES;
51 import gnu.javax.crypto.mode.IMode;
52 import gnu.javax.crypto.mode.ModeFactory;
54 import java.security.InvalidKeyException;
55 import java.security.SecureRandom;
56 import java.util.Arrays;
57 import java.util.HashMap;
58 import java.util.Map;
60 /**
61 * The GNU implementation of the Triple DES Key Wrap Algorithm as described in
62 * [1].
63 * <p>
64 * <b>IMPORTANT</b>: This class is NOT thread safe.
65 * <p>
66 * References:
67 * <ol>
68 * <li><a href="http://www.rfc-archive.org/getrfc.php?rfc=3217">Triple-DES and
69 * RC2 Key Wrapping</a>.</li>
70 * <li><a href="http://www.w3.org/TR/xmlenc-core/">XML Encryption Syntax and
71 * Processing</a>.</li>
72 * </ol>
74 public class TripleDESKeyWrap
75 extends BaseKeyWrappingAlgorithm
77 private static final byte[] DEFAULT_IV = new byte[] {
78 (byte) 0x4A, (byte) 0xDD, (byte) 0xA2, (byte) 0x2C,
79 (byte) 0x79, (byte) 0xE8, (byte) 0x21, (byte) 0x05 };
81 private Assembly asm;
82 private HashMap asmAttributes = new HashMap();
83 private HashMap modeAttributes = new HashMap();
84 private Sha160 sha = new Sha160();
85 private SecureRandom rnd;
87 public TripleDESKeyWrap()
89 super(Registry.TRIPLEDES_KWA);
92 protected void engineInit(Map attributes) throws InvalidKeyException
94 rnd = (SecureRandom) attributes.get(IKeyWrappingAlgorithm.SOURCE_OF_RANDOMNESS);
95 IMode des3CBC = ModeFactory.getInstance(Registry.CBC_MODE, new TripleDES(), 8);
96 Stage des3CBCStage = Stage.getInstance(des3CBC, Direction.FORWARD);
97 Cascade cascade = new Cascade();
98 Object modeNdx = cascade.append(des3CBCStage);
100 asmAttributes.put(modeNdx, modeAttributes);
102 asm = new Assembly();
103 asm.addPreTransformer(Transformer.getCascadeTransformer(cascade));
105 modeAttributes.put(IBlockCipher.KEY_MATERIAL,
106 attributes.get(KEY_ENCRYPTION_KEY_MATERIAL));
107 asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD);
110 protected byte[] engineWrap(byte[] in, int inOffset, int length)
112 // The same key wrap algorithm is used for both Two-key Triple-DES and
113 // Three-key Triple-DES keys. When a Two-key Triple-DES key is to be
114 // wrapped, a third DES key with the same value as the first DES key is
115 // created. Thus, all wrapped Triple-DES keys include three DES keys.
116 if (length != 16 && length != 24)
117 throw new IllegalArgumentException("Only 2- and 3-key Triple DES keys are alowed");
119 byte[] CEK = new byte[24];
120 if (length == 16)
122 System.arraycopy(in, inOffset, CEK, 0, 16);
123 System.arraycopy(in, inOffset, CEK, 16, 8);
125 else
126 System.arraycopy(in, inOffset, CEK, 0, 24);
128 // TODO: check for the following:
129 // However, a Two-key Triple-DES key MUST NOT be used to wrap a Three-
130 // key Triple-DES key that is comprised of three unique DES keys.
132 // 1. Set odd parity for each of the DES key octets comprising the
133 // Three-Key Triple-DES key that is to be wrapped, call the result
134 // CEK.
135 TripleDES.adjustParity(CEK, 0);
137 // 2. Compute an 8 octet key checksum value on CEK as described above in
138 // Section 2, call the result ICV.
139 sha.update(CEK);
140 byte[] hash = sha.digest();
141 byte[] ICV = new byte[8];
142 System.arraycopy(hash, 0, ICV, 0, 8);
144 // 3. Let CEKICV = CEK || ICV.
145 byte[] CEKICV = new byte[CEK.length + ICV.length];
146 System.arraycopy(CEK, 0, CEKICV, 0, CEK.length);
147 System.arraycopy(ICV, 0, CEKICV, CEK.length, ICV.length);
149 // 4. Generate 8 octets at random, call the result IV.
150 byte[] IV = new byte[8];
151 nextRandomBytes(IV);
153 // 5. Encrypt CEKICV in CBC mode using the key-encryption key. Use the
154 // random value generated in the previous step as the initialization
155 // vector (IV). Call the ciphertext TEMP1.
156 modeAttributes.put(IMode.IV, IV);
157 asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD);
158 byte[] TEMP1;
161 asm.init(asmAttributes);
162 TEMP1 = asm.lastUpdate(CEKICV);
164 catch (TransformerException x)
166 throw new RuntimeException(x);
169 // 6. Let TEMP2 = IV || TEMP1.
170 byte[] TEMP2 = new byte[IV.length + TEMP1.length];
171 System.arraycopy(IV, 0, TEMP2, 0, IV.length);
172 System.arraycopy(TEMP1, 0, TEMP2, IV.length, TEMP1.length);
174 // 7. Reverse the order of the octets in TEMP2. That is, the most
175 // significant (first) octet is swapped with the least significant
176 // (last) octet, and so on. Call the result TEMP3.
177 byte[] TEMP3 = new byte[TEMP2.length];
178 for (int i = 0, j = TEMP2.length - 1; i < TEMP2.length; i++, j--)
179 TEMP3[j] = TEMP2[i];
181 // 8. Encrypt TEMP3 in CBC mode using the key-encryption key. Use an
182 // initialization vector (IV) of 0x4adda22c79e82105. The ciphertext
183 // is 40 octets long.
184 modeAttributes.put(IMode.IV, DEFAULT_IV);
185 asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD);
186 byte[] result;
189 asm.init(asmAttributes);
190 result = asm.lastUpdate(TEMP3);
192 catch (TransformerException x)
194 throw new RuntimeException(x);
196 return result;
199 protected byte[] engineUnwrap(byte[] in, int inOffset, int length)
200 throws KeyUnwrappingException
202 // 1. If the wrapped key is not 40 octets, then error.
203 if (length != 40)
204 throw new IllegalArgumentException("length MUST be 40");
206 // 2. Decrypt the wrapped key in CBC mode using the key-encryption key.
207 // Use an initialization vector (IV) of 0x4adda22c79e82105. Call the
208 // output TEMP3.
209 modeAttributes.put(IMode.IV, DEFAULT_IV);
210 asmAttributes.put(Assembly.DIRECTION, Direction.REVERSED);
211 byte[] TEMP3;
214 asm.init(asmAttributes);
215 TEMP3 = asm.lastUpdate(in, inOffset, 40);
217 catch (TransformerException x)
219 throw new RuntimeException(x);
222 // 3. Reverse the order of the octets in TEMP3. That is, the most
223 // significant (first) octet is swapped with the least significant
224 // (last) octet, and so on. Call the result TEMP2.
225 byte[] TEMP2 = new byte[40];
226 for (int i = 0, j = 40 - 1; i < 40; i++, j--)
227 TEMP2[j] = TEMP3[i];
229 // 4. Decompose TEMP2 into IV and TEMP1. IV is the most significant
230 // (first) 8 octets, and TEMP1 is the least significant (last) 32
231 // octets.
232 byte[] IV = new byte[8];
233 byte[] TEMP1 = new byte[32];
234 System.arraycopy(TEMP2, 0, IV, 0, 8);
235 System.arraycopy(TEMP2, 8, TEMP1, 0, 32);
237 // 5. Decrypt TEMP1 in CBC mode using the key-encryption key. Use the
238 // IV value from the previous step as the initialization vector.
239 // Call the ciphertext CEKICV.
240 modeAttributes.put(IMode.IV, IV);
241 asmAttributes.put(Assembly.DIRECTION, Direction.REVERSED);
242 byte[] CEKICV;
245 asm.init(asmAttributes);
246 CEKICV = asm.lastUpdate(TEMP1, 0, 32);
248 catch (TransformerException x)
250 throw new RuntimeException(x);
253 // 6. Decompose CEKICV into CEK and ICV. CEK is the most significant
254 // (first) 24 octets, and ICV is the least significant (last) 8
255 // octets.
256 byte[] CEK = new byte[24];
257 byte[] ICV = new byte[8];
258 System.arraycopy(CEKICV, 0, CEK, 0, 24);
259 System.arraycopy(CEKICV, 24, ICV, 0, 8);
261 // 7. Compute an 8 octet key checksum value on CEK as described above in
262 // Section 2. If the computed key checksum value does not match the
263 // decrypted key checksum value, ICV, then error.
264 sha.update(CEK);
265 byte[] hash = sha.digest();
266 byte[] computedICV = new byte[8];
267 System.arraycopy(hash, 0, computedICV, 0, 8);
268 if (! Arrays.equals(ICV, computedICV))
269 throw new KeyUnwrappingException("ICV and computed ICV MUST match");
271 // 8. Check for odd parity each of the DES key octets comprising CEK.
272 // If parity is incorrect, then error.
273 if (! TripleDES.isParityAdjusted(CEK, 0))
274 throw new KeyUnwrappingException("Triple-DES key parity MUST be adjusted");
276 // 9. Use CEK as a Triple-DES key.
277 return CEK;
281 * Fills the designated byte array with random data.
283 * @param buffer the byte array to fill with random data.
285 private void nextRandomBytes(byte[] buffer)
287 if (rnd != null)
288 rnd.nextBytes(buffer);
289 else
290 getDefaultPRNG().nextBytes(buffer);