diff options
Diffstat (limited to 'libjava/classpath/gnu/javax/crypto/kwa')
6 files changed, 942 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/crypto/kwa/AESKeyWrap.java b/libjava/classpath/gnu/javax/crypto/kwa/AESKeyWrap.java new file mode 100644 index 000000000..bb86c5477 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/kwa/AESKeyWrap.java @@ -0,0 +1,168 @@ +/* AESWrap.java -- An implementation of RFC-3394 AES Key Wrap Algorithm + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.kwa; + +import gnu.java.security.Registry; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.cipher.Rijndael; + +import java.security.InvalidKeyException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The GNU implementation of the AES Key Wrap Algorithm as described in [1]. + * <p> + * References: + * <ol> + * <li><a href="http://csrc.nist.gov/encryption/kms/key-wrap.pdf"></a>.</li> + * <li><a href="http://www.rfc-archive.org/getrfc.php?rfc=3394">Advanced + * Encryption Standard (AES) Key Wrap Algorithm</a>.</li> + * <li><a href="http://www.w3.org/TR/xmlenc-core/">XML Encryption Syntax and + * Processing</a>.</li> + * </ol> + */ +public class AESKeyWrap + extends BaseKeyWrappingAlgorithm +{ + private static final byte[] DEFAULT_IV = new byte[] { + (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, + (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6 }; + + private Rijndael aes; + private byte[] iv; + + public AESKeyWrap() + { + super(Registry.AES_KWA); + + aes = new Rijndael(); + } + + protected void engineInit(Map attributes) throws InvalidKeyException + { + Map cipherAttributes = new HashMap(); + cipherAttributes.put(IBlockCipher.CIPHER_BLOCK_SIZE, Integer.valueOf(16)); + cipherAttributes.put(IBlockCipher.KEY_MATERIAL, + attributes.get(KEY_ENCRYPTION_KEY_MATERIAL)); + aes.reset(); + aes.init(cipherAttributes); + byte[] initialValue = (byte[]) attributes.get(INITIAL_VALUE); + iv = initialValue == null ? DEFAULT_IV : (byte[]) initialValue.clone(); + } + + protected byte[] engineWrap(byte[] in, int inOffset, int length) + { + // TODO: handle input length which is not a multiple of 8 as suggested by + // section 2.2.3.2 of RFC-3394 + if (length % 8 != 0) + throw new IllegalArgumentException("Input length MUST be a multiple of 8"); + int n = length / 8; + // output is always one block larger than input + byte[] result = new byte[length + 8]; + + // 1. init variables: we'll use out buffer for our work buffer; + // A will be the first block in out, while R will be the rest + System.arraycopy(iv, 0, result, 0, 8); + System.arraycopy(in, inOffset, result, 8, length); + byte[] B = new byte[2 * 8]; + // 2. compute intermediate values + long t; + for (int j = 0; j < 6; j++) + for (int i = 1; i <= n; i++) + { + System.arraycopy(result, 0, B, 0, 8); + System.arraycopy(result, i * 8, B, 8, 8); + aes.encryptBlock(B, 0, B, 0); + t = (n * j) + i; + result[0] = (byte)(B[0] ^ (t >>> 56)); + result[1] = (byte)(B[1] ^ (t >>> 48)); + result[2] = (byte)(B[2] ^ (t >>> 40)); + result[3] = (byte)(B[3] ^ (t >>> 32)); + result[4] = (byte)(B[4] ^ (t >>> 24)); + result[5] = (byte)(B[5] ^ (t >>> 16)); + result[6] = (byte)(B[6] ^ (t >>> 8)); + result[7] = (byte)(B[7] ^ t ); + System.arraycopy(B, 8, result, i * 8, 8); + } + return result; + } + + protected byte[] engineUnwrap(byte[] in, int inOffset, int length) + throws KeyUnwrappingException + { + // TODO: handle input length which is not a multiple of 8 as suggested by + // section 2.2.3.2 of RFC-3394 + if (length % 8 != 0) + throw new IllegalArgumentException("Input length MUST be a multiple of 8"); + // output is always one block shorter than input + byte[] result = new byte[length - 8]; + + // 1. init variables: we'll use out buffer for our R work buffer + byte[] A = new byte[8]; + System.arraycopy(in, inOffset, A, 0, 8); + System.arraycopy(in, inOffset + 8, result, 0, result.length); + byte[] B = new byte[2 * 8]; + // 2. compute intermediate values + int n = length / 8 - 1; + long t; + for (int j = 5; j >= 0; j--) + for (int i = n; i >= 1; i--) + { + t = (n * j) + i; + B[0] = (byte)(A[0] ^ (t >>> 56)); + B[1] = (byte)(A[1] ^ (t >>> 48)); + B[2] = (byte)(A[2] ^ (t >>> 40)); + B[3] = (byte)(A[3] ^ (t >>> 32)); + B[4] = (byte)(A[4] ^ (t >>> 24)); + B[5] = (byte)(A[5] ^ (t >>> 16)); + B[6] = (byte)(A[6] ^ (t >>> 8)); + B[7] = (byte)(A[7] ^ t ); + System.arraycopy(result, (i - 1) * 8, B, 8, 8); + aes.decryptBlock(B, 0, B, 0); + System.arraycopy(B, 0, A, 0, 8); + System.arraycopy(B, 8, result, (i - 1) * 8, 8); + } + if (! Arrays.equals(A, iv)) + throw new KeyUnwrappingException(); + + return result; + } +} diff --git a/libjava/classpath/gnu/javax/crypto/kwa/BaseKeyWrappingAlgorithm.java b/libjava/classpath/gnu/javax/crypto/kwa/BaseKeyWrappingAlgorithm.java new file mode 100644 index 000000000..80b114a02 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/kwa/BaseKeyWrappingAlgorithm.java @@ -0,0 +1,145 @@ +/* BaseKeyWrappingAlgorithm.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.kwa; + +import gnu.java.security.util.PRNG; + +import java.security.InvalidKeyException; +import java.util.Collections; +import java.util.Map; + +import javax.crypto.ShortBufferException; + +/** + * A base class to facilitate implementation of concrete Key Wrapping + * Algorithms. + */ +public abstract class BaseKeyWrappingAlgorithm + implements IKeyWrappingAlgorithm +{ + /** The canonical name of the key wrapping algorithm. */ + protected String name; + /** A source of randomness if/when needed by concrete implementations. */ + private PRNG prng; + + /** + * Protected constructor. + * + * @param name the key wrapping algorithm canonical name. + */ + protected BaseKeyWrappingAlgorithm(String name) + { + super(); + } + + public String name() + { + return this.name; + } + + public void init(Map attributes) throws InvalidKeyException + { + if (attributes == null) + attributes = Collections.EMPTY_MAP; + + engineInit(attributes); + } + + public int wrap(byte[] in, int inOffset, int length, byte[] out, int outOffset) + throws ShortBufferException + { + if (outOffset < 0) + throw new IllegalArgumentException("Output offset MUST NOT be negative"); + byte[] result = wrap(in, inOffset, length); + if (outOffset + result.length > out.length) + throw new ShortBufferException(); + System.arraycopy(result, 0, out, outOffset, result.length); + return result.length; + } + + public byte[] wrap(byte[] in, int inOffset, int length) + { + if (inOffset < 0) + throw new IllegalArgumentException("Input offset MUST NOT be negative"); + if (length < 0) + throw new IllegalArgumentException("Input length MUST NOT be negative"); + + return engineWrap(in, inOffset, length); + } + + public int unwrap(byte[] in, int inOffset, int length, + byte[] out, int outOffset) + throws ShortBufferException, KeyUnwrappingException + { + if (outOffset < 0) + throw new IllegalArgumentException("Output offset MUST NOT be negative"); + byte[] result = engineUnwrap(in, inOffset, length); + if (outOffset + result.length > out.length) + throw new ShortBufferException(); + System.arraycopy(result, 0, out, outOffset, result.length); + return result.length; + } + + public byte[] unwrap(byte[] in, int inOffset, int length) + throws KeyUnwrappingException + { + if (inOffset < 0) + throw new IllegalArgumentException("Input offset MUST NOT be negative"); + if (length < 0) + throw new IllegalArgumentException("Input length MUST NOT be negative"); + + return engineUnwrap(in, inOffset, length); + } + + protected abstract void engineInit(Map attributes) throws InvalidKeyException; + + protected abstract byte[] engineWrap(byte[] in, int inOffset, int length); + + protected abstract byte[] engineUnwrap(byte[] in, int inOffset, int length) + throws KeyUnwrappingException; + + /** @return a strong pseudo-random number generator if/when needed. */ + protected PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; + } +} diff --git a/libjava/classpath/gnu/javax/crypto/kwa/IKeyWrappingAlgorithm.java b/libjava/classpath/gnu/javax/crypto/kwa/IKeyWrappingAlgorithm.java new file mode 100644 index 000000000..271ec5c1b --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/kwa/IKeyWrappingAlgorithm.java @@ -0,0 +1,160 @@ +/* IKeyWrappingAlgorithm.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.kwa; + +import java.security.InvalidKeyException; +import java.security.SecureRandom; +import java.util.Map; + +import javax.crypto.ShortBufferException; + +/** + * Constants and visible methods available to all GNU Key Wrapping Algorithm + * implementations. + */ +public interface IKeyWrappingAlgorithm +{ + /** + * Name of the property, in the attributes map, that references the Key + * Wrapping Algorithm KEK (Key Encryption Key) material. The object referenced + * by this property is a byte array containing the keying material for the + * underlying block cipher. + */ + String KEY_ENCRYPTION_KEY_MATERIAL = "gnu.crypto.kwa.kek"; + /** + * Name of the property, in the attributes map, that references the Initial + * Value (IV) material. The object referenced by this property is a byte array + * containing the initial integrity check register value. + */ + String INITIAL_VALUE = "gnu.crypto.kwa.iv"; + /** + * Property name of an optional {@link SecureRandom} instance to use. The + * default is to use a {@link gnu.java.security.util.PRNG} instance. + */ + String SOURCE_OF_RANDOMNESS = "gnu.crypto.kwa.prng"; + + /** + * Returns the canonical name of this Key Wrapping Algorithm. + * + * @return the canonical name of this Key Wrapping Algorithm. + */ + String name(); + + /** + * Initializes this instance with the designated algorithm specific + * attributes. + * + * @param attributes a map of name-to-value pairs the Key Wrapping Algorithm + * must use for its setup. + * @throws InvalidKeyException if an exception is encountered while seting up + * the Key Wrapping Algorithm keying material (KEK). + */ + void init(Map attributes) throws InvalidKeyException; + + /** + * Wraps the designated plain text bytes. + * + * @param in the input byte array containing the plain text. + * @param inOffset the offset into <code>in</code> where the first byte of + * the plain text (key material) to wrap is located. + * @param length the number of bytes to wrap. + * @param out the output byte array where the wrapped key material will be + * stored. + * @param outOffset the offset into <code>out</code> of the first wrapped + * byte. + * @return the number of bytes of the wrapped key material; i.e. the length, + * in <code>out</code>, starting from <code>outOffset</code> + * where the cipher text (wrapped key material) are stored. + * @throws ShortBufferException if the output buffer is not long enough to + * accomodate the number of bytes resulting from wrapping the plain + * text. + */ + int wrap(byte[] in, int inOffset, int length, byte[] out, int outOffset) + throws ShortBufferException; + + /** + * Wraps the designated plain text bytes. + * + * @param in the input byte array containing the plain text. + * @param inOffset the offset into <code>in</code> where the first byte of + * the plain text (key material) to wrap is located. + * @param length the number of bytes to wrap. + * @return a newly allocated byte array containing the cipher text. + */ + byte[] wrap(byte[] in, int inOffset, int length); + + /** + * Unwraps the designated cipher text bytes. + * + * @param in the input byte array containing the cipher text. + * @param inOffset the offset into <code>in</code> where the first byte of + * the cipher text (already wrapped key material) to unwrap is + * located. + * @param length the number of bytes to unwrap. + * @param out the output byte array where the unwrapped key material will be + * stored. + * @param outOffset the offset into <code>out</code> of the first unwrapped + * byte. + * @return the number of bytes of the unwrapped key material; i.e. the length, + * in <code>out</code>, starting from <code>outOffset</code> + * where the plain text (unwrapped key material) are stored. + * @throws ShortBufferException if the output buffer is not long enough to + * accomodate the number of bytes resulting from unwrapping the + * cipher text. + * @throws KeyUnwrappingException if after unwrapping the cipher text, the + * bytes at the begining did not match the initial value. + */ + int unwrap(byte[] in, int inOffset, int length, byte[] out, int outOffset) + throws ShortBufferException, KeyUnwrappingException; + + /** + * Unwraps the designated cipher text bytes. + * + * @param in the input byte array containing the cipher text. + * @param inOffset the offset into <code>in</code> where the first byte of + * the cipher text (already wrapped key material) to unwrap is + * located. + * @param length the number of bytes to unwrap. + * @return a newly allocated byte array containing the plain text. + * @throws KeyUnwrappingException if after unwrapping the cipher text, the + * bytes at the begining did not match the initial value. + */ + byte[] unwrap(byte[] in, int inOffset, int length) + throws KeyUnwrappingException; +} diff --git a/libjava/classpath/gnu/javax/crypto/kwa/KeyUnwrappingException.java b/libjava/classpath/gnu/javax/crypto/kwa/KeyUnwrappingException.java new file mode 100644 index 000000000..54b4aff0a --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/kwa/KeyUnwrappingException.java @@ -0,0 +1,67 @@ +/* KeyUnwrappingException.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.kwa; + +import java.security.GeneralSecurityException; + +/** + * A checked security exception to denote an unexpected problem while unwrapping + * key material with a Key Wrapping Algorithm. + */ +public class KeyUnwrappingException + extends GeneralSecurityException +{ + /** + * Create a new instance with no descriptive error message. + */ + public KeyUnwrappingException() + { + super(); + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public KeyUnwrappingException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/gnu/javax/crypto/kwa/KeyWrappingAlgorithmFactory.java b/libjava/classpath/gnu/javax/crypto/kwa/KeyWrappingAlgorithmFactory.java new file mode 100644 index 000000000..abd208c07 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/kwa/KeyWrappingAlgorithmFactory.java @@ -0,0 +1,110 @@ +/* KeyWrappingAlgorithmFactory.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.kwa; + +import gnu.java.security.Registry; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A Factory class for the Key Wrapping Algorithm implementations. + */ +public class KeyWrappingAlgorithmFactory +{ + /** Names of Key Wrapping Algorihms cached for speed. */ + private static Set names; + + /** Trivial constructor to enforce Singleton pattern. */ + private KeyWrappingAlgorithmFactory() + { + super(); + } + + /** + * Returns an instance of a key-wrapping algorithm given its name. + * + * @param name the case-insensitive name of the key-wrapping algorithm. + * @return an instance of the designated key-wrapping algorithm, or + * <code>null</code> if none was found. + * @exception InternalError if the implementation does not pass its self-test. + */ + public static final IKeyWrappingAlgorithm getInstance(String name) + { + if (name == null) + return null; + name = name.trim(); + IKeyWrappingAlgorithm result = null; + if (name.equalsIgnoreCase(Registry.AES_KWA) + || name.equalsIgnoreCase(Registry.AES128_KWA) + || name.equalsIgnoreCase(Registry.AES192_KWA) + || name.equalsIgnoreCase(Registry.AES256_KWA) + || name.equalsIgnoreCase(Registry.RIJNDAEL_KWA)) + result = new AESKeyWrap(); + else if (name.equalsIgnoreCase(Registry.TRIPLEDES_KWA) + || name.equalsIgnoreCase(Registry.DESEDE_KWA)) + result = new TripleDESKeyWrap(); + + return result; + } + + /** + * Returns a {@link Set} of key wrapping algorithm names supported by this + * <i>Factory</i>. + * + * @return a {@link Set} of key wrapping algorithm names (Strings). + */ + public static synchronized final Set getNames() + { + if (names == null) + { + HashSet hs = new HashSet(); + hs.add(Registry.AES_KWA); + hs.add(Registry.AES128_KWA); + hs.add(Registry.AES192_KWA); + hs.add(Registry.AES256_KWA); + hs.add(Registry.RIJNDAEL_KWA); + hs.add(Registry.TRIPLEDES_KWA); + hs.add(Registry.DESEDE_KWA); + names = Collections.unmodifiableSet(hs); + } + return names; + } +} diff --git a/libjava/classpath/gnu/javax/crypto/kwa/TripleDESKeyWrap.java b/libjava/classpath/gnu/javax/crypto/kwa/TripleDESKeyWrap.java new file mode 100644 index 000000000..28b16cf31 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/kwa/TripleDESKeyWrap.java @@ -0,0 +1,292 @@ +/* TripleDESKeyWrap.java -- FIXME: briefly describe file purpose + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.kwa; + +import gnu.java.security.Registry; +import gnu.java.security.hash.Sha160; +import gnu.javax.crypto.assembly.Assembly; +import gnu.javax.crypto.assembly.Cascade; +import gnu.javax.crypto.assembly.Direction; +import gnu.javax.crypto.assembly.Stage; +import gnu.javax.crypto.assembly.Transformer; +import gnu.javax.crypto.assembly.TransformerException; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.cipher.TripleDES; +import gnu.javax.crypto.mode.IMode; +import gnu.javax.crypto.mode.ModeFactory; + +import java.security.InvalidKeyException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The GNU implementation of the Triple DES Key Wrap Algorithm as described in + * [1]. + * <p> + * <b>IMPORTANT</b>: This class is NOT thread safe. + * <p> + * References: + * <ol> + * <li><a href="http://www.rfc-archive.org/getrfc.php?rfc=3217">Triple-DES and + * RC2 Key Wrapping</a>.</li> + * <li><a href="http://www.w3.org/TR/xmlenc-core/">XML Encryption Syntax and + * Processing</a>.</li> + * </ol> + */ +public class TripleDESKeyWrap + extends BaseKeyWrappingAlgorithm +{ + private static final byte[] DEFAULT_IV = new byte[] { + (byte) 0x4A, (byte) 0xDD, (byte) 0xA2, (byte) 0x2C, + (byte) 0x79, (byte) 0xE8, (byte) 0x21, (byte) 0x05 }; + + private Assembly asm; + private HashMap asmAttributes = new HashMap(); + private HashMap modeAttributes = new HashMap(); + private Sha160 sha = new Sha160(); + private SecureRandom rnd; + + public TripleDESKeyWrap() + { + super(Registry.TRIPLEDES_KWA); + } + + protected void engineInit(Map attributes) throws InvalidKeyException + { + rnd = (SecureRandom) attributes.get(IKeyWrappingAlgorithm.SOURCE_OF_RANDOMNESS); + IMode des3CBC = ModeFactory.getInstance(Registry.CBC_MODE, new TripleDES(), 8); + Stage des3CBCStage = Stage.getInstance(des3CBC, Direction.FORWARD); + Cascade cascade = new Cascade(); + Object modeNdx = cascade.append(des3CBCStage); + + asmAttributes.put(modeNdx, modeAttributes); + + asm = new Assembly(); + asm.addPreTransformer(Transformer.getCascadeTransformer(cascade)); + + modeAttributes.put(IBlockCipher.KEY_MATERIAL, + attributes.get(KEY_ENCRYPTION_KEY_MATERIAL)); + asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD); + } + + protected byte[] engineWrap(byte[] in, int inOffset, int length) + { + // The same key wrap algorithm is used for both Two-key Triple-DES and + // Three-key Triple-DES keys. When a Two-key Triple-DES key is to be + // wrapped, a third DES key with the same value as the first DES key is + // created. Thus, all wrapped Triple-DES keys include three DES keys. + if (length != 16 && length != 24) + throw new IllegalArgumentException("Only 2- and 3-key Triple DES keys are alowed"); + + byte[] CEK = new byte[24]; + if (length == 16) + { + System.arraycopy(in, inOffset, CEK, 0, 16); + System.arraycopy(in, inOffset, CEK, 16, 8); + } + else + System.arraycopy(in, inOffset, CEK, 0, 24); + + // TODO: check for the following: + // However, a Two-key Triple-DES key MUST NOT be used to wrap a Three- + // key Triple-DES key that is comprised of three unique DES keys. + + // 1. Set odd parity for each of the DES key octets comprising the + // Three-Key Triple-DES key that is to be wrapped, call the result + // CEK. + TripleDES.adjustParity(CEK, 0); + + // 2. Compute an 8 octet key checksum value on CEK as described above in + // Section 2, call the result ICV. + sha.update(CEK); + byte[] hash = sha.digest(); + byte[] ICV = new byte[8]; + System.arraycopy(hash, 0, ICV, 0, 8); + + // 3. Let CEKICV = CEK || ICV. + byte[] CEKICV = new byte[CEK.length + ICV.length]; + System.arraycopy(CEK, 0, CEKICV, 0, CEK.length); + System.arraycopy(ICV, 0, CEKICV, CEK.length, ICV.length); + + // 4. Generate 8 octets at random, call the result IV. + byte[] IV = new byte[8]; + nextRandomBytes(IV); + + // 5. Encrypt CEKICV in CBC mode using the key-encryption key. Use the + // random value generated in the previous step as the initialization + // vector (IV). Call the ciphertext TEMP1. + modeAttributes.put(IMode.IV, IV); + asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD); + byte[] TEMP1; + try + { + asm.init(asmAttributes); + TEMP1 = asm.lastUpdate(CEKICV); + } + catch (TransformerException x) + { + throw new RuntimeException(x); + } + + // 6. Let TEMP2 = IV || TEMP1. + byte[] TEMP2 = new byte[IV.length + TEMP1.length]; + System.arraycopy(IV, 0, TEMP2, 0, IV.length); + System.arraycopy(TEMP1, 0, TEMP2, IV.length, TEMP1.length); + + // 7. Reverse the order of the octets in TEMP2. That is, the most + // significant (first) octet is swapped with the least significant + // (last) octet, and so on. Call the result TEMP3. + byte[] TEMP3 = new byte[TEMP2.length]; + for (int i = 0, j = TEMP2.length - 1; i < TEMP2.length; i++, j--) + TEMP3[j] = TEMP2[i]; + + // 8. Encrypt TEMP3 in CBC mode using the key-encryption key. Use an + // initialization vector (IV) of 0x4adda22c79e82105. The ciphertext + // is 40 octets long. + modeAttributes.put(IMode.IV, DEFAULT_IV); + asmAttributes.put(Assembly.DIRECTION, Direction.FORWARD); + byte[] result; + try + { + asm.init(asmAttributes); + result = asm.lastUpdate(TEMP3); + } + catch (TransformerException x) + { + throw new RuntimeException(x); + } + return result; + } + + protected byte[] engineUnwrap(byte[] in, int inOffset, int length) + throws KeyUnwrappingException + { + // 1. If the wrapped key is not 40 octets, then error. + if (length != 40) + throw new IllegalArgumentException("length MUST be 40"); + + // 2. Decrypt the wrapped key in CBC mode using the key-encryption key. + // Use an initialization vector (IV) of 0x4adda22c79e82105. Call the + // output TEMP3. + modeAttributes.put(IMode.IV, DEFAULT_IV); + asmAttributes.put(Assembly.DIRECTION, Direction.REVERSED); + byte[] TEMP3; + try + { + asm.init(asmAttributes); + TEMP3 = asm.lastUpdate(in, inOffset, 40); + } + catch (TransformerException x) + { + throw new RuntimeException(x); + } + + // 3. Reverse the order of the octets in TEMP3. That is, the most + // significant (first) octet is swapped with the least significant + // (last) octet, and so on. Call the result TEMP2. + byte[] TEMP2 = new byte[40]; + for (int i = 0, j = 40 - 1; i < 40; i++, j--) + TEMP2[j] = TEMP3[i]; + + // 4. Decompose TEMP2 into IV and TEMP1. IV is the most significant + // (first) 8 octets, and TEMP1 is the least significant (last) 32 + // octets. + byte[] IV = new byte[8]; + byte[] TEMP1 = new byte[32]; + System.arraycopy(TEMP2, 0, IV, 0, 8); + System.arraycopy(TEMP2, 8, TEMP1, 0, 32); + + // 5. Decrypt TEMP1 in CBC mode using the key-encryption key. Use the + // IV value from the previous step as the initialization vector. + // Call the ciphertext CEKICV. + modeAttributes.put(IMode.IV, IV); + asmAttributes.put(Assembly.DIRECTION, Direction.REVERSED); + byte[] CEKICV; + try + { + asm.init(asmAttributes); + CEKICV = asm.lastUpdate(TEMP1, 0, 32); + } + catch (TransformerException x) + { + throw new RuntimeException(x); + } + + // 6. Decompose CEKICV into CEK and ICV. CEK is the most significant + // (first) 24 octets, and ICV is the least significant (last) 8 + // octets. + byte[] CEK = new byte[24]; + byte[] ICV = new byte[8]; + System.arraycopy(CEKICV, 0, CEK, 0, 24); + System.arraycopy(CEKICV, 24, ICV, 0, 8); + + // 7. Compute an 8 octet key checksum value on CEK as described above in + // Section 2. If the computed key checksum value does not match the + // decrypted key checksum value, ICV, then error. + sha.update(CEK); + byte[] hash = sha.digest(); + byte[] computedICV = new byte[8]; + System.arraycopy(hash, 0, computedICV, 0, 8); + if (! Arrays.equals(ICV, computedICV)) + throw new KeyUnwrappingException("ICV and computed ICV MUST match"); + + // 8. Check for odd parity each of the DES key octets comprising CEK. + // If parity is incorrect, then error. + if (! TripleDES.isParityAdjusted(CEK, 0)) + throw new KeyUnwrappingException("Triple-DES key parity MUST be adjusted"); + + // 9. Use CEK as a Triple-DES key. + return CEK; + } + + /** + * Fills the designated byte array with random data. + * + * @param buffer the byte array to fill with random data. + */ + private void nextRandomBytes(byte[] buffer) + { + if (rnd != null) + rnd.nextBytes(buffer); + else + getDefaultPRNG().nextBytes(buffer); + } +} |