#![allow( clippy::unwrap_used, clippy::indexing_slicing, clippy::arithmetic_side_effects )] use super::*; fn header(profile: u8) -> ([u8; 32], Header) { for candidate in 0u8..=255 { let seed = [candidate; 32]; let public_key = public_key_from_seed(profile, &seed).unwrap(); let current = commitment(profile, &public_key).unwrap(); let descriptor = Descriptor { version: VERSION, algorithm: profile, initial_commitment: current, }; let account = account_id(&descriptor); if is_fixed_profile(profile) && !rejects_legacy_public_keys(&account) { continue; } return ( seed, Header { account, version: VERSION, algorithm: profile, generation: 0, public_key, next_commitment: if is_fixed_profile(profile) { current } else { [0x47; 64] }, }, ); } panic!("bounded fixture derivation failed") } #[test] fn all_profiles_bind_entire_transaction_and_preserve_raw_wire_lengths() { let payload = vec![0x42; 1024]; for profile in [ ALGORITHM_512, FIXED_ALGORITHM_512, ALGORITHM_1024, FIXED_ALGORITHM_1024, ] { let (seed, header) = header(profile); let proof = header.sign(&seed, &payload, &[0x59; 32]).unwrap(); assert!(proof.verify(&payload)); assert_eq!(proof.encode().len(), proof_len(profile).unwrap()); assert_eq!(header.encode().len(), header_len(profile).unwrap()); assert_eq!(SignerAddress::from_bytes(&proof.encode()).unwrap(), proof); let mut changed = payload.clone(); changed[999] ^= 1; assert!(!proof.verify(&changed)); for field in 0..6 { let mut bad = proof.clone(); match field { 0 => bad.account[0] ^= 1, 1 => bad.generation = 1, 2 => bad.next_commitment[0] ^= 1, 3 => bad.public_key[42] ^= 1, 4 => bad.signature[50] ^= 1, _ => { bad.algorithm = if profile % 2 == 0 { profile + 1 } else { profile - 1 } } } assert!(!bad.verify(&payload), "profile {profile}, field {field}"); } let bytes = proof.encode(); for size in [0, 33, 42, bytes.len() - 1] { assert!(SignerAddress::from_bytes(&bytes[..size]).is_err()); } let mut extended = bytes; extended.push(0); assert!(SignerAddress::from_bytes(&extended).is_err()); } } #[test] fn fixed_accounts_are_self_authenticating_and_rotating_profiles_are_not() { for profile in [FIXED_ALGORITHM_512, FIXED_ALGORITHM_1024] { let (_, mut header) = header(profile); assert!(header.is_valid_stateless()); assert!(rejects_legacy_public_keys(&header.account)); header.generation = 1; assert!(!header.is_valid_stateless()); header.generation = 0; header.next_commitment[0] ^= 1; assert!(!header.is_valid_stateless()); } for profile in [ALGORITHM_512, ALGORITHM_1024] { assert!(!header(profile).1.is_valid_stateless()); } } #[test] fn every_encoded_proof_byte_is_authenticated_for_every_falcon_profile() { let payload = b"release validation: canonical proof mutation sweep"; for profile in [ ALGORITHM_512, FIXED_ALGORITHM_512, ALGORITHM_1024, FIXED_ALGORITHM_1024, ] { let (seed, header) = header(profile); let proof = header.sign(&seed, payload, &[0x63; 32]).unwrap(); assert!(proof.verify(payload)); let mut encoded = proof.encode(); // Includes every public-key and signature byte, including Falcon padding. for offset in 0..encoded.len() { encoded[offset] ^= 1; if let Ok(mutated) = SignerAddress::from_bytes(&encoded) { assert!( !mutated.verify(payload), "profile {profile} accepted mutated proof byte {offset}" ); } encoded[offset] ^= 1; } assert_eq!(SignerAddress::from_bytes(&encoded).unwrap(), proof); } } #[test] fn valid_falcon_signatures_cannot_bypass_fixed_account_authority_rules() { let payload = b"valid cryptography with invalid account authority"; for profile in [FIXED_ALGORITHM_512, FIXED_ALGORITHM_1024] { let (seed, header) = header(profile); let proof = header.sign(&seed, payload, &[0x64; 32]).unwrap(); for mutation in 0..4 { let mut forged = proof.clone(); match mutation { 0 => forged.generation = 1, 1 => forged.generation = u64::MAX, 2 => forged.next_commitment = [0; COMMITMENT_LEN], _ => forged.account = [0; 32], } let forged_header = forged.header(); assert!(!forged_header.is_valid_stateless()); assert!(forged_header.sign(&seed, payload, &[0x64; 32]).is_err()); // Bypass the safe signing API to give the invalid authority a real, // cryptographically valid signature, as a hostile client could. let digest = forged_header.representative(&RawPayload(payload)).unwrap(); let (_, signature) = signing::sign_digest(profile, &seed, &digest, &[0x64; 32]).unwrap(); forged.signature = signature; assert!(verify_digest( profile, &forged.public_key, &digest, &forged.signature, )); assert!(!forged.verify(payload)); assert!( !SignerAddress::from_bytes(&forged.encode()) .unwrap() .verify(payload) ); } } } #[test] fn detached_messages_bind_context_profile_and_canonical_padding() { for profile in [ALGORITHM_512, ALGORITHM_1024] { let seed = [0x39; 32]; let key = public_key_from_seed(profile, &seed).unwrap(); let signature = sign_message(profile, &seed, b"message", b"purpose", &[0x51; 32]).unwrap(); assert!(verify_message( profile, &key, b"message", b"purpose", &signature )); assert!(!verify_message( profile, &key, b"other", b"purpose", &signature )); assert!(!verify_message( profile, &key, b"message", b"different", &signature )); assert!(!verify_message( profile + 1, &key, b"message", b"purpose", &signature )); let mut wrong_header = signature.clone(); wrong_header[0] ^= 1; assert!(!verify_message( profile, &key, b"message", b"purpose", &wrong_header )); let mut bad_padding = signature.clone(); *bad_padding.last_mut().unwrap() = 0xff; assert!(!verify_message( profile, &key, b"message", b"purpose", &bad_padding )); assert!(!verify_message( profile, &key, b"message", b"purpose", &signature[..signature.len() - 1] )); } } #[test] fn payload_bound_is_enforced_without_encoding_a_large_allocation() { struct Oversized; impl Encode for Oversized { fn encode_to(&self, output: &mut O) { for _ in 0..=MAX_PAYLOAD_LEN / 1024 { output.write(&[0; 1024]); } } } let (seed, header) = header(ALGORITHM_512); let proof = header.sign(&seed, b"payload", &[1; 32]).unwrap(); assert!(!proof.verify_encoded(&Oversized)); } #[test] fn registration_descriptor_preserves_old_tags_and_routes_falcon() { for profile in [ALGORITHM_512, ALGORITHM_1024] { let descriptor = Descriptor { version: VERSION, algorithm: profile, initial_commitment: [1; 64], }; assert!(descriptor.is_supported()); assert!(descriptor.is_rotating()); assert_eq!( crate::RegistrationDescriptor::from(descriptor).encode()[0], 2 ); assert_eq!( Descriptor::from_bytes(&descriptor.encode()).unwrap(), descriptor ); } let old = Descriptor { version: VERSION, algorithm: crate::mldsa::ALGORITHM, initial_commitment: [1; 64], }; assert_eq!(crate::RegistrationDescriptor::from(old).encode()[0], 1); } // Original Falcon KATs distributed by Thomas Pornin's fn-dsa-vrfy 0.3.0 // under the Unlicense. These fixtures predate this wallet implementation. #[test] fn original_falcon_known_answer_vectors() { for (public, message, signature) in [ORIGINAL_512, ORIGINAL_1024] { let public = hex::decode(public).unwrap(); let message = hex::decode(message).unwrap(); let mut signature = hex::decode(signature).unwrap(); let key = VerifyingKeyStandard::decode(&public).unwrap(); assert!(key.verify(&signature, &DOMAIN_NONE, &HASH_ID_ORIGINAL_FALCON, &message)); signature[50] ^= 1; assert!(!key.verify(&signature, &DOMAIN_NONE, &HASH_ID_ORIGINAL_FALCON, &message)); } } const ORIGINAL_512: (&str, &str, &str) = ( concat!( "091164798255c8c721e8aa5a4ae3ab9ad824d9f98a81724d26517cd6d2354ceb51b30a006d40ab3bb8a43f4b50b74000", "d9b098d0a903291c9eb172b994e1e7afac4d2c952944715e086a1980047d3822c75187d74fa2184a6a1f9fe30eac998b", "00aa8689be52925d69672ddd4b17918f827c156d9458a0b05f1c9ac007e56c86f5e84ab846df2b8449e8a99154be3024", "74e747dc868052f7e146b03eb00e09f39dc28bc4810793245a6ab04dc6974957ace9a0b8ec994a9a19994b3a14bc5c61", "2b884fe028069d65f776da0eb6a28504b7836e964600b9c3ef730562ddc87be93b9186aadab807f615557ac4911fcb00", "301612e690845109e456e70a99bfbdf8da07625f9fea908339d85ca995766835c4d99ffa8708adaa936c319fc7cfc648", "2e940f452e2bdf8c8ca147178e587139cde26b562c4e71b425bae6c7abb20f134cee7e8cd5019589dddb6caaf025d2a4", "cf28859b52fb20aa44782b86cf4a2f716c1d2963509c29ba3d65a18eaa6b29baa93f2b8f0dd063722f284fa969da0a4c", "dfc98c0abfe60c8e938429da80496fb49dc43f6a40646eac71f6b98f5de2960e4a02bba6d13db9fb445b59ddbef0fbb9", "e09e08ad3917b4a78f9869a625a705c77f1bce6ad973d09e590d42b09503fa9220f17820a0fea7053e798a02d0047ebb", "b866cbd65c68c78b1721de87acd566dda80e3c5ed7b108e79e6620afd2f1589302b63e62aae2c826ac7332f0acfab756", "c688d8926d58f4242517c98e917d62aadd57ae51fa3c70d1d19bbeb03f6857c1bb0aeed7674b5bea84fa67150a1fd214", "66cb9a5a13acb1329130d25ff778f6619619eca65e794d8b3518979688c8b219d4496d867a53fde4659125a3a194c714", "b5f5b80684cebc95a8a3059e08ab2d04fc3b10cb938f1e53021ea40d0a1e229bdf08a105b59ea6c32308260644abe756", "720a71fee11c280b0ca02a947ca22295dd17e4652dc6587e374bd05b9538f06433209aa21bca958bcec2fe9f75682190", "a17660da7207ec2fd42ed8240842dccd0551c7207d1ce851ac49f07689e2ae7de379b4133497482cf7846df70e586b71", "0f44bb7f263c19151af89ad1ad6152d6478c6d3f65fed4d0ad1823b74491e0b56a00644ec178363c13a509208f4f8c83", "e4d5eebf7e3cbb193018b257798c82f86dab852e4900aa41091d72f10101cec640281fe3b1618402893d9607aeba2044", "ed42a843b88d32e680fc5b849ae193d4a00cc524993d561b8e112441c933225005", ), "dbd71151d7a9b83b0b6e2556ac824225", concat!( "39d33b744e2ceadc9c9deb583e2d091f65e623e6dfeafbb27b287137d08ace8fae59e59b76568913b72f6a8fce049c15", "4bc6a5494ffe4a5b6d8d75fdec7395e47bded536b270314dd4073d72a536ed7f9e96fa48f04c427b3ad34396078df9cf", "3271fce47194ccefa1071a7ef24098372aad45a6e2929c830f8aa3256869134778290ac8e5a56583a1348cc5a5997a93", "7583a6d02408ce8ad695bcf6a48cb97b943c088d10ec1886312a0e39df86883429fbebe62eb49ca2956143ce53063dc4", "7129d33c2ab5dbdb5df0f1514606e71beafce39a5b0aaa437e2c2efeac7b71be4bfafdeedc3752876a6b8eeba5d4c65e", "24ceeb5b978855d79cd30c61adfe6cf31c8bff9b56a631cd635a83af5a8c3a294990b1ef79d4c64f9f004f0c429fb9f3", "551b79aef1b4a4d41b54a7c92332ccfac30e894f99ead77a98ce93ae0d2def8c65508a7c957c6b4dae61a5284b4ce8e2", "b65b4c7d9e4a5f7aef0492b78ed76a33d20c32db58902276b51b3cd56bb07534a88d4025515aaad33f45548fecea106b", "5bea66b4a0fbb08c13abe08adef96a04d68f729bbd0d3539623829791d47a0b873b9bee6dc78475d069cee90e6fd63ee", "3cecdc473bd1323735fde745ad754171eb30d614daee543fbfbb59bb89a05908173d0f7a48ab6f8872b30a2f996ba6eb", "556f950d749d5269f0b3356ae4b44daf8e3667594e41b794cb481d11cc8be2ca06b90e9094cb1c03448164ddd71e0b94", "8a6f5015211e46bb12a1216412e33ed8c690c637bd33581c6a0cd593c12f79c2a320dd71e203c3184ca08fcfcb0d52e9", "6b4d9adec31d2f153a1b76d834ce00b9cc9437d91e74382bc7bd0e609aadbd9775d0eb15af5575c461a55ba52ed12523", "649f9ff922a37923c99f49cea601c9496f9c0e7e33e77085a7fcd839f789a00000000000000000000000", ), ); const ORIGINAL_1024: (&str, &str, &str) = ( concat!( "0aab5887ca451b1e5cf5464503dca9b5f44c3a7786a9b1648014f224b4a1164840584034104513f614af92440417c008", "6b513a5514d01ccfb4b53d7c583dc90cbe16586821652b79d049c56bacb67339e3253f60a2597c4f4920e2c9bfe00924", "02517a7e54e6d4bba8d681908ca39cecbc445ab976b4d6c34a76ad68ff086110497bd72fcede519542b387959955f24e", "4a75642f0c5617b2b7a9c8bd3e064be560120d0d2e376e6b869ac2e8c8925f591718c71b0a1d7a90e61a429a995da258", "2cf8470a7e554ef8c7eed71f2a8e8114bc2b755aa67a62560c23712819943b2301223d3f5b6a056a41a02e24b29dc50d", "95560601dc1a6bf15184c52fd2ba70e431a91a4b9de44c61c6ce0db8a19ad164d5a1b11df302f19fe773bc39565c2f67", "362af543b6de0302cab89bd40d271711841ba74b9b96059bd6989e905a309e64678b8b2cfcf81adf9cfa31e247456301", "a37145412956c09a40e1dfd8d7cac123b5bc0791aaa8131126c887c186790923abeac61abd780065158b2eb562ec30a4", "9255b7fcfa9a8ca15879f95938354608b8da33686fc7498140743003124183a2ca55d3dad7b000b4c7cb2f10055a572a", "92ac9d1cc6eaf721afef1a56859087a4677dd9b76822624e4e9de4a02780d89a5534c8f1840a482233a95befb5342c55", "c0f02c8a3dd6fd370fa17485e65f932fee0f50c8b8e78a525f1ce61f11e7d40a03fd6eb99f97c8b262a92598c8d497b6", "2ed6e36618033cbb9992ac554834d36b4a2fda56cc4d2615df402aa2afc3da8a1dad86a9c6e6d99695a6d10167c17500", "d8df74a4ca841cc9347a330ffbb0a90bc00dcf9b2310fba40752ea2df4c0b862a025678ee9296bd8e060ca7a8385907e", "3a2a4bf2962ee71e7ddcd7d3d0e708b94bca1ea0cc6380abd52e8b5c0378b6ea6a55c3646532d5b740740c20a5180180", "a156c19aa48715d9214e2129c049489a80dec9e157066a2844227f0de3788e68c13f19d1042eeba9210fc847c8b5b823", "29c678963d94e129b14d4a9de9fac7224552cc66a3dc85bf99cc7389cab90636be851c5bae91acd5a53d118dbee23209", "164c1e7daddc282008cc5c021dd8cad2810aa24ee3769d8c070d8f13445ac2bb0eba1bdd2d5d76ba114666d8a76295d0", "c1c9d96e98e49679cea5151b9e7472781d67a9b4b238c1e6ceb7f26a13086a8d66da7daa7bae6c6972b0fb82a5f55919", "cebb8a5ce346d9f863e7d49f75d9a7355e29a00de4395a462758ba9207da5f21430b56fd3121967f67346456f8e098a2", "bab3872921205406f427c7134243aaab9847d59eacca83350e20248274b3b69152ac9b9806b60e4b904a64a5609629f0", "2d7289973d5790bf4cdae4f599d4bfc25e95f3e93ca70d8952cd6b776de8ee90e1847562c81eb6dccefb0881678386c0", "7c084cc6daaa88a840ed45daf0a79036240fd47aaf09bb6849b2011cdb51dc4649f81e2010fe353659d0a58f41bc05a7", "a93db7c48851c42caa41356110d4d86c0f10ec135116d125c4e863e2cb8c5be5ef1a2449d287ae34e693ee1c82ecf4c5", "58252d79fef5548ee09e0d799f2511a8edbcb699e4bac17238c6173013e3619d35b6eba4654419f92503c7ae8d7fedfb", "08bc91a5a3ac2c04da04e89081ffe2cf9aa892bcb05ad3102846f9197b2455080796ddf23d474213cc05f90a425018a0", "547749c3697b5905e6b50bb28b7a4fd3bc57a76f9ef4d71f2b208b507e7a47856b4c08f6c198a26e681aefd3ef2ece42", "1275f3652a529cc4df987e54761d1d9352edcd3b1dd11e93dc61b35f999e51e68e1c86daf4433d4cb898b41ad595d690", "2034b2d2bcd7b7a825d0593b8d0c9f8eec2901293f67b9f9e8149dd15f550f044f9c52870078640f19218cdacc932d9f", "7026b9a0860d0f0e47f69fe4b1dd875f6504907fef746d0d0122c381bd60c14c50c8e4b72b1de7f6886f358d5c90c425", "4cb7293e29aabfd22b311648252185b216ddfd007eebdfaa35ce0ad619256ba964f18599915fc03d2510631855657210", "da5c04138597a290aa1d701ef7a51c67af8ac8f566ded7a04c0eb482ff9ecf4f3259d5e9ea2154ae437bb52b7f2cea69", "329b5c4111690c7347d77441f8e6f4f9de24486ae6d9de9f2b41764fda9c832c8699ddb92d9cfc9365e3b45759889b72", "d8026b69df2bd5bf57d2498713f6467bd207616f796004b0a4ada981acc98f44c9b0bef8474187444ae1fabb920dfea4", "85ba6123199b7162fc64128e8539f263a1350878bcace361a5d65f61e8ca54f4da1526bd66529f8624065625c70003f0", "6b3a0e44801b35b4ca036110a31a868a45980c085c23241750c98f2a20d01a07e820618689b58615458b5b35c1843c0a", "b2465d1ab2d446cb981e19593c42c1923106e1d2037780b6f488cdc322ae4d46d6c44a713bde0927455c5338808458fb", "5eda901939086eca0e16ce43354f42e6baa1c1c5afdc7a0a9e42e27e87d7aad66013d01767692a90f785190571316000", "e8501e3d1e364577a7798b31b061b21166", ), "b87670516460a6ea2cc450374b6c1cb6", concat!( "3a12b43c773f49a7d7ac4c1ca469c72aa64c6a0227bba8bb7b86e48cddc9346d1f7a9cb1a56d6bd45a9d4a60c9ff7e17", "cea21cf9455e259bcf71ef6722f3b467ff8ad4aebae3b1daf6b37398ad2e3ef37d218869665da41cdad5080b0d994216", "8436b9835cb7b66578da1895fe57f751cc26591572716a55fb7532e276d3d81a8bf998aa19c636016829ce7f76a07d20", "64759ba59befdfea72d629356611f275ccc232cf61e55219f30c89f0326d17b0fb47779b2e4d29b2d7666772c67cc146", "e51b7d8dd2194215bf4d15d7a920183624d2e6711b8e9a5f6b5cba91da1e1553a1c3316501539d4b62a61902de7c15b1", "184ab6565ca39f69f8e2353f085456f161ed7eb0b69ad388495edef9cba76f1414ba5ce3174ad600c37931d5cb23597d", "8cb2ff7805511a73afd72ce18c9232e65119c4513397996f99564e18d8e139dbde92186c2671da8a5d90aca1166e7d3d", "a96c38500a65f747b4e7fcfedbbbff9d2dea542846f3929433126a435d36a19db60062da920723acf1e05ac204b8b984", "5600415db11a4a589c0b90a667d4fc2d61e543b22b57061dbd7f1a44cd8fcf31d4189df1213abd9caf7151ef6388f2e0", "ddf7f286553c83e51d5681879148195bf7cdc4c29c8eddf7e77e43f43694313bf99786fe6acb8fa765e3f94d48addb1c", "bfef1e195dcb4b91c5535a74187a511aba0ad7351dee172ad04bb07dc8be7df61942b6bcba525cc62254c828a80d7f2e", "e85bacfb96528f62bebad7e84b1b86eba3919acfdde8ad34df6717fa314ed75a3de7a350b56652498df96bb36a4c10b0", "f08b0a9f242d58d6afae60a42dca99aa2c93384110ad6211eae4f28739ed17f90438fb75e638fe4db76794f62f767f96", "ae508d4d32da092e39063b8475863f11d3eb6a66b6935396692ed1ecdc857f4c6c7455db83b5675a5abe36dfb231ba46", "612f6319b7a91caa2b062a99d2dad7b2296b8ba0508a0eddd24b73e841f1802edc2b6b203673bb2b3c65aa66e13875dc", "4bc7ee7130490efa7989b93df07bfdad784668907c08c0a32c59df9b4bb8452e7e449ab49262d1266369b6e028a9e22c", "69942903d719b5cf9b5c27ff20cfeadd9b2f1b86c7cae2cf36a328505dacd22500373535f7e8955b53857f93194c8a4c", "2f0cc2a80f865302c2a829f14785e0f151e237c693fcf769c320c0d0ed5dcd61daa32ccc676f28acb56f06214c896693", "f32092b56f07c6b35a99a66f3376c9667354c84dad8839d83dc1c28995b7b8f43759e595b57469713cc666bd7272ac52", "2a7afdaa7f24261da1e6a799940b6dad6765f94b65d738d1aa705c62c7c4559db60d15ed25d2e34b17a1767c0f8fbf81", "a7af9ff8199c4adff757e690e4f86659d9681c7502d2a191e807d9555f12aa5d9746c8dfc8ba6c42322fd6f4eb75da0e", "aa0febbe4749766cffa056552ae6aef1126bf1617834bc47f1d187ca64b214aa5da1fad1d147c52b851c16a3e6cc4115", "d755404cc813fcc795342d60c7f0286fd67b6235edbb8d9c410a9befba8f7a2a2a6cddf2bd41bdce639884e3ac250172", "c593d9643139db4f3c3748b78d209bcf61b39d9bc6d02c176b646f8fb8a6cb0964104218fcba9ff16da9d4549f57a2bb", "f2ec383361a3fb501da93451ba446b9c31fb86d7ef4d96af3732bd4f1f7471624593a49fd08e6d37854bd4d62a9506da", "63866634a8982ec8811618d2d04f2b5084aeefd2f9ffefc99265535960f33bfebad326107646c3a3ce14d69e568d6f53", "56708a26cdf68ef885278b0ca2f5105fb459d800000000000000000000000000", ), );