fips204 0.5.0

FIPS 204: Module-Lattice-Based Digital Signature Standard
Documentation
use fips204::traits::{KeyGen, Signer, Verifier};

#[cfg(feature = "ml-dsa-44")]
use fips204::ml_dsa_44;

#[cfg(feature = "ml-dsa-44")]
use fips204::traits::SerDes;

#[cfg(all(feature = "ml-dsa-44", feature = "default-rng"))]
use rand_core::RngCore;

use rand_chacha::rand_core::SeedableRng;

// cargo flamegraph --test integration

// $ cargo test --package fips204 --test integration forever -- --ignored --nocapture
#[cfg(all(feature = "ml-dsa-44", feature = "default-rng"))]
#[ignore]
#[test]
fn forever() {
    let mut msg = [0u8; 32];
    let mut i = 0u64;
    let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
    let mut flip = [0u8; 5];
    loop {
        rng.fill_bytes(&mut msg);
        let (pk, sk) = ml_dsa_44::KG::try_keygen_with_rng(&mut rng).unwrap();
        let sig = sk.try_sign(&msg, &[0]).unwrap();
        let ver = pk.verify(&msg, &sig, &[0]);
        assert!(ver);
        // now, confirm that we do not accept an invalid signature
        rng.fill_bytes(&mut flip);
        let index = u32::from_le_bytes(flip[0..4].try_into().unwrap()); // index of byte to flip
        let mut sig2 = core::array::from_fn(|i| sig[i]);
        sig2[index as usize % (sig2.len() - 2)] ^= if flip[4] != 0 { flip[4] } else { 0x55 }; // investigate sig[last]
        let ver = pk.verify(&msg, &sig2, &[0]);
        if ver {
            eprintln!("Msg is      {}\n", hex::encode(msg));
            eprintln!("Index is {}\nflip[4] is{}\n", index, flip[4]);
            eprintln!("sk is       {}\n", hex::encode(sk.into_bytes()));
            eprintln!("pk is       {}\n", hex::encode(pk.into_bytes()));
            eprintln!("good sig is {}\n", hex::encode(sig));
            eprintln!("bad sig is  {}\n", hex::encode(sig2));
            assert!(!ver); // fail and stop if 'verified'
        }
        if i % 10000 == 0 {
            println!("So far i: {}", i)
        };
        i += 1;
    }
}

// This test originally demonstrated in **draft** FIPS 204 that two different signatures can be
// verified for the same pk/sig. However, it now demonstrates for **released** FIPS 204 that this
// problem is now fixed.
// Spec error, see https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/TQo-qFbBO1A/m/YcYKjMblAAAJ
// Ref https://github.com/pq-crystals/dilithium/blob/master/ref/packing.c
#[cfg(feature = "ml-dsa-44")]
#[test]
fn bad_sig() {
    let msg: [u8; 32] = <[u8; 32]>::try_from(
        hex::decode("2e77de619a8963d1ec5ff9e7db269e3ed9076a35badf49960571c8a98055d8eb").unwrap(),
    )
    .unwrap();
    let sk = ml_dsa_44::PrivateKey::try_from_bytes(<[u8; 2560]>::try_from(hex::decode("6ef73b20d211607012d7697a8277141e905239801f7bd539cbb8ea8e3d9146a515bbd2e28754acdf3ca5a9a17feaa3c0b336a365aa3e02f02c438845823b6be5d63f8b5e3deec3a297b3906f3b5f094da7bb165acaa37ab4dbdb2a1f63198910ee9212a21feaa334a02b3875a03fd1b78ed68a03595303d0c3e8b9a7808512af0c037193884812396153401289b410102548db1065d2c66959b8301411910c2522c3b44908250940c285a1c004d2360522c100cc12299848412105881a49700449302202104c822c23a96820a185dc006a0bc68c21354cd8801020416c8316660b310ae2b66c121024d2c6718b246401c60c8b266401895110996d22a00d042920202069518404ca2682d9128c1b026084c85158b051831086123328484622ca841184264e21c6600a070d58306419016d21380c1b266d99b4855c0651d0187142286e24a4842182459aa80c50b685a310615ba001c096008ac071c0a04050a200124246513030d4384ac9286110265023a240502682c0900902114602a92889b210db324190342813c78dd2204d19a52521b97188902123362212178d6414690033481ac481d294405a300dc914650cb129c1422d43c4615396299c302109c26c09426021968920b490219485da3681c3b80943142149086543848d93088c00954413076a40304ada806580343223968161c644e0285120058892048522158858068a40241042b86508166c89c884a01430ca9051d04289449688e1b061181000503282484648e1280e8a12491a3845a23880e238529c048dd3362112060114a71110094519a72c0128681ba92d218625a41809c4b62493105158126150c86d9306425cb685db982819306e23b10da42204d1b800da4866ca124489460ec8144963462d912864990686093810134000128865144628042429dac844a320284c9691e204224290114aa664c0482d0129500aa96c60a2044a124251c20402276d43b211000680e44012cb268a03c30851042249326991308e61064a0c346621900450b84119430609362e01344c1a386d0b898c11a2600208091b3128193811db1032d1840d4b4286d4b271dc066c00326008412888981109b180ca206824016510324a21936589b26d82a8440c893144b610128321a31285d2422a89321018442804a82959842941c201094782d0a02562226ccb18245208121a130008a040a10602e4a66514134094008a8c360ec9a089610640210241d3028013316dd206029b51781ac3a5960cb8833902e9f8b9e3ee16565ea0f7ff99bf78e1615a6c13e255c493ff1e0f4470ffb3726cab9209301832c2f499c158cd5bf40ddcd3c9ce7c659e660ee05740b169199ff1c866f7019ab53379481a8d3fe35356600e650de1dbfd9a42a1362af0b45d0d483fe2f427f303ba6296d05dd779babfb8d00d3acb24a2de06382cfad42b58fc1ff641d37cd2a43a3eace737e3e84732ee0d1109482d2cc4b22f38ca7f32ee16828ad732523edb39f0446ac568cbf166e9b31147dbd5db72f0b8c0e4d2a81606e9857c81bde135c431bcf489c8342d8c586493c52cb98a845f765dee285a1c48fb610312ebdb731464cca484e733b3cc333c208d9538416ea9dafe8d997016e3a724bfd6b00680e541613f5d3efc23b7e94a7ce71b1918ee78cb0d856c804b107ecc55541b2696958de0d06e030a6002eaff42b0e8ab7b64fd6592e42c582480ec539e286d3298e429f33e40fc8924ed70140a931f60afa9f81084578ccda3f6edf568f48cc8d6ef01d8875f9530793fd8bd099be129c14abdd7d5930a16aa317a4d40d2c22b6ff4fbb4e6d261206d4333c321911df41892109d412c5aed2299e4688113000eaab6a86e07c5979a251f44dbd2d45dc313adcc632d5f59da19dba7c4bcd17b40f7f96bfc49c6959155400d64fa53c2268c1a6b1ee5f630e9146e75e2ce1045af964127b2d29ba232cbffb7067f10b18880ce635e3d0e4c6f27430a34875a206623a08761d48519c53d9d68464f4687322020fa06b5d63d87e97a556271b1af1d7e7ae2db32d80bcf5fd09d9c283e3d28e29602dacb99ae56db9f7bc4b3b7247328d4b70497470c58e8b6f9154c2e87b2d92800bd17ea2b71c5239d55aa15d0a6e6efb67890ed272ebf69edcba8574fd788f57fe62141ea584219ac3cc926121f7fcbe46589f7e3f76c02f499b2c271ec3af1e43470243c52cdaac42e123900a832b175dda043295ff13af0239da5c724d30442a17d89ef07f8eaf9c16e6506f63eaf0995a9e103635e6052126ecb2774bc745619396e0eecb9cc00a4b35b094316653fa86995724350b90440e077c1523acdf980c2b29d4e54cbca6948c065e36eb08517be32a0f591e0d4887aba99097d75a97b2e14686b9e725752b75a7c312af0a3dff4222dee6ce015b56799829dcbc43b1e12ec97b354515558b22066afd8e1a6316f75598cc9f347eb120fc47cec2d750c411eddfd0df24fa1fe5e05e65eacab1536455540c5353a6fccb4ae6f86e3ca967d181942e8213684a8aac9d528de027fd984e7ded3a84458b4270abc847be85f9a3fd736f3c3cdb3b0f18cf0e793f331227989ff0ed0415e22feeee0b9800b29af84ff06e920b6e7fcb758f8733fe444c5a6f9b55c9e8b5eb0185da1bd08c25dfdd4e427a29eacfbc3a5da364759fd8f9c71afeff42e5dbe1432aefcffb727d55b67d7f61d9b8f52db5e9caf946d594d7cf2a1a55422a9e5700074219b0c3045ef572e60318ef3e7ae8dd7b821d07d7df54a563458c6dec05dfff6fcd462ae26caca3323bdc1929bcb4723c8a78d24a5b135167b4602b97902968bd5aa58fb798d8b92f31bbdf3ac3fab6f87926989acc9443fe5c7ea4f8f54bdad5f138eb0b75826837836e33915d95347bb69090cb774a26ef15cda81ca98cfe0cec911d85e70745877e563f17f43322b0b8995840491f197a58a3dc76f95695ff10d48286bc2755337eb1dad68b7371c52b8f491bc6e751a6347f8fbe429c65e33668467ae0dac3f4dd28ebeb5c5aecb1eb478e7a70a1a14ef1190d2843c0b992f5d8b0134d34016e153167e731e85280a36338b8df0477d46b747000d2b168be6dbcf2f138063cc15014357173c6e1674bae16be57ea7043aa6bae0c67d8caf801d7ce0055bb8c4bd52c5010be6dd6c28287c1f88391faeb19917af085f724c48c23b120d70bc9ae4ce292ef97f099cfae69eef756ca50e0dbf8486ac04c400b3188756c9d7ed3422fd3170dce3f66d6b90858159ae64f8b30b73fad255d0214f550b337306f02b16e96c18d1426e31bbc46180893908b0d3773c0699eb60fcf236b480fb665c573e0abd59e113e3ba295a94cf95879aa8373af97ae9a90298c218b95807a8e799b194b68aed731146bab58fa5494b42ef9acb7181e82bc3225b1f00dd79ee40897402cbeac266954a0dfa24406c686a922e65defc7e0f131e58c892df216c6b96a088199984bb4410f9ef1eef8c1cc2c05501072c2a674ff8fb7bffdfd0ccaeb66f77d897e3c5b1adfe5f79ec5e94c12ef097da5c4393d165e84a9b69f53661670bde2663d7b99d3713fbfef511710141798837dab0d153fc").unwrap()).unwrap()).unwrap();
    let pk = sk.get_public_key();
    let good_sig =<[u8; 2420]>::try_from(hex::decode("16dc0f699d45dd1975cf3b83f20be47dbae479795f6745edea17e5638467ff339b571b0a457e3afc2a6b088b0254191edb93d226fe96cad277587b8033cca6e36607ec99666a80259bc3e76e76006ee25b08e8b3b59a773aa6e7358e9990d01b40b0e0640a304e8746439157a2e7167ba972d4e1772025afd20d3ef76fc000af63885df718aafd943638355adb22575d554856042cbda36eb3d84ed8ef5341bd8534e84d2582838d2aea84b844a7243a702eb21d4b48e90b26ecbfdef1eb80f501f502255c178c195df34e6436f4d8f6d399a076495b5e690e239bbf22e3a40486ca63ddf03ff1ee25ba99b719b92f0d5b8b2d3e72473e267ab3ecdf54d9ca94e60979b849d66bf7e0c521d89c8f3f712333efebc8e1c83a035a5332ab94f4f77025635413d79a8f48eb6d42ebef58bcbbc6e73280ce30b499abba011b84a2230375fe1438c278dd35a0d335cb346f9adc3c7ff8fd487e049f081eb7d213b83d5eadfd805055bfe92857007792e2dd9137b3ff335ca8102ba83cc946e7ace92bdeba216b6a15eca310c04089ba8abd5b901959288d95349b290d65ccc27d921fb2ce8f9671ae4f9186e55cfbe53f910bc5cfc6e01d8b74003d873ad8f016f3e394b954325ce1bb2fd549237d7d48b1f91f1de4e7869d2c180dce0573b98282923bb898fe5fa71b5c563067200eb12870219cbea732987f8b98bfae6a52df3ff8bbd4a45ab3fc846a59a4d98832eb0cb9a4ab4e021765cf39dbd76d7ac84b0f2986febbf5c0a5f5d77bdf750e1a01c9502687e991bfc4537f9343471a288d319100ade8f8f13e3455fb804824ab57088b3a8a70187d84eeb3712a17e607c1abc0c3221936ea498e80d38ce02b9d2663c94f3a4c5a008530692acdbca66330b16a11a5aa361347779cc0a5dd54eac2ac7bfc37007453777eaf4f37a383d04473e1c8a28e7ba9770b4efdc257c3ca7471593bfe13c6e2b8b22453f95a3e5fb230e3743ca3604b5a0ab2378ac694902624c648221ce2ec31e5c75a0e2d7fa3b5c65b150705411a1ca7b90a6ac79953093f919252f7db8ac008a2465f0b91c617a3b322893f50917806bb9f4e84e7fe537e6669e2c09daa8ffe35015dacdeb4a33adcdcdc535121d69ba5ccfaf5d055922dc336755250aa4b8a03d5a213a976675c1dc3b64799aa1b2b9e778964913a4713c8022f18d3bffb37d81de510400498dd87dd15663cf1e6115dee8db28df9ab2a599db09016433a9390dac0c6c02acbce1d26a9ff3287aef6c41eb2a5229d022f2ecef19edbe2cfa404acc6bc941d7db35b30d92258387bf0e6f4cb68802eeb21988cdc04ab63a561c609ebe69bf44fad5674dea9d66020b22e6d46772887548d1377ee7c8f774eb54ccd4c7b0861d620c7bc038eb8aa97693745c204ff4f4ec99a1d9354fe970d01bd3c08d344744e4ae61f80b3720db53f285f1c27489292b3fc6529a591f17202a4904273cf61e5060370063dd6e48b87fd8a340d81c1ff133fb8abbe25b7d54894e8ae1ba9af0815d4834c2a8be7147996cda460a124e42889bfb65732d1b7c50b04a639247bfa27880151ff009179aadf67642ec0a876488a72e7cbe716792dd6308ff1c9d5ac695522aba8736b91b8320690e2a1c306c870417ad1bbb7e3ebdc31fa235d495f15aeef418a0d2cf3b56391fba88d172693a85212d444d6829b61bff714bfcf77647c0ef14ed19364d0fd929da71c0c77aa4160fde68c84cc2426f3f8e2f331db1a21e8db8a03d0462847d7d0250c085a9c510c21c0afeb88bd33fbc99a4909ec50c29c0d6110d09819d2af4cc99542cf3b6fc14a8e9ab64a2416d07736277eca93337e903aa31ffc001e266b7130a5cc0d4316560821e0a2a82d6e81e49e3fbdb45aba03da2a49392caff67944a3f82831a35e216b6560e19d2989fefd79d8ee057d7596746cb4dd735df85816b703c925a1e47429f389f10ff273fab53ea93b8431317d206b744caacb90fa47c041b8ddbda68533a79c7e02dce300bc07fee1f80bc0094b4c084b93dc7c9ea2e68e0b2ee7ed03ef9db3a6f6488eb6754a13d11dbf89fdd5317951159cb0d715e8ba8443165b2d9d6f483143f1266372302de40c2f2917ef5eabc6c30a59bdf3fdf6354fa0c038f305da39e591867cb833dc6cb74444c2fe7c8866e6766f69aebfc963aa62ead95549d2f8dd4ed939362f93002f1e0230a4d7b36e9e86a6db3aec1a5fb2d52504a828aee37b765a4013bfbc84f59f61629279e07e51b15ecbfff803d65463df3e81939de43b3ed04259a9a39755de342bd4d5ef4bc7715979a1d9c61fdc794e689a8fcc050d489c6f111ba3de9d672123e7b905f88efa586d86c57180ad81595fc5276036bdeb4a355f7cd0ee66284a5dbac5d8886451a022ae5d87002c2c0d59bb5d37741d7a82a99f8108941d50e84de94e22bb1a200afd2b994a720735c07db7883e1245b5d44ab5930b88159c5fa63a062881fd73465cb31c5189c47fd151ce8affb21c35a30d2aa22b48d50220a58014fa2ed0d9fc534571a944360bf4c0339bf4b46d26e02653f99d3b19e6908b1ac9ba2cdb968da9076cbeefd83aec41128f403f3874c6ae83c69747227753a975edf959f9d71b86067fc35d7f4c69d4a62620c92367c17e3c6f9db58f4b54f65c455f58e09d5fef86b5ed301764b52e521620b56351a885bf5f90f0776c83618bfcaa027770e3e2c1d70bde12193ed40f33ce3cc57e7572c544ddecaeb066326daa0e2d85164a5a08eab906b4a08c940da4e8329d47b5782a4ce3ee0718fe714965e360f547b7d1c66a19a01772696d6c38242402d85f3351f7d6e4ef5698155ac80285176ea1f52b4d983b16baa764e00da0bff745a83634426010a9982000bfee932513cb86414efd0cdd5a659efd0a3274e9a2fda7160909eb046681b551ba038cae8e626e4e4aee138e75684d62e4758833a8d1d3f2b718a46dc4deec123268bf6615d05b823375c2b9059bdd74ad6d4962da6c71368687e06f69c48b331a6bf0b4f53f79389deb40857a48336dd5962b19a9408d201e3ae12b8171730f4ab425bd231a787f653e46996b5ac460a640361b92db107b2178e5763d79d78e4c33863a1b989cd3328f16bd3cc82b4239bac1440a42fe961243dfa790937379b7b082ca0f48a3923e7bb5514eb5f6531f016bb699a7a2e5d2254f767a45a2949820953ccb2352f7f197caed236f066d95d156acac483a6a4c9ceb49e14d49f2c0deb8905f52e9a9151662f5db0add1f868fab2d896b66b2f53f26eea0231c2641454a666b898b8ca2a3aad6e3376f72898e97d6dcdddfe103193f585e61667692b0bdbec2d1d4f0f70a17243e41507e8d989aadbecadfeaff0000000000000000000000000000000000000000000f1a2b3b").unwrap()).unwrap();
    // Mutate the good sig by incrementing the bit-packed hint index's final maxval:
    let mut bad_sig: [u8; 2420] = good_sig;
    bad_sig[2419] += 1;

    assert!(pk.verify(&msg, &good_sig, &[]));
    assert!(!pk.verify(&msg, &bad_sig, &[]));
}


