Source for gnu.javax.crypto.prng.PBKDF2

   1: /* PBKDF2.java --
   2:    Copyright (C) 2003, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is a 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 of the License, or (at
   9: your option) 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; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: 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.prng;
  40: 
  41: import gnu.java.security.prng.BasePRNG;
  42: import gnu.java.security.prng.LimitReachedException;
  43: import gnu.javax.crypto.mac.HMac;
  44: import gnu.javax.crypto.mac.IMac;
  45: 
  46: import java.io.UnsupportedEncodingException;
  47: import java.util.Arrays;
  48: import java.util.HashMap;
  49: import java.util.Map;
  50: 
  51: /**
  52:  * An implementation of the <i>key derivation function</i> KDF2 from PKCS #5:
  53:  * Password-Based Cryptography (<b>PBE</b>). This KDF is essentially a way to
  54:  * transform a password and a salt into a stream of random bytes, which may then
  55:  * be used to initialize a cipher or a MAC.
  56:  * <p>
  57:  * This version uses a MAC as its pseudo-random function, and the password is
  58:  * used as the key.
  59:  * <p>
  60:  * References:
  61:  * <ol>
  62:  * <li>B. Kaliski, <a href="http://www.ietf.org/rfc/rfc2898.txt">RFC 2898:
  63:  * Password-Based Cryptography Specification, Version 2.0</a></li>
  64:  * </ol>
  65:  */
  66: public class PBKDF2
  67:     extends BasePRNG
  68:     implements Cloneable
  69: {
  70:   /**
  71:    * The bytes fed into the MAC. This is initially the concatenation of the salt
  72:    * and the block number.
  73:    */
  74:   private byte[] in;
  75:   /** The iteration count. */
  76:   private int iterationCount;
  77:   /** The salt. */
  78:   private byte[] salt;
  79:   /** The MAC (the pseudo-random function we use). */
  80:   private IMac mac;
  81:   /** The number of hLen-sized blocks generated. */
  82:   private long count;
  83: 
  84:   /**
  85:    * Creates a new PBKDF2 object. The argument is the MAC that will serve as the
  86:    * pseudo-random function. The MAC does not need to be initialized.
  87:    *
  88:    * @param mac The pseudo-random function.
  89:    */
  90:   public PBKDF2(IMac mac)
  91:   {
  92:     super("PBKDF2-" + mac.name());
  93:     this.mac = mac;
  94:     iterationCount = -1;
  95:   }
  96: 
  97:   public void setup(Map attributes)
  98:   {
  99:     Map macAttrib = new HashMap();
 100:     macAttrib.put(HMac.USE_WITH_PKCS5_V2, Boolean.TRUE);
 101:     byte[] s = (byte[]) attributes.get(IPBE.SALT);
 102:     if (s == null)
 103:       {
 104:         if (salt == null)
 105:           throw new IllegalArgumentException("no salt specified");
 106:         // Otherwise re-use.
 107:       }
 108:     else
 109:       salt = s;
 110:     byte[] macKeyMaterial;
 111:     char[] password = (char[]) attributes.get(IPBE.PASSWORD);
 112:     if (password != null)
 113:       {
 114:         String encoding = (String) attributes.get(IPBE.PASSWORD_ENCODING);
 115:         if (encoding == null || encoding.trim().length() == 0)
 116:           encoding = IPBE.DEFAULT_PASSWORD_ENCODING;
 117:         else
 118:           encoding = encoding.trim();
 119:         try
 120:           {
 121:             macKeyMaterial = new String(password).getBytes(encoding);
 122:           }
 123:         catch (UnsupportedEncodingException uee)
 124:           {
 125:             throw new IllegalArgumentException("Unknown or unsupported encoding: "
 126:                                                + encoding, uee);
 127:           }
 128:       }
 129:     else
 130:       macKeyMaterial = (byte[]) attributes.get(IMac.MAC_KEY_MATERIAL);
 131: 
 132:     if (macKeyMaterial != null)
 133:       macAttrib.put(IMac.MAC_KEY_MATERIAL, macKeyMaterial);
 134:     else if (! initialised)
 135:       throw new IllegalArgumentException(
 136:           "Neither password nor key-material were specified");
 137:     // otherwise re-use previous password/key-material
 138:     try
 139:       {
 140:         mac.init(macAttrib);
 141:       }
 142:     catch (Exception x)
 143:       {
 144:         throw new IllegalArgumentException(x.getMessage());
 145:       }
 146:     Integer ic = (Integer) attributes.get(IPBE.ITERATION_COUNT);
 147:     if (ic != null)
 148:       iterationCount = ic.intValue();
 149:     if (iterationCount <= 0)
 150:       throw new IllegalArgumentException("bad iteration count");
 151:     count = 0L;
 152:     buffer = new byte[mac.macSize()];
 153:     try
 154:       {
 155:         fillBlock();
 156:       }
 157:     catch (LimitReachedException x)
 158:       {
 159:         throw new Error(x.getMessage());
 160:       }
 161:   }
 162: 
 163:   public void fillBlock() throws LimitReachedException
 164:   {
 165:     if (++count > ((1L << 32) - 1))
 166:       throw new LimitReachedException();
 167:     Arrays.fill(buffer, (byte) 0x00);
 168:     int limit = salt.length;
 169:     in = new byte[limit + 4];
 170:     System.arraycopy(salt, 0, in, 0, salt.length);
 171:     in[limit++] = (byte)(count >>> 24);
 172:     in[limit++] = (byte)(count >>> 16);
 173:     in[limit++] = (byte)(count >>> 8);
 174:     in[limit  ] = (byte) count;
 175:     for (int i = 0; i < iterationCount; i++)
 176:       {
 177:         mac.reset();
 178:         mac.update(in, 0, in.length);
 179:         in = mac.digest();
 180:         for (int j = 0; j < buffer.length; j++)
 181:           buffer[j] ^= in[j];
 182:       }
 183:   }
 184: }