/
githubmirror
/
galahad
Обзор
Документация
Войти
/
githubmirror
/
galahad
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/javax/crypto/spec/HKDFParameterSpec.java
503 строки
19 KB
Weijun Wang
8353888: Implement JEP 510: Key Derivation Function API
16 май 2025, 15:25
16 май 2025, 15:25
079fccf
Код
Авторство
О чём код?
/* * Copyright (c) 2024, 2025, 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 javax.crypto.spec; import javax.crypto.SecretKey; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Parameters for the combined Extract, Expand, or Extract-then-Expand * operations of the HMAC-based Key Derivation Function (HKDF). The HKDF * function is defined in <a href="http://tools.ietf.org/html/rfc5869">RFC * 5869</a>. * <p> * In the Extract and Extract-then-Expand cases, users may call the {@code * addIKM} and/or {@code addSalt} methods repeatedly (and chain these calls). * This provides for use-cases where a portion of the input keying material * (IKM) resides in a non-extractable {@code SecretKey} and the whole IKM * cannot be provided as a single object. The same feature is available for * salts. * <p> * The above feature is particularly useful for "labeled" HKDF Extract used in * TLS 1.3 and HPKE, where the IKM consists of concatenated components, which * may include both byte arrays and (possibly non-extractable) secret keys. * <p> * Examples: * {@snippet lang = java: * // this usage depicts the initialization of an HKDF-Extract AlgorithmParameterSpec * AlgorithmParameterSpec derivationSpec = * HKDFParameterSpec.ofExtract() * .addIKM(label) * .addIKM(ikm) * .addSalt(salt).extractOnly(); *} * {@snippet lang = java: * // this usage depicts the initialization of an HKDF-Expand AlgorithmParameterSpec * AlgorithmParameterSpec derivationSpec = * HKDFParameterSpec.expandOnly(prk, info, 32); *} * {@snippet lang = java: * // this usage depicts the initialization of an HKDF-ExtractExpand AlgorithmParameterSpec * AlgorithmParameterSpec derivationSpec = * HKDFParameterSpec.ofExtract() * .addIKM(ikm) * .addSalt(salt).thenExpand(info, 32); *} * * @spec https://www.rfc-editor.org/info/rfc5869 * RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF) * @see javax.crypto.KDF * @since 25 */ public interface HKDFParameterSpec extends AlgorithmParameterSpec { /** * This {@code Builder} builds {@code Extract} and {@code ExtractThenExpand} * objects. * <p> * The {@code Builder} is initialized via the {@code ofExtract} method of * {@code HKDFParameterSpec}. As stated in the class description, * {@code addIKM} and/or {@code addSalt} may be called as needed. Finally, * an object is "built" by calling either {@code extractOnly} or * {@code thenExpand} for {@code Extract} and {@code ExtractThenExpand} * use-cases respectively. Note that the {@code Builder} is not * thread-safe. */ final class Builder { private List<SecretKey> ikms = new ArrayList<>(); private List<SecretKey> salts = new ArrayList<>(); private Builder() {} /** * Builds an {@code Extract} object from the current state of the * {@code Builder}. * * @return an immutable {@code Extract} object */ public Extract extractOnly() { return new Extract(ikms, salts); } /** * Builds an {@code ExtractThenExpand} object from the current state of * the {@code Builder}. * * @implNote HKDF implementations will enforce that the length * is not greater than 255 * HMAC length. HKDF implementations * will also enforce that a {code null} info value is treated as * zero-length byte array. * * @param info * the optional context and application specific information * (may be {@code null}); the byte array is cloned to prevent * subsequent modification * @param length * the length of the output keying material (must be greater * than 0) * * @return an immutable {@code ExtractThenExpand} object * * @throws IllegalArgumentException * if {@code length} is not greater than 0 */ public ExtractThenExpand thenExpand(byte[] info, int length) { return new ExtractThenExpand( extractOnly(), info, length); } /** * Adds input keying material (IKM) to the builder. * <p> * Users may call {@code addIKM} multiple times when the input keying * material value is to be assembled piece-meal or if part of the IKM is * to be supplied by a hardware crypto device. The {@code ikms()} * method of the {@code Extract} or {@code ExtractThenExpand} object * that is subsequently built returns the assembled input keying * material as a list of {@code SecretKey} objects. * * @param ikm * the input keying material (IKM) value * * @return this builder * * @throws NullPointerException * if the {@code ikm} argument is null */ public Builder addIKM(SecretKey ikm) { Objects.requireNonNull(ikm, "ikm must not be null"); ikms.add(ikm); return this; } /** * Adds input keying material (IKM) to the builder. Note that an * {@code ikm} byte array of length zero will be discarded. * <p> * Users may call {@code addIKM} multiple times when the input keying * material value is to be assembled piece-meal or if part of the IKM is * to be supplied by a hardware crypto device. The {@code ikms()} * method of the {@code Extract} or {@code ExtractThenExpand} object * that is subsequently built returns the assembled input keying * material as a list of {@code SecretKey} objects. * * @param ikm * the input keying material (IKM) value; the {@code ikm} * byte array will be converted to a {@code SecretKeySpec}, * which means that the byte array will be cloned inside the * {@code SecretKeySpec} constructor * * @return this builder * * @throws NullPointerException * if the {@code ikm} argument is null */ public Builder addIKM(byte[] ikm) { Objects.requireNonNull(ikm, "ikm must not be null"); if (ikm.length != 0) { return addIKM(new SecretKeySpec(ikm, "Generic")); } else { return this; } } /** * Adds a salt to the builder. * <p> * Users may call {@code addSalt} multiple times when the salt value is * to be assembled piece-meal or if part of the salt is to be supplied * by a hardware crypto device. The {@code salts()} method of the * {@code Extract} or {@code ExtractThenExpand} object that is * subsequently built returns the assembled salt as a list of * {@code SecretKey} objects. * * @param salt * the salt value * * @return this builder * * @throws NullPointerException * if the {@code salt} is null */ public Builder addSalt(SecretKey salt) { Objects.requireNonNull(salt, "salt must not be null"); salts.add(salt); return this; } /** * Adds a salt to the builder. Note that a {@code salt} byte array of * length zero will be discarded. * <p> * Users may call {@code addSalt} multiple times when the salt value is * to be assembled piece-meal or if part of the salt is to be supplied * by a hardware crypto device. The {@code salts()} method of the * {@code Extract} or {@code ExtractThenExpand} object that is * subsequently built returns the assembled salt as a list of * {@code SecretKey} objects. * * @param salt * the salt value; the {@code salt} byte array will be * converted to a {@code SecretKeySpec}, which means that the * byte array will be cloned inside the {@code SecretKeySpec} * constructor * * @return this builder * * @throws NullPointerException * if the {@code salt} is null */ public Builder addSalt(byte[] salt) { Objects.requireNonNull(salt, "salt must not be null"); if (salt.length != 0) { return addSalt(new SecretKeySpec(salt, "Generic")); } else { return this; } } } /** * Returns a {@code Builder} for building {@code Extract} and * {@code ExtractThenExpand} objects. * * @return a new {@code Builder} */ static Builder ofExtract() { return new Builder(); } /** * Creates an {@code Expand} object. * * @implNote HKDF implementations will enforce that the length is * not greater than 255 * HMAC length. Implementations will also * enforce that the prk argument is at least as many bytes as the * HMAC length. Implementations will also enforce that a {code null} * info value is treated as zero-length byte array. * * @param prk * the pseudorandom key (PRK); must not be {@code null} * @param info * the optional context and application specific information (may be * {@code null}); the byte array is cloned to prevent subsequent * modification * @param length * the length of the output keying material (must be greater than * 0) * * @return an {@code Expand} object * * @throws NullPointerException * if the {@code prk} argument is {@code null} * @throws IllegalArgumentException * if {@code length} is not greater than 0 */ static Expand expandOnly(SecretKey prk, byte[] info, int length) { if (prk == null) { throw new NullPointerException("prk must not be null"); } return new Expand(prk, info, length); } /** * Defines the input parameters of an Extract operation as defined in <a * href="http://tools.ietf.org/html/rfc5869">RFC 5869</a>. */ final class Extract implements HKDFParameterSpec { // HKDF-Extract(salt, IKM) -> PRK private final List<SecretKey> ikms; private final List<SecretKey> salts; private Extract(List<SecretKey> ikms, List<SecretKey> salts) { this.ikms = List.copyOf(ikms); this.salts = List.copyOf(salts); } /** * Returns an unmodifiable {@code List} of input keying material values * in the order they were added. Returns an empty list if there are no * input keying material values. * <p> * Input keying material values added by {@link Builder#addIKM(byte[])} * are converted to a {@code SecretKeySpec} object. Empty arrays are * discarded. * * @implNote An HKDF implementation should concatenate the input * keying materials into a single value to be used in * HKDF-Extract. * * @return the unmodifiable {@code List} of input keying material * values */ public List<SecretKey> ikms() { return ikms; } /** * Returns an unmodifiable {@code List} of salt values in the order they * were added. Returns an empty list if there are no salt values. * <p> * Salt values added by {@link Builder#addSalt(byte[])} are converted to * a {@code SecretKeySpec} object. Empty arrays are discarded. * * @implNote An HKDF implementation should concatenate the salts * into a single value to be used in HKDF-Extract. * * @return the unmodifiable {@code List} of salt values */ public List<SecretKey> salts() { return salts; } } /** * Defines the input parameters of an Expand operation as defined in <a * href="http://tools.ietf.org/html/rfc5869">RFC 5869</a>. */ final class Expand implements HKDFParameterSpec { // HKDF-Expand(PRK, info, L) -> OKM private final SecretKey prk; private final byte[] info; private final int length; /** * Constructor that may be used to initialize an {@code Expand} object * * @param prk * the pseudorandom key (PRK); in the case of * {@code ExtractThenExpand}, the {@code prk} argument may be * {@null} since the output of extract phase is used * @param info * the optional context and application specific information * (may be {@code null}); the byte array is cloned to prevent * subsequent modification * @param length * the length of the output keying material * * @throws IllegalArgumentException * if {@code length} not greater than 0 */ private Expand(SecretKey prk, byte[] info, int length) { // a null prk argument could be indicative of ExtractThenExpand this.prk = prk; this.info = (info == null) ? null : info.clone(); if (!(length > 0)) { throw new IllegalArgumentException("length must be > 0"); } this.length = length; } /** * Returns the pseudorandom key (PRK). * * @return the pseudorandom key */ public SecretKey prk() { return prk; } /** * Returns the optional context and application specific information. * * @return a clone of the optional context and application specific * information, or {@code null} if not specified */ public byte[] info() { return (info == null) ? null : info.clone(); } /** * Returns the length of the output keying material. * * @return the length of the output keying material */ public int length() { return length; } } /** * Defines the input parameters of an Extract-then-Expand operation as * defined in <a href="http://tools.ietf.org/html/rfc5869">RFC 5869</a>. */ final class ExtractThenExpand implements HKDFParameterSpec { private final Extract ext; private final Expand exp; /** * Constructor that may be used to initialize an * {@code ExtractThenExpand} object * * @param ext * a pre-generated {@code Extract} * @param info * the optional context and application specific information * (may be {@code null}); the byte array is cloned to prevent * subsequent modification * @param length * the length of the output keying material * * @throws IllegalArgumentException * if {@code length} is not greater than 0 */ private ExtractThenExpand(Extract ext, byte[] info, int length) { Objects.requireNonNull(ext, "Extract object must not be null"); this.ext = ext; // - null prk argument is ok here (it's a signal) // - {@code Expand} constructor can deal with a null info // - length is checked in {@code Expand} constructor this.exp = new Expand(null, info, length); } /** * Returns an unmodifiable {@code List} of input keying material values * in the order they were added. Returns an empty list if there are no * input keying material values. * <p> * Input keying material values added by {@link Builder#addIKM(byte[])} * are converted to a {@code SecretKeySpec} object. Empty arrays are * discarded. * * @implNote An HKDF implementation should concatenate the input * keying materials into a single value to be used in the * HKDF-Extract phase. * * @return the unmodifiable {@code List} of input keying material * values */ public List<SecretKey> ikms() { return ext.ikms(); } /** * Returns an unmodifiable {@code List} of salt values in the order they * were added. Returns an empty list if there are no salt values. * <p> * Salt values added by {@link Builder#addSalt(byte[])} are converted to * a {@code SecretKeySpec} object. Empty arrays are discarded. * * @implNote An HKDF implementation should concatenate the salts * into a single value to be used in the HKDF-Extract phase. * * @return the unmodifiable {@code List} of salt values * */ public List<SecretKey> salts() { return ext.salts(); } /** * Returns the optional context and application specific information. * * @return a clone of the optional context and application specific * information, or {@code null} if not specified */ public byte[] info() { return exp.info(); } /** * Returns the length of the output keying material. * * @return the length of the output keying material */ public int length() { return exp.length(); } } }