content: /*
  * Copyright 2010 Srikanth Reddy Lingala  
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); 
  * you may not use this file except in compliance with the License. 
  * You may obtain a copy of the License at 
  * 
  * https://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, 
  * software distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  * See the License for the specific language governing permissions and 
  * limitations under the License. 
  */
  
  package net.lingala.zip4j.crypto.PBKDF2;
  
  import net.lingala.zip4j.util.Raw;
  
  /*
   * Source referred from Matthias Gartner's PKCS#5 implementation - 
   * see https://rtner.de/software/PBKDF2.html
   */
  
  public class PBKDF2Engine
  {
      protected PBKDF2Parameters parameters;
  
      protected PRF prf;
  
      public PBKDF2Engine()
      {
          this.parameters = null;
          prf = null;
      }
  
      public PBKDF2Engine(PBKDF2Parameters parameters)
      {
          this.parameters = parameters;
          prf = null;
      }
  
      public PBKDF2Engine(PBKDF2Parameters parameters, PRF prf)
      {
          this.parameters = parameters;
          this.prf = prf;
      }
  
      public byte[] deriveKey(char[] inputPassword)
      {
          return deriveKey(inputPassword, 0);
      }
  
      public byte[] deriveKey(char[] inputPassword, int dkLen)
      {
          byte[] r = null;
          byte P[] = null;
          if (inputPassword == null)
          {
              throw new NullPointerException();
          }
          
          P = Raw.convertCharArrayToByteArray(inputPassword);
          
          assertPRF(P);
          if (dkLen == 0)
          {
              dkLen = prf.getHLen();
          }
          r = PBKDF2(prf, parameters.getSalt(), parameters.getIterationCount(),
                  dkLen);
          return r;
      }
  
      public boolean verifyKey(char[] inputPassword)
      {
          byte[] referenceKey = getParameters().getDerivedKey();
          if (referenceKey == null || referenceKey.length == 0)
          {
              return false;
          }
          byte[] inputKey = deriveKey(inputPassword, referenceKey.length);
  
          if (inputKey == null || inputKey.length != referenceKey.length)
          {
              return false;
          }
          for (int i = 0; i < inputKey.length; i++)
          {
              if (inputKey[i] != referenceKey[i])
              {
                  return false;
              }
          }
          return true;
      }
  
      protected void assertPRF(byte[] P)
      {
          if (prf == null)
          {
              prf = new MacBasedPRF(parameters.getHashAlgorithm());
          }
          prf.init(P);
      }
  
      public PRF getPseudoRandomFunction()
      {
          return prf;
      }
  
      protected byte[] PBKDF2(PRF prf, byte[] S, int c, int dkLen)
      {
          if (S == null)
          {
              S = new byte[0];
          }
          int hLen = prf.getHLen();
          int l = ceil(dkLen, hLen);
          int r = dkLen - (l - 1) * hLen;
          byte T[] = new byte[l * hLen];
          int ti_offset = 0;
          for (int i = 1; i <= l; i++)
          {
              _F(T, ti_offset, prf, S, c, i);
              ti_offset += hLen;
          }
          if (r < hLen)
          {
              // Incomplete last block
              byte DK[] = new byte[dkLen];
              System.arraycopy(T, 0, DK, 0, dkLen);
              return DK;
          }
          return T;
      }
  
      protected int ceil(int a, int b)
      {
          int m = 0;
          if (a % b > 0)
          {
              m = 1;
          }
          return a / b + m;
      }
  
      protected void _F(byte[] dest, int offset, PRF prf, byte[] S, int c,
              int blockIndex)
      {
          int hLen = prf.getHLen();
          byte U_r[] = new byte[hLen];
  
          // U0 = S || INT (i);
          byte U_i[] = new byte[S.length + 4];
          System.arraycopy(S, 0, U_i, 0, S.length);
          INT(U_i, S.length, blockIndex);
  
          for (int i = 0; i < c; i++)
          {
              U_i = prf.doFinal(U_i);
              xor(U_r, U_i);
          }
          System.arraycopy(U_r, 0, dest, offset, hLen);
      }
  
      protected void xor(byte[] dest, byte[] src)
      {
          for (int i = 0; i < dest.length; i++)
          {
              dest[i] ^= src[i];
          }
      }
  
      protected void INT(byte[] dest, int offset, int i)
      {
          dest[offset + 0] = (byte) (i / (256 * 256 * 256));
          dest[offset + 1] = (byte) (i / (256 * 256));
          dest[offset + 2] = (byte) (i / (256));
          dest[offset + 3] = (byte) (i);
      }
  
      public PBKDF2Parameters getParameters()
      {
          return parameters;
      }
  
      public void setParameters(PBKDF2Parameters parameters)
      {
          this.parameters = parameters;
      }
  
      public void setPseudoRandomFunction(PRF prf)
      {
          this.prf = prf;
      }
  }
  