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
Generated
+152 -280
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -22,7 +22,7 @@ rust-embed = "8"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
sha1 = "0.10" sha1 = "0.11"
sha2 = "0.11.0" sha2 = "0.11.0"
hmac = "0.13" hmac = "0.13"
subtle = "2" subtle = "2"
+5 -5
View File
@@ -15,15 +15,15 @@ bft = []
[dependencies] [dependencies]
bft-crdt-derive = { path = "bft-crdt-derive" } bft-crdt-derive = { path = "bft-crdt-derive" }
colored = "2.0.0" colored = "3"
fastcrypto = "0.1.9" fastcrypto = "0.1.9"
indexmap = { version = "2.2.6", features = ["serde"] } indexmap = { version = "2.2.6", features = ["serde"] }
rand = "0.8" rand = "0.9"
random_color = "0.6.1" random_color = "1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0.85", features = ["preserve_order"] } serde_json = { version = "1.0.85", features = ["preserve_order"] }
serde_with = "3.18" serde_with = "3"
sha2 = "0.10.6" sha2 = "0.11"
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] } criterion = { version = "0.5", features = ["html_reports"] }
+1 -1
View File
@@ -33,7 +33,7 @@ fn bench_insert_many_agents_conflicts(c: &mut Criterion) {
c.bench_function("bench insert many agents conflicts", |b| { c.bench_function("bench insert many agents conflicts", |b| {
b.iter(|| { b.iter(|| {
const N: u8 = 10; const N: u8 = 10;
let mut rng = rand::thread_rng(); let mut rng = rand::rng();
let mut crdts: Vec<ListCrdt<i64>> = Vec::with_capacity(N as usize); let mut crdts: Vec<ListCrdt<i64>> = Vec::with_capacity(N as usize);
let mut logs: Vec<Op<JsonValue>> = Vec::new(); let mut logs: Vec<Op<JsonValue>> = Vec::new();
for i in 0..N { for i in 0..N {
+1 -1
View File
@@ -18,7 +18,7 @@ use {
op::{print_hex, print_path, ROOT_ID}, op::{print_hex, print_path, ROOT_ID},
}, },
colored::Colorize, colored::Colorize,
random_color::{Luminosity, RandomColor}, random_color::{options::Luminosity, RandomColor},
}; };
#[cfg(feature = "logging-list")] #[cfg(feature = "logging-list")]
+5 -3
View File
@@ -10,9 +10,10 @@ pub use fastcrypto::{
Ed25519KeyPair, Ed25519PublicKey, Ed25519Signature, ED25519_PUBLIC_KEY_LENGTH, Ed25519KeyPair, Ed25519PublicKey, Ed25519Signature, ED25519_PUBLIC_KEY_LENGTH,
ED25519_SIGNATURE_LENGTH, ED25519_SIGNATURE_LENGTH,
}, },
traits::{KeyPair, Signer}, traits::{KeyPair, Signer, ToFromBytes},
// Verifier, // Verifier,
}; };
use rand::RngCore as _;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
/// Represents the ID of a unique node. An Ed25519 public key /// Represents the ID of a unique node. An Ed25519 public key
@@ -48,8 +49,9 @@ pub fn sha256(input: String) -> [u8; 32] {
/// Generate a random Ed25519 keypair from OS rng /// Generate a random Ed25519 keypair from OS rng
pub fn make_keypair() -> Ed25519KeyPair { pub fn make_keypair() -> Ed25519KeyPair {
let mut csprng = rand::thread_rng(); let mut seed = [0u8; 32];
Ed25519KeyPair::generate(&mut csprng) rand::rng().fill_bytes(&mut seed);
Ed25519KeyPair::from_bytes(&seed).expect("32-byte seed always yields a valid Ed25519 keypair")
} }
/// Sign a byte array /// Sign a byte array
+12 -9
View File
@@ -5,9 +5,12 @@ use bft_json_crdt::{
list_crdt::ListCrdt, list_crdt::ListCrdt,
op::{Op, OpId, ROOT_ID}, op::{Op, OpId, ROOT_ID},
}; };
use rand::{rngs::ThreadRng, seq::SliceRandom, Rng}; use rand::{
seq::{IndexedRandom, SliceRandom},
Rng,
};
fn random_op<T: CrdtNode>(arr: &[Op<T>], rng: &mut ThreadRng) -> OpId { fn random_op<T: CrdtNode>(arr: &[Op<T>], rng: &mut impl Rng) -> OpId {
arr.choose(rng).map(|op| op.id).unwrap_or(ROOT_ID) arr.choose(rng).map(|op| op.id).unwrap_or(ROOT_ID)
} }
@@ -15,7 +18,7 @@ const TEST_N: usize = 100;
#[test] #[test]
fn test_list_fuzz_commutative() { fn test_list_fuzz_commutative() {
let mut rng = rand::thread_rng(); let mut rng = rand::rng();
let mut op_log = Vec::<Op<JsonValue>>::new(); let mut op_log = Vec::<Op<JsonValue>>::new();
let mut op_log1 = Vec::<Op<JsonValue>>::new(); let mut op_log1 = Vec::<Op<JsonValue>>::new();
let mut op_log2 = Vec::<Op<JsonValue>>::new(); let mut op_log2 = Vec::<Op<JsonValue>>::new();
@@ -23,14 +26,14 @@ fn test_list_fuzz_commutative() {
let mut l2 = ListCrdt::<char>::new(make_author(2), vec![]); let mut l2 = ListCrdt::<char>::new(make_author(2), vec![]);
let mut chk = ListCrdt::<char>::new(make_author(3), vec![]); let mut chk = ListCrdt::<char>::new(make_author(3), vec![]);
for _ in 0..TEST_N { for _ in 0..TEST_N {
let letter1: char = rng.gen_range(b'a'..=b'z') as char; let letter1: char = rng.random_range(b'a'..=b'z') as char;
let letter2: char = rng.gen_range(b'a'..=b'z') as char; let letter2: char = rng.random_range(b'a'..=b'z') as char;
let op1 = if rng.gen_bool(4.0 / 5.0) { let op1 = if rng.random_bool(4.0 / 5.0) {
l1.insert(random_op(&op_log1, &mut rng), letter1) l1.insert(random_op(&op_log1, &mut rng), letter1)
} else { } else {
l1.delete(random_op(&op_log1, &mut rng)) l1.delete(random_op(&op_log1, &mut rng))
}; };
let op2 = if rng.gen_bool(4.0 / 5.0) { let op2 = if rng.random_bool(4.0 / 5.0) {
l2.insert(random_op(&op_log2, &mut rng), letter2) l2.insert(random_op(&op_log2, &mut rng), letter2)
} else { } else {
l2.delete(random_op(&op_log2, &mut rng)) l2.delete(random_op(&op_log2, &mut rng))
@@ -67,8 +70,8 @@ fn test_list_fuzz_commutative() {
let mut op_log1 = Vec::<Op<JsonValue>>::new(); let mut op_log1 = Vec::<Op<JsonValue>>::new();
let mut op_log2 = Vec::<Op<JsonValue>>::new(); let mut op_log2 = Vec::<Op<JsonValue>>::new();
for _ in 0..TEST_N { for _ in 0..TEST_N {
let letter1: char = rng.gen_range(b'a'..=b'z') as char; let letter1: char = rng.random_range(b'a'..=b'z') as char;
let letter2: char = rng.gen_range(b'a'..=b'z') as char; let letter2: char = rng.random_range(b'a'..=b'z') as char;
let op1 = l1.insert(random_op(&op_log, &mut rng), letter1); let op1 = l1.insert(random_op(&op_log, &mut rng), letter1);
let op2 = l2.insert(random_op(&op_log, &mut rng), letter2); let op2 = l2.insert(random_op(&op_log, &mut rng), letter2);
op_log1.push(op1); op_log1.push(op1);
+1 -1
View File
@@ -48,7 +48,7 @@ bft-json-crdt = { path = "../crates/bft-json-crdt", default-features = false, fe
source-map-gen = { path = "../crates/source-map-gen" } source-map-gen = { path = "../crates/source-map-gen" }
ed25519-dalek = { version = "2", features = ["rand_core"] } ed25519-dalek = { version = "2", features = ["rand_core"] }
fastcrypto = "0.1.8" fastcrypto = "0.1.8"
rand = "0.8" rand = "0.9"
indexmap = { version = "2.2.6", features = ["serde"] } indexmap = { version = "2.2.6", features = ["serde"] }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
+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 base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
use hmac::{Hmac, KeyInit, Mac}; use hmac::{Hmac, KeyInit, Mac};
use sha1::Digest as Sha1Digest; use sha1::Sha1;
use sha2::Sha256; use sha2::Sha256;
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
type HmacSha256 = Hmac<Sha256>; type HmacSha256 = Hmac<Sha256>;
type HmacSha1 = Hmac<Sha1>;
/// Verify the `X-Hub-Signature-256` header sent by Meta. /// 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() 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] { fn hmac_sha1(key: &[u8], message: &[u8]) -> [u8; 20] {
const BLOCK: usize = 64; let mut mac = HmacSha1::new_from_slice(key).expect("HMAC accepts any key size");
mac.update(message);
// Shorten key to its SHA-1 hash if it exceeds the block size. mac.finalize().into_bytes().into()
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()
} }
/// Build the string that Twilio signs for a POST webhook. /// 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. /// which must respond with a valid signature from its node keypair.
pub fn generate_challenge() -> ChallengeHex { pub fn generate_challenge() -> ChallengeHex {
let mut bytes = [0u8; 32]; let mut bytes = [0u8; 32];
rand::thread_rng().fill_bytes(&mut bytes); rand::rng().fill_bytes(&mut bytes);
hex_encode(&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) SigningKey::from_bytes(&seed)
} else { } else {
// Generate a fresh keypair and persist the seed. // Generate a fresh keypair and persist the seed.
let sk = SigningKey::generate(&mut rand::rngs::OsRng); let mut seed = [0u8; 32];
let seed = sk.to_bytes(); 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 // Create the file with mode 0600 at creation time (Unix) so the seed
// is never visible to other users even transiently. // is never visible to other users even transiently.