Skip to content

Commit 7ee26a7

Browse files
committed
tests: port signature tests to Rust
1 parent c9a50ad commit 7ee26a7

File tree

57 files changed

+299
-1459
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+299
-1459
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ once_cell = "1.17.2"
9292
rcgen = { version = "0.14.2", default-features = false, features = ["aws_lc_rs"] }
9393
serde = { version = "1.0", features = ["derive"] }
9494
serde_json = "1.0"
95+
x509-parser = "0.18"
9596

9697
[profile.bench]
9798
opt-level = 3

tests/generate.py

Lines changed: 2 additions & 288 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
from cryptography import x509
1818
from cryptography.hazmat.primitives import hashes
19-
from cryptography.hazmat.primitives.asymmetric import rsa, ec, ed25519, padding
20-
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
19+
from cryptography.hazmat.primitives.asymmetric import rsa, ec, ed25519
20+
from cryptography.hazmat.primitives.serialization import Encoding
2121
from cryptography.hazmat.backends import default_backend
2222
from cryptography.x509.oid import NameOID
2323
import datetime
@@ -189,284 +189,6 @@ def ca_cert(
189189
)
190190

191191

192-
def signatures(force: bool) -> None:
193-
rsa_pub_exponent: int = 0x10001
194-
backend: Any = default_backend()
195-
all_key_types: dict[str, ANY_PRIV_KEY] = {
196-
"ed25519": ed25519.Ed25519PrivateKey.generate(),
197-
"ecdsa_p256": ec.generate_private_key(ec.SECP256R1(), backend),
198-
"ecdsa_p384": ec.generate_private_key(ec.SECP384R1(), backend),
199-
"ecdsa_p521": ec.generate_private_key(ec.SECP521R1(), backend),
200-
"rsa_1024_not_supported": rsa.generate_private_key(
201-
rsa_pub_exponent, 1024, backend
202-
),
203-
"rsa_2048": rsa.generate_private_key(rsa_pub_exponent, 2048, backend),
204-
"rsa_3072": rsa.generate_private_key(rsa_pub_exponent, 3072, backend),
205-
"rsa_4096": rsa.generate_private_key(rsa_pub_exponent, 4096, backend),
206-
}
207-
208-
feature_gates = {
209-
"ECDSA_P521_SHA256": 'all(not(feature = "ring"), feature = "aws-lc-rs")',
210-
"ECDSA_P521_SHA384": 'all(not(feature = "ring"), feature = "aws-lc-rs")',
211-
"ECDSA_P521_SHA512": 'all(not(feature = "ring"), feature = "aws-lc-rs")',
212-
}
213-
214-
rsa_types: list[str] = [
215-
"RSA_PKCS1_2048_8192_SHA256",
216-
"RSA_PKCS1_2048_8192_SHA384",
217-
"RSA_PKCS1_2048_8192_SHA512",
218-
"RSA_PSS_2048_8192_SHA256_LEGACY_KEY",
219-
"RSA_PSS_2048_8192_SHA384_LEGACY_KEY",
220-
"RSA_PSS_2048_8192_SHA512_LEGACY_KEY",
221-
]
222-
223-
webpki_algs: dict[str, Iterable[str]] = {
224-
"ed25519": ["ED25519"],
225-
"ecdsa_p256": ["ECDSA_P256_SHA384", "ECDSA_P256_SHA256"],
226-
"ecdsa_p384": ["ECDSA_P384_SHA384", "ECDSA_P384_SHA256"],
227-
"ecdsa_p521": ["ECDSA_P521_SHA512", "ECDSA_P521_SHA256", "ECDSA_P521_SHA384"],
228-
"rsa_2048": rsa_types,
229-
"rsa_3072": rsa_types + ["RSA_PKCS1_3072_8192_SHA384"],
230-
"rsa_4096": rsa_types + ["RSA_PKCS1_3072_8192_SHA384"],
231-
}
232-
233-
pss_sha256: padding.PSS = padding.PSS(
234-
mgf=padding.MGF1(hashes.SHA256()), salt_length=32
235-
)
236-
pss_sha384: padding.PSS = padding.PSS(
237-
mgf=padding.MGF1(hashes.SHA384()), salt_length=48
238-
)
239-
pss_sha512: padding.PSS = padding.PSS(
240-
mgf=padding.MGF1(hashes.SHA512()), salt_length=64
241-
)
242-
243-
how_to_sign: dict[str, SIGNER] = {
244-
"ED25519": lambda key, message: key.sign(message),
245-
"ECDSA_P256_SHA256": lambda key, message: key.sign(
246-
message, ec.ECDSA(hashes.SHA256())
247-
),
248-
"ECDSA_P256_SHA384": lambda key, message: key.sign(
249-
message, ec.ECDSA(hashes.SHA384())
250-
),
251-
"ECDSA_P384_SHA256": lambda key, message: key.sign(
252-
message, ec.ECDSA(hashes.SHA256())
253-
),
254-
"ECDSA_P384_SHA384": lambda key, message: key.sign(
255-
message, ec.ECDSA(hashes.SHA384())
256-
),
257-
"ECDSA_P521_SHA256": lambda key, message: key.sign(
258-
message, ec.ECDSA(hashes.SHA256())
259-
),
260-
"ECDSA_P521_SHA384": lambda key, message: key.sign(
261-
message, ec.ECDSA(hashes.SHA384())
262-
),
263-
"ECDSA_P521_SHA512": lambda key, message: key.sign(
264-
message, ec.ECDSA(hashes.SHA512())
265-
),
266-
"RSA_PKCS1_2048_8192_SHA256": lambda key, message: key.sign(
267-
message, padding.PKCS1v15(), hashes.SHA256()
268-
),
269-
"RSA_PKCS1_2048_8192_SHA384": lambda key, message: key.sign(
270-
message, padding.PKCS1v15(), hashes.SHA384()
271-
),
272-
"RSA_PKCS1_2048_8192_SHA512": lambda key, message: key.sign(
273-
message, padding.PKCS1v15(), hashes.SHA512()
274-
),
275-
"RSA_PKCS1_3072_8192_SHA384": lambda key, message: key.sign(
276-
message, padding.PKCS1v15(), hashes.SHA384()
277-
),
278-
"RSA_PSS_2048_8192_SHA256_LEGACY_KEY": lambda key, message: key.sign(
279-
message, pss_sha256, hashes.SHA256()
280-
),
281-
"RSA_PSS_2048_8192_SHA384_LEGACY_KEY": lambda key, message: key.sign(
282-
message, pss_sha384, hashes.SHA384()
283-
),
284-
"RSA_PSS_2048_8192_SHA512_LEGACY_KEY": lambda key, message: key.sign(
285-
message, pss_sha512, hashes.SHA512()
286-
),
287-
}
288-
289-
output_dir: str = "signatures"
290-
if not os.path.isdir(output_dir):
291-
os.mkdir(output_dir)
292-
293-
message = b"hello world!"
294-
message_path: str = os.path.join(output_dir, "message.bin")
295-
write_der(message_path, message, force)
296-
297-
def _cert_path(cert_type: str) -> str:
298-
return os.path.join(output_dir, f"{cert_type}.ee.der")
299-
300-
def _rpk_path(rpk_type: str) -> str:
301-
return os.path.join(output_dir, f"{rpk_type}.spki.der")
302-
303-
for name, private_key in all_key_types.items():
304-
ee_subject = x509.Name(
305-
[x509.NameAttribute(NameOID.ORGANIZATION_NAME, name + " test")]
306-
)
307-
issuer_subject = x509.Name(
308-
[x509.NameAttribute(NameOID.ORGANIZATION_NAME, name + " issuer")]
309-
)
310-
certificate: x509.Certificate = end_entity_cert(
311-
subject_name=ee_subject,
312-
subject_key=private_key,
313-
issuer_name=issuer_subject,
314-
)
315-
316-
rpk_pub_key = private_key.public_key().public_bytes(
317-
Encoding.DER, PublicFormat.SubjectPublicKeyInfo
318-
)
319-
write_der(_rpk_path(name), rpk_pub_key, force)
320-
write_der(_cert_path(name), certificate.public_bytes(Encoding.DER), force)
321-
322-
def _test(
323-
test_name: str, cert_type: str, algorithm: str, signature: bytes, expected: str
324-
) -> None:
325-
nonlocal message_path # noqa: F824
326-
cert_path: str = _cert_path(cert_type)
327-
rpk_path: str = _rpk_path(cert_type)
328-
lower_test_name: str = test_name.lower()
329-
330-
sig_path: str = os.path.join(output_dir, f"{lower_test_name}.sig.bin")
331-
write_der(sig_path, signature, force)
332-
feature_gate: str = feature_gates.get(algorithm, "")
333-
if feature_gate:
334-
feature_gate = f"#[cfg({feature_gate})]\n"
335-
336-
print(
337-
"""
338-
#[test]
339-
%(feature_gate)sfn %(lower_test_name)s() {
340-
let ee = include_bytes!("%(cert_path)s");
341-
let message = include_bytes!("%(message_path)s");
342-
let signature = include_bytes!("%(sig_path)s");
343-
assert_eq!(
344-
check_sig(ee, %(algorithm)s, message, signature),
345-
%(expected)s
346-
);
347-
}""" % locals(),
348-
file=output,
349-
)
350-
351-
print(
352-
"""
353-
#[test]
354-
%(feature_gate)sfn %(lower_test_name)s_rpk() {
355-
let rpk = include_bytes!("%(rpk_path)s");
356-
let message = include_bytes!("%(message_path)s");
357-
let signature = include_bytes!("%(sig_path)s");
358-
assert_eq!(
359-
check_sig_rpk(rpk, %(algorithm)s, message, signature),
360-
%(expected)s
361-
);
362-
}""" % locals(),
363-
file=output,
364-
)
365-
366-
def good_signature(
367-
test_name: str, cert_type: str, algorithm: str, signer: SIGNER
368-
) -> None:
369-
signature: bytes = signer(all_key_types[cert_type], message)
370-
_test(test_name, cert_type, algorithm, signature, expected="Ok(())")
371-
372-
def good_signature_but_rejected(
373-
test_name: str, cert_type: str, algorithm: str, signer: SIGNER
374-
) -> None:
375-
signature: bytes = signer(all_key_types[cert_type], message)
376-
_test(
377-
test_name,
378-
cert_type,
379-
algorithm,
380-
signature,
381-
expected="Err(webpki::Error::InvalidSignatureForPublicKey)",
382-
)
383-
384-
def bad_signature(
385-
test_name: str, cert_type: str, algorithm: str, signer: SIGNER
386-
) -> None:
387-
signature: bytes = signer(all_key_types[cert_type], message + b"?")
388-
_test(
389-
test_name,
390-
cert_type,
391-
algorithm,
392-
signature,
393-
expected="Err(webpki::Error::InvalidSignatureForPublicKey)",
394-
)
395-
396-
def bad_algorithms_for_key(
397-
test_name: str, cert_type: str, unusable_algs: set[str]
398-
) -> None:
399-
cert_path: str = _cert_path(cert_type)
400-
test_name_lower: str = test_name.lower()
401-
unusable_algs_str: str = ", ".join(alg for alg in sorted(unusable_algs))
402-
print(
403-
"""
404-
#[test]
405-
fn %(test_name_lower)s() {
406-
let ee = include_bytes!("%(cert_path)s");
407-
for algorithm in &[ %(unusable_algs_str)s ] {
408-
assert!(matches!(
409-
check_sig(ee, *algorithm, b"", b""),
410-
Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey(_))
411-
));
412-
}
413-
}""" % locals(),
414-
file=output,
415-
)
416-
417-
with trim_top("signatures.rs") as output:
418-
# compute all webpki algorithms covered by these tests
419-
all_webpki_algs: set[str] = set(
420-
[item for algs in webpki_algs.values() for item in algs]
421-
)
422-
423-
for type, algs in webpki_algs.items():
424-
for alg in algs:
425-
signer: SIGNER = how_to_sign[alg]
426-
good_signature(
427-
type + "_key_and_" + alg + "_good_signature",
428-
cert_type=type,
429-
algorithm=alg,
430-
signer=signer,
431-
)
432-
bad_signature(
433-
type + "_key_and_" + alg + "_detects_bad_signature",
434-
cert_type=type,
435-
algorithm=alg,
436-
signer=signer,
437-
)
438-
439-
unusable_algs = set(all_webpki_algs)
440-
for alg in algs:
441-
unusable_algs.remove(alg)
442-
443-
# special case: tested separately below
444-
if type == "rsa_2048":
445-
unusable_algs.remove("RSA_PKCS1_3072_8192_SHA384")
446-
447-
unusable_algs = {
448-
(
449-
"#[cfg(%s)] %s" % (feature_gates[alg], alg)
450-
if alg in feature_gates
451-
else alg
452-
)
453-
for alg in unusable_algs
454-
}
455-
456-
bad_algorithms_for_key(
457-
type + "_key_rejected_by_other_algorithms",
458-
cert_type=type,
459-
unusable_algs=unusable_algs,
460-
)
461-
462-
good_signature_but_rejected(
463-
"rsa_2048_key_rejected_by_RSA_PKCS1_3072_8192_SHA384",
464-
cert_type="rsa_2048",
465-
algorithm="RSA_PKCS1_3072_8192_SHA384",
466-
signer=signer,
467-
)
468-
469-
470192
def client_auth_revocation(force: bool) -> None:
471193
output_dir: str = "client_auth_revocation"
472194
if not os.path.isdir(output_dir):
@@ -1840,12 +1562,6 @@ def _expired_crl_enforce_expiration() -> None:
18401562

18411563
if __name__ == "__main__":
18421564
parser = argparse.ArgumentParser()
1843-
parser.add_argument(
1844-
"--signatures",
1845-
action=argparse.BooleanOptionalAction,
1846-
default=True,
1847-
help="Generate signature testcases",
1848-
)
18491565
parser.add_argument(
18501566
"--client-auth-revocation",
18511567
action=argparse.BooleanOptionalAction,
@@ -1872,8 +1588,6 @@ def _expired_crl_enforce_expiration() -> None:
18721588
)
18731589
args = parser.parse_args()
18741590

1875-
if args.signatures:
1876-
signatures(args.force)
18771591
if args.client_auth_revocation:
18781592
client_auth_revocation(args.force)
18791593

0 commit comments

Comments
 (0)