From 5bfc8d2221b090f100ff42d2e43ff49d47571b5e Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 3 Sep 2023 17:43:24 +0800 Subject: [PATCH] feat: updates --- Cargo.toml | 1 + src/main.rs | 1 + src/util.rs | 11 +++++ src/v4/access_key.rs | 33 +++++++++++++- src/v4/aliyun_util.rs | 102 +++++++++++++++++++++++++++++++++++++----- src/v4/constant.rs | 5 +++ 6 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index e053c78..956f82b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" hex = "0.4.3" hmac-sha256 = "1.1.7" hmac-sm3 = "0.1.0" +rand = "0.8.5" sha256 = "1.4.0" sm3 = "0.4.2" zeroize = "1.6.0" diff --git a/src/main.rs b/src/main.rs index d51f7ac..9ab73bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod v4; +mod util; // mod v4_algorithm; fn main() { diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..6b558eb --- /dev/null +++ b/src/util.rs @@ -0,0 +1,11 @@ +use std::collections::BTreeMap; + +pub(crate) trait BTreeMapAddKv { + fn insert_kv(&mut self, key: impl Into, value: impl Into); +} + +impl BTreeMapAddKv for BTreeMap { + fn insert_kv(&mut self, key: impl Into, value: impl Into) { + self.insert(key.into(), value.into()); + } +} \ No newline at end of file diff --git a/src/v4/access_key.rs b/src/v4/access_key.rs index 5bcc49f..32f546d 100644 --- a/src/v4/access_key.rs +++ b/src/v4/access_key.rs @@ -5,6 +5,7 @@ use zeroize::Zeroize; #[derive(Clone, PartialEq, Eq)] pub struct AccessKey { pub access_key_id: String, + pub security_token: Option, pub access_key_secret: String, } @@ -17,7 +18,10 @@ impl Drop for AccessKey { impl Debug for AccessKey { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "AccessKey{{ID: {}}}", &self.access_key_id) + write!(f, "AccessKey{{ID: {}, Token: {}}}", &self.access_key_id, match self.security_token { + Some(_) => "Some(...)", + None => "None", + }) } } @@ -37,4 +41,29 @@ impl Debug for DerivedAccessKey { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "DerivedAccessKey{{ID: {}}}", &self.access_key_id) } -} \ No newline at end of file +} + +#[test] +fn test_access_key() { + let access_key = AccessKey { + access_key_id: "key_id".to_string(), + security_token: None, + access_key_secret: "key_secret".to_string(), + }; + assert_eq!("AccessKey{ID: key_id, Token: None}", format!("{:?}", access_key)); + let access_key = AccessKey { + access_key_id: "key_id".to_string(), + security_token: Some("token".to_string()), + access_key_secret: "key_secret".to_string(), + }; + assert_eq!("AccessKey{ID: key_id, Token: Some(...)}", format!("{:?}", access_key)); +} + +#[test] +fn test_derived_access_key() { + let access_key = DerivedAccessKey { + access_key_id: "key_id".to_string(), + derived_access_key_secret: vec![0u8; 32], + }; + assert_eq!("DerivedAccessKey{ID: key_id}", format!("{:?}", access_key)); +} diff --git a/src/v4/aliyun_util.rs b/src/v4/aliyun_util.rs index 46e1cb4..4e7cf83 100644 --- a/src/v4/aliyun_util.rs +++ b/src/v4/aliyun_util.rs @@ -1,17 +1,99 @@ use std::collections::BTreeMap; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::SystemTime; -use sm3::Digest; +use rand::random; -use crate::v4::access_key::DerivedAccessKey; +use crate::util::BTreeMapAddKv; +use crate::v4::access_key::{AccessKey, DerivedAccessKey}; use crate::v4::common_util::{join_slices, percent_encode}; -use crate::v4::constant::{ - ALIYUN_V4, ALIYUN_V4_REQUEST, - HEADER_CONTENT_TYPE, - HEADER_HOST, HEADER_X_ACS_PREFIX, REGION_CENTER, -}; +use crate::v4::constant::{ALIYUN_V4, ALIYUN_V4_REQUEST, CONTENT_TYPE_APPLICATION_JSON, HEADER_ACCEPT, HEADER_AUTHORIZATION, HEADER_CONTENT_TYPE, HEADER_HOST, HEADER_USER_AGENT, HEADER_X_ACS_PREFIX, HEADER_X_ASC_ACCESS_KEY_ID, HEADER_X_ASC_ACTION, HEADER_X_ASC_CONTENT_SHA256, HEADER_X_ASC_CONTENT_SM3, HEADER_X_ASC_DATE, HEADER_X_ASC_SECURITY_TOKEN, HEADER_X_ASC_SIGNATURE_NONCE, HEADER_X_ASC_VERSION, REGION_CENTER}; use crate::v4::sign_algorithm::SignAlgorithm; -fn get_authorization(sign_algorithm: SignAlgorithm, +const SEQ: AtomicU64 = AtomicU64::new(0); + +struct Request { + pub version: String, + pub action: String, + pub access_key: Option, + pub user_agent: String, + pub sign_algorithm: SignAlgorithm, + pub headers: BTreeMap, + pub stream: Option>, +} + +fn add_common_headers(header: &mut BTreeMap, request: &Request) { + header.insert_kv(HEADER_HOST, "endpoint"); // TODO + header.insert_kv(HEADER_X_ASC_VERSION, &request.version); + header.insert_kv(HEADER_X_ASC_ACTION, &request.action); + header.insert_kv(HEADER_USER_AGENT, &request.user_agent); + header.insert_kv(HEADER_X_ASC_DATE, get_timestamp()); + header.insert_kv(HEADER_X_ASC_SIGNATURE_NONCE, get_nonce()); + header.insert_kv(HEADER_ACCEPT, CONTENT_TYPE_APPLICATION_JSON); + + // TODO BODY ... + + match &request.sign_algorithm { + // TODO ... + SignAlgorithm::Sha256 => { + header.insert_kv(HEADER_X_ASC_CONTENT_SHA256, ""); + } + SignAlgorithm::Sm3 => { + header.insert_kv(HEADER_X_ASC_CONTENT_SM3, ""); + } + } + + let query = BTreeMap::new(); + + if let Some(access_key) = &request.access_key { + if let Some(security_token) = &access_key.security_token { + header.insert_kv(HEADER_X_ASC_ACCESS_KEY_ID, &access_key.access_key_id); + header.insert_kv(HEADER_X_ASC_SECURITY_TOKEN, security_token); + } + + let signing_key = get_signing_key( + &request.sign_algorithm, + &access_key.access_key_secret, + "yyyy-mm-dd",// TODO DATE, + "region", // TODO REGION + "product", // TODO PRODUCT + ); + let derived_access_key = DerivedAccessKey { + access_key_id: access_key.access_key_id.clone(), + derived_access_key_secret: signing_key, + }; + let authorization = get_authorization( + &request.sign_algorithm, + &derived_access_key, + "yyyy-mm-dd",// TODO DATE, + "region", // TODO REGION + "product", // TODO PRODUCT + "pathname", // TODO pathname + "method", // TODO method + &query, + header, + "payload", // TODO payload + ); + header.insert_kv(HEADER_AUTHORIZATION, authorization); + } +} + + +fn get_timestamp() -> String { + // TODO ... + "yyyy-mm-dd".into() +} + + +fn get_nonce() -> String { + let seq = SEQ.fetch_add(1, Ordering::Relaxed); + let now = SystemTime::now(); + let rand_bytes: [u8; 32] = random(); + let seed = format!("{}-{:?}-{:?}", seq, now, rand_bytes); + "nonce-".to_string() + &SignAlgorithm::Sha256.digest(seed.as_bytes()) +} + +fn get_authorization(sign_algorithm: &SignAlgorithm, access_key: &DerivedAccessKey, date: &str, region: &str, product: &str, pathname: &str, @@ -40,7 +122,7 @@ fn get_authorization(sign_algorithm: SignAlgorithm, authorization } -fn get_signature(sign_algorithm: SignAlgorithm, +fn get_signature(sign_algorithm: &SignAlgorithm, signing_key: &[u8], pathname: &str, method: &str, @@ -73,7 +155,7 @@ fn get_signature(sign_algorithm: SignAlgorithm, hex::encode(sign_algorithm.hmac_sign(string_to_sign.as_bytes(), signing_key)) } -fn get_signing_key(sign_algorithm: SignAlgorithm, secret: &str, date: &str, region: &str, product: &str) -> Vec { +fn get_signing_key(sign_algorithm: &SignAlgorithm, secret: &str, date: &str, region: &str, product: &str) -> Vec { let sc1 = join_slices(ALIYUN_V4.as_bytes(), secret.as_bytes()); let sc2 = sign_algorithm.hmac_sign(date.as_bytes(), &sc1); let sc3 = sign_algorithm.hmac_sign(region.as_bytes(), &sc2); diff --git a/src/v4/constant.rs b/src/v4/constant.rs index fcbede4..667b3ac 100644 --- a/src/v4/constant.rs +++ b/src/v4/constant.rs @@ -5,11 +5,16 @@ pub(crate) const ALIYUN_V4_REQUEST: &str = "aliyun_v4_request"; pub(crate) const HEADER_X_ACS_PREFIX: &str = "x-acs-"; pub(crate) const HEADER_HOST: &str = "host"; pub(crate) const HEADER_ACCEPT: &str = "accept"; +pub(crate) const HEADER_AUTHORIZATION: &str = "Authorization"; pub(crate) const HEADER_USER_AGENT: &str = "user-agent"; pub(crate) const HEADER_CONTENT_TYPE: &str = "content-type"; pub(crate) const HEADER_X_ASC_VERSION: &str = "x-acs-version"; pub(crate) const HEADER_X_ASC_ACTION: &str = "x-acs-action"; pub(crate) const HEADER_X_ASC_DATE: &str = "x-acs-date"; +pub(crate) const HEADER_X_ASC_ACCESS_KEY_ID: &str = "x-acs-accesskey-id"; +pub(crate) const HEADER_X_ASC_SECURITY_TOKEN: &str = "x-acs-security-token"; pub(crate) const HEADER_X_ASC_SIGNATURE_NONCE: &str = "x-acs-signature-nonce"; +pub(crate) const HEADER_X_ASC_CONTENT_SM3: &str = "x-acs-content-sm3"; +pub(crate) const HEADER_X_ASC_CONTENT_SHA256: &str = "x-acs-content-sha256"; pub(crate) const CONTENT_TYPE_APPLICATION_JSON: &str = "application/json";