|
1 |
| -#![warn(clippy::all)] |
2 |
| - |
3 |
| -//! The `aws_sig_verify` crate provides AWS SigV4 _verification_ routines. This *is not* the library you want if you |
4 |
| -//! just want to call AWS services or other services that use AWS SigV4 signatures. |
5 |
| -//! [Rusoto](https://github.com/rusoto/rusoto) already has a library, |
6 |
| -//! [rusoto_signature](https://docs.rs/rusoto_signature/), that provides this functionality. |
| 1 | +//! AWS API request signatures verification routines. |
| 2 | +//! |
| 3 | +//! The `scratchstack_aws_signature` crate provides |
| 4 | +//! AWS [SigV4](http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) |
| 5 | +//! _validation_ routines. This *is not* the library you want if you just want to call AWS services |
| 6 | +//! or other services that use AWS SigV4 signatures. [Rusoto](https://github.com/rusoto/rusoto) |
| 7 | +//! already has a library, [rusoto_signature](https://docs.rs/rusoto_signature/), that provides |
| 8 | +//! this functionality. |
| 9 | +//! |
| 10 | +//! If you are attempting to perform AWS SigV4 verification using AWS-vended credentials, this |
| 11 | +//! library also ___will not work for you___. You need the caller's secret key (or a derivative), |
| 12 | +//! and AWS does not allow this for obvious reasons. Instead, you should be using [API Gateway with |
| 13 | +//! IAM authentication](https://docs.aws.amazon.com/apigateway/latest/developerguide/permissions.html). |
| 14 | +//! |
| 15 | +//! On the other hand, if you have your own ecosystem of AWS-like credentials and are developing |
| 16 | +//! mock-AWS services or other services that need to use AWS SigV4, this _might_ be the right |
| 17 | +//! crate for you. |
| 18 | +//! |
| 19 | +//! # Workflow |
| 20 | +//! This assumes you have a complete HTTP request (headers _and_ body) already. As a result, you may not be able to |
| 21 | +//! implement this as a middleware layer for a web server—those typically only provide the headers. Having the body is |
| 22 | +//! required for almost all modes of AWS SigV4. |
| 23 | +//! |
| 24 | +//! The typical workflow is: |
| 25 | +//! 1. Convert an HTTP `Request` object into a scratchstack `Request` object. |
| 26 | +//! 2. Create a `GetSigningKeyRequest` from this `Request`. |
| 27 | +//! 3. Call your service to obtain the principal and signing key for this request. |
| 28 | +//! 4. Verify the request using `sigv4_verify` or `sigv4_verify_at`. |
| 29 | +//! |
| 30 | +//! ## Example |
| 31 | +//! ```rust |
| 32 | +//! use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc}; |
| 33 | +//! use http::Request; |
| 34 | +//! use scratchstack_aws_principal::{Principal, User}; |
| 35 | +//! use scratchstack_aws_signature::{ |
| 36 | +//! GetSigningKeyRequest, GetSigningKeyResponse, KSecretKey, SignatureOptions, |
| 37 | +//! SignedHeaderRequirements, service_for_signing_key_fn, sigv4_validate_request, |
| 38 | +//! }; |
| 39 | +//! use tower::{BoxError, Service}; |
| 40 | +//! |
| 41 | +//! const ACCESS_KEY: &str = "AKIAIOSFODNN7EXAMPLE"; |
| 42 | +//! const SECRET_KEY: &str = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; |
| 43 | +//! const ACCOUNT_ID: &str = "123456789012"; |
| 44 | +//! const PARTITION: &str = "aws"; |
| 45 | +//! const PATH: &str = "/engineering/"; |
| 46 | +//! const REGION: &str = "us-east-1"; |
| 47 | +//! const SERVICE: &str = "example"; |
| 48 | +//! const USER_NAME: &str = "user"; |
| 49 | +//! const USER_ID: &str = "AIDAQXZEAEXAMPLEUSER"; |
| 50 | +//! |
| 51 | +//! // The date for which the signature calculation was made. |
| 52 | +//! #[allow(deprecated)] |
| 53 | +//! const TEST_TIMESTAMP: DateTime<Utc> = DateTime::<Utc>::from_naive_utc_and_offset( |
| 54 | +//! NaiveDateTime::new( |
| 55 | +//! NaiveDate::from_ymd(2021, 1, 1), |
| 56 | +//! NaiveTime::from_hms(0, 0, 0)), |
| 57 | +//! Utc |
| 58 | +//! ); |
| 59 | +//! |
| 60 | +//! // This is a mock function that returns a static secret key converted into the requested type |
| 61 | +//! // of signing key. For actual use, you would call out to a database or other service to obtain |
| 62 | +//! // a signing key. |
| 63 | +//! async fn get_signing_key( |
| 64 | +//! request: GetSigningKeyRequest) |
| 65 | +//! -> Result<GetSigningKeyResponse, BoxError> { |
| 66 | +//! assert_eq!(request.access_key(), ACCESS_KEY); |
| 67 | +//! assert_eq!(request.region(), REGION); |
| 68 | +//! assert_eq!(request.service(), SERVICE); |
| 69 | +//! let user = User::new(PARTITION, ACCOUNT_ID, PATH, USER_NAME)?; |
| 70 | +//! let secret_key = KSecretKey::from_str(SECRET_KEY); |
| 71 | +//! let signing_key = secret_key.to_ksigning(request.request_date(), REGION, SERVICE); |
| 72 | +//! Ok(GetSigningKeyResponse::builder() |
| 73 | +//! .principal(user) |
| 74 | +//! .signing_key(signing_key) |
| 75 | +//! .build()?) |
| 76 | +//! } |
| 77 | +//! |
| 78 | +//! // Wrap `get_signing_key` in a `tower::Service`. |
| 79 | +//! let mut get_signing_key_service = service_for_signing_key_fn(get_signing_key); |
| 80 | +//! |
| 81 | +//! // Normally this would come from your web framework. |
| 82 | +//! let req = Request::get("https://example.com") |
| 83 | +//! .header("Host", "example.com") |
| 84 | +//! .header("X-Amz-Date", "20210101T000000Z") |
| 85 | +//! .header("Authorization", "AWS4-HMAC-SHA256 \ |
| 86 | +//! Credential=AKIAIOSFODNN7EXAMPLE/20210101/us-east-1/example/aws4_request, \ |
| 87 | +//! SignedHeaders=host;x-amz-date, \ |
| 88 | +//! Signature=3ea4679d2ecf5a8293e1fb10298c82988f024a2e937e9b37876b34bb119da0bc") |
| 89 | +//! .body(()) |
| 90 | +//! .unwrap(); |
| 91 | +//! |
| 92 | +//! // The headers that _must_ be signed (beyond the default SigV4 headers) for this service. |
| 93 | +//! // In this case, we're not requiring any additional headers. |
| 94 | +//! let signed_headers = SignedHeaderRequirements::default(); |
7 | 95 | //!
|
8 |
| -//! If you are attempting to perform AWS SigV4 verification using AWS-vended credentials, this library also |
9 |
| -//! ___will not work for you___. You need the caller's secret key (or a derivative), and AWS does not allow this for |
10 |
| -//! obvious reasons. Instead, you should be using [API Gateway with IAM |
11 |
| -//! authentication](https://docs.aws.amazon.com/apigateway/latest/developerguide/permissions.html). |
| 96 | +//! // Signature options for the request. Defaults are typically used, except for S3. |
| 97 | +//! let signature_options = SignatureOptions::default(); |
12 | 98 | //!
|
13 |
| -//! On the other hand, if you have your own ecosystem of AWS-like credentials and are developing mock-AWS services or |
14 |
| -//! just really like AWS SigV4 but can't run within AWS, this library _might_ be for you. |
| 99 | +//! # tokio_test::block_on(async { |
| 100 | +//! // Validate the request. |
| 101 | +//! let (parts, body, auth) = sigv4_validate_request( |
| 102 | +//! req, ®ION, &SERVICE, &mut get_signing_key_service, TEST_TIMESTAMP, &signed_headers, |
| 103 | +//! signature_options).await.unwrap(); |
15 | 104 | //!
|
16 |
| -#![allow(clippy::all)] |
| 105 | +//! // The principal we expect to be associated with the request. |
| 106 | +//! let expected_principal: Principal = User::new(PARTITION, ACCOUNT_ID, PATH, USER_NAME) |
| 107 | +//! .unwrap() |
| 108 | +//! .into(); |
| 109 | +//! assert_eq!(auth.principal(), &expected_principal); |
| 110 | +//! # }); |
| 111 | +//! ``` |
| 112 | +#![warn(missing_docs)] |
| 113 | +#![deny(rustdoc::broken_intra_doc_links)] |
| 114 | +#![warn(rustdoc::missing_crate_level_docs)] |
17 | 115 |
|
18 | 116 | mod auth;
|
19 |
| -pub mod canonical; |
| 117 | +mod canonical; |
20 | 118 | mod chronoutil;
|
21 | 119 | mod crypto;
|
22 | 120 | mod error;
|
23 |
| -pub mod signature; |
| 121 | +mod signature; |
24 | 122 | mod signing_key;
|
25 | 123 |
|
26 | 124 | pub use {
|
|
0 commit comments