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
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Changelog
We now only publish ``arm64`` wheels for macOS.
* **BACKWARDS INCOMPATIBLE:** Support for 32-bit Windows has been removed.
Users should move to a 64-bit Python installation.
* Added support for signing and verifying X.509 certificates, certificate
signing requests, and certificate revocation lists with
:doc:`/hazmat/primitives/asymmetric/mldsa` keys, as well as loading
certificates that contain ML-DSA public keys.

.. _v48-0-0:

Expand Down
33 changes: 24 additions & 9 deletions docs/x509/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1022,9 +1022,14 @@ X.509 Certificate Builder
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that
will be used to generate the signature. This must be ``None`` if
the ``private_key`` is an
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`
or an
:class:`~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey`
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`,
an
:class:`~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey`,
or an ML-DSA key (one of
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA44PrivateKey`,
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA65PrivateKey`,
or
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA87PrivateKey`),
and an instance of a
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
otherwise.
Expand Down Expand Up @@ -1311,9 +1316,14 @@ X.509 Certificate Revocation List Builder
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that
will be used to generate the signature.
This must be ``None`` if the ``private_key`` is an
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`
or an
:class:`~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey`
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`,
an
:class:`~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey`,
or an ML-DSA key (one of
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA44PrivateKey`,
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA65PrivateKey`,
or
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA87PrivateKey`),
and an instance of a
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
otherwise.
Expand Down Expand Up @@ -1538,9 +1548,14 @@ X.509 CSR (Certificate Signing Request) Builder Object
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
that will be used to generate the request signature.
This must be ``None`` if the ``private_key`` is an
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`
or an
:class:`~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey`
:class:`~cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey`,
an
:class:`~cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey`,
or an ML-DSA key (one of
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA44PrivateKey`,
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA65PrivateKey`,
or
:class:`~cryptography.hazmat.primitives.asymmetric.mldsa.MLDSA87PrivateKey`),
and an instance of a
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
otherwise.
Expand Down
3 changes: 3 additions & 0 deletions src/cryptography/hazmat/_oid.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ class SignatureAlgorithmOID:
SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(),
SignatureAlgorithmOID.ED25519: None,
SignatureAlgorithmOID.ED448: None,
SignatureAlgorithmOID.ML_DSA_44: None,
SignatureAlgorithmOID.ML_DSA_65: None,
SignatureAlgorithmOID.ML_DSA_87: None,
SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None,
SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None,
SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None,
Expand Down
9 changes: 9 additions & 0 deletions src/cryptography/hazmat/primitives/asymmetric/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
rsa.RSAPrivateKey,
dsa.DSAPrivateKey,
ec.EllipticCurvePrivateKey,
mldsa.MLDSA44PrivateKey,
mldsa.MLDSA65PrivateKey,
mldsa.MLDSA87PrivateKey,
]
CERTIFICATE_PRIVATE_KEY_TYPES = CertificateIssuerPrivateKeyTypes
utils.deprecated(
Expand All @@ -93,6 +96,9 @@
ec.EllipticCurvePublicKey,
ed25519.Ed25519PublicKey,
ed448.Ed448PublicKey,
mldsa.MLDSA44PublicKey,
mldsa.MLDSA65PublicKey,
mldsa.MLDSA87PublicKey,
]
CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = CertificateIssuerPublicKeyTypes
utils.deprecated(
Expand All @@ -110,6 +116,9 @@
ec.EllipticCurvePublicKey,
ed25519.Ed25519PublicKey,
ed448.Ed448PublicKey,
mldsa.MLDSA44PublicKey,
mldsa.MLDSA65PublicKey,
mldsa.MLDSA87PublicKey,
x25519.X25519PublicKey,
x448.X448PublicKey,
]
Expand Down
7 changes: 6 additions & 1 deletion src/cryptography/x509/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ec,
ed448,
ed25519,
mldsa,
padding,
rsa,
x448,
Expand Down Expand Up @@ -362,14 +363,18 @@ def public_key(
ec.EllipticCurvePublicKey,
ed25519.Ed25519PublicKey,
ed448.Ed448PublicKey,
mldsa.MLDSA44PublicKey,
mldsa.MLDSA65PublicKey,
mldsa.MLDSA87PublicKey,
x25519.X25519PublicKey,
x448.X448PublicKey,
),
):
raise TypeError(
"Expecting one of DSAPublicKey, RSAPublicKey,"
" EllipticCurvePublicKey, Ed25519PublicKey,"
" Ed448PublicKey, X25519PublicKey, or "
" Ed448PublicKey, MLDSA44PublicKey, MLDSA65PublicKey,"
" MLDSA87PublicKey, X25519PublicKey, or "
"X448PublicKey."
)
if self._public_key is not None:
Expand Down
25 changes: 25 additions & 0 deletions src/rust/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,31 @@ pub static ED448_PUBLIC_KEY: LazyPyImport = LazyPyImport::new(
&["Ed448PublicKey"],
);

pub static MLDSA44_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mldsa",
&["MLDSA44PrivateKey"],
);
pub static MLDSA44_PUBLIC_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mldsa",
&["MLDSA44PublicKey"],
);
pub static MLDSA65_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mldsa",
&["MLDSA65PrivateKey"],
);
pub static MLDSA65_PUBLIC_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mldsa",
&["MLDSA65PublicKey"],
);
pub static MLDSA87_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mldsa",
&["MLDSA87PrivateKey"],
);
pub static MLDSA87_PUBLIC_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mldsa",
&["MLDSA87PublicKey"],
);

pub static DSA_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.dsa",
&["DSAPrivateKey"],
Expand Down
59 changes: 53 additions & 6 deletions src/rust/src/x509/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub(crate) enum KeyType {
Ec,
Ed25519,
Ed448,
MlDsa44,
MlDsa65,
MlDsa87,
}

enum HashType {
Expand Down Expand Up @@ -68,9 +71,16 @@ pub(crate) fn identify_key_type(
Ok(KeyType::Ed25519)
} else if private_key.is_instance(&types::ED448_PRIVATE_KEY.get(py)?)? {
Ok(KeyType::Ed448)
} else if private_key.is_instance(&types::MLDSA44_PRIVATE_KEY.get(py)?)? {
Ok(KeyType::MlDsa44)
} else if private_key.is_instance(&types::MLDSA65_PRIVATE_KEY.get(py)?)? {
Ok(KeyType::MlDsa65)
} else if private_key.is_instance(&types::MLDSA87_PRIVATE_KEY.get(py)?)? {
Ok(KeyType::MlDsa87)
} else {
Err(pyo3::exceptions::PyTypeError::new_err(
"Key must be an rsa, dsa, ec, ed25519, or ed448 private key.",
"Key must be an rsa, dsa, ec, ed25519, ed448, ml-dsa-44, \
ml-dsa-65, or ml-dsa-87 private key.",
))
}
}
Expand Down Expand Up @@ -190,6 +200,24 @@ pub(crate) fn compute_signature_algorithm<'p>(
"Algorithm must be None when signing via ed25519 or ed448",
)),

(KeyType::MlDsa44, HashType::None) => Ok(common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: common::AlgorithmParameters::MlDsa44,
}),
(KeyType::MlDsa65, HashType::None) => Ok(common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: common::AlgorithmParameters::MlDsa65,
}),
(KeyType::MlDsa87, HashType::None) => Ok(common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: common::AlgorithmParameters::MlDsa87,
}),
(KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87, _) => {
Err(pyo3::exceptions::PyValueError::new_err(
"Algorithm must be None when signing via ml-dsa-44, ml-dsa-65, or ml-dsa-87",
))
}

(KeyType::Ec, HashType::Sha224) => Ok(common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: common::AlgorithmParameters::EcDsaWithSha224(None),
Expand Down Expand Up @@ -295,9 +323,11 @@ pub(crate) fn sign_data<'p>(
let key_type = identify_key_type(py, private_key.clone())?;

let signature = match key_type {
KeyType::Ed25519 | KeyType::Ed448 => {
private_key.call_method1(pyo3::intern!(py, "sign"), (data,))?
}
KeyType::Ed25519
| KeyType::Ed448
| KeyType::MlDsa44
| KeyType::MlDsa65
| KeyType::MlDsa87 => private_key.call_method1(pyo3::intern!(py, "sign"), (data,))?,
KeyType::Ec => {
let ecdsa = types::ECDSA
.get(py)?
Expand Down Expand Up @@ -338,7 +368,11 @@ pub(crate) fn verify_signature_with_signature_algorithm<'p>(
identify_signature_algorithm_parameters(py, signature_algorithm)?;
let py_signature_hash_algorithm = identify_signature_hash_algorithm(py, signature_algorithm)?;
match key_type {
KeyType::Ed25519 | KeyType::Ed448 => {
KeyType::Ed25519
| KeyType::Ed448
| KeyType::MlDsa44
| KeyType::MlDsa65
| KeyType::MlDsa87 => {
issuer_public_key.call_method1(pyo3::intern!(py, "verify"), (signature, data))?
}
KeyType::Ec => issuer_public_key.call_method1(
Expand Down Expand Up @@ -376,9 +410,16 @@ pub(crate) fn identify_public_key_type(
Ok(KeyType::Ed25519)
} else if public_key.is_instance(&types::ED448_PUBLIC_KEY.get(py)?)? {
Ok(KeyType::Ed448)
} else if public_key.is_instance(&types::MLDSA44_PUBLIC_KEY.get(py)?)? {
Ok(KeyType::MlDsa44)
} else if public_key.is_instance(&types::MLDSA65_PUBLIC_KEY.get(py)?)? {
Ok(KeyType::MlDsa65)
} else if public_key.is_instance(&types::MLDSA87_PUBLIC_KEY.get(py)?)? {
Ok(KeyType::MlDsa87)
} else {
Err(pyo3::exceptions::PyTypeError::new_err(
"Key must be an rsa, dsa, ec, ed25519, or ed448 public key.",
"Key must be an rsa, dsa, ec, ed25519, ed448, ml-dsa-44, \
ml-dsa-65, or ml-dsa-87 public key.",
))
}
}
Expand Down Expand Up @@ -406,6 +447,9 @@ fn identify_key_type_for_algorithm_params(
| common::AlgorithmParameters::EcDsaWithSha3_512 => Ok(KeyType::Ec),
common::AlgorithmParameters::Ed25519 => Ok(KeyType::Ed25519),
common::AlgorithmParameters::Ed448 => Ok(KeyType::Ed448),
common::AlgorithmParameters::MlDsa44 => Ok(KeyType::MlDsa44),
common::AlgorithmParameters::MlDsa65 => Ok(KeyType::MlDsa65),
common::AlgorithmParameters::MlDsa87 => Ok(KeyType::MlDsa87),
common::AlgorithmParameters::DsaWithSha224(..)
| common::AlgorithmParameters::DsaWithSha256(..)
| common::AlgorithmParameters::DsaWithSha384(..)
Expand Down Expand Up @@ -594,6 +638,9 @@ mod tests {
(&common::AlgorithmParameters::EcDsaWithSha3_512, KeyType::Ec),
(&common::AlgorithmParameters::Ed25519, KeyType::Ed25519),
(&common::AlgorithmParameters::Ed448, KeyType::Ed448),
(&common::AlgorithmParameters::MlDsa44, KeyType::MlDsa44),
(&common::AlgorithmParameters::MlDsa65, KeyType::MlDsa65),
(&common::AlgorithmParameters::MlDsa87, KeyType::MlDsa87),
(
&common::AlgorithmParameters::DsaWithSha224(None),
KeyType::Dsa,
Expand Down
Loading
Loading