Include payload in integrity controls
This commit is contained in:
		
							
								
								
									
										108
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										108
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -235,7 +235,7 @@ version = "0.5.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "crypto-common 0.1.6",
 | 
			
		||||
 "generic-array",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -577,6 +577,15 @@ dependencies = [
 | 
			
		||||
 "generic-array",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "block-buffer"
 | 
			
		||||
version = "0.11.0-rc.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3fd016a0ddc7cb13661bf5576073ce07330a693f8608a1320b4e20561cc12cdc"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "hybrid-array",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "brotli"
 | 
			
		||||
version = "6.0.0"
 | 
			
		||||
@@ -671,7 +680,7 @@ version = "0.4.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "crypto-common 0.1.6",
 | 
			
		||||
 "inout",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -765,6 +774,12 @@ version = "0.9.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "const-oid"
 | 
			
		||||
version = "0.10.0-rc.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "68ff6be19477a1bd5441f382916a89bc2a0b2c35db6d41e0f6e8538bf6d6463f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "const-random"
 | 
			
		||||
version = "0.1.18"
 | 
			
		||||
@@ -809,7 +824,7 @@ dependencies = [
 | 
			
		||||
 "hmac",
 | 
			
		||||
 "percent-encoding",
 | 
			
		||||
 "rand 0.8.5",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
 "subtle",
 | 
			
		||||
 "time",
 | 
			
		||||
 "version_check",
 | 
			
		||||
@@ -878,6 +893,17 @@ dependencies = [
 | 
			
		||||
 "typenum",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crypto-common"
 | 
			
		||||
version = "0.2.0-rc.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b0b8ce8218c97789f16356e7896b3714f26c2ee1079b79c0b7ae7064bb9089fa"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "getrandom 0.2.15",
 | 
			
		||||
 "hybrid-array",
 | 
			
		||||
 "rand_core 0.6.4",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ct-codecs"
 | 
			
		||||
version = "1.1.3"
 | 
			
		||||
@@ -899,7 +925,7 @@ version = "0.7.9"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "const-oid",
 | 
			
		||||
 "const-oid 0.9.6",
 | 
			
		||||
 "pem-rfc7468",
 | 
			
		||||
 "zeroize",
 | 
			
		||||
]
 | 
			
		||||
@@ -954,12 +980,23 @@ version = "0.10.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "block-buffer",
 | 
			
		||||
 "const-oid",
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "block-buffer 0.10.4",
 | 
			
		||||
 "const-oid 0.9.6",
 | 
			
		||||
 "crypto-common 0.1.6",
 | 
			
		||||
 "subtle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "digest"
 | 
			
		||||
version = "0.11.0-pre.9"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cf2e3d6615d99707295a9673e889bf363a04b2a466bd320c65a72536f7577379"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "block-buffer 0.11.0-rc.3",
 | 
			
		||||
 "const-oid 0.10.0-rc.3",
 | 
			
		||||
 "crypto-common 0.2.0-rc.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "displaydoc"
 | 
			
		||||
version = "0.2.5"
 | 
			
		||||
@@ -987,7 +1024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "der",
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
 "elliptic-curve",
 | 
			
		||||
 "rfc6979",
 | 
			
		||||
 "signature",
 | 
			
		||||
@@ -1012,7 +1049,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "base16ct",
 | 
			
		||||
 "crypto-bigint",
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
 "ff",
 | 
			
		||||
 "generic-array",
 | 
			
		||||
 "group",
 | 
			
		||||
@@ -1361,7 +1398,7 @@ version = "0.12.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -1376,7 +1413,7 @@ version = "1.1.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4a8575493d277c9092b988c780c94737fb9fd8651a1001e16bee3eccfc1baedb"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -1385,7 +1422,7 @@ version = "1.1.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b0b3a0f572aa8389d325f5852b9e0a333a15b0f86ecccbb3fdb6e97cd86dc67c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -1469,6 +1506,15 @@ version = "2.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hybrid-array"
 | 
			
		||||
version = "0.2.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "typenum",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hyper"
 | 
			
		||||
version = "1.5.2"
 | 
			
		||||
@@ -1804,7 +1850,7 @@ dependencies = [
 | 
			
		||||
 "ecdsa",
 | 
			
		||||
 "elliptic-curve",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
 "signature",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -1903,6 +1949,8 @@ dependencies = [
 | 
			
		||||
 "actix-web",
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "askama",
 | 
			
		||||
 "base16ct",
 | 
			
		||||
 "bytes",
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "clap",
 | 
			
		||||
 "env_logger",
 | 
			
		||||
@@ -1918,6 +1966,7 @@ dependencies = [
 | 
			
		||||
 "rust-s3",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "sha2 0.11.0-pre.4",
 | 
			
		||||
 "thiserror 2.0.11",
 | 
			
		||||
 "urlencoding",
 | 
			
		||||
 "uuid",
 | 
			
		||||
@@ -2172,7 +2221,7 @@ dependencies = [
 | 
			
		||||
 "ecdsa",
 | 
			
		||||
 "elliptic-curve",
 | 
			
		||||
 "primeorder",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -2184,7 +2233,7 @@ dependencies = [
 | 
			
		||||
 "ecdsa",
 | 
			
		||||
 "elliptic-curve",
 | 
			
		||||
 "primeorder",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -2569,15 +2618,15 @@ version = "0.9.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "const-oid",
 | 
			
		||||
 "digest",
 | 
			
		||||
 "const-oid 0.9.6",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
 "num-bigint-dig",
 | 
			
		||||
 "num-integer",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
 "pkcs1",
 | 
			
		||||
 "pkcs8",
 | 
			
		||||
 "rand_core 0.6.4",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
 "signature",
 | 
			
		||||
 "spki",
 | 
			
		||||
 "subtle",
 | 
			
		||||
@@ -2614,7 +2663,7 @@ version = "8.5.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
 "walkdir",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -2655,7 +2704,7 @@ dependencies = [
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "sha2 0.10.8",
 | 
			
		||||
 "thiserror 1.0.69",
 | 
			
		||||
 "time",
 | 
			
		||||
 "tokio",
 | 
			
		||||
@@ -2880,7 +2929,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -2891,7 +2940,18 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sha2"
 | 
			
		||||
version = "0.11.0-pre.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "540c0893cce56cdbcfebcec191ec8e0f470dd1889b6e7a0b503e310a94a168f5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest 0.11.0-pre.9",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
@@ -2915,7 +2975,7 @@ version = "2.2.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "digest",
 | 
			
		||||
 "digest 0.10.7",
 | 
			
		||||
 "rand_core 0.6.4",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@@ -3314,7 +3374,7 @@ version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
 "crypto-common 0.1.6",
 | 
			
		||||
 "subtle",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,3 +27,6 @@ chrono = "0.4.39"
 | 
			
		||||
futures-util = "0.3.31"
 | 
			
		||||
jwt-simple = { version = "0.12.11", default-features=false, features=["pure-rust"] }
 | 
			
		||||
actix-remote-ip = "0.1.0"
 | 
			
		||||
bytes = "1.9.0"
 | 
			
		||||
sha2 = "0.11.0-pre.4"
 | 
			
		||||
base16ct = "0.2.0"
 | 
			
		||||
@@ -31,6 +31,10 @@ struct Args {
 | 
			
		||||
    #[arg(short('X'), long, default_value = "GET")]
 | 
			
		||||
    method: String,
 | 
			
		||||
 | 
			
		||||
    /// Payload SHA256 digest
 | 
			
		||||
    #[arg(short('D'), long)]
 | 
			
		||||
    payload_digest: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Request URI
 | 
			
		||||
    uri: String,
 | 
			
		||||
 | 
			
		||||
@@ -59,6 +63,7 @@ fn main() {
 | 
			
		||||
        custom: TokenClaims {
 | 
			
		||||
            method: args.method.to_string(),
 | 
			
		||||
            uri: args.uri,
 | 
			
		||||
            payload_sha256: args.payload_digest.clone(),
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,25 +3,34 @@ use crate::utils::curr_time;
 | 
			
		||||
use actix_remote_ip::RemoteIP;
 | 
			
		||||
use actix_web::dev::Payload;
 | 
			
		||||
use actix_web::{FromRequest, HttpRequest};
 | 
			
		||||
use bytes::Bytes;
 | 
			
		||||
use jwt_simple::common::VerificationOptions;
 | 
			
		||||
use jwt_simple::prelude::{Duration, HS256Key, MACLike};
 | 
			
		||||
use sha2::{Digest, Sha256};
 | 
			
		||||
use std::net::IpAddr;
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
pub struct APIClientAuth {
 | 
			
		||||
    pub user: UserConfig,
 | 
			
		||||
    client: APIClient,
 | 
			
		||||
    payload: Option<Vec<u8>>,
 | 
			
		||||
    pub payload: Option<Vec<u8>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct TokenClaims {
 | 
			
		||||
    #[serde(rename = "met")]
 | 
			
		||||
    pub method: String,
 | 
			
		||||
    pub uri: String,
 | 
			
		||||
    #[serde(rename = "pay", skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub payload_sha256: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl APIClientAuth {
 | 
			
		||||
    async fn extract_auth(req: &HttpRequest, remote_ip: IpAddr) -> Result<Self, actix_web::Error> {
 | 
			
		||||
    async fn extract_auth(
 | 
			
		||||
        req: &HttpRequest,
 | 
			
		||||
        remote_ip: IpAddr,
 | 
			
		||||
        payload_bytes: Option<Bytes>,
 | 
			
		||||
    ) -> Result<Self, actix_web::Error> {
 | 
			
		||||
        let Some(token) = req.headers().get("x-client-auth") else {
 | 
			
		||||
            return Err(actix_web::error::ErrorBadRequest(
 | 
			
		||||
                "Missing authentication header!",
 | 
			
		||||
@@ -129,7 +138,27 @@ impl APIClientAuth {
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO : handle payload
 | 
			
		||||
        let payload = match (payload_bytes, claims.custom.payload_sha256) {
 | 
			
		||||
            (None, _) => None,
 | 
			
		||||
            (Some(_), None) => {
 | 
			
		||||
                return Err(actix_web::error::ErrorBadRequest(
 | 
			
		||||
                    "A payload digest must be included in the JWT when the request has a payload!",
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
            (Some(payload), Some(provided_digest)) => {
 | 
			
		||||
                let computed_digest = base16ct::lower::encode_string(&Sha256::digest(&payload));
 | 
			
		||||
                if computed_digest != provided_digest {
 | 
			
		||||
                    log::error!(
 | 
			
		||||
                        "Expected digest {provided_digest} but computed {computed_digest}!"
 | 
			
		||||
                    );
 | 
			
		||||
                    return Err(actix_web::error::ErrorBadRequest(
 | 
			
		||||
                        "Computed digest is different from the one provided in the JWT!",
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Some(payload.to_vec())
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Update last use (if needed)
 | 
			
		||||
        if client.need_update_last_used() {
 | 
			
		||||
@@ -145,7 +174,7 @@ impl APIClientAuth {
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            client: client.clone(),
 | 
			
		||||
            payload: None,
 | 
			
		||||
            payload,
 | 
			
		||||
            user,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
@@ -163,6 +192,24 @@ impl FromRequest for APIClientAuth {
 | 
			
		||||
            Err(e) => return Box::pin(async { Err(e) }),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Box::pin(async move { Self::extract_auth(&req, remote_ip.0).await })
 | 
			
		||||
        let mut payload = payload.take();
 | 
			
		||||
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            let payload_bytes = match Bytes::from_request(&req, &mut payload).await {
 | 
			
		||||
                Ok(b) => {
 | 
			
		||||
                    if b.is_empty() {
 | 
			
		||||
                        None
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Some(b)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    log::error!("Failed to extract request payload! {e}");
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Self::extract_auth(&req, remote_ip.0, payload_bytes).await
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user