diff --git a/Cargo.toml b/Cargo.toml index 05077427..98df448c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ toml = { version = "0.5.9", default-features = false } [dev-dependencies] tower = { version = "^0.4.11", features = ["util"] } +axum = "^0.5.1" http = "^0.2.6" memoffset = "0.6.4" testaso = "0.1" diff --git a/certs/test/crt.der b/certs/test/crt.der deleted file mode 100644 index 2c0fd8e3..00000000 Binary files a/certs/test/crt.der and /dev/null differ diff --git a/certs/test/key.der b/certs/test/key.der deleted file mode 100644 index eac407d0..00000000 Binary files a/certs/test/key.der and /dev/null differ diff --git a/src/main.rs b/src/main.rs index cbebd865..c6bf592c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,6 +78,7 @@ struct Args { } #[derive(Debug)] +#[cfg_attr(test, derive(Clone))] struct State { key: Zeroizing>, crt: Vec, @@ -364,19 +365,17 @@ mod tests { use x509::request::CertReqInfo; use x509::{ext::Extension, name::RdnSequence}; + use axum::response::Response; use http::{header::CONTENT_TYPE, Request}; use hyper::Body; use tower::ServiceExt; // for `app.oneshot()` - const CRT: &[u8] = include_bytes!("../certs/test/crt.der"); - const KEY: &[u8] = include_bytes!("../certs/test/key.der"); + fn certificates_state() -> State { + State::load(None, "testdata/ca.key", "testdata/ca.crt").unwrap() + } - fn state() -> State { - State { - key: KEY.to_owned().into(), - crt: CRT.into(), - san: None, - } + fn hostname_state() -> State { + State::generate(None, "localhost").unwrap() } fn cr(curve: ObjectIdentifier, exts: Vec>) -> Vec { @@ -403,6 +402,15 @@ mod tests { cri.sign(&pki).unwrap() } + async fn attest_response(state: State, response: Response) { + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let path = PkiPath::from_der(&body).unwrap(); + let issr = Certificate::from_der(&state.crt).unwrap(); + assert_eq!(2, path.0.len()); + assert_eq!(issr, path.0[0]); + issr.tbs_certificate.verify_crt(&path.0[1]).unwrap(); + } + #[test] fn reencode() { let encoded = cr(SECP_256_R_1, vec![]); @@ -418,7 +426,7 @@ mod tests { } #[tokio::test] - async fn kvm() { + async fn kvm_certs() { let ext = Extension { extn_id: Kvm::OID, critical: false, @@ -432,19 +440,59 @@ mod tests { .body(Body::from(cr(SECP_256_R_1, vec![ext]))) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(certificates_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::OK); + attest_response(certificates_state(), response).await; + } - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let path = PkiPath::from_der(&body).unwrap(); - let issr = Certificate::from_der(CRT).unwrap(); - assert_eq!(2, path.0.len()); - assert_eq!(issr, path.0[0]); - issr.tbs_certificate.verify_crt(&path.0[1]).unwrap(); + #[tokio::test] + async fn kvm_hostname() { + let ext = Extension { + extn_id: Kvm::OID, + critical: false, + extn_value: &[], + }; + + let request = Request::builder() + .method("POST") + .uri("/") + .header(CONTENT_TYPE, PKCS10) + .body(Body::from(cr(SECP_256_R_1, vec![ext]))) + .unwrap(); + + let state = hostname_state(); + let response = app(state.clone()).oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + attest_response(state, response).await; + } + + #[tokio::test] + async fn sgx_certs() { + for quote in [ + include_bytes!("ext/sgx/quote.unknown").as_slice(), + include_bytes!("ext/sgx/quote.icelake").as_slice(), + ] { + let ext = Extension { + extn_id: Sgx::OID, + critical: false, + extn_value: quote, + }; + + let request = Request::builder() + .method("POST") + .uri("/") + .header(CONTENT_TYPE, PKCS10) + .body(Body::from(cr(SECP_256_R_1, vec![ext]))) + .unwrap(); + + let response = app(certificates_state()).oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + attest_response(certificates_state(), response).await; + } } #[tokio::test] - async fn sgx() { + async fn sgx_hostname() { for quote in [ include_bytes!("ext/sgx/quote.unknown").as_slice(), include_bytes!("ext/sgx/quote.icelake").as_slice(), @@ -462,20 +510,42 @@ mod tests { .body(Body::from(cr(SECP_256_R_1, vec![ext]))) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let state = hostname_state(); + let response = app(state.clone()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::OK); + attest_response(state, response).await; + } + } - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let path = PkiPath::from_der(&body).unwrap(); - let issr = Certificate::from_der(CRT).unwrap(); - assert_eq!(2, path.0.len()); - assert_eq!(issr, path.0[0]); - issr.tbs_certificate.verify_crt(&path.0[1]).unwrap(); + #[tokio::test] + async fn snp_certs() { + let evidence = ext::snp::Evidence { + vcek: Certificate::from_der(include_bytes!("ext/snp/milan.vcek")).unwrap(), + report: include_bytes!("ext/snp/milan.rprt"), } + .to_vec() + .unwrap(); + + let ext = Extension { + extn_id: Snp::OID, + critical: false, + extn_value: &evidence, + }; + + let request = Request::builder() + .method("POST") + .uri("/") + .header(CONTENT_TYPE, PKCS10) + .body(Body::from(cr(SECP_384_R_1, vec![ext]))) + .unwrap(); + + let response = app(certificates_state()).oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + attest_response(certificates_state(), response).await; } #[tokio::test] - async fn snp() { + async fn snp_hostname() { let evidence = ext::snp::Evidence { vcek: Certificate::from_der(include_bytes!("ext/snp/milan.vcek")).unwrap(), report: include_bytes!("ext/snp/milan.rprt"), @@ -496,19 +566,27 @@ mod tests { .body(Body::from(cr(SECP_384_R_1, vec![ext]))) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let state = hostname_state(); + let response = app(state.clone()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::OK); + attest_response(state, response).await; + } - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let path = PkiPath::from_der(&body).unwrap(); - let issr = Certificate::from_der(CRT).unwrap(); - assert_eq!(2, path.0.len()); - assert_eq!(issr, path.0[0]); - issr.tbs_certificate.verify_crt(&path.0[1]).unwrap(); + #[tokio::test] + async fn err_no_attestation_certs() { + let request = Request::builder() + .method("POST") + .uri("/") + .header(CONTENT_TYPE, PKCS10) + .body(Body::from(cr(SECP_256_R_1, vec![]))) + .unwrap(); + + let response = app(certificates_state()).oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); } #[tokio::test] - async fn err_no_attestation() { + async fn err_no_attestation_hostname() { let request = Request::builder() .method("POST") .uri("/") @@ -516,7 +594,7 @@ mod tests { .body(Body::from(cr(SECP_256_R_1, vec![]))) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(hostname_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::UNAUTHORIZED); } @@ -528,7 +606,7 @@ mod tests { .body(Body::from(cr(SECP_256_R_1, vec![]))) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(certificates_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } @@ -541,7 +619,7 @@ mod tests { .body(Body::from(cr(SECP_256_R_1, vec![]))) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(certificates_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } @@ -554,7 +632,7 @@ mod tests { .body(Body::empty()) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(certificates_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } @@ -567,7 +645,7 @@ mod tests { .body(Body::from(vec![0x01, 0x02, 0x03, 0x04])) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(certificates_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } @@ -584,7 +662,7 @@ mod tests { .body(Body::from(cr)) .unwrap(); - let response = app(state()).oneshot(request).await.unwrap(); + let response = app(certificates_state()).oneshot(request).await.unwrap(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); } } diff --git a/testdata/ca.srl b/testdata/ca.srl deleted file mode 100644 index 3bb87be3..00000000 --- a/testdata/ca.srl +++ /dev/null @@ -1 +0,0 @@ -3B87E1D88A9361A95FAF38FA158992F077ED2899 diff --git a/testdata/generate.sh b/testdata/generate.sh index 148073d1..f5676348 100755 --- a/testdata/generate.sh +++ b/testdata/generate.sh @@ -8,18 +8,3 @@ printf "\nGenerating CA certificate\n" openssl req -new -x509 -config ca.conf -key ca.key -out ca.crt printf "\nCA " openssl x509 -noout -text -in ca.crt - -printf "\nGenerating Server key\n" -openssl ecparam -genkey -name prime256v1 | openssl pkcs8 -topk8 -nocrypt -out server.key -printf "\nServer " -openssl pkey -noout -text -in server.key - -printf "\nGenerating Server Certificate Signing Request\n" -openssl req -new -config server.conf -key server.key -out server.csr -printf "\nServer " -openssl req -text -in server.csr - -printf "\nGenerating Server Certificate\n" -openssl x509 -req -days 9999 -CAcreateserial -CA ca.crt -CAkey ca.key -in server.csr -out server.crt -extfile server.conf -extensions server_crt -printf "\nServer " -openssl x509 -noout -text -in server.crt diff --git a/testdata/server.conf b/testdata/server.conf deleted file mode 100644 index 564d38f5..00000000 --- a/testdata/server.conf +++ /dev/null @@ -1,28 +0,0 @@ -[req] -distinguished_name = req_distinguished_name -prompt = no -req_extensions = v3_req -x509_extensions = server_crt - -[req_distinguished_name] -C = US -ST = North Carolina -L = Raleigh -O = Profian -CN = steward.profian.com - -[server_crt] -nsCertType = server -nsComment = "Server Certificate" -subjectAltName = @alt_names - -[alt_names] -DNS.1 = localhost -DNS.2 = *.localhost -IP.1 = 127.0.0.1 -IP.2 = ::1 - -[v3_req] -basicConstraints = CA:FALSE -extendedKeyUsage = serverAuth -keyUsage = digitalSignature, nonRepudiation, keyEncipherment diff --git a/testdata/server.crt b/testdata/server.crt deleted file mode 100644 index 5d8b1b7a..00000000 --- a/testdata/server.crt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICNDCCAdmgAwIBAgIUO4fh2IqTYalfrzj6FYmS8HftKJkwCgYIKoZIzj0EAwIw -VjELMAkGA1UEBhMCVVMxFzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMRAwDgYDVQQH -DAdSYWxlaWdoMRwwGgYDVQQDDBNzdGV3YXJkLnByb2ZpYW4uY29tMB4XDTIyMDYy -NDE4MDcxNVoXDTQ5MTEwODE4MDcxNVowaDELMAkGA1UEBhMCVVMxFzAVBgNVBAgM -Dk5vcnRoIENhcm9saW5hMRAwDgYDVQQHDAdSYWxlaWdoMRAwDgYDVQQKDAdQcm9m -aWFuMRwwGgYDVQQDDBNzdGV3YXJkLnByb2ZpYW4uY29tMFkwEwYHKoZIzj0CAQYI -KoZIzj0DAQcDQgAEKT33uYo8nFgz+mtSMQFsczpMp9BEmN8N1MgsoQnn3zCItMHi -vhgr5QmzKQpWEgJiXedOcyBdi7CdHpRr5vxfQqNzMHEwEQYJYIZIAYb4QgEBBAQD -AgZAMCEGCWCGSAGG+EIBDQQUFhJTZXJ2ZXIgQ2VydGlmaWNhdGUwOQYDVR0RBDIw -MIIJbG9jYWxob3N0ggsqLmxvY2FsaG9zdIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAA -ATAKBggqhkjOPQQDAgNJADBGAiEAg35qGKEsJ/bdm8i42Jprbgp9pmeMNNBV6Z6s -8GzXwKACIQCqq1otHDfoRkvAHEt3Gz2iv6U6Cf9qkWx76M4xemopmQ== ------END CERTIFICATE----- diff --git a/testdata/server.csr b/testdata/server.csr deleted file mode 100644 index 8c448206..00000000 --- a/testdata/server.csr +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBYzCCAQgCAQAwaDELMAkGA1UEBhMCVVMxFzAVBgNVBAgMDk5vcnRoIENhcm9s -aW5hMRAwDgYDVQQHDAdSYWxlaWdoMRAwDgYDVQQKDAdQcm9maWFuMRwwGgYDVQQD -DBNzdGV3YXJkLnByb2ZpYW4uY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -KT33uYo8nFgz+mtSMQFsczpMp9BEmN8N1MgsoQnn3zCItMHivhgr5QmzKQpWEgJi -XedOcyBdi7CdHpRr5vxfQqA+MDwGCSqGSIb3DQEJDjEvMC0wCQYDVR0TBAIwADAT -BgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBeAwCgYIKoZIzj0EAwIDSQAw -RgIhAL8hfEz77v4hNdYVA+iwiTc+XxBOFYPi7t42Ep1e+OuPAiEAvWkj8X7R3eYs -6ZMWvtYuPFI8utKM5o0NT624dqRT7ug= ------END CERTIFICATE REQUEST----- diff --git a/testdata/server.key b/testdata/server.key deleted file mode 100644 index 72dfa94a..00000000 --- a/testdata/server.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgV0MagOXQCObvtUq+ -8btGBLzSU6MGziLjj4EQpbEPpHChRANCAAQpPfe5ijycWDP6a1IxAWxzOkyn0ESY -3w3UyCyhCeffMIi0weK+GCvlCbMpClYSAmJd505zIF2LsJ0elGvm/F9C ------END PRIVATE KEY-----