| /* |
| * 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(); |
| } |
| } |