forked from openjdk/jdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHKDF.java
More file actions
186 lines (171 loc) · 7.32 KB
/
Copy pathHKDF.java
File metadata and controls
186 lines (171 loc) · 7.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.ssl;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
import java.util.Objects;
/**
* An implementation of the HKDF key derivation algorithm outlined in RFC 5869,
* specific to the needs of TLS 1.3 key derivation in JSSE. This is not a
* general purpose HKDF implementation and is suited only to single-key output
* derivations.
*
* HKDF objects are created by specifying a message digest algorithm. That
* digest algorithm will be used by the HMAC function as part of the HKDF
* derivation process.
*/
final class HKDF {
private final String hmacAlg;
private final Mac hmacObj;
private final int hmacLen;
/**
* Create an HDKF object, specifying the underlying message digest
* algorithm.
*
* @param hashAlg a standard name corresponding to a supported message
* digest algorithm.
*
* @throws NoSuchAlgorithmException if that message digest algorithm does
* not have an HMAC variant supported on any available provider.
*/
HKDF(String hashAlg) throws NoSuchAlgorithmException {
Objects.requireNonNull(hashAlg,
"Must provide underlying HKDF Digest algorithm.");
hmacAlg = "Hmac" + hashAlg.replace("-", "");
hmacObj = Mac.getInstance(hmacAlg);
hmacLen = hmacObj.getMacLength();
}
/**
* Perform the HMAC-Extract derivation.
*
* @param salt a salt value, implemented as a {@code SecretKey}. A
* {@code null} value is allowed, which will internally use an array of
* zero bytes the same size as the underlying hash output length.
* @param inputKey the input keying material provided as a
* {@code SecretKey}.
* @param keyAlg the algorithm name assigned to the resulting
* {@code SecretKey} object.
*
* @return a {@code SecretKey} that is the result of the HKDF extract
* operation.
*
* @throws InvalidKeyException if the {@code salt} parameter cannot be
* used to initialize the underlying HMAC.
*/
SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
}
hmacObj.init(salt);
return new SecretKeySpec(hmacObj.doFinal(inputKey.getEncoded()),
keyAlg);
}
/**
* Perform the HMAC-Extract derivation.
*
* @param salt a salt value as cleartext bytes. A {@code null} value is
* allowed, which will internally use an array of zero bytes the same
* size as the underlying hash output length.
* @param inputKey the input keying material provided as a
* {@code SecretKey}.
* @param keyAlg the algorithm name assigned to the resulting
* {@code SecretKey} object.
*
* @return a {@code SecretKey} that is the result of the HKDF extract
* operation.
*
* @throws InvalidKeyException if the {@code salt} parameter cannot be
* used to initialize the underlying HMAC.
*/
SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new byte[hmacLen];
}
return extract(new SecretKeySpec(salt, "HKDF-Salt"), inputKey, keyAlg);
}
/**
* Perform the HKDF-Expand derivation for a single-key output.
*
* @param pseudoRandKey the pseudo random key (PRK).
* @param info optional context-specific info. A {@code null} value is
* allowed in which case a zero-length byte array will be used.
* @param outLen the length of the resulting {@code SecretKey}
* @param keyAlg the algorithm name applied to the resulting
* {@code SecretKey}
*
* @return the resulting key derivation as a {@code SecretKey} object
*
* @throws InvalidKeyException if the underlying HMAC operation cannot
* be initialized using the provided {@code pseudoRandKey} object.
*/
SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
String keyAlg) throws InvalidKeyException {
byte[] kdfOutput;
// Calculate the number of rounds of HMAC that are needed to
// meet the requested data. Then set up the buffers we will need.
Objects.requireNonNull(pseudoRandKey, "A null PRK is not allowed.");
// Output from the expand operation must be <= 255 * hmac length
if (outLen > 255 * hmacLen) {
throw new IllegalArgumentException("Requested output length " +
"exceeds maximum length allowed for HKDF expansion");
}
hmacObj.init(pseudoRandKey);
if (info == null) {
info = new byte[0];
}
int rounds = (outLen + hmacLen - 1) / hmacLen;
kdfOutput = new byte[rounds * hmacLen];
int offset = 0;
int tLength = 0;
for (int i = 0; i < rounds ; i++) {
// Calculate this round
try {
// Add T(i). This will be an empty string on the first
// iteration since tLength starts at zero. After the first
// iteration, tLength is changed to the HMAC length for the
// rest of the loop.
hmacObj.update(kdfOutput,
Math.max(0, offset - hmacLen), tLength);
hmacObj.update(info); // Add info
hmacObj.update((byte)(i + 1)); // Add round number
hmacObj.doFinal(kdfOutput, offset);
tLength = hmacLen;
offset += hmacLen; // For next iteration
} catch (ShortBufferException sbe) {
// This really shouldn't happen given that we've
// sized the buffers to their largest possible size up-front,
// but just in case...
throw new RuntimeException(sbe);
}
}
return new SecretKeySpec(kdfOutput, 0, outLen, keyAlg);
}
}