From 4dd2597623a04d3180c39205785886f062d24ca5 Mon Sep 17 00:00:00 2001 From: Quentin Bourgerie Date: Tue, 20 Dec 2022 11:29:02 +0100 Subject: [PATCH] refactor: change directory layout, add arguments to python scripts and instrument dependencies in Makefile --- Makefile | 47 ++++++++++-- .../include/concrete/curves.gen.h | 5 -- .../src/curves.gen.rs | 5 -- concrete-security-curves-rust/src/lib.rs | 2 +- data/v0.1.sobj | Bin 27238 -> 0 bytes data/v0.2/112.sobj | Bin 708 -> 0 bytes data/v0.2/128.sobj | Bin 703 -> 0 bytes data/v0.2/144.sobj | Bin 710 -> 0 bytes data/v0.2/160.sobj | Bin 721 -> 0 bytes data/v0.2/176.sobj | Bin 697 -> 0 bytes data/v0.2/192.sobj | Bin 699 -> 0 bytes data/v0.2/256.sobj | Bin 726 -> 0 bytes data/v0.2/80.sobj | Bin 715 -> 0 bytes data/v0.2/96.sobj | Bin 716 -> 0 bytes data/v0.sobj | Bin 7733 -> 0 bytes job.sh | 69 ------------------ json/curves.json | 2 +- .../compare_curves_and_estimator.py | 33 ++++++++- .../generate_data.py | 51 +++++++++++-- lattice-scripts/generate_data.sh | 42 +++++++++++ .../verify_curves.py | 63 +++++++++++----- 112.sobj => sage-object/112.sobj | Bin 128.sobj => sage-object/128.sobj | Bin 144.sobj => sage-object/144.sobj | Bin 160.sobj => sage-object/160.sobj | Bin 176.sobj => sage-object/176.sobj | Bin 192.sobj => sage-object/192.sobj | Bin 256.sobj => sage-object/256.sobj | Bin 80.sobj => sage-object/80.sobj | Bin 96.sobj => sage-object/96.sobj | Bin sage-object/outdated_curves.timestamp | 0 sage-object/verified_curves.sobj | Bin 0 -> 479 bytes verified_curves.sobj | 2 - 33 files changed, 203 insertions(+), 118 deletions(-) delete mode 100644 data/v0.1.sobj delete mode 100644 data/v0.2/112.sobj delete mode 100644 data/v0.2/128.sobj delete mode 100644 data/v0.2/144.sobj delete mode 100644 data/v0.2/160.sobj delete mode 100644 data/v0.2/176.sobj delete mode 100644 data/v0.2/192.sobj delete mode 100644 data/v0.2/256.sobj delete mode 100644 data/v0.2/80.sobj delete mode 100644 data/v0.2/96.sobj delete mode 100644 data/v0.sobj delete mode 100755 job.sh rename compare_curves_and_estimator.py => lattice-scripts/compare_curves_and_estimator.py (79%) rename generate_data.py => lattice-scripts/generate_data.py (87%) create mode 100755 lattice-scripts/generate_data.sh rename verify_curves.py => lattice-scripts/verify_curves.py (55%) rename 112.sobj => sage-object/112.sobj (100%) rename 128.sobj => sage-object/128.sobj (100%) rename 144.sobj => sage-object/144.sobj (100%) rename 160.sobj => sage-object/160.sobj (100%) rename 176.sobj => sage-object/176.sobj (100%) rename 192.sobj => sage-object/192.sobj (100%) rename 256.sobj => sage-object/256.sobj (100%) rename 80.sobj => sage-object/80.sobj (100%) rename 96.sobj => sage-object/96.sobj (100%) create mode 100644 sage-object/outdated_curves.timestamp create mode 100644 sage-object/verified_curves.sobj delete mode 100644 verified_curves.sobj diff --git a/Makefile b/Makefile index 93e4799cf..241bcdc6d 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,55 @@ +SECURITY_LEVELS=80 112 128 192 +SAGE_OBJECT_DIR=sage-object +SAGE_SECURITY_CURVES=$(SECURITY_LEVELS:%=$(SAGE_OBJECT_DIR)/%.sobj) +SAGE_VERIFED_CURVES=$(CURVES_SAGE_OBJECT_DIR)/verified_curves.sobj CURVES_JSON_PATH=json/curves.json CURVES_CPP_GEN_H=concrete-security-curves-cpp/include/concrete/curves.gen.h CURVES_RUST_GEN_TXT=concrete-security-curves-rust/src/curves.gen.rs -$(CURVES_JSON_PATH): verify_curves.py - sage verify_curves.py > $@ +generate-code: generate-cpp generate-rust + +# Generate CPP ######################## $(CURVES_CPP_GEN_H): concrete-security-curves-cpp/gen_header.py $(CURVES_JSON_PATH) cat $(CURVES_JSON_PATH) | python concrete-security-curves-cpp/gen_header.py > $(CURVES_CPP_GEN_H) generate-cpp: $(CURVES_CPP_GEN_H) -$(CURVES_RUST_GEN_TXT): rust/gen_table.py $(CURVES_JSON_PATH) - cat $(CURVES_JSON_PATH) | python concrete-security-curves-cpp/gen_table.py > $(CURVES_CPP_GEN_H) +# Generate RUST ######################## + +$(CURVES_RUST_GEN_TXT): concrete-security-curves-rust/gen_table.py $(CURVES_JSON_PATH) + cat $(CURVES_JSON_PATH) | python concrete-security-curves-rust/gen_table.py > $(CURVES_RUST_GEN_TXT) generate-rust: $(CURVES_RUST_GEN_TXT) -.PHONY: generate-cpp-header \ No newline at end of file +# Compare curves ####################### + +$(SAGE_OBJECT_DIR)/outdated_curves.timestamp: ./lattice-scripts/compare_curves_and_estimator.py + python ./lattice-scripts/compare_curves_and_estimator.py \ + --curves-dir $(SAGE_OBJECT_DIR) --security-levels $(SECURITY_LEVELS) --log-q 64 \ + || touch $(SAGE_OBJECT_DIR)/outdated_curves.timestamp + +compare-curves: $(SAGE_OBJECT_DIR)/outdated_curves.timestamp + +# Generate curves ###################### + +$(SAGE_OBJECT_DIR)/%.sobj: $(SAGE_OBJECT_DIR)/outdated_curves.timestamp ./lattice-scripts/generate_data.sh ./lattice-scripts/generate_data.py + PYTHONPATH=$(PWD)/lattice-estimator ./lattice-scripts/generate_data.sh \ + $* --output $(SAGE_OBJECT_DIR) --old-models $(SAGE_OBJECT_DIR)/verified_curves.sobj + +generate-curves: $(SAGE_SECURITY_CURVES) + +# Verify curves ####################### + +$(CURVES_JSON_PATH): ./lattice-scripts/verify_curves.py $(SAGE_SECURITY_CURVES) + python ./lattice-scripts/verify_curves.py \ + --curves-dir $(SAGE_OBJECT_DIR) --security-levels $(SECURITY_LEVELS) --log-q 64 > $(CURVES_JSON_PATH) + +verify-curves: $(CURVES_JSON_PATH) + +.PHONY: generate-cpp \ + generate-rust \ + generate-code \ + compare-curves \ + generate-curves \ + verify-curves diff --git a/concrete-security-curves-cpp/include/concrete/curves.gen.h b/concrete-security-curves-cpp/include/concrete/curves.gen.h index 05ff21944..e33c5d3f5 100644 --- a/concrete-security-curves-cpp/include/concrete/curves.gen.h +++ b/concrete-security-curves-cpp/include/concrete/curves.gen.h @@ -1,12 +1,7 @@ std::vector curves { SecurityCurve(80,-0.0404263311936459, 1.660978864143658, 450, KeyFormat::BINARY), - SecurityCurve(96,-0.03414780360867054, 2.0173102586603733, 450, KeyFormat::BINARY), SecurityCurve(112,-0.02967013708113588, 2.16246371408387, 450, KeyFormat::BINARY), SecurityCurve(128,-0.026405028765226296, 2.482642269104389, 450, KeyFormat::BINARY), - SecurityCurve(144,-0.023821437305989134, 2.7177789440636673, 450, KeyFormat::BINARY), - SecurityCurve(160,-0.021743582187160406, 2.9388105484933504, 498, KeyFormat::BINARY), - SecurityCurve(176,-0.019904056582117705, 2.8161252801542673, 551, KeyFormat::BINARY), SecurityCurve(192,-0.018610403247590064, 3.2996236848399008, 606, KeyFormat::BINARY), - SecurityCurve(256,-0.014606812351714961, 3.8493629234693145, 826, KeyFormat::BINARY), } diff --git a/concrete-security-curves-rust/src/curves.gen.rs b/concrete-security-curves-rust/src/curves.gen.rs index faca91268..d95a357b6 100644 --- a/concrete-security-curves-rust/src/curves.gen.rs +++ b/concrete-security-curves-rust/src/curves.gen.rs @@ -1,11 +1,6 @@ [ (80, SecurityWeights { slope: -0.0404263311936459, bias: 1.660978864143658, minimal_lwe_dimension: 450 }), - (96, SecurityWeights { slope: -0.03414780360867054, bias: 2.0173102586603733, minimal_lwe_dimension: 450 }), (112, SecurityWeights { slope: -0.02967013708113588, bias: 2.16246371408387, minimal_lwe_dimension: 450 }), (128, SecurityWeights { slope: -0.026405028765226296, bias: 2.482642269104389, minimal_lwe_dimension: 450 }), - (144, SecurityWeights { slope: -0.023821437305989134, bias: 2.7177789440636673, minimal_lwe_dimension: 450 }), - (160, SecurityWeights { slope: -0.021743582187160406, bias: 2.9388105484933504, minimal_lwe_dimension: 498 }), - (176, SecurityWeights { slope: -0.019904056582117705, bias: 2.8161252801542673, minimal_lwe_dimension: 551 }), (192, SecurityWeights { slope: -0.018610403247590064, bias: 3.2996236848399008, minimal_lwe_dimension: 606 }), - (256, SecurityWeights { slope: -0.014606812351714961, bias: 3.8493629234693145, minimal_lwe_dimension: 826 }), ] diff --git a/concrete-security-curves-rust/src/lib.rs b/concrete-security-curves-rust/src/lib.rs index 75890f2e7..d0c941f17 100644 --- a/concrete-security-curves-rust/src/lib.rs +++ b/concrete-security-curves-rust/src/lib.rs @@ -1,4 +1,4 @@ -const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 9] = include!("./curves.gen.rs"); +const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 4] = include!("./curves.gen.rs"); #[derive(Clone, Copy)] pub struct SecurityWeights { diff --git a/data/v0.1.sobj b/data/v0.1.sobj deleted file mode 100644 index 6b6d685537242dc56c799445986da537e33b3477..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27238 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 diff --git a/data/v0.2/112.sobj b/data/v0.2/112.sobj deleted file mode 100644 index a50b2f74cb6a1101814de0686621aa684bfb6383..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 708 zcmV;#0z3V9oMqKJOp{R*2k^dVO96QmNEu=dAaJW1yE>w3SqegC1znC8E{Y%ITkm%R%R~=n>KI z+W8^B4JSZXwdfX6=0t3+8Lj}0Xps_8pLLhvN>H5^5YcUSTLt!#~6?YjEa^ux=)M-?B2soQEugFxIT7(KXN39;w}O5}lsG8Do1ivOt0Xi57=;PJeqD?gZV|;;@J@Tcr!cTI5A!odH^UWOxrKrNxYhSi-&hhPy!vzKm5* zc=NFr6!B$<@J_!6^fj!+5vdZkH>wwOPm5U*m2lf`I0bs;%aAJ7MVA=f=S+|wvpOB2O?N~qPQ%H$<$?*EIAJnWf=0w!kdpiJn=F5;OjrQaQL4UNE z6H(=EW!i8WbW4kZRH?HQbO3Zxiv)hF)dDrn6+~{>D&gNkSas=Y;&M_El4uFH)!hea{3=EpN(6?#!q_v9LJ$}o(JLq2O^#W;>q!Ofas~{Au>j6v_PYU7FLXI zg(0~_(Gp9c#ZoN|oe3r;CfaCWV?$vDi4{gW_sB3iFK???{PLgO*_~GjFOqjF;(1=7 zzeu6m6b8U&b>-4^Z+DeT-rmPuZ|z57f(%6fi&`WDA_{m%i@1o;)>3x;hM^k3lon|b zH5yR~E}4){omPPfw?Tc+Dk9pEP|T1BMnHg-N5 zN&wDi(Iq0zr>zIfX^|6=WU&wMZLJc$B4Q7!%g}zne_DX6vio7E0q}(ud3W0RY8gWZ z0KaR|Bce7jVU3|AV51iOBEp;Hcj+gF4g%iOqEAHhi(Ow0r2v1&lo)Us)glQ)hXBWQ zMnOcx?sNXNp)}xUoiXU{)^1~A(@-PenifOu=D4R#fCVjzfqonYT-0LN^}}B6#MhwB67Nz`qflA)=n& zsr?sz(_+%~gS*}h_{JwBBhG)A9y<+MT$YS9zp;~m&$YM|xVc_Cx4sOiviFxdW#`tH zA)bG{4_8SYrWohU7?NR?*3 z+W|Y-J|P)x{Kf|D%!QPg7SYasx*zvnUC?UHEB6!9mrh^+Z0JefrmZ_ ziolJ_C?bel1Yv7eK~fP^1VN9jYZpOa6a)tCy4OC>cP{r^UARB~=X~eh?_18l!;$mR zNF>si$#C8U&If2Jp39z#?5^C}V zxCHcCi$*6F{8kOeK|@-!i74_Sa_fdmL1kKWxQzJZ(%PosGSJH&gqM z)q*B;#*mwb{l`+EH#%c9wA(sRmChJ;RqSb~2R+moW1-zPfU;UlxQW?6xe;_#i*ff| z?1?%6s?lOHG>Q1u{Z? zcz5B97AK`jrJYA7s4*ZUBVlitF3^*x5;KxfZU3>upx;`Y60vX5UxvFuCXgXj>TDlJ zKpz7cB9ivo_JHQJ$Vru2+v!ozPQDVeBI^B1>bBut(5HZqDk*#R`#?>BDxp65K~HtY zX{nO7yBz@C3<$|+u-`TVYSm&+MB3h#gCGhBsnYCU4xNUFK!5X;I3uFPp4(y2*MJD^ sWd!tGi?fpP|H1Gm=w(1i#)v%)W1tN!NHW^}HYN>^U%t`v4?}dGeB{wu#sB~S diff --git a/data/v0.2/160.sobj b/data/v0.2/160.sobj deleted file mode 100644 index 005181dd19b8e9006bb9819bc629ef6f904c4d7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 721 zcmV;?0xtb{oMqHKOjA)52k=KJPzp$af>f~O+dma+`Ti2Aq7*+)B09)`iGwCCCY*R( zG$zCZ2bYdU9UNqGAPx>B#s%YGB8!Wg2`(mLjM1Qji?7Y?dFQ2Xddd6cf9^f!-nSaK z$Ieo@<2Zw3X)e3M0f6RfvY87`;mKwq#g}V=o$N;RuHkaf3oRTG_2HS7=bsD*L9JRu zMN~g_pC^VZKt_v@h~NkJ**07W`W9BARYZfkj5|Aqt3Yd7bcm>Qzh^fLhd{5jXcrM# z++B-r8mtg9j(}ci0<@&XtS9XG=m2e3C~-nW z@n&~|k~$+JqQQ2(3-rvFv04=S?5*qu{nH{NqS&z>5NmN#sx;g4(FjR*Vpu`!e(rafs40_`el99B3+!4@KEzXMQw(A%L<$OY_z;(2p`D&X@Fb67?ftv+J0?ek=7K`1X}O DskB#( diff --git a/data/v0.2/176.sobj b/data/v0.2/176.sobj deleted file mode 100644 index cfc0d59717c40672795f51eb20df0c2299662bab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 697 zcmV;q0!ICKoMqHGXjD-Y2H=;OEHjfmnQUe;%Ov?H%b0yJ#*AAg8V@8EA{Yw`)7a#a zD@Y^|5Wz-L*r;F?MX=CH5F0_U5ydi%O>8VoC8&*s_v!Jx^O#$`;^Ulq|9jruifkJ1 zLe%rT;mHY8aLGgfnlH;`m%P6#m#zB$v0Ac%A8)p;iGrSL;fW~tcB}2>Ico|*NiC8h zDigD7JG<5tfv#&26A|0lP0Si=ia~p&N;Hb7SoN1RC7^3sw2LTpCDtBU69YZbqD4g1 zZFG6xnmA}ii%t<9?;`<a}h@v=|mqzUD_}P14PW7Gpt;QGZ!e2l^6IVq8Scwj0B@*3^R*wU`R>;5lsoJ=bC) zFVjX)lNQs#X?ez)K$}`jil|!lFV~s_psyuL91c$FLdQ>Qnn5?UI3l8!6Kesz&|*eJ z@`GC+t!V`1?hTKpkgv8E4nS&Mn80p4jp=$#g)M0EOr-LPf= zlnK?4F0FhogP^;i8X`LQv>DLvs1iBp(#3D#A<#W77DV*&-421?hiU{}_@x^Lt%hod z805r8Kx0~*mM;B#x1*p)NJxz#e#gc@4L4iUMP@sHsz*>P83IzletfD6o6BA>KgNeqG8?Q?f z5)$L&z=}E=T}+IL3!^ftiIamaj7C?PJ&&AsdS2d}I{flKx99!u+YRlQz@2a)5SU(= zF~Kbp0>HI&b@gW8MC1!SU#&?4X0*tQ2-86~|60=m z_$#GEK}1v0d8}y#tm=%(#uVqT)U0U(tmurH#+!3qt+6Hp_*RQ4-`3j!BU%(287Dui z=>Xi;Vp>G<1wAjV$pU^$Dp6{@xsx&0bOP>cG3(pa740P{MdETXx7RDGYd<}~1Qolz0d&Ko-enA2iOL>u3iUg}s}iDeOP3;O_X z>x@Ma89GJ%$eMm?q0YDqZVqhBBGP`F#z~8ro=_xe;K5X>5NqoS?<^nHOP~( z>xv$J`@__APlkvN-uehNNM~G07w3#hOeR=95lP$2>wZ2Kfs!3HT|Z#1+XH<*lEkj_Hhb5y1a8PhHm;>oR4Gzn13! zpL(WR4Ebt1@MdGtL8|&ZvrTzd=)g?>reYCBnC18nDZgAtJ~1e5N;qN7EOkt0Y{--)KIt6b8=Y}oM2SV2TIdnJDf57L hwYVV}?!8?Ata*g59~HoRTA=S7iw_Oo(k3W^7Pe~9dvfR8^Xje!=Ewisd+xb!vF5IF zE`}Y)=^rer&}CHvP^_U+KI?3+N;y;gF&|ogIEUX_g+Y&{a9G5ba%<;)XcYmy$q12U z5ncAy6Ro148JUr=RlJDrOIpQ1T4pq|aJ>I>Yg)xYU(-UgvIwtc7d9ugN`QK0Ml*}Z z!e(VPt5q%Nsm#b*fi3U6TDhQ0QncAB8*XWSMXMwzD@D5%rXMNLM7jc8R53Q5spGfMN%He9XJp!-sE+6>nxT4g}5>V)XE_u{woyjBgMf)xEM zYAb$Iwb}u?Ek&Od=5BX_ex`&dut@kV?a(RLLsDq$iY{fSAz87b4TPEfZL6E?%l*b&f6 zDbBD+n$vcHzC~q*tzxFI8+0>}!NN7S-ve5a85ORQF)!*Us7Z>+|9097x)R9XDrqx? zeW1?~nPGQoUUom|krY$*wB{>S0L=!(;=lVC0Ii3Gm}XI(!a-16W=wOH)b|kvB6o0vZYk&d8W5qo7eKh%<6oe}uFeyLPMR IFS(kTRoJFu0RR91 diff --git a/data/v0.2/80.sobj b/data/v0.2/80.sobj deleted file mode 100644 index 14697f236394e52974d679fb352c69ec2bccc243..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 715 zcmV;+0yO=2oMqKJOjJP>2H@QV7FZDxmiKE}0e=vX_tQlLagp^TVkp#LW6;io15=?U zG&E|Yve3dpv89!m*r>76P^rYm(3xm778qmYy)b+3x$acA`0~%pnKN@&Lbo|^F%$>{ z20J-;g+l=S$VrZ!5A2QPSW)_8HkjOqF7Fu50@a6=2#CmD-f@iKY|sat5f!ojVK@gA z)){%;EZRy@?heBTKtozId%J!5DUx8r1)!hVO01hzBG5gZ(Ji6|yRX*f3?Bx))S}Z{sm&+`EospsqAIcA zti*5$=$RH>8M}>wE^E=7aW18xaV`2pl)FthWw;FVCrgPz@APe-mV;V!M!$$sXRr8e zxB~P=XAEWRwh~mNGX_MII7MGrH(UjJt22hZn%X|C20hT?gow(A&Uj?F2DG8Yh=^*Z z8Z+Mw*Me?ok?`sfam#MF4m7XDsE9_}LG_@N7AHl-ZLh~bK`l;Y?4<$Jt;Lv#*xrZX zIOuCgXRM?J?AaaxRr)eS)Vd9nFx&{*(i!72OOrjhCeULoCcM+PcfT3*+?OG3Y?RK{ePz6q5ps+q?=;GIDLH;~%can2uc3UYY;^ diff --git a/data/v0.2/96.sobj b/data/v0.2/96.sobj deleted file mode 100644 index aa776fb427106c3aa424f775f03bf23309de3195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 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 diff --git a/job.sh b/job.sh deleted file mode 100755 index cfe8c2375..000000000 --- a/job.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh -# 80-bits -sage generate_data.py 80 2 12 0 -sage generate_data.py 80 12 22 0 -sage generate_data.py 80 22 32 0 -sage generate_data.py 80 32 42 0 -sage generate_data.py 80 42 52 0 -sage generate_data.py 80 52 59 0 -# 112-bits -sage generate_data.py 112 2 12 0 -sage generate_data.py 112 12 22 0 -sage generate_data.py 112 22 32 0 -sage generate_data.py 112 32 42 0 -sage generate_data.py 112 42 52 0 -sage generate_data.py 112 52 59 0 -# 128-bits -sage generate_data.py 128 2 12 0 -sage generate_data.py 128 12 22 0 -sage generate_data.py 128 22 32 0 -sage generate_data.py 128 32 42 0 -sage generate_data.py 128 42 52 0 -sage generate_data.py 128 52 59 0 -# 192-bits -sage generate_data.py 192 2 12 0 -sage generate_data.py 192 12 22 0 -sage generate_data.py 192 22 32 0 -sage generate_data.py 192 32 42 0 -sage generate_data.py 192 42 52 0 -sage generate_data.py 192 52 59 0 - - - -# 96-bits -#sage generate_data.py 96 2 12 0 -#sage generate_data.py 96 12 22 0 -#sage generate_data.py 96 22 32 0 -#sage generate_data.py 96 32 42 0 -#sage generate_data.py 96 42 52 0 -#sage generate_data.py 96 52 59 0 -# 144-bits -#sage generate_data.py 144 2 12 0 -#sage generate_data.py 144 12 22 0 -#sage generate_data.py 144 22 32 0 -#sage generate_data.py 144 32 42 0 -#sage generate_data.py 144 42 52 0 -#sage generate_data.py 144 52 59 0 -# 160-bits -#sage generate_data.py 160 2 12 0 -#sage generate_data.py 160 12 22 0 -#sage generate_data.py 160 22 32 0 -#sage generate_data.py 160 32 42 0 -#sage generate_data.py 160 42 52 0 -#sage generate_data.py 160 52 59 0 -# 176-bits -#sage generate_data.py 176 2 12 0 -#sage generate_data.py 176 12 22 0 -#sage generate_data.py 176 22 32 0 -#sage generate_data.py 176 32 42 0 -#sage generate_data.py 176 42 52 0 -#sage generate_data.py 176 52 59 0 -# 256-bits -#sage generate_data.py 256 2 12 0 -#sage generate_data.py 256 12 22 0 -#sage generate_data.py 256 22 32 0 -#sage generate_data.py 256 32 42 0 -#sage generate_data.py 256 42 52 0 -#sage generate_data.py 256 52 59 0 - - diff --git a/json/curves.json b/json/curves.json index 4c621f7ac..ea5bc7cc4 100644 --- a/json/curves.json +++ b/json/curves.json @@ -1 +1 @@ -[{"slope": -0.0404263311936459, "bias": 1.660978864143658, "security_level": 80, "minimal_lwe_dimension": 450}, {"slope": -0.03414780360867054, "bias": 2.0173102586603733, "security_level": 96, "minimal_lwe_dimension": 450}, {"slope": -0.02967013708113588, "bias": 2.16246371408387, "security_level": 112, "minimal_lwe_dimension": 450}, {"slope": -0.026405028765226296, "bias": 2.482642269104389, "security_level": 128, "minimal_lwe_dimension": 450}, {"slope": -0.023821437305989134, "bias": 2.7177789440636673, "security_level": 144, "minimal_lwe_dimension": 450}, {"slope": -0.021743582187160406, "bias": 2.9388105484933504, "security_level": 160, "minimal_lwe_dimension": 498}, {"slope": -0.019904056582117705, "bias": 2.8161252801542673, "security_level": 176, "minimal_lwe_dimension": 551}, {"slope": -0.018610403247590064, "bias": 3.2996236848399008, "security_level": 192, "minimal_lwe_dimension": 606}, {"slope": -0.014606812351714961, "bias": 3.8493629234693145, "security_level": 256, "minimal_lwe_dimension": 826}] +[{"slope": -0.0404263311936459, "bias": 1.660978864143658, "security_level": 80, "minimal_lwe_dimension": 450}, {"slope": -0.02967013708113588, "bias": 2.16246371408387, "security_level": 112, "minimal_lwe_dimension": 450}, {"slope": -0.026405028765226296, "bias": 2.482642269104389, "security_level": 128, "minimal_lwe_dimension": 450}, {"slope": -0.018610403247590064, "bias": 3.2996236848399008, "security_level": 192, "minimal_lwe_dimension": 606}] diff --git a/compare_curves_and_estimator.py b/lattice-scripts/compare_curves_and_estimator.py similarity index 79% rename from compare_curves_and_estimator.py rename to lattice-scripts/compare_curves_and_estimator.py index fd9f9bb6b..6e965f9f7 100644 --- a/compare_curves_and_estimator.py +++ b/lattice-scripts/compare_curves_and_estimator.py @@ -2,7 +2,9 @@ import sys sys.path.insert(1, 'lattice-estimator') from estimator import * from sage.all import oo, save, load, ceil, floor -from generate_data import estimate, get_security_level, old_models +from generate_data import estimate, get_security_level +import argparse +import os LOG_N_MAX = 17 + 1 @@ -67,7 +69,7 @@ def estimate_stddev_with_current_curve(curve, lwe_dimension, log_q): return stddev -def compare_curve_and_estimator(security_level, log_q): +def compare_curve_and_estimator(security_level, log_q, curves_dir): """ For a subset of every lwe dimension possibles, estimate the security of those lwe dimension associated with the stddev recommanded by our current curve. @@ -84,7 +86,7 @@ def compare_curve_and_estimator(security_level, log_q): print(f"Security Target: {security_level} bits") # step 0. loading the right curve - curves = load("verified_curves.sobj") + curves = load(os.path.join(curves_dir, "verified_curves.sobj")) j = get_index(security_level, curves) curve = curves[j] @@ -113,4 +115,27 @@ def compare_curve_and_estimator(security_level, log_q): return True if __name__ == "__main__": - compare_curve_and_estimator(128, 64) \ No newline at end of file + CLI = argparse.ArgumentParser() + CLI.add_argument( + "--curves-dir", + help="The directory where curves has been saved (sage object)", + type=str, + required=True, + ) + CLI.add_argument( + "--security-levels", + help="The security levels to verify", + nargs="+", + type=int, + required=True + ) + CLI.add_argument( + "--log-q", + type=int, + required=True + ) + args = CLI.parse_args() + for security_level in args.security_levels: + if not(compare_curve_and_estimator(security_level, args.log_q, args.curves_dir)): + exit(1) + exit(0) \ No newline at end of file diff --git a/generate_data.py b/lattice-scripts/generate_data.py similarity index 87% rename from generate_data.py rename to lattice-scripts/generate_data.py index 808e1af58..735213300 100644 --- a/generate_data.py +++ b/lattice-scripts/generate_data.py @@ -1,10 +1,13 @@ import sys -sys.path.insert(1, '../lattice-estimator') +sys.path.insert(1, 'lattice-estimator') from estimator import * from sage.all import oo, save, load, ceil from math import log2 import multiprocessing +import argparse +import os +old_models_sobj = "" def old_models(security_level, sd, logq=32): """ @@ -22,7 +25,10 @@ def old_models(security_level, sd, logq=32): if curves[i][2] == sec: return i - curves = load("verified_curves.sobj") + if old_models_sobj is None or not(os.path.exists(old_models_sobj)): + return 450 + + curves = load(old_models_sobj) j = get_index(security_level, curves) a = curves[j][0] @@ -189,11 +195,44 @@ def generate_zama_curves64( return "done" if __name__ == "__main__": + CLI = argparse.ArgumentParser() + CLI.add_argument( + "--security-level", + type=int, + required=True, + ) + CLI.add_argument( + "--output", + type=str, + required=True, + ) + CLI.add_argument( + "--old-models", + type=str, + ) + CLI.add_argument( + "--sd-min", + type=int, + required=True, + ) + CLI.add_argument( + "--sd-max", + type=int, + required=True, + ) + CLI.add_argument( + "--margin", + type=int, + default=0, + ) + args = CLI.parse_args() # The script runs the following commands # grab values of the command-line input arguments - security = int(sys.argv[1]) - sd_min = int(sys.argv[2]) - sd_max = int(sys.argv[3]) - margin = int(sys.argv[4]) + security = args.security_level + sd_min = args.sd_min + sd_max = args.sd_max + margin = args.margin + output = args.output + old_models_sobj = args.old_models # run the code generate_zama_curves64(sd_range=(sd_min, sd_max), target_security_levels=[security + margin], name="security_{}_margin_{} ".format(security, margin)) diff --git a/lattice-scripts/generate_data.sh b/lattice-scripts/generate_data.sh new file mode 100755 index 000000000..81640299e --- /dev/null +++ b/lattice-scripts/generate_data.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +set -e + +output_dir="" +old_models="" +while : +do + case $1 in + --help) + echo "generate_data.sh -o [output_dir] [security_levels]" + exit 2 + ;; + --output) + output_dir="$2" + shift 2 + ;; + --old-models) + old_models="$2" + shift 2 + ;; + --) + break; + ;; + "") + break + ;; + *) + security_levels="$security_levels $1" + shift; + ;; + esac +done + +for security_level in $security_levels; do + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 2 --sd-max 12 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 12 --sd-max 22 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 22 --sd-max 32 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 32 --sd-max 42 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 42 --sd-max 52 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 52 --sd-max 59 --margin 0 +done diff --git a/verify_curves.py b/lattice-scripts/verify_curves.py similarity index 55% rename from verify_curves.py rename to lattice-scripts/verify_curves.py index f6dd7e233..85af038c2 100644 --- a/verify_curves.py +++ b/lattice-scripts/verify_curves.py @@ -1,13 +1,15 @@ import numpy as np from sage.all import save, load, ceil import json +import os +import argparse -def sort_data(security_level): +def sort_data(security_level, curves_dir): from operator import itemgetter # step 1. load the data - X = load("{}.sobj".format(security_level)) + X = load(os.path.join(curves_dir, f"{security_level}.sobj")) # step 2. sort by SD x = sorted(X["{}".format(security_level)], key=itemgetter(2)) @@ -18,10 +20,10 @@ def sort_data(security_level): return X -def generate_curve(security_level): +def generate_curve(security_level, curves_dir): # step 1. get the data - X = sort_data(security_level) + X = sort_data(security_level, curves_dir) # step 2. group the n and sigma data into lists N = [] @@ -36,10 +38,10 @@ def generate_curve(security_level): return a, b -def verify_curve(security_level, a=None, b=None): +def verify_curve(security_level, a, b, curves_dir): # step 1. get the table and max values of n, sd - X = sort_data(security_level) + X = sort_data(security_level, curves_dir) n_max = X["{}".format(security_level)][0][0] # step 2. a function to get model values @@ -76,34 +78,55 @@ def verify_curve(security_level, a=None, b=None): return True, n_min -def generate_and_verify(security_levels, log_q, name="verified_curves"): - +def generate_and_verify(security_levels, log_q, curves_dir, name="verified_curves.sobj"): success = [] + json = [] fail = [] for sec in security_levels: #print("WE GO FOR {}".format(sec)) # generate the model for security level sec - (a_sec, b_sec) = generate_curve(sec) + (a_sec, b_sec) = generate_curve(sec, curves_dir) # verify the model for security level sec - (status, n_alpha) = verify_curve(sec, a_sec, b_sec) + (status, n_alpha) = verify_curve(sec, a_sec, b_sec, curves_dir) # append the information into a list - x = {"slope": a_sec, "bias": b_sec - log_q, "security_level": sec, "minimal_lwe_dimension": n_alpha} if status: - success.append(x) + json.append({"slope": a_sec, "bias": b_sec - log_q, "security_level": sec, "minimal_lwe_dimension": n_alpha}) + success.append((a_sec, b_sec - log_q, sec, a_sec, b_sec)) else: fail.append(x) - save(success, "{}.sobj".format(name)) + save(success, os.path.join(curves_dir, name)) - return success, fail + return json, fail -(success, fail) = generate_and_verify([80, 96, 112, 128, 144, 160, 176, 192, 256], log_q=64) -if (fail): - print("FAILURE: Fail to verify the following curves") - print(json.dumps(fail)) - exit(1) +if __name__ == "__main__": + CLI = argparse.ArgumentParser() + CLI.add_argument( + "--curves-dir", + help="The directory where curves has been saved (sage object)", + type=str, + required=True, + ) + CLI.add_argument( + "--security-levels", + help="The security levels to verify", + nargs="+", + type=int, + required=True + ) + CLI.add_argument( + "--log-q", + type=int, + required=True + ) + args = CLI.parse_args() + (success, fail) = generate_and_verify(args.security_levels, log_q=args.log_q, curves_dir=args.curves_dir) + if (fail): + print("FAILURE: Fail to verify the following curves") + print(json.dumps(fail)) + exit(1) -print(json.dumps(success)) + print(json.dumps(success)) diff --git a/112.sobj b/sage-object/112.sobj similarity index 100% rename from 112.sobj rename to sage-object/112.sobj diff --git a/128.sobj b/sage-object/128.sobj similarity index 100% rename from 128.sobj rename to sage-object/128.sobj diff --git a/144.sobj b/sage-object/144.sobj similarity index 100% rename from 144.sobj rename to sage-object/144.sobj diff --git a/160.sobj b/sage-object/160.sobj similarity index 100% rename from 160.sobj rename to sage-object/160.sobj diff --git a/176.sobj b/sage-object/176.sobj similarity index 100% rename from 176.sobj rename to sage-object/176.sobj diff --git a/192.sobj b/sage-object/192.sobj similarity index 100% rename from 192.sobj rename to sage-object/192.sobj diff --git a/256.sobj b/sage-object/256.sobj similarity index 100% rename from 256.sobj rename to sage-object/256.sobj diff --git a/80.sobj b/sage-object/80.sobj similarity index 100% rename from 80.sobj rename to sage-object/80.sobj diff --git a/96.sobj b/sage-object/96.sobj similarity index 100% rename from 96.sobj rename to sage-object/96.sobj diff --git a/sage-object/outdated_curves.timestamp b/sage-object/outdated_curves.timestamp new file mode 100644 index 000000000..e69de29bb diff --git a/sage-object/verified_curves.sobj b/sage-object/verified_curves.sobj new file mode 100644 index 0000000000000000000000000000000000000000..8048bb4c0f70d1a6c573d77ccc21a6d81acd9b10 GIT binary patch literal 479 zcmV<50U-W(oK4T$PZL2D2Jmfbaao|=6;ToI>xF6+0R^FW*Oi8xr@n|;mnDlSrC+-| zR1;G%j&~ESF(D9)ktco9_)=qnIrE3X=DQ9mr0`IN#BtGvSRnXttf3)ZZusM+h>AH6r0#a7L>5w;8L zAhd_rX|FO}Lq6dz+{nmzmzPwH4vlvab_?twbgse{1-ttAgB0Aj70Y6;R$YXB0{aOE zoKmnRTXo3>f9sKNe14(Q4{Cada9H37!47fMj(n({AIV=2{I{% literal 0 HcmV?d00001 diff --git a/verified_curves.sobj b/verified_curves.sobj deleted file mode 100644 index 331cd44ee..000000000 --- a/verified_curves.sobj +++ /dev/null @@ -1,2 +0,0 @@ -x]s QgF [}OۗږљiY,ԔQ*%ֲV`B)QGC0ë9{ow)tH;"tA(*$3ņx:4$ ~޳Yhog |1cT\pH4lUmlG4Gx,N8êVE3AR};1@` Z`aG]yvCܰ[@Up[K~jPWY x!19nFrKyIۏ'XK:Nad3 qC!P/Ǫ:M#00n[|%79`bM LDFӧM s?6ʻ6?T8P/!L/,[RǭA3n&alBdzsF|ՏH'0w$N&j"b%P%,+j- u`6 XEh0Ԭ.K SV%SW{חO oOkj=ؠ} 6;'ok2­f6VoK۸?|Cvh#v -".|o|>_ibV%XBԇ@ODͫ+^5wu*#.\c0ak]!. 8*JoG74KjK-țiYm$!%&d@P! No* \ No newline at end of file