9346444bdaff
fix: small-order DH guard compares the leading 32 bytes of snow's 56-byte MAXDHLEN buffer — the strict try_from never fired on real handshakes (found by the Android parity test); regression test now drives all nine blocklist entries through the KK handshake plus a padded-buffer case, WASM rebuilt; bump 5.0.16→5.0.17
- time
- 2026-08-30 02:32 UTC · 1 month ago
- machine
- koh2
- agent
- glm-5.3-flash
- session
- small-order dh rejection
- parent
- 282a941beb44
↓ peach-browser-9346444b.tar.gzatlas at this save
files changed6 files
crypto-core/src/lib.rs+33 −11manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/shared/noise-core-wasm/peach_noise_core_bg.wasmbinarysrc/shared/noise-core-wasm/peach_noise_core_bytes.ts+2 −2crypto-core/src/lib.rs+33 −11
@@ -426,9 +426,16 @@426426 ];427427 428428 fn is_small_order_x25519_pubkey(public_key: &[u8]) -> bool {429- let Ok(key) = <[u8; 32]>::try_from(public_key) else {429+ // snow 0.10 passes the remote static inside its 56-byte MAXDHLEN buffer430+ // (32 key bytes + 24 trailing zeros), so a strict 32-byte try_from always431+ // fails and the guard would never fire on a real handshake. Compare the432+ // leading 32 bytes whenever a full key is present.433+ let Some(key) = public_key.get(..32) else {430434 return false;431435 };436+ let Ok(key) = <[u8; 32]>::try_from(key) else {437+ return false;438+ };432439 X25519_SMALL_ORDER_PUBKEYS433440 .iter()434441 .any(|low| low.ct_eq(&key).unwrap_u8() == 1)@@ -802,24 +809,39 @@802809 803810 // PB-26-014: contributory behaviour — a small-order remote static must804811 // fail the handshake at the DH step instead of contributing a constant805- // low-entropy secret to the keying material.812+ // low-entropy secret to the keying material. Drive ALL nine entries:813+ // seven have an all-zero DH output (the pre-existing check catches them)814+ // but two produce non-zero output and only this guard rejects them.806815 #[test]807816 fn small_order_remote_static_fails_handshake() {808817 let (local_private, local_public) = keypair(11);809- let mut initiator = CoreNoiseSession::kk_initiator(810- &local_private,811- &local_public,812- &X25519_SMALL_ORDER_PUBKEYS[3],813- )814- .unwrap();815- let result = initiator.write_handshake(&[]);816- assert!(result.is_err());817- // Every small-order key is recognized by the guard; real keys never are.818818 for low in X25519_SMALL_ORDER_PUBKEYS.iter() {819+ let mut initiator =820+ CoreNoiseSession::kk_initiator(&local_private, &local_public, low).unwrap();821+ assert!(822+ initiator.write_handshake(&[]).is_err(),823+ "handshake against small-order static must fail"824+ );819825 assert!(is_small_order_x25519_pubkey(low));820826 }821827 let (_, real_public) = keypair(9);822828 assert!(!is_small_order_x25519_pubkey(&real_public));829+ }830+831+ // snow hands the guard the remote static inside its 56-byte MAXDHLEN832+ // buffer (32 key bytes + 24 trailing zeros) — the guard must compare the833+ // leading 32 bytes, not require exactly 32 (PB-26-014 regression).834+ #[test]835+ fn small_order_guard_handles_snow_padded_buffers() {836+ for low in X25519_SMALL_ORDER_PUBKEYS.iter() {837+ let mut padded = [0u8; 56];838+ padded[..32].copy_from_slice(low);839+ assert!(is_small_order_x25519_pubkey(&padded));840+ }841+ let (_, real_public) = keypair(13);842+ let mut padded = [0u8; 56];843+ padded[..32].copy_from_slice(&real_public);844+ assert!(!is_small_order_x25519_pubkey(&padded));823845 }824846 825847 #[test]manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.0.16",4+ "version": "5.0.17",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {manifest.json+1 −1
@@ -2,7 +2,7 @@22 "manifest_version": 3,33 "minimum_chrome_version": "116",44 "name": "Peach Password Manager",5- "version": "5.0.16",5+ "version": "5.0.17",66 "description": "Zero-knowledge password manager",77 "action": {88 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "5.0.16",3+ "version": "5.0.17",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/shared/noise-core-wasm/peach_noise_core_bytes.ts+2 −2
@@ -866,7 +866,7 @@866866 "WyAGIAQgBSACEFsgA0EsaiADQbACakHQAPwKAAAgA0GwAmogA0H8AGogA0HgAWogAhBbIAYgBSAHIAIQWyADQfwAaiADQbACakHQAPwKAAAgA0EsaiADQfwA",867867 "aiADQQRqECcgAyABOgDPASABIQgMAAsLIAgQ5AIhAiADQeABaiADQSxqQdAA/AoAACADQbACaiADQSxqIANB/ABqIAIQWyADQbACakEoaiIBIAQgBSACEFsg",868868 "A0EsaiADQbACakHQAPwKAAAgA0GwAmogA0H8AGogA0HgAWogAhBbIAEgBSADQeABakEoaiACEFsgA0EAOgDPASADIANBzwFqNgKwAiADQbACaiECIANBzwFq",869- "EKcDIAAgA0EsahCqAiADQYADaiQADwsgCUEgQbDUwAAQ+wEAC9IEAQh/IwBBwABrIgYkAAJAAkACQAJAAkAgA0EgRw0AIAYgAikAGDcDICAGIAIpABA3Axgg",869+ "EKcDIAAgA0EsahCqAiADQYADaiQADwsgCUEgQbDUwAAQ+wEAC9IEAQh/IwBBwABrIgYkAAJAAkACQAJAAkAgA0EgSQ0AIAYgAikAGDcDICAGIAIpABA3Axgg",870870 "BiACKQAINwMQIAYgAikAADcDCCAGQQhqQSBqIQdBACEIA0AgCEGgAkYNASAGQShqIAhB7aXAAGogCEGNpsAAaiAGQQhqIAcQmgJBASEJAkAgBigCPCIKIAYo",871871 "AjgiC00NACAGKAIoIAtqIQwgBigCMCALaiENIAogC2shC0EBIQkDQCANLQAAIAwtAABzIgpBACAKa3LAQX9KEOQCIAlxIQkgDEEBaiEMIA1BAWohDSALQX9q",872872 "IgsNAAsLIAhBIGohCCAJEOQCQf8BcUEBRw0ADAILCyAGIAEoAgAiDSACIAMgBCAFIAEoAgQiCSgCKBELAAJAIAYtAAAiDEEIRg0AIAYtAAEhDQwDCyANIAko",@@ -1583,7 +1583,7 @@15831583 "RFzEWByOhtgiTt3QnxFX7P///////////////////////////////////////3/t////////////////////////////////////////f+7/////////////",15841584 "//////////////////////////9/zet6fDtBuK4WVuP68Z/EatoJjeucMrH9hmIFFl9JuIBMnJW8o1CMJLHQsVWcg+9bBERcxFgcjobYIk7d0J8R1wAAAAcA",15851585 "AAAMAAAABAAAAAgAAAAJAAAACgAAAAAAAAAAAAAAAQAAAAsAAABhIERpc3BsYXkgaW1wbGVtZW50YXRpb24gcmV0dXJuZWQgYW4gZXJyb3IgdW5leHBlY3Rl",1586- "ZGx5AHwIEABLAAAAZAsAAA4AAAAAAAAAAAAAAAEAAAAMAAAAug0QAAoAAADYAQAAEgAAAA0AAAAIAAAABAAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQA",1586+ "ZGx5AHwIEABLAAAAZAsAAA4AAAAAAAAAAAAAAAEAAAAMAAAAug0QAAoAAADfAQAAEgAAAA0AAAAIAAAABAAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQA",15871587 "AAAVAAAAFgAAAAAAAQAAAAAABAAAAAQAAAACAAAAcwcQAE8AAADNAQAANwAAAFRyeUZyb21TbGljZUVycm9yAAAA+wUQAE4AAAANCAAAEQAAAKwKEABqAAAA",15881588 "fAAAACQAAABODBAAXwAAAKQAAAAgAAAATgwQAF8AAACuAAAAGgAAAE4MEABfAAAAnQAAACUAAABODBAAXwAAADgBAAAeAAAATgwQAF8AAAA1AQAAGAAAAAAA",15891589 "AgEAAAIBAQACAwAAAgECAwAAAgAAANAUEAABAAAAihUQAAIAAAB4FRAAAgAAAIoVEAACAAAAAAACAAEAAgHQFBAAAQAAALAVEAAEAAAAAQACAtAUEAABAAAA",