Coverage Report

Created: 2026-09-28 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/liboqs/tests/fuzz_test_sig_stfl_lms.c
Line
Count
Source
1
/*
2
 * fuzz_test_sig_stfl_lms.c
3
 *
4
 * Fuzz test for LMS stateful hash-based signature verification in liboqs.
5
 * Targets OQS_SIG_STFL_verify for the following parameter sets as specified
6
 * in https://github.com/open-quantum-safe/liboqs/issues/2398:
7
 *   - LMS_SHA256_H5_W2
8
 *   - LMS_SHA256_H5_W8_H5_W8
9
 *
10
 * Baseline pk / message / signature bytes are copied from the KAT .rsp files
11
 * under tests/KATs/sig_stfl/lms/ (count = 0 vectors). The fuzzer mutates
12
 * those inputs to detect crashes, memory errors, and undefined behaviour.
13
 *
14
 * SPDX-License-Identifier: MIT
15
 */
16
17
#include <stdbool.h>
18
#include <stdio.h>
19
#include <stdlib.h>
20
#include <string.h>
21
22
#include <oqs/oqs.h>
23
24
/* The two target algorithms specified in issue #2398 */
25
static const char *TARGET_ALGS[] = {
26
  "LMS_SHA256_H5_W2",
27
  "LMS_SHA256_H5_W8_H5_W8",
28
};
29
static const size_t NUM_TARGET_ALGS = 2;
30
31
/*
32
 * Fuzz input layout:
33
 *   [0]    algorithm selector byte (index into TARGET_ALGS)
34
 *   [1]    field selector byte (% 3 → 0: pk/sig/msg, 1: sig/msg/pk, 2: msg/pk/sig)
35
 *   [2..]  fuzz bytes applied sequentially to the three fields in selected order
36
 */
37
38
typedef struct {
39
  OQS_SIG_STFL *sig;
40
  uint8_t *public_key;
41
  uint8_t *signature;
42
  size_t signature_len;
43
  uint8_t *message;
44
  size_t message_len;
45
} stfl_keypair_t;
46
47
/* One cached vector per target algorithm, initialised once */
48
static stfl_keypair_t cached[2];
49
static bool initialised = false;
50
51
/* LMS_SHA256_H5_W2.rsp */
52
static const char KAT_LMS_SHA256_H5_W2_PK_HEX[] =
53
    "000000010000000500000002ad412ccdb2962114e64225d1add892b4adf8f6b43057169755d96b6ebc59eaa37904fb719e358e6759f598593ea69f7f";
54
static const char KAT_LMS_SHA256_H5_W2_MSG_HEX[] =
55
    "54686520706f77657273206e6f742064656c65676174656420746f2074686520556e69746564205374617465732062792074686520436f6e737469747574696f6e2c206e6f722070726f6869626974656420627920697420746f20746865205374617465732c2061726520726573657276656420746f207468652053746174657320726573706563746976656c792c206f7220746f207468652070656f706c652e2e0a0a";
