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