OSDN Git Service

2006-08-14 Mark Wielaard <mark@klomp.org>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / javax / crypto / kwa / TripleDESKeyWrap.java
1 /* TripleDESKeyWrap.java -- FIXME: briefly describe file purpose
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10
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.
15
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.
20
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.
25
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. */
37
38
39 package gnu.javax.crypto.kwa;
40
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;
53
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;
59
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>
73  */
74 public class TripleDESKeyWrap
75     extends BaseKeyWrappingAlgorithm
76 {
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 };
80
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;
86
87   public TripleDESKeyWrap()
88   {
89     super(Registry.TRIPLEDES_KWA);
90   }
91
92   protected void engineInit(Map attributes) throws InvalidKeyException
93   {
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);
99
100     asmAttributes.put(modeNdx, modeAttributes);
101
102     asm = new Assembly();
103     asm.addPreTransformer(Transformer.getCascadeTransformer(cascade));
104
105     modeAttributes.put(IBlockCipher.KEY_MATERIAL,
106                        attributes.get(KEY_ENCRYPTION_KEY_MATERIAL));
107     asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD);
108   }
109
110   protected byte[] engineWrap(byte[] in, int inOffset, int length)
111   {
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");
118
119     byte[] CEK = new byte[24];
120     if (length == 16)
121       {
122         System.arraycopy(in, inOffset, CEK, 0,  16);
123         System.arraycopy(in, inOffset, CEK, 16, 8);
124       }
125     else
126       System.arraycopy(in, inOffset, CEK, 0, 24);
127     
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.
131
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);
136
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);
143
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);
148
149     // 4. Generate 8 octets at random, call the result IV.
150     byte[] IV = new byte[8];
151     nextRandomBytes(IV);
152
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;
159     try
160       {
161         asm.init(asmAttributes);
162         TEMP1 = asm.lastUpdate(CEKICV);
163       }
164     catch (TransformerException x)
165       {
166         throw new RuntimeException(x);
167       }
168
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);
173
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];
180
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;
187     try
188       {
189         asm.init(asmAttributes);
190         result = asm.lastUpdate(TEMP3);
191       }
192     catch (TransformerException x)
193       {
194         throw new RuntimeException(x);
195       }
196     return result;
197   }
198
199   protected byte[] engineUnwrap(byte[] in, int inOffset, int length)
200       throws KeyUnwrappingException
201   {
202     // 1. If the wrapped key is not 40 octets, then error.
203     if (length != 40)
204       throw new IllegalArgumentException("length MUST be 40");
205
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;
212     try
213       {
214         asm.init(asmAttributes);
215         TEMP3 = asm.lastUpdate(in, inOffset, 40);
216       }
217     catch (TransformerException x)
218       {
219         throw new RuntimeException(x);
220       }
221
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];
228
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);
236
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;
243     try
244       {
245         asm.init(asmAttributes);
246         CEKICV = asm.lastUpdate(TEMP1, 0, 32);
247       }
248     catch (TransformerException x)
249       {
250         throw new RuntimeException(x);
251       }
252
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);
260
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");
270
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");
275
276     // 9. Use CEK as a Triple-DES key.
277     return CEK;
278   }
279   
280   /**
281    * Fills the designated byte array with random data.
282    * 
283    * @param buffer the byte array to fill with random data.
284    */
285   private void nextRandomBytes(byte[] buffer)
286   {
287     if (rnd != null)
288       rnd.nextBytes(buffer);
289     else
290       getDefaultPRNG().nextBytes(buffer);
291   }
292 }