Skip to content

Add rsa_padding parameter to CertificateBuilder.public_key()#15000

Open
reaperhulk wants to merge 8 commits into
mainfrom
claude/epic-shannon-e8ttfp
Open

Add rsa_padding parameter to CertificateBuilder.public_key()#15000
reaperhulk wants to merge 8 commits into
mainfrom
claude/epic-shannon-e8ttfp

Conversation

@reaperhulk

Copy link
Copy Markdown
Member

Summary

Adds support for encoding RSA public keys in X.509 certificates with the id-RSASSA-PSS OID and no parameters (the unrestricted form from RFC 4055), which marks the key as usable only for RSASSA-PSS signatures. This is required for TLS 1.3 rsa_pss_pss_* signature schemes.

Changes

  • Python API: Added rsa_padding keyword-only parameter to CertificateBuilder.public_key() that accepts the uninstantiated PSS class (not an instance). When provided with an RSA public key, the certificate's subjectPublicKeyInfo is encoded with the id-RSASSA-PSS OID and no parameters instead of the standard rsaEncryption OID.

  • Validation: Added type checking to ensure:

    • rsa_padding must be the PSS class itself, not an instance
    • rsa_padding is only valid with RSAPublicKey types
    • Appropriate TypeError exceptions are raised for invalid usage
  • Rust implementation: Modified certificate creation to detect when PSS padding is specified and rewrite the SPKI (SubjectPublicKeyInfo) structure with the id-RSASSA-PSS OID and absent parameters, while preserving the actual public key material.

  • State management: Updated CertificateBuilder.__init__() and all builder methods to properly thread the _public_key_rsa_padding state through the builder chain.

  • Documentation: Updated reference documentation and CHANGELOG to describe the new parameter and its behavior.

Implementation Details

  • The rsa_padding parameter does not affect how the certificate itself is signed; it only controls the encoding of the subject public key in the certificate. The rsa_padding parameter of the sign() method continues to control the certificate's signature algorithm.
  • The implementation parses the standard SPKI structure and rewrites it with the PSS OID and no parameters, maintaining the original public key bits.
  • Three new test cases validate: successful PSS SPKI encoding, proper type validation of the rsa_padding parameter, and rejection of rsa_padding with non-RSA keys.

https://claude.ai/code/session_01HBR1P3RLnJ6r2avs6DZxUy

claude added 5 commits June 9, 2026 22:20
… SPKI

Passing the uninstantiated PSS class encodes an RSA subject public key
with the id-RSASSA-PSS OID (parameters absent, the unrestricted form
from RFC 4055) in the certificate's subjectPublicKeyInfo, as required
by the rsa_pss_pss_* signature schemes in TLS 1.3 and by some hardware
consumers.

Fixes #10655

https://claude.ai/code/session_01HBR1P3RLnJ6r2avs6DZxUy
The Python layer validates rsa_padding in public_key(), so the value is
always either None or the PSS class by the time it reaches Rust, like
every other builder attribute.

https://claude.ai/code/session_01HBR1P3RLnJ6r2avs6DZxUy
Comment thread docs/x509/reference.rst Outdated
Comment on lines +962 to +972
:param rsa_padding: The uninstantiated
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
class (not an instance), only valid with
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`.
When set, the certificate's ``subjectPublicKeyInfo`` encodes the
key with the ``id-RSASSA-PSS`` OID and no parameters (the
unrestricted form from :rfc:`4055`) instead of ``rsaEncryption``,
marking the key as usable only for RSASSA-PSS signatures. It does
not change how this certificate itself is signed; use the
``rsa_padding`` parameter of :meth:`sign` to control that.

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.

The docs feel slightly off to me, but I don't have a more concrete suggestion. Might need a human pass rewrite?

Comment thread src/rust/src/x509/certificate.rs Outdated
Comment on lines +1065 to +1081
let spki_der: &[u8] = if py_public_key_rsa_padding.is_none() {
&spki_bytes
} else {
// The Python layer ensures this is only ever the PSS class.
let spki = asn1::parse_single::<common::SubjectPublicKeyInfo<'_>>(&spki_bytes)?;
// id-RSASSA-PSS with the parameters absent (the unrestricted form
// from RFC 4055), which is the encoding required for the TLS 1.3
// rsa_pss_pss_* signature schemes.
pss_spki_bytes = asn1::write_single(&common::SubjectPublicKeyInfo {
algorithm: common::AlgorithmIdentifier {
oid: asn1::DefinedByMarker::marker(),
params: AlgorithmParameters::RsaPss(None),
},
subject_public_key: spki.subject_public_key,
})?;
&pss_spki_bytes
};

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.

I think this round trips too much -- the usage line is asn1::parse_single(spki_der)?. We should just do that in the non-PSS case, and in the other case we can skip the write_single.

claude added 2 commits June 9, 2026 23:26
Address review feedback: parse the SPKI bytes once in the default case,
and in the PSS case build the WithTlv value directly via a new
constructor rather than round-tripping through write_single.

https://claude.ai/code/session_01HBR1P3RLnJ6r2avs6DZxUy
Comment thread docs/x509/reference.rst Outdated
:param public_key: The subject's public key. This can be one of
:data:`~cryptography.hazmat.primitives.asymmetric.types.CertificatePublicKeyTypes`.

:param rsa_padding: The uninstantiated

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.

@reaperhulk suggest you take a pass over this

"uninstantiated" is not in the dictionary; say "the PSS class (not an
instance)" instead.

https://claude.ai/code/session_01HBR1P3RLnJ6r2avs6DZxUy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants