From cce105a7d00e0ea1426204856f725ba1b7a84748 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 11 Jun 2024 09:14:52 +0200 Subject: [PATCH 01/11] Test to illustrate a mont form inversion problem --- src/modular/monty_form/inv.rs | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index 80c7ee4d..bd1b7d81 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -145,4 +145,70 @@ mod tests { assert_eq!(res.retrieve(), U256::ONE); } + + /// Test to illustrate a potential problem with the Bernstein&Yang code to + /// invert numbers in Montgomery form. The specific parameters shown here + /// come from intermittent test failures of [homomorphic_mul] in the + /// Synedrion signining library (i.e. parameters are generated with an + /// unseeded CSPRNG). + /// + /// For convenience, after this test follows a commented out version of the + /// same that passes with crypto-bigint v0.5.5. + /// + /// Run the test with `cargo t inversion_v06_vs_v055`. + /// + /// [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 + #[test] + fn inversion_v06_vs_v055() { + use crate::{Limb, U2048}; + + let params = MontyParams { + modulus: Odd::new(U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49")).unwrap(), + one: U2048::from_be_hex("44753512AC1F101A05F4679445E9DF3EF50654B77FDCEDC25F44E75394CAD9FA06C09C529A017DC0A79CFFEC75CC0904EF8A619B07A07DCFD37A90BF127049B76A9B345BF3F4AB5429F69415DFE8D3CB2F7F988562CEB74405AAA569D93B4FEE0186D708791DA62E5AB52EF50C1EE9495B88C7227993D59590D3BE16C2D66F55EEB23E5B36BAF63812987577705448CB7BC5C083ABC14C000DA5C4C12ECF435C79D26BFB096324CAA225E4C17C4110601FBC80E10BA3543C1A8FEF4C77332F39F531C73D51508AD1DB5D8ADB13618CC96F31B956CD13FB682010AD6F677E143134DF6397103C92F6D72E55D87B2CD05AAC85F8F5DC6CE36E0A396D8272B0036E"), + r2: U2048::from_be_hex("3ECCFA8643CBBC3E03764FE8F9FFFE05DB6018B71A743939857318AE945CE4BBA0306A61719CA20FCE1910BD2A97BB34358334E776F7B12CB6289844D690D3B2DD95B84BD461BAB76C1544177307F2AB4FD55A14A9680CD1CBD777D24E534117CC2902BEB690BA620A7EDA562D9BF424512F36CAE3C4F77B5F89044A2725E44ACB1140CF2E7E3CADF9454685A2D9713EEE9A96D00EAC1089828DDE989090C255A3A59BB5BFC13713F987FA6593ADF0A1BEB15FB292D113CF7D12214EB971ABA43C1F876398D647D2E16F8AE0833B08D435DE7BACE60AB3CE021A8DD76CD2D0499AA69999D7F903DB04699F914106A1B704908D0F8380F4457248320D210AD120"), + r3: U2048::from_be_hex("1046AA244A33A93BD07B563B4D087C8992640A2E008BF50A9C6F206F532A11FE591BD54971C105FEB67EE6AD19B00DF67505C8701526707A46834B43B7785E10CBE653EA83F7574C3703D4D5F3BEF68AF1F743008DD87DFBB3FA63D4DED4FD51EE1401FC160134755E399C7D81C5406D93E1CCF0A8AE42DD9E1E7ACF2EBEFA47B5CEA6D93DB4F63737FCC8294FD0D09445D3189C8A9CBC5FC3B58B65C2A41CEE9D3BEFB17FFEF1A595C8488765A3C5606EEE6EC983F177A0917EA711F235B75FC041D5D4462E59470D974D5FFD2F62C9343D7063F6093A58699B80AD3C59531673E8A8A6E725CBF5166ACC893E94F76A249AD90CFC6ACC9963B0AC2CA3419557"), + mod_neg_inv: Limb(0x1A5316810A17AC07) + }; + + let a_number = MontyForm { + montgomery_form: U2048::from_be_hex("38A5363CDDEFB1E9EE7CF941A8C90F8AC9ADBE88B829D4E82837EA944627B06C74099EB61442EC90D425536E5DC6B9244FF5115083D321E8B961243169FCB667E74667054EED5A839E2D1D5D16A1D24C202F3D9154874FB23103F0DC2A19AEE4DFDB97FB12325854B829E9BA191542A94B7FB10EDD2CCD50A5F03CECC16AA5FCCAB51F54651B8E9AAD2E3E3EE7BBF4EB613FF19AD0D7BB8FBA8854962A92BB7DA34511A1E5EB0CE02686B5F8A3E6B5CE148534B3D63A666429B80CB49D49416BD93D7BC99B9890FF908035349BBDDDEE047B472AE61CDE3357D8381A3E16FED1981C286CC54918DEE6ADE4EBB136C0F2E7F5AEF4608D80486C317025C34EE711"), + params + }; + let inverted = a_number.invert(); + + // This is what v0.5.5 outputs using the same inputs. + let expected_inv = MontyForm { + montgomery_form: U2048::from_be_hex("2CA53822D62E244A0EA0545549CDE8118F50FC38C18B462E0B408ABF14D7082041178C7BE85BB28657F036BB9A8B45E6AD0FF4791061E6D79079F7E070729032A3DA57512EB130C1F83E11139669C08501643E985CF337697F20EE6F4611A551140E0E48C9EC68274E7F485D8FD766E1D697B946A60CAA315F5439241529A0042765322ADE388D1CC7C93D93AA1F5D7551AE115C05E5E9CBAD2C42C0F10B1294C7D08CD566A413AD1C0E161F1ACA21C6E300E0BA17B9FD2440384D0D674302C01F2C98BAA54A2E9532AFBF286B4ADBF73EFD64608CAEB886C3B21A53C0B199CBA489805BB92781D3BD2203C51D34198A3DBF364931EE948362B176DC7E231659"), + params + }; + assert!(bool::from(inverted.is_some())); + assert_eq!(inverted.unwrap(), expected_inv); + } + + // Version of the above test using v0.5.5: + // #[test] + // fn inverson_v0.6v_v0.5.5() { + // use crate::U2048; + + // let params = DynResidueParams { + // modulus: U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"), + // r: U2048::from_be_hex("44753512AC1F101A05F4679445E9DF3EF50654B77FDCEDC25F44E75394CAD9FA06C09C529A017DC0A79CFFEC75CC0904EF8A619B07A07DCFD37A90BF127049B76A9B345BF3F4AB5429F69415DFE8D3CB2F7F988562CEB74405AAA569D93B4FEE0186D708791DA62E5AB52EF50C1EE9495B88C7227993D59590D3BE16C2D66F55EEB23E5B36BAF63812987577705448CB7BC5C083ABC14C000DA5C4C12ECF435C79D26BFB096324CAA225E4C17C4110601FBC80E10BA3543C1A8FEF4C77332F39F531C73D51508AD1DB5D8ADB13618CC96F31B956CD13FB682010AD6F677E143134DF6397103C92F6D72E55D87B2CD05AAC85F8F5DC6CE36E0A396D8272B0036E"), + // r2: U2048::from_be_hex("3ECCFA8643CBBC3E03764FE8F9FFFE05DB6018B71A743939857318AE945CE4BBA0306A61719CA20FCE1910BD2A97BB34358334E776F7B12CB6289844D690D3B2DD95B84BD461BAB76C1544177307F2AB4FD55A14A9680CD1CBD777D24E534117CC2902BEB690BA620A7EDA562D9BF424512F36CAE3C4F77B5F89044A2725E44ACB1140CF2E7E3CADF9454685A2D9713EEE9A96D00EAC1089828DDE989090C255A3A59BB5BFC13713F987FA6593ADF0A1BEB15FB292D113CF7D12214EB971ABA43C1F876398D647D2E16F8AE0833B08D435DE7BACE60AB3CE021A8DD76CD2D0499AA69999D7F903DB04699F914106A1B704908D0F8380F4457248320D210AD120"), + // r3: U2048::from_be_hex("1046AA244A33A93BD07B563B4D087C8992640A2E008BF50A9C6F206F532A11FE591BD54971C105FEB67EE6AD19B00DF67505C8701526707A46834B43B7785E10CBE653EA83F7574C3703D4D5F3BEF68AF1F743008DD87DFBB3FA63D4DED4FD51EE1401FC160134755E399C7D81C5406D93E1CCF0A8AE42DD9E1E7ACF2EBEFA47B5CEA6D93DB4F63737FCC8294FD0D09445D3189C8A9CBC5FC3B58B65C2A41CEE9D3BEFB17FFEF1A595C8488765A3C5606EEE6EC983F177A0917EA711F235B75FC041D5D4462E59470D974D5FFD2F62C9343D7063F6093A58699B80AD3C59531673E8A8A6E725CBF5166ACC893E94F76A249AD90CFC6ACC9963B0AC2CA3419557"), + // mod_neg_inv: Limb(0x1A5316810A17AC07) + // }; + + // let a_number = DynResidue { + // montgomery_form: U2048::from_be_hex("38A5363CDDEFB1E9EE7CF941A8C90F8AC9ADBE88B829D4E82837EA944627B06C74099EB61442EC90D425536E5DC6B9244FF5115083D321E8B961243169FCB667E74667054EED5A839E2D1D5D16A1D24C202F3D9154874FB23103F0DC2A19AEE4DFDB97FB12325854B829E9BA191542A94B7FB10EDD2CCD50A5F03CECC16AA5FCCAB51F54651B8E9AAD2E3E3EE7BBF4EB613FF19AD0D7BB8FBA8854962A92BB7DA34511A1E5EB0CE02686B5F8A3E6B5CE148534B3D63A666429B80CB49D49416BD93D7BC99B9890FF908035349BBDDDEE047B472AE61CDE3357D8381A3E16FED1981C286CC54918DEE6ADE4EBB136C0F2E7F5AEF4608D80486C317025C34EE711"), + // residue_params: params.clone() + // }; + // let (inverted, choice) = a_number.invert(); + + // let expected_inv = DynResidue { + // montgomery_form: U2048::from_be_hex("2CA53822D62E244A0EA0545549CDE8118F50FC38C18B462E0B408ABF14D7082041178C7BE85BB28657F036BB9A8B45E6AD0FF4791061E6D79079F7E070729032A3DA57512EB130C1F83E11139669C08501643E985CF337697F20EE6F4611A551140E0E48C9EC68274E7F485D8FD766E1D697B946A60CAA315F5439241529A0042765322ADE388D1CC7C93D93AA1F5D7551AE115C05E5E9CBAD2C42C0F10B1294C7D08CD566A413AD1C0E161F1ACA21C6E300E0BA17B9FD2440384D0D674302C01F2C98BAA54A2E9532AFBF286B4ADBF73EFD64608CAEB886C3B21A53C0B199CBA489805BB92781D3BD2203C51D34198A3DBF364931EE948362B176DC7E231659"), + // residue_params: params, + // }; + // assert!(choice.is_true_vartime()); + // assert_eq!(inverted, expected_inv); + // } } From 723aa4ce3c7d9d1141e3654bf8d5dd40b789669b Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 11 Jun 2024 09:23:08 +0200 Subject: [PATCH 02/11] Update descr --- src/modular/monty_form/inv.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index bd1b7d81..89dfe0ae 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -152,10 +152,20 @@ mod tests { /// Synedrion signining library (i.e. parameters are generated with an /// unseeded CSPRNG). /// + /// Run the test with `cargo t inversion_v06_vs_v055`. + /// /// For convenience, after this test follows a commented out version of the /// same that passes with crypto-bigint v0.5.5. /// - /// Run the test with `cargo t inversion_v06_vs_v055`. + /// To compare with v0.5.5 do the following: + /// + /// 1. Checkout the old code: `git checkout v0.5.5` + /// 1. Run `cargo update -p proc-macro2` to work around a recent compiler + /// incompatibility + /// 1. Paste the commented out v0.5 test in the test module of + /// `runtime_mod.rs` + /// 1. Run the test with `cargo t inversion_v06v_v055` + /// 1. Notice it passes /// /// [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 #[test] @@ -187,7 +197,7 @@ mod tests { // Version of the above test using v0.5.5: // #[test] - // fn inverson_v0.6v_v0.5.5() { + // fn inversion_v06_vs_v055() { // use crate::U2048; // let params = DynResidueParams { From 2f050b7b39ec86b506e40678a195cbd72b82e49c Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 13 Jun 2024 08:24:05 +0200 Subject: [PATCH 03/11] Rewrite tests using public API --- src/modular/monty_form/inv.rs | 96 +++++++++++++++-------------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index 89dfe0ae..4874a42a 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -146,77 +146,61 @@ mod tests { assert_eq!(res.retrieve(), U256::ONE); } - /// Test to illustrate a potential problem with the Bernstein&Yang code to - /// invert numbers in Montgomery form. The specific parameters shown here - /// come from intermittent test failures of [homomorphic_mul] in the - /// Synedrion signining library (i.e. parameters are generated with an - /// unseeded CSPRNG). - /// - /// Run the test with `cargo t inversion_v06_vs_v055`. - /// - /// For convenience, after this test follows a commented out version of the - /// same that passes with crypto-bigint v0.5.5. - /// - /// To compare with v0.5.5 do the following: - /// - /// 1. Checkout the old code: `git checkout v0.5.5` - /// 1. Run `cargo update -p proc-macro2` to work around a recent compiler - /// incompatibility - /// 1. Paste the commented out v0.5 test in the test module of - /// `runtime_mod.rs` - /// 1. Run the test with `cargo t inversion_v06v_v055` - /// 1. Notice it passes - /// - /// [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 + // Test to illustrate a potential problem with the Bernstein&Yang code to + // invert numbers in Montgomery form. The specific parameters shown here + // come from intermittent test failures of [homomorphic_mul] in the + // Synedrion signining library (i.e. parameters are generated with an + // unseeded CSPRNG). + + // Run the test with `cargo t inversion_v06_vs_v055`. + + // For convenience, after this test follows a commented out version of the + // same that passes with crypto-bigint v0.5.5. + + // To compare with v0.5.5 do the following: + + // 1. Checkout the old code: `git checkout v0.5.5` + // 1. Run `cargo update -p proc-macro2` to work around a recent compiler + // incompatibility + // 1. Paste the commented out v0.5 test in the test module of + // `runtime_mod.rs` + // 1. Run the test with `cargo t inversion_v06v_v055` + // 1. Notice it passes + + // [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 #[test] fn inversion_v06_vs_v055() { - use crate::{Limb, U2048}; - - let params = MontyParams { - modulus: Odd::new(U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49")).unwrap(), - one: U2048::from_be_hex("44753512AC1F101A05F4679445E9DF3EF50654B77FDCEDC25F44E75394CAD9FA06C09C529A017DC0A79CFFEC75CC0904EF8A619B07A07DCFD37A90BF127049B76A9B345BF3F4AB5429F69415DFE8D3CB2F7F988562CEB74405AAA569D93B4FEE0186D708791DA62E5AB52EF50C1EE9495B88C7227993D59590D3BE16C2D66F55EEB23E5B36BAF63812987577705448CB7BC5C083ABC14C000DA5C4C12ECF435C79D26BFB096324CAA225E4C17C4110601FBC80E10BA3543C1A8FEF4C77332F39F531C73D51508AD1DB5D8ADB13618CC96F31B956CD13FB682010AD6F677E143134DF6397103C92F6D72E55D87B2CD05AAC85F8F5DC6CE36E0A396D8272B0036E"), - r2: U2048::from_be_hex("3ECCFA8643CBBC3E03764FE8F9FFFE05DB6018B71A743939857318AE945CE4BBA0306A61719CA20FCE1910BD2A97BB34358334E776F7B12CB6289844D690D3B2DD95B84BD461BAB76C1544177307F2AB4FD55A14A9680CD1CBD777D24E534117CC2902BEB690BA620A7EDA562D9BF424512F36CAE3C4F77B5F89044A2725E44ACB1140CF2E7E3CADF9454685A2D9713EEE9A96D00EAC1089828DDE989090C255A3A59BB5BFC13713F987FA6593ADF0A1BEB15FB292D113CF7D12214EB971ABA43C1F876398D647D2E16F8AE0833B08D435DE7BACE60AB3CE021A8DD76CD2D0499AA69999D7F903DB04699F914106A1B704908D0F8380F4457248320D210AD120"), - r3: U2048::from_be_hex("1046AA244A33A93BD07B563B4D087C8992640A2E008BF50A9C6F206F532A11FE591BD54971C105FEB67EE6AD19B00DF67505C8701526707A46834B43B7785E10CBE653EA83F7574C3703D4D5F3BEF68AF1F743008DD87DFBB3FA63D4DED4FD51EE1401FC160134755E399C7D81C5406D93E1CCF0A8AE42DD9E1E7ACF2EBEFA47B5CEA6D93DB4F63737FCC8294FD0D09445D3189C8A9CBC5FC3B58B65C2A41CEE9D3BEFB17FFEF1A595C8488765A3C5606EEE6EC983F177A0917EA711F235B75FC041D5D4462E59470D974D5FFD2F62C9343D7063F6093A58699B80AD3C59531673E8A8A6E725CBF5166ACC893E94F76A249AD90CFC6ACC9963B0AC2CA3419557"), - mod_neg_inv: Limb(0x1A5316810A17AC07) - }; + use crate::U2048; - let a_number = MontyForm { - montgomery_form: U2048::from_be_hex("38A5363CDDEFB1E9EE7CF941A8C90F8AC9ADBE88B829D4E82837EA944627B06C74099EB61442EC90D425536E5DC6B9244FF5115083D321E8B961243169FCB667E74667054EED5A839E2D1D5D16A1D24C202F3D9154874FB23103F0DC2A19AEE4DFDB97FB12325854B829E9BA191542A94B7FB10EDD2CCD50A5F03CECC16AA5FCCAB51F54651B8E9AAD2E3E3EE7BBF4EB613FF19AD0D7BB8FBA8854962A92BB7DA34511A1E5EB0CE02686B5F8A3E6B5CE148534B3D63A666429B80CB49D49416BD93D7BC99B9890FF908035349BBDDDEE047B472AE61CDE3357D8381A3E16FED1981C286CC54918DEE6ADE4EBB136C0F2E7F5AEF4608D80486C317025C34EE711"), - params - }; - let inverted = a_number.invert(); + let modulus = Odd::new(U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49")).unwrap(); + let params = MontyParams::new_vartime(modulus); + let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); + let mont = MontyForm::new(&int, params); + let inverted = mont.invert(); // This is what v0.5.5 outputs using the same inputs. - let expected_inv = MontyForm { - montgomery_form: U2048::from_be_hex("2CA53822D62E244A0EA0545549CDE8118F50FC38C18B462E0B408ABF14D7082041178C7BE85BB28657F036BB9A8B45E6AD0FF4791061E6D79079F7E070729032A3DA57512EB130C1F83E11139669C08501643E985CF337697F20EE6F4611A551140E0E48C9EC68274E7F485D8FD766E1D697B946A60CAA315F5439241529A0042765322ADE388D1CC7C93D93AA1F5D7551AE115C05E5E9CBAD2C42C0F10B1294C7D08CD566A413AD1C0E161F1ACA21C6E300E0BA17B9FD2440384D0D674302C01F2C98BAA54A2E9532AFBF286B4ADBF73EFD64608CAEB886C3B21A53C0B199CBA489805BB92781D3BD2203C51D34198A3DBF364931EE948362B176DC7E231659"), - params + let expected_inv = { + let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); + MontyForm::new(&int, params) }; assert!(bool::from(inverted.is_some())); assert_eq!(inverted.unwrap(), expected_inv); } - // Version of the above test using v0.5.5: + // // Version of the above test using v0.5.5: // #[test] // fn inversion_v06_vs_v055() { // use crate::U2048; + // let modulus = U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); + // let params = DynResidueParams::new(&modulus); + // let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); + // let mont = DynResidue::new(&int, params); - // let params = DynResidueParams { - // modulus: U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"), - // r: U2048::from_be_hex("44753512AC1F101A05F4679445E9DF3EF50654B77FDCEDC25F44E75394CAD9FA06C09C529A017DC0A79CFFEC75CC0904EF8A619B07A07DCFD37A90BF127049B76A9B345BF3F4AB5429F69415DFE8D3CB2F7F988562CEB74405AAA569D93B4FEE0186D708791DA62E5AB52EF50C1EE9495B88C7227993D59590D3BE16C2D66F55EEB23E5B36BAF63812987577705448CB7BC5C083ABC14C000DA5C4C12ECF435C79D26BFB096324CAA225E4C17C4110601FBC80E10BA3543C1A8FEF4C77332F39F531C73D51508AD1DB5D8ADB13618CC96F31B956CD13FB682010AD6F677E143134DF6397103C92F6D72E55D87B2CD05AAC85F8F5DC6CE36E0A396D8272B0036E"), - // r2: U2048::from_be_hex("3ECCFA8643CBBC3E03764FE8F9FFFE05DB6018B71A743939857318AE945CE4BBA0306A61719CA20FCE1910BD2A97BB34358334E776F7B12CB6289844D690D3B2DD95B84BD461BAB76C1544177307F2AB4FD55A14A9680CD1CBD777D24E534117CC2902BEB690BA620A7EDA562D9BF424512F36CAE3C4F77B5F89044A2725E44ACB1140CF2E7E3CADF9454685A2D9713EEE9A96D00EAC1089828DDE989090C255A3A59BB5BFC13713F987FA6593ADF0A1BEB15FB292D113CF7D12214EB971ABA43C1F876398D647D2E16F8AE0833B08D435DE7BACE60AB3CE021A8DD76CD2D0499AA69999D7F903DB04699F914106A1B704908D0F8380F4457248320D210AD120"), - // r3: U2048::from_be_hex("1046AA244A33A93BD07B563B4D087C8992640A2E008BF50A9C6F206F532A11FE591BD54971C105FEB67EE6AD19B00DF67505C8701526707A46834B43B7785E10CBE653EA83F7574C3703D4D5F3BEF68AF1F743008DD87DFBB3FA63D4DED4FD51EE1401FC160134755E399C7D81C5406D93E1CCF0A8AE42DD9E1E7ACF2EBEFA47B5CEA6D93DB4F63737FCC8294FD0D09445D3189C8A9CBC5FC3B58B65C2A41CEE9D3BEFB17FFEF1A595C8488765A3C5606EEE6EC983F177A0917EA711F235B75FC041D5D4462E59470D974D5FFD2F62C9343D7063F6093A58699B80AD3C59531673E8A8A6E725CBF5166ACC893E94F76A249AD90CFC6ACC9963B0AC2CA3419557"), - // mod_neg_inv: Limb(0x1A5316810A17AC07) - // }; - - // let a_number = DynResidue { - // montgomery_form: U2048::from_be_hex("38A5363CDDEFB1E9EE7CF941A8C90F8AC9ADBE88B829D4E82837EA944627B06C74099EB61442EC90D425536E5DC6B9244FF5115083D321E8B961243169FCB667E74667054EED5A839E2D1D5D16A1D24C202F3D9154874FB23103F0DC2A19AEE4DFDB97FB12325854B829E9BA191542A94B7FB10EDD2CCD50A5F03CECC16AA5FCCAB51F54651B8E9AAD2E3E3EE7BBF4EB613FF19AD0D7BB8FBA8854962A92BB7DA34511A1E5EB0CE02686B5F8A3E6B5CE148534B3D63A666429B80CB49D49416BD93D7BC99B9890FF908035349BBDDDEE047B472AE61CDE3357D8381A3E16FED1981C286CC54918DEE6ADE4EBB136C0F2E7F5AEF4608D80486C317025C34EE711"), - // residue_params: params.clone() - // }; - // let (inverted, choice) = a_number.invert(); + // let (inverted, choice) = mont.invert(); - // let expected_inv = DynResidue { - // montgomery_form: U2048::from_be_hex("2CA53822D62E244A0EA0545549CDE8118F50FC38C18B462E0B408ABF14D7082041178C7BE85BB28657F036BB9A8B45E6AD0FF4791061E6D79079F7E070729032A3DA57512EB130C1F83E11139669C08501643E985CF337697F20EE6F4611A551140E0E48C9EC68274E7F485D8FD766E1D697B946A60CAA315F5439241529A0042765322ADE388D1CC7C93D93AA1F5D7551AE115C05E5E9CBAD2C42C0F10B1294C7D08CD566A413AD1C0E161F1ACA21C6E300E0BA17B9FD2440384D0D674302C01F2C98BAA54A2E9532AFBF286B4ADBF73EFD64608CAEB886C3B21A53C0B199CBA489805BB92781D3BD2203C51D34198A3DBF364931EE948362B176DC7E231659"), - // residue_params: params, + // let expected_inv = { + // let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); + // DynResidue::new(&int, params) // }; // assert!(choice.is_true_vartime()); // assert_eq!(inverted, expected_inv); From 5063f506e6c2ab8fcc8bb1273c4d879ef9a284ce Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 14 Jun 2024 13:18:16 +0200 Subject: [PATCH 04/11] Add a test using `num-modular` --- src/modular/monty_form/inv.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index 4874a42a..edd6cb33 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -110,6 +110,11 @@ where #[cfg(test)] mod tests { + use hex_literal::hex; + use num_bigint::BigUint; + use num_modular::ModularUnaryOps; + use num_traits::FromBytes; + use super::{MontyForm, MontyParams}; use crate::{Invert, Inverter, Odd, PrecomputeInverter, U256}; @@ -187,6 +192,24 @@ mod tests { assert_eq!(inverted.unwrap(), expected_inv); } + /// This test use the same inputs as the `inversion_v06_vs_v055` test above + /// but using the `num-modular` code. That crate lacks support for big integer Montgomery form + /// so for this test the values are in normal form. + /// + /// This test passes and agrees with crypto-bigint v0.5.5. + #[test] + fn invert_with_num_modular() { + let modulus = BigUint::from_be_bytes(&hex!( + "5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49" + )); + + let int = BigUint::from_be_bytes(&hex!("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2")); + let inverted = int.clone().invm(&modulus).expect("inverse exists"); + + let expected_inv = BigUint::from_bytes_be(&hex!("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E")); + assert_eq!(inverted, expected_inv); + } + // // Version of the above test using v0.5.5: // #[test] // fn inversion_v06_vs_v055() { From 6c803e6d8231ddcc4e91149b00ea248435a087fd Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 18 Jun 2024 12:28:30 +0200 Subject: [PATCH 05/11] Add v0.5.5 as dev-dep Add tests for random uints and their inverses for both v06 and v05 --- Cargo.toml | 1 + src/modular/monty_form/inv.rs | 195 +++++++++++++++++++++++++++------- 2 files changed, 158 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c6522f8..1eb138d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ num-modular = { version = "0.6", features = ["num-bigint", "num-integer", "num-t proptest = "1" rand_core = { version = "0.6", features = ["std"] } rand_chacha = "0.3" +crypto_bigint_05 = {package = "crypto-bigint", version = "0.5.5"} [features] default = ["rand"] diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index edd6cb33..bf74c347 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -112,8 +112,8 @@ where mod tests { use hex_literal::hex; use num_bigint::BigUint; - use num_modular::ModularUnaryOps; - use num_traits::FromBytes; + use num_modular::{ModularCoreOps, ModularUnaryOps}; + use num_traits::{FromBytes, One}; use super::{MontyForm, MontyParams}; use crate::{Invert, Inverter, Odd, PrecomputeInverter, U256}; @@ -156,25 +156,12 @@ mod tests { // come from intermittent test failures of [homomorphic_mul] in the // Synedrion signining library (i.e. parameters are generated with an // unseeded CSPRNG). - - // Run the test with `cargo t inversion_v06_vs_v055`. - - // For convenience, after this test follows a commented out version of the - // same that passes with crypto-bigint v0.5.5. - - // To compare with v0.5.5 do the following: - - // 1. Checkout the old code: `git checkout v0.5.5` - // 1. Run `cargo update -p proc-macro2` to work around a recent compiler - // incompatibility - // 1. Paste the commented out v0.5 test in the test module of - // `runtime_mod.rs` - // 1. Run the test with `cargo t inversion_v06v_v055` - // 1. Notice it passes - + // Run the test with `cargo t v06_inversion`. + // After this test follows a test doing the same thing, but using v0.5.5 + // // [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 #[test] - fn inversion_v06_vs_v055() { + fn v06_inversion() { use crate::U2048; let modulus = Odd::new(U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49")).unwrap(); @@ -189,7 +176,37 @@ mod tests { MontyForm::new(&int, params) }; assert!(bool::from(inverted.is_some())); - assert_eq!(inverted.unwrap(), expected_inv); + let inverted = inverted.unwrap(); + assert_eq!(inverted, expected_inv); + + let one = mont * inverted; + assert_eq!(one.retrieve(), U2048::ONE); + } + + // Version of the above test using v0.5.5 + #[test] + fn v05_inversion() { + use crypto_bigint_05::{ + modular::runtime_mod::{DynResidue, DynResidueParams}, + U2048, + }; + let modulus = U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); + let params = DynResidueParams::new(&modulus); + let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); + let mont = DynResidue::new(&int, params); + + let (inverted, choice) = mont.invert(); + + let expected_inv = { + let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); + DynResidue::new(&int, params) + }; + + assert!(bool::from(choice)); + assert_eq!(inverted, expected_inv); + + let one = mont * inverted; + assert_eq!(one.retrieve(), U2048::ONE); } /// This test use the same inputs as the `inversion_v06_vs_v055` test above @@ -208,24 +225,126 @@ mod tests { let expected_inv = BigUint::from_bytes_be(&hex!("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E")); assert_eq!(inverted, expected_inv); + let one = int.mulm(inverted, &modulus); + assert_eq!(one, BigUint::one()); } - // // Version of the above test using v0.5.5: - // #[test] - // fn inversion_v06_vs_v055() { - // use crate::U2048; - // let modulus = U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); - // let params = DynResidueParams::new(&modulus); - // let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); - // let mont = DynResidue::new(&int, params); - - // let (inverted, choice) = mont.invert(); - - // let expected_inv = { - // let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); - // DynResidue::new(&int, params) - // }; - // assert!(choice.is_true_vartime()); - // assert_eq!(inverted, expected_inv); - // } + // v0.6 test that creates random U1024s, inverts and sanity checks the result. + #[test] + fn v06_invert_random_uint() { + use crate::{Integer, NonZero, RandomMod, U1024, U2048}; + use rand_chacha::ChaChaRng; + use rand_core::{CryptoRngCore, SeedableRng}; + + fn random_invertible( + monty_params: MontyParams<16>, + rng: &mut impl CryptoRngCore, + ) -> (U1024, MontyForm<16>, MontyForm<16>) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = U1024::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + return (r, rm, rm_inv.unwrap()); + } + } + } + + let mut rng = ChaChaRng::from_seed([17u8; 32]); + let modulus = U1024::from([ + 0xd69d42ede255db9d, + 0x20d23f0e7e55c2f3, + 0xb11955f4354ad01e, + 0xe1598344a83da132, + 0xa40162a5315c9b7f, + 0xa3e41cc5720f4dca, + 0x3a28db743e07f87b, + 0x717d2332171a2bd7, + 0x7f6917818473334e, + 0x7fc6c2ffd071667d, + 0x1bdb77d72f57ac49, + 0x3c857fff2fe7f7ee, + 0x33ca8e3a359428ad, + 0x12a442daca8af09d, + 0x872ac90b36ebd1bc, + 0x9aefced07fa13351, + ]); + + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + for _ in 0..100 { + let (r, rm, rmi) = random_invertible(monty_params, &mut rng); + let one_monty = rm * rmi; + assert_eq!(one_monty, MontyForm::one(monty_params)); + assert_eq!(one_monty.retrieve(), U1024::ONE); + + let one = rmi.retrieve().widening_mul(&r); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + assert_eq!(one % wide_modulus, U2048::ONE); + + let ri = r.inv_mod(&modulus).unwrap(); + assert_eq!(ri, rmi.retrieve()); + } + } + + // v0.5.5 test that creates random U1024s, inverts and sanity checks the result. + #[test] + fn v05_invert_random_uint() { + use crypto_bigint_05::{ + modular::runtime_mod::{DynResidue, DynResidueParams}, + NonZero, RandomMod, U1024, U2048, + }; + use rand_chacha::ChaChaRng; + use rand_core::{CryptoRngCore, SeedableRng}; + + fn random_invertible( + monty_params: DynResidueParams<16>, + rng: &mut impl CryptoRngCore, + ) -> (U1024, DynResidue<16>, DynResidue<16>) { + let modulus = monty_params.modulus().clone(); + loop { + let r = U1024::random_mod(rng, &NonZero::new(modulus).unwrap()); + let rm = DynResidue::new(&r, monty_params); + let (rm_inv, rm_inv_opt) = rm.invert(); + if bool::from(rm_inv_opt) { + return (r, rm, rm_inv); + } + } + } + let mut rng = ChaChaRng::from_seed([17u8; 32]); + let modulus = U1024::from([ + 0xd69d42ede255db9d, + 0x20d23f0e7e55c2f3, + 0xb11955f4354ad01e, + 0xe1598344a83da132, + 0xa40162a5315c9b7f, + 0xa3e41cc5720f4dca, + 0x3a28db743e07f87b, + 0x717d2332171a2bd7, + 0x7f6917818473334e, + 0x7fc6c2ffd071667d, + 0x1bdb77d72f57ac49, + 0x3c857fff2fe7f7ee, + 0x33ca8e3a359428ad, + 0x12a442daca8af09d, + 0x872ac90b36ebd1bc, + 0x9aefced07fa13351, + ]); + + let monty_params = DynResidueParams::new(&modulus); + for _ in 0..100 { + let (r, rm, rmi) = random_invertible(monty_params, &mut rng); + + let one_monty = rm * rmi; + assert_eq!(one_monty, DynResidue::one(monty_params)); + assert_eq!(one_monty.retrieve(), U1024::ONE); + + let one = rmi.retrieve() * r; + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + assert_eq!(one % wide_modulus, U2048::ONE); + + let (ri, _) = r.inv_mod(&modulus); + assert_eq!(ri, rmi.retrieve()); + } + } } From 3f84e2daa6fe5e029a3ce06c6c5d60118d54ff8b Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 18 Jun 2024 12:29:53 +0200 Subject: [PATCH 06/11] Lockfile --- Cargo.lock | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f5333470..3c585979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,12 +216,23 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "crypto-bigint" version = "0.6.0-pre.12" dependencies = [ "bincode", "criterion", + "crypto-bigint 0.5.5", "der", "hex-literal", "hybrid-array", From e0da58b77a6272391271f7dd8b781fee032cfa3a Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 20 Jun 2024 11:22:58 +0200 Subject: [PATCH 07/11] More tests, better description, cleanup --- src/modular/monty_form/inv.rs | 361 +++++++++++++++++++++++++++------- 1 file changed, 289 insertions(+), 72 deletions(-) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index bf74c347..eba6b206 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -116,7 +116,102 @@ mod tests { use num_traits::{FromBytes, One}; use super::{MontyForm, MontyParams}; - use crate::{Invert, Inverter, Odd, PrecomputeInverter, U256}; + use crate::{ + Integer, Invert, Inverter, NonZero, Odd, PrecomputeInverter, RandomMod, Uint, U1024, U2048, + U256, U4096, U8192, + }; + + use crypto_bigint_05::{ + modular::runtime_mod::{DynResidue, DynResidueParams}, + NonZero as NonZero_05, RandomMod as RandomMod_05, Uint as Uint_05, U1024 as U1024_05, + U2048 as U2048_05, U4096 as U4096_05, + }; + use rand_chacha::ChaChaRng; + use rand_core::{CryptoRngCore, SeedableRng}; + + // Seed used to ensure deterministc random sequences in tests across versions. + const RANDOM_SEED: [u8; 32] = [17; 32]; + // Modulus that resulted from a test-run in Synedrion (random), used in + // tests below to test different versions. + const SUSPECT_MODULUS: [u8; 256] = hex!("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); + + // Generates a random `Uint` and returns it as a tuple in normal form, + // montgomery form and inverted montgomery form. + // Uses `crypto-bigint` v0.5.5. + fn random_invertible_05( + monty_params: DynResidueParams, + rng: &mut impl CryptoRngCore, + ) -> (Uint_05, DynResidue, DynResidue) { + let modulus = monty_params.modulus().clone(); + loop { + let r = Uint_05::random_mod(rng, &NonZero_05::new(modulus).unwrap()); + let rm = DynResidue::new(&r, monty_params); + let (rm_inv, rm_inv_opt) = rm.invert(); + if bool::from(rm_inv_opt) { + return (r, rm, rm_inv); + } + } + } + + // Generates a random `U1024` and returns it as a tuple in normal form, + // montgomery form and inverted montgomery form. + // Uses `crypto-bigint` v0.6. + // Must make copies because `PrecomputeInverter` is only impl'd for concrete + // types (by way of macro). + fn random_invertible_u1024_06( + monty_params: MontyParams<16>, + rng: &mut impl CryptoRngCore, + ) -> (U1024, MontyForm<16>, MontyForm<16>) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = Uint::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + return (r, rm, rm_inv.unwrap()); + } + } + } + + // Generates a random `U1024` and returns it as a tuple in normal form, + // montgomery form and inverted montgomery form. + // Uses `crypto-bigint` v0.6. + // Must make copies because `PrecomputeInverter` is only impl'd for concrete + // types (by way of macro). + fn random_invertible_u2048_06( + monty_params: MontyParams<32>, + rng: &mut impl CryptoRngCore, + ) -> (U2048, MontyForm<32>, MontyForm<32>) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = Uint::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + return (r, rm, rm_inv.unwrap()); + } + } + } + + // Generates a random `U1024` and returns it as a tuple in normal form, + // montgomery form and inverted montgomery form. + // Uses `crypto-bigint` v0.6. + // Must make copies because `PrecomputeInverter` is only impl'd for concrete + // types (by way of macro). + fn random_invertible_u4096_06( + monty_params: MontyParams<64>, + rng: &mut impl CryptoRngCore, + ) -> (U4096, MontyForm<64>, MontyForm<64>) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = Uint::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + return (r, rm, rm_inv.unwrap()); + } + } + } fn params() -> MontyParams<{ U256::LIMBS }> { MontyParams::new_vartime(Odd::::from_be_hex( @@ -156,21 +251,19 @@ mod tests { // come from intermittent test failures of [homomorphic_mul] in the // Synedrion signining library (i.e. parameters are generated with an // unseeded CSPRNG). - // Run the test with `cargo t v06_inversion`. - // After this test follows a test doing the same thing, but using v0.5.5 + // + // After this test follows a test doing the same thing but using v0.5.5 and another one using `num-modular`. // // [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 #[test] - fn v06_inversion() { - use crate::U2048; - - let modulus = Odd::new(U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49")).unwrap(); + fn v06_invert_u2048() { + let modulus = Odd::new(U2048::from_be_slice(&SUSPECT_MODULUS)).unwrap(); let params = MontyParams::new_vartime(modulus); let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); let mont = MontyForm::new(&int, params); let inverted = mont.invert(); - // This is what v0.5.5 outputs using the same inputs. + // This is what v0.5.5 outputs using the same inputs. let expected_inv = { let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); MontyForm::new(&int, params) @@ -183,22 +276,18 @@ mod tests { assert_eq!(one.retrieve(), U2048::ONE); } - // Version of the above test using v0.5.5 + // Version of the above test using v0.5.5. #[test] - fn v05_inversion() { - use crypto_bigint_05::{ - modular::runtime_mod::{DynResidue, DynResidueParams}, - U2048, - }; - let modulus = U2048::from_be_hex("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); + fn v05_invert_u2048() { + let modulus = U2048_05::from_be_slice(&SUSPECT_MODULUS); let params = DynResidueParams::new(&modulus); - let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); + let int = U2048_05::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); let mont = DynResidue::new(&int, params); let (inverted, choice) = mont.invert(); let expected_inv = { - let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); + let int = U2048_05::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); DynResidue::new(&int, params) }; @@ -206,19 +295,17 @@ mod tests { assert_eq!(inverted, expected_inv); let one = mont * inverted; - assert_eq!(one.retrieve(), U2048::ONE); + assert_eq!(one.retrieve(), U2048_05::ONE); } - /// This test use the same inputs as the `inversion_v06_vs_v055` test above - /// but using the `num-modular` code. That crate lacks support for big integer Montgomery form + /// This test use the same inputs as the two tests above + /// but using the `num-modular` crate. That crate lacks support for big integer Montgomery form /// so for this test the values are in normal form. /// /// This test passes and agrees with crypto-bigint v0.5.5. #[test] - fn invert_with_num_modular() { - let modulus = BigUint::from_be_bytes(&hex!( - "5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49" - )); + fn num_modular_invert_u2048() { + let modulus = BigUint::from_be_bytes(&SUSPECT_MODULUS); let int = BigUint::from_be_bytes(&hex!("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2")); let inverted = int.clone().invm(&modulus).expect("inverse exists"); @@ -229,29 +316,180 @@ mod tests { assert_eq!(one, BigUint::one()); } - // v0.6 test that creates random U1024s, inverts and sanity checks the result. #[test] - fn v06_invert_random_uint() { - use crate::{Integer, NonZero, RandomMod, U1024, U2048}; - use rand_chacha::ChaChaRng; - use rand_core::{CryptoRngCore, SeedableRng}; - - fn random_invertible( - monty_params: MontyParams<16>, - rng: &mut impl CryptoRngCore, - ) -> (U1024, MontyForm<16>, MontyForm<16>) { - let modulus = monty_params.modulus().to_nz().unwrap(); - loop { - let r = U1024::random_mod(rng, &modulus); - let rm = ::Monty::new(&r, monty_params); - let rm_inv = rm.invert(); - if rm_inv.is_some().into() { - return (r, rm, rm_inv.unwrap()); - } - } + fn v05_inversion_random_uints_u2048_modulus() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U2048_05::from_be_hex(concat!( + "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )); + + let monty_params = DynResidueParams::new(&modulus); + for _ in 0..100 { + let (r, rm, rmi) = random_invertible_05(monty_params, &mut rng); + + let one_monty = rm * rmi; + assert_eq!(one_monty, DynResidue::one(monty_params)); + assert_eq!(one_monty.retrieve(), U2048_05::ONE); + + let one = rmi.retrieve() * r; + let wide_modulus = NonZero_05::new(Into::::into(&modulus)).unwrap(); + assert_eq!(one % wide_modulus, U4096_05::ONE); + + let (ri, _) = r.inv_mod(&modulus); + assert_eq!(ri, rmi.retrieve()); } + } - let mut rng = ChaChaRng::from_seed([17u8; 32]); + // Moduli strictly smaller than U2048::MAX / 6 work (i.e. 3 msbs are zero). + // In other words, the most significant byte must be smaller than 31 (0x1F). + // The test passes when the first bytes is: + // 0x1e 00011110, + // …and fails when first byte is: + // 0x1f 00011111, + // + // Weirdly enough it seems like U1024 is *not* affected by this problem. + // U4096 behaves the same but the threshold is different (see below). + #[test] + fn v06_inversion_random_uints_u2048_modulus() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U2048::from_be_hex(concat!( + "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )); + // println!("modulus: {:?}\nmodulus bin: {:02048b}", modulus, modulus); + + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + // At index 2414 with seed `RANDOM_SEED` there's another failure in the "normal form inverted" case. Unrelated? + for _ in 0..100 { + let (r, rm, rmi) = random_invertible_u2048_06(monty_params, &mut rng); + let one_monty = rm * rmi; + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", + modulus + ); + assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + let one = rmi.retrieve().widening_mul(&r); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + assert_eq!( + one % wide_modulus, + U4096::ONE, + "a*a⁻¹ ≠ 1 (normal form, rem)" + ); + + let ri = r.inv_mod(&modulus).unwrap(); + assert_eq!(ri, rmi.retrieve(), "a*a⁻¹ ≠ 1 (normal form inverted)"); + } + } + + #[test] + fn v06_inversion_random_uints_u4096_modulus() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U4096::from_be_hex(concat!( + // 0x07… fails (bin 00000111) + "06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + )); + // println!("modulus: {:?}\nmodulus bin: {:04096b}", modulus, modulus); + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + + // TODO: At index 1760 with seed `RANDOM_SEED` there's another failure in + // the "normal form inverted" case. Possibly related to the problem with + // big moduli? Or not? + for _ in 0..100 { + let (r, rm, rmi) = random_invertible_u4096_06(monty_params, &mut rng); + let one_monty = rm * rmi; + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", + modulus + ); + assert_eq!(one_monty.retrieve(), U4096::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + let one = rmi.retrieve().widening_mul(&r); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + assert_eq!( + one % wide_modulus, + U8192::ONE, + "a*a⁻¹ ≠ 1 (normal form, rem)" + ); + + let ri = r.inv_mod(&modulus).unwrap(); + assert_eq!(ri, rmi.retrieve(), "a*a⁻¹ ≠ 1 (normal form inverted)"); + } + } + + #[test] + fn v06_inversion_random_uints_u1024_modulus() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U1024::from_be_hex(concat!( + "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + )); + // println!("modulus: {:?}\nmodulus bin: {:01024b}", modulus, modulus); + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + + // Tested up to 1_000_000 + for _ in 0..100 { + let (r, rm, rmi) = random_invertible_u1024_06(monty_params, &mut rng); + let one_monty = rm * rmi; + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", + modulus + ); + assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + let one = rmi.retrieve().widening_mul(&r); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + assert_eq!( + one % wide_modulus, + U2048::ONE, + "a*a⁻¹ ≠ 1 (normal form, rem)" + ); + + let ri = r.inv_mod(&modulus).unwrap(); + assert_eq!(ri, rmi.retrieve(), "a*a⁻¹ ≠ 1 (normal form inverted)"); + } + } + + // v0.6 test that creates random U1024s, inverts and sanity checks the result. + #[test] + fn v06_invert_random_uint() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); let modulus = U1024::from([ 0xd69d42ede255db9d, 0x20d23f0e7e55c2f3, @@ -273,7 +511,7 @@ mod tests { let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); for _ in 0..100 { - let (r, rm, rmi) = random_invertible(monty_params, &mut rng); + let (r, rm, rmi) = random_invertible_u1024_06(monty_params, &mut rng); let one_monty = rm * rmi; assert_eq!(one_monty, MontyForm::one(monty_params)); assert_eq!(one_monty.retrieve(), U1024::ONE); @@ -290,29 +528,8 @@ mod tests { // v0.5.5 test that creates random U1024s, inverts and sanity checks the result. #[test] fn v05_invert_random_uint() { - use crypto_bigint_05::{ - modular::runtime_mod::{DynResidue, DynResidueParams}, - NonZero, RandomMod, U1024, U2048, - }; - use rand_chacha::ChaChaRng; - use rand_core::{CryptoRngCore, SeedableRng}; - - fn random_invertible( - monty_params: DynResidueParams<16>, - rng: &mut impl CryptoRngCore, - ) -> (U1024, DynResidue<16>, DynResidue<16>) { - let modulus = monty_params.modulus().clone(); - loop { - let r = U1024::random_mod(rng, &NonZero::new(modulus).unwrap()); - let rm = DynResidue::new(&r, monty_params); - let (rm_inv, rm_inv_opt) = rm.invert(); - if bool::from(rm_inv_opt) { - return (r, rm, rm_inv); - } - } - } - let mut rng = ChaChaRng::from_seed([17u8; 32]); - let modulus = U1024::from([ + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U1024_05::from([ 0xd69d42ede255db9d, 0x20d23f0e7e55c2f3, 0xb11955f4354ad01e, @@ -333,15 +550,15 @@ mod tests { let monty_params = DynResidueParams::new(&modulus); for _ in 0..100 { - let (r, rm, rmi) = random_invertible(monty_params, &mut rng); + let (r, rm, rmi) = random_invertible_05(monty_params, &mut rng); let one_monty = rm * rmi; assert_eq!(one_monty, DynResidue::one(monty_params)); - assert_eq!(one_monty.retrieve(), U1024::ONE); + assert_eq!(one_monty.retrieve(), U1024_05::ONE); let one = rmi.retrieve() * r; - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); - assert_eq!(one % wide_modulus, U2048::ONE); + let wide_modulus = NonZero_05::new(Into::::into(&modulus)).unwrap(); + assert_eq!(one % wide_modulus, U2048_05::ONE); let (ri, _) = r.inv_mod(&modulus); assert_eq!(ri, rmi.retrieve()); From 6cd86f8d1025e3ebf736db938a61006626b61be5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 5 Aug 2024 16:06:08 +0200 Subject: [PATCH 08/11] Clean up inversion tests --- Cargo.lock | 11 - Cargo.toml | 1 - src/modular/monty_form/inv.rs | 368 +++++++++++++--------------------- 3 files changed, 144 insertions(+), 236 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 561a8672..31171356 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,23 +208,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "rand_core", - "subtle", -] - [[package]] name = "crypto-bigint" version = "0.6.0-rc.2" dependencies = [ "bincode", "criterion", - "crypto-bigint 0.5.5", "der", "hex-literal", "hybrid-array", diff --git a/Cargo.toml b/Cargo.toml index 83cbaa81..79c00dd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ num-modular = { version = "0.6", features = ["num-bigint", "num-integer", "num-t proptest = "1" rand_core = { version = "0.6", features = ["std"] } rand_chacha = "0.3" -crypto_bigint_05 = {package = "crypto-bigint", version = "0.5.5"} [features] default = ["rand"] diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index eba6b206..7c6d09d8 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -121,94 +121,84 @@ mod tests { U256, U4096, U8192, }; - use crypto_bigint_05::{ - modular::runtime_mod::{DynResidue, DynResidueParams}, - NonZero as NonZero_05, RandomMod as RandomMod_05, Uint as Uint_05, U1024 as U1024_05, - U2048 as U2048_05, U4096 as U4096_05, - }; use rand_chacha::ChaChaRng; use rand_core::{CryptoRngCore, SeedableRng}; - // Seed used to ensure deterministc random sequences in tests across versions. + // Seed used to ensure deterministc random sequences in tests. const RANDOM_SEED: [u8; 32] = [17; 32]; - // Modulus that resulted from a test-run in Synedrion (random), used in - // tests below to test different versions. - const SUSPECT_MODULUS: [u8; 256] = hex!("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); - - // Generates a random `Uint` and returns it as a tuple in normal form, - // montgomery form and inverted montgomery form. - // Uses `crypto-bigint` v0.5.5. - fn random_invertible_05( - monty_params: DynResidueParams, - rng: &mut impl CryptoRngCore, - ) -> (Uint_05, DynResidue, DynResidue) { - let modulus = monty_params.modulus().clone(); - loop { - let r = Uint_05::random_mod(rng, &NonZero_05::new(modulus).unwrap()); - let rm = DynResidue::new(&r, monty_params); - let (rm_inv, rm_inv_opt) = rm.invert(); - if bool::from(rm_inv_opt) { - return (r, rm, rm_inv); - } - } - } + // Random modulus. + const MODULUS: [u8; 256] = hex!("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); - // Generates a random `U1024` and returns it as a tuple in normal form, - // montgomery form and inverted montgomery form. - // Uses `crypto-bigint` v0.6. - // Must make copies because `PrecomputeInverter` is only impl'd for concrete - // types (by way of macro). - fn random_invertible_u1024_06( + // Generates a random `U1024` and returns it as a tuple in: normal form, montgomery form, + // inverted montgomery form and the normal form inverse from the num_modular crate. + fn random_invertible_u1024( monty_params: MontyParams<16>, rng: &mut impl CryptoRngCore, - ) -> (U1024, MontyForm<16>, MontyForm<16>) { + ) -> (U1024, MontyForm<16>, MontyForm<16>, BigUint) { let modulus = monty_params.modulus().to_nz().unwrap(); loop { let r = Uint::random_mod(rng, &modulus); let rm = ::Monty::new(&r, monty_params); let rm_inv = rm.invert(); if rm_inv.is_some().into() { - return (r, rm, rm_inv.unwrap()); + // Calculate the inverse using the num_modular crate as well + let num_modular_modulus = + BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); + let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) + .clone() + .invm(&num_modular_modulus) + .unwrap(); + + return (r, rm, rm_inv.unwrap(), num_modular_inverse); } } } - - // Generates a random `U1024` and returns it as a tuple in normal form, - // montgomery form and inverted montgomery form. - // Uses `crypto-bigint` v0.6. - // Must make copies because `PrecomputeInverter` is only impl'd for concrete - // types (by way of macro). - fn random_invertible_u2048_06( + // Generates a random `U2048` and returns it as a tuple in: normal form, montgomery form, + // inverted montgomery form and the normal form inverse from the num_modular crate. + fn random_invertible_u2048( monty_params: MontyParams<32>, rng: &mut impl CryptoRngCore, - ) -> (U2048, MontyForm<32>, MontyForm<32>) { + ) -> (U2048, MontyForm<32>, MontyForm<32>, BigUint) { let modulus = monty_params.modulus().to_nz().unwrap(); loop { let r = Uint::random_mod(rng, &modulus); let rm = ::Monty::new(&r, monty_params); let rm_inv = rm.invert(); if rm_inv.is_some().into() { - return (r, rm, rm_inv.unwrap()); + // Calculate the inverse using the num_modular crate as well + let num_modular_modulus = + BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); + let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) + .clone() + .invm(&num_modular_modulus) + .unwrap(); + + return (r, rm, rm_inv.unwrap(), num_modular_inverse); } } } - // Generates a random `U1024` and returns it as a tuple in normal form, - // montgomery form and inverted montgomery form. - // Uses `crypto-bigint` v0.6. - // Must make copies because `PrecomputeInverter` is only impl'd for concrete - // types (by way of macro). - fn random_invertible_u4096_06( + // Generates a random `U4096` and returns it as a tuple in: normal form, montgomery form, + // inverted montgomery form and the normal form inverse from the num_modular crate. + fn random_invertible_u4096( monty_params: MontyParams<64>, rng: &mut impl CryptoRngCore, - ) -> (U4096, MontyForm<64>, MontyForm<64>) { + ) -> (U4096, MontyForm<64>, MontyForm<64>, BigUint) { let modulus = monty_params.modulus().to_nz().unwrap(); loop { let r = Uint::random_mod(rng, &modulus); let rm = ::Monty::new(&r, monty_params); let rm_inv = rm.invert(); if rm_inv.is_some().into() { - return (r, rm, rm_inv.unwrap()); + // Calculate the inverse using the num_modular crate as well + let num_modular_modulus = + BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); + let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) + .clone() + .invm(&num_modular_modulus) + .unwrap(); + + return (r, rm, rm_inv.unwrap(), num_modular_inverse); } } } @@ -224,10 +214,10 @@ mod tests { let params = params(); let x = U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); - let x_mod = MontyForm::new(&x, params); + let x_monty = MontyForm::new(&x, params); - let inv = x_mod.invert().unwrap(); - let res = x_mod * inv; + let inv = x_monty.invert().unwrap(); + let res = x_monty * inv; assert_eq!(res.retrieve(), U256::ONE); } @@ -237,33 +227,24 @@ mod tests { let params = params(); let x = U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685"); - let x_mod = MontyForm::new(&x, params); + let x_monty = MontyForm::new(&x, params); let inverter = params.precompute_inverter(); - let inv = inverter.invert(&x_mod).unwrap(); - let res = x_mod * inv; + let inv = inverter.invert(&x_monty).unwrap(); + let res = x_monty * inv; assert_eq!(res.retrieve(), U256::ONE); } - // Test to illustrate a potential problem with the Bernstein&Yang code to - // invert numbers in Montgomery form. The specific parameters shown here - // come from intermittent test failures of [homomorphic_mul] in the - // Synedrion signining library (i.e. parameters are generated with an - // unseeded CSPRNG). - // - // After this test follows a test doing the same thing but using v0.5.5 and another one using `num-modular`. - // - // [homomorphic_mul]: https://github.com/dvdplm/synedrion/blob/520e3246e6032a100db64eef47b3dee62cd7c055/synedrion/src/paillier/encryption.rs#L518 #[test] - fn v06_invert_u2048() { - let modulus = Odd::new(U2048::from_be_slice(&SUSPECT_MODULUS)).unwrap(); + fn invert_u2048() { + let modulus = Odd::new(U2048::from_be_slice(&MODULUS)).unwrap(); let params = MontyParams::new_vartime(modulus); let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); let mont = MontyForm::new(&int, params); let inverted = mont.invert(); - // This is what v0.5.5 outputs using the same inputs. + // This is what v0.5.5 outputs using the same inputs. let expected_inv = { let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); MontyForm::new(&int, params) @@ -276,36 +257,12 @@ mod tests { assert_eq!(one.retrieve(), U2048::ONE); } - // Version of the above test using v0.5.5. - #[test] - fn v05_invert_u2048() { - let modulus = U2048_05::from_be_slice(&SUSPECT_MODULUS); - let params = DynResidueParams::new(&modulus); - let int = U2048_05::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"); - let mont = DynResidue::new(&int, params); - - let (inverted, choice) = mont.invert(); - - let expected_inv = { - let int = U2048_05::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"); - DynResidue::new(&int, params) - }; - - assert!(bool::from(choice)); - assert_eq!(inverted, expected_inv); - - let one = mont * inverted; - assert_eq!(one.retrieve(), U2048_05::ONE); - } - - /// This test use the same inputs as the two tests above + /// This test use the same inputs as the test above /// but using the `num-modular` crate. That crate lacks support for big integer Montgomery form /// so for this test the values are in normal form. - /// - /// This test passes and agrees with crypto-bigint v0.5.5. #[test] - fn num_modular_invert_u2048() { - let modulus = BigUint::from_be_bytes(&SUSPECT_MODULUS); + fn invert_u2048_with_num_modular() { + let modulus = BigUint::from_be_bytes(&MODULUS); let int = BigUint::from_be_bytes(&hex!("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2")); let inverted = int.clone().invm(&modulus).expect("inverse exists"); @@ -316,48 +273,62 @@ mod tests { assert_eq!(one, BigUint::one()); } + // Creates random U1024s, inverts and sanity checks the result. #[test] - fn v05_inversion_random_uints_u2048_modulus() { + fn inversion_random_uints_u1024() { let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U2048_05::from_be_hex(concat!( + let modulus = U1024::from_be_hex(concat!( "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" )); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - let monty_params = DynResidueParams::new(&modulus); + // Tested up to 10_000_000 for _ in 0..100 { - let (r, rm, rmi) = random_invertible_05(monty_params, &mut rng); + let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = + random_invertible_u1024(monty_params, &mut rng); + let one_monty = rand_uint_monty * rand_uint_monty_inv; - let one_monty = rm * rmi; - assert_eq!(one_monty, DynResidue::one(monty_params)); - assert_eq!(one_monty.retrieve(), U2048_05::ONE); + // Inversion works in monty form + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:01024b}", + modulus + ); + // …and in normal form + assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + // …and when converted back to normal form and used in a widening operation + let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); + assert_eq!( + one % wide_modulus, + U2048::ONE, + "a*a⁻¹ ≠ 1 (normal form, wide)" + ); - let one = rmi.retrieve() * r; - let wide_modulus = NonZero_05::new(Into::::into(&modulus)).unwrap(); - assert_eq!(one % wide_modulus, U4096_05::ONE); + // …and agrees with normal form inversion + let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); + assert_eq!( + normal_form_inv, + rand_uint_monty_inv.retrieve(), + "a*a⁻¹ ≠ 1 (normal form inverted)" + ); - let (ri, _) = r.inv_mod(&modulus); - assert_eq!(ri, rmi.retrieve()); + // …and agrees with the num_modular crate + assert_eq!( + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + num_modular_inv, + "num_modular ≠ crypto_bigint" + ) } } - // Moduli strictly smaller than U2048::MAX / 6 work (i.e. 3 msbs are zero). - // In other words, the most significant byte must be smaller than 31 (0x1F). - // The test passes when the first bytes is: - // 0x1e 00011110, - // …and fails when first byte is: - // 0x1f 00011111, - // - // Weirdly enough it seems like U1024 is *not* affected by this problem. - // U4096 behaves the same but the threshold is different (see below). #[test] - fn v06_inversion_random_uints_u2048_modulus() { + fn inversion_random_uints_u2048() { let mut rng = ChaChaRng::from_seed(RANDOM_SEED); let modulus = U2048::from_be_hex(concat!( "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -369,40 +340,53 @@ mod tests { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" )); - // println!("modulus: {:?}\nmodulus bin: {:02048b}", modulus, modulus); - + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - // At index 2414 with seed `RANDOM_SEED` there's another failure in the "normal form inverted" case. Unrelated? for _ in 0..100 { - let (r, rm, rmi) = random_invertible_u2048_06(monty_params, &mut rng); - let one_monty = rm * rmi; + let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = + random_invertible_u2048(monty_params, &mut rng); + let one_monty = rand_uint_monty * rand_uint_monty_inv; + + // Inversion works in monty form assert_eq!( one_monty, MontyForm::one(monty_params), "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", modulus ); + // …and in normal form assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)"); - let one = rmi.retrieve().widening_mul(&r); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + // …and when converted back to normal form and used in a widening operation + let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); assert_eq!( one % wide_modulus, U4096::ONE, - "a*a⁻¹ ≠ 1 (normal form, rem)" + "a*a⁻¹ ≠ 1 (normal form, wide)" ); - let ri = r.inv_mod(&modulus).unwrap(); - assert_eq!(ri, rmi.retrieve(), "a*a⁻¹ ≠ 1 (normal form inverted)"); + // …and agrees with normal form inversion + let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); + assert_eq!( + normal_form_inv, + rand_uint_monty_inv.retrieve(), + "a*a⁻¹ ≠ 1 (normal form inverted)" + ); + + // …and agrees with the num_modular crate + assert_eq!( + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + num_modular_inv, + "num_modular ≠ crypto_bigint" + ) } } #[test] - fn v06_inversion_random_uints_u4096_modulus() { + fn inversion_random_uints_u4096() { let mut rng = ChaChaRng::from_seed(RANDOM_SEED); let modulus = U4096::from_be_hex(concat!( - // 0x07… fails (bin 00000111) - "06ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -419,76 +403,52 @@ mod tests { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", )); - // println!("modulus: {:?}\nmodulus bin: {:04096b}", modulus, modulus); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - // TODO: At index 1760 with seed `RANDOM_SEED` there's another failure in - // the "normal form inverted" case. Possibly related to the problem with - // big moduli? Or not? - for _ in 0..100 { - let (r, rm, rmi) = random_invertible_u4096_06(monty_params, &mut rng); - let one_monty = rm * rmi; + for _ in 0..20 { + let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = + random_invertible_u4096(monty_params, &mut rng); + let one_monty = rand_uint_monty * rand_uint_monty_inv; + + // Inversion works in monty form assert_eq!( one_monty, MontyForm::one(monty_params), "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", modulus ); + // …and in normal form assert_eq!(one_monty.retrieve(), U4096::ONE, "a*a⁻¹ ≠ 1 (normal form)"); - let one = rmi.retrieve().widening_mul(&r); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + // …and when converted back to normal form and used in a widening operation + let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); assert_eq!( one % wide_modulus, U8192::ONE, - "a*a⁻¹ ≠ 1 (normal form, rem)" + "a*a⁻¹ ≠ 1 (normal form, wide)" ); - let ri = r.inv_mod(&modulus).unwrap(); - assert_eq!(ri, rmi.retrieve(), "a*a⁻¹ ≠ 1 (normal form inverted)"); - } - } - - #[test] - fn v06_inversion_random_uints_u1024_modulus() { - let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U1024::from_be_hex(concat!( - "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - )); - // println!("modulus: {:?}\nmodulus bin: {:01024b}", modulus, modulus); - let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - - // Tested up to 1_000_000 - for _ in 0..100 { - let (r, rm, rmi) = random_invertible_u1024_06(monty_params, &mut rng); - let one_monty = rm * rmi; + // …and agrees with normal form inversion + let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); assert_eq!( - one_monty, - MontyForm::one(monty_params), - "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", - modulus + normal_form_inv, + rand_uint_monty_inv.retrieve(), + "a*a⁻¹ ≠ 1 (normal form inverted)" ); - assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); - let one = rmi.retrieve().widening_mul(&r); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + // …and agrees with the num_modular crate assert_eq!( - one % wide_modulus, - U2048::ONE, - "a*a⁻¹ ≠ 1 (normal form, rem)" - ); - - let ri = r.inv_mod(&modulus).unwrap(); - assert_eq!(ri, rmi.retrieve(), "a*a⁻¹ ≠ 1 (normal form inverted)"); + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + num_modular_inv, + "num_modular ≠ crypto_bigint" + ) } } - // v0.6 test that creates random U1024s, inverts and sanity checks the result. + // Creates random U1024s, inverts and sanity checks the result. #[test] - fn v06_invert_random_uint() { + fn invert_random_u1024s() { let mut rng = ChaChaRng::from_seed(RANDOM_SEED); let modulus = U1024::from([ 0xd69d42ede255db9d, @@ -511,7 +471,7 @@ mod tests { let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); for _ in 0..100 { - let (r, rm, rmi) = random_invertible_u1024_06(monty_params, &mut rng); + let (r, rm, rmi, _) = random_invertible_u1024(monty_params, &mut rng); let one_monty = rm * rmi; assert_eq!(one_monty, MontyForm::one(monty_params)); assert_eq!(one_monty.retrieve(), U1024::ONE); @@ -524,44 +484,4 @@ mod tests { assert_eq!(ri, rmi.retrieve()); } } - - // v0.5.5 test that creates random U1024s, inverts and sanity checks the result. - #[test] - fn v05_invert_random_uint() { - let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U1024_05::from([ - 0xd69d42ede255db9d, - 0x20d23f0e7e55c2f3, - 0xb11955f4354ad01e, - 0xe1598344a83da132, - 0xa40162a5315c9b7f, - 0xa3e41cc5720f4dca, - 0x3a28db743e07f87b, - 0x717d2332171a2bd7, - 0x7f6917818473334e, - 0x7fc6c2ffd071667d, - 0x1bdb77d72f57ac49, - 0x3c857fff2fe7f7ee, - 0x33ca8e3a359428ad, - 0x12a442daca8af09d, - 0x872ac90b36ebd1bc, - 0x9aefced07fa13351, - ]); - - let monty_params = DynResidueParams::new(&modulus); - for _ in 0..100 { - let (r, rm, rmi) = random_invertible_05(monty_params, &mut rng); - - let one_monty = rm * rmi; - assert_eq!(one_monty, DynResidue::one(monty_params)); - assert_eq!(one_monty.retrieve(), U1024_05::ONE); - - let one = rmi.retrieve() * r; - let wide_modulus = NonZero_05::new(Into::::into(&modulus)).unwrap(); - assert_eq!(one % wide_modulus, U2048_05::ONE); - - let (ri, _) = r.inv_mod(&modulus); - assert_eq!(ri, rmi.retrieve()); - } - } } From 24720205d7c9361431e99a742ae671814341c3d3 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 5 Aug 2024 16:43:33 +0200 Subject: [PATCH 09/11] Feature gate tests using randomness --- src/modular/monty_form/inv.rs | 577 +++++++++++++++++----------------- 1 file changed, 291 insertions(+), 286 deletions(-) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index 7c6d09d8..f3588ed3 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -116,89 +116,306 @@ mod tests { use num_traits::{FromBytes, One}; use super::{MontyForm, MontyParams}; - use crate::{ - Integer, Invert, Inverter, NonZero, Odd, PrecomputeInverter, RandomMod, Uint, U1024, U2048, - U256, U4096, U8192, - }; + use crate::{Invert, Inverter, Odd, PrecomputeInverter, U2048, U256}; - use rand_chacha::ChaChaRng; - use rand_core::{CryptoRngCore, SeedableRng}; - - // Seed used to ensure deterministc random sequences in tests. - const RANDOM_SEED: [u8; 32] = [17; 32]; // Random modulus. const MODULUS: [u8; 256] = hex!("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49"); - // Generates a random `U1024` and returns it as a tuple in: normal form, montgomery form, - // inverted montgomery form and the normal form inverse from the num_modular crate. - fn random_invertible_u1024( - monty_params: MontyParams<16>, - rng: &mut impl CryptoRngCore, - ) -> (U1024, MontyForm<16>, MontyForm<16>, BigUint) { - let modulus = monty_params.modulus().to_nz().unwrap(); - loop { - let r = Uint::random_mod(rng, &modulus); - let rm = ::Monty::new(&r, monty_params); - let rm_inv = rm.invert(); - if rm_inv.is_some().into() { - // Calculate the inverse using the num_modular crate as well - let num_modular_modulus = - BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); - let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) - .clone() - .invm(&num_modular_modulus) - .unwrap(); - - return (r, rm, rm_inv.unwrap(), num_modular_inverse); + #[cfg(feature = "rand_core")] + mod randomized_tests { + use super::*; + use crate::{Integer, NonZero, Uint, U1024, U4096, U8192}; + + use crate::RandomMod; + use rand_chacha::ChaChaRng; + use rand_core::{CryptoRngCore, SeedableRng}; + + // Seed used to ensure deterministc random sequences in tests. + const RANDOM_SEED: [u8; 32] = [17; 32]; + + // Generates a random `U1024` and returns it as a tuple in: normal form, montgomery form, + // inverted montgomery form and the normal form inverse from the num_modular crate. + fn random_invertible_u1024( + monty_params: MontyParams<16>, + rng: &mut impl CryptoRngCore, + ) -> (U1024, MontyForm<16>, MontyForm<16>, BigUint) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = Uint::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + // Calculate the inverse using the num_modular crate as well + let num_modular_modulus = + BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); + let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) + .clone() + .invm(&num_modular_modulus) + .unwrap(); + + return (r, rm, rm_inv.unwrap(), num_modular_inverse); + } } } - } - // Generates a random `U2048` and returns it as a tuple in: normal form, montgomery form, - // inverted montgomery form and the normal form inverse from the num_modular crate. - fn random_invertible_u2048( - monty_params: MontyParams<32>, - rng: &mut impl CryptoRngCore, - ) -> (U2048, MontyForm<32>, MontyForm<32>, BigUint) { - let modulus = monty_params.modulus().to_nz().unwrap(); - loop { - let r = Uint::random_mod(rng, &modulus); - let rm = ::Monty::new(&r, monty_params); - let rm_inv = rm.invert(); - if rm_inv.is_some().into() { - // Calculate the inverse using the num_modular crate as well - let num_modular_modulus = - BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); - let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) - .clone() - .invm(&num_modular_modulus) - .unwrap(); - - return (r, rm, rm_inv.unwrap(), num_modular_inverse); + // Generates a random `U2048` and returns it as a tuple in: normal form, montgomery form, + // inverted montgomery form and the normal form inverse from the num_modular crate. + fn random_invertible_u2048( + monty_params: MontyParams<32>, + rng: &mut impl CryptoRngCore, + ) -> (U2048, MontyForm<32>, MontyForm<32>, BigUint) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = Uint::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + // Calculate the inverse using the num_modular crate as well + let num_modular_modulus = + BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); + let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) + .clone() + .invm(&num_modular_modulus) + .unwrap(); + + return (r, rm, rm_inv.unwrap(), num_modular_inverse); + } } } - } - // Generates a random `U4096` and returns it as a tuple in: normal form, montgomery form, - // inverted montgomery form and the normal form inverse from the num_modular crate. - fn random_invertible_u4096( - monty_params: MontyParams<64>, - rng: &mut impl CryptoRngCore, - ) -> (U4096, MontyForm<64>, MontyForm<64>, BigUint) { - let modulus = monty_params.modulus().to_nz().unwrap(); - loop { - let r = Uint::random_mod(rng, &modulus); - let rm = ::Monty::new(&r, monty_params); - let rm_inv = rm.invert(); - if rm_inv.is_some().into() { - // Calculate the inverse using the num_modular crate as well - let num_modular_modulus = - BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); - let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) - .clone() - .invm(&num_modular_modulus) - .unwrap(); - - return (r, rm, rm_inv.unwrap(), num_modular_inverse); + // Generates a random `U4096` and returns it as a tuple in: normal form, montgomery form, + // inverted montgomery form and the normal form inverse from the num_modular crate. + fn random_invertible_u4096( + monty_params: MontyParams<64>, + rng: &mut impl CryptoRngCore, + ) -> (U4096, MontyForm<64>, MontyForm<64>, BigUint) { + let modulus = monty_params.modulus().to_nz().unwrap(); + loop { + let r = Uint::random_mod(rng, &modulus); + let rm = ::Monty::new(&r, monty_params); + let rm_inv = rm.invert(); + if rm_inv.is_some().into() { + // Calculate the inverse using the num_modular crate as well + let num_modular_modulus = + BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes()); + let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes()) + .clone() + .invm(&num_modular_modulus) + .unwrap(); + + return (r, rm, rm_inv.unwrap(), num_modular_inverse); + } + } + } + + // Creates random U1024s, inverts and sanity checks the result. + #[test] + fn inversion_random_uints_u1024() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U1024::from_be_hex(concat!( + "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + )); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + + // Tested up to 10_000_000 + for _ in 0..100 { + let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = + random_invertible_u1024(monty_params, &mut rng); + let one_monty = rand_uint_monty * rand_uint_monty_inv; + + // Inversion works in monty form + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:01024b}", + modulus + ); + // …and in normal form + assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + // …and when converted back to normal form and used in a widening operation + let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); + assert_eq!( + one % wide_modulus, + U2048::ONE, + "a*a⁻¹ ≠ 1 (normal form, wide)" + ); + + // …and agrees with normal form inversion + let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); + assert_eq!( + normal_form_inv, + rand_uint_monty_inv.retrieve(), + "a*a⁻¹ ≠ 1 (normal form inverted)" + ); + + // …and agrees with the num_modular crate + assert_eq!( + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + num_modular_inv, + "num_modular ≠ crypto_bigint" + ) + } + } + + #[test] + fn inversion_random_uints_u2048() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U2048::from_be_hex(concat!( + "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + )); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + for _ in 0..100 { + let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = + random_invertible_u2048(monty_params, &mut rng); + let one_monty = rand_uint_monty * rand_uint_monty_inv; + + // Inversion works in monty form + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", + modulus + ); + // …and in normal form + assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + // …and when converted back to normal form and used in a widening operation + let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); + assert_eq!( + one % wide_modulus, + U4096::ONE, + "a*a⁻¹ ≠ 1 (normal form, wide)" + ); + + // …and agrees with normal form inversion + let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); + assert_eq!( + normal_form_inv, + rand_uint_monty_inv.retrieve(), + "a*a⁻¹ ≠ 1 (normal form inverted)" + ); + + // …and agrees with the num_modular crate + assert_eq!( + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + num_modular_inv, + "num_modular ≠ crypto_bigint" + ) + } + } + + #[test] + fn inversion_random_uints_u4096() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U4096::from_be_hex(concat!( + "07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + )); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + + for _ in 0..20 { + let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = + random_invertible_u4096(monty_params, &mut rng); + let one_monty = rand_uint_monty * rand_uint_monty_inv; + + // Inversion works in monty form + assert_eq!( + one_monty, + MontyForm::one(monty_params), + "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", + modulus + ); + // …and in normal form + assert_eq!(one_monty.retrieve(), U4096::ONE, "a*a⁻¹ ≠ 1 (normal form)"); + + // …and when converted back to normal form and used in a widening operation + let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); + assert_eq!( + one % wide_modulus, + U8192::ONE, + "a*a⁻¹ ≠ 1 (normal form, wide)" + ); + + // …and agrees with normal form inversion + let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); + assert_eq!( + normal_form_inv, + rand_uint_monty_inv.retrieve(), + "a*a⁻¹ ≠ 1 (normal form inverted)" + ); + + // …and agrees with the num_modular crate + assert_eq!( + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), + num_modular_inv, + "num_modular ≠ crypto_bigint" + ) + } + } + + // Creates random U1024s, inverts and sanity checks the result. + #[test] + fn invert_random_u1024s() { + let mut rng = ChaChaRng::from_seed(RANDOM_SEED); + let modulus = U1024::from([ + 0xd69d42ede255db9d, + 0x20d23f0e7e55c2f3, + 0xb11955f4354ad01e, + 0xe1598344a83da132, + 0xa40162a5315c9b7f, + 0xa3e41cc5720f4dca, + 0x3a28db743e07f87b, + 0x717d2332171a2bd7, + 0x7f6917818473334e, + 0x7fc6c2ffd071667d, + 0x1bdb77d72f57ac49, + 0x3c857fff2fe7f7ee, + 0x33ca8e3a359428ad, + 0x12a442daca8af09d, + 0x872ac90b36ebd1bc, + 0x9aefced07fa13351, + ]); + + let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); + for _ in 0..100 { + let (r, rm, rmi, _) = random_invertible_u1024(monty_params, &mut rng); + let one_monty = rm * rmi; + assert_eq!(one_monty, MontyForm::one(monty_params)); + assert_eq!(one_monty.retrieve(), U1024::ONE); + + let one = rmi.retrieve().widening_mul(&r); + let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); + assert_eq!(one % wide_modulus, U2048::ONE); + + let ri = r.inv_mod(&modulus).unwrap(); + assert_eq!(ri, rmi.retrieve()); } } } @@ -272,216 +489,4 @@ mod tests { let one = int.mulm(inverted, &modulus); assert_eq!(one, BigUint::one()); } - - // Creates random U1024s, inverts and sanity checks the result. - #[test] - fn inversion_random_uints_u1024() { - let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U1024::from_be_hex(concat!( - "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - )); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); - let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - - // Tested up to 10_000_000 - for _ in 0..100 { - let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = - random_invertible_u1024(monty_params, &mut rng); - let one_monty = rand_uint_monty * rand_uint_monty_inv; - - // Inversion works in monty form - assert_eq!( - one_monty, - MontyForm::one(monty_params), - "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:01024b}", - modulus - ); - // …and in normal form - assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); - - // …and when converted back to normal form and used in a widening operation - let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); - assert_eq!( - one % wide_modulus, - U2048::ONE, - "a*a⁻¹ ≠ 1 (normal form, wide)" - ); - - // …and agrees with normal form inversion - let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); - assert_eq!( - normal_form_inv, - rand_uint_monty_inv.retrieve(), - "a*a⁻¹ ≠ 1 (normal form inverted)" - ); - - // …and agrees with the num_modular crate - assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), - num_modular_inv, - "num_modular ≠ crypto_bigint" - ) - } - } - - #[test] - fn inversion_random_uints_u2048() { - let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U2048::from_be_hex(concat!( - "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - )); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); - let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - for _ in 0..100 { - let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = - random_invertible_u2048(monty_params, &mut rng); - let one_monty = rand_uint_monty * rand_uint_monty_inv; - - // Inversion works in monty form - assert_eq!( - one_monty, - MontyForm::one(monty_params), - "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", - modulus - ); - // …and in normal form - assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)"); - - // …and when converted back to normal form and used in a widening operation - let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); - assert_eq!( - one % wide_modulus, - U4096::ONE, - "a*a⁻¹ ≠ 1 (normal form, wide)" - ); - - // …and agrees with normal form inversion - let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); - assert_eq!( - normal_form_inv, - rand_uint_monty_inv.retrieve(), - "a*a⁻¹ ≠ 1 (normal form inverted)" - ); - - // …and agrees with the num_modular crate - assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), - num_modular_inv, - "num_modular ≠ crypto_bigint" - ) - } - } - - #[test] - fn inversion_random_uints_u4096() { - let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U4096::from_be_hex(concat!( - "07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - )); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); - let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - - for _ in 0..20 { - let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) = - random_invertible_u4096(monty_params, &mut rng); - let one_monty = rand_uint_monty * rand_uint_monty_inv; - - // Inversion works in monty form - assert_eq!( - one_monty, - MontyForm::one(monty_params), - "a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}", - modulus - ); - // …and in normal form - assert_eq!(one_monty.retrieve(), U4096::ONE, "a*a⁻¹ ≠ 1 (normal form)"); - - // …and when converted back to normal form and used in a widening operation - let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint); - assert_eq!( - one % wide_modulus, - U8192::ONE, - "a*a⁻¹ ≠ 1 (normal form, wide)" - ); - - // …and agrees with normal form inversion - let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap(); - assert_eq!( - normal_form_inv, - rand_uint_monty_inv.retrieve(), - "a*a⁻¹ ≠ 1 (normal form inverted)" - ); - - // …and agrees with the num_modular crate - assert_eq!( - BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), - num_modular_inv, - "num_modular ≠ crypto_bigint" - ) - } - } - - // Creates random U1024s, inverts and sanity checks the result. - #[test] - fn invert_random_u1024s() { - let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U1024::from([ - 0xd69d42ede255db9d, - 0x20d23f0e7e55c2f3, - 0xb11955f4354ad01e, - 0xe1598344a83da132, - 0xa40162a5315c9b7f, - 0xa3e41cc5720f4dca, - 0x3a28db743e07f87b, - 0x717d2332171a2bd7, - 0x7f6917818473334e, - 0x7fc6c2ffd071667d, - 0x1bdb77d72f57ac49, - 0x3c857fff2fe7f7ee, - 0x33ca8e3a359428ad, - 0x12a442daca8af09d, - 0x872ac90b36ebd1bc, - 0x9aefced07fa13351, - ]); - - let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); - for _ in 0..100 { - let (r, rm, rmi, _) = random_invertible_u1024(monty_params, &mut rng); - let one_monty = rm * rmi; - assert_eq!(one_monty, MontyForm::one(monty_params)); - assert_eq!(one_monty.retrieve(), U1024::ONE); - - let one = rmi.retrieve().widening_mul(&r); - let wide_modulus = NonZero::new(Into::::into(&modulus)).unwrap(); - assert_eq!(one % wide_modulus, U2048::ONE); - - let ri = r.inv_mod(&modulus).unwrap(); - assert_eq!(ri, rmi.retrieve()); - } - } } From 12e55aa465614ee43131116ba0bc7fd97bed170b Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 6 Aug 2024 18:17:04 +0200 Subject: [PATCH 10/11] Don't assume limb count is the same on all platforms --- src/modular/monty_form/inv.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index f3588ed3..f3decba3 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -133,12 +133,25 @@ mod tests { // Seed used to ensure deterministc random sequences in tests. const RANDOM_SEED: [u8; 32] = [17; 32]; + #[cfg(target_pointer_width = "32")] + const LIMBS_1024: usize = 32; + #[cfg(target_pointer_width = "32")] + const LIMBS_2048: usize = 64; + #[cfg(target_pointer_width = "32")] + const LIMBS_4096: usize = 128; + + #[cfg(target_pointer_width = "64")] + pub const LIMBS_1024: usize = 16; + #[cfg(target_pointer_width = "64")] + pub const LIMBS_2048: usize = 32; + #[cfg(target_pointer_width = "64")] + pub const LIMBS_4096: usize = 64; // Generates a random `U1024` and returns it as a tuple in: normal form, montgomery form, // inverted montgomery form and the normal form inverse from the num_modular crate. fn random_invertible_u1024( - monty_params: MontyParams<16>, + monty_params: MontyParams, rng: &mut impl CryptoRngCore, - ) -> (U1024, MontyForm<16>, MontyForm<16>, BigUint) { + ) -> (U1024, MontyForm, MontyForm, BigUint) { let modulus = monty_params.modulus().to_nz().unwrap(); loop { let r = Uint::random_mod(rng, &modulus); @@ -160,9 +173,9 @@ mod tests { // Generates a random `U2048` and returns it as a tuple in: normal form, montgomery form, // inverted montgomery form and the normal form inverse from the num_modular crate. fn random_invertible_u2048( - monty_params: MontyParams<32>, + monty_params: MontyParams, rng: &mut impl CryptoRngCore, - ) -> (U2048, MontyForm<32>, MontyForm<32>, BigUint) { + ) -> (U2048, MontyForm, MontyForm, BigUint) { let modulus = monty_params.modulus().to_nz().unwrap(); loop { let r = Uint::random_mod(rng, &modulus); @@ -185,9 +198,9 @@ mod tests { // Generates a random `U4096` and returns it as a tuple in: normal form, montgomery form, // inverted montgomery form and the normal form inverse from the num_modular crate. fn random_invertible_u4096( - monty_params: MontyParams<64>, + monty_params: MontyParams, rng: &mut impl CryptoRngCore, - ) -> (U4096, MontyForm<64>, MontyForm<64>, BigUint) { + ) -> (U4096, MontyForm, MontyForm, BigUint) { let modulus = monty_params.modulus().to_nz().unwrap(); loop { let r = Uint::random_mod(rng, &modulus); From bc1093bd964fb71ba61749db9c554392844af5d8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 6 Aug 2024 18:42:00 +0200 Subject: [PATCH 11/11] Prefer from_be_hex --- src/modular/monty_form/inv.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/modular/monty_form/inv.rs b/src/modular/monty_form/inv.rs index f3decba3..0ec3c3d6 100644 --- a/src/modular/monty_form/inv.rs +++ b/src/modular/monty_form/inv.rs @@ -397,25 +397,7 @@ mod tests { #[test] fn invert_random_u1024s() { let mut rng = ChaChaRng::from_seed(RANDOM_SEED); - let modulus = U1024::from([ - 0xd69d42ede255db9d, - 0x20d23f0e7e55c2f3, - 0xb11955f4354ad01e, - 0xe1598344a83da132, - 0xa40162a5315c9b7f, - 0xa3e41cc5720f4dca, - 0x3a28db743e07f87b, - 0x717d2332171a2bd7, - 0x7f6917818473334e, - 0x7fc6c2ffd071667d, - 0x1bdb77d72f57ac49, - 0x3c857fff2fe7f7ee, - 0x33ca8e3a359428ad, - 0x12a442daca8af09d, - 0x872ac90b36ebd1bc, - 0x9aefced07fa13351, - ]); - + let modulus = U1024::from_be_hex("d69d42ede255db9d20d23f0e7e55c2f3b11955f4354ad01ee1598344a83da132a40162a5315c9b7fa3e41cc5720f4dca3a28db743e07f87b717d2332171a2bd77f6917818473334e7fc6c2ffd071667d1bdb77d72f57ac493c857fff2fe7f7ee33ca8e3a359428ad12a442daca8af09d872ac90b36ebd1bc9aefced07fa13351"); let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap()); for _ in 0..100 { let (r, rm, rmi, _) = random_invertible_u1024(monty_params, &mut rng);