From e6d9682b2d2b2f175377582a0751ef5cbe70fd4d Mon Sep 17 00:00:00 2001 From: Alexis Date: Wed, 2 Oct 2024 15:24:07 +0200 Subject: [PATCH] Start tests --- rust/src/lib.rs | 41 ++++++++++++++-------- src/sigstore_tsp/_rust/__init__.pyi | 2 ++ src/sigstore_tsp/impl.py | 6 ---- src/sigstore_tsp/tsp.py | 13 +++++++ src/sigstore_tsp/verify.py | 19 ++++++++--- test/fixtures/response.tsr | Bin 0 -> 752 bytes test/fixtures/ts_chain.pem | 35 +++++++++++++++++++ test/test_verify.py | 51 ++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 25 deletions(-) delete mode 100644 src/sigstore_tsp/impl.py create mode 100644 test/fixtures/response.tsr create mode 100644 test/fixtures/ts_chain.pem create mode 100644 test/test_verify.py diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9039a39..31175d9 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -32,21 +32,24 @@ impl TimeStampReq { } #[getter] - fn nonce<'p>(&self, py: pyo3::Python<'p>) -> PyResult> { + fn nonce<'p>(&self, py: pyo3::Python<'p>) -> PyResult> { match self.raw.borrow_dependent().nonce { Some(nonce) => { let py_nonce = crate::util::big_asn1_uint_to_py(py, nonce)?; - Ok(py_nonce) + Ok(Some(py_nonce.into_py(py))) } - None => todo!(), + None => Ok(None), } } #[getter] - fn policy<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult> { + fn policy<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult> { match &self.raw.borrow_dependent().req_policy { - Some(req_policy) => crate::util::oid_to_py_oid(py, &req_policy), - None => todo!(), + Some(req_policy) => { + let py_oid = crate::util::oid_to_py_oid(py, &req_policy)?; + Ok(Some(py_oid.into_py(py))) + }, + None => Ok(None), } } @@ -59,9 +62,14 @@ impl TimeStampReq { fn message_imprint(&self, py: pyo3::Python<'_>) -> PyResult { Ok(PyMessageImprint { contents: OwnedMessageImprint::try_new(self.raw.borrow_owner().clone_ref(py), |v| { - RawMessageImprint::parse_data(v.as_bytes(py)) - }) - .map_err(|_| PyValueError::new_err("invalid message imprint"))?, + + let req = asn1::parse_single::(v.as_bytes(py)) + .map_err(|e| PyValueError::new_err(format!("invalid message imprint: {:?}", e))); + match req { + Ok(res) => Ok(res.message_imprint), + Err(_) => Err(PyValueError::new_err("Unable to retrieve message imprint")) + } + }).map_err(|e| PyValueError::new_err(format!("invalid message imprint: {:?}", e)))?, }) } @@ -371,10 +379,13 @@ impl PyTSTInfo { } #[getter] - fn policy<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult> { + fn policy<'p>(&self, py: pyo3::Python<'p>) -> pyo3::PyResult> { match &self.raw.borrow_dependent().policy { - Some(req_policy) => crate::util::oid_to_py_oid(py, &req_policy), - None => todo!(), + Some(req_policy) => { + let py_oid = crate::util::oid_to_py_oid(py, &req_policy)?; + Ok(Some(py_oid.into_py(py))) + }, + None => Ok(None), } } @@ -418,13 +429,13 @@ impl PyTSTInfo { } #[getter] - fn nonce<'p>(&self, py: Python<'p>) -> PyResult> { + fn nonce<'p>(&self, py: Python<'p>) -> PyResult> { match self.raw.borrow_dependent().nonce { Some(nonce) => { let py_nonce = crate::util::big_asn1_uint_to_py(py, nonce)?; - Ok(py_nonce) + Ok(Some(py_nonce.into_py(py))) } - None => todo!(), + None => Ok(None), } } diff --git a/src/sigstore_tsp/_rust/__init__.pyi b/src/sigstore_tsp/_rust/__init__.pyi index 0304bdf..9fd5ce4 100644 --- a/src/sigstore_tsp/_rust/__init__.pyi +++ b/src/sigstore_tsp/_rust/__init__.pyi @@ -14,6 +14,8 @@ class PyTSTInfo: ... class Accuracy: ... +class SignerInfo: ... + def create_timestamp_request( data: bytes, ) -> TimeStampRequest: ... diff --git a/src/sigstore_tsp/impl.py b/src/sigstore_tsp/impl.py deleted file mode 100644 index 8fbd1fc..0000000 --- a/src/sigstore_tsp/impl.py +++ /dev/null @@ -1,6 +0,0 @@ -"""The sigstore-tsp entrypoint.""" - - -def verify_request() -> bool: - """TODO.""" - return False diff --git a/src/sigstore_tsp/tsp.py b/src/sigstore_tsp/tsp.py index a3d68d4..534d707 100644 --- a/src/sigstore_tsp/tsp.py +++ b/src/sigstore_tsp/tsp.py @@ -197,5 +197,18 @@ def certificates(self) -> set[bytes]: Warning: they are returned as a byte array and should be loaded. """ + @property + @abc.abstractmethod + def signer_infos(self) -> set[SignerInfo]: + """Returns the signers infos.""" SignedData.register(_rust.SignedData) + + +class SignerInfo(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def version(self) -> int: + """Returns the version.""" + +SignerInfo.register(_rust.SignerInfo) \ No newline at end of file diff --git a/src/sigstore_tsp/verify.py b/src/sigstore_tsp/verify.py index 3d66650..a322f8a 100644 --- a/src/sigstore_tsp/verify.py +++ b/src/sigstore_tsp/verify.py @@ -85,11 +85,22 @@ def _verify_tsr_with_chains(tsp_response: TimeStampResponse, opts: VerifyOpts) - return False signed_data = tsp_response.signed_data - if not signed_data.certificates and opts.tsa_certificate: - # TODO(dm) I can't assign here because that's read only - signed_data.certificates = opts.tsa_certificate - # TODO(dm) + verification_certificate = [] + if signed_data.certificates: + verification_certificate = signed_data.certificates + elif not signed_data.certificates and opts.tsa_certificate: + verification_certificate = [ opts.tsa_certificate ] + + # https://github.com/digitorus/pkcs7/blob/3a137a8743524b3683ca4e11608d0dde37caee99/verify.go#L74 + if len(signed_data.signer_infos) == 0: + # No signer presents + return False + + # TODO(dm): Here we would need to check the pkcs7 signer info + # and verify the signature. Instead of implementing it here, + # we should probably leverage some library that already does that. + return True diff --git a/test/fixtures/response.tsr b/test/fixtures/response.tsr new file mode 100644 index 0000000000000000000000000000000000000000..e6c2554281f0eaebbcc9c891d7d61a897dd9ae54 GIT binary patch literal 752 zcmXqLVtQl1%*4oG(8TnZjZ>@5qwPB{BRkWACZ;Pup$klm%!a%M+-#f)Z61uN%q&cd z28}n_xS{Ge1sGWtG@fK>JZ8{%n2C{*jZ>SAk%h5&zX6j$paCyJ7c(mZi^J@~ODcPl zK3Bv}yOMSJm2I)%?B9nvc~;$ebpN}mWV+3AW#z*+XD>Y{TT^u=Ph`i@TRd*b?lF2X zR` zO86~kVpKDbf(JDxFnm$M6X-%%G;p#S@{9ALdQKl9%VMa+qR?45=dAmzKTWn0U%hVe5fe!Ve`50sxIW B{ptV! literal 0 HcmV?d00001 diff --git a/test/fixtures/ts_chain.pem b/test/fixtures/ts_chain.pem new file mode 100644 index 0000000..abbbe74 --- /dev/null +++ b/test/fixtures/ts_chain.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIBzTCCAXKgAwIBAgIUB1fm1kzgFYdI15DCbcR2znmNvX4wCgYIKoZIzj0EAwIw +MDEOMAwGA1UEChMFbG9jYWwxHjAcBgNVBAMTFVRlc3QgVFNBIEludGVybWVkaWF0 +ZTAeFw0yNDA5MjYxNTA0MzFaFw0zMzA5MjYxNTA3MzFaMDAxDjAMBgNVBAoTBWxv +Y2FsMR4wHAYDVQQDExVUZXN0IFRTQSBUaW1lc3RhbXBpbmcwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAAQJLOx1er3OYoh8ye10R33JuC36SdbPUeX/0OrzIfPLIzNt +318lOG4/M88AcQUUP8/cfvUYDfYvz13Y/BKqyJqgo2owaDAOBgNVHQ8BAf8EBAMC +B4AwHQYDVR0OBBYEFDR3RW7sspK5nJCgMYmaLjLzQgsGMB8GA1UdIwQYMBaAFALx +2SsaTew5WmKDNUC5W7rBxmnbMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMAoGCCqG +SM49BAMCA0kAMEYCIQCf9psWVtVrxe4x9EhAqHB1p6HAbOO1T7eFAxkzUedOpgIh +APpWUuquAsYBmwKOYSf/X4WconKtNy0tm44N3WhiH5rz +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB0jCCAXigAwIBAgIUHol7Z0HtRLCFwDD2ymKu/VcyZ9UwCgYIKoZIzj0EAwIw +KDEOMAwGA1UEChMFbG9jYWwxFjAUBgNVBAMTDVRlc3QgVFNBIFJvb3QwHhcNMjQw +OTI2MTUwMjMxWhcNMzQwOTI2MTUwNzMxWjAwMQ4wDAYDVQQKEwVsb2NhbDEeMBwG +A1UEAxMVVGVzdCBUU0EgSW50ZXJtZWRpYXRlMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAEGzVCgkzDQkMqxRlXPXADxFS7/e8u3/j/PI4xkOZtdN6/9PPPdy5IimK/ +OoQq8sNmMfb0CriyIYnKoNwJ1dUdHKN4MHYwDgYDVR0PAQH/BAQDAgEGMBMGA1Ud +JQQMMAoGCCsGAQUFBwMIMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFALx2Ssa +Tew5WmKDNUC5W7rBxmnbMB8GA1UdIwQYMBaAFCnKpTwP0pqRflw/vUbiZHjEZc5M +MAoGCCqGSM49BAMCA0gAMEUCIQC9S7bLB8bi4cjMEaX4ZAooXT7vNbAvfseaWUW8 +dv8RQwIgZgQF1amkhMU1H8aXprou7vYH5rbmzuElAyS6TA//kzs= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBkzCCATqgAwIBAgIUGpdpdZ+1u5mA3jRd/m7vmPQWyWMwCgYIKoZIzj0EAwIw +KDEOMAwGA1UEChMFbG9jYWwxFjAUBgNVBAMTDVRlc3QgVFNBIFJvb3QwHhcNMjQw +OTI2MTUwMjMxWhcNMzQwOTI2MTUwNzMxWjAoMQ4wDAYDVQQKEwVsb2NhbDEWMBQG +A1UEAxMNVGVzdCBUU0EgUm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIA1 +o2pTsSxfoQRya3E+GAM7a9cOrvhG5mkpEsQaO+7CL3VyIpszValjuqYwDpEaXei9 +Ko9P5iEoru0uYEmoT7mjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBQpyqU8D9KakX5cP71G4mR4xGXOTDAKBggqhkjOPQQDAgNH +ADBEAiAggb/CCtUjIxxuoffQ85ugdylmO9xYgzhjJqcjiYodQwIgT8h6J4Ace2on +JkTMML0sqzXiqoUfwMzWJlBhKC7y12Y= +-----END CERTIFICATE----- diff --git a/test/test_verify.py b/test/test_verify.py new file mode 100644 index 0000000..6c0e426 --- /dev/null +++ b/test/test_verify.py @@ -0,0 +1,51 @@ +from pathlib import Path +import cryptography.x509 + +from sigstore_tsp.base import TimestampRequestBuilder, decode_timestamp_response +from sigstore_tsp.verify import verify_timestamp_response, VerifyOpts, create_verify_opts + + + +_HERE = Path(__file__).parent.resolve() +_FIXTURE = _HERE / "fixtures" + +def test_create_verify_opts(): + request = TimestampRequestBuilder().data(b"hello").build() + + certificates = cryptography.x509.load_pem_x509_certificates( + (_FIXTURE / "ts_chain.pem").read_bytes() + ) + + verify_opts = create_verify_opts( + request, + tsa_certifiate=certificates[0], + common_name=certificates[0].subject.rfc4514_string(), + root_certificates=[certificates[-1]], + intermediates=certificates[1:-1], + ) + + assert verify_opts.nonce == request.nonce + assert verify_opts.policy_id == request.policy + assert verify_opts.tsa_certificate == certificates[0] + + +def test_verify(): + + request = TimestampRequestBuilder().data(b"hello").build() + response = (_FIXTURE / "response.tsr").read_bytes() + certificates = cryptography.x509.load_pem_x509_certificates( + (_FIXTURE / "ts_chain.pem").read_bytes() + ) + + verify_opts = create_verify_opts( + request, + tsa_certifiate=certificates[0], + common_name=certificates[0].subject.rfc4514_string(), + root_certificates=[certificates[-1]], + ) + + verify_timestamp_response( + timestamp_response=decode_timestamp_response(response), + hashed_message=request.message_imprint.message, + verify_opts=verify_opts, + )