56
static const char KAT_LMS_SHA256_H5_W2_SM_HEX[] =
57
    "000000000000000000000002150417e62d167e4676733e92d1394e814545ed6b9b5bcdb5abfe12ef0d5e14f418017877e0ae217554001f178e1fbb78c6517a7b91a4768e2133c5c0ffc3541c1c957502a004405cb29dc9f86b37da393939e10c6050fc178853f99a31f96dee2893a203398e3cb632d761c2552e0f181eb568ce5984b89658da394dd4138df820dc6974ab9baae3253756c4c90e329464b5663e5b4cd00ec4efd4e3d4224b2fd0d0c6e0241fa222e9ca3057d903c958a9e3bba381d3b3e91406f34f368be3f40f09a4d7ae479944113e69709ea4a8eb3a0dc3c02b49822f35deaa94bf1d98335bb0a10966eaa9fc19167d9d07956f1e2cb1eeaa6ce5ef1d1860824911d8d151aa133eaa0974823d9b0ebc6218b5f1d3d39560832c303c99fa3a73f4a5c983cf80937cc18788a162e025515213225f357dcd2e2d8a9411fd4abb915892461db7d02e15c4b7f774837ca87d764d2f07e8c437465c53152691e39c65e7c6649e83ddb3568e2e31c661c649ba174f9829351817456b4e527ef6e2db153033becc31af7ffb1ec040d7f4f6ccfad654a34d7041a854cddcb8ab905356d37e4332f5632aef346eae7a49d6d3f465d3a918a532e4efe3f458bc3c2a17200e18e71bd05730f44d1d3b9490fec0e4a52d532bc987c7f29b0c4d1a1375b302a48476645f6f9d938ed7c4b9d874b496488439a299e9108be95b2c695e5049e739a9a3d14684d88b064884ea5438f2e009c0126509541457c87a3f06af3c75da61373ecda9a693b980113b22c34ba15c95696388c2e66d90b2fbb800a97a1b96b31a954e411229bbd86cac0fe668990a9c15c6181f847f2a1db288e995257b2c525e049609ee143555eb36649e119c0f427c998ffb45592ea6faffeff2a9e498428b09b6f1d3fc3a0c8b62a749c51b279be712be38f7b323455b50ac4e325703cf564025e2aa1e5876dd56a7be2ccb920dd22e475be7db8b3ff442711e9d0f3b392eda27da8a34cae12cd4f99fa17d8364d5a2a1303e996621b649a6d7105343b0a5af6dd65f2b1f13d8f36545eb65b99b8b6db5f022bc702e8bd2a1ddbc99b64a291affd1e2adcf8a01e9dd9c117e80e32f874beea046b56b44ee8cc2240d03de68c90d9f4d67e5f3aea949cf6f47d5356a6232e979329c10422193f206c74cbe2d06d290d40fb0ffcf2bafbca374a7c327006cb1b21608af282955d3259a4f4b845ac2d9b386be83e96eeda3a84a64810ae3c81b709fef15078424298917b2697d2bdd3e7f4d365b9a84bbab07c0be4d0929be663a06345d1a67360141c03c3f37891f09c24776177e34a9093fa62005187511268fb2fa80740cb751f5e9fd0d74b0f61b643d139f8ccd040c9a9a32cc740b825a2e6a5beb28a8915fbbb1bcd41c9694fcc72ed570b1e96cafaedde8adf8d5028f80eead64f9407e57ff5465da3e5e341e587cbf091f2f5bab89e95666a3fb7158718afcf685df10ea4a22ae7fe55c8f70d5d5a5973f3dde9c80f2a9eb13d58ca3485268f6e380464892f4c3c3598c81fc3d77d1ce0d3d9f50c78be78b88f6a9e17d6ec4ad9e6a86edbd5a4e5e17219fde097f422498c171ec1d0f6dbb6a2a49d34a66ae854e2650fa4a6c2833af5106472e7c787ef2da06f28f84f0f1197c7b4c4cff6ab3ceaac5ee819809ef20ee9b535252c23117da43e31ce778abfd84bf647875573217cc79c088f91ab07adc4c930a4819b936a98834ba2f9b034996d26bff2cc901317536b5481431656c083780d774b686ac5b0abd7ae2e05981c39726e6efb4ea2507b20e85cffecac815d6d9f84fa86240e6ded4dcaa67a2e2f944ac6d38f986b7268f2c240d8f5eafc0b2b67dfc11992e305e64264e19e021fd29b72dea8be8c315e3aa5f02c49fb9e77a34c48c58a28c4340ffaa7fb5b5d0c8a4aa4abd7bbf3955a0b71cbff160c5dd9a6a4ddefdf00367f414dad1c7ec0f77c1a777ec1c726a1049a2c4bc01554b95665496584cb13b28dafd7c668baa7d0c1e425e5c8cef9f75f21819a5708db01e5a5918df096424abcea2d758d4a8c5fbd96e10ba91bef727bcc47930bd45bc7d6851b21c7daf94c016e8cd10498f18ea44c6474d865a8841800dd19fe3d57611660c3729b5c2a2f1d1765a1237db7b97bcb6af56f79f275677bfee3c37fb1a0d7c2b453feea1f7adc60892a95a7e00421b4fa320ea25119fbd6af73d9049c8e109e2b1b683f5bf83933d63bd552f65fbfb806f346db3592e244acc0878c450962956f4e47bb0bced88d1ee0d18f93acdaf6bff6e6d9b451db35755c1bbb547dc4e098012823874546757f5aa94cd0bd294709da538e8c8675e96d47269451bb63d35b6f027d68aa0871b23114553e883463512890d489286886a93227ad5ba80c53f0bb4d383f15f607091a3ddf07cb199a7d8abee54097371c8ae59b354a8f86765288dc635e4f5a3f1bed2f0998f0562755035f39a4fb0f69618ebdbc3427f1fec8d1040aef11da99c0d3f0864615b1283fb2f9e66b957cf666ae08b2e11fc9153dd32b5fc43736758f65d896d253dbe16d7b16db56130851b7a0935d92d33fa75affb9d509ee73c40c9b61359ff814267e962d4aa80f6fb0ddb76dd4cbaf49fe65ad51b406a2f33bbf698a6dde309a7d7ce95f7c68ee5bccd1631f1443f0b1e823af31b2245095afdf073708fa9afa0c3b1c4606d5428cfbe3799003437b74b136b1d0d2f80848a1e5ab14fe7e9ffe02a09e21bd5d9c4eb0607bb5549f0399c2336cbc76a6e55c7f80fc0ee968c696beaa803fb020438a9ca6c89ef1e8823801e91c1cd7cbacf09c79f128f22f29c9d49355ecfe470b8df5edf9a47bdc781a3ff0ae8e5596e13bc06c610bab6121f85d54ef764e6a82a290a4ac3db0f76820d986ffeb5f19460db6246b59aa659afe6753093998876cb961b07fac8b0c0896d0931bc5843c4f545263a950523b7ad8520654564e27d0661f1cbe60812ad232b458ca36c6439750ed12df2f5d89b12f584e0023a2b8fa39c2a13da2b7497a968189f183f65bb37c5999fd6187f9979f39ec459271d67676ae157271b24ac7b3d3876840c48296096ee2730e14adb8af5ae11666d02e441f83c859aad9657c1216a9c8116d660abfb5e061f2face2"
