content: /*
   * Copyright (C) 2018 The Android Open Source Project
   *
   * 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
   *
   *      http://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 com.android.server.backup.encryption.chunking.cdc;
  
  import static com.android.internal.util.Preconditions.checkNotNull;
  
  import java.security.InvalidKeyException;
  import java.security.NoSuchAlgorithmException;
  
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  
  /**
   * Secure HKDF utils. Allows client to deterministically derive additional key material from a base
   * secret. If the derived key material is compromised, this does not in of itself compromise the
   * root secret.
   *
   * <p>TODO(b/116575321): After all code is ported, rename this class to HkdfUtils.
   */
  public final class Hkdf {
      private static final byte[] CONSTANT_01 = {0x01};
      private static final String HmacSHA256 = "HmacSHA256";
      private static final String AES = "AES";
  
      /**
       * Implements HKDF (RFC 5869) with the SHA-256 hash and a 256-bit output key length.
       *
       * <p>IMPORTANT: The use or edit of this method requires a security review.
       *
       * @param masterKey Master key from which to derive sub-keys.
       * @param salt A randomly generated 256-bit byte string.
       * @param data Arbitrary information that is bound to the derived key (i.e., used in its
       *     creation).
       * @return Raw derived key bytes = HKDF-SHA256(masterKey, salt, data).
       * @throws InvalidKeyException If the salt can not be used as a valid key.
       */
      static byte[] hkdf(byte[] masterKey, byte[] salt, byte[] data) throws InvalidKeyException {
          checkNotNull(masterKey, "HKDF requires master key to be set.");
          checkNotNull(salt, "HKDF requires a salt.");
          checkNotNull(data, "No data provided to HKDF.");
          return hkdfSha256Expand(hkdfSha256Extract(masterKey, salt), data);
      }
  
      private Hkdf() {}
  
      /**
       * The HKDF (RFC 5869) extraction function, using the SHA-256 hash function. This function is
       * used to pre-process the {@code inputKeyMaterial} and mix it with the {@code salt}, producing
       * output suitable for use with HKDF expansion function (which produces the actual derived key).
       *
       * <p>IMPORTANT: The use or edit of this method requires a security review.
       *
       * @see #hkdfSha256Expand(byte[], byte[])
       * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC)
       * @throws InvalidKeyException If the salt can not be used as a valid key.
       */
      private static byte[] hkdfSha256Extract(byte[] inputKeyMaterial, byte[] salt)
              throws InvalidKeyException {
          // Note that the SecretKey encoding format is defined to be RAW, so the encoded form should
          // be consistent across implementations.
          Mac sha256;
          try {
              sha256 = Mac.getInstance(HmacSHA256);
          } catch (NoSuchAlgorithmException e) {
              // This can not happen - HmacSHA256 is supported by the platform.
              throw new AssertionError(e);
          }
          sha256.init(new SecretKeySpec(salt, AES));
  
          return sha256.doFinal(inputKeyMaterial);
      }
  
      /**
       * Special case of HKDF (RFC 5869) expansion function, using the SHA-256 hash function and
       * allowing for a maximum output length of 256 bits.
       *
       * <p>IMPORTANT: The use or edit of this method requires a security review.
       *
       * @param pseudoRandomKey Generated by {@link #hkdfSha256Extract(byte[], byte[])}.
       * @param info Arbitrary information the derived key should be bound to.
       * @return Raw derived key bytes = HMAC-SHA256(pseudoRandomKey, info | 0x01).
       * @throws InvalidKeyException If the salt can not be used as a valid key.
       */
      private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info)
              throws InvalidKeyException {
          // Note that RFC 5869 computes number of blocks N = ceil(hash length / output length), but
          // here we only deal with a 256 bit hash up to a 256 bit output, yielding N=1.
          Mac sha256;
          try {
              sha256 = Mac.getInstance(HmacSHA256);
          } catch (NoSuchAlgorithmException e) {
              // This can not happen - HmacSHA256 is supported by the platform.
              throw new AssertionError(e);
          }
          sha256.init(new SecretKeySpec(pseudoRandomKey, AES));
  
          sha256.update(info);
          sha256.update(CONSTANT_01);
          return sha256.doFinal();
      }
  }
  