|
1 | 1 | use std::{fs::File, io::Read, time::Duration};
|
2 | 2 |
|
3 | 3 | use chrono::{offset::Utc, DateTime};
|
| 4 | +use hmac::Hmac; |
4 | 5 | use once_cell::sync::Lazy;
|
5 | 6 | use rand::distributions::{Alphanumeric, DistString};
|
6 | 7 | use serde::Deserialize;
|
| 8 | +use sha2::{Digest, Sha256}; |
7 | 9 | use tokio::sync::Mutex;
|
8 | 10 |
|
9 | 11 | use crate::{
|
@@ -142,24 +144,6 @@ async fn authenticate_stream_inner(
|
142 | 144 | // &server_first.server_nonce,
|
143 | 145 | // )?;
|
144 | 146 |
|
145 |
| - // let mut client_second_payload = doc! { |
146 |
| - // "a": authorization_header, |
147 |
| - // "d": date.format(AWS_LONG_DATE_FMT).to_string(), |
148 |
| - // }; |
149 |
| - |
150 |
| - // if let Some(security_token) = aws_credential.session_token { |
151 |
| - // client_second_payload.insert("t", security_token); |
152 |
| - // } |
153 |
| - |
154 |
| - // attempt 1 |
155 |
| - // let mut client_second_payload = doc! { |
156 |
| - // "a": authorization_header, |
157 |
| - // "d": date_header, |
158 |
| - // }; |
159 |
| - // if let Some(token) = token_header { |
160 |
| - // client_second_payload.insert("t", token); |
161 |
| - // } |
162 |
| - |
163 | 147 | let sigv4_headers = compute_aws_sigv4_headers(
|
164 | 148 | creds,
|
165 | 149 | date,
|
@@ -547,129 +531,128 @@ impl AwsCredential {
|
547 | 531 | .map_err(|_| Error::unknown_authentication_error(MECH_NAME))
|
548 | 532 | }
|
549 | 533 |
|
550 |
| - // Computes the signed authorization header for the credentials to send to the server in a sasl |
551 |
| - // payload. |
552 |
| - // fn compute_authorization_header( |
553 |
| - // &self, |
554 |
| - // date: DateTime<Utc>, |
555 |
| - // host: &str, |
556 |
| - // server_nonce: &[u8], |
557 |
| - // ) -> Result<String> { |
558 |
| - // let date_str = date.format(AWS_LONG_DATE_FMT).to_string(); |
559 |
| - |
560 |
| - // // We need to include the security token header if the user provided a token. If not, we |
561 |
| - // // just use the empty string. |
562 |
| - // let token = self |
563 |
| - // .session_token |
564 |
| - // .as_ref() |
565 |
| - // .map(|s| format!("x-amz-security-token:{}\n", s)) |
566 |
| - // .unwrap_or_default(); |
567 |
| - |
568 |
| - // // Similarly, we need to put "x-amz-security-token" into the list of signed headers if |
569 |
| - // the // // user provided a token. If not, we just use the empty string. |
570 |
| - // let token_signed_header = if self.session_token.is_some() { |
571 |
| - // "x-amz-security-token;" |
572 |
| - // } else { |
573 |
| - // "" |
574 |
| - // }; |
575 |
| - |
576 |
| - // // Generate the list of signed headers (either with or without the security token |
577 |
| - // header). #[rustfmt::skip] |
578 |
| - // let signed_headers = format!( |
579 |
| - // "\ |
580 |
| - // content-length;\ |
581 |
| - // content-type;\ |
582 |
| - // host;\ |
583 |
| - // x-amz-date;\ |
584 |
| - // {token_signed_header}\ |
585 |
| - // x-mongodb-gs2-cb-flag;\ |
586 |
| - // x-mongodb-server-nonce\ |
587 |
| - // ", |
588 |
| - // token_signed_header = token_signed_header, |
589 |
| - // ); |
590 |
| - |
591 |
| - // let body = "Action=GetCallerIdentity&Version=2011-06-15"; |
592 |
| - // let hashed_body = hex::encode(Sha256::digest(body.as_bytes())); |
593 |
| - |
594 |
| - // let nonce = base64::encode(server_nonce); |
595 |
| - |
596 |
| - // #[rustfmt::skip] |
597 |
| - // let request = format!( |
598 |
| - // "\ |
599 |
| - // POST\n\ |
600 |
| - // /\n\n\ |
601 |
| - // content-length:43\n\ |
602 |
| - // content-type:application/x-www-form-urlencoded\n\ |
603 |
| - // host:{host}\n\ |
604 |
| - // x-amz-date:{date}\n\ |
605 |
| - // {token}\ |
606 |
| - // x-mongodb-gs2-cb-flag:n\n\ |
607 |
| - // x-mongodb-server-nonce:{nonce}\n\n\ |
608 |
| - // {signed_headers}\n\ |
609 |
| - // {hashed_body}\ |
610 |
| - // ", |
611 |
| - // host = host, |
612 |
| - // date = date_str, |
613 |
| - // token = token, |
614 |
| - // nonce = nonce, |
615 |
| - // signed_headers = signed_headers, |
616 |
| - // hashed_body = hashed_body, |
617 |
| - // ); |
618 |
| - |
619 |
| - // let hashed_request = hex::encode(Sha256::digest(request.as_bytes())); |
620 |
| - |
621 |
| - // let small_date = date.format("%Y%m%d").to_string(); |
622 |
| - |
623 |
| - // let region = if host == "sts.amazonaws.com" { |
624 |
| - // "us-east-1" |
625 |
| - // } else { |
626 |
| - // let parts: Vec<_> = host.split('.').collect(); |
627 |
| - // parts.get(1).copied().unwrap_or("us-east-1") |
628 |
| - // }; |
629 |
| - |
630 |
| - // #[rustfmt::skip] |
631 |
| - // let string_to_sign = format!( |
632 |
| - // "\ |
633 |
| - // AWS4-HMAC-SHA256\n\ |
634 |
| - // {full_date}\n\ |
635 |
| - // {small_date}/{region}/sts/aws4_request\n\ |
636 |
| - // {hashed_request}\ |
637 |
| - // ", |
638 |
| - // full_date = date_str, |
639 |
| - // small_date = small_date, |
640 |
| - // region = region, |
641 |
| - // hashed_request = hashed_request, |
642 |
| - // ); |
643 |
| - |
644 |
| - // let first_hmac_key = format!("AWS4{}", self.secret_access_key); |
645 |
| - // let k_date = |
646 |
| - // auth::mac::<Hmac<Sha256>>(first_hmac_key.as_ref(), small_date.as_ref(), MECH_NAME)?; |
647 |
| - // let k_region = auth::mac::<Hmac<Sha256>>(k_date.as_ref(), region.as_ref(), MECH_NAME)?; |
648 |
| - // let k_service = auth::mac::<Hmac<Sha256>>(k_region.as_ref(), b"sts", MECH_NAME)?; |
649 |
| - // let k_signing = auth::mac::<Hmac<Sha256>>(k_service.as_ref(), b"aws4_request", |
650 |
| - // MECH_NAME)?; |
651 |
| - |
652 |
| - // let signature_bytes = |
653 |
| - // auth::mac::<Hmac<Sha256>>(k_signing.as_ref(), string_to_sign.as_ref(), MECH_NAME)?; |
654 |
| - // let signature = hex::encode(signature_bytes); |
655 |
| - |
656 |
| - // #[rustfmt::skip] |
657 |
| - // let auth_header = format!( |
658 |
| - // "\ |
659 |
| - // AWS4-HMAC-SHA256 \ |
660 |
| - // Credential={access_key}/{small_date}/{region}/sts/aws4_request, \ |
661 |
| - // SignedHeaders={signed_headers}, \ |
662 |
| - // Signature={signature}\ |
663 |
| - // ", |
664 |
| - // access_key = self.access_key_id, |
665 |
| - // small_date = small_date, |
666 |
| - // region = region, |
667 |
| - // signed_headers = signed_headers, |
668 |
| - // signature = signature |
669 |
| - // ); |
670 |
| - |
671 |
| - // Ok(auth_header) |
672 |
| - // } |
| 534 | + /// Computes the signed authorization header for the credentials to send to the server in a sasl |
| 535 | + /// payload. |
| 536 | + fn compute_authorization_header( |
| 537 | + &self, |
| 538 | + date: DateTime<Utc>, |
| 539 | + host: &str, |
| 540 | + server_nonce: &[u8], |
| 541 | + ) -> Result<String> { |
| 542 | + let date_str = date.format(AWS_LONG_DATE_FMT).to_string(); |
| 543 | + |
| 544 | + // We need to include the security token header if the user provided a token. If not, we |
| 545 | + // just use the empty string. |
| 546 | + let token = self |
| 547 | + .session_token |
| 548 | + .as_ref() |
| 549 | + .map(|s| format!("x-amz-security-token:{}\n", s)) |
| 550 | + .unwrap_or_default(); |
| 551 | + |
| 552 | + // Similarly, we need to put "x-amz-security-token" into the list of signed headers if the |
| 553 | + // user provided a token. If not, we just use the empty string. |
| 554 | + let token_signed_header = if self.session_token.is_some() { |
| 555 | + "x-amz-security-token;" |
| 556 | + } else { |
| 557 | + "" |
| 558 | + }; |
| 559 | + |
| 560 | + // Generate the list of signed headers (either with or without the security token header). |
| 561 | + #[rustfmt::skip] |
| 562 | + let signed_headers = format!( |
| 563 | + "\ |
| 564 | + content-length;\ |
| 565 | + content-type;\ |
| 566 | + host;\ |
| 567 | + x-amz-date;\ |
| 568 | + {token_signed_header}\ |
| 569 | + x-mongodb-gs2-cb-flag;\ |
| 570 | + x-mongodb-server-nonce\ |
| 571 | + ", |
| 572 | + token_signed_header = token_signed_header, |
| 573 | + ); |
| 574 | + |
| 575 | + let body = "Action=GetCallerIdentity&Version=2011-06-15"; |
| 576 | + let hashed_body = hex::encode(Sha256::digest(body.as_bytes())); |
| 577 | + |
| 578 | + let nonce = base64::encode(server_nonce); |
| 579 | + |
| 580 | + #[rustfmt::skip] |
| 581 | + let request = format!( |
| 582 | + "\ |
| 583 | + POST\n\ |
| 584 | + /\n\n\ |
| 585 | + content-length:43\n\ |
| 586 | + content-type:application/x-www-form-urlencoded\n\ |
| 587 | + host:{host}\n\ |
| 588 | + x-amz-date:{date}\n\ |
| 589 | + {token}\ |
| 590 | + x-mongodb-gs2-cb-flag:n\n\ |
| 591 | + x-mongodb-server-nonce:{nonce}\n\n\ |
| 592 | + {signed_headers}\n\ |
| 593 | + {hashed_body}\ |
| 594 | + ", |
| 595 | + host = host, |
| 596 | + date = date_str, |
| 597 | + token = token, |
| 598 | + nonce = nonce, |
| 599 | + signed_headers = signed_headers, |
| 600 | + hashed_body = hashed_body, |
| 601 | + ); |
| 602 | + |
| 603 | + let hashed_request = hex::encode(Sha256::digest(request.as_bytes())); |
| 604 | + |
| 605 | + let small_date = date.format("%Y%m%d").to_string(); |
| 606 | + |
| 607 | + let region = if host == "sts.amazonaws.com" { |
| 608 | + "us-east-1" |
| 609 | + } else { |
| 610 | + let parts: Vec<_> = host.split('.').collect(); |
| 611 | + parts.get(1).copied().unwrap_or("us-east-1") |
| 612 | + }; |
| 613 | + |
| 614 | + #[rustfmt::skip] |
| 615 | + let string_to_sign = format!( |
| 616 | + "\ |
| 617 | + AWS4-HMAC-SHA256\n\ |
| 618 | + {full_date}\n\ |
| 619 | + {small_date}/{region}/sts/aws4_request\n\ |
| 620 | + {hashed_request}\ |
| 621 | + ", |
| 622 | + full_date = date_str, |
| 623 | + small_date = small_date, |
| 624 | + region = region, |
| 625 | + hashed_request = hashed_request, |
| 626 | + ); |
| 627 | + |
| 628 | + let first_hmac_key = format!("AWS4{}", self.secret_access_key); |
| 629 | + let k_date = |
| 630 | + auth::mac::<Hmac<Sha256>>(first_hmac_key.as_ref(), small_date.as_ref(), MECH_NAME)?; |
| 631 | + let k_region = auth::mac::<Hmac<Sha256>>(k_date.as_ref(), region.as_ref(), MECH_NAME)?; |
| 632 | + let k_service = auth::mac::<Hmac<Sha256>>(k_region.as_ref(), b"sts", MECH_NAME)?; |
| 633 | + let k_signing = auth::mac::<Hmac<Sha256>>(k_service.as_ref(), b"aws4_request", MECH_NAME)?; |
| 634 | + |
| 635 | + let signature_bytes = |
| 636 | + auth::mac::<Hmac<Sha256>>(k_signing.as_ref(), string_to_sign.as_ref(), MECH_NAME)?; |
| 637 | + let signature = hex::encode(signature_bytes); |
| 638 | + |
| 639 | + #[rustfmt::skip] |
| 640 | + let auth_header = format!( |
| 641 | + "\ |
| 642 | + AWS4-HMAC-SHA256 \ |
| 643 | + Credential={access_key}/{small_date}/{region}/sts/aws4_request, \ |
| 644 | + SignedHeaders={signed_headers}, \ |
| 645 | + Signature={signature}\ |
| 646 | + ", |
| 647 | + access_key = self.access_key_id, |
| 648 | + small_date = small_date, |
| 649 | + region = region, |
| 650 | + signed_headers = signed_headers, |
| 651 | + signature = signature |
| 652 | + ); |
| 653 | + |
| 654 | + Ok(auth_header) |
| 655 | + } |
673 | 656 |
|
674 | 657 | #[cfg(feature = "in-use-encryption")]
|
675 | 658 | pub(crate) fn access_key(&self) -> &str {
|
|
0 commit comments