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
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ abstract class AbstractDOMSignatureMethod extends DOMStructure
implements SignatureMethod {

// denotes the type of signature algorithm
enum Type { DSA, RSA, ECDSA, EDDSA, HMAC }
enum Type { DSA, RSA, ECDSA, EDDSA, MLDSA, HMAC }

/**
* Verifies the passed-in signature with the specified key, using the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ public abstract class DOMSignatureMethod extends AbstractDOMSignatureMethod {
"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519";
static final String ED448 =
"http://www.w3.org/2021/04/xmldsig-more#eddsa-ed448";

// URIs for ML-DSA (FIPS 204) per draft-eastlake-rfc9231bis-xmlsec-uris-09
// section 3.3.15 (see SANTUARIO-634).
static final String ML_DSA_44 =
"http://www.w3.org/2026/08/xmldsig-more#ml-dsa-44";
static final String ML_DSA_65 =
"http://www.w3.org/2026/08/xmldsig-more#ml-dsa-65";
static final String ML_DSA_87 =
"http://www.w3.org/2026/08/xmldsig-more#ml-dsa-87";
static final String ECDSA_SHA3_224 =
"http://www.w3.org/2021/04/xmldsig-more#ecdsa-sha3-224";
static final String ECDSA_SHA3_256 =
Expand Down Expand Up @@ -269,6 +278,12 @@ static SignatureMethod unmarshal(Element smElem) throws MarshalException {
return new EDDSA_ED25519(smElem);
} else if (alg.equals(ED448)) {
return new EDDSA_ED448(smElem);
} else if (alg.equals(ML_DSA_44)) {

@seanjmullan seanjmullan Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to think about as a (perhaps subsequent) improvement - we could store all the algorithm Strings in a static Map (then you could just do a Map.containsKey()) and reduce the number of classes by passing in arguments (stored with each map entry). Most of these subclasses are the same except for algorithm names. There are a few special cases though for algs that take parameters but I think the code could be structured differently for those to create them by the caller and only if needed.

return new MLDSA_44(smElem);
} else if (alg.equals(ML_DSA_65)) {
return new MLDSA_65(smElem);
} else if (alg.equals(ML_DSA_87)) {
return new MLDSA_87(smElem);
} else {
throw new MarshalException
("unsupported SignatureMethod algorithm: " + alg);
Expand Down Expand Up @@ -1291,4 +1306,87 @@ String getJCAAlgorithm() {
return "Ed448";
}
}

abstract static class AbstractMLDSASignatureMethod extends DOMSignatureMethod {

AbstractMLDSASignatureMethod(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
super(params);
}

AbstractMLDSASignatureMethod(Element dmElem) throws MarshalException {
super(dmElem);
}

/** ML-DSA signatures are raw bytes; no reformatting needed. */
@Override
byte[] postSignFormat(Key key, byte[] sig) {
return sig;
}

/** ML-DSA signatures are raw bytes; no reformatting needed. */
@Override
byte[] preVerifyFormat(Key key, byte[] sig) {
return sig;
}

@Override
Type getAlgorithmType() {
return Type.MLDSA;
}
}

static final class MLDSA_44 extends AbstractMLDSASignatureMethod {
MLDSA_44(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
super(params);
}
MLDSA_44(Element dmElem) throws MarshalException {
super(dmElem);
}
@Override
public String getAlgorithm() {
return ML_DSA_44;
}
@Override
String getJCAAlgorithm() {
return "ML-DSA-44";
}
}

static final class MLDSA_65 extends AbstractMLDSASignatureMethod {
MLDSA_65(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
super(params);
}
MLDSA_65(Element dmElem) throws MarshalException {
super(dmElem);
}
@Override
public String getAlgorithm() {
return ML_DSA_65;
}
@Override
String getJCAAlgorithm() {
return "ML-DSA-65";
}
}

