Source for gnu.javax.crypto.kwa.TripleDESKeyWrap

   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: }