mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8353578: Refactor existing usage of internal HKDF impl to use the KDF API
Co-authored-by: Kevin Driver <kdriver@openjdk.org> Reviewed-by: djelinski, wetmore, mullan, kdriver, weijun
This commit is contained in:
parent
10dcdf1b47
commit
4c0a0ab6bc
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 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
|
||||
@ -37,11 +37,13 @@ import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
import sun.security.ssl.HKDF;
|
||||
import sun.security.util.*;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
// Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html,
|
||||
// without the AuthEncap and AuthDecap functions
|
||||
public class DHKEM implements KEMSpi {
|
||||
@ -153,19 +155,19 @@ public class DHKEM implements KEMSpi {
|
||||
private enum Params {
|
||||
|
||||
P256(0x10, 32, 32, 2 * 32 + 1,
|
||||
"ECDH", "EC", CurveDB.P_256, "SHA-256"),
|
||||
"ECDH", "EC", CurveDB.P_256, "HKDF-SHA256"),
|
||||
|
||||
P384(0x11, 48, 48, 2 * 48 + 1,
|
||||
"ECDH", "EC", CurveDB.P_384, "SHA-384"),
|
||||
"ECDH", "EC", CurveDB.P_384, "HKDF-SHA384"),
|
||||
|
||||
P521(0x12, 64, 66, 2 * 66 + 1,
|
||||
"ECDH", "EC", CurveDB.P_521, "SHA-512"),
|
||||
"ECDH", "EC", CurveDB.P_521, "HKDF-SHA512"),
|
||||
|
||||
X25519(0x20, 32, 32, 32,
|
||||
"XDH", "XDH", NamedParameterSpec.X25519, "SHA-256"),
|
||||
"XDH", "XDH", NamedParameterSpec.X25519, "HKDF-SHA256"),
|
||||
|
||||
X448(0x21, 64, 56, 56,
|
||||
"XDH", "XDH", NamedParameterSpec.X448, "SHA-512"),
|
||||
"XDH", "XDH", NamedParameterSpec.X448, "HKDF-SHA512"),
|
||||
;
|
||||
|
||||
private final int kem_id;
|
||||
@ -247,10 +249,17 @@ public class DHKEM implements KEMSpi {
|
||||
|
||||
private byte[] ExtractAndExpand(byte[] dh, byte[] kem_context)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
HKDF kdf = new HKDF(hkdfAlgorithm);
|
||||
SecretKey eae_prk = LabeledExtract(kdf, suiteId, null, EAE_PRK, dh);
|
||||
return LabeledExpand(kdf, suiteId, eae_prk, SHARED_SECRET,
|
||||
kem_context, Nsecret);
|
||||
KDF hkdf = KDF.getInstance(hkdfAlgorithm);
|
||||
SecretKey eae_prk = LabeledExtract(hkdf, suiteId, EAE_PRK, dh);
|
||||
try {
|
||||
return LabeledExpand(hkdf, suiteId, eae_prk, SHARED_SECRET,
|
||||
kem_context, Nsecret);
|
||||
} finally {
|
||||
if (eae_prk instanceof SecretKeySpec s) {
|
||||
SharedSecrets.getJavaxCryptoSpecAccess()
|
||||
.clearSecretKeySpec(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PublicKey getPublicKey(PrivateKey sk)
|
||||
@ -277,31 +286,41 @@ public class DHKEM implements KEMSpi {
|
||||
|
||||
// For KAT tests only. See RFC9180DeriveKeyPairSR.
|
||||
public KeyPair deriveKeyPair(byte[] ikm) throws Exception {
|
||||
HKDF kdf = new HKDF(hkdfAlgorithm);
|
||||
SecretKey dkp_prk = LabeledExtract(kdf, suiteId, null, DKP_PRK, ikm);
|
||||
if (isEC()) {
|
||||
NamedCurve curve = (NamedCurve) spec;
|
||||
BigInteger sk = BigInteger.ZERO;
|
||||
int counter = 0;
|
||||
while (sk.signum() == 0 || sk.compareTo(curve.getOrder()) >= 0) {
|
||||
if (counter > 255) {
|
||||
throw new RuntimeException();
|
||||
KDF hkdf = KDF.getInstance(hkdfAlgorithm);
|
||||
SecretKey dkp_prk = LabeledExtract(hkdf, suiteId, DKP_PRK, ikm);
|
||||
try {
|
||||
if (isEC()) {
|
||||
NamedCurve curve = (NamedCurve) spec;
|
||||
BigInteger sk = BigInteger.ZERO;
|
||||
int counter = 0;
|
||||
while (sk.signum() == 0 ||
|
||||
sk.compareTo(curve.getOrder()) >= 0) {
|
||||
if (counter > 255) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
byte[] bytes = LabeledExpand(hkdf, suiteId, dkp_prk,
|
||||
CANDIDATE, I2OSP(counter, 1), Nsk);
|
||||
// bitmask is defined to be 0xFF for P-256 and P-384,
|
||||
// and 0x01 for P-521
|
||||
if (this == Params.P521) {
|
||||
bytes[0] = (byte) (bytes[0] & 0x01);
|
||||
}
|
||||
sk = new BigInteger(1, (bytes));
|
||||
counter = counter + 1;
|
||||
}
|
||||
byte[] bytes = LabeledExpand(kdf, suiteId, dkp_prk,
|
||||
CANDIDATE, I2OSP(counter, 1), Nsk);
|
||||
// bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521
|
||||
if (this == Params.P521) {
|
||||
bytes[0] = (byte) (bytes[0] & 0x01);
|
||||
}
|
||||
sk = new BigInteger(1, (bytes));
|
||||
counter = counter + 1;
|
||||
PrivateKey k = DeserializePrivateKey(sk.toByteArray());
|
||||
return new KeyPair(getPublicKey(k), k);
|
||||
} else {
|
||||
byte[] sk = LabeledExpand(hkdf, suiteId, dkp_prk, SK, EMPTY,
|
||||
Nsk);
|
||||
PrivateKey k = DeserializePrivateKey(sk);
|
||||
return new KeyPair(getPublicKey(k), k);
|
||||
}
|
||||
} finally {
|
||||
if (dkp_prk instanceof SecretKeySpec s) {
|
||||
SharedSecrets.getJavaxCryptoSpecAccess()
|
||||
.clearSecretKeySpec(s);
|
||||
}
|
||||
PrivateKey k = DeserializePrivateKey(sk.toByteArray());
|
||||
return new KeyPair(getPublicKey(k), k);
|
||||
} else {
|
||||
byte[] sk = LabeledExpand(kdf, suiteId, dkp_prk, SK, EMPTY, Nsk);
|
||||
PrivateKey k = DeserializePrivateKey(sk);
|
||||
return new KeyPair(getPublicKey(k), k);
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,18 +399,32 @@ public class DHKEM implements KEMSpi {
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretKey LabeledExtract(HKDF kdf, byte[] suite_id,
|
||||
byte[] salt, byte[] label, byte[] ikm) throws InvalidKeyException {
|
||||
return kdf.extract(salt,
|
||||
new SecretKeySpec(concat(HPKE_V1, suite_id, label, ikm), "IKM"),
|
||||
"HKDF-PRK");
|
||||
private static SecretKey LabeledExtract(KDF hkdf, byte[] suite_id,
|
||||
byte[] label, byte[] ikm) throws InvalidKeyException {
|
||||
SecretKeySpec s = new SecretKeySpec(concat(HPKE_V1, suite_id, label,
|
||||
ikm), "IKM");
|
||||
try {
|
||||
HKDFParameterSpec spec =
|
||||
HKDFParameterSpec.ofExtract().addIKM(s).extractOnly();
|
||||
return hkdf.deriveKey("Generic", spec);
|
||||
} catch (InvalidAlgorithmParameterException |
|
||||
NoSuchAlgorithmException e) {
|
||||
throw new InvalidKeyException(e.getMessage(), e);
|
||||
} finally {
|
||||
SharedSecrets.getJavaxCryptoSpecAccess().clearSecretKeySpec(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] LabeledExpand(HKDF kdf, byte[] suite_id,
|
||||
private static byte[] LabeledExpand(KDF hkdf, byte[] suite_id,
|
||||
SecretKey prk, byte[] label, byte[] info, int L)
|
||||
throws InvalidKeyException {
|
||||
byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1,
|
||||
suite_id, label, info);
|
||||
return kdf.expand(prk, labeled_info, L, "NONE").getEncoded();
|
||||
byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1, suite_id, label,
|
||||
info);
|
||||
try {
|
||||
return hkdf.deriveData(HKDFParameterSpec.expandOnly(
|
||||
prk, labeled_info, L));
|
||||
} catch (InvalidAlgorithmParameterException iape) {
|
||||
throw new InvalidKeyException(iape.getMessage(), iape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -77,22 +77,20 @@ final class ChangeCipherSpec {
|
||||
try {
|
||||
writeAuthenticator = Authenticator.valueOf(
|
||||
hc.negotiatedProtocol, ncs.macAlg,
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
tkd.deriveKey(hc.sslConfig.isClientMode ?
|
||||
"clientMacKey" : "serverMacKey"));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
// unlikely
|
||||
throw new SSLException("Algorithm missing: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
SecretKey writeKey =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"clientWriteKey" : "serverWriteKey");
|
||||
SecretKey writeIv =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"clientWriteIv" : "serverWriteIv");
|
||||
SecretKey writeKey = tkd.deriveKey(hc.sslConfig.isClientMode ?
|
||||
"clientWriteKey" : "serverWriteKey");
|
||||
byte[] writeIv = tkd.deriveData(hc.sslConfig.isClientMode ?
|
||||
"clientWriteIv" : "serverWriteIv");
|
||||
IvParameterSpec iv = (writeIv == null) ? null :
|
||||
new IvParameterSpec(writeIv.getEncoded());
|
||||
new IvParameterSpec(writeIv);
|
||||
|
||||
SSLWriteCipher writeCipher;
|
||||
try {
|
||||
writeCipher = ncs.bulkCipher.createWriteCipher(
|
||||
@ -173,7 +171,7 @@ final class ChangeCipherSpec {
|
||||
try {
|
||||
readAuthenticator = Authenticator.valueOf(
|
||||
hc.negotiatedProtocol, ncs.macAlg,
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
tkd.deriveKey(hc.sslConfig.isClientMode ?
|
||||
"serverMacKey" : "clientMacKey"));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
// unlikely
|
||||
@ -181,14 +179,12 @@ final class ChangeCipherSpec {
|
||||
}
|
||||
}
|
||||
|
||||
SecretKey readKey =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"serverWriteKey" : "clientWriteKey");
|
||||
SecretKey readIv =
|
||||
tkd.getTrafficKey(hc.sslConfig.isClientMode ?
|
||||
"serverWriteIv" : "clientWriteIv");
|
||||
SecretKey readKey = tkd.deriveKey(hc.sslConfig.isClientMode ?
|
||||
"serverWriteKey" : "clientWriteKey");
|
||||
byte[] readIv = tkd.deriveData(hc.sslConfig.isClientMode ?
|
||||
"serverWriteIv" : "clientWriteIv");
|
||||
IvParameterSpec iv = (readIv == null) ? null :
|
||||
new IvParameterSpec(readIv.getEncoded());
|
||||
new IvParameterSpec(readIv);
|
||||
SSLReadCipher readCipher;
|
||||
try {
|
||||
readCipher = ncs.bulkCipher.createReadCipher(
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 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
|
||||
@ -1199,11 +1199,13 @@ enum CipherSuite {
|
||||
final String name;
|
||||
final int hashLength;
|
||||
final int blockSize;
|
||||
final String hkdfAlgorithm;
|
||||
|
||||
HashAlg(String hashAlg, int hashLength, int blockSize) {
|
||||
this.name = hashAlg;
|
||||
this.hashLength = hashLength;
|
||||
this.blockSize = blockSize;
|
||||
this.hkdfAlgorithm = "HKDF-" + hashAlg.replace("-", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -206,8 +206,7 @@ final class DHClientKeyExchange {
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
@ -302,8 +301,7 @@ final class DHClientKeyExchange {
|
||||
|
||||
// update the states
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -218,8 +218,7 @@ final class ECDHClientKeyExchange {
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
@ -338,8 +337,7 @@ final class ECDHClientKeyExchange {
|
||||
|
||||
// update the states
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
@ -418,8 +416,7 @@ final class ECDHClientKeyExchange {
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
@ -522,8 +519,7 @@ final class ECDHClientKeyExchange {
|
||||
|
||||
// update the states
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
SSLTrafficKeyDerivation kd =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -32,14 +32,14 @@ import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
|
||||
import jdk.internal.event.EventHelper;
|
||||
@ -47,11 +47,11 @@ import jdk.internal.event.TLSHandshakeEvent;
|
||||
import sun.security.internal.spec.TlsPrfParameterSpec;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
import static sun.security.ssl.CipherSuite.HashAlg.H_NONE;
|
||||
import sun.security.ssl.SSLBasicKeyDerivation.SecretSizeSpec;
|
||||
import sun.security.ssl.SSLCipher.SSLReadCipher;
|
||||
import sun.security.ssl.SSLCipher.SSLWriteCipher;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.util.HexDumpEncoder;
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
/**
|
||||
* Pack of the Finished handshake message.
|
||||
@ -338,12 +338,8 @@ final class Finished {
|
||||
SecretKey secret = isValidation ?
|
||||
context.baseReadSecret : context.baseWriteSecret;
|
||||
SSLBasicKeyDerivation kdf = new SSLBasicKeyDerivation(
|
||||
secret, hashAlg.name,
|
||||
hkdfLabel, hkdfContext, hashAlg.hashLength);
|
||||
AlgorithmParameterSpec keySpec =
|
||||
new SecretSizeSpec(hashAlg.hashLength);
|
||||
SecretKey finishedSecret =
|
||||
kdf.deriveKey("TlsFinishedSecret", keySpec);
|
||||
secret, hashAlg, hkdfLabel, hkdfContext);
|
||||
SecretKey finishedSecret = kdf.deriveKey("TlsFinishedSecret");
|
||||
|
||||
String hmacAlg =
|
||||
"Hmac" + hashAlg.name.replace("-", "");
|
||||
@ -354,6 +350,8 @@ final class Finished {
|
||||
} catch (NoSuchAlgorithmException |InvalidKeyException ex) {
|
||||
throw new ProviderException(
|
||||
"Failed to generate verify_data", ex);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(finishedSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -717,17 +715,14 @@ final class Finished {
|
||||
|
||||
try {
|
||||
// update the application traffic read keys.
|
||||
SecretKey writeSecret = kd.deriveKey(
|
||||
"TlsClientAppTrafficSecret", null);
|
||||
SecretKey writeSecret =
|
||||
kd.deriveKey("TlsClientAppTrafficSecret");
|
||||
|
||||
SSLKeyDerivation writeKD =
|
||||
kdg.createKeyDerivation(chc, writeSecret);
|
||||
SecretKey writeKey = writeKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey writeIvSecret = writeKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey writeKey = writeKD.deriveKey("TlsKey");
|
||||
IvParameterSpec writeIv =
|
||||
new IvParameterSpec(writeIvSecret.getEncoded());
|
||||
new IvParameterSpec(writeKD.deriveData("TlsIv"));
|
||||
SSLWriteCipher writeCipher =
|
||||
chc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
|
||||
Authenticator.valueOf(chc.negotiatedProtocol),
|
||||
@ -754,7 +749,7 @@ final class Finished {
|
||||
// it can be used after the handshake is completed.
|
||||
SSLSecretDerivation sd = ((SSLSecretDerivation) kd).forContext(chc);
|
||||
SecretKey resumptionMasterSecret = sd.deriveKey(
|
||||
"TlsResumptionMasterSecret", null);
|
||||
"TlsResumptionMasterSecret");
|
||||
chc.handshakeSession.setResumptionMasterSecret(
|
||||
resumptionMasterSecret);
|
||||
|
||||
@ -803,33 +798,29 @@ final class Finished {
|
||||
shc.negotiatedProtocol);
|
||||
}
|
||||
|
||||
// derive salt secret
|
||||
SecretKey saltSecret = null;
|
||||
try {
|
||||
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
|
||||
// derive salt secret
|
||||
saltSecret = kd.deriveKey("TlsSaltSecret");
|
||||
|
||||
// derive application secrets
|
||||
HashAlg hashAlg = shc.negotiatedCipherSuite.hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKeySpec sharedSecret =
|
||||
new SecretKeySpec(zeros, "TlsZeroSecret");
|
||||
SecretKey masterSecret =
|
||||
hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret");
|
||||
|
||||
SecretKey masterSecret = hkdf.deriveKey("TlsMasterSecret",
|
||||
HKDFParameterSpec.ofExtract().addSalt(saltSecret)
|
||||
.addIKM(zeros).extractOnly());
|
||||
SSLKeyDerivation secretKD =
|
||||
new SSLSecretDerivation(shc, masterSecret);
|
||||
|
||||
// update the handshake traffic write keys.
|
||||
SecretKey writeSecret = secretKD.deriveKey(
|
||||
"TlsServerAppTrafficSecret", null);
|
||||
"TlsServerAppTrafficSecret");
|
||||
SSLKeyDerivation writeKD =
|
||||
kdg.createKeyDerivation(shc, writeSecret);
|
||||
SecretKey writeKey = writeKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey writeIvSecret = writeKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey writeKey = writeKD.deriveKey("TlsKey");
|
||||
IvParameterSpec writeIv =
|
||||
new IvParameterSpec(writeIvSecret.getEncoded());
|
||||
new IvParameterSpec(writeKD.deriveData("TlsIv"));
|
||||
SSLWriteCipher writeCipher =
|
||||
shc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
|
||||
Authenticator.valueOf(shc.negotiatedProtocol),
|
||||
@ -852,6 +843,8 @@ final class Finished {
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failure to derive application secrets", gse);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(saltSecret);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -960,34 +953,31 @@ final class Finished {
|
||||
engineGetClientSessionContext()).
|
||||
put(chc.handshakeSession);
|
||||
}
|
||||
|
||||
// derive salt secret
|
||||
SecretKey saltSecret = null;
|
||||
try {
|
||||
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
|
||||
// derive salt secret
|
||||
saltSecret = kd.deriveKey("TlsSaltSecret");
|
||||
|
||||
// derive application secrets
|
||||
HashAlg hashAlg = chc.negotiatedCipherSuite.hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKeySpec sharedSecret =
|
||||
new SecretKeySpec(zeros, "TlsZeroSecret");
|
||||
SecretKey masterSecret =
|
||||
hkdf.extract(saltSecret, sharedSecret, "TlsMasterSecret");
|
||||
SecretKey masterSecret = hkdf.deriveKey("TlsMasterSecret",
|
||||
HKDFParameterSpec.ofExtract()
|
||||
.addSalt(saltSecret)
|
||||
.addIKM(zeros).extractOnly());
|
||||
|
||||
SSLKeyDerivation secretKD =
|
||||
new SSLSecretDerivation(chc, masterSecret);
|
||||
|
||||
// update the handshake traffic read keys.
|
||||
SecretKey readSecret = secretKD.deriveKey(
|
||||
"TlsServerAppTrafficSecret", null);
|
||||
"TlsServerAppTrafficSecret");
|
||||
SSLKeyDerivation writeKD =
|
||||
kdg.createKeyDerivation(chc, readSecret);
|
||||
SecretKey readKey = writeKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey readIvSecret = writeKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey readKey = writeKD.deriveKey("TlsKey");
|
||||
IvParameterSpec readIv =
|
||||
new IvParameterSpec(readIvSecret.getEncoded());
|
||||
new IvParameterSpec(writeKD.deriveData("TlsIv"));
|
||||
SSLReadCipher readCipher =
|
||||
chc.negotiatedCipherSuite.bulkCipher.createReadCipher(
|
||||
Authenticator.valueOf(chc.negotiatedProtocol),
|
||||
@ -1009,6 +999,8 @@ final class Finished {
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw chc.conContext.fatal(Alert.INTERNAL_ERROR,
|
||||
"Failure to derive application secrets", gse);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(saltSecret);
|
||||
}
|
||||
|
||||
//
|
||||
@ -1084,16 +1076,13 @@ final class Finished {
|
||||
try {
|
||||
// update the application traffic read keys.
|
||||
SecretKey readSecret = kd.deriveKey(
|
||||
"TlsClientAppTrafficSecret", null);
|
||||
"TlsClientAppTrafficSecret");
|
||||
|
||||
SSLKeyDerivation readKD =
|
||||
kdg.createKeyDerivation(shc, readSecret);
|
||||
SecretKey readKey = readKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey readIvSecret = readKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey readKey = readKD.deriveKey("TlsKey");
|
||||
IvParameterSpec readIv =
|
||||
new IvParameterSpec(readIvSecret.getEncoded());
|
||||
new IvParameterSpec(readKD.deriveData("TlsIv"));
|
||||
SSLReadCipher readCipher =
|
||||
shc.negotiatedCipherSuite.bulkCipher.createReadCipher(
|
||||
Authenticator.valueOf(shc.negotiatedProtocol),
|
||||
@ -1115,8 +1104,8 @@ final class Finished {
|
||||
shc.handshakeHash.update();
|
||||
SSLSecretDerivation sd =
|
||||
((SSLSecretDerivation)kd).forContext(shc);
|
||||
SecretKey resumptionMasterSecret = sd.deriveKey(
|
||||
"TlsResumptionMasterSecret", null);
|
||||
SecretKey resumptionMasterSecret =
|
||||
sd.deriveKey("TlsResumptionMasterSecret");
|
||||
shc.handshakeSession.setResumptionMasterSecret(
|
||||
resumptionMasterSecret);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
|
||||
@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, 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.
|
||||
*/
|
||||
public final class HKDF {
|
||||
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.
|
||||
*/
|
||||
public HKDF(String hashAlg) throws NoSuchAlgorithmException {
|
||||
Objects.requireNonNull(hashAlg,
|
||||
"Must provide underlying HKDF Digest algorithm.");
|
||||
String 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.
|
||||
*/
|
||||
public 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.
|
||||
*/
|
||||
public 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.
|
||||
*/
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -24,15 +24,18 @@
|
||||
*/
|
||||
package sun.security.ssl;
|
||||
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.KeyAgreement;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
/**
|
||||
* A common class for creating various KeyDerivation types.
|
||||
@ -55,29 +58,26 @@ public class KAKeyDerivation implements SSLKeyDerivation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
public SecretKey deriveKey(String type) throws IOException {
|
||||
if (!context.negotiatedProtocol.useTLS13PlusSpec()) {
|
||||
return t12DeriveKey(algorithm, params);
|
||||
return t12DeriveKey();
|
||||
} else {
|
||||
return t13DeriveKey(algorithm, params);
|
||||
return t13DeriveKey(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the TLSv1-1.2 objects, which don't use the HKDF algorithms.
|
||||
*/
|
||||
private SecretKey t12DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
private SecretKey t12DeriveKey() throws IOException {
|
||||
SecretKey preMasterSecret = null;
|
||||
try {
|
||||
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey preMasterSecret
|
||||
= ka.generateSecret("TlsPremasterSecret");
|
||||
SSLMasterKeyDerivation mskd
|
||||
= SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
preMasterSecret = ka.generateSecret("TlsPremasterSecret");
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(context.negotiatedProtocol);
|
||||
if (mskd == null) {
|
||||
// unlikely
|
||||
throw new SSLHandshakeException(
|
||||
@ -86,45 +86,55 @@ public class KAKeyDerivation implements SSLKeyDerivation {
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
return kd.deriveKey("MasterSecret");
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(preMasterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the TLSv1.3 objects, which use the HKDF algorithms.
|
||||
*/
|
||||
private SecretKey t13DeriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
private SecretKey t13DeriveKey(String type)
|
||||
throws IOException {
|
||||
SecretKey sharedSecret = null;
|
||||
SecretKey earlySecret = null;
|
||||
SecretKey saltSecret = null;
|
||||
try {
|
||||
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
|
||||
ka.init(localPrivateKey);
|
||||
ka.doPhase(peerPublicKey, true);
|
||||
SecretKey sharedSecret
|
||||
= ka.generateSecret("TlsPremasterSecret");
|
||||
sharedSecret = ka.generateSecret("TlsPremasterSecret");
|
||||
|
||||
CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg;
|
||||
SSLKeyDerivation kd = context.handshakeKeyDerivation;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
if (kd == null) { // No PSK is in use.
|
||||
// If PSK is not in use Early Secret will still be
|
||||
// If PSK is not in use, Early Secret will still be
|
||||
// HKDF-Extract(0, 0).
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKeySpec ikm
|
||||
= new SecretKeySpec(zeros, "TlsPreSharedSecret");
|
||||
SecretKey earlySecret
|
||||
= hkdf.extract(zeros, ikm, "TlsEarlySecret");
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
earlySecret = hkdf.deriveKey("TlsEarlySecret",
|
||||
HKDFParameterSpec.ofExtract().addSalt(zeros)
|
||||
.addIKM(zeros).extractOnly());
|
||||
kd = new SSLSecretDerivation(context, earlySecret);
|
||||
}
|
||||
|
||||
// derive salt secret
|
||||
SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null);
|
||||
saltSecret = kd.deriveKey("TlsSaltSecret");
|
||||
|
||||
// derive handshake secret
|
||||
return hkdf.extract(saltSecret, sharedSecret, algorithm);
|
||||
// NOTE: do not reuse the HKDF object for "TlsEarlySecret" for
|
||||
// the handshake secret key derivation (below) as it may not
|
||||
// work with the "sharedSecret" obj.
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
return hkdf.deriveKey(type, HKDFParameterSpec.ofExtract()
|
||||
.addSalt(saltSecret).addIKM(sharedSecret).extractOnly());
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(sharedSecret, earlySecret, saltSecret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -214,11 +214,11 @@ final class KeyUpdate {
|
||||
Alert.INTERNAL_ERROR, "no key derivation");
|
||||
}
|
||||
|
||||
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
|
||||
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1");
|
||||
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
|
||||
SecretKey key = kd.deriveKey("TlsKey", null);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(
|
||||
kd.deriveKey("TlsIv", null).getEncoded());
|
||||
SecretKey key = kd.deriveKey("TlsKey");
|
||||
IvParameterSpec ivSpec =
|
||||
new IvParameterSpec(kd.deriveData("TlsIv"));
|
||||
try {
|
||||
SSLReadCipher rc =
|
||||
hc.negotiatedCipherSuite.bulkCipher.createReadCipher(
|
||||
@ -293,11 +293,11 @@ final class KeyUpdate {
|
||||
Alert.INTERNAL_ERROR, "no key derivation");
|
||||
}
|
||||
|
||||
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
|
||||
SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1");
|
||||
SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
|
||||
SecretKey key = kd.deriveKey("TlsKey", null);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(
|
||||
kd.deriveKey("TlsIv", null).getEncoded());
|
||||
SecretKey key = kd.deriveKey("TlsKey");
|
||||
IvParameterSpec ivSpec =
|
||||
new IvParameterSpec(kd.deriveData("TlsIv"));
|
||||
|
||||
SSLWriteCipher wc;
|
||||
try {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -31,7 +31,10 @@ import java.security.SecureRandom;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeMode;
|
||||
import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
|
||||
@ -286,11 +289,16 @@ final class NewSessionTicket {
|
||||
private static SecretKey derivePreSharedKey(CipherSuite.HashAlg hashAlg,
|
||||
SecretKey resumptionMasterSecret, byte[] nonce) throws IOException {
|
||||
try {
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
|
||||
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
|
||||
"tls13 resumption".getBytes(), nonce, hashAlg.hashLength);
|
||||
return hkdf.expand(resumptionMasterSecret, hkdfInfo,
|
||||
hashAlg.hashLength, "TlsPreSharedKey");
|
||||
// SSLSessionImpl.write() uses the PreSharedKey encoding for
|
||||
// the stateless session ticket; use SecretKeySpec instead of opaque
|
||||
// Key objects
|
||||
return new SecretKeySpec(hkdf.deriveData(
|
||||
HKDFParameterSpec.expandOnly(resumptionMasterSecret,
|
||||
hkdfInfo, hashAlg.hashLength)), "TlsPreSharedKey");
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not derive PSK", gse);
|
||||
}
|
||||
|
||||
@ -29,8 +29,10 @@ import java.nio.ByteBuffer;
|
||||
import java.security.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import javax.net.ssl.SSLProtocolException;
|
||||
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
|
||||
@ -40,6 +42,7 @@ import sun.security.ssl.SSLExtension.SSLExtensionSpec;
|
||||
import sun.security.ssl.SSLHandshake.HandshakeMessage;
|
||||
import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
|
||||
import sun.security.util.HexDumpEncoder;
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
import static sun.security.ssl.SSLExtension.*;
|
||||
|
||||
@ -559,11 +562,15 @@ final class PreSharedKeyExtension {
|
||||
}
|
||||
|
||||
SecretKey binderKey = deriveBinderKey(shc, psk, session);
|
||||
byte[] computedBinder =
|
||||
computeBinder(shc, binderKey, session, pskBinderHash);
|
||||
if (!MessageDigest.isEqual(binder, computedBinder)) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Incorrect PSK binder value");
|
||||
try {
|
||||
byte[] computedBinder =
|
||||
computeBinder(shc, binderKey, session, pskBinderHash);
|
||||
if (!MessageDigest.isEqual(binder, computedBinder)) {
|
||||
throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
|
||||
"Incorrect PSK binder value");
|
||||
}
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(binderKey);
|
||||
}
|
||||
}
|
||||
|
||||
@ -712,21 +719,25 @@ final class PreSharedKeyExtension {
|
||||
|
||||
SecretKey binderKey =
|
||||
deriveBinderKey(chc, psk, chc.resumingSession);
|
||||
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
||||
CHPreSharedKeySpec pskPrototype = createPskPrototype(
|
||||
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
|
||||
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
|
||||
try {
|
||||
ClientHelloMessage clientHello = (ClientHelloMessage)message;
|
||||
CHPreSharedKeySpec pskPrototype = createPskPrototype(
|
||||
chc.resumingSession.getSuite().hashAlg.hashLength, identities);
|
||||
HandshakeHash pskBinderHash = chc.handshakeHash.copy();
|
||||
|
||||
byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
|
||||
chc.resumingSession, chc, clientHello, pskPrototype);
|
||||
byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
|
||||
chc.resumingSession, chc, clientHello, pskPrototype);
|
||||
|
||||
List<byte[]> binders = new ArrayList<>();
|
||||
binders.add(binder);
|
||||
List<byte[]> binders = new ArrayList<>();
|
||||
binders.add(binder);
|
||||
|
||||
CHPreSharedKeySpec pskMessage =
|
||||
new CHPreSharedKeySpec(identities, binders);
|
||||
chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
|
||||
return pskMessage.getEncoded();
|
||||
CHPreSharedKeySpec pskMessage =
|
||||
new CHPreSharedKeySpec(identities, binders);
|
||||
chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
|
||||
return pskMessage.getEncoded();
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(binderKey);
|
||||
}
|
||||
}
|
||||
|
||||
private CHPreSharedKeySpec createPskPrototype(
|
||||
@ -780,12 +791,13 @@ final class PreSharedKeyExtension {
|
||||
SSLSessionImpl session, byte[] digest) throws IOException {
|
||||
try {
|
||||
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
byte[] label = ("tls13 finished").getBytes();
|
||||
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
|
||||
label, new byte[0], hashAlg.hashLength);
|
||||
SecretKey finishedKey = hkdf.expand(
|
||||
binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
|
||||
SecretKey finishedKey = hkdf.deriveKey("TlsBinderKey",
|
||||
HKDFParameterSpec.expandOnly(binderKey, hkdfInfo,
|
||||
hashAlg.hashLength));
|
||||
|
||||
String hmacAlg =
|
||||
"Hmac" + hashAlg.name.replace("-", "");
|
||||
@ -795,6 +807,8 @@ final class PreSharedKeyExtension {
|
||||
return hmac.doFinal(digest);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
|
||||
} finally {
|
||||
KeyUtil.destroySecretKeys(finishedKey);
|
||||
}
|
||||
} catch (GeneralSecurityException ex) {
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
|
||||
@ -805,16 +819,16 @@ final class PreSharedKeyExtension {
|
||||
SecretKey psk, SSLSessionImpl session) throws IOException {
|
||||
try {
|
||||
CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
|
||||
|
||||
byte[] label = ("tls13 res binder").getBytes();
|
||||
MessageDigest md = MessageDigest.getInstance(hashAlg.name);
|
||||
byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
|
||||
label, md.digest(new byte[0]), hashAlg.hashLength);
|
||||
return hkdf.expand(earlySecret,
|
||||
hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
HKDFParameterSpec spec = HKDFParameterSpec.ofExtract()
|
||||
.addSalt(zeros).addIKM(psk)
|
||||
.thenExpand(hkdfInfo, hashAlg.hashLength);
|
||||
return hkdf.deriveKey("TlsBinderKey", spec);
|
||||
} catch (GeneralSecurityException ex) {
|
||||
throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -208,8 +208,7 @@ final class RSAClientKeyExchange {
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
|
||||
// update the states
|
||||
chc.handshakeSession.setMasterSecret(masterSecret);
|
||||
@ -296,8 +295,7 @@ final class RSAClientKeyExchange {
|
||||
"Not supported key exchange type");
|
||||
} else {
|
||||
SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);
|
||||
SecretKey masterSecret =
|
||||
masterKD.deriveKey("MasterSecret", null);
|
||||
SecretKey masterSecret = masterKD.deriveKey("MasterSecret");
|
||||
|
||||
// update the states
|
||||
shc.handshakeSession.setMasterSecret(masterSecret);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -292,8 +292,7 @@ final class RSAKeyExchange {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
public SecretKey deriveKey(String typeNotUsed) throws IOException {
|
||||
SSLMasterKeyDerivation mskd =
|
||||
SSLMasterKeyDerivation.valueOf(
|
||||
context.negotiatedProtocol);
|
||||
@ -305,7 +304,7 @@ final class RSAKeyExchange {
|
||||
}
|
||||
SSLKeyDerivation kd = mskd.createKeyDerivation(
|
||||
context, preMasterSecret);
|
||||
return kd.deriveKey("MasterSecret", params);
|
||||
return kd.deriveKey("MasterSecret");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -28,29 +28,33 @@ package sun.security.ssl;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
|
||||
final class SSLBasicKeyDerivation implements SSLKeyDerivation {
|
||||
private final String hashAlg;
|
||||
private final String hkdfAlg;
|
||||
private final SecretKey secret;
|
||||
private final byte[] hkdfInfo;
|
||||
private final int keyLen;
|
||||
|
||||
SSLBasicKeyDerivation(SecretKey secret, String hashAlg,
|
||||
byte[] label, byte[] context, int length) {
|
||||
this.hashAlg = hashAlg.replace("-", "");
|
||||
SSLBasicKeyDerivation(SecretKey secret, HashAlg hashAlg, byte[] label,
|
||||
byte[] context) {
|
||||
this.hkdfAlg = hashAlg.hkdfAlgorithm;
|
||||
this.secret = secret;
|
||||
this.hkdfInfo = createHkdfInfo(label, context, length);
|
||||
this.hkdfInfo = createHkdfInfo(label, context, hashAlg.hashLength);
|
||||
this.keyLen = hashAlg.hashLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec keySpec) throws IOException {
|
||||
public SecretKey deriveKey(String type) throws IOException {
|
||||
try {
|
||||
HKDF hkdf = new HKDF(hashAlg);
|
||||
return hkdf.expand(secret, hkdfInfo,
|
||||
((SecretSizeSpec)keySpec).length, algorithm);
|
||||
KDF hkdf = KDF.getInstance(hkdfAlg);
|
||||
return hkdf.deriveKey(type,
|
||||
HKDFParameterSpec.expandOnly(secret, hkdfInfo, keyLen));
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
}
|
||||
@ -69,12 +73,4 @@ final class SSLBasicKeyDerivation implements SSLKeyDerivation {
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static class SecretSizeSpec implements AlgorithmParameterSpec {
|
||||
final int length;
|
||||
|
||||
SecretSizeSpec(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -30,6 +30,9 @@ import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
interface SSLKeyDerivation {
|
||||
SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException;
|
||||
SecretKey deriveKey(String purpose) throws IOException;
|
||||
|
||||
default byte[] deriveData(String purpose) throws IOException {
|
||||
throw new UnsupportedOperationException("No support for deriveData!");
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -84,9 +84,7 @@ enum SSLMasterKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
|
||||
public SecretKey deriveKey(String typeNotUsed) throws IOException {
|
||||
CipherSuite cipherSuite = context.negotiatedCipherSuite;
|
||||
ProtocolVersion protocolVersion = context.negotiatedProtocol;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -29,7 +29,9 @@ import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import sun.security.ssl.CipherSuite.HashAlg;
|
||||
|
||||
@ -87,9 +89,8 @@ final class SSLSecretDerivation implements SSLKeyDerivation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
SecretSchedule ks = SecretSchedule.valueOf(algorithm);
|
||||
public SecretKey deriveKey(String type) throws IOException {
|
||||
SecretSchedule ks = SecretSchedule.valueOf(type);
|
||||
try {
|
||||
byte[] expandContext;
|
||||
if (ks == SecretSchedule.TlsSaltSecret) {
|
||||
@ -102,16 +103,16 @@ final class SSLSecretDerivation implements SSLKeyDerivation {
|
||||
// get supported in the future.
|
||||
throw new SSLHandshakeException(
|
||||
"Unexpected unsupported hash algorithm: " +
|
||||
algorithm);
|
||||
hashAlg);
|
||||
}
|
||||
} else {
|
||||
expandContext = transcriptHash;
|
||||
}
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
byte[] hkdfInfo = createHkdfInfo(ks.label,
|
||||
expandContext, hashAlg.hashLength);
|
||||
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
return hkdf.expand(secret, hkdfInfo, hashAlg.hashLength, algorithm);
|
||||
return hkdf.deriveKey(type, HKDFParameterSpec.expandOnly(
|
||||
secret, hkdfInfo, hashAlg.hashLength));
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 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
|
||||
@ -37,7 +37,7 @@ import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSessionContext;
|
||||
|
||||
import sun.security.util.Cache;
|
||||
|
||||
import sun.security.util.KeyUtil;
|
||||
|
||||
/**
|
||||
* {@systemProperty jdk.tls.server.enableSessionTicketExtension} determines if the
|
||||
@ -197,11 +197,7 @@ final class SSLSessionContextImpl implements SSLSessionContext {
|
||||
SessionTicketExtension.StatelessKey k = entry.getValue();
|
||||
if (k.isInvalid(this)) {
|
||||
it.remove();
|
||||
try {
|
||||
k.key.destroy();
|
||||
} catch (Exception e) {
|
||||
// Suppress
|
||||
}
|
||||
KeyUtil.destroySecretKeys(k.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -30,8 +30,10 @@ import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
@ -69,7 +71,6 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
case TLS13:
|
||||
return SSLTrafficKeyDerivation.TLS13;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -143,16 +144,29 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
KeySchedule ks = KeySchedule.valueOf(algorithm);
|
||||
public SecretKey deriveKey(String type) throws IOException {
|
||||
KeySchedule ks = KeySchedule.valueOf(type);
|
||||
try {
|
||||
HKDF hkdf = new HKDF(cs.hashAlg.name);
|
||||
byte[] hkdfInfo =
|
||||
createHkdfInfo(ks.label, ks.getKeyLength(cs));
|
||||
return hkdf.expand(secret, hkdfInfo,
|
||||
ks.getKeyLength(cs),
|
||||
ks.getAlgorithm(cs, algorithm));
|
||||
KDF hkdf = KDF.getInstance(cs.hashAlg.hkdfAlgorithm);
|
||||
byte[] hkdfInfo = createHkdfInfo(ks.label, ks.getKeyLength(cs));
|
||||
HKDFParameterSpec spec = HKDFParameterSpec.expandOnly(secret,
|
||||
hkdfInfo, ks.getKeyLength(cs));
|
||||
return hkdf.deriveKey(ks.getAlgorithm(cs, type), spec);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException(
|
||||
"Could not generate secret", gse);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] deriveData(String type) throws IOException {
|
||||
KeySchedule ks = KeySchedule.valueOf(type);
|
||||
try {
|
||||
KDF hkdf = KDF.getInstance(cs.hashAlg.hkdfAlgorithm);
|
||||
byte[] hkdfInfo = createHkdfInfo(ks.label, ks.getKeyLength(cs));
|
||||
HKDFParameterSpec spec = HKDFParameterSpec.expandOnly(secret,
|
||||
hkdfInfo, ks.getKeyLength(cs));
|
||||
return hkdf.deriveData(spec);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException(
|
||||
"Could not generate secret", gse);
|
||||
@ -171,13 +185,12 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
// unlikely
|
||||
throw new RuntimeException("Unexpected exception", ioe);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
private enum KeySchedule {
|
||||
// Note that we use enum name as the key/ name.
|
||||
// Note that we use enum name as the key name.
|
||||
TlsKey ("key", false),
|
||||
TlsIv ("iv", true),
|
||||
TlsUpdateNplus1 ("traffic upd", false);
|
||||
@ -285,8 +298,9 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
SecretKey getTrafficKey(String algorithm) {
|
||||
switch (algorithm) {
|
||||
@Override
|
||||
public SecretKey deriveKey(String type) throws IOException {
|
||||
switch (type) {
|
||||
case "clientMacKey":
|
||||
return keyMaterialSpec.getClientMacKey();
|
||||
case "serverMacKey":
|
||||
@ -295,24 +309,25 @@ enum SSLTrafficKeyDerivation implements SSLKeyDerivationGenerator {
|
||||
return keyMaterialSpec.getClientCipherKey();
|
||||
case "serverWriteKey":
|
||||
return keyMaterialSpec.getServerCipherKey();
|
||||
case "clientWriteIv":
|
||||
IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv();
|
||||
return (cliIvSpec == null) ? null :
|
||||
new SecretKeySpec(cliIvSpec.getIV(), "TlsIv");
|
||||
case "serverWriteIv":
|
||||
IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv();
|
||||
return (srvIvSpec == null) ? null :
|
||||
new SecretKeySpec(srvIvSpec.getIV(), "TlsIv");
|
||||
default:
|
||||
throw new SSLHandshakeException(
|
||||
"Cannot deriveKey for " + type);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveKey(String algorithm,
|
||||
AlgorithmParameterSpec params) throws IOException {
|
||||
return getTrafficKey(algorithm);
|
||||
public byte[] deriveData(String type) throws IOException {
|
||||
switch (type) {
|
||||
case "clientWriteIv":
|
||||
IvParameterSpec cliIvSpec = keyMaterialSpec.getClientIv();
|
||||
return (cliIvSpec == null) ? null : cliIvSpec.getIV();
|
||||
case "serverWriteIv":
|
||||
IvParameterSpec srvIvSpec = keyMaterialSpec.getServerIv();
|
||||
return (srvIvSpec == null) ? null : srvIvSpec.getIV();
|
||||
default:
|
||||
throw new SSLHandshakeException(
|
||||
"Cannot deriveData for " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,9 @@ import java.security.CryptoPrimitive;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import javax.crypto.KDF;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.HKDFParameterSpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
@ -585,7 +587,7 @@ final class ServerHello {
|
||||
|
||||
SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc);
|
||||
SecretKey handshakeSecret = handshakeKD.deriveKey(
|
||||
"TlsHandshakeSecret", null);
|
||||
"TlsHandshakeSecret");
|
||||
|
||||
SSLTrafficKeyDerivation kdg =
|
||||
SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);
|
||||
@ -601,15 +603,12 @@ final class ServerHello {
|
||||
|
||||
// update the handshake traffic read keys.
|
||||
SecretKey readSecret = kd.deriveKey(
|
||||
"TlsClientHandshakeTrafficSecret", null);
|
||||
"TlsClientHandshakeTrafficSecret");
|
||||
SSLKeyDerivation readKD =
|
||||
kdg.createKeyDerivation(shc, readSecret);
|
||||
SecretKey readKey = readKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey readIvSecret = readKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey readKey = readKD.deriveKey("TlsKey");
|
||||
IvParameterSpec readIv =
|
||||
new IvParameterSpec(readIvSecret.getEncoded());
|
||||
new IvParameterSpec(readKD.deriveData("TlsIv"));
|
||||
SSLReadCipher readCipher;
|
||||
try {
|
||||
readCipher =
|
||||
@ -635,15 +634,12 @@ final class ServerHello {
|
||||
|
||||
// update the handshake traffic write secret.
|
||||
SecretKey writeSecret = kd.deriveKey(
|
||||
"TlsServerHandshakeTrafficSecret", null);
|
||||
"TlsServerHandshakeTrafficSecret");
|
||||
SSLKeyDerivation writeKD =
|
||||
kdg.createKeyDerivation(shc, writeSecret);
|
||||
SecretKey writeKey = writeKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey writeIvSecret = writeKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey writeKey = writeKD.deriveKey("TlsKey");
|
||||
IvParameterSpec writeIv =
|
||||
new IvParameterSpec(writeIvSecret.getEncoded());
|
||||
new IvParameterSpec(writeKD.deriveData("TlsIv"));
|
||||
SSLWriteCipher writeCipher;
|
||||
try {
|
||||
writeCipher =
|
||||
@ -1195,12 +1191,13 @@ final class ServerHello {
|
||||
|
||||
try {
|
||||
CipherSuite.HashAlg hashAlg = hc.negotiatedCipherSuite.hashAlg;
|
||||
HKDF hkdf = new HKDF(hashAlg.name);
|
||||
byte[] zeros = new byte[hashAlg.hashLength];
|
||||
SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
|
||||
KDF hkdf = KDF.getInstance(hashAlg.hkdfAlgorithm);
|
||||
SecretKey earlySecret = hkdf.deriveKey("TlsEarlySecret",
|
||||
HKDFParameterSpec.ofExtract().addIKM(psk)
|
||||
.addSalt(new byte[hashAlg.hashLength]).extractOnly());
|
||||
hc.handshakeKeyDerivation =
|
||||
new SSLSecretDerivation(hc, earlySecret);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new SSLHandshakeException("Could not generate secret", gse);
|
||||
}
|
||||
}
|
||||
@ -1284,7 +1281,7 @@ final class ServerHello {
|
||||
|
||||
SSLKeyDerivation handshakeKD = ke.createKeyDerivation(chc);
|
||||
SecretKey handshakeSecret = handshakeKD.deriveKey(
|
||||
"TlsHandshakeSecret", null);
|
||||
"TlsHandshakeSecret");
|
||||
SSLTrafficKeyDerivation kdg =
|
||||
SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);
|
||||
if (kdg == null) {
|
||||
@ -1299,16 +1296,13 @@ final class ServerHello {
|
||||
|
||||
// update the handshake traffic read keys.
|
||||
SecretKey readSecret = secretKD.deriveKey(
|
||||
"TlsServerHandshakeTrafficSecret", null);
|
||||
"TlsServerHandshakeTrafficSecret");
|
||||
|
||||
SSLKeyDerivation readKD =
|
||||
kdg.createKeyDerivation(chc, readSecret);
|
||||
SecretKey readKey = readKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey readIvSecret = readKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey readKey = readKD.deriveKey("TlsKey");
|
||||
IvParameterSpec readIv =
|
||||
new IvParameterSpec(readIvSecret.getEncoded());
|
||||
new IvParameterSpec(readKD.deriveData("TlsIv"));
|
||||
SSLReadCipher readCipher;
|
||||
try {
|
||||
readCipher =
|
||||
@ -1334,15 +1328,12 @@ final class ServerHello {
|
||||
|
||||
// update the handshake traffic write keys.
|
||||
SecretKey writeSecret = secretKD.deriveKey(
|
||||
"TlsClientHandshakeTrafficSecret", null);
|
||||
"TlsClientHandshakeTrafficSecret");
|
||||
SSLKeyDerivation writeKD =
|
||||
kdg.createKeyDerivation(chc, writeSecret);
|
||||
SecretKey writeKey = writeKD.deriveKey(
|
||||
"TlsKey", null);
|
||||
SecretKey writeIvSecret = writeKD.deriveKey(
|
||||
"TlsIv", null);
|
||||
SecretKey writeKey = writeKD.deriveKey("TlsKey");
|
||||
IvParameterSpec writeIv =
|
||||
new IvParameterSpec(writeIvSecret.getEncoded());
|
||||
new IvParameterSpec(writeKD.deriveData("TlsIv"));
|
||||
SSLWriteCipher writeCipher;
|
||||
try {
|
||||
writeCipher =
|
||||
|
||||
@ -36,6 +36,9 @@ import javax.crypto.interfaces.DHKey;
|
||||
import javax.crypto.interfaces.DHPublicKey;
|
||||
import javax.crypto.spec.DHParameterSpec;
|
||||
import javax.crypto.spec.DHPublicKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
import sun.security.jca.JCAUtil;
|
||||
|
||||
@ -457,5 +460,23 @@ public final class KeyUtil {
|
||||
return alg.equalsIgnoreCase("TlsPremasterSecret")
|
||||
|| alg.equalsIgnoreCase("Generic");
|
||||
}
|
||||
|
||||
// destroy secret keys in a best-effort way
|
||||
public static void destroySecretKeys(SecretKey... keys) {
|
||||
for (SecretKey k : keys) {
|
||||
if (k != null) {
|
||||
if (k instanceof SecretKeySpec sk) {
|
||||
SharedSecrets.getJavaxCryptoSpecAccess()
|
||||
.clearSecretKeySpec(sk);
|
||||
} else {
|
||||
try {
|
||||
k.destroy();
|
||||
} catch (DestroyFailedException e) {
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
modules = \
|
||||
java.base/sun.security.ssl
|
||||
bootclasspath.dirs=.
|
||||
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8145255
|
||||
* @run main/othervm java.base/sun.security.ssl.TestHkdf
|
||||
* @summary HKDF for Sun JSSE
|
||||
*/
|
||||
|
||||
@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Actual test code for the package private HKDF implementation
|
||||
*/
|
||||
|
||||
package sun.security.ssl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.InvalidKeyException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class TestHkdf {
|
||||
public static class TestData {
|
||||
public TestData(String name, String algStr, String ikmStr,
|
||||
String saltStr, String infoStr, int oLen, String expPrkStr,
|
||||
String expOkmStr) {
|
||||
testName = Objects.requireNonNull(name);
|
||||
algName = Objects.requireNonNull(algStr);
|
||||
IKM = hex2bin(Objects.requireNonNull(ikmStr));
|
||||
if ((outLen = oLen) <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Output length must be greater than 0");
|
||||
}
|
||||
expectedPRK = hex2bin(Objects.requireNonNull(expPrkStr));
|
||||
expectedOKM = hex2bin(Objects.requireNonNull(expOkmStr));
|
||||
|
||||
// Non-mandatory fields - may be null
|
||||
salt = (saltStr != null) ? hex2bin(saltStr) : null;
|
||||
info = (infoStr != null) ? hex2bin(infoStr) : null;
|
||||
}
|
||||
|
||||
public final String testName;
|
||||
public final String algName;
|
||||
public final byte[] IKM;
|
||||
public final byte[] salt;
|
||||
public final byte[] info;
|
||||
public final int outLen;
|
||||
public final byte[] expectedPRK;
|
||||
public final byte[] expectedOKM;
|
||||
}
|
||||
|
||||
public static final List<TestData> testList = new LinkedList<TestData>() {{
|
||||
add(new TestData("RFC 5689 Test Case 1", "SHA-256",
|
||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
||||
"000102030405060708090a0b0c",
|
||||
"f0f1f2f3f4f5f6f7f8f9",
|
||||
42,
|
||||
"077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5",
|
||||
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf" +
|
||||
"34007208d5b887185865"));
|
||||
add(new TestData("RFC 5689 Test Case 2", "SHA-256",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" +
|
||||
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" +
|
||||
"404142434445464748494a4b4c4d4e4f",
|
||||
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" +
|
||||
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" +
|
||||
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
|
||||
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" +
|
||||
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" +
|
||||
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
||||
82,
|
||||
"06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244",
|
||||
"b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c" +
|
||||
"59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71" +
|
||||
"cc30c58179ec3e87c14c01d5c1f3434f1d87"));
|
||||
add(new TestData("RFC 5689 Test Case 3", "SHA-256",
|
||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
||||
null, null, 42,
|
||||
"19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04",
|
||||
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d" +
|
||||
"9d201395faa4b61a96c8"));
|
||||
add(new TestData("RFC 5689 Test Case 4", "SHA-1",
|
||||
"0b0b0b0b0b0b0b0b0b0b0b",
|
||||
"000102030405060708090a0b0c",
|
||||
"f0f1f2f3f4f5f6f7f8f9",
|
||||
42,
|
||||
"9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243",
|
||||
"085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2" +
|
||||
"c22e422478d305f3f896"));
|
||||
add(new TestData("RFC 5689 Test Case 5", "SHA-1",
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" +
|
||||
"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" +
|
||||
"404142434445464748494a4b4c4d4e4f",
|
||||
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" +
|
||||
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" +
|
||||
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
|
||||
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf" +
|
||||
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef" +
|
||||
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
||||
82,
|
||||
"8adae09a2a307059478d309b26c4115a224cfaf6",
|
||||
"0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe" +
|
||||
"8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e" +
|
||||
"927336d0441f4c4300e2cff0d0900b52d3b4"));
|
||||
add(new TestData("RFC 5689 Test Case 6", "SHA-1",
|
||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
||||
null, null, 42,
|
||||
"da8c8a73c7fa77288ec6f5e7c297786aa0d32d01",
|
||||
"0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0" +
|
||||
"ea00033de03984d34918"));
|
||||
add(new TestData("RFC 5689 Test Case 7", "SHA-1",
|
||||
"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
|
||||
null, null, 42,
|
||||
"2adccada18779e7c2077ad2eb19d3f3e731385dd",
|
||||
"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5" +
|
||||
"673a081d70cce7acfc48"));
|
||||
}};
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
int testsPassed = 0;
|
||||
|
||||
int testNo = 0;
|
||||
for (TestData test : testList) {
|
||||
System.out.println("*** Test " + ++testNo + ": " +
|
||||
test.testName);
|
||||
if (runVector(test)) {
|
||||
testsPassed++;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Total tests: " + testList.size() +
|
||||
", Passed: " + testsPassed + ", Failed: " +
|
||||
(testList.size() - testsPassed));
|
||||
if (testsPassed != testList.size()) {
|
||||
throw new RuntimeException("One or more tests failed. " +
|
||||
"Check output for details");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean runVector(TestData testData)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
String kdfName, prfName;
|
||||
HKDF kdfHkdf;
|
||||
boolean result = true;
|
||||
SecretKey actualPRK;
|
||||
SecretKey actualOKM;
|
||||
byte[] deriveData;
|
||||
|
||||
// Get an instance of the HKDF derivation engine
|
||||
kdfHkdf = new HKDF(testData.algName);
|
||||
|
||||
// Set up the input keying material and the salt as a secret
|
||||
SecretKey ikmKey = new SecretKeySpec(testData.IKM, "HKDF-IKM");
|
||||
SecretKey saltKey = (testData.salt != null) ?
|
||||
new SecretKeySpec(testData.salt, "HKDF-Salt") : null;
|
||||
|
||||
// *** HKDF-Extract-only testing
|
||||
System.out.println("* HKDF-Extract-Only:");
|
||||
actualPRK = kdfHkdf.extract(saltKey, ikmKey, "HKDF-PRK");
|
||||
result &= compareKeyAndData(actualPRK, testData.expectedPRK);
|
||||
|
||||
// *** HKDF Expand-Only testing
|
||||
// For these tests, we'll use the actualPRK as the input key
|
||||
System.out.println("* HKDF-Expand-Only:");
|
||||
actualOKM = kdfHkdf.expand(actualPRK, testData.info, testData.outLen,
|
||||
"HKDF-OKM");
|
||||
result &= compareKeyAndData(actualOKM, testData.expectedOKM);
|
||||
|
||||
// *** HKDF Extract-then-Expand testing
|
||||
// System.out.println("* HKDF-Extract-then-Expand:");
|
||||
// actualOKM = kdfHkdf.extractExpand(ikmKey, saltKey, testData.info,
|
||||
// testData.outLen, "HKDF-OKM2");
|
||||
// result &= compareKeyAndData(actualOKM, testData.expectedOKM);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare actual key output from HKDF against an expected output value.
|
||||
*
|
||||
* @param outKey the KDF output in key form
|
||||
* @param expectedOut the expected value
|
||||
*
|
||||
* @return true if the underlying data for outKey, outData and
|
||||
* expectedOut are the same.
|
||||
*/
|
||||
private static boolean compareKeyAndData(SecretKey outKey,
|
||||
byte[] expectedOut) {
|
||||
boolean result = false;
|
||||
|
||||
if (Arrays.equals(outKey.getEncoded(), expectedOut)) {
|
||||
System.out.println("\t* Key output: Pass");
|
||||
result = true;
|
||||
} else {
|
||||
System.out.println("\t* Key output: FAIL");
|
||||
System.out.println("Expected:\n" +
|
||||
dumpHexBytes(expectedOut, 16, "\n", " "));
|
||||
System.out.println("Actual:\n" +
|
||||
dumpHexBytes(outKey.getEncoded(), 16, "\n", " "));
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the hex bytes of a buffer into string form.
|
||||
*
|
||||
* @param data The array of bytes to dump to stdout.
|
||||
* @param itemsPerLine The number of bytes to display per line
|
||||
* if the {@code lineDelim} character is blank then all bytes
|
||||
* will be printed on a single line.
|
||||
* @param lineDelim The delimiter between lines
|
||||
* @param itemDelim The delimiter between bytes
|
||||
*
|
||||
* @return The hexdump of the byte array
|
||||
*/
|
||||
private static String dumpHexBytes(byte[] data, int itemsPerLine,
|
||||
String lineDelim, String itemDelim) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (data != null) {
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
if (i % itemsPerLine == 0 && i != 0) {
|
||||
sb.append(lineDelim);
|
||||
}
|
||||
sb.append(String.format("%02X", data[i])).append(itemDelim);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static byte[] hex2bin(String hex) {
|
||||
int i;
|
||||
int len = hex.length();
|
||||
byte[] data = new byte [len / 2];
|
||||
for (i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte)((Character.digit(hex.charAt(i), 16) << 4) +
|
||||
Character.digit(hex.charAt(i + 1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user