blob: 5bdcb135442948047f1ac4fb37188ade1481f732 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.crypto.provider;
27
28import java.util.Arrays;
29import java.security.*;
30import java.security.spec.*;
31import javax.crypto.*;
32import javax.crypto.spec.*;
33
34/**
35 * This class implements the AES KeyWrap algorithm as defined
36 * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
37 * "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".
38 * Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
39 * can be used for this algorithm.
40 *
41 * @author Valerie Peng
42 *
43 *
44 * @see AESCipher
45 */
46public final class AESWrapCipher extends CipherSpi {
47
48 private static final byte[] IV = {
49 (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
50 (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
51 };
52
53 private static final int blksize = AESConstants.AES_BLOCK_SIZE;
54
55 /*
56 * internal cipher object which does the real work.
57 */
58 private AESCrypt cipher;
59
60 /*
61 * are we encrypting or decrypting?
62 */
63 private boolean decrypting = false;
64
65 /**
66 * Creates an instance of AES KeyWrap cipher with default
67 * mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
68 *
69 * @exception SecurityException if this constructor fails to verify
70 * its own integrity
71 */
72 public AESWrapCipher() {
73 SunJCE.ensureIntegrity(getClass());
74 cipher = new AESCrypt();
75 }
76
77 /**
78 * Sets the mode of this cipher. Only "ECB" mode is accepted for this
79 * cipher.
80 *
81 * @param mode the cipher mode
82 *
83 * @exception NoSuchAlgorithmException if the requested cipher mode
84 * is not "ECB".
85 */
86 protected void engineSetMode(String mode)
87 throws NoSuchAlgorithmException {
88 if (!mode.equalsIgnoreCase("ECB")) {
89 throw new NoSuchAlgorithmException(mode + " cannot be used");
90 }
91 }
92
93 /**
94 * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
95 * is accepted for this cipher.
96 *
97 * @param padding the padding mechanism
98 *
99 * @exception NoSuchPaddingException if the requested padding mechanism
100 * is not "NoPadding".
101 */
102 protected void engineSetPadding(String padding)
103 throws NoSuchPaddingException {
104 if (!padding.equalsIgnoreCase("NoPadding")) {
105 throw new NoSuchPaddingException(padding + " cannot be used");
106 }
107 }
108
109 /**
110 * Returns the block size (in bytes). i.e. 16 bytes.
111 *
112 * @return the block size (in bytes), i.e. 16 bytes.
113 */
114 protected int engineGetBlockSize() {
115 return blksize;
116 }
117
118 /**
119 * Returns the length in bytes that an output buffer would need to be
120 * given the input length <code>inputLen</code> (in bytes).
121 *
122 * <p>The actual output length of the next <code>update</code> or
123 * <code>doFinal</code> call may be smaller than the length returned
124 * by this method.
125 *
126 * @param inputLen the input length (in bytes)
127 *
128 * @return the required output buffer size (in bytes)
129 */
130 protected int engineGetOutputSize(int inputLen) {
131 // can only return an upper-limit if not initialized yet.
132 int result = 0;
133 if (decrypting) {
134 result = inputLen - 8;
135 } else {
136 result = inputLen + 8;
137 }
138 return (result < 0? 0:result);
139 }
140
141 /**
142 * Returns the initialization vector (IV) which is null for this cipher.
143 *
144 * @return null for this cipher.
145 */
146 protected byte[] engineGetIV() {
147 return null;
148 }
149
150 /**
151 * Initializes this cipher with a key and a source of randomness.
152 *
153 * <p>The cipher only supports the following two operation modes:<b>
154 * Cipher.WRAP_MODE, and <b>
155 * Cipher.UNWRAP_MODE.
156 * <p>For modes other than the above two, UnsupportedOperationException
157 * will be thrown.
158 *
159 * @param opmode the operation mode of this cipher. Only
160 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
161 * @param key the secret key.
162 * @param random the source of randomness.
163 *
164 * @exception InvalidKeyException if the given key is inappropriate for
165 * initializing this cipher.
166 */
167 protected void engineInit(int opmode, Key key, SecureRandom random)
168 throws InvalidKeyException {
169 if (opmode == Cipher.WRAP_MODE) {
170 decrypting = false;
171 } else if (opmode == Cipher.UNWRAP_MODE) {
172 decrypting = true;
173 } else {
174 throw new UnsupportedOperationException("This cipher can " +
175 "only be used for key wrapping and unwrapping");
176 }
177 cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());
178 }
179
180 /**
181 * Initializes this cipher with a key, a set of algorithm parameters,
182 * and a source of randomness.
183 *
184 * <p>The cipher only supports the following two operation modes:<b>
185 * Cipher.WRAP_MODE, and <b>
186 * Cipher.UNWRAP_MODE.
187 * <p>For modes other than the above two, UnsupportedOperationException
188 * will be thrown.
189 *
190 * @param opmode the operation mode of this cipher. Only
191 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
192 * @param key the secret key.
193 * @param params the algorithm parameters; must be null for this cipher.
194 * @param random the source of randomness.
195 *
196 * @exception InvalidKeyException if the given key is inappropriate for
197 * initializing this cipher
198 * @exception InvalidAlgorithmParameterException if the given algorithm
199 * parameters is not null.
200 */
201 protected void engineInit(int opmode, Key key,
202 AlgorithmParameterSpec params,
203 SecureRandom random)
204 throws InvalidKeyException, InvalidAlgorithmParameterException {
205 if (params != null) {
206 throw new InvalidAlgorithmParameterException("This cipher " +
207 "does not accept any parameters");
208 }
209 engineInit(opmode, key, random);
210 }
211
212 /**
213 * Initializes this cipher with a key, a set of algorithm parameters,
214 * and a source of randomness.
215 *
216 * <p>The cipher only supports the following two operation modes:<b>
217 * Cipher.WRAP_MODE, and <b>
218 * Cipher.UNWRAP_MODE.
219 * <p>For modes other than the above two, UnsupportedOperationException
220 * will be thrown.
221 *
222 * @param opmode the operation mode of this cipher. Only
223 * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
224 * @param key the secret key.
225 * @param params the algorithm parameters; must be null for this cipher.
226 * @param random the source of randomness.
227 *
228 * @exception InvalidKeyException if the given key is inappropriate.
229 * @exception InvalidAlgorithmParameterException if the given algorithm
230 * parameters is not null.
231 */
232 protected void engineInit(int opmode, Key key,
233 AlgorithmParameters params,
234 SecureRandom random)
235 throws InvalidKeyException, InvalidAlgorithmParameterException {
236 if (params != null) {
237 throw new InvalidAlgorithmParameterException("This cipher " +
238 "does not accept any parameters");
239 }
240 engineInit(opmode, key, random);
241 }
242
243 /**
244 * This operation is not supported by this cipher.
245 * Since it's impossible to initialize this cipher given the
246 * current Cipher.engineInit(...) implementation,
247 * IllegalStateException will always be thrown upon invocation.
248 *
249 * @param in the input buffer.
250 * @param inOffset the offset in <code>in</code> where the input
251 * starts.
252 * @param inLen the input length.
253 *
254 * @return n/a.
255 *
256 * @exception IllegalStateException upon invocation of this method.
257 */
258 protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
259 throw new IllegalStateException("Cipher has not been initialized");
260 }
261
262 /**
263 * This operation is not supported by this cipher.
264 * Since it's impossible to initialize this cipher given the
265 * current Cipher.engineInit(...) implementation,
266 * IllegalStateException will always be thrown upon invocation.
267 *
268 * @param in the input buffer.
269 * @param inOffset the offset in <code>in</code> where the input
270 * starts.
271 * @param inLen the input length.
272 * @param out the buffer for the result.
273 * @param outOffset the offset in <code>out</code> where the result
274 * is stored.
275 *
276 * @return n/a.
277 *
278 * @exception IllegalStateException upon invocation of this method.
279 */
280 protected int engineUpdate(byte[] in, int inOffset, int inLen,
281 byte[] out, int outOffset)
282 throws ShortBufferException {
283 throw new IllegalStateException("Cipher has not been initialized");
284 }
285
286 /**
287 * This operation is not supported by this cipher.
288 * Since it's impossible to initialize this cipher given the
289 * current Cipher.engineInit(...) implementation,
290 * IllegalStateException will always be thrown upon invocation.
291 *
292 * @param in the input buffer
293 * @param inOffset the offset in <code>in</code> where the input
294 * starts
295 * @param inLen the input length.
296 *
297 * @return n/a.
298 *
299 * @exception IllegalStateException upon invocation of this method.
300 */
301 protected byte[] engineDoFinal(byte[] input, int inputOffset,
302 int inputLen)
303 throws IllegalBlockSizeException, BadPaddingException {
304 throw new IllegalStateException("Cipher has not been initialized");
305 }
306
307 /**
308 * This operation is not supported by this cipher.
309 * Since it's impossible to initialize this cipher given the
310 * current Cipher.engineInit(...) implementation,
311 * IllegalStateException will always be thrown upon invocation.
312 *
313 * @param in the input buffer.
314 * @param inOffset the offset in <code>in</code> where the input
315 * starts.
316 * @param inLen the input length.
317 * @param out the buffer for the result.
318 * @param outOffset the ofset in <code>out</code> where the result
319 * is stored.
320 *
321 * @return n/a.
322 *
323 * @exception IllegalStateException upon invocation of this method.
324 */
325 protected int engineDoFinal(byte[] in, int inOffset, int inLen,
326 byte[] out, int outOffset)
327 throws IllegalBlockSizeException, ShortBufferException,
328 BadPaddingException {
329 throw new IllegalStateException("Cipher has not been initialized");
330 }
331
332 /**
333 * Returns the parameters used with this cipher which is always null
334 * for this cipher.
335 *
336 * @return null since this cipher does not use any parameters.
337 */
338 protected AlgorithmParameters engineGetParameters() {
339 return null;
340 }
341
342 /**
343 * Returns the key size of the given key object in number of bits.
344 *
345 * @param key the key object.
346 *
347 * @return the "effective" key size of the given key object.
348 *
349 * @exception InvalidKeyException if <code>key</code> is invalid.
350 */
351 protected int engineGetKeySize(Key key) throws InvalidKeyException {
352 byte[] encoded = key.getEncoded();
353 if (!AESCrypt.isKeySizeValid(encoded.length)) {
354 throw new InvalidKeyException("Invalid key length: " +
355 encoded.length + " bytes");
356 }
357 return encoded.length * 8;
358 }
359
360 /**
361 * Wrap a key.
362 *
363 * @param key the key to be wrapped.
364 *
365 * @return the wrapped key.
366 *
367 * @exception IllegalBlockSizeException if this cipher is a block
368 * cipher, no padding has been requested, and the length of the
369 * encoding of the key to be wrapped is not a
370 * multiple of the block size.
371 *
372 * @exception InvalidKeyException if it is impossible or unsafe to
373 * wrap the key with this cipher (e.g., a hardware protected key is
374 * being passed to a software only cipher).
375 */
376 protected byte[] engineWrap(Key key)
377 throws IllegalBlockSizeException, InvalidKeyException {
378 byte[] keyVal = key.getEncoded();
379 if ((keyVal == null) || (keyVal.length == 0)) {
380 throw new InvalidKeyException("Cannot get an encoding of " +
381 "the key to be wrapped");
382 }
383 byte[] out = new byte[keyVal.length + 8];
384
385 if (keyVal.length == 8) {
386 System.arraycopy(IV, 0, out, 0, IV.length);
387 System.arraycopy(keyVal, 0, out, IV.length, 8);
388 cipher.encryptBlock(out, 0, out, 0);
389 } else {
390 if (keyVal.length % 8 != 0) {
391 throw new IllegalBlockSizeException("length of the " +
392 "to be wrapped key should be multiples of 8 bytes");
393 }
394 System.arraycopy(IV, 0, out, 0, IV.length);
395 System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
396 int N = keyVal.length/8;
397 byte[] buffer = new byte[blksize];
398 for (int j = 0; j < 6; j++) {
399 for (int i = 1; i <= N; i++) {
400 int T = i + j*N;
401 System.arraycopy(out, 0, buffer, 0, IV.length);
402 System.arraycopy(out, i*8, buffer, IV.length, 8);
403 cipher.encryptBlock(buffer, 0, buffer, 0);
404 for (int k = 1; T != 0; k++) {
405 byte v = (byte) T;
406 buffer[IV.length - k] ^= v;
407 T >>>= 8;
408 }
409 System.arraycopy(buffer, 0, out, 0, IV.length);
410 System.arraycopy(buffer, 8, out, 8*i, 8);
411 }
412 }
413 }
414 return out;
415 }
416
417 /**
418 * Unwrap a previously wrapped key.
419 *
420 * @param wrappedKey the key to be unwrapped.
421 *
422 * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
423 *
424 * @param wrappedKeyType the type of the wrapped key.
425 * This is one of <code>Cipher.SECRET_KEY</code>,
426 * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
427 *
428 * @return the unwrapped key.
429 *
430 * @exception NoSuchAlgorithmException if no installed providers
431 * can create keys of type <code>wrappedKeyType</code> for the
432 * <code>wrappedKeyAlgorithm</code>.
433 *
434 * @exception InvalidKeyException if <code>wrappedKey</code> does not
435 * represent a wrapped key of type <code>wrappedKeyType</code> for
436 * the <code>wrappedKeyAlgorithm</code>.
437 */
438 protected Key engineUnwrap(byte[] wrappedKey,
439 String wrappedKeyAlgorithm,
440 int wrappedKeyType)
441 throws InvalidKeyException, NoSuchAlgorithmException {
442 int wrappedKeyLen = wrappedKey.length;
443 // ensure the wrappedKey length is multiples of 8 bytes and non-zero
444 if (wrappedKeyLen == 0) {
445 throw new InvalidKeyException("The wrapped key is empty");
446 }
447 if (wrappedKeyLen % 8 != 0) {
448 throw new InvalidKeyException
449 ("The wrapped key has invalid key length");
450 }
451 byte[] out = new byte[wrappedKeyLen - 8];
452 byte[] buffer = new byte[blksize];
453 if (wrappedKeyLen == 16) {
454 cipher.decryptBlock(wrappedKey, 0, buffer, 0);
455 for (int i = 0; i < IV.length; i++) {
456 if (IV[i] != buffer[i]) {
457 throw new InvalidKeyException("Integrity check failed");
458 }
459 }
460 System.arraycopy(buffer, IV.length, out, 0, out.length);
461 } else {
462 System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
463 System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
464 int N = out.length/8;
465 for (int j = 5; j >= 0; j--) {
466 for (int i = N; i > 0; i--) {
467 int T = i + j*N;
468 System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);
469 for (int k = 1; T != 0; k++) {
470 byte v = (byte) T;
471 buffer[IV.length - k] ^= v;
472 T >>>= 8;
473 }
474 cipher.decryptBlock(buffer, 0, buffer, 0);
475 System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);
476 }
477 }
478 for (int i = 0; i < IV.length; i++) {
479 if (IV[i] != buffer[i]) {
480 throw new InvalidKeyException("Integrity check failed");
481 }
482 }
483 }
484 return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
485 wrappedKeyType);
486 }
487}