huskies: merge 925

This commit is contained in:
dave
2026-05-12 18:28:42 +00:00
parent 7660a460a5
commit f9f16d6a14
10 changed files with 187 additions and 344 deletions
+5 -40
View File
@@ -12,11 +12,12 @@ use std::fmt::Write as FmtWrite;
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
use hmac::{Hmac, KeyInit, Mac};
use sha1::Digest as Sha1Digest;
use sha1::Sha1;
use sha2::Sha256;
use subtle::ConstantTimeEq;
type HmacSha256 = Hmac<Sha256>;
type HmacSha1 = Hmac<Sha1>;
/// Verify the `X-Hub-Signature-256` header sent by Meta.
///
@@ -75,46 +76,10 @@ pub(super) fn verify_twilio_signature(
computed_b64.as_bytes().ct_eq(signature.as_bytes()).into()
}
/// HMAC-SHA1 using the `sha1 = "0.10"` audited crate as the hash primitive.
///
/// `sha1 = "0.10"` uses `digest 0.10`, which is incompatible with
/// `hmac = "0.13"` (digest 0.11), so the HMAC construction (ipad/opad
/// per RFC 2104) is done here using sha1's `Digest` trait directly.
fn hmac_sha1(key: &[u8], message: &[u8]) -> [u8; 20] {
const BLOCK: usize = 64;
// Shorten key to its SHA-1 hash if it exceeds the block size.
let key_hash: [u8; 20];
let key = if key.len() > BLOCK {
key_hash = sha1::Sha1::digest(key).into();
&key_hash[..]
} else {
key
};
// Pad key to block size.
let mut k = [0u8; BLOCK];
k[..key.len()].copy_from_slice(key);
// Inner hash: SHA1(k ^ ipad || message)
let mut ipad = k;
for b in &mut ipad {
*b ^= 0x36;
}
let mut h = sha1::Sha1::new();
Sha1Digest::update(&mut h, ipad);
Sha1Digest::update(&mut h, message);
let inner: [u8; 20] = h.finalize().into();
// Outer hash: SHA1(k ^ opad || inner)
let mut opad = k;
for b in &mut opad {
*b ^= 0x5c;
}
let mut h = sha1::Sha1::new();
Sha1Digest::update(&mut h, opad);
Sha1Digest::update(&mut h, inner);
h.finalize().into()
let mut mac = HmacSha1::new_from_slice(key).expect("HMAC accepts any key size");
mac.update(message);
mac.finalize().into_bytes().into()
}
/// Build the string that Twilio signs for a POST webhook.
+4 -3
View File
@@ -65,7 +65,7 @@ pub type SignatureHex = String;
/// which must respond with a valid signature from its node keypair.
pub fn generate_challenge() -> ChallengeHex {
let mut bytes = [0u8; 32];
rand::thread_rng().fill_bytes(&mut bytes);
rand::rng().fill_bytes(&mut bytes);
hex_encode(&bytes)
}
@@ -184,8 +184,9 @@ pub fn load_or_create_keypair_file(path: &std::path::Path) -> std::io::Result<No
SigningKey::from_bytes(&seed)
} else {
// Generate a fresh keypair and persist the seed.
let sk = SigningKey::generate(&mut rand::rngs::OsRng);
let seed = sk.to_bytes();
let mut seed = [0u8; 32];
rand::rng().fill_bytes(&mut seed);
let sk = SigningKey::from_bytes(&seed);
// Create the file with mode 0600 at creation time (Unix) so the seed
// is never visible to other users even transiently.