58
    "f11088e661ab2046b969e72c3d23c6410c951ad210572e3cbddba77c5fb81c7594e60e1a4784d3d606b254a49f05521a0289dd14687cf8efb78a2eef2211e6021aa14f2a3d2a5a0d5013282591239e7df7991e3f44442b544e14ae382181f0790d4901b30f659f0cf9b3080a113d2bf869e44149667438b194bca40f1ce542ef7021222e0d6b7bcf40b8631e9a23b87dc1b97cd6eea407d3f7149e250c70a021b3614b5f68c065eccba5ac481fc4435f2da541a1e368d69259bc179338e641b9b14e77abe6762ae8db9a980061c85f49aa161aa92851f6f1e6a38152edac3f121326e517215cfab5746db3e026ffa2b5b4ea41d297bc4bf91f1d07c866b21425e022f0081c7284fa81c0b6b43bea2ce4d61b910aaa539b5485b79f0f766bc704eba870b33855a589ddf7525cbc07c828cf51e687f6d804e4d43535820171d38a67a8025486f0594872c996768b56177a916b13bdf622ea4d05ac608bd1f9220420733fabcbee129a4e89b243bfb4cb7cafebdbd37b1b505014ac3b9419bb51e274f8aa81f82725693341424df75c08ff6acdf622fea4988d0925ffb33466489331b00dc3432d19dddf30fd64194d4523dd3cafef94bacaf6c72b68665d1731edc8c2e9933d9302c61d576160c3c98a9fa393aa60a843238acc975f5af75532b5088a06299dfe395bf4ffdd4ba2c96ca3c7536c6986d34bdf61e01004e537748f427a7cff650918ec3c0916cdc7ba3037d078c1f96d1d77ec7560d1fa3a55443a8b2876c882a7ae2ddfeb611af3806be2f02e39a82e734728348bd90394b732da8798ea1552925e36a8ae92697bca66ec1cc4f6c2d0d60348f81f74ddae919516cb53455e4642f0782acd678e0f2daf973f866ff0f5576517038ffc52301bc53624eee8cacd1d9fdc4c74bf2afd75bd3d0c7a8010ab8d082e6edba64106cff64b5a9ac166f02b906b9b84539361a68703e43c74439306382964d3f2245a1cb9699b9aafe48922dcfec3515cf9d737d72888562b4830e5a80594ea437556ca1c7921629438c620a439196d194a862dba078524bbfd801d2cb33cd500957b84719352baae64d8163ff85cf8236c509ba495633d4972ba8094d1e293b07f7693bc939ffcf4e3979c1f077e48b6a7b2666360759b82adffc811635b860062fe55d70b1397d6dbebe2f73e722fcb90af4d98a55d4043edea799954e8950e7f6744a1e3a98b2fd48f2ff685208c8ad85becaac5ae51e187e75beaf6589773d23367379a6b46082761f247d79341d9498d210a206424d07bb88fe5018a302f4bbdb2d0ceafd4b25603fce716eddbd18ba669fd0a18c93112082b2941924ba80c5a50efaf1f391e6e0d7d6e27cc991c29f4065d75be997762e4032b62bba80be85d4c8ec05ebf54354ebed5c32ffc47caf0e7c703c38559f125521ba736225d9284e33fbab9d32ad54e3ddce4a79a6a0adb2fcd15c57281a9f150a38d8fc96a27c751a888daa65c4461b54efaf2dd2e0d3b5a6ac12027e3b9721f6bb1a32239824faf8574574845a4d649820b1a003cbe2b3e6466f47062356432d3280edc53b1c4fffc63de4fac290e4525c151ce05314c8a8b0c744acd5768936f93a7421eea8e69522ad6d188f71f4b285a535d84ca72c965bc4e40f03b09703d534b6de536b2da8bb36704ee930f1252fbaab91d5974a9d89e1411c5662d65328334a3982c31d4d0588000204acb1f5fc359302d931d8844dbf24201483041dde064ac06dc020768f40039ab61e8a4b893a1a782a49632e38d5a70120aa4ae391d69c9fe11aff5c3f4fc55153ecf9bab047e130e4585e5de2fb875ae4841d21abc580a945e4738674ee2423a237e9f03edd0bf5e395760d5ffffa1645846019fac8a58ca6741a221ea52c0c3020ca6590a7633388da3daed3ea8425ad3671f235420b00ccdaa006a0cee192855a6d8f6cc17794c4bb1da3c6d1b3b2269572bc2f9d93ba886fe30cdcdd2f105536472a580d0e9ccbaf9d2bd091cd33a2243c816da0fd4a3e8f1c01657dc236ea26cb83e05f1c6c184d9c0391535307cb945f80f9329800b39dcbb10e1ba5d13185a3050772d694429e17426136db817cffe563a77703666f1dc827d81790bcc4e9ceba15c34a28c420b6cc7df34cea563d48e9fd20a208497e0cb5a02eb25c3f67095f3f3a63d62d06ad7953ebfddbfddbb613c676394ed866622ba3323d6b1ae1b1324aa41e99170ee1e66560fa78b74051fb172f9b82e53a02be5859ca2d59a133551b7f1cfde0ec524b4fb4aefa69df632a385db308044922dce6258d71b36b14cb481e82928fafc56111ebd5969a3588ecab557b8c15654cbd559f0792b6caed5413d750867d00ef53aa597949fdb837c468c41bdeb081aae58040a50172cbf3d075a9263f4ad7da16062e80e5fe39da2e05366b79a148ae181c465aae82ecec18e98f696649bca24931a702c338f25bb2d135b95bddea4f9f92a2f925f31ee784f195d29eb4003a5b9a21cda79c6a35a579a470bdfa05ce3ed9c2a693ec92ac559691cdf22e5b074c479f58ef2395781369738f7de2c13de64090dc6575920c28a2c5164c8266e9dee906ef55c69c73fc932a64549470fcfe4da13bda91672870957d0cc002998510826d6452f7b400d09c47925eb3e4bb8f53bbd3f657d2afcd7eecc992966bb1ea7774ed15d22d931dacd5bd45993151ce89e173548d0d9324ef74be4c0cb89a1d824f1eb164821249116274a536b1b760846a4f6d1426f946d338ef04f06494a8ad0e3a59fc2269cbd411b0106e8b1245dcc3814ccf330790dd352e68a675736119d839155378f573524f5071487329e2b30405d2b0f07ad82cf2a7a3514821356f126349668f35350f3f3b71045bf60f1f608da2ebb1b067f0e9b673a1e6bf221eee8aaaae8000000051c142d7004cafdc21431a71b81e6ddc44a8ab75a3a2c8fa3a9a8cc4a781d9079063f7a97bf683473a44e5ec3877cb60d400f6a6d8a3e4fdcb02f51160f6d828ea4358ff1f2fdeb78042387d9cf0452ad8653f511d53435401a55c32b0c03a76b0edfec3b1fcb0b864da0ef3663930feab4e318cb7c271ac7b0ee8f7fa2c67838b293d94a7bddb92e05b5810f88ac6eea3182bbe37704a0190fd4658e268be368";
