From 3c9aa7b50355eecbcf2d328ffad80b0dbbdaf468 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:04:24 +0200 Subject: [PATCH 01/25] Update dependencies. Signed-off-by: Guiliano99 guilianolehmann@live.de Signed-off-by: Guiliano99 --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 86e31a6..b50d645 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,8 @@ dev = [ "pre-commit==4.1.0", "ruff==0.9.4", "nose2==0.15.1", + "pyasn1-alt-modules==0.4.6", + "pyasn1==0.6.1", ] lint = [ "mypy==1.14.1", From b0b6d25132cdf96fe3226a32afda4fb71a9daffe Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:06:35 +0200 Subject: [PATCH 02/25] Add Serialization utils. Signed-off-by: Guiliano99 --- oqs/serialize.py | 141 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 oqs/serialize.py diff --git a/oqs/serialize.py b/oqs/serialize.py new file mode 100644 index 0000000..7ea601d --- /dev/null +++ b/oqs/serialize.py @@ -0,0 +1,141 @@ +""" +Serialization and deserialization of stateful signature keys +using OneAsymmetricKey (PKCS#8) structure. +""" + +import logging +from pathlib import Path +from typing import Optional, Union + +from pyasn1.codec.der import encoder, decoder +from pyasn1.type import univ, tag + +import oqs +from pyasn1_alt_modules import rfc5958 + +_NAME_2_OIDS = { + "hss": "1.2.840.113549.1.9.16.3.17", # RFC 9708 + "xmss": "1.3.6.1.5.5.7.6.34", # RFC 9802 + "xmssmt": "1.3.6.1.5.5.7.6.35", # RFC 9802 +} +_OID_2_NAME = {v: k for k, v in _NAME_2_OIDS.items()} + +_KEY_DIR = Path(__file__).resolve().parent.parent / "data" / "xmss_xmssmt_keys" + + +def _get_oid_from_name(name: str) -> str: + """Get the OID corresponding to the stateful signature name.""" + if name.startswith("LMS"): + return _NAME_2_OIDS["hss"] + if name.startswith("XMSS-"): + return _NAME_2_OIDS["xmss"] + if name.startswith("XMSSMT-"): + return _NAME_2_OIDS["xmssmt"] + msg = f"Unsupported stateful signature name: {name}" + raise ValueError(msg) + + +def serialize_stateful_signature_key( + stateful_sig: oqs.StatefulSignature, public_key: bytes, fpath: str +) -> None: + """ + Serialize the stateful signature key to a `OneAsymmetricKey` structure. + + :param stateful_sig: The stateful signature object. + :param public_key: The public key bytes. + :param fpath: The file path to save the serialized key. + """ + one_asym_key = rfc5958.OneAsymmetricKey() + one_asym_key["version"] = 1 + one_asym_key["privateKeyAlgorithm"]["algorithm"] = univ.ObjectIdentifier( + _get_oid_from_name(stateful_sig.method_name.decode("utf-8")) + ) + one_asym_key["privateKey"] = stateful_sig.export_secret_key() + one_asym_key["publicKey"] = univ.BitString.fromOctetString(public_key).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) + der_data = encoder.encode(one_asym_key) + fpath_obj = Path(fpath) + with fpath_obj.open("wb") as f: + f.write(der_data) + logging.info("Wrote: %s", fpath_obj.name) + + +def deserialize_stateful_signature_key( + key_name: str, dir_name: Union[str, Path] = _KEY_DIR +) -> tuple[bytes, bytes]: + """ + Deserialize the stateful signature key from a `OneAsymmetricKey` structure. + + :param key_name: The file path to load the serialized key. + :param dir_name: The directory where the key files are stored. + :return: A tuple containing the method name, private key bytes, and public key bytes. + """ + key_name = key_name.replace("/", "_layers_", 1).lower() + fpath = Path(dir_name) / f"{key_name}.der" + + with fpath.open("rb") as f: + der_data = f.read() + one_asym_key = decoder.decode(der_data, asn1Spec=rfc5958.OneAsymmetricKey())[0] + oid = str(one_asym_key["privateKeyAlgorithm"]["algorithm"]) + # Accept any OID for supported families + if oid not in _OID_2_NAME and oid not in _NAME_2_OIDS.values(): + msg = f"Unsupported stateful signature OID: {oid}" + raise ValueError(msg) + + private_key_bytes = one_asym_key["privateKey"].asOctets() + public_key_bytes = one_asym_key["publicKey"].asOctets() + return private_key_bytes, public_key_bytes + + +def gen_or_load_stateful_signature_key( + key_name: str, dir_name: str = _KEY_DIR +) -> tuple[Optional[bytes], Optional[bytes]]: + """ + Generate or load a stateful signature key pair. + + :param key_name: The name of the stateful signature mechanism. + :param dir_name: The directory where the key files are stored. + :return: A tuple containing the stateful signature object and public key bytes. + """ + key_file_name = key_name.replace("/", "_layers_", 1).lower() + fpath = Path(dir_name) / f"{key_file_name}.der" + + if Path(fpath).exists(): + return deserialize_stateful_signature_key(key_file_name, dir_name=dir_name) + + # Check alternative path for test keys, to not generate them for every test run, + # to save time. + alt_path = Path(str(_KEY_DIR).replace("xmss_xmssmt_keys", "tmp_keys", 1)) + alt_fpath = alt_path / f"{key_file_name}.der" + if Path(alt_fpath).exists(): + return deserialize_stateful_signature_key(key_name, dir_name=alt_path) + + if key_name.startswith("XMSS-") and "_16_" in key_name: + Path(alt_path).mkdir(parents=True, exist_ok=True) + with oqs.StatefulSignature(key_name) as stfl_sig: + public_key_bytes = stfl_sig.generate_keypair() + serialize_stateful_signature_key(stfl_sig, public_key_bytes, alt_fpath) + serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) + return deserialize_stateful_signature_key(key_name, dir_name=alt_path) + + return None, None + + +if __name__ == "__main__": + xmss_names = [ + name for name in oqs.get_enabled_stateful_sig_mechanisms() if name.startswith("XMSS-") + ] + xmssmt_names = [ + name for name in oqs.get_enabled_stateful_sig_mechanisms() if name.startswith("XMSSMT-") + ] + hss_names = [ + name for name in oqs.get_enabled_stateful_sig_mechanisms() if name.startswith("LMS") + ] + logging.info("xmss_names: %s", str(xmss_names)) + private_bytes, public_bytes = deserialize_stateful_signature_key( + "XMSS-sha2_20_512", dir_name=_KEY_DIR + ) + if private_bytes is None or public_bytes is None: + ERROR_MSG = "Could not load the XMSS key" + raise ValueError(ERROR_MSG) From cc103a7ab3d06a47cb8e5f64fc5386a0a5e82180 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:07:04 +0200 Subject: [PATCH 03/25] Add XMSS and XMSS^MT keys. Signed-off-by: Guiliano99 --- .gitignore | 1 + data/xmss_xmssmt_keys/xmss-sha2_16_512.der | Bin 0 -> 4208 bytes data/xmss_xmssmt_keys/xmss-sha2_20_192.der | Bin 0 -> 2055 bytes data/xmss_xmssmt_keys/xmss-sha2_20_256.der | Bin 0 -> 2671 bytes data/xmss_xmssmt_keys/xmss-sha2_20_512.der | Bin 0 -> 5136 bytes data/xmss_xmssmt_keys/xmss-shake256_20_192.der | Bin 0 -> 2055 bytes data/xmss_xmssmt_keys/xmss-shake256_20_256.der | Bin 0 -> 2671 bytes data/xmss_xmssmt_keys/xmss-shake_16_256.der | Bin 0 -> 2191 bytes data/xmss_xmssmt_keys/xmss-shake_16_512.der | Bin 0 -> 4208 bytes data/xmss_xmssmt_keys/xmss-shake_20_256.der | Bin 0 -> 2671 bytes data/xmss_xmssmt_keys/xmss-shake_20_512.der | Bin 0 -> 5136 bytes .../xmssmt-sha2_40_layers_2_256.der | Bin 0 -> 9698 bytes .../xmssmt-sha2_60_layers_3_256.der | Bin 0 -> 16727 bytes .../xmssmt-shake_40_layers_2_256.der | Bin 0 -> 9698 bytes .../xmssmt-shake_60_layers_3_256.der | Bin 0 -> 16727 bytes 15 files changed, 1 insertion(+) create mode 100644 data/xmss_xmssmt_keys/xmss-sha2_16_512.der create mode 100644 data/xmss_xmssmt_keys/xmss-sha2_20_192.der create mode 100644 data/xmss_xmssmt_keys/xmss-sha2_20_256.der create mode 100644 data/xmss_xmssmt_keys/xmss-sha2_20_512.der create mode 100644 data/xmss_xmssmt_keys/xmss-shake256_20_192.der create mode 100644 data/xmss_xmssmt_keys/xmss-shake256_20_256.der create mode 100644 data/xmss_xmssmt_keys/xmss-shake_16_256.der create mode 100644 data/xmss_xmssmt_keys/xmss-shake_16_512.der create mode 100644 data/xmss_xmssmt_keys/xmss-shake_20_256.der create mode 100644 data/xmss_xmssmt_keys/xmss-shake_20_512.der create mode 100644 data/xmss_xmssmt_keys/xmssmt-sha2_40_layers_2_256.der create mode 100644 data/xmss_xmssmt_keys/xmssmt-sha2_60_layers_3_256.der create mode 100644 data/xmss_xmssmt_keys/xmssmt-shake_40_layers_2_256.der create mode 100644 data/xmss_xmssmt_keys/xmssmt-shake_60_layers_3_256.der diff --git a/.gitignore b/.gitignore index 23b8431..492c284 100644 --- a/.gitignore +++ b/.gitignore @@ -121,3 +121,4 @@ pyvenv.cfg # uv /uv.lock +/data/tmp_keys/ diff --git a/data/xmss_xmssmt_keys/xmss-sha2_16_512.der b/data/xmss_xmssmt_keys/xmss-sha2_16_512.der new file mode 100644 index 0000000000000000000000000000000000000000..bb0b1f885ea8625bd9c10e4c16127bc0f2f1e1e3 GIT binary patch literal 4208 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 PCUFprrXUG^QtM#=Ru41Q literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmss-sha2_20_256.der b/data/xmss_xmssmt_keys/xmss-sha2_20_256.der new file mode 100644 index 0000000000000000000000000000000000000000..3a1a8d474f4f3fd1f286b146cc03301f9be0e806 GIT binary patch literal 2671 zcmeI!`#TeQ90%}i*xcqGV#YX0liX4c=CbG{qSe;OFy#_rNrpHsDNQkVxsNPDA(!On z;<&8kZW2j^39Yl{mgII$?GHFVoL^4Q)AxD4pPydO_jx~`=lML}kKPqge-IF;CkhtU z1_OnJAYgUDE27c>06^fM3$Pr{lB4!LtOYv_J@WsWDY3wQX%GVFw3+!-+jCK0ZrOjM z>v|VRbpUU9yxyS36^)69aT%G5IlfN*9cL_LY%> z){9JeNSVyUyfP7wj10p5VS0p)tl8nGWta*KNHMWI0&i7(3T3{Ncm`&o&-wiX*4&{y z)p^`bh7ejc)DaDTs5RbIW6EVLVzyLxGI-3cV;9662)Wt)pkRd$p%pZg;}+tX^<*|Q z-pZCam`^%hT5`&8bJdlKFio|tFu!s3bv^-~c!h#z6_!WMdnBW5E&doZijnn9Qas>b`W>d0tV)#RKC+&xFr@BZJbc( z);ePXm72gEyj`%q9f$UX&EK|QyAO_Lndijv<`C_=mfG{1QM&>lpb;nbU1yWVeQZSh z;o2YG4%VXjBS1A_`jKT~ZhWygb8(gHM+GzULRMi_*Y+&~iRP$^77$KtANJL6ET zjMAlw)z&^_790^;?8(v`$am1}pqz{}y$B~xe!1KsZS*Dt|EYJyuQw=k)ajn|{+Kl5 zW;?+ILf&clqM3BL)Th;EJ;Sq~|?U~Vxmnjfa3 z!V3KNMFFjopfs+Y{>IJ%a%!4K+t)0Kdlu zaz+{9&ezi*jC9WMwR&Ct!;&Fj9kPL&&&&n|_rwoAjzGcL#vDotJT7VtK;zOdV&y8@=X9H->46%XOF z{c|SKvZy%3x^izFrV^&Y%S4KAt;YN|2VO@K?9ZqP^I5T>x{8iIS{AQu`=uvvt!*~9 zq0;A%PV8p(QK5-xCRT*csKb`$TzV-+sm2mp%_tcx$*8a@hON$M6|YZJTX_5|GW6I{SA}J|j*= zB*&b-R&(IJdv@np@22x!vwvAoB!H2U{ho-U-gpTYePe+YAf>vUB@7#6WcmukCQ!#7NjR1FR`L=ty2L(v+S!;UI z2M=_h@gS?F{R`uLs~Qqbth)b| za875HXvl>~En<}yAToTWL5nQpY(Lq1BkU|#ESA}{d0w(Hs%OZbLRa&ljx#RF^4a(L OFD2II8xZ)f``q7xKF>M; literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmss-sha2_20_512.der b/data/xmss_xmssmt_keys/xmss-sha2_20_512.der new file mode 100644 index 0000000000000000000000000000000000000000..e218cefdce96efb73d74835c79138d1a5b24d244 GIT binary patch literal 5136 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 KNr;kK4+8*ZB_5{$ literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmss-shake256_20_256.der b/data/xmss_xmssmt_keys/xmss-shake256_20_256.der new file mode 100644 index 0000000000000000000000000000000000000000..5612728369807894b6391ad1d38276dd2d5c4f52 GIT binary patch literal 2671 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 literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmss-shake_16_512.der b/data/xmss_xmssmt_keys/xmss-shake_16_512.der new file mode 100644 index 0000000000000000000000000000000000000000..7613b0d6a0f6f950917c3f7ea8212fea55d091a5 GIT binary patch literal 4208 zcmeI#`8O2mAHeaMVK8>7m?BHo7*xdM+Osbu+fY%A%961)W4jDlk~M^JDT8F+_hn3y zu}9WPSsS8gm>6m%_j~gveD68m^F62Moagi7>pai(OY0o`^)1 zm?2V)^6>PHMExo&=a6;^f1dDWMvnMRfZjX$))Ch+73}E3o`Mo&Oi3y1#lXPP+Ly-MkX zWq@ntfOQtb>U8QozRa@({T~=u9l&zLq3KO#dKKxw^DH+8WaR+K?{y8nPu}hS`p2Zr zq-rPFoz?0c|33|WuipiJ7x-P^Un_tP<0AIg@5{aO<9v*<^-3S{?e@gbUjFr0kW7VJ zBw7>QTc4IXi;ZXPNxKH+QQY2CLq61Eo}|_YR{+u0Jn4Itd8u{Mqrp3#kBG$6oB&$0MH9ffwo7aOFWyWFUW|XWKC^QbYdE| zX%d~eBkyBu;V+Oo-ntqD#>kkL9}H^P8IPQ|M63^XFDAjIgX&r(4BBdv{e(h|1%(~E zo*;*uJxAcL5;nt{`WA4Q-A`7lDU^?X<{RfP~Da5u??GcP=;X3&HZY`(~OIvH>*KHQK#E&9@G!V6?Ah%q|V>xZh2K%@NW6}tw)fMOI+pLT7q#6GFWh`bTC zTtI2L<9%JMOQR#Thdew8LNcQi^WJ!aLs zq$biEOw^T2LnRW%Jd~idvTe2Ci5_Sdh=svGX(DVUj2c10DS-)T1{RzHn{WAiY(?4p zuwy!IAgx-$0h{ zNbCI8b}eqEKFp2qlu6Iv1F&8d=aWYEEWsAz>dod{(M zy3aAQXmYM908vfCfBM!Q+fiA9DhkG^*dBLUz006Ii%t3#Z6a$Ywf6Sh4@<6FH1V)U z|Jpwc|KRV=l%;#rMei55iFikwsLLOcZ>GR)>vI=j3X=yAEO zOUKh9NbEXch~*1KD7t1t5-}cd?CMzt(V_ba{XuL%7uH`2)k-M(612N)sL`PcN!hvG z%yP&O%2YpTy_N!^RavJYXW)4sG@evuzFRM@ZSXd|g?BPjmbl0#;%MVPWuG0Yx@h7C zTY!;jvI1ma)5EVmr7=*fhs=dYOxYjKji?4|Z4+8NRNe^gSAxuZv*^9D5d_W3Zf+aU z2qOOoRi=#$l_^^M+-{-wqRbzdyYghO{%K_yO#~k@xYu!}Qqln*cV^R!ik&q&k{EAT zmO-AT15cpu6AwIrIVH`5i+QO{!-D=HW?qig0Yj(3+KJ$NiOl#^ZWWFnk72 zzs#$rOg8pI2H09hkWe0)dP%-|tbW1DGEZ?JfcMf$o(Ebv!zQW@mE-Q~wYgF3`%}e+ z2JOq*;u9MLf6y^!oCp%~{Rs$KN+`>m`jnbYXQJHB&vJMcNeTP~bdd zV)xH!JA~I`m=Z^0kcA$)YZ{NH4Q0f2_Db>Px0{ZOn4dbv9VNa{McO<;xRtS0mP3tv zfBY%lsI*p4o#f$c_UoGo0EP@q*gHP|U_G}*wm7?ay|B@LTgq_)9u}8`sga426|jK^ zf(8w=-<;(l=cREx=ct`$OjSuH$MAO97w+BIzQcWZnj@lMiSJYR^f@NpJ9LS*v*AQ- zI+N&nf5WD&2eoe%Ym=D)qN(@_Cq78~kF~yH@Tx~LlIM`IZ_7ad z7$gYClfj7exa;tbQI*{zOP$rkGj2^~);@zBdPW>oAv(Lc>ln?Nk_o>ogeZW}64!hC z;_BPVPRMiKLneWJ0S|j74I&Sj%x`6`B{qMc)Gn<{2qgvUL#m5?lpU40ze8dcUzW%z qWAPrkAH-IEhNkcrAjUfr?&DzcGJJ>3ulZk&$jF#~1g`(}hx%_Dg|+?w literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmss-shake_20_256.der b/data/xmss_xmssmt_keys/xmss-shake_20_256.der new file mode 100644 index 0000000000000000000000000000000000000000..fa11516ca41296ae27c6026274b2a54f0ed397f4 GIT binary patch literal 2671 zcmeI!`!^GO90%}iZrjpk2v5j;l1GNzg@@cDr7d>x%|KYs?Zdxs(_*gdzX{0OX$w2t7Xh0cvn;9YG$E6BBCzyb7aez9db}WQWXb;`wwTswKV_ z?E=bklcwF2PV|&Z zQA|{lNL!-efEY&Xsg-_*%q!!;1K3Lm4FoXK#yoNZ;SSr@1cK_nXZUq~ZQ!Td0J01p zpK^W={0L^E!u0qmhP#` zU2tD2GSrTTD6^ye87?9~94pFF5i%;2i8+waqt4AqO4IQB_%1)HB4UKKPSx~%UVc@- zv^$Ma;S;&4A1Qg4vNT6LM1gCU=2ubrS%b5OH`~L_=kD+8swWV_NHty2h{=jH&Mi!{ zuyF|qWpi~o?X>#MKIstTH|$-PrS%o{+su^8YTuwCEy7okkK!qBUkqq0@=K(bAUZu{{^B29A60?c9_S$WXF>xYTaziKS@hE*|{ART_||E?7wjyXagK zRhcmg)da1+^s19KnNLr)new{R_~6AK>eXywS#BYk8JiLH=kEFjfUT6A^n{44iY4C4 z?09F;>3&6%TFO9g&4Qx!#tSMP{b_kzpr_erKKNd!tg)-GPvN0V4ck0K+2G}z5z=JY zx~)-s3)|0|6zs1plIr&bqmz}1QO=TD>7qNR&B^H9Xhn5Gr6dEkTj(LJZ%Rg%+=R_HYqvJ<70ol7B`PApq z4@+;{X~%~Bp7hgwnEz#9Aa0UA$R%>g5eP=~7zW<#yH=nz>fNb^aCK$Csj490kJv!d zo0ej!R_BprTipCR34aNUS|mHRvWjny-Cz`Gu0f1IJVu|h&(Fy*nUOK|cCfCfe5{LK zexzsctja5Q49?kXapvPO?13lt9w#q_As()g+o$F9R{KWAlkH6{$IQ0agIlL2`FZS> z>cB&yT|i8&&`V_ptzksg?fGmZK(**3l{_tzvZ zDAYZG^p6;DzgSoWf}|+NpKEhNjQLocx)ay z*wb4V?=Q#yjlu*jtaWT2Q82;eFbDzDfd*#Z*Bm$kMVf-Of7msA4AP&!NI9Yd*$qr)ABv`Gah?=|aj zP=Unl7{fB1v9^1YHnfwsSQo?3#P$c~KTk#4EC}!5G2%6>2YG9U%!eDoNu{>W9l|{9+E?t9{eT%}b?MDn(_kE5iM(C)Hfx#TE(zl?wJNSFvCge>epAsL{hQK6QT@Ie zx3s`Ffvg2o$97x{oW~AQaPGxv(;(WZ&6I}=hNMgin(Xft+U8B2DS~8h!bNy&2pLoC zjT$V6)1P^wOCofc7tHru+TS9o#cx&+HLT?Ry_f+2z&@g;L}cIQfNb<4RF9UCIHOY?b1ix=#rNwJJ3qA)?*Q^&``q6q2+Fqr literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmss-shake_20_512.der b/data/xmss_xmssmt_keys/xmss-shake_20_512.der new file mode 100644 index 0000000000000000000000000000000000000000..19581ea4f68b2704437d0ea17f0218f9db973d6f GIT binary patch literal 5136 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 literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmssmt-sha2_40_layers_2_256.der b/data/xmss_xmssmt_keys/xmssmt-sha2_40_layers_2_256.der new file mode 100644 index 0000000000000000000000000000000000000000..2c8659636103313b6dec2260dadcd4124970ee83 GIT binary patch literal 9698 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 JU;WR5{{fKDDoy|Z literal 0 HcmV?d00001 diff --git a/data/xmss_xmssmt_keys/xmssmt-shake_40_layers_2_256.der b/data/xmss_xmssmt_keys/xmssmt-shake_40_layers_2_256.der new file mode 100644 index 0000000000000000000000000000000000000000..fc56808183e190812c51467fe898dcc9005a5331 GIT binary patch literal 9698 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 PuJ}JOLh*n7gX+HkJ9B6E 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 K=YRXpg8u@mX#mgw literal 0 HcmV?d00001 From 351de52f30d4d81382a238b30a7624b4db81dbf2 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:07:33 +0200 Subject: [PATCH 04/25] Update STFL tests to load some keys. Signed-off-by: Guiliano99 --- tests/test_stfl_sig.py | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/tests/test_stfl_sig.py b/tests/test_stfl_sig.py index c2459ed..3cb6565 100644 --- a/tests/test_stfl_sig.py +++ b/tests/test_stfl_sig.py @@ -1,11 +1,15 @@ import logging import platform # to learn the OS we're on import random +from pathlib import Path + +from oqs.serialize import gen_or_load_stateful_signature_key import oqs _skip_names = ["LMS_SHA256_H20_W8_H10_W8", "LMS_SHA256_H20_W8_H15_W8", "LMS_SHA256_H20_W8_H20_W8"] +_KEY_DIR = Path(__file__).resolve().parent.parent / "data" / "xmss_xmssmt_keys" # Sigs for which unit testing is disabled disabled_sig_patterns = [] @@ -16,30 +20,41 @@ def test_correctness() -> tuple[None, str]: for alg_name in oqs.get_enabled_stateful_sig_mechanisms(): + if alg_name.startswith("LMS"): + continue + if any(item in alg_name for item in disabled_sig_patterns): continue yield check_correctness, alg_name def check_correctness(alg_name: str) -> None: - with oqs.StatefulSignature(alg_name) as sig: + private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) + + with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = sig.generate_keypair() + public_key = public_key or sig.generate_keypair() signature = sig.sign(message) assert sig.verify(message, signature, public_key) # noqa: S101 def test_wrong_message() -> tuple[None, str]: for alg_name in oqs.get_enabled_stateful_sig_mechanisms(): + if alg_name.startswith("LMS"): + continue + if any(item in alg_name for item in disabled_sig_patterns): continue + yield check_wrong_message, alg_name def check_wrong_message(alg_name: str) -> None: - with oqs.StatefulSignature(alg_name) as sig: + private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) + + with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = sig.generate_keypair() + public_key = public_key or sig.generate_keypair() signature = sig.sign(message) wrong_message = bytes(random.getrandbits(8) for _ in range(len(message))) assert not (sig.verify(wrong_message, signature, public_key)) # noqa: S101 @@ -47,15 +62,20 @@ def check_wrong_message(alg_name: str) -> None: def test_wrong_signature() -> tuple[None, str]: for alg_name in oqs.get_enabled_stateful_sig_mechanisms(): + if alg_name.startswith("LMS"): + continue + if any(item in alg_name for item in disabled_sig_patterns): continue yield check_wrong_signature, alg_name def check_wrong_signature(alg_name: str) -> None: - with oqs.StatefulSignature(alg_name) as sig: + private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) + + with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = sig.generate_keypair() + public_key = public_key or sig.generate_keypair() signature = sig.sign(message) wrong_signature = bytes(random.getrandbits(8) for _ in range(len(signature))) assert not (sig.verify(message, wrong_signature, public_key)) # noqa: S101 @@ -63,15 +83,20 @@ def check_wrong_signature(alg_name: str) -> None: def test_wrong_public_key() -> tuple[None, str]: for alg_name in oqs.get_enabled_stateful_sig_mechanisms(): + if alg_name.startswith("LMS"): + continue + if any(item in alg_name for item in disabled_sig_patterns): continue yield check_wrong_public_key, alg_name def check_wrong_public_key(alg_name: str) -> None: - with oqs.StatefulSignature(alg_name) as sig: + private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) + + with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = sig.generate_keypair() + public_key = public_key or sig.generate_keypair() signature = sig.sign(message) wrong_public_key = bytes(random.getrandbits(8) for _ in range(len(public_key))) assert not (sig.verify(message, signature, wrong_public_key)) # noqa: S101 From e69482efcc4647589df5670d3576315cc56b1d5c Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:07:41 +0200 Subject: [PATCH 05/25] Run ruff. Signed-off-by: Guiliano99 --- docker/minitest.py | 3 +-- oqs/oqs.py | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docker/minitest.py b/docker/minitest.py index 5cc7bb1..ae001d0 100644 --- a/docker/minitest.py +++ b/docker/minitest.py @@ -65,8 +65,7 @@ except: print( "Test of algorithm combination SIG %s/KEX %s failed. " - "Are all algorithms supported by current OQS library?" - % (sigs, kex) + "Are all algorithms supported by current OQS library?" % (sigs, kex) ) if "SHORT_TEST" in os.environ: diff --git a/oqs/oqs.py b/oqs/oqs.py index 9a24f0a..322c2cf 100644 --- a/oqs/oqs.py +++ b/oqs/oqs.py @@ -36,6 +36,7 @@ cast, Optional, ) + if TYPE_CHECKING: from collections.abc import Sequence, Iterable from types import TracebackType @@ -746,8 +747,10 @@ def sign_with_ctx_str(self, message: bytes, context: bytes) -> bytes: :param message: the message to sign. """ if context and not self._sig.contents.sig_with_ctx_support: - msg = (f"Signing with context is not supported for: " - f"{self._sig.contents.method_name.decode()}") + msg = ( + f"Signing with context is not supported for: " + f"{self._sig.contents.method_name.decode()}" + ) raise RuntimeError(msg) # Provide length to avoid extra null char From 7044055ade3984d0a14830bab6d53b5bf3c3084d Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:43:32 +0200 Subject: [PATCH 06/25] Update serialize.py Signed-off-by: Guiliano99 --- oqs/serialize.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 7ea601d..933f006 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -113,9 +113,10 @@ def gen_or_load_stateful_signature_key( if key_name.startswith("XMSS-") and "_16_" in key_name: Path(alt_path).mkdir(parents=True, exist_ok=True) - with oqs.StatefulSignature(key_name) as stfl_sig: - public_key_bytes = stfl_sig.generate_keypair() - serialize_stateful_signature_key(stfl_sig, public_key_bytes, alt_fpath) + # Generate and serialize while the object is still open + + stfl_sig = oqs.StatefulSignature(key_name) + public_key_bytes = stfl_sig.generate_keypair() serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) return deserialize_stateful_signature_key(key_name, dir_name=alt_path) From 149b2b18dcf78d80ade1fbac56156b9ca441d4a6 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:44:09 +0200 Subject: [PATCH 07/25] Fix version check for latest. Signed-off-by: Guiliano99 --- oqs/oqs.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/oqs/oqs.py b/oqs/oqs.py index 322c2cf..b5075f0 100644 --- a/oqs/oqs.py +++ b/oqs/oqs.py @@ -153,10 +153,16 @@ def _install_liboqs( oqs_version_to_install: Union[str, None] = None, ) -> None: """Install liboqs version oqs_version (if None, installs latest at HEAD) in the target_directory.""" # noqa: E501 - if "rc" in oqs_version_to_install: + + # Set explicit to `None` to install the lastest `liboqs` code. + if oqs_version_to_install is None: + pass + + elif "rc" in oqs_version_to_install: # removed the "-" from the version string tmp = oqs_version_to_install.split("rc") oqs_version_to_install = tmp[0] + "-rc" + tmp[1] + with tempfile.TemporaryDirectory() as tmpdirname: oqs_install_cmd = [ "cd", From d33c9700c053bf30ba996d66a8cab01d0de6753d Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:53:40 +0200 Subject: [PATCH 08/25] Test gen_or_load_stateful_signature_key deactivate file writing. Signed-off-by: Guiliano99 --- oqs/serialize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/oqs/serialize.py b/oqs/serialize.py index 933f006..84e7bf8 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -112,6 +112,7 @@ def gen_or_load_stateful_signature_key( return deserialize_stateful_signature_key(key_name, dir_name=alt_path) if key_name.startswith("XMSS-") and "_16_" in key_name: + return None, None Path(alt_path).mkdir(parents=True, exist_ok=True) # Generate and serialize while the object is still open From 353530b02cf32fcee8de225e8547daaadb8df5c4 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 14:03:04 +0200 Subject: [PATCH 09/25] Add pipeline sync. Signed-off-by: Guiliano99 --- .github/workflows/python_detailed.yml | 4 ++++ .github/workflows/python_simplified.yml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/python_detailed.yml b/.github/workflows/python_detailed.yml index 8c16a94..eead031 100644 --- a/.github/workflows/python_detailed.yml +++ b/.github/workflows/python_detailed.yml @@ -66,6 +66,8 @@ jobs: - name: Run unit tests POSIX if: matrix.os != 'windows-latest' run: | + # Ensure dev extras (nose2, pyasn1, etc.) are present + uv sync --extra dev uv run nose2 --verbose - name: Install liboqs Windows @@ -96,4 +98,6 @@ jobs: if: matrix.os == 'windows-latest' run: | set PATH=%PATH%;${{env.WIN_LIBOQS_INSTALL_PATH}}\bin + rem Ensure dev extras (nose2, pyasn1, etc.) are present + uv sync --extra dev uv run nose2 --verbose diff --git a/.github/workflows/python_simplified.yml b/.github/workflows/python_simplified.yml index 18a0f8f..184c6d2 100644 --- a/.github/workflows/python_simplified.yml +++ b/.github/workflows/python_simplified.yml @@ -41,4 +41,6 @@ jobs: - name: Run unit tests run: | + # Ensure dev extras (nose2, pyasn1, etc.) are present + uv sync --extra dev uv run nose2 --verbose From 0872fb24fd7d5fd928a538ff0f85455f0d9c1fdd Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 14:16:34 +0200 Subject: [PATCH 10/25] Update simplified tests. Signed-off-by: Guiliano99 --- .github/workflows/python_simplified.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python_simplified.yml b/.github/workflows/python_simplified.yml index 184c6d2..e2e51fa 100644 --- a/.github/workflows/python_simplified.yml +++ b/.github/workflows/python_simplified.yml @@ -38,6 +38,7 @@ jobs: uv run examples/sig.py uv run examples/rand.py uv run examples/stfl_sig.py + uv run oqs/serialize.py - name: Run unit tests run: | From 8db8d597045108949ca953c9069f015ecbec531e Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 14:31:04 +0200 Subject: [PATCH 11/25] Update serialize.py. Signed-off-by: Guiliano99 --- oqs/serialize.py | 59 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 84e7bf8..24ace93 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -5,7 +5,7 @@ import logging from pathlib import Path -from typing import Optional, Union +from typing import Optional, Union, Tuple from pyasn1.codec.der import encoder, decoder from pyasn1.type import univ, tag @@ -50,9 +50,11 @@ def serialize_stateful_signature_key( one_asym_key["privateKeyAlgorithm"]["algorithm"] = univ.ObjectIdentifier( _get_oid_from_name(stateful_sig.method_name.decode("utf-8")) ) + # OCTET STRING privateKey one_asym_key["privateKey"] = stateful_sig.export_secret_key() + # [1] IMPLICIT BIT STRING publicKey (use simple tag, not constructed) one_asym_key["publicKey"] = univ.BitString.fromOctetString(public_key).subtype( - implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1) ) der_data = encoder.encode(one_asym_key) fpath_obj = Path(fpath) @@ -62,64 +64,84 @@ def serialize_stateful_signature_key( def deserialize_stateful_signature_key( - key_name: str, dir_name: Union[str, Path] = _KEY_DIR -) -> tuple[bytes, bytes]: + key_name: str, dir_name: Union[str, Path] = _KEY_DIR +) -> Tuple[bytes, bytes]: """ Deserialize the stateful signature key from a `OneAsymmetricKey` structure. - :param key_name: The file path to load the serialized key. + :param key_name: The base name of the serialized key (without extension). :param dir_name: The directory where the key files are stored. - :return: A tuple containing the method name, private key bytes, and public key bytes. + :return: A tuple (private_key_bytes, public_key_bytes). """ key_name = key_name.replace("/", "_layers_", 1).lower() fpath = Path(dir_name) / f"{key_name}.der" with fpath.open("rb") as f: der_data = f.read() + one_asym_key = decoder.decode(der_data, asn1Spec=rfc5958.OneAsymmetricKey())[0] oid = str(one_asym_key["privateKeyAlgorithm"]["algorithm"]) + # Accept any OID for supported families if oid not in _OID_2_NAME and oid not in _NAME_2_OIDS.values(): msg = f"Unsupported stateful signature OID: {oid}" raise ValueError(msg) private_key_bytes = one_asym_key["privateKey"].asOctets() - public_key_bytes = one_asym_key["publicKey"].asOctets() + + # publicKey is OPTIONAL; guard and normalize to bytes + public_key_bytes: Optional[bytes] + try: + public_field = one_asym_key["publicKey"] + public_key_bytes = public_field.asOctets() if getattr(public_field, "isValue", False) else None + except Exception: + public_key_bytes = None + + if public_key_bytes is None: + raise ValueError("Serialized key does not contain a publicKey field") + return private_key_bytes, public_key_bytes def gen_or_load_stateful_signature_key( - key_name: str, dir_name: str = _KEY_DIR -) -> tuple[Optional[bytes], Optional[bytes]]: + key_name: str, dir_name: Union[str, Path] = _KEY_DIR +) -> Tuple[Optional[oqs.StatefulSignature], Optional[bytes]]: """ Generate or load a stateful signature key pair. :param key_name: The name of the stateful signature mechanism. :param dir_name: The directory where the key files are stored. - :return: A tuple containing the stateful signature object and public key bytes. + :return: A tuple (stateful_signature_object, public_key_bytes). """ key_file_name = key_name.replace("/", "_layers_", 1).lower() fpath = Path(dir_name) / f"{key_file_name}.der" if Path(fpath).exists(): - return deserialize_stateful_signature_key(key_file_name, dir_name=dir_name) + private_key_bytes, public_key_bytes = deserialize_stateful_signature_key( + key_file_name, dir_name=dir_name + ) + stfl_sig = oqs.StatefulSignature(key_name) + stfl_sig.import_secret_key(private_key_bytes) + return stfl_sig, public_key_bytes - # Check alternative path for test keys, to not generate them for every test run, - # to save time. + # Check alternative path for test keys, to avoid regenerating for every test run. alt_path = Path(str(_KEY_DIR).replace("xmss_xmssmt_keys", "tmp_keys", 1)) alt_fpath = alt_path / f"{key_file_name}.der" if Path(alt_fpath).exists(): - return deserialize_stateful_signature_key(key_name, dir_name=alt_path) + private_key_bytes, public_key_bytes = deserialize_stateful_signature_key( + key_name, dir_name=alt_path + ) + stfl_sig = oqs.StatefulSignature(key_name) + stfl_sig.import_secret_key(private_key_bytes) + return stfl_sig, public_key_bytes + # Opportunistic generation for fast XMSS parameter sets used in tests if key_name.startswith("XMSS-") and "_16_" in key_name: - return None, None Path(alt_path).mkdir(parents=True, exist_ok=True) - # Generate and serialize while the object is still open - stfl_sig = oqs.StatefulSignature(key_name) public_key_bytes = stfl_sig.generate_keypair() serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) - return deserialize_stateful_signature_key(key_name, dir_name=alt_path) + return stfl_sig, public_key_bytes return None, None @@ -141,3 +163,4 @@ def gen_or_load_stateful_signature_key( if private_bytes is None or public_bytes is None: ERROR_MSG = "Could not load the XMSS key" raise ValueError(ERROR_MSG) + logging.info("Loaded XMSS key, public key len: %d", len(public_bytes)) \ No newline at end of file From ca4bdcc2ef0716a934627095c448f89a457f8f2f Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 14:50:53 +0200 Subject: [PATCH 12/25] Update serialize.py. Signed-off-by: Guiliano99 --- oqs/serialize.py | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 24ace93..58e3c57 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -48,14 +48,12 @@ def serialize_stateful_signature_key( one_asym_key = rfc5958.OneAsymmetricKey() one_asym_key["version"] = 1 one_asym_key["privateKeyAlgorithm"]["algorithm"] = univ.ObjectIdentifier( - _get_oid_from_name(stateful_sig.method_name.decode("utf-8")) + _get_oid_from_name(stateful_sig.method_name.decode()) ) - # OCTET STRING privateKey one_asym_key["privateKey"] = stateful_sig.export_secret_key() - # [1] IMPLICIT BIT STRING publicKey (use simple tag, not constructed) - one_asym_key["publicKey"] = univ.BitString.fromOctetString(public_key).subtype( - implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1) - ) + one_asym_key["publicKey"] = rfc5958.PublicKey().fromOctetString(public_key).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)) + der_data = encoder.encode(one_asym_key) fpath_obj = Path(fpath) with fpath_obj.open("wb") as f: @@ -83,29 +81,18 @@ def deserialize_stateful_signature_key( oid = str(one_asym_key["privateKeyAlgorithm"]["algorithm"]) # Accept any OID for supported families - if oid not in _OID_2_NAME and oid not in _NAME_2_OIDS.values(): + if oid not in _OID_2_NAME: msg = f"Unsupported stateful signature OID: {oid}" raise ValueError(msg) private_key_bytes = one_asym_key["privateKey"].asOctets() - - # publicKey is OPTIONAL; guard and normalize to bytes - public_key_bytes: Optional[bytes] - try: - public_field = one_asym_key["publicKey"] - public_key_bytes = public_field.asOctets() if getattr(public_field, "isValue", False) else None - except Exception: - public_key_bytes = None - - if public_key_bytes is None: - raise ValueError("Serialized key does not contain a publicKey field") - + public_key_bytes = one_asym_key["publicKey"].asOctets() return private_key_bytes, public_key_bytes def gen_or_load_stateful_signature_key( key_name: str, dir_name: Union[str, Path] = _KEY_DIR -) -> Tuple[Optional[oqs.StatefulSignature], Optional[bytes]]: +) -> Tuple[Optional[bytes], Optional[bytes]]: """ Generate or load a stateful signature key pair. @@ -117,12 +104,9 @@ def gen_or_load_stateful_signature_key( fpath = Path(dir_name) / f"{key_file_name}.der" if Path(fpath).exists(): - private_key_bytes, public_key_bytes = deserialize_stateful_signature_key( + return deserialize_stateful_signature_key( key_file_name, dir_name=dir_name ) - stfl_sig = oqs.StatefulSignature(key_name) - stfl_sig.import_secret_key(private_key_bytes) - return stfl_sig, public_key_bytes # Check alternative path for test keys, to avoid regenerating for every test run. alt_path = Path(str(_KEY_DIR).replace("xmss_xmssmt_keys", "tmp_keys", 1)) @@ -131,17 +115,15 @@ def gen_or_load_stateful_signature_key( private_key_bytes, public_key_bytes = deserialize_stateful_signature_key( key_name, dir_name=alt_path ) - stfl_sig = oqs.StatefulSignature(key_name) - stfl_sig.import_secret_key(private_key_bytes) - return stfl_sig, public_key_bytes + return private_key_bytes, public_key_bytes # Opportunistic generation for fast XMSS parameter sets used in tests if key_name.startswith("XMSS-") and "_16_" in key_name: Path(alt_path).mkdir(parents=True, exist_ok=True) stfl_sig = oqs.StatefulSignature(key_name) public_key_bytes = stfl_sig.generate_keypair() - serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) - return stfl_sig, public_key_bytes + private_key_bytes = stfl_sig.export_secret_key() + return private_key_bytes, public_key_bytes return None, None From b51a75377ed7f239ce7a3bc9b971df5c65c09dc6 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 14:59:06 +0200 Subject: [PATCH 13/25] Update oqs error message. Signed-off-by: Guiliano99 --- oqs/oqs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/oqs/oqs.py b/oqs/oqs.py index b5075f0..199b386 100644 --- a/oqs/oqs.py +++ b/oqs/oqs.py @@ -17,6 +17,9 @@ import subprocess import tempfile # to install liboqs on demand import time +import faulthandler + +faulthandler.enable() try: import tomllib # Python 3.11+ @@ -153,7 +156,6 @@ def _install_liboqs( oqs_version_to_install: Union[str, None] = None, ) -> None: """Install liboqs version oqs_version (if None, installs latest at HEAD) in the target_directory.""" # noqa: E501 - # Set explicit to `None` to install the lastest `liboqs` code. if oqs_version_to_install is None: pass From 04b4fa2ec3d9536d433bafc8e898ad3c5dce12b0 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 15:07:12 +0200 Subject: [PATCH 14/25] Update oqs error message. Signed-off-by: Guiliano99 --- oqs/serialize.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 58e3c57..5013dab 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -51,8 +51,11 @@ def serialize_stateful_signature_key( _get_oid_from_name(stateful_sig.method_name.decode()) ) one_asym_key["privateKey"] = stateful_sig.export_secret_key() - one_asym_key["publicKey"] = rfc5958.PublicKey().fromOctetString(public_key).subtype( - implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)) + one_asym_key["publicKey"] = ( + rfc5958.PublicKey() + .fromOctetString(public_key) + .subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)) + ) der_data = encoder.encode(one_asym_key) fpath_obj = Path(fpath) @@ -62,7 +65,7 @@ def serialize_stateful_signature_key( def deserialize_stateful_signature_key( - key_name: str, dir_name: Union[str, Path] = _KEY_DIR + key_name: str, dir_name: Union[str, Path] = _KEY_DIR ) -> Tuple[bytes, bytes]: """ Deserialize the stateful signature key from a `OneAsymmetricKey` structure. @@ -91,7 +94,7 @@ def deserialize_stateful_signature_key( def gen_or_load_stateful_signature_key( - key_name: str, dir_name: Union[str, Path] = _KEY_DIR + key_name: str, dir_name: Union[str, Path] = _KEY_DIR ) -> Tuple[Optional[bytes], Optional[bytes]]: """ Generate or load a stateful signature key pair. @@ -104,9 +107,7 @@ def gen_or_load_stateful_signature_key( fpath = Path(dir_name) / f"{key_file_name}.der" if Path(fpath).exists(): - return deserialize_stateful_signature_key( - key_file_name, dir_name=dir_name - ) + return deserialize_stateful_signature_key(key_file_name, dir_name=dir_name) # Check alternative path for test keys, to avoid regenerating for every test run. alt_path = Path(str(_KEY_DIR).replace("xmss_xmssmt_keys", "tmp_keys", 1)) @@ -120,11 +121,11 @@ def gen_or_load_stateful_signature_key( # Opportunistic generation for fast XMSS parameter sets used in tests if key_name.startswith("XMSS-") and "_16_" in key_name: Path(alt_path).mkdir(parents=True, exist_ok=True) - stfl_sig = oqs.StatefulSignature(key_name) - public_key_bytes = stfl_sig.generate_keypair() - private_key_bytes = stfl_sig.export_secret_key() - return private_key_bytes, public_key_bytes - + with oqs.StatefulSignature(key_name) as stfl_sig: + public_key_bytes = stfl_sig.generate_keypair() + private_key_bytes = stfl_sig.export_secret_key() + serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) + return private_key_bytes, public_key_bytes return None, None @@ -145,4 +146,4 @@ def gen_or_load_stateful_signature_key( if private_bytes is None or public_bytes is None: ERROR_MSG = "Could not load the XMSS key" raise ValueError(ERROR_MSG) - logging.info("Loaded XMSS key, public key len: %d", len(public_bytes)) \ No newline at end of file + logging.info("Loaded XMSS key, public key len: %d", len(public_bytes)) From a4c99aae1a4f2776957bb7d0ddda84c11bcf110e Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 15:53:11 +0200 Subject: [PATCH 15/25] Update STFL tests. Signed-off-by: Guiliano99 --- oqs/serialize.py | 1 + tests/test_stfl_sig.py | 63 +++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 5013dab..675008a 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -118,6 +118,7 @@ def gen_or_load_stateful_signature_key( ) return private_key_bytes, public_key_bytes + return None, None # Opportunistic generation for fast XMSS parameter sets used in tests if key_name.startswith("XMSS-") and "_16_" in key_name: Path(alt_path).mkdir(parents=True, exist_ok=True) diff --git a/tests/test_stfl_sig.py b/tests/test_stfl_sig.py index 3cb6565..6825a84 100644 --- a/tests/test_stfl_sig.py +++ b/tests/test_stfl_sig.py @@ -3,6 +3,8 @@ import random from pathlib import Path +from typing import Tuple + from oqs.serialize import gen_or_load_stateful_signature_key import oqs @@ -18,6 +20,17 @@ disabled_sig_patterns = [""] +def _load_or_generate_key(alg_name: str) -> Tuple[oqs.StatefulSignature, bytes]: + private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) + + if private_key is not None: + sig = oqs.StatefulSignature(alg_name, secret_key=private_key) + return sig, public_key + sig = oqs.StatefulSignature(alg_name) + public_key = sig.generate_keypair() + return sig, public_key + + def test_correctness() -> tuple[None, str]: for alg_name in oqs.get_enabled_stateful_sig_mechanisms(): if alg_name.startswith("LMS"): @@ -29,13 +42,10 @@ def test_correctness() -> tuple[None, str]: def check_correctness(alg_name: str) -> None: - private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) - - with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: - message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = public_key or sig.generate_keypair() - signature = sig.sign(message) - assert sig.verify(message, signature, public_key) # noqa: S101 + sig, public_key = _load_or_generate_key(alg_name) + message = bytes(random.getrandbits(8) for _ in range(100)) + signature = sig.sign(message) + assert sig.verify(message, signature, public_key) # noqa: S101 def test_wrong_message() -> tuple[None, str]: @@ -50,14 +60,11 @@ def test_wrong_message() -> tuple[None, str]: def check_wrong_message(alg_name: str) -> None: - private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) - - with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: - message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = public_key or sig.generate_keypair() - signature = sig.sign(message) - wrong_message = bytes(random.getrandbits(8) for _ in range(len(message))) - assert not (sig.verify(wrong_message, signature, public_key)) # noqa: S101 + sig, public_key = _load_or_generate_key(alg_name) + message = bytes(random.getrandbits(8) for _ in range(100)) + signature = sig.sign(message) + wrong_message = bytes(random.getrandbits(8) for _ in range(len(message))) + assert not (sig.verify(wrong_message, signature, public_key)) # noqa: S101 def test_wrong_signature() -> tuple[None, str]: @@ -71,14 +78,11 @@ def test_wrong_signature() -> tuple[None, str]: def check_wrong_signature(alg_name: str) -> None: - private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) - - with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: - message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = public_key or sig.generate_keypair() - signature = sig.sign(message) - wrong_signature = bytes(random.getrandbits(8) for _ in range(len(signature))) - assert not (sig.verify(message, wrong_signature, public_key)) # noqa: S101 + sig, public_key = _load_or_generate_key(alg_name) + message = bytes(random.getrandbits(8) for _ in range(100)) + signature = sig.sign(message) + wrong_signature = bytes(random.getrandbits(8) for _ in range(len(signature))) + assert not (sig.verify(message, wrong_signature, public_key)) # noqa: S101 def test_wrong_public_key() -> tuple[None, str]: @@ -92,14 +96,11 @@ def test_wrong_public_key() -> tuple[None, str]: def check_wrong_public_key(alg_name: str) -> None: - private_key, public_key = gen_or_load_stateful_signature_key(alg_name, dir_name=_KEY_DIR) - - with oqs.StatefulSignature(alg_name, secret_key=private_key) as sig: - message = bytes(random.getrandbits(8) for _ in range(100)) - public_key = public_key or sig.generate_keypair() - signature = sig.sign(message) - wrong_public_key = bytes(random.getrandbits(8) for _ in range(len(public_key))) - assert not (sig.verify(message, signature, wrong_public_key)) # noqa: S101 + sig, public_key = _load_or_generate_key(alg_name) + message = bytes(random.getrandbits(8) for _ in range(100)) + signature = sig.sign(message) + wrong_public_key = bytes(random.getrandbits(8) for _ in range(len(public_key))) + assert not (sig.verify(message, signature, wrong_public_key)) # noqa: S101 def test_not_supported() -> None: From 8c3eb20bb31198ffbde6fe96f6ab3b6fb7b013db Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Fri, 29 Aug 2025 10:56:56 +0200 Subject: [PATCH 16/25] Update OQS typing. Signed-off-by: Guiliano99 --- oqs/oqs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/oqs/oqs.py b/oqs/oqs.py index 199b386..0737255 100644 --- a/oqs/oqs.py +++ b/oqs/oqs.py @@ -1184,6 +1184,20 @@ def free(self) -> None: native().OQS_SIG_STFL_new.restype = ct.POINTER(StatefulSignature) native().OQS_SIG_STFL_SECRET_KEY_new.restype = ct.c_void_p native().OQS_SIG_STFL_SECRET_KEY_new.argtypes = [ct.c_char_p] +# Added precise signatures for (de)serialization to avoid ABI issues +native().OQS_SIG_STFL_SECRET_KEY_serialize.restype = ct.c_int +native().OQS_SIG_STFL_SECRET_KEY_serialize.argtypes = [ + ct.POINTER(ct.POINTER(ct.c_uint8)), + ct.POINTER(ct.c_size_t), + ct.c_void_p, +] +native().OQS_SIG_STFL_SECRET_KEY_deserialize.restype = ct.c_int +native().OQS_SIG_STFL_SECRET_KEY_deserialize.argtypes = [ + ct.c_void_p, + ct.c_void_p, + ct.c_size_t, + ct.c_void_p, +] native().OQS_SIG_STFL_SECRET_KEY_SET_store_cb.argtypes = [ct.c_void_p, ct.c_void_p, ct.c_void_p] native().OQS_SIG_STFL_keypair.argtypes = [ct.POINTER(StatefulSignature), ct.c_void_p, ct.c_void_p] native().OQS_SIG_STFL_sign.argtypes = [ From 1188132f80a789251960754495546266c9582392 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Fri, 29 Aug 2025 11:05:01 +0200 Subject: [PATCH 17/25] Add STFL private key size error msg. Signed-off-by: Guiliano99 --- oqs/oqs.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/oqs/oqs.py b/oqs/oqs.py index 0737255..54e36b1 100644 --- a/oqs/oqs.py +++ b/oqs/oqs.py @@ -1035,6 +1035,13 @@ def _load_secret_key(self, data: bytes) -> None: buf = ct.create_string_buffer(data, len(data)) rc = native().OQS_SIG_STFL_SECRET_KEY_deserialize(self._secret_key, buf, len(data), None) if rc != OQS_SUCCESS: + if len(data) != int(self.length_secret_key): + msg = ( + f"Secret key length must be {self.length_secret_key} bytes, " + f"got {len(data)} bytes" + ) + raise ValueError(msg) + msg = "Secret‑key deserialization failed" raise RuntimeError(msg) From 7314b2ffa9e031751be4c87bfeb7cdffb2ca7655 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Fri, 29 Aug 2025 11:05:33 +0200 Subject: [PATCH 18/25] Clean gen_or_load_stateful_signature_key. Signed-off-by: Guiliano99 --- oqs/serialize.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 675008a..2bc5b27 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -88,6 +88,30 @@ def deserialize_stateful_signature_key( msg = f"Unsupported stateful signature OID: {oid}" raise ValueError(msg) +def _may_generate_stfl_key( + key_name: str, dir_name: str +) -> tuple[Optional[bytes], Optional[bytes]]: + """ + Decide whether to generate a stateful signature key for the given algorithm name. + + Currently, this function allows opportunistic generation only for fast XMSS parameter sets + used in tests, specifically those starting with "XMSS-" and containing "_16_". + + :param key_name: The name of the stateful signature mechanism. + :param dir_name: The directory where the key files are stored. + :return: A tuple (private_key_bytes, public_key_bytes) if generated, else (None, None). + """ + alt_path = Path(str(dir_name).replace("xmss_xmssmt_keys", "tmp_keys", 1)) + alt_fpath = alt_path / f"{key_name.replace('/', '_layers_', 1).lower()}.der" + if key_name.startswith("XMSS-") and "_16_" in key_name: + Path(alt_path).mkdir(parents=True, exist_ok=True) + with oqs.StatefulSignature(key_name) as stfl_sig: + public_key_bytes = stfl_sig.generate_keypair() + private_key_bytes = stfl_sig.export_secret_key() + serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) + return private_key_bytes, public_key_bytes + + return None, None private_key_bytes = one_asym_key["privateKey"].asOctets() public_key_bytes = one_asym_key["publicKey"].asOctets() return private_key_bytes, public_key_bytes @@ -118,16 +142,8 @@ def gen_or_load_stateful_signature_key( ) return private_key_bytes, public_key_bytes - return None, None # Opportunistic generation for fast XMSS parameter sets used in tests - if key_name.startswith("XMSS-") and "_16_" in key_name: - Path(alt_path).mkdir(parents=True, exist_ok=True) - with oqs.StatefulSignature(key_name) as stfl_sig: - public_key_bytes = stfl_sig.generate_keypair() - private_key_bytes = stfl_sig.export_secret_key() - serialize_stateful_signature_key(stfl_sig, public_key_bytes, str(alt_fpath)) - return private_key_bytes, public_key_bytes - return None, None + return _may_generate_stfl_key(key_name, dir_name) if __name__ == "__main__": From 17478f1069fe11ea006896d6a208b96e4b2702b1 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Fri, 29 Aug 2025 11:05:55 +0200 Subject: [PATCH 19/25] Run ruff. Signed-off-by: Guiliano99 --- oqs/serialize.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 2bc5b27..6f70893 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -5,7 +5,7 @@ import logging from pathlib import Path -from typing import Optional, Union, Tuple +from typing import Optional, Union from pyasn1.codec.der import encoder, decoder from pyasn1.type import univ, tag @@ -66,7 +66,7 @@ def serialize_stateful_signature_key( def deserialize_stateful_signature_key( key_name: str, dir_name: Union[str, Path] = _KEY_DIR -) -> Tuple[bytes, bytes]: +) -> tuple[bytes, bytes]: """ Deserialize the stateful signature key from a `OneAsymmetricKey` structure. @@ -119,7 +119,7 @@ def _may_generate_stfl_key( def gen_or_load_stateful_signature_key( key_name: str, dir_name: Union[str, Path] = _KEY_DIR -) -> Tuple[Optional[bytes], Optional[bytes]]: +) -> tuple[Optional[bytes], Optional[bytes]]: """ Generate or load a stateful signature key pair. From 1b307ef8ae4486c59756df5b3d485b2a4b232965 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Fri, 29 Aug 2025 11:07:21 +0200 Subject: [PATCH 20/25] Fix code. Signed-off-by: Guiliano99 --- oqs/serialize.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/oqs/serialize.py b/oqs/serialize.py index 6f70893..eee6355 100644 --- a/oqs/serialize.py +++ b/oqs/serialize.py @@ -88,6 +88,10 @@ def deserialize_stateful_signature_key( msg = f"Unsupported stateful signature OID: {oid}" raise ValueError(msg) + private_key_bytes = one_asym_key["privateKey"].asOctets() + public_key_bytes = one_asym_key["publicKey"].asOctets() + return private_key_bytes, public_key_bytes + def _may_generate_stfl_key( key_name: str, dir_name: str ) -> tuple[Optional[bytes], Optional[bytes]]: @@ -112,9 +116,6 @@ def _may_generate_stfl_key( return private_key_bytes, public_key_bytes return None, None - private_key_bytes = one_asym_key["privateKey"].asOctets() - public_key_bytes = one_asym_key["publicKey"].asOctets() - return private_key_bytes, public_key_bytes def gen_or_load_stateful_signature_key( From 097fdc1b5501af35388d32bee9a29a89520d3dfe Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Mon, 1 Sep 2025 11:40:06 +0200 Subject: [PATCH 21/25] Revert "Update dependencies." This reverts commit 9828bd32e5fb898040c644d37e26fc96d7294b8e. Signed-off-by: Guiliano99 --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b50d645..86e31a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,8 +21,6 @@ dev = [ "pre-commit==4.1.0", "ruff==0.9.4", "nose2==0.15.1", - "pyasn1-alt-modules==0.4.6", - "pyasn1==0.6.1", ] lint = [ "mypy==1.14.1", From dfcdd25a8f20f8205ddc19400da18a163c819d08 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Thu, 28 Aug 2025 13:04:24 +0200 Subject: [PATCH 22/25] Update dependencies. Signed-off-by: Guiliano99 (cherry picked from commit 9828bd32e5fb898040c644d37e26fc96d7294b8e) Signed-off-by: Guiliano99 --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 86e31a6..b50d645 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,8 @@ dev = [ "pre-commit==4.1.0", "ruff==0.9.4", "nose2==0.15.1", + "pyasn1-alt-modules==0.4.6", + "pyasn1==0.6.1", ] lint = [ "mypy==1.14.1", From fb35a3708626b129a2c4840f8c8c4ae902d077f5 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Mon, 1 Sep 2025 11:54:36 +0200 Subject: [PATCH 23/25] Update Pipeline to fix duplicated tests. Signed-off-by: Guiliano99 --- .github/workflows/python_detailed.yml | 4 ++++ .github/workflows/python_simplified.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/python_detailed.yml b/.github/workflows/python_detailed.yml index eead031..abdac4b 100644 --- a/.github/workflows/python_detailed.yml +++ b/.github/workflows/python_detailed.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: 'test-python-detailed' + cancel-in-progress: true + env: BUILD_TYPE: Debug LD_LIBRARY_PATH: /usr/local/lib diff --git a/.github/workflows/python_simplified.yml b/.github/workflows/python_simplified.yml index e2e51fa..1fa44c0 100644 --- a/.github/workflows/python_simplified.yml +++ b/.github/workflows/python_simplified.yml @@ -11,6 +11,10 @@ on: permissions: contents: read +concurrency: + group: 'test-python_simplified' + cancel-in-progress: true + jobs: build: strategy: From 26d138837e2c7a4baec117dca2982a37a77c4214 Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Mon, 1 Sep 2025 11:58:50 +0200 Subject: [PATCH 24/25] Update Pipeline to fix duplicated tests. Signed-off-by: Guiliano99 --- .github/workflows/python_detailed.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python_detailed.yml b/.github/workflows/python_detailed.yml index abdac4b..0d6c531 100644 --- a/.github/workflows/python_detailed.yml +++ b/.github/workflows/python_detailed.yml @@ -11,16 +11,16 @@ on: permissions: contents: read -concurrency: - group: 'test-python-detailed' - cancel-in-progress: true - env: BUILD_TYPE: Debug LD_LIBRARY_PATH: /usr/local/lib WIN_LIBOQS_INSTALL_PATH: C:\liboqs VERSION: 0.14.0 +concurrency: + group: test-python-detailed-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: build: strategy: From f84e74801c671a3ab076193563474cf7f253f43c Mon Sep 17 00:00:00 2001 From: Guiliano99 Date: Mon, 1 Sep 2025 12:10:10 +0200 Subject: [PATCH 25/25] Update Pipeline to fix duplicated tests. Signed-off-by: Guiliano99 --- .github/workflows/python_simplified.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python_simplified.yml b/.github/workflows/python_simplified.yml index 1fa44c0..c8899d2 100644 --- a/.github/workflows/python_simplified.yml +++ b/.github/workflows/python_simplified.yml @@ -12,8 +12,9 @@ permissions: contents: read concurrency: - group: 'test-python_simplified' - cancel-in-progress: true + group: test-python-simplified-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: build: