1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.backup.encryption.chunking.cdc; 18 19 import java.security.InvalidKeyException; 20 import java.security.NoSuchAlgorithmException; 21 import java.util.Objects; 22 23 import javax.crypto.Mac; 24 import javax.crypto.spec.SecretKeySpec; 25 26 /** 27 * Secure HKDF utils. Allows client to deterministically derive additional key material from a base 28 * secret. If the derived key material is compromised, this does not in of itself compromise the 29 * root secret. 30 * 31 * <p>TODO(b/116575321): After all code is ported, rename this class to HkdfUtils. 32 */ 33 public final class Hkdf { 34 private static final byte[] CONSTANT_01 = {0x01}; 35 private static final String HmacSHA256 = "HmacSHA256"; 36 private static final String AES = "AES"; 37 38 /** 39 * Implements HKDF (RFC 5869) with the SHA-256 hash and a 256-bit output key length. 40 * 41 * <p>IMPORTANT: The use or edit of this method requires a security review. 42 * 43 * @param mainKey Main key from which to derive sub-keys. 44 * @param salt A randomly generated 256-bit byte string. 45 * @param data Arbitrary information that is bound to the derived key (i.e., used in its 46 * creation). 47 * @return Raw derived key bytes = HKDF-SHA256(mainKey, salt, data). 48 * @throws InvalidKeyException If the salt can not be used as a valid key. 49 */ hkdf(byte[] mainKey, byte[] salt, byte[] data)50 static byte[] hkdf(byte[] mainKey, byte[] salt, byte[] data) throws InvalidKeyException { 51 Objects.requireNonNull(mainKey, "HKDF requires main key to be set."); 52 Objects.requireNonNull(salt, "HKDF requires a salt."); 53 Objects.requireNonNull(data, "No data provided to HKDF."); 54 return hkdfSha256Expand(hkdfSha256Extract(mainKey, salt), data); 55 } 56 Hkdf()57 private Hkdf() {} 58 59 /** 60 * The HKDF (RFC 5869) extraction function, using the SHA-256 hash function. This function is 61 * used to pre-process the {@code inputKeyMaterial} and mix it with the {@code salt}, producing 62 * output suitable for use with HKDF expansion function (which produces the actual derived key). 63 * 64 * <p>IMPORTANT: The use or edit of this method requires a security review. 65 * 66 * @see #hkdfSha256Expand(byte[], byte[]) 67 * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC) 68 * @throws InvalidKeyException If the salt can not be used as a valid key. 69 */ hkdfSha256Extract(byte[] inputKeyMaterial, byte[] salt)70 private static byte[] hkdfSha256Extract(byte[] inputKeyMaterial, byte[] salt) 71 throws InvalidKeyException { 72 // Note that the SecretKey encoding format is defined to be RAW, so the encoded form should 73 // be consistent across implementations. 74 Mac sha256; 75 try { 76 sha256 = Mac.getInstance(HmacSHA256); 77 } catch (NoSuchAlgorithmException e) { 78 // This can not happen - HmacSHA256 is supported by the platform. 79 throw new AssertionError(e); 80 } 81 sha256.init(new SecretKeySpec(salt, AES)); 82 83 return sha256.doFinal(inputKeyMaterial); 84 } 85 86 /** 87 * Special case of HKDF (RFC 5869) expansion function, using the SHA-256 hash function and 88 * allowing for a maximum output length of 256 bits. 89 * 90 * <p>IMPORTANT: The use or edit of this method requires a security review. 91 * 92 * @param pseudoRandomKey Generated by {@link #hkdfSha256Extract(byte[], byte[])}. 93 * @param info Arbitrary information the derived key should be bound to. 94 * @return Raw derived key bytes = HMAC-SHA256(pseudoRandomKey, info | 0x01). 95 * @throws InvalidKeyException If the salt can not be used as a valid key. 96 */ hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info)97 private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info) 98 throws InvalidKeyException { 99 // Note that RFC 5869 computes number of blocks N = ceil(hash length / output length), but 100 // here we only deal with a 256 bit hash up to a 256 bit output, yielding N=1. 101 Mac sha256; 102 try { 103 sha256 = Mac.getInstance(HmacSHA256); 104 } catch (NoSuchAlgorithmException e) { 105 // This can not happen - HmacSHA256 is supported by the platform. 106 throw new AssertionError(e); 107 } 108 sha256.init(new SecretKeySpec(pseudoRandomKey, AES)); 109 110 sha256.update(info); 111 sha256.update(CONSTANT_01); 112 return sha256.doFinal(); 113 } 114 } 115