59
/* LMS_SHA256_H5_W8_H5_W8.rsp */
60
static const char KAT_LMS_SHA256_H5_W8_H5_W8_PK_HEX[] =
61
    "00000002000000050000000461a5d57d37f5e46bfb7520806b07a1b850650e3b31fe4a773ea29a07f09cf2ea30e579f0df58ef8e298da0434cb2b878";
62
static const char KAT_LMS_SHA256_H5_W8_H5_W8_MSG_HEX[] =
63
    "54686520706f77657273206e6f742064656c65676174656420746f2074686520556e69746564205374617465732062792074686520436f6e737469747574696f6e2c206e6f722070726f6869626974656420627920697420746f20746865205374617465732c2061726520726573657276656420746f207468652053746174657320726573706563746976656c792c206f7220746f207468652070656f706c652e0a";
64
static const char KAT_LMS_SHA256_H5_W8_H5_W8_SM_HEX[] =
65
    "000000010000000500000004d32b56671d7eb98833c49b433c272586bc4a1c8a8970528ffa04b966f9426eb9965a25bfd37f196b9073f3d4a232feb69128ec45146f86292f9dff9610a7bf95a64c7f60f6261a62043f86c70324b7707f5b4a8a6e19c114c7be866d488778a0e05fd5c6509a6e61d559cf1a77a970de927d60c70d3de31a7fa0100994e162a2582e8ff1b10cd99d4e8e413ef469559f7d7ed12c838342f9b9c96b83a4943d1681d84b15357ff48ca579f19f5e71f18466f2bbef4bf660c2518eb20de2f66e3b14784269d7d876f5d35d3fbfc7039a462c716bb9f6891a7f41ad133e9e1f6d9560b960e7777c52f060492f2d7c660e1471e07e72655562035abc9a701b473ecbc3943c6b9c4f2405a3cb8bf8a691ca51d3f6ad2f428bab6f3a30f55dd9625563f0a75ee390e385e3ae0b906961ecf41ae073a0590c2eb6204f44831c26dd768c35b167b28ce8dc988a3748255230cef99ebf14e730632f27414489808afab1d1e783ed04516de012498682212b07810579b250365941bcc98142da13609e9768aaf65de7620dabec29eb82a17fde35af15ad238c73f81bdb8dec2fc0e7f932701099762b37f43c4a3c20010a3d72e2f606be108d310e639f09ce7286800d9ef8a1a40281cc5a7ea98d2adc7c7400c2fe5a101552df4e3cccfd0cbf2ddf5dc6779cbbc68fee0c3efe4ec22b83a2caa3e48e0809a0a750b73ccdcf3c79e6580c154f8a58f7f24335eec5c5eb5e0cf01dcf4439424095fceb077f66ded5bec73b27c5b9f64a2a9af2f07c05e99e5cf80f00252e39db32f6c19674f190c9fbc506d826857713afd2ca6bb85cd8c107347552f30575a5417816ab4db3f603f2df56fbc413e7d0acd8bdd81352b2471fc1bc4f1ef296fea1220403466b1afe78b94f7ecf7cc62fb92be14f18c2192384ebceaf8801afdf947f698ce9c6ceb696ed70e9e87b0144417e8d7baf25eb5f70f09f016fc925b4db048ab8d8cb2a661ce3b57ada67571f5dd546fc22cb1f97e0ebd1a65926b1234fd04f171cf469c76b884cf3115cce6f792cc84e36da58960c5f1d760f32c12faef477e94c92eb75625b6a371efc72d60ca5e908b3a7dd69fef0249150e3eebdfed39cbdc3ce9704882a2072c75e13527b7a581a556168783dc1e97545e31865ddc46b3c957835da252bb7328d3ee2062445dfb85ef8c35f8e1f3371af34023cef626e0af1e0bc017351aae2ab8f5c612ead0b729a1d059d02bfe18efa971b7300e882360a93b025ff97e9e0eec0f3f3f13039a17f88b0cf808f488431606cb13f9241f40f44e537d302c64a4f1f4ab949b9feefadcb71ab50ef27d6d6ca8510f150c85fb525bf25703df7209b6066f09c37280d59128d2f0f637c7d7d7fad4ed1c1ea04e628d221e3d8db77b7c878c9411cafc5071a34a00f4cf07738912753dfce48f07576f0d4f94f42c6d76f7ce973e9367095ba7e9a3649b7f461d9f9ac1332a4d1044c96aefee67676401b64457c54d65fef6500c59cdfb69af7b6dddfcb0f086278dd8ad0686078dfb0f3f79cd893d314168648499898fbc0ced5f95b74e8ff14d735cdea968bee7400000005d8b8112f9200a5e50c4a262165bd342cd800b8496810bc716277435ac376728d129ac6eda839a6f357b5a04387c5ce97382a78f2a4372917eefcbf93f63bb59112f5dbe400bd49e4501e859f885bf0736e90a509b30a26bfac8c17b5991c157eb5971115aa39efd8d564a6b90282c3168af2d30ef89d51bf14654510a12b8a144cca1848cf7da59cc2b3d9d0692dd2a20ba3863480e25b1b85ee860c62bf51360000000500000004d2f14ff6346af964569f7d6cb880a1b66c50"
66
    "04917da6eafe4d9ef6c6407b3db0e5485b122d9ebe15cda93cfec582d7ab0000000a000000040703c491e7558b35011ece3592eaa5da4d918786771233e8353bc4f62323185c95cae05b899e35dffd717054706209988ebfdf6e37960bb5c38d7657e8bffeef9bc042da4b4525650485c66d0ce19b317587c6ba4bffcc428e25d08931e72dfb6a120c5612344258b85efdb7db1db9e1865a73caf96557eb39ed3e3f426933ac9eeddb03a1d2374af7bf77185577456237f9de2d60113c23f846df26fa942008a698994c0827d90e86d43e0df7f4bfcdb09b86a373b98288b7094ad81a0185ac100e4f2c5fc38c003c1ab6fea479eb2f5ebe48f584d7159b8ada03586e65ad9c969f6aecbfe44cf356888a7b15a3ff074f771760b26f9c04884ee1faa329fbf4e61af23aee7fa5d4d9a5dfcf43c4c26ce8aea2ce8a2990d7ba7b57108b47dabfbeadb2b25b3cacc1ac0cef346cbb90fb044beee4fac2603a442bdf7e507243b7319c9944b1586e899d431c7f91bcccc8690dbf59b28386b2315f3d36ef2eaa3cf30b2b51f48b71b003dfb08249484201043f65f5a3ef6bbd61ddfee81aca9ce60081262a00000480dcbc9a3da6fbef5c1c0a55e48a0e729f9184fcb1407c31529db268f6fe50032a363c9801306837fafabdf957fd97eafc80dbd165e435d0e2dfd836a28b354023924b6fb7e48bc0b3ed95eea64c2d402f4d734c8dc26f3ac591825daef01eae3c38e3328d00a77dc657034f287ccb0f0e1c9a7cbdc828f627205e4737b84b58376551d44c12c3c215c812a0970789c83de51d6ad787271963327f0a5fbb6b5907dec02c9a90934af5a1c63b72c82653605d1dcce51596b3c2b45696689f2eb382007497557692caac4d57b5de9f5569bc2ad0137fd47fb47e664fcb6db4971f5b3e07aceda9ac130e9f38182de994cff192ec0e82fd6d4cb7f3fe00812589b7a7ce515440456433016b84a59bec6619a1c6c0b37dd1450ed4f2d8b584410ceda8025f5d2d8dd0d2176fc1cf2cc06fa8c82bed4d944e71339ece780fd025bd41ec34ebff9d4270a3224e019fcb444474d482fd2dbe75efb20389cc10cd600abb54c47ede93e08c114edb04117d714dc1d525e11bed8756192f929d15462b939ff3f52f2252da2ed64d8fae88818b1efa2c7b08c8794fb1b214aa233db3162833141ea4383f1a6f120be1db82ce3630b3429114463157a64e91234d475e2f79cbf05e4db6a9407d72c6bff7d1198b5c4d6aad2831db61274993715a0182c7dc8089e32c8531deed4f7431c07c02195eba2ef91efb5613c37af7ae0c066babc69369700e1dd26eddc0d216c781d56e4ce47e3303fa73007ff7b949ef23be2aa4dbf25206fe45c20dd888395b2526391a724996a44156beac808212858792bf8e74cba49dee5e8812e019da87454bff9e847ed83db07af313743082f880a278f682c2bd0ad6887cb59f652e155987d61bbf6a88d36ee93b6072e6656d9ccbaae3d655852e38deb3a2dcf8058dc9fb6f2ab3d3b3539eb77b248a661091d05eb6e2f297774fe6053598457cc61908318de4b826f0fc86d4bb117d33e865aa805009cc2918d9c2f840c4da43a703ad9f5b5806163d7161696b5a0adc00000005d5c0d1bebb06048ed6fe2ef2c6cef305b3ed633941ebc8b3bec9738754cddd60e1920ada52f43d055b5031cee6192520d6a5115514851ce7fd448d4a39fae2ab2335b525f484e9b40d6a4a969394843bdcf6d14c48e8015e08ab92662c05c6e9f90b65a7a6201689999f32bfd368e5e3ec9cb70ac7b8399003f175c40885081a09ab3034911fe125631051df0408b3946b0bde790911e8978ba07dd56c73e7ee";
67
68
/* Decode even-length hex ASCII into out[0..out_len-1]. Returns OQS_SUCCESS or OQS_ERROR. */
69
0
static OQS_STATUS hex_decode(const char *hex, uint8_t *out, size_t out_len) {
70
0
  size_t hlen = strlen(hex);
71
0
  if (hlen != out_len * 2) {
72
0
    return OQS_ERROR;
73
0
  }
74
0
  for (size_t i = 0; i < out_len; i++) {
75
0
    unsigned char c0 = (unsigned char)hex[2 * i];
76
0
    unsigned char c1 = (unsigned char)hex[2 * i + 1];
77
0
    unsigned v0, v1;
78
0
    if (c0 >= '0' && c0 <= '9') {
79
0
      v0 = c0 - (unsigned char)'0';
80
0
    } else if (c0 >= 'A' && c0 <= 'F') {
81
0
      v0 = 10 + c0 - (unsigned char)'A';
82
0
    } else if (c0 >= 'a' && c0 <= 'f') {
83
0
      v0 = 10 + c0 - (unsigned char)'a';
84
0
    } else {
85
0
      return OQS_ERROR;
86
0
    }
87
0
    if (c1 >= '0' && c1 <= '9') {
88
0
      v1 = c1 - (unsigned char)'0';
89
0
    } else if (c1 >= 'A' && c1 <= 'F') {
90
0
      v1 = 10 + c1 - (unsigned char)'A';
91
0
    } else if (c1 >= 'a' && c1 <= 'f') {
92
0
      v1 = 10 + c1 - (unsigned char)'a';
93
0
    } else {
94
0
      return OQS_ERROR;
95
0
    }
96
0
    out[i] = (uint8_t)((v0 << 4) | v1);
97
0
  }
98
0
  return OQS_SUCCESS;
99
0
}
100
101
2
static void cleanup_cached(void) {
102
6
  for (size_t i = 0; i < NUM_TARGET_ALGS; i++) {
103
4
    if (cached[i].sig != NULL) {
104
0
      OQS_MEM_insecure_free(cached[i].public_key);
105
0
      OQS_MEM_insecure_free(cached[i].signature);
106
0
      OQS_MEM_secure_free(cached[i].message, cached[i].message_len);
107
0
      OQS_SIG_STFL_free(cached[i].sig);
108
0
      cached[i].sig = NULL;
109
0
    }
110
4
  }
111
2
}
112
113
typedef struct {
114
  const char *pk_hex;
115
  const char *msg_hex;
116
  const char *sm_hex;
117
} kat_hex_triple_t;
118
119
static const kat_hex_triple_t KAT_HEX[NUM_TARGET_ALGS] = {
120
  { KAT_LMS_SHA256_H5_W2_PK_HEX, KAT_LMS_SHA256_H5_W2_MSG_HEX, KAT_LMS_SHA256_H5_W2_SM_HEX },
121
  { KAT_LMS_SHA256_H5_W8_H5_W8_PK_HEX, KAT_LMS_SHA256_H5_W8_H5_W8_MSG_HEX, KAT_LMS_SHA256_H5_W8_H5_W8_SM_HEX },
122
};
123
124
/*
125
 * Load KAT vectors once at startup. Avoids LMS key generation cost.
126
 */
127
2
static OQS_STATUS init_vectors(void) {
128
6
  for (size_t i = 0; i < NUM_TARGET_ALGS; i++) {
129
4
    cached[i].sig = NULL;
130
4
    cached[i].public_key = NULL;
131
4
    cached[i].signature = NULL;
132
4
    cached[i].message = NULL;
133
4
  }
134
135
2
  for (size_t i = 0; i < NUM_TARGET_ALGS; i++) {
136
2
    OQS_SIG_STFL *sig = OQS_SIG_STFL_new(TARGET_ALGS[i]);
137
2
    if (sig == NULL) {
138
2
      fprintf(stderr, "ERROR: %s not enabled at compile-time.\n",
139
2
              TARGET_ALGS[i]);
140
2
      cleanup_cached();
141
2
      return OQS_ERROR;
142
2
    }
143
0
    cached[i].sig = sig;
144
145
0
    cached[i].public_key = OQS_MEM_malloc(sig->length_public_key);
146
0
    cached[i].signature = OQS_MEM_malloc(sig->length_signature);
147
0
    cached[i].signature_len = sig->length_signature;
148
0
    cached[i].message_len = strlen(KAT_HEX[i].msg_hex) / 2;
149
0
    cached[i].message = OQS_MEM_malloc(cached[i].message_len);
150
151
0
    if (!cached[i].public_key || !cached[i].message || !cached[i].signature) {
152
0
      fprintf(stderr, "ERROR: OQS_MEM_malloc failed for %s.\n",
153
0
              TARGET_ALGS[i]);
154
0
      cleanup_cached();
155
0
      return OQS_ERROR;
156
0
    }
157
158
0
    if (hex_decode(KAT_HEX[i].pk_hex, cached[i].public_key,
159
0
                   sig->length_public_key) != OQS_SUCCESS) {
160
0
      fprintf(stderr, "ERROR: KAT pk hex length mismatch for %s.\n",
161
0
              TARGET_ALGS[i]);
162
0
      cleanup_cached();
163
0
      return OQS_ERROR;
164
0
    }
165
0
    if (hex_decode(KAT_HEX[i].msg_hex, cached[i].message,
166
0
                   cached[i].message_len) != OQS_SUCCESS) {
167
0
      fprintf(stderr, "ERROR: KAT msg hex for %s.\n", TARGET_ALGS[i]);
168
0
      cleanup_cached();
169
0
      return OQS_ERROR;
170
0
    }
171
0
    if (hex_decode(KAT_HEX[i].sm_hex, cached[i].signature,
172
0
                   sig->length_signature) != OQS_SUCCESS) {
173
0
      fprintf(stderr, "ERROR: KAT sm hex length mismatch for %s.\n",
174
0
              TARGET_ALGS[i]);
175
0
      cleanup_cached();
176
0
      return OQS_ERROR;
177
0
    }
178
0
  }
179
0
  return OQS_SUCCESS;
180
2
}
181
182
0
static OQS_STATUS fuzz_sig_stfl_lms(const uint8_t *data, size_t data_len) {
183
0
  if (data_len < 1) {
184
0
    return OQS_ERROR;
185
0
  }
186
187
  /* Byte 0: algorithm selector */
188
0
  size_t alg_idx = data[0] % NUM_TARGET_ALGS;
189
0
  stfl_keypair_t *kp = &cached[alg_idx];
190
0
  OQS_SIG_STFL *sig = kp->sig;
191
192
0
  const uint8_t *fuzz_data = data + 1;
193
0
  size_t fuzz_len = data_len - 1;
194
195
0
  if (fuzz_len < 1) {
196
0
    return OQS_ERROR;
197
0
  }
198
199
  /* Byte 1: field selector — determines which field is mutated first */
200
0
  size_t field_selector = fuzz_data[0] % 3;
201
0
  fuzz_data++;
202
0
  fuzz_len--;
203
204
0
  size_t pk_len = sig->length_public_key;
205
0
  size_t sig_len = kp->signature_len;
206
0
  size_t msg_len = kp->message_len;
207
208
0
  uint8_t *mutated_pk = OQS_MEM_malloc(pk_len);
209
0
  uint8_t *mutated_sig = OQS_MEM_malloc(sig_len);
210
0
  uint8_t *mutated_msg = OQS_MEM_malloc(msg_len);
211
212
0
  if (!mutated_pk || !mutated_sig || !mutated_msg) {
213
0
    OQS_MEM_insecure_free(mutated_pk);
214
0
    OQS_MEM_insecure_free(mutated_sig);
215
0
    OQS_MEM_insecure_free(mutated_msg);
216
0
    return OQS_ERROR;
217
0
  }
218
219
  /* Start from KAT values, then overwrite with fuzz bytes in selected order */
220
0
  memcpy(mutated_pk, kp->public_key, pk_len);
221
0
  memcpy(mutated_sig, kp->signature, sig_len);
222
0
  memcpy(mutated_msg, kp->message, msg_len);
223
224
  /*
225
   * field_selector % 3 controls which field is overwritten first:
226
   *   0 → pk, sig, msg   1 → sig, msg, pk   2 → msg, pk, sig
227
   */
228
0
  uint8_t *fp[3];
229
0
  size_t fl[3];
230
0
  switch (field_selector) {
231
0
  case 1:
232
0
    fp[0] = mutated_sig;
233
0
    fl[0] = sig_len;
234
0
    fp[1] = mutated_msg;
235
0
    fl[1] = msg_len;
236
0
    fp[2] = mutated_pk;
237
0
    fl[2] = pk_len;
238
0
    break;
239
0
  case 2:
240
0
    fp[0] = mutated_msg;
241
0
    fl[0] = msg_len;
242
0
    fp[1] = mutated_pk;
243
0
    fl[1] = pk_len;
244
0
    fp[2] = mutated_sig;
245
0
    fl[2] = sig_len;
246
0
    break;
247
0
  case 0:
248
0
  default:
249
0
    fp[0] = mutated_pk;
250
0
    fl[0] = pk_len;
251
0
    fp[1] = mutated_sig;
252
0
    fl[1] = sig_len;
253
0
    fp[2] = mutated_msg;
254
0
    fl[2] = msg_len;
255
0
    break;
256
0
  }
257
258
0
  size_t offset = 0;
259
0
  for (size_t i = 0; i < 3; i++) {
260
0
    if (offset < fuzz_len) {
261
0
      size_t copy = (fuzz_len - offset) < fl[i] ? (fuzz_len - offset) : fl[i];
262
0
      memcpy(fp[i], fuzz_data + offset, copy);
263
0
      offset += copy;
264
0
    }
265
0
  }
266
267
  /*
268
   * Call OQS_SIG_STFL_verify with mutated inputs.
269
   * We expect OQS_ERROR for invalid inputs — a crash here is a bug.
270
   */
271
0
  OQS_SIG_STFL_verify(sig, mutated_msg, msg_len, mutated_sig, sig_len,
272
0
                      mutated_pk);
273
274
0
  OQS_MEM_insecure_free(mutated_pk);
275
0
  OQS_MEM_insecure_free(mutated_sig);
276
0
  OQS_MEM_secure_free(mutated_msg, msg_len);
277
278
0
  return OQS_SUCCESS;
279
0
}
280
281
4
int LLVMFuzzerInitialize(int *argc, char ***argv) {
282
4
  (void)argc;
283
4
  (void)argv;
284
4
  OQS_init();
285
4
  if (OQS_SUCCESS != init_vectors()) {
286
4
    return -1;
287
4
  }
288
0
  initialised = true;
289
0
  return 0;
290
4
}
291
292
2
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
293
2
  if (!initialised) {
294
2
    return -1;
295
2
  }
296
0
  fuzz_sig_stfl_lms(data, size);
297
0
  return 0;
298
2
}