Unverified Commit 44111311 authored by 82marbag's avatar 82marbag Committed by GitHub
Browse files

Constant time comparison for SigV4a (#3174)



Closes #3162 

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Signed-off-by: default avatarDaniele Ahmed <ahmeddan@amazon.de>
Co-authored-by: default avatarRussell Cohen <rcoh@amazon.com>
parent 31625f5b
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ default = ["sign-http"]
http0-compat = ["dep:http"]
sign-http = ["dep:http", "dep:percent-encoding", "dep:form_urlencoded"]
sign-eventstream = ["dep:aws-smithy-eventstream"]
sigv4a = ["dep:p256", "dep:num-bigint", "dep:zeroize", "dep:ring"]
sigv4a = ["dep:p256", "dep:crypto-bigint", "dep:subtle", "dep:zeroize", "dep:ring"]

[dependencies]
aws-credential-types = { path = "../aws-credential-types" }
@@ -33,6 +33,8 @@ percent-encoding = { version = "2.1", optional = true }
regex = "1.5"
ring = { version = "0.17.5", optional = true }
sha2 = "0.10"
crypto-bigint = { version = "0.5.4", optional = true }
subtle = { version = "2.5.0", optional = true }
time = "0.3.5"
tracing = "0.1"
zeroize = { version = "^1", optional = true }
+11 −12
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@

use aws_smithy_runtime_api::client::identity::Identity;
use bytes::{BufMut, BytesMut};
use num_bigint::BigInt;
use crypto_bigint::{CheckedAdd, CheckedSub, Encoding, U256};
use once_cell::sync::Lazy;
use p256::ecdsa::signature::Signer;
use p256::ecdsa::{Signature, SigningKey};
@@ -14,16 +14,13 @@ use std::time::SystemTime;
use zeroize::Zeroizing;

const ALGORITHM: &[u8] = b"AWS4-ECDSA-P256-SHA256";
static BIG_N_MINUS_2: Lazy<BigInt> = Lazy::new(|| {
static BIG_N_MINUS_2: Lazy<U256> = Lazy::new(|| {
    // The N value from section 3.2.1.3 of https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf
    // Used as the N value for the algorithm described in section A.2.2 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
    // *(Basically a prime number blessed by the NSA for use in p256)*
    const ORDER: &[u32] = &[
        0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xBCE6FAAD, 0xA7179E84, 0xF3B9CAC2,
        0xFC632551,
    ];
    let big_n = BigInt::from_slice(num_bigint::Sign::Plus, ORDER);
    big_n - BigInt::from(2i32)
    const ORDER: U256 =
        U256::from_be_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
    ORDER.checked_sub(&U256::from(2u32)).unwrap()
});

/// Calculates a Sigv4a signature
@@ -64,14 +61,16 @@ pub fn generate_signing_key(access_key: &str, secret_access_key: &str) -> impl A
        let tag = ring::hmac::sign(&key, &buf);
        let tag = &tag.as_ref()[0..32];

        let k0 = BigInt::from_bytes_be(num_bigint::Sign::Plus, tag);
        let k0 = U256::from_be_bytes(tag.try_into().expect("convert to [u8; 32]"));

        // It would be more secure for this to be a constant time comparison, but because this
        // is for client usage, that's not as big a deal.
        if k0 <= *BIG_N_MINUS_2 {
            let pk = k0 + BigInt::from(1i32);
            let d = Zeroizing::new(pk.to_bytes_be().1);
            break SigningKey::from_bytes(&d).unwrap();
            let pk = k0
                .checked_add(&U256::ONE)
                .expect("k0 is always less than U256::MAX");
            let d = Zeroizing::new(pk.to_be_bytes());
            break SigningKey::from_bytes(d.as_ref()).unwrap();
        }

        *counter = counter