Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions src/java.base/share/classes/sun/security/ssl/DHasKEM.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 2026, 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
Expand Down Expand Up @@ -101,7 +101,18 @@ public KEM.Encapsulated engineEncapsulate(int from, int to,
return new KEM.Encapsulated(
sub(dh, from, to),
pkEm, null);

} catch (IllegalArgumentException e) {
// ECDH validation failure
// all-zero shared secret
throw e;
} catch (InvalidKeyException e) {
// Invalid peer public key
// Convert InvalidKeyException to an unchecked exception
throw new IllegalArgumentException("Invalid peer public key",
e);
} catch (Exception e) {
// Unexpected internal failure
throw new ProviderException("internal error", e);
}
}
Expand All @@ -126,6 +137,11 @@ public SecretKey engineDecapsulate(byte[] encapsulation, int from,
PublicKey pkE = params.DeserializePublicKey(encapsulation);
SecretKey dh = params.DH(algorithm, skR, pkE);
return sub(dh, from, to);

} catch (IllegalArgumentException e) {
// ECDH validation failure
// all-zero shared secret
throw e;
} catch (IOException | InvalidKeyException e) {
throw new DecapsulateException("Cannot decapsulate", e);
} catch (Exception e) {
Expand Down Expand Up @@ -248,7 +264,24 @@ private SecretKey DH(String alg, PrivateKey skE, PublicKey pkR)
KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm);
ka.init(skE);
ka.doPhase(pkR, true);
return ka.generateSecret(alg);
SecretKey secret = ka.generateSecret(alg);

// RFC 8446 section 7.4.2: checks for all-zero
// X25519/X448 shared secret.
if (kaAlgorithm.equals("X25519") ||
kaAlgorithm.equals("X448")) {
byte[] s = secret.getEncoded();
for (byte b : s) {
if (b != 0) {
return secret;
}
}
// Trigger ILLEGAL_PARAMETER alert
throw new IllegalArgumentException(
"All-zero shared secret");
}

return secret;
}
}
}
53 changes: 43 additions & 10 deletions src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, 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
Expand All @@ -26,6 +26,7 @@

import sun.security.util.RawKeySpec;

import javax.crypto.DecapsulateException;
import javax.crypto.KDF;
import javax.crypto.KEM;
import javax.crypto.KeyAgreement;
Expand All @@ -35,6 +36,7 @@

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Provider;
Expand Down Expand Up @@ -173,6 +175,9 @@ KEM.Encapsulated encapsulate(String algorithm, SecureRandom random)
"encapsulation");
}

// All exceptions thrown during KEM encapsulation are mapped
// to TLS fatal alerts:
// illegal_parameter alert or internal_error alert.
try {
KeyFactory kf = (provider != null) ?
KeyFactory.getInstance(algorithmName, provider) :
Expand All @@ -189,8 +194,18 @@ KEM.Encapsulated encapsulate(String algorithm, SecureRandom random)
SecretKey derived = deriveHandshakeSecret(algorithm, sharedSecret);

return new KEM.Encapsulated(derived, enc.encapsulation(), null);
} catch (GeneralSecurityException gse) {
throw new SSLHandshakeException("Could not generate secret", gse);
} catch (IllegalArgumentException | InvalidKeyException e) {
// Peer validation failure
// ECDH all-zero shared secret (RFC 8446 section 7.4.2),
// ML-KEM encapsulation key check failure (FIPS-203 section 7.2)
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e);
} catch (GeneralSecurityException e) {
// Cryptographic failure,
// deriveHandshakeSecret failure.
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
} catch (RuntimeException e) {
// unexpected provider/runtime failure
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
} finally {
KeyUtil.destroySecretKeys(sharedSecret);
}
Expand All @@ -208,13 +223,30 @@ private SecretKey t13DeriveKey(String type)
// Using KEM: called by the client after receiving the KEM
// ciphertext (keyshare) from the server in ServerHello.
// The client decapsulates it using its private key.
KEM kem = (provider != null)
? KEM.getInstance(algorithmName, provider)
: KEM.getInstance(algorithmName);
var decapsulator = kem.newDecapsulator(localPrivateKey);
sharedSecret = decapsulator.decapsulate(
keyshare, 0, decapsulator.secretSize(),
"TlsPremasterSecret");

// All exceptions thrown during KEM decapsulation are mapped
// to TLS fatal alerts:
// illegal_parameter alert or internal_error alert.
try {
KEM kem = (provider != null)
? KEM.getInstance(algorithmName, provider)
: KEM.getInstance(algorithmName);
var decapsulator = kem.newDecapsulator(localPrivateKey);
sharedSecret = decapsulator.decapsulate(
keyshare, 0, decapsulator.secretSize(),
"TlsPremasterSecret");
} catch (IllegalArgumentException | InvalidKeyException |
DecapsulateException e) {
// Peer validation failure
// ECDH all-zero shared secret (RFC 8446 section 7.4.2)
throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER, e);
} catch (GeneralSecurityException e) {
// cryptographic failure
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
} catch (RuntimeException e) {
// unexpected provider/runtime failure
throw context.conContext.fatal(Alert.INTERNAL_ERROR, e);
}
} else {
// Using traditional DH-style Key Agreement
KeyAgreement ka = KeyAgreement.getInstance(algorithmName);
Expand All @@ -225,6 +257,7 @@ private SecretKey t13DeriveKey(String type)

return deriveHandshakeSecret(type, sharedSecret);
} catch (GeneralSecurityException gse) {
// deriveHandshakeSecret() failure
throw new SSLHandshakeException("Could not generate secret", gse);
} finally {
KeyUtil.destroySecretKeys(sharedSecret);
Expand Down