-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: expose the low-level X.509 signature layer #12018
Comments
I'm not sure I understand what the actual feature request is here, can you try rephrasing it? |
Thanks for the quick response :)
Sure! When exposed to Python, the Example API 1def compute_signature_algorithm(
private_key: CertificateIssuerPrivateKeyTypes,
algorithm: _AllowedHashTypes,
rsa_padding: padding.PSS | padding.PKCS1v15 | None,
) -> bytes:
"""
Serializes a hash algorithm and parameters into an X.509 signature algorithm,
returning its DER representation.
"""
...
def sign_data(
private_key: CertificateIssuerPrivateKeyTypes,
algorithm: _AllowedHashTypes,
rsa_padding: padding.PSS | padding.PKCS1v15 | None,
data: bytes,
) -> bytes:
"""
Signs the specified data with the private key, returning the X.509 signature.
"""
...
def verify_data(
issuer_public_key: CertificatePublicKeyTypes,
signature_algorithm: bytes,
signature: bytes,
data: bytes,
):
"""
Verifies an X.509 signature against the public key and specified data.
An `InvalidSignature` exception will be raised if the signature fails to verify.
"""
...
def identify_signature_algorithm(
signature_algorithm: bytes,
) -> tuple[_AllowedHashTypes, padding.PSS | padding.PKCS1v15 | ec.ECDSA | None]:
"""
Deserializes the DER representation of an X.509 signature algorithm
into the hash algorithm and its parameters.
"""
... 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 |
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? |
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 |
Sure! Off the top of my head:
|
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:The text was updated successfully, but these errors were encountered: