| /* TripleDES.java -- |
| Copyright (C) 2002, 2006 Free Software Foundation, Inc. |
| |
| This file is a 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 of the License, 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; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, 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.cipher; |
| |
| import gnu.java.security.Registry; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.security.InvalidKeyException; |
| |
| /** |
| * Triple-DES, 3DES, or DESede is a <i>combined cipher</i> that uses three |
| * iterations of the Data Encryption Standard cipher to theoretically improve |
| * the security of plain DES, at the cost of speed. |
| * <p> |
| * Triple-DES runs the DES algorithm three times with one, two or three |
| * independent 56-bit (DES) keys. When used with one DES key, the cipher behaves |
| * exactly like a (slower) DES. |
| * <p> |
| * To encrypt: |
| * <blockquote><i>C<sub>i</sub> = E<sub>k3</sub> ( E<sub>k2</sub><sup>-1</sup> ( |
| * E<sub>k1</sub> ( P<sub>i</sub> )))</i> |
| * </blockquote> |
| * <p> |
| * And to decrypt: |
| * <blockquote><i>P<sub>i</sub> = E<sub>k1</sub><sup>-1</sup> ( |
| * E<sub>k2</sub> ( E<sub>k3</sub><sup>-1</sup> ( C<sub>i</sub> )))</i> |
| * </blockquote> |
| * <p> |
| * (The "ede" comes from the encryption operation, which runs |
| * Encrypt-Decrypt-Encrypt) |
| * <p> |
| * References: |
| * <ol> |
| * <li>Bruce Schneier, <i>Applied Cryptography: Protocols, Algorithms, and |
| * Source Code in C, Second Edition</i>. (1996 John Wiley and Sons) ISBN |
| * 0-471-11709-9. Page 294--295.</li> |
| * </ol> |
| */ |
| public class TripleDES |
| extends BaseCipher |
| { |
| /** Triple-DES only operates on 64 bit blocks. */ |
| public static final int BLOCK_SIZE = 8; |
| /** By default, Triple-DES uses 168 bits of a parity-adjusted 192 bit key. */ |
| public static final int KEY_SIZE = 24; |
| /** The underlying DES instance. */ |
| private DES des; |
| |
| /** |
| * Default 0-arguments constructor. |
| */ |
| public TripleDES() |
| { |
| super(Registry.TRIPLEDES_CIPHER, BLOCK_SIZE, KEY_SIZE); |
| des = new DES(); |
| } |
| |
| /** |
| * Convenience method which calls the method with same name and three |
| * arguments, passing <code>3</code> as the value of the first parameter. |
| * |
| * @param kb The key bytes to adjust. |
| * @param offset The starting offset into the key bytes. |
| */ |
| public static void adjustParity(byte[] kb, int offset) |
| { |
| adjustParity(3, kb, offset); |
| } |
| |
| /** |
| * Adjusts, in-situ, the parity of the designated bytes, so they can be used |
| * as DES keys for a 3-DES 1-, 2- or 3-key cipher. |
| * |
| * @param keyCount the number of independent DES keys. Can be either |
| * <code>1</code>, <code>2</code> or <code>3</code>. Any other value |
| * will cause an {@link IllegalArgumentException} to be raised. |
| * @param kb the array containing the key bytes to adjust. MUST have at least |
| * <code>8 * keyCount</code> bytes starting at offset position |
| * <code>offset</code>, otherwise an |
| * {@link ArrayIndexOutOfBoundsException} will be raised. |
| * @param offset the starting offset into the array. |
| * @see DES#adjustParity(byte[],int) |
| */ |
| public static void adjustParity(int keyCount, byte[] kb, int offset) |
| { |
| if (keyCount < 1 || keyCount > 3) |
| throw new IllegalArgumentException("Invalid keyCount value: " + keyCount); |
| DES.adjustParity(kb, offset); |
| if (keyCount > 1) |
| DES.adjustParity(kb, offset + 8); |
| if (keyCount > 2) |
| DES.adjustParity(kb, offset + 16); |
| } |
| |
| /** |
| * Convenience method which calls the method with same name and three |
| * arguments, passing <code>3</code> as the value of the first parameter. |
| * |
| * @param kb The key bytes to test. |
| * @param offset The starting offset into the key bytes. |
| * @return <code>true</code> if the bytes in <i>kb</i> starting at |
| * <i>offset</i> are parity adjusted. |
| * @see DES#isParityAdjusted(byte[],int) |
| * @see #adjustParity(byte[],int) |
| */ |
| public static boolean isParityAdjusted(byte[] kb, int offset) |
| { |
| return isParityAdjusted(3, kb, offset); |
| } |
| |
| /** |
| * Tests if enough bytes, expected to be used as DES keys for a 3-DES 1-, 2- |
| * or 3-key cipher, located in a designated byte array, has already been |
| * parity adjusted. |
| * |
| * @param keyCount the number of independent DES keys. Can be either |
| * <code>1</code>, <code>2</code> or <code>3</code>. Any other value |
| * will cause an {@link IllegalArgumentException} to be raised. |
| * @param kb the array containing the key bytes to test. MUST have at least |
| * <code>8 * keyCount</code> bytes starting at offset position |
| * <code>offset</code>, otherwise an |
| * {@link ArrayIndexOutOfBoundsException} will be raised. |
| * @param offset the starting offset into the array. |
| * @return <code>true</code> if the bytes in <i>kb</i> starting at |
| * <i>offset</i> are parity adjusted. |
| * @see DES#isParityAdjusted(byte[],int) |
| * @see #adjustParity(int,byte[],int) |
| */ |
| public static boolean isParityAdjusted(int keyCount, byte[] kb, int offset) |
| { |
| if (keyCount < 1 || keyCount > 3) |
| throw new IllegalArgumentException("Invalid keyCount value: " + keyCount); |
| boolean result = DES.isParityAdjusted(kb, offset); |
| if (keyCount > 1) |
| result = result && DES.isParityAdjusted(kb, offset + 8); |
| if (keyCount > 2) |
| result = result && DES.isParityAdjusted(kb, offset + 16); |
| return result; |
| } |
| |
| public Object clone() |
| { |
| return new TripleDES(); |
| } |
| |
| public Iterator blockSizes() |
| { |
| return Collections.singleton(Integer.valueOf(BLOCK_SIZE)).iterator(); |
| } |
| |
| public Iterator keySizes() |
| { |
| ArrayList al = new ArrayList(); |
| al.add(Integer.valueOf(8)); |
| al.add(Integer.valueOf(16)); |
| al.add(Integer.valueOf(24)); |
| return Collections.unmodifiableList(al).iterator(); |
| } |
| |
| public Object makeKey(byte[] kb, int bs) throws InvalidKeyException |
| { |
| if (kb.length != 8 && kb.length != 16 && kb.length != 24) |
| throw new InvalidKeyException("TripleDES key must be 8, 16 or 24 bytes: " |
| + kb.length); |
| Context ctx = new Context(); |
| byte[] k1 = new byte[DES.KEY_SIZE]; |
| System.arraycopy(kb, 0, k1, 0, DES.KEY_SIZE); |
| if (! DES.isParityAdjusted(k1, 0)) |
| DES.adjustParity(k1, 0); |
| ctx.k1 = (DES.Context) des.makeKey(k1, bs); |
| |
| if (kb.length == 8) |
| { |
| ctx.k2 = (DES.Context) des.makeKey(k1, bs); |
| ctx.k3 = (DES.Context) des.makeKey(k1, bs); |
| } |
| else |
| { |
| byte[] k2 = new byte[DES.KEY_SIZE]; |
| System.arraycopy(kb, DES.KEY_SIZE, k2, 0, DES.KEY_SIZE); |
| if (! DES.isParityAdjusted(k2, 0)) |
| DES.adjustParity(k2, 0); |
| ctx.k2 = (DES.Context) des.makeKey(k2, bs); |
| |
| byte[] k3 = new byte[DES.KEY_SIZE]; |
| if (kb.length == 16) |
| ctx.k3 = (DES.Context) des.makeKey(k1, bs); |
| else |
| { |
| System.arraycopy(kb, 2 * DES.KEY_SIZE, k3, 0, DES.KEY_SIZE); |
| if (! DES.isParityAdjusted(k3, 0)) |
| DES.adjustParity(k3, 0); |
| ctx.k3 = (DES.Context) des.makeKey(k3, bs); |
| } |
| } |
| return ctx; |
| } |
| |
| public void encrypt(byte[] in, int i, byte[] out, int o, Object K, int bs) |
| { |
| byte[] temp = new byte[BLOCK_SIZE]; |
| des.encrypt(in, i, temp, 0, ((Context) K).k1, bs); |
| des.decrypt(temp, 0, temp, 0, ((Context) K).k2, bs); |
| des.encrypt(temp, 0, out, o, ((Context) K).k3, bs); |
| } |
| |
| public void decrypt(byte[] in, int i, byte[] out, int o, Object K, int bs) |
| { |
| byte[] temp = new byte[BLOCK_SIZE]; |
| des.decrypt(in, i, temp, 0, ((Context) K).k3, bs); |
| des.encrypt(temp, 0, temp, 0, ((Context) K).k2, bs); |
| des.decrypt(temp, 0, out, o, ((Context) K).k1, bs); |
| } |
| |
| private final class Context |
| { |
| DES.Context k1, k2, k3; |
| } |
| } |