static final class MLDSA_87 extends AbstractMLDSASignatureMethod {
MLDSA_87(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
super(params);
}
MLDSA_87(Element dmElem) throws MarshalException {
super(dmElem);
}
@Override
public String getAlgorithm() {
return ML_DSA_87;
}
@Override
String getJCAAlgorithm() {
return "ML-DSA-87";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;

import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* DOM-based implementation of XMLSignature.
Expand Down Expand Up @@ -278,6 +280,8 @@ public boolean validate(XMLValidateContext vc)
return validationStatus;
}

checkForUnsupportedSignatureContext(localSigElem);

// validate the signature
boolean sigValidity = sv.validate(vc);
if (!sigValidity) {
Expand Down Expand Up @@ -339,6 +343,30 @@ public boolean validate(XMLValidateContext vc)
return validationStatus;
}

/**
* Rejects a signature that carries an ML-DSA {@code SignatureContext} element
* (draft-eastlake-rfc9231bis-xmlsec-uris-09, section 3.3.15). The
* {@code java.security.Signature} API offers no way to pass a signature context
* to ML-DSA (see the Non-Goals of JEP 497), so such a signature can be neither
* created nor verified correctly here; bail out rather than silently ignoring
* the context.
*/
private static void checkForUnsupportedSignatureContext(Element sigElem)
throws XMLSignatureException
{
if (sigElem == null) {
return;
}
NodeList contexts = sigElem.getElementsByTagNameNS(
Constants.XML_DSIG_NS_MORE_26_08, Constants._TAG_SIGNATURECONTEXT);
if (contexts.getLength() > 0) {
throw new XMLSignatureException("The ML-DSA SignatureContext element ("
+ Constants.XML_DSIG_NS_MORE_26_08 + Constants._TAG_SIGNATURECONTEXT
+ ") is not supported: the java.security.Signature API cannot pass a "
+ "signature context to ML-DSA (JEP 497)");
}
}

@Override
public void sign(XMLSignContext signContext)
throws MarshalException, XMLSignatureException
Expand All @@ -350,6 +378,8 @@ public void sign(XMLSignContext signContext)
marshal(context.getParent(), context.getNextSibling(),
DOMUtils.getSignaturePrefix(context), context);

checkForUnsupportedSignatureContext(sigElem);

// generate references and signature value
List<Reference> allReferences = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,13 @@ public SignatureMethod newSignatureMethod(String algorithm,
return new DOMSignatureMethod.EDDSA_ED25519(params);
} else if (algorithm.equals(DOMSignatureMethod.ED448)) {
return new DOMSignatureMethod.EDDSA_ED448(params);
}else {
} else if (algorithm.equals(DOMSignatureMethod.ML_DSA_44)) {
return new DOMSignatureMethod.MLDSA_44(params);
} else if (algorithm.equals(DOMSignatureMethod.ML_DSA_65)) {
return new DOMSignatureMethod.MLDSA_65(params);
} else if (algorithm.equals(DOMSignatureMethod.ML_DSA_87)) {
return new DOMSignatureMethod.MLDSA_87(params);
} else {
throw new NoSuchAlgorithmException("unsupported algorithm");
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/apache/xml/security/algorithms/JCEMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,18 @@ public static void registerDefaultAlgorithms() {
XMLSignature.ALGO_ID_SIGNATURE_EDDSA_ED448,
new Algorithm("Ed448", "Ed448", "Signature")
);
algorithmsMap.put(
XMLSignature.ALGO_ID_SIGNATURE_MLDSA_44,
new Algorithm("ML-DSA-44", "ML-DSA-44", "Signature")
);
algorithmsMap.put(
XMLSignature.ALGO_ID_SIGNATURE_MLDSA_65,
new Algorithm("ML-DSA-65", "ML-DSA-65", "Signature")
);
algorithmsMap.put(
XMLSignature.ALGO_ID_SIGNATURE_MLDSA_87,
new Algorithm("ML-DSA-87", "ML-DSA-87", "Signature")
);
algorithmsMap.put(
XMLSignature.ALGO_ID_MAC_HMAC_NOT_RECOMMENDED_MD5,
new Algorithm("", "HmacMD5", "Mac", 0, 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.xml.security.algorithms.implementations.SignatureDSA;
import org.apache.xml.security.algorithms.implementations.SignatureECDSA;
import org.apache.xml.security.algorithms.implementations.SignatureEDDSA;
import org.apache.xml.security.algorithms.implementations.SignatureMLDSA;
import org.apache.xml.security.exceptions.AlgorithmAlreadyRegisteredException;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.XMLSignature;
Expand Down Expand Up @@ -513,6 +514,15 @@ public static void registerDefaultAlgorithms() {
algorithmHash.put(
XMLSignature.ALGO_ID_SIGNATURE_EDDSA_ED448, SignatureEDDSA.SignatureEd448.class
);
algorithmHash.put(
XMLSignature.ALGO_ID_SIGNATURE_MLDSA_44, SignatureMLDSA.SignatureMLDSA44.class
);
algorithmHash.put(
XMLSignature.ALGO_ID_SIGNATURE_MLDSA_65, SignatureMLDSA.SignatureMLDSA65.class
);
algorithmHash.put(
XMLSignature.ALGO_ID_SIGNATURE_MLDSA_87, SignatureMLDSA.SignatureMLDSA87.class
);
algorithmHash.put(
XMLSignature.ALGO_ID_MAC_HMAC_NOT_RECOMMENDED_MD5, IntegrityHmac.IntegrityHmacMD5.class
);
Expand Down
Loading
Loading