Skip to content

Commit 1597c27

Browse files
reaperhulkemlunalex
authored
Add parameter ecdsa_deterministic to x509 sign methods (#12887)
* Add parameter ecdsa_deterministic_signing to x509 sign methods * simplify * Boolean -> boolean also some whitespace trimming and text rewrap * also simplify the tests, we don't need to derive keys this way * simplify even more * change to ecdsa_deterministic, check if the right key type is used * Apply suggestions from code review Co-authored-by: Alex Gaynor <[email protected]> * format --------- Co-authored-by: Emil Lundberg <[email protected]> Co-authored-by: Alex Gaynor <[email protected]>
1 parent 9a0881d commit 1597c27

File tree

13 files changed

+312
-18
lines changed

13 files changed

+312
-18
lines changed

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ Changelog
6969
involving small buffers.
7070
* Added :func:`~cryptography.hazmat.primitives.serialization.ssh_key_fingerprint`
7171
for computing fingerprints of SSH public keys.
72+
* Added support for deterministic ECDSA signing via the new keyword-only argument
73+
``ecdsa_deterministic`` in :meth:`~cryptography.x509.CertificateBuilder.sign`,
74+
:meth:`~cryptography.x509.CertificateRevocationListBuilder.sign`
75+
and :meth:`~cryptography.x509.CertificateSigningRequestBuilder.sign`.
7276

7377
.. _v44-0-3:
7478

docs/x509/reference.rst

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ X.509 Certificate Builder
885885
:canonical: cryptography.x509.base.CertificateBuilder
886886

887887
.. versionadded:: 1.0
888-
888+
889889
.. note::
890890
All methods, except :meth:`sign`, return a **new** CertificateBuilder
891891
instance with the corresponding updated value. They do not modify the
@@ -936,7 +936,7 @@ X.509 Certificate Builder
936936

937937
:param name: The :class:`~cryptography.x509.Name` that describes the
938938
issuer (CA).
939-
939+
940940
:return: A new :class:`CertificateBuilder` with the updated issuer name.
941941

942942
.. method:: subject_name(name)
@@ -945,7 +945,7 @@ X.509 Certificate Builder
945945

946946
:param name: The :class:`~cryptography.x509.Name` that describes the
947947
subject.
948-
948+
949949
:return: A new :class:`CertificateBuilder` with the updated subject name.
950950

951951
.. method:: public_key(public_key)
@@ -954,7 +954,7 @@ X.509 Certificate Builder
954954

955955
:param public_key: The subject's public key. This can be one of
956956
:data:`~cryptography.hazmat.primitives.asymmetric.types.CertificatePublicKeyTypes`.
957-
957+
958958
:return: A new :class:`CertificateBuilder` with the updated public key.
959959

960960
.. method:: serial_number(serial_number)
@@ -971,7 +971,7 @@ X.509 Certificate Builder
971971
identify this certificate (most notably during certificate
972972
revocation checking). Users should consider using
973973
:func:`~cryptography.x509.random_serial_number` when possible.
974-
974+
975975
:return: A new :class:`CertificateBuilder` with the updated serial number.
976976

977977
.. method:: not_valid_before(time)
@@ -983,7 +983,7 @@ X.509 Certificate Builder
983983
:param time: The :class:`datetime.datetime` object (in UTC) that marks the
984984
activation time for the certificate. The certificate may not be
985985
trusted clients if it is used before this time.
986-
986+
987987
:return: A new :class:`CertificateBuilder` with the updated activation time.
988988

989989
.. method:: not_valid_after(time)
@@ -995,7 +995,7 @@ X.509 Certificate Builder
995995
:param time: The :class:`datetime.datetime` object (in UTC) that marks the
996996
expiration time for the certificate. The certificate may not be
997997
trusted clients if it is used after this time.
998-
998+
999999
:return: A new :class:`CertificateBuilder` with the updated expiration time.
10001000

10011001
.. method:: add_extension(extval, critical)
@@ -1007,10 +1007,10 @@ X.509 Certificate Builder
10071007

10081008
:param critical: Set to ``True`` if the extension must be understood and
10091009
handled by whoever reads the certificate.
1010-
1010+
10111011
:return: A new :class:`CertificateBuilder` with the additional extension.
10121012

1013-
.. method:: sign(private_key, algorithm, *, rsa_padding=None)
1013+
.. method:: sign(private_key, algorithm, *, rsa_padding=None, ecdsa_deterministic=None)
10141014

10151015
Sign the certificate using the CA's private key.
10161016

@@ -1045,6 +1045,20 @@ X.509 Certificate Builder
10451045
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`,
10461046
or :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
10471047

1048+
:param ecdsa_deterministic:
1049+
1050+
.. versionadded:: 45.0.0
1051+
1052+
This is a keyword-only argument. If ``private_key`` is an
1053+
``EllipticCurvePrivateKey`` then this can be set to true to
1054+
use deterministic signing, as defined in :rfc:`6979`. This only
1055+
impacts the signing process, verification is not affected
1056+
(the verification process is the same for both
1057+
deterministic and non-deterministic signed messages). All other
1058+
key types **must** not pass a value other than ``None``.
1059+
1060+
:type ecdsa_deterministic: ``None``, ``bool``
1061+
10481062
:returns: :class:`~cryptography.x509.Certificate`
10491063

10501064

@@ -1285,7 +1299,7 @@ X.509 Certificate Revocation List Builder
12851299
obtained from an existing CRL or created with
12861300
:class:`~cryptography.x509.RevokedCertificateBuilder`.
12871301

1288-
.. method:: sign(private_key, algorithm, *, rsa_padding=None)
1302+
.. method:: sign(private_key, algorithm, *, rsa_padding=None, ecdsa_deterministic=None)
12891303

12901304
Sign this CRL using the CA's private key.
12911305

@@ -1320,6 +1334,20 @@ X.509 Certificate Revocation List Builder
13201334
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`,
13211335
or :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
13221336

1337+
:param ecdsa_deterministic:
1338+
1339+
.. versionadded:: 45.0.0
1340+
1341+
This is a keyword-only argument. If ``private_key`` is an
1342+
``EllipticCurvePrivateKey`` then this can be set to true to
1343+
use deterministic signing, as defined in :rfc:`6979`. This only
1344+
impacts the signing process, verification is not affected
1345+
(the verification process is the same for both
1346+
deterministic and non-deterministic signed messages). All other
1347+
key types **must** not pass a value other than ``None``.
1348+
1349+
:type ecdsa_deterministic: ``None``, ``bool``
1350+
13231351
:returns: :class:`~cryptography.x509.CertificateRevocationList`
13241352

13251353
X.509 Revoked Certificate Object
@@ -1498,7 +1526,7 @@ X.509 CSR (Certificate Signing Request) Builder Object
14981526
:returns: A new
14991527
:class:`~cryptography.x509.CertificateSigningRequestBuilder`.
15001528

1501-
.. method:: sign(private_key, algorithm, *, rsa_padding=None)
1529+
.. method:: sign(private_key, algorithm, *, rsa_padding=None, ecdsa_deterministic=None)
15021530

15031531
:param private_key: The private key
15041532
that will be used to sign the request. When the request is
@@ -1533,6 +1561,20 @@ X.509 CSR (Certificate Signing Request) Builder Object
15331561
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`,
15341562
or :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
15351563

1564+
:param ecdsa_deterministic:
1565+
1566+
.. versionadded:: 45.0.0
1567+
1568+
This is a keyword-only argument. If ``private_key`` is an
1569+
``EllipticCurvePrivateKey`` then this can be set to true to
1570+
use deterministic signing, as defined in :rfc:`6979`. This only
1571+
impacts the signing process, verification is not affected
1572+
(the verification process is the same for both
1573+
deterministic and non-deterministic signed messages). All other
1574+
key types **must** not pass a value other than ``None``.
1575+
1576+
:type ecdsa_deterministic: ``None``, ``bool``
1577+
15361578
:returns: A new
15371579
:class:`~cryptography.x509.CertificateSigningRequest`.
15381580

@@ -2541,7 +2583,7 @@ X.509 Extensions
25412583
:return: A ``bytes`` object containing the encoded extension.
25422584

25432585
.. attribute:: oid
2544-
2586+
25452587
:type: :class:`ObjectIdentifier`
25462588

25472589
Returns

src/cryptography/hazmat/bindings/_rust/x509.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,21 @@ def create_x509_certificate(
4545
private_key: PrivateKeyTypes,
4646
hash_algorithm: hashes.HashAlgorithm | None,
4747
rsa_padding: PKCS1v15 | PSS | None,
48+
ecdsa_deterministic: bool | None,
4849
) -> x509.Certificate: ...
4950
def create_x509_csr(
5051
builder: x509.CertificateSigningRequestBuilder,
5152
private_key: PrivateKeyTypes,
5253
hash_algorithm: hashes.HashAlgorithm | None,
5354
rsa_padding: PKCS1v15 | PSS | None,
55+
ecdsa_deterministic: bool | None,
5456
) -> x509.CertificateSigningRequest: ...
5557
def create_x509_crl(
5658
builder: x509.CertificateRevocationListBuilder,
5759
private_key: PrivateKeyTypes,
5860
hash_algorithm: hashes.HashAlgorithm | None,
5961
rsa_padding: PKCS1v15 | PSS | None,
62+
ecdsa_deterministic: bool | None,
6063
) -> x509.CertificateRevocationList: ...
6164

6265
class Sct:

src/cryptography/x509/base.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ def sign(
331331
backend: typing.Any = None,
332332
*,
333333
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
334+
ecdsa_deterministic: bool | None = None,
334335
) -> CertificateSigningRequest:
335336
"""
336337
Signs the request using the requestor's private key.
@@ -344,8 +345,18 @@ def sign(
344345
if not isinstance(private_key, rsa.RSAPrivateKey):
345346
raise TypeError("Padding is only supported for RSA keys")
346347

348+
if ecdsa_deterministic is not None:
349+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
350+
raise TypeError(
351+
"Deterministic ECDSA is only supported for EC keys"
352+
)
353+
347354
return rust_x509.create_x509_csr(
348-
self, private_key, algorithm, rsa_padding
355+
self,
356+
private_key,
357+
algorithm,
358+
rsa_padding,
359+
ecdsa_deterministic,
349360
)
350361

351362

@@ -560,6 +571,7 @@ def sign(
560571
backend: typing.Any = None,
561572
*,
562573
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
574+
ecdsa_deterministic: bool | None = None,
563575
) -> Certificate:
564576
"""
565577
Signs the certificate using the CA's private key.
@@ -588,8 +600,18 @@ def sign(
588600
if not isinstance(private_key, rsa.RSAPrivateKey):
589601
raise TypeError("Padding is only supported for RSA keys")
590602

603+
if ecdsa_deterministic is not None:
604+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
605+
raise TypeError(
606+
"Deterministic ECDSA is only supported for EC keys"
607+
)
608+
591609
return rust_x509.create_x509_certificate(
592-
self, private_key, algorithm, rsa_padding
610+
self,
611+
private_key,
612+
algorithm,
613+
rsa_padding,
614+
ecdsa_deterministic,
593615
)
594616

595617

@@ -717,6 +739,7 @@ def sign(
717739
backend: typing.Any = None,
718740
*,
719741
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
742+
ecdsa_deterministic: bool | None = None,
720743
) -> CertificateRevocationList:
721744
if self._issuer_name is None:
722745
raise ValueError("A CRL must have an issuer name")
@@ -733,8 +756,18 @@ def sign(
733756
if not isinstance(private_key, rsa.RSAPrivateKey):
734757
raise TypeError("Padding is only supported for RSA keys")
735758

759+
if ecdsa_deterministic is not None:
760+
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
761+
raise TypeError(
762+
"Deterministic ECDSA is only supported for EC keys"
763+
)
764+
736765
return rust_x509.create_x509_crl(
737-
self, private_key, algorithm, rsa_padding
766+
self,
767+
private_key,
768+
algorithm,
769+
rsa_padding,
770+
ecdsa_deterministic,
738771
)
739772

740773

src/rust/src/pkcs7.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ fn sign_and_serialize<'p>(
517517
py_private_key.clone(),
518518
py_hash_alg.clone(),
519519
rsa_padding.clone(),
520+
None,
520521
&data_with_header,
521522
)?,
522523
)
@@ -566,6 +567,7 @@ fn sign_and_serialize<'p>(
566567
py_private_key.clone(),
567568
py_hash_alg.clone(),
568569
rsa_padding.clone(),
570+
None,
569571
&signed_data,
570572
)?,
571573
)

src/rust/src/x509/certificate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ pub(crate) fn create_x509_certificate(
10031003
private_key: &pyo3::Bound<'_, pyo3::PyAny>,
10041004
hash_algorithm: &pyo3::Bound<'_, pyo3::PyAny>,
10051005
rsa_padding: &pyo3::Bound<'_, pyo3::PyAny>,
1006+
ecdsa_deterministic: Option<bool>,
10061007
) -> CryptographyResult<Certificate> {
10071008
let sigalg = x509::sign::compute_signature_algorithm(
10081009
py,
@@ -1065,6 +1066,7 @@ pub(crate) fn create_x509_certificate(
10651066
private_key.clone(),
10661067
hash_algorithm.clone(),
10671068
rsa_padding.clone(),
1069+
ecdsa_deterministic,
10681070
&tbs_bytes,
10691071
)?;
10701072
let data = asn1::write_single(&cryptography_x509::certificate::Certificate {

src/rust/src/x509/crl.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ pub(crate) fn create_x509_crl(
629629
private_key: &pyo3::Bound<'_, pyo3::PyAny>,
630630
hash_algorithm: &pyo3::Bound<'_, pyo3::PyAny>,
631631
rsa_padding: &pyo3::Bound<'_, pyo3::PyAny>,
632+
ecdsa_deterministic: Option<bool>,
632633
) -> CryptographyResult<CertificateRevocationList> {
633634
let sigalg = x509::sign::compute_signature_algorithm(
634635
py,
@@ -696,6 +697,7 @@ pub(crate) fn create_x509_crl(
696697
private_key.clone(),
697698
hash_algorithm.clone(),
698699
rsa_padding.clone(),
700+
ecdsa_deterministic,
699701
&tbs_bytes,
700702
)?;
701703
let data = asn1::write_single(&crl::CertificateRevocationList {

src/rust/src/x509/csr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ pub(crate) fn create_x509_csr(
292292
private_key: &pyo3::Bound<'_, pyo3::PyAny>,
293293
hash_algorithm: &pyo3::Bound<'_, pyo3::PyAny>,
294294
rsa_padding: &pyo3::Bound<'_, pyo3::PyAny>,
295+
ecdsa_deterministic: Option<bool>,
295296
) -> CryptographyResult<CertificateSigningRequest> {
296297
let sigalg = x509::sign::compute_signature_algorithm(
297298
py,
@@ -381,6 +382,7 @@ pub(crate) fn create_x509_csr(
381382
private_key.clone(),
382383
hash_algorithm.clone(),
383384
rsa_padding.clone(),
385+
ecdsa_deterministic,
384386
&tbs_bytes,
385387
)?;
386388
let data = asn1::write_single(&Csr {

src/rust/src/x509/ocsp_resp.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ pub(crate) fn create_ocsp_response(
834834
private_key.clone(),
835835
hash_algorithm.clone(),
836836
py.None().into_bound(py),
837+
None,
837838
&tbs_bytes,
838839
)?;
839840

src/rust/src/x509/sign.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ pub(crate) fn sign_data<'p>(
285285
private_key: pyo3::Bound<'p, pyo3::PyAny>,
286286
hash_algorithm: pyo3::Bound<'p, pyo3::PyAny>,
287287
rsa_padding: pyo3::Bound<'p, pyo3::PyAny>,
288+
ecdsa_deterministic: Option<bool>,
288289
data: &[u8],
289290
) -> pyo3::PyResult<PyBackedBytes> {
290291
let key_type = identify_key_type(py, private_key.clone())?;
@@ -294,7 +295,9 @@ pub(crate) fn sign_data<'p>(
294295
private_key.call_method1(pyo3::intern!(py, "sign"), (data,))?
295296
}
296297
KeyType::Ec => {
297-
let ecdsa = types::ECDSA.get(py)?.call1((hash_algorithm,))?;
298+
let ecdsa = types::ECDSA
299+
.get(py)?
300+
.call1((hash_algorithm, ecdsa_deterministic.unwrap_or(false)))?;
298301
private_key.call_method1(pyo3::intern!(py, "sign"), (data, ecdsa))?
299302
}
300303
KeyType::Rsa => {

0 commit comments

Comments
 (0)