Skip to content

Commit

Permalink
feat: handle many cert requests
Browse files Browse the repository at this point in the history
Co-authored-by: Roman Volosatovs <roman@profian.com>
Co-authored-by: Nathaniel McCallum <nathaniel@profian.com>
Signed-off-by: Richard Zak <richard@profian.com>
  • Loading branch information
3 people committed Sep 16, 2022
1 parent df3e412 commit ba8c702
Showing 1 changed file with 164 additions and 99 deletions.
263 changes: 164 additions & 99 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -93,6 +93,15 @@ struct State {
san: Option<String>,
}

#[derive(Clone, Debug, Default, Sequence)]
struct Output<'a> {
/// The signing certificate chain back to the root.
pub chain: Vec<Certificate<'a>>,

/// All issued certificates.
pub issued: Vec<Certificate<'a>>,
}

impl State {
pub fn load(
san: Option<String>,
Expand Down Expand Up @@ -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<Vec<u8>> = 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<u8> = 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<u8> = 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::<Result<Vec<_>, _>>()
.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)]
Expand Down Expand Up @@ -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<Vec<u8>> = 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]);
Expand All @@ -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<Vec<u8>> = Decode::from_der(&encoded).unwrap();
assert_eq!(cr_vec.len(), 1);
let decoded = CertReq::from_der(&cr_vec[0]).unwrap();
let reencoded: Vec<u8> = 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<u8> = decoded.to_vec().unwrap();
// assert_eq!(encoded, reencoded);
}

#[tokio::test]
Expand Down

0 comments on commit ba8c702

Please sign in to comment.