perimeterx-fastly-enforcer 2.2.2

PerimeterX Fastly Compute@Edge Rust Enforcer
Documentation
use crate::modules::pxconstants::*;
use crate::px_debug;
use aes::cipher::{block_padding::Pkcs7, BlockModeDecrypt, KeyIvInit};
use hmac::{digest::KeyInit, Hmac, Mac};
use pbkdf2::pbkdf2_hmac_array;
use sha2::{Digest, Sha256};

type HmacSha256 = Hmac<Sha256>;
type Aes128CbcDec = cbc::Decryptor<aes::Aes256>;

pub fn is_hmac_valid(timestamp: &str, hmac: &str, cookie_secret: &str) -> bool {
    let encoded = create_hmac(timestamp, cookie_secret);
    match encoded {
        Some(e) => e.to_lowercase() == hmac.to_lowercase(),
        None => false,
    }
}

/// SHA-256 hex digest of `input` (lowercase), used for credential intelligence hashing.
pub fn sha256_hex(input: &str) -> Option<String> {
    let mut hasher = Sha256::new();
    hasher.update(input.as_bytes());
    Some(hex::encode(hasher.finalize()))
}

pub fn create_hmac(input: &str, cookie_secret: &str) -> Option<String> {
    let content = input.as_bytes();
    let key = cookie_secret.as_bytes();

    let mut mac = HmacSha256::new_from_slice(key).ok()?;

    mac.update(content);
    let result = mac.finalize();
    let s = result.into_bytes();
    Some(hex::encode(s))
}

pub fn get_cookie_hmac(
    cookie_json: &serde_json::Value,
    action_type: &str,
    cookie_secret: &str,
) -> Option<String> {
    let data = format!(
        "{}{}{}{}",
        cookie_json["t"],
        cookie_json["u"].as_str().unwrap_or_default(),
        cookie_json["v"].as_str().unwrap_or_default(),
        action_type
    );

    create_hmac(&data, cookie_secret)
}

pub fn decrypt_cookie_v3<'a>(
    cookie_secret: &String,
    salt: Vec<u8>,
    iterations: usize,
    payload: &'a mut [u8],
) -> Option<&'a [u8]> {
    let cookie_key = cookie_secret.as_bytes();
    const LEN: usize = KEY_LEN + IV_LEN;
    let decoded_key = pbkdf2_hmac_array::<Sha256, LEN>(cookie_key, &salt, iterations as u32);

    let key: &[u8; KEY_LEN] = decoded_key[0..KEY_LEN].try_into().ok()?;
    let iv: &[u8; IV_LEN] = decoded_key[KEY_LEN..].try_into().ok()?;

    match Aes128CbcDec::new(key.into(), iv.into()).decrypt_padded::<Pkcs7>(payload) {
        Ok(d) => Some(d),
        Err(e) => {
            px_debug!("Cookie decryption failed: {}", e);
            None
        }
    }
}