From ba8c702b90816d96f1085c0099e32f3753ab6833 Mon Sep 17 00:00:00 2001 From: Richard Zak Date: Thu, 15 Sep 2022 12:38:50 -0400 Subject: [PATCH] feat: handle many cert requests Co-authored-by: Roman Volosatovs Co-authored-by: Nathaniel McCallum Signed-off-by: Richard Zak --- src/main.rs | 263 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 164 insertions(+), 99 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0e3a961e..633d1da3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ use const_oid::db::rfc5280::{ }; use const_oid::db::rfc5912::ID_EXTENSION_REQ; use der::asn1::{GeneralizedTime, Ia5StringRef, UIntRef}; -use der::{Decode, Encode}; +use der::{Decode, Encode, Sequence}; use pkcs8::PrivateKeyInfo; use x509::ext::pkix::{BasicConstraints, ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName}; use x509::name::RdnSequence; @@ -93,6 +93,15 @@ struct State { san: Option, } +#[derive(Clone, Debug, Default, Sequence)] +struct Output<'a> { + /// The signing certificate chain back to the root. + pub chain: Vec>, + + /// All issued certificates. + pub issued: Vec>, +} + impl State { pub fn load( san: Option, @@ -263,105 +272,127 @@ async fn attest( return Err(StatusCode::BAD_REQUEST); } - // Decode and verify the certification request. - let cr = CertReq::from_der(body.as_ref()).or(Err(StatusCode::BAD_REQUEST))?; - let cri = cr.verify().or(Err(StatusCode::BAD_REQUEST))?; - - // Validate requested extensions. - let mut attested = false; - let mut extensions = Vec::new(); - for attr in cri.attributes.iter() { - if attr.oid != ID_EXTENSION_REQ { - return Err(StatusCode::BAD_REQUEST); - } + eprintln!("attest() der bytes: {}", body.as_ref().len()); - for any in attr.values.iter() { - let ereq: ExtensionReq<'_> = any.decode_into().or(Err(StatusCode::BAD_REQUEST))?; - for ext in Vec::from(ereq) { - // If the issuer is self-signed, we are in debug mode. - let iss = &issuer.tbs_certificate; - let dbg = iss.issuer_unique_id == iss.subject_unique_id; - let dbg = dbg && iss.issuer == iss.subject; - - // Validate the extension. - let (copy, att) = match ext.extn_id { - Kvm::OID => (Kvm::default().verify(&cri, &ext, dbg), Kvm::ATT), - Sgx::OID => (Sgx::default().verify(&cri, &ext, dbg), Sgx::ATT), - Snp::OID => (Snp::default().verify(&cri, &ext, dbg), Snp::ATT), - _ => return Err(StatusCode::BAD_REQUEST), // unsupported extension - }; + let crs: Vec> = Decode::from_der(body.as_ref()).map_err(|e| { + eprintln!("attest() error unpacking ASN1 array {:?}", e); + StatusCode::BAD_REQUEST + })?; - // Save results. - attested |= att; - if copy.or(Err(StatusCode::BAD_REQUEST))? { - extensions.push(ext); + // Decode and verify the certification request. + crs.into_iter() + .map(|cr| { + let issuer = issuer.clone(); + let cr = CertReq::from_der(&cr).or(Err(StatusCode::BAD_REQUEST))?; + let cri = cr.verify().or(Err(StatusCode::BAD_REQUEST))?; + + // Validate requested extensions. + let mut attested = false; + let mut extensions = Vec::new(); + for attr in cri.attributes.iter() { + if attr.oid != ID_EXTENSION_REQ { + return Err(StatusCode::BAD_REQUEST); } - } - } - } - if !attested { - return Err(StatusCode::UNAUTHORIZED); - } - // Get the current time and the expiration of the cert. - let now = SystemTime::now(); - let end = now + Duration::from_secs(60 * 60 * 24 * 28); - let validity = Validity { - not_before: Time::try_from(now).or(Err(ISE))?, - not_after: Time::try_from(end).or(Err(ISE))?, - }; + for any in attr.values.iter() { + let ereq: ExtensionReq<'_> = + any.decode_into().or(Err(StatusCode::BAD_REQUEST))?; + for ext in Vec::from(ereq) { + // If the issuer is self-signed, we are in debug mode. + let iss = &issuer.tbs_certificate; + let dbg = iss.issuer_unique_id == iss.subject_unique_id; + let dbg = dbg && iss.issuer == iss.subject; + + // Validate the extension. + let (copy, att) = match ext.extn_id { + Kvm::OID => (Kvm::default().verify(&cri, &ext, dbg), Kvm::ATT), + Sgx::OID => (Sgx::default().verify(&cri, &ext, dbg), Sgx::ATT), + Snp::OID => (Snp::default().verify(&cri, &ext, dbg), Snp::ATT), + _ => return Err(StatusCode::BAD_REQUEST), // unsupported extension + }; + + // Save results. + attested |= att; + if copy.or(Err(StatusCode::BAD_REQUEST))? { + extensions.push(ext); + } + } + } + } + if !attested { + return Err(StatusCode::UNAUTHORIZED); + } - // Create the basic subject alt name. - let mut sans = vec![GeneralName::DnsName( - Ia5StringRef::new("foo.bar.hub.profian.com").or(Err(ISE))?, - )]; + // Get the current time and the expiration of the cert. + let now = SystemTime::now(); + let end = now + Duration::from_secs(60 * 60 * 24 * 28); + let validity = Validity { + not_before: Time::try_from(now).or(Err(ISE))?, + not_after: Time::try_from(end).or(Err(ISE))?, + }; - // Optionally, add the configured subject alt name. - if let Some(name) = state.san.as_ref() { - sans.push(GeneralName::DnsName(Ia5StringRef::new(name).or(Err(ISE))?)); - } + // Create the basic subject alt name. + let mut sans = vec![GeneralName::DnsName( + Ia5StringRef::new("foo.bar.hub.profian.com").or(Err(ISE))?, + )]; - // Encode the subject alt name. - let sans: Vec = SubjectAltName(sans).to_vec().or(Err(ISE))?; - extensions.push(x509::ext::Extension { - extn_id: ID_CE_SUBJECT_ALT_NAME, - critical: false, - extn_value: &sans, - }); - - // Add extended key usage. - let eku = ExtendedKeyUsage(vec![ID_KP_SERVER_AUTH, ID_KP_CLIENT_AUTH]) - .to_vec() - .or(Err(ISE))?; - extensions.push(x509::ext::Extension { - extn_id: ID_CE_EXT_KEY_USAGE, - critical: false, - extn_value: &eku, - }); - - // Generate the instance id. - let uuid = uuid::Uuid::new_v4(); - - // Create the new certificate. - let tbs = TbsCertificate { - version: x509::Version::V3, - serial_number: UIntRef::new(uuid.as_bytes()).or(Err(ISE))?, - signature: isskey.signs_with().or(Err(ISE))?, - issuer: issuer.tbs_certificate.subject.clone(), - validity, - subject: RdnSequence(Vec::new()), - subject_public_key_info: cri.public_key, - issuer_unique_id: issuer.tbs_certificate.subject_unique_id, - subject_unique_id: None, - extensions: Some(extensions), - }; + // Optionally, add the configured subject alt name. + if let Some(name) = state.san.as_ref() { + sans.push(GeneralName::DnsName(Ia5StringRef::new(name).or(Err(ISE))?)); + } - // Sign the certificate. - let crt = tbs.sign(&isskey).or(Err(ISE))?; - let crt = Certificate::from_der(&crt).or(Err(ISE))?; + // Encode the subject alt name. + let sans: Vec = SubjectAltName(sans).to_vec().or(Err(ISE))?; + extensions.push(x509::ext::Extension { + extn_id: ID_CE_SUBJECT_ALT_NAME, + critical: false, + extn_value: &sans, + }); + + // Add extended key usage. + let eku = ExtendedKeyUsage(vec![ID_KP_SERVER_AUTH, ID_KP_CLIENT_AUTH]) + .to_vec() + .or(Err(ISE))?; + extensions.push(x509::ext::Extension { + extn_id: ID_CE_EXT_KEY_USAGE, + critical: false, + extn_value: &eku, + }); + + // Generate the instance id. + let uuid = uuid::Uuid::new_v4(); + + // Create the new certificate. + let tbs = TbsCertificate { + version: x509::Version::V3, + serial_number: UIntRef::new(uuid.as_bytes()).or(Err(ISE))?, + signature: isskey.signs_with().or(Err(ISE))?, + issuer: issuer.clone().tbs_certificate.subject.clone(), + validity, + subject: RdnSequence(Vec::new()), + subject_public_key_info: cri.public_key, + issuer_unique_id: issuer.clone().tbs_certificate.subject_unique_id, + subject_unique_id: None, + extensions: Some(extensions), + }; - // Create and return the PkiPath. - vec![issuer, crt].to_vec().or(Err(ISE)) + // Sign the certificate. + tbs.sign(&isskey).or(Err(ISE)) + }) + .collect::, _>>() + .and_then(|crts| { + let mut issued = Vec::with_capacity(crts.len()); + for crt in crts.iter() { + let crt = Certificate::from_der(&crt).or(Err(ISE))?; + issued.push(crt) + } + Output { + chain: vec![issuer.clone()], + issued, + } + .to_vec() + .or(Err(ISE)) + }) } #[cfg(test)] @@ -411,12 +442,40 @@ mod tests { }; // Sign the request. - cri.sign(&pki).unwrap() + let ret_vec = vec![cri.sign(&pki).unwrap()]; + assert_eq!(ret_vec.len(), 1); + eprintln!("cr() bytes len {}", ret_vec.to_vec().unwrap().len()); + let ret_vec = ret_vec.to_vec().unwrap(); + + let vec_vec: Vec> = match Decode::from_der(&ret_vec) { + Ok(x) => x, + Err(e) => { + eprintln!("Failed to get a cert from vec[0]: {:?}", e); + Vec::new() + } + }; + + match CertReq::from_der(&vec_vec[0]) { + Ok(_) => { + eprintln!("Converted vec[0] into one cert object"); + } + Err(e) => { + if !vec_vec[0].is_empty() { + eprintln!("Failed to get a cert from vec[0]: {:?}", e); + } + } + }; + + ret_vec } 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 response = Output::from_der(body.as_ref()).unwrap(); + + let mut path = PkiPath::from(response.chain); + path.push(response.issued[0].clone()); let issr = Certificate::from_der(&state.crt).unwrap(); assert_eq!(2, path.len()); assert_eq!(issr, path[0]); @@ -427,14 +486,20 @@ mod tests { fn reencode() { let encoded = cr(SECP_256_R_1, vec![]); - for byte in &encoded { + /*for byte in &encoded { eprint!("{:02X}", byte); } - eprintln!(); - - let decoded = CertReq::from_der(&encoded).unwrap(); - let reencoded = decoded.to_vec().unwrap(); - assert_eq!(encoded, reencoded); + eprintln!();*/ + + let cr_vec: Vec> = Decode::from_der(&encoded).unwrap(); + assert_eq!(cr_vec.len(), 1); + let decoded = CertReq::from_der(&cr_vec[0]).unwrap(); + let reencoded: Vec = decoded.to_vec().unwrap(); + assert_eq!(cr_vec[0], reencoded); + // Should work, but I suspect the final decoded to vec isn't catching the der::Encode trait + // let decoded = vec![CertReq::from_der(&cr_vec[0]).unwrap()]; + // let reencoded: Vec = decoded.to_vec().unwrap(); + // assert_eq!(encoded, reencoded); } #[tokio::test]