Skip to content

Commit 2cf9ee9

Browse files
committed
Improve signature verification interface.
1 parent a73f64e commit 2cf9ee9

File tree

10 files changed

+809
-212
lines changed

10 files changed

+809
-212
lines changed

base-library/src/auth.rs

+70-18
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ mod tests {
299299
log::LevelFilter,
300300
ring::digest::SHA256_OUTPUT_LEN,
301301
scratchstack_aws_principal::{Principal, User},
302+
std::{error::Error, fs::File},
302303
tower::BoxError,
303304
};
304305

@@ -364,6 +365,10 @@ mod tests {
364365
"The security token included in the request is invalid".to_string(),
365366
)))
366367
}
368+
"io-error" => {
369+
let e = File::open("/00Hi1i6V4qad5nF/6KPlcyW4H9miTOD02meLgTaV09O2UToMPTE9j6sNmHZ/08EzM4qOs8bYOINWJ9RheQVadpgixRTh0VjcwpVPoo1Rh4gNAJhS4cj/this-path/does//not/exist").unwrap_err();
370+
return Err(Box::new(SignatureError::from(e)));
371+
}
367372
"expired" => {
368373
return Err(Box::new(SignatureError::ExpiredToken(
369374
"The security token included in the request is expired".to_string(),
@@ -427,11 +432,13 @@ mod tests {
427432
.await
428433
.unwrap_err();
429434

430-
if let SignatureError::SignatureDoesNotMatch(msg) = e {
435+
if let SignatureError::SignatureDoesNotMatch(ref msg) = e {
431436
assert_eq!(
432-
msg.unwrap().as_str(),
437+
msg.as_ref().unwrap(),
433438
"Signature expired: 20150830T122059Z is now earlier than 20150830T122100Z (20150830T123600Z - 15 min.)"
434439
);
440+
assert_eq!(e.error_code(), "SignatureDoesNotMatch");
441+
assert_eq!(e.http_status(), 403);
435442
} else {
436443
panic!("Unexpected error: {:?}", e);
437444
}
@@ -450,11 +457,13 @@ mod tests {
450457
.await
451458
.unwrap_err();
452459

453-
if let SignatureError::SignatureDoesNotMatch(msg) = e {
460+
if let SignatureError::SignatureDoesNotMatch(ref msg) = e {
454461
assert_eq!(
455-
msg.unwrap().as_str(),
462+
msg.as_ref().unwrap(),
456463
"Signature not yet current: 20150830T125101Z is still later than 20150830T125100Z (20150830T123600Z + 15 min.)"
457464
);
465+
assert_eq!(e.error_code(), "SignatureDoesNotMatch");
466+
assert_eq!(e.http_status(), 403);
458467
} else {
459468
panic!("Unexpected error: {:?}", e);
460469
}
@@ -473,11 +482,13 @@ mod tests {
473482
.await
474483
.unwrap_err();
475484

476-
if let SignatureError::IncompleteSignature(msg) = e {
485+
if let SignatureError::IncompleteSignature(_) = e {
477486
assert_eq!(
478-
msg.as_str(),
487+
e.to_string(),
479488
"Credential must have exactly 5 slash-delimited elements, e.g. keyid/date/region/service/term, got 'AKIDFOO/20130101/wrong-region/wrong-service'"
480489
);
490+
assert_eq!(e.error_code(), "IncompleteSignature");
491+
assert_eq!(e.http_status(), 400);
481492
} else {
482493
panic!("Unexpected error: {:?}", e);
483494
}
@@ -496,11 +507,13 @@ mod tests {
496507
.await
497508
.unwrap_err();
498509

499-
if let SignatureError::SignatureDoesNotMatch(msg) = e {
510+
if let SignatureError::SignatureDoesNotMatch(_) = e {
500511
assert_eq!(
501-
msg.unwrap().as_str(),
512+
e.to_string(),
502513
"Credential should be scoped to a valid region, not 'wrong-region'. Credential should be scoped to correct service: 'example'. Credential should be scoped with a valid terminator: 'aws4_request', not 'aws5_request'. Date in Credential scope does not match YYYYMMDD from ISO-8601 version of date from HTTP: '20130101' != '20150830', from '20150830T123600Z'."
503514
);
515+
assert_eq!(e.error_code(), "SignatureDoesNotMatch");
516+
assert_eq!(e.http_status(), 403);
504517
} else {
505518
panic!("Unexpected error: {:?}", e);
506519
}
@@ -519,8 +532,10 @@ mod tests {
519532
.await
520533
.unwrap_err();
521534

522-
if let SignatureError::InvalidClientTokenId(msg) = e {
523-
assert_eq!(msg.as_str(), "The security token included in the request is invalid");
535+
if let SignatureError::InvalidClientTokenId(_) = e {
536+
assert_eq!(e.to_string(), "The security token included in the request is invalid");
537+
assert_eq!(e.error_code(), "InvalidClientTokenId");
538+
assert_eq!(e.http_status(), 403);
524539
} else {
525540
panic!("Unexpected error: {:?}", e);
526541
}
@@ -539,8 +554,10 @@ mod tests {
539554
.await
540555
.unwrap_err();
541556

542-
if let SignatureError::ExpiredToken(msg) = e {
543-
assert_eq!(msg.as_str(), "The security token included in the request is expired");
557+
if let SignatureError::ExpiredToken(_) = e {
558+
assert_eq!(e.to_string(), "The security token included in the request is expired");
559+
assert_eq!(e.error_code(), "ExpiredToken");
560+
assert_eq!(e.http_status(), 403);
544561
} else {
545562
panic!("Unexpected error: {:?}", e);
546563
}
@@ -559,8 +576,40 @@ mod tests {
559576
.await
560577
.unwrap_err();
561578

562-
if let SignatureError::InternalServiceError(err) = e {
579+
if let SignatureError::InternalServiceError(ref err) = e {
563580
assert_eq!(format!("{:?}", err), r#""internal service error""#);
581+
assert_eq!(e.to_string(), "internal service error");
582+
assert_eq!(e.error_code(), "InternalFailure");
583+
assert_eq!(e.http_status(), 500);
584+
} else {
585+
panic!("Unexpected error: {:?}", e);
586+
}
587+
588+
let auth = SigV4Authenticator::builder()
589+
.canonical_request_sha256(creq_sha256)
590+
.credential("AKIDFOO/20150830/us-east-1/example/aws4_request".to_string())
591+
.session_token("io-error")
592+
.signature("invalid".to_string())
593+
.request_timestamp(test_timestamp)
594+
.build()
595+
.unwrap();
596+
597+
let e = auth
598+
.validate_signature("us-east-1", "example", test_timestamp, mismatch, &mut get_signing_key_svc.clone())
599+
.await
600+
.unwrap_err();
601+
602+
if let SignatureError::IO(_) = e {
603+
let e_string = e.to_string();
604+
assert!(
605+
e_string.contains("No such file or directory")
606+
|| e_string.contains("The system cannot find the file specified"),
607+
"Error message: {:#?}",
608+
e_string
609+
);
610+
assert_eq!(e.error_code(), "InternalFailure");
611+
assert_eq!(e.http_status(), 500);
612+
assert!(e.source().is_some());
564613
} else {
565614
panic!("Unexpected error: {:?}", e);
566615
}
@@ -579,8 +628,10 @@ mod tests {
579628
.await
580629
.unwrap_err();
581630

582-
if let SignatureError::InvalidClientTokenId(msg) = e {
583-
assert_eq!(msg.as_str(), "The AWS access key provided does not exist in our records");
631+
if let SignatureError::InvalidClientTokenId(_) = e {
632+
assert_eq!(e.to_string(), "The AWS access key provided does not exist in our records");
633+
assert_eq!(e.error_code(), "InvalidClientTokenId");
634+
assert_eq!(e.http_status(), 403);
584635
} else {
585636
panic!("Unexpected error: {:?}", e);
586637
}
@@ -599,8 +650,10 @@ mod tests {
599650
.await
600651
.unwrap_err();
601652

602-
if let SignatureError::SignatureDoesNotMatch(msg) = e {
603-
assert_eq!(msg.unwrap().as_str(), "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.");
653+
if let SignatureError::SignatureDoesNotMatch(_) = e {
654+
assert_eq!(e.to_string(), "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.");
655+
assert_eq!(e.error_code(), "SignatureDoesNotMatch");
656+
assert_eq!(e.http_status(), 403);
604657
} else {
605658
panic!("Unexpected error: {:?}", e);
606659
}
@@ -629,4 +682,3 @@ mod tests {
629682
assert_eq!(duration_to_string(Duration::seconds(600)).as_str(), "10 min");
630683
}
631684
}
632-
// end tests -- do not delete; needed for coverage.

base-library/src/aws4.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use {
22
crate::{
3-
service_for_signing_key_fn, sigv4_validate_request_bytes, CanonicalRequest, GetSigningKeyRequest,
4-
GetSigningKeyResponse, KSecretKey,
3+
service_for_signing_key_fn, sigv4_validate_request, CanonicalRequest, GetSigningKeyRequest,
4+
GetSigningKeyResponse, KSecretKey, SignedHeaderRequirements,
55
},
66
bytes::{Bytes, BytesMut},
77
chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc},
@@ -224,7 +224,8 @@ async fn run(basename: &str) {
224224
expected_canonical_request.retain(|c| *c != b'\r'); // Remove carriage returns (not newlines)
225225

226226
// Check the canonical request.
227-
let auth_params = canonical.get_auth_parameters().expect("Failed to get auth parameters");
227+
let req = SignedHeaderRequirements::empty();
228+
let auth_params = canonical.get_auth_parameters(&req).expect("Failed to get auth parameters");
228229
let canonical_request = canonical.canonical_request(&auth_params.signed_headers);
229230
assert_eq!(
230231
String::from_utf8_lossy(canonical_request.as_slice()),
@@ -271,7 +272,8 @@ async fn run(basename: &str) {
271272
// Create a GetSigningKeyRequest from our existing request.
272273
debug!("body: {:?}", body);
273274
let request = Request::from_parts(parts, body);
274-
sigv4_validate_request_bytes(request, TEST_REGION, TEST_SERVICE, &mut signing_key_svc, test_time)
275+
let required_headers = SignedHeaderRequirements::empty();
276+
sigv4_validate_request(request, TEST_REGION, TEST_SERVICE, &mut signing_key_svc, test_time, &required_headers)
275277
.await
276278
.expect(&format!("Failed to validate request: {:?}", sreq_path));
277279
}

0 commit comments

Comments
 (0)