#[cfg(feature = "ml-dsa-44")]
#[test]
fn test_44_no_verif() {
    let msg = [0u8, 1, 2, 3, 4, 5, 6, 7];
    let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
    let (pk, sk) = ml_dsa_44::KG::try_keygen_with_rng(&mut rng).unwrap();
    let sig = sk.try_sign_with_rng(&mut rng, &msg, &[0]).unwrap();

    // Bad messages
    for i in 0..8 {
        let mut msg_bad = msg;
        msg_bad[i] ^= 0x08;
        let ver = pk.verify(&msg_bad, &sig, &[0]);
        assert!(!ver)
    }

    // Bad secret key  (intriguing, byte 40 is 'k' which is allowed variance)
    for i in 0..8 {
        let mut sk_bad = sk.clone().into_bytes();
        sk_bad[70 + i * 10] ^= 0x08;
        let sk_bad = ml_dsa_44::PrivateKey::try_from_bytes(sk_bad).unwrap();
        let sig = sk_bad.try_sign_with_rng(&mut rng, &msg, &[0]).unwrap();
        let ver = pk.verify(&msg, &sig, &[0]);
        assert!(!ver)
    }

    // Bad public key
    for i in 0..8 {
        let mut pk_bad = pk.clone().into_bytes();
        pk_bad[i * 10] ^= 0x08;
        let pk_bad = ml_dsa_44::PublicKey::try_from_bytes(pk_bad).unwrap();
        let ver = pk_bad.verify(&msg, &sig, &[0]);
        assert!(!ver)
    }

    // Bad signature
    for i in 0..8 {
        let mut sig_bad = sig;
        sig_bad[i * 10] ^= 0x08;
        let ver = pk.verify(&msg, &sig_bad, &[0]);
        assert!(!ver)
    }
}