text: package org.bouncycastle.crypto.generators;
  
  import org.bouncycastle.crypto.CipherParameters;
  import org.bouncycastle.crypto.Mac;
  import org.bouncycastle.crypto.PBEParametersGenerator;
  import org.bouncycastle.crypto.digests.SHA1Digest;
  import org.bouncycastle.crypto.macs.HMac;
  import org.bouncycastle.crypto.params.KeyParameter;
  import org.bouncycastle.crypto.params.ParametersWithIV;
  
  /**
   * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2.
   * This generator uses a SHA-1 HMac as the calculation function.
   * <p>
   * The document this implementation is based on can be found at
   * <a href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html>
   * RSA's PKCS5 Page</a>
   */
  public class PKCS5S2ParametersGenerator
      extends PBEParametersGenerator
  {
      private Mac    hMac = new HMac(new SHA1Digest());
  
      /**
       * construct a PKCS5 Scheme 2 Parameters generator.
       */
      public PKCS5S2ParametersGenerator()
      {
      }
  
      private void F(
          byte[]  P,
          byte[]  S,
          int     c,
          byte[]  iBuf,
          byte[]  out,
          int     outOff)
      {
          byte[]              state = new byte[hMac.getMacSize()];
          CipherParameters    param = new KeyParameter(P);
  
          hMac.init(param);
  
          if (S != null)
          {
              hMac.update(S, 0, S.length);
          }
  
          hMac.update(iBuf, 0, iBuf.length);
  
          hMac.doFinal(state, 0);
  
          System.arraycopy(state, 0, out, outOff, state.length);
  
          if (c == 0)
          {
              throw new IllegalArgumentException("iteration count must be at least 1.");
          }
          
          for (int count = 1; count < c; count++)
          {
              hMac.init(param);
              hMac.update(state, 0, state.length);
              hMac.doFinal(state, 0);
  
              for (int j = 0; j != state.length; j++)
              {
                  out[outOff + j] ^= state[j];
              }
          }
      }
  
      private void intToOctet(
          byte[]  buf,
          int     i)
      {
          buf[0] = (byte)(i >>> 24);
          buf[1] = (byte)(i >>> 16);
          buf[2] = (byte)(i >>> 8);
          buf[3] = (byte)i;
      }
  
      private byte[] generateDerivedKey(
          int dkLen)
      {
          int     hLen = hMac.getMacSize();
          int     l = (dkLen + hLen - 1) / hLen;
          byte[]  iBuf = new byte[4];
          byte[]  out = new byte[l * hLen];
  
          for (int i = 1; i <= l; i++)
          {
              intToOctet(iBuf, i);
  
              F(password, salt, iterationCount, iBuf, out, (i - 1) * hLen);
          }
  
          return out;
      }
  
      /**
       * Generate a key parameter derived from the password, salt, and iteration
       * count we are currently initialised with.
       *
       * @param keySize the size of the key we want (in bits)
       * @return a KeyParameter object.
       */
      public CipherParameters generateDerivedParameters(
          int keySize)
      {
          keySize = keySize / 8;
  
          byte[]  dKey = generateDerivedKey(keySize);
  
          return new KeyParameter(dKey, 0, keySize);
      }
  
      /**
       * Generate a key with initialisation vector parameter derived from
       * the password, salt, and iteration count we are currently initialised
       * with.
       *
       * @param keySize the size of the key we want (in bits)
       * @param ivSize the size of the iv we want (in bits)
       * @return a ParametersWithIV object.
       */
      public CipherParameters generateDerivedParameters(
          int     keySize,
          int     ivSize)
      {
          keySize = keySize / 8;
          ivSize = ivSize / 8;
  
          byte[]  dKey = generateDerivedKey(keySize + ivSize);
  
          return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize);
      }
  
      /**
       * Generate a key parameter for use with a MAC derived from the password,
       * salt, and iteration count we are currently initialised with.
       *
       * @param keySize the size of the key we want (in bits)
       * @return a KeyParameter object.
       */
      public CipherParameters generateDerivedMacParameters(
          int keySize)
      {
          return generateDerivedParameters(keySize);
      }
  }
  