Open mildsunrise opened 5 days ago
I'm not sure I understand what the actual feature request is here, can you try rephrasing it?
Thanks for the quick response :)
I'm not sure I understand what the actual feature request is here, can you try rephrasing it?
Sure! When exposed to Python, the x509::sign
API could look like this:
I'm making a ~simple and direct translation here, but from what I see in the existing Python APIs maybe a class with methods would be preferred:
class SignatureAlgorithm:
"""
An X.509 signature algorithm.
"""
def __init__(
self,
oid: ObjectIdentifier,
parameters: padding.PSS | padding.PKCS1v15 | ec.ECDSA | None,
):
"""
Creates an signature algorithm object from its parsed components.
This does not validate that `parameters` has the correct type for `oid`.
"""
...
# PROPERTIES (these already exist in each X.509 object,
# with a `signature_` prefix)
@property
def oid(self) -> ObjectIdentifier:
"""
Returns the `ObjectIdentifier` for the algorithm to be used.
This will be one of the OIDs from `SignatureAlgorithmOID`.
"""
...
@property
def parameters(self) -> padding.PSS | padding.PKCS1v15 | ec.ECDSA | None:
"""
Returns the parameters of the signature algorithm.
For RSA signatures it will return either a `PKCS1v15` or `PSS` object.
For ECDSA signatures it will return an `ECDSA` object.
For EdDSA and DSA signatures it will return None.
"""
...
@property
def hash_algorithm(self) -> HashAlgorithm | None:
"""
Returns the `HashAlgorithm` to be used as part of the signature algorithm.
Can be `None` if signature does not use separate hash (ED25519, ED448).
"""
...
# EXPOSED API
@static_method
def create(
private_key: CertificateIssuerPrivateKeyTypes,
algorithm: _AllowedHashTypes | None,
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
) -> SignatureAlgorithm:
"""
Prepares a X.509 signature algorithm to use with the specified key.
This method fails if the passed `algorithm` and `rsa_padding` do
not match the type of `private_key`.
"""
...
@static_method
def load(der: bytes) -> SignatureAlgorithm:
"""
Loads an X.509 signature algorithm from its DER representation.
"""
...
def bytes(self) -> bytes:
"""
Serializes an X.509 signature algorithm into its DER representation.
"""
...
def sign(
self,
private_key: CertificateIssuerPrivateKeyTypes,
data: bytes,
) -> bytes:
"""
Signs the specified data with the private key, returning the X.509 signature.
This method fails if the passed key is not compatible with the signature algorithm.
"""
...
def verify(
self,
issuer_public_key: CertificatePublicKeyTypes,
signature: bytes,
data: bytes,
):
"""
Verifies an X.509 signature against the public key and specified data.
This method fails if the passed key is not compatible with the signature algorithm.
An `InvalidSignature` exception will be raised if the signature fails to verify.
"""
...
This would allow:
...without having to resort to algorithm-dependent operations. For example, the documentation I linked earlier could now tell users to do this:
x509.SignatureAlgorithm(
cert_to_check.signature_algorithm_oid,
cert_to_check.signature_algorithm_parameters,
).verify(
issuer_public_key,
cert_to_check.signature,
cert_to_check.tbs_certificate_bytes,
)
(or x509.Certificate
could directly expose a signature_algorithm
property rather than exposing the 3 properties separately. and the same with the other objects)
Are there other motivations for this besides the two you listed?
In general, working with broken X.509 structures isn't really something we endeavor to support.
For non-X.509 structures that use the same signatures structures, can you give us an example?
For non-X.509 structures that use the same signatures structures, can you give us an example?
This would also reduce the need for custom code to create and verify APK signatures -- which use X.509 certificates but custom signature formats (though unlike later bespoke formats the legacy format is based on PKCS#7) -- in apksigtool
and androguard
.
For non-X.509 structures that use the same signatures structures, can you give us an example?
Sure! Off the top of my head:
IKEv2 supports X.509 (referred here as PKIX) signatures via RFC7427:
The Internet Key Exchange Version 2 (IKEv2) protocol has limited support for the Elliptic Curve Digital Signature Algorithm (ECDSA). [...] This document generalizes IKEv2 signature support to allow any signature method supported by PKIX and also adds signature hash algorithm negotiation. This is a generic mechanism and is not limited to ECDSA; it can also be used with other signature algorithms.
SPKAC, a simpler version of the PKCS#10 CSR that (like the PKCS#10 CSR) uses X.509 to self-sign the public key. It almost made its way into HTML5's <keygen>
element. (this was the use case that prompted me to suggest the feature)
as @obfusk points above, PKCS#7 Cryptographic Message Syntax (CMS), a generic encrypted/signed container for all kinds of content. Forms the basis for S/MIME.
There are several
sign(self, privkey, algorithm, padding)
methods, but they all work on high-level builders of different types. There doesn't seem to be a method that operates on an arbitrary to-be-signed byte string. A similar thing happens with verification.The logic to encode, decode, create and verify X.509 signatures is neatly encapsulated in the
x509::sign
module, and exposing it to Python would be very helpful. Otherwise, as the current documentation states, creating or verifying X.509 signatures requires highly algorithm-specific code: