Skip to content

Commit 64d7434

Browse files
committed
chore: greater protection for release mode
Signed-off-by: Richard Zak <[email protected]>
1 parent 2311adc commit 64d7434

File tree

6 files changed

+144
-17
lines changed

6 files changed

+144
-17
lines changed

src/main.rs

+91-17
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ use axum::routing::{get, post};
2626
use axum::Router;
2727
use clap::Parser;
2828
use confargs::{prefix_char_filter, Toml};
29+
#[cfg(debug_assertions)]
30+
use const_oid::db::rfc5280::{ID_CE_BASIC_CONSTRAINTS, ID_CE_KEY_USAGE};
2931
use const_oid::db::rfc5280::{
30-
ID_CE_BASIC_CONSTRAINTS, ID_CE_EXT_KEY_USAGE, ID_CE_KEY_USAGE, ID_CE_SUBJECT_ALT_NAME,
31-
ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH,
32+
ID_CE_EXT_KEY_USAGE, ID_CE_SUBJECT_ALT_NAME, ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH,
3233
};
3334
use const_oid::db::rfc5912::ID_EXTENSION_REQ;
34-
use der::asn1::{GeneralizedTime, Ia5StringRef, UIntRef};
35+
#[cfg(debug_assertions)]
36+
use der::asn1::GeneralizedTime;
37+
use der::asn1::{Ia5StringRef, UIntRef};
3538
use der::{Decode, Encode, Sequence};
3639
use ext::kvm::Kvm;
3740
use ext::sgx::Sgx;
@@ -48,7 +51,9 @@ use tower_http::LatencyUnit;
4851
use tracing::{debug, Level};
4952
use x509::attr::Attribute;
5053
use x509::ext::pkix::name::GeneralName;
51-
use x509::ext::pkix::{BasicConstraints, ExtendedKeyUsage, KeyUsage, KeyUsages, SubjectAltName};
54+
#[cfg(debug_assertions)]
55+
use x509::ext::pkix::{BasicConstraints, KeyUsage, KeyUsages};
56+
use x509::ext::pkix::{ExtendedKeyUsage, SubjectAltName};
5257
use x509::name::RdnSequence;
5358
use x509::request::{CertReq, ExtensionReq};
5459
use x509::time::{Time, Validity};
@@ -80,6 +85,7 @@ struct Args {
8085
#[clap(short, long, env = "ROCKET_ADDRESS", default_value = "::")]
8186
addr: IpAddr,
8287

88+
#[cfg(debug_assertions)]
8389
#[clap(short, long, env = "RENDER_EXTERNAL_HOSTNAME")]
8490
host: Option<String>,
8591

@@ -141,11 +147,22 @@ impl State {
141147

142148
// Validate the syntax of the files.
143149
PrivateKeyInfo::from_der(key.as_ref())?;
150+
#[cfg(debug_assertions)]
144151
Certificate::from_der(crt.as_ref())?;
152+
#[cfg(not(debug_assertions))]
153+
{
154+
let cert = Certificate::from_der(crt.as_ref())?;
155+
let iss = &cert.tbs_certificate;
156+
if iss.issuer_unique_id == iss.subject_unique_id && iss.issuer == iss.subject {
157+
// A self-signed certificate is not appropriate for Release mode.
158+
return Err(anyhow!("invalid certificate"));
159+
}
160+
}
145161

146162
Ok(State { crt, san, key })
147163
}
148164

165+
#[cfg(debug_assertions)]
149166
pub fn generate(san: Option<String>, hostname: &str) -> anyhow::Result<Self> {
150167
use const_oid::db::rfc5912::SECP_256_R_1 as P256;
151168

@@ -219,6 +236,8 @@ async fn main() -> anyhow::Result<()> {
219236
let args = confargs::args::<Toml>(prefix_char_filter::<'@'>)
220237
.context("Failed to parse config")
221238
.map(Args::parse_from)?;
239+
240+
#[cfg(debug_assertions)]
222241
let state = match (args.key, args.crt, args.host) {
223242
(None, None, Some(host)) => State::generate(args.san, &host)?,
224243
(Some(key), Some(crt), _) => State::load(args.san, key, crt)?,
@@ -228,6 +247,15 @@ async fn main() -> anyhow::Result<()> {
228247
}
229248
};
230249

250+
#[cfg(not(debug_assertions))]
251+
let state = match (args.key, args.crt) {
252+
(Some(key), Some(crt)) => State::load(args.san, key, crt)?,
253+
_ => {
254+
eprintln!("Specify the public key `--crt` and private key `--key`.\nRun with `--help` for more information.");
255+
return Err(anyhow!("invalid configuration"));
256+
}
257+
};
258+
231259
#[cfg(not(target_os = "wasi"))]
232260
{
233261
use std::net::SocketAddr;
@@ -316,6 +344,14 @@ fn attest_request(
316344
StatusCode::BAD_REQUEST
317345
})?;
318346

347+
let dbg = if cfg!(debug_assertions) {
348+
// If the issuer is self-signed, we are in debug mode.
349+
let iss = &issuer.tbs_certificate;
350+
iss.issuer_unique_id == iss.subject_unique_id && iss.issuer == iss.subject
351+
} else {
352+
false
353+
};
354+
319355
let mut extensions = Vec::new();
320356
let mut attested = false;
321357
for Attribute { oid, values } in info.attributes.iter() {
@@ -329,11 +365,6 @@ fn attest_request(
329365
StatusCode::BAD_REQUEST
330366
})?;
331367
for ext in Vec::from(ereq) {
332-
// If the issuer is self-signed, we are in debug mode.
333-
let iss = &issuer.tbs_certificate;
334-
let dbg = iss.issuer_unique_id == iss.subject_unique_id;
335-
let dbg = dbg && iss.issuer == iss.subject;
336-
337368
// Validate the extension.
338369
let (copy, att) = match ext.extn_id {
339370
Kvm::OID => (Kvm::default().verify(&info, &ext, dbg), Kvm::ATT),
@@ -471,16 +502,22 @@ async fn attest(
471502
#[cfg(test)]
472503
mod tests {
473504
mod attest {
474-
use crate::ext::{kvm::Kvm, sgx::Sgx, snp::Snp, ExtVerifier};
505+
use crate::ext::{kvm::Kvm, ExtVerifier};
506+
#[cfg(debug_assertions)]
507+
use crate::ext::{sgx::Sgx, snp::Snp};
475508
use crate::*;
476-
use const_oid::db::rfc5912::{ID_EXTENSION_REQ, SECP_256_R_1, SECP_384_R_1};
509+
#[cfg(debug_assertions)]
510+
use const_oid::db::rfc5912::SECP_384_R_1;
511+
use const_oid::db::rfc5912::{ID_EXTENSION_REQ, SECP_256_R_1};
477512
use const_oid::ObjectIdentifier;
478513
use der::{AnyRef, Encode};
479514
use x509::attr::Attribute;
480515
use x509::request::{CertReq, CertReqInfo, ExtensionReq};
516+
#[cfg(debug_assertions)]
481517
use x509::PkiPath;
482518
use x509::{ext::Extension, name::RdnSequence};
483519

520+
#[cfg(debug_assertions)]
484521
use axum::response::Response;
485522
use http::header::CONTENT_TYPE;
486523
use http::Request;
@@ -490,17 +527,34 @@ mod tests {
490527

491528
fn certificates_state() -> State {
492529
#[cfg(not(target_os = "wasi"))]
493-
return State::load(None, "testdata/ca.key", "testdata/ca.crt")
494-
.expect("failed to load state");
530+
{
531+
#[cfg(debug_assertions)]
532+
return State::load(None, "testdata/ca.key", "testdata/ca.crt")
533+
.expect("failed to load state");
534+
#[cfg(not(debug_assertions))]
535+
return State::load(None, "testdata/test.key", "testdata/test.crt")
536+
.expect("failed to load state");
537+
}
538+
495539
#[cfg(target_os = "wasi")]
496540
{
497-
let crt = std::io::BufReader::new(include_bytes!("../testdata/ca.crt").as_slice());
498-
let key = std::io::BufReader::new(include_bytes!("../testdata/ca.key").as_slice());
541+
let (crt, key) = if cfg!(debug_assertions) {
542+
(
543+
std::io::BufReader::new(include_bytes!("../testdata/ca.crt").as_slice()),
544+
std::io::BufReader::new(include_bytes!("../testdata/ca.key").as_slice()),
545+
)
546+
} else {
547+
(
548+
std::io::BufReader::new(include_bytes!("../testdata/test.crt").as_slice()),
549+
std::io::BufReader::new(include_bytes!("../testdata/test.key").as_slice()),
550+
)
551+
};
499552

500553
State::read(None, key, crt).expect("failed to load state")
501554
}
502555
}
503556

557+
#[cfg(debug_assertions)]
504558
fn hostname_state() -> State {
505559
State::generate(None, "localhost").unwrap()
506560
}
@@ -534,6 +588,7 @@ mod tests {
534588
}
535589
}
536590

591+
#[cfg(debug_assertions)]
537592
async fn attest_response(state: State, response: Response, multi: bool) {
538593
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
539594

@@ -591,10 +646,18 @@ mod tests {
591646
.unwrap();
592647

593648
let response = app(certificates_state()).oneshot(request).await.unwrap();
594-
assert_eq!(response.status(), StatusCode::OK);
595-
attest_response(certificates_state(), response, multi).await;
649+
#[cfg(debug_assertions)]
650+
{
651+
assert_eq!(response.status(), StatusCode::OK);
652+
attest_response(certificates_state(), response, multi).await;
653+
}
654+
#[cfg(not(debug_assertions))]
655+
{
656+
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
657+
}
596658
}
597659

660+
#[cfg(debug_assertions)]
598661
#[rstest]
599662
#[case(PKCS10, false)]
600663
#[case(BUNDLE, true)]
@@ -621,6 +684,7 @@ mod tests {
621684

622685
// Though similar to the above test, this is the only test which
623686
// actually sends many CSRs, versus an array of just one CSR.
687+
#[cfg(debug_assertions)]
624688
#[tokio::test]
625689
async fn kvm_hostname_many_certs() {
626690
let ext = Extension {
@@ -658,6 +722,7 @@ mod tests {
658722
assert_eq!(output.issued.len(), five_crs.len());
659723
}
660724

725+
#[cfg(debug_assertions)]
661726
#[rstest]
662727
#[case(PKCS10, false)]
663728
#[case(BUNDLE, true)]
@@ -686,6 +751,7 @@ mod tests {
686751
}
687752
}
688753

754+
#[cfg(debug_assertions)]
689755
#[rstest]
690756
#[case(PKCS10, false)]
691757
#[case(BUNDLE, true)]
@@ -715,6 +781,7 @@ mod tests {
715781
}
716782
}
717783

784+
#[cfg(debug_assertions)]
718785
#[rstest]
719786
#[case(PKCS10, false)]
720787
#[case(BUNDLE, true)]
@@ -745,6 +812,7 @@ mod tests {
745812
attest_response(certificates_state(), response, multi).await;
746813
}
747814

815+
#[cfg(debug_assertions)]
748816
#[rstest]
749817
#[case(PKCS10, false)]
750818
#[case(BUNDLE, true)]
@@ -776,6 +844,7 @@ mod tests {
776844
attest_response(state, response, multi).await;
777845
}
778846

847+
#[cfg(debug_assertions)]
779848
#[rstest]
780849
#[case(PKCS10, false)]
781850
#[case(BUNDLE, true)]
@@ -792,6 +861,7 @@ mod tests {
792861
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
793862
}
794863

864+
#[cfg(debug_assertions)]
795865
#[tokio::test]
796866
async fn err_no_attestation_hostname() {
797867
let request = Request::builder()
@@ -805,6 +875,7 @@ mod tests {
805875
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
806876
}
807877

878+
#[cfg(debug_assertions)]
808879
#[rstest]
809880
#[case(false)]
810881
#[case(true)]
@@ -820,6 +891,7 @@ mod tests {
820891
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
821892
}
822893

894+
#[cfg(debug_assertions)]
823895
#[tokio::test]
824896
async fn err_empty_body() {
825897
let request = Request::builder()
@@ -832,6 +904,7 @@ mod tests {
832904
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
833905
}
834906

907+
#[cfg(debug_assertions)]
835908
#[tokio::test]
836909
async fn err_bad_body() {
837910
let request = Request::builder()
@@ -844,6 +917,7 @@ mod tests {
844917
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
845918
}
846919

920+
#[cfg(debug_assertions)]
847921
#[tokio::test]
848922
async fn err_bad_csr_sig() {
849923
let mut cr = cr(SECP_256_R_1, vec![], true);

testdata/generate.sh

+13
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,16 @@ printf "\nGenerating CA certificate\n"
88
openssl req -new -x509 -days 9999 -config ca.conf -key ca.key -out ca.crt
99
printf "\nCA "
1010
openssl x509 -noout -text -in ca.crt
11+
12+
printf "\nGenerating test key\n"
13+
openssl ecparam -genkey -name prime256v1 | openssl pkcs8 -topk8 -nocrypt -out test.key
14+
printf "\nKey "
15+
openssl pkey -noout -text -in test.key
16+
17+
printf "\nGenerating test cert request\n"
18+
openssl req -new -config test.conf -key test.key -out test.csr
19+
20+
printf "\nSigning test cert request\n"
21+
openssl x509 -req -in test.csr -days 9999 -CA ca.crt -extfile test.conf -CAkey ca.key -set_serial 99 -out test.crt
22+
printf "\nCert "
23+
openssl x509 -noout -text -in test.crt

testdata/test.conf

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[req]
2+
distinguished_name = req_distinguished_name
3+
prompt = no
4+
x509_extensions = v3_ca
5+
6+
[req_distinguished_name]
7+
C = US
8+
ST = North Carolina
9+
L = Raleigh
10+
CN = steward2.profian.com
11+
12+
[v3_ca]
13+
basicConstraints = critical,CA:TRUE
14+
keyUsage = cRLSign, keyCertSign
15+
nsComment = "CA certificate"
16+
subjectKeyIdentifier = hash

testdata/test.crt

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBmzCCAUKgAwIBAgIBYzAKBggqhkjOPQQDAjBWMQswCQYDVQQGEwJVUzEXMBUG
3+
A1UECAwOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcMB1JhbGVpZ2gxHDAaBgNVBAMM
4+
E3N0ZXdhcmQucHJvZmlhbi5jb20wIBcNMjIxMDEwMjEzNDI1WhgPMjA1MDAyMjQy
5+
MTM0MjVaMFcxCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJvbGluYTEQ
6+
MA4GA1UEBwwHUmFsZWlnaDEdMBsGA1UEAwwUc3Rld2FyZDIucHJvZmlhbi5jb20w
7+
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQbabCqJJfsfzPQzHDcMYbtrcj64aHY
8+
Jme3vNPiSsJ2YRYE+DrjxEBOBI4WD03vAXmnpp0jOZNZQe5zz27ZSLc8MAoGCCqG
9+
SM49BAMCA0cAMEQCIHuP3z6/fF6r8jJ03zzvwL1SM7n2QLXLhzynD9o5X1AaAiA3
10+
Zfd0rJN7Y7aYFqro9GsUSSoFwmGBLs/H3uiEvXSOyw==
11+
-----END CERTIFICATE-----

testdata/test.csr

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN CERTIFICATE REQUEST-----
2+
MIIBEjCBuQIBADBXMQswCQYDVQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xp
3+
bmExEDAOBgNVBAcMB1JhbGVpZ2gxHTAbBgNVBAMMFHN0ZXdhcmQyLnByb2ZpYW4u
4+
Y29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEG2mwqiSX7H8z0Mxw3DGG7a3I
5+
+uGh2CZnt7zT4krCdmEWBPg648RATgSOFg9N7wF5p6adIzmTWUHuc89u2Ui3PKAA
6+
MAoGCCqGSM49BAMCA0gAMEUCIQCpgD05/WTApNkLsXoaGPjj/gOEFG6d1KYUcPDH
7+
mc8XLwIgLwmftO4XLlI4evWxvxrGmppi5Bq3IEVugRyRwi9Sg2M=
8+
-----END CERTIFICATE REQUEST-----

testdata/test.key

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgnsotpIzPM5AsQbR3
3+
mjGjYrhOlk76yokFzzklCy8ULeuhRANCAAQbabCqJJfsfzPQzHDcMYbtrcj64aHY
4+
Jme3vNPiSsJ2YRYE+DrjxEBOBI4WD03vAXmnpp0jOZNZQe5zz27ZSLc8
5+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)