nss/nss-3.112-add-ml-dsa-base.patch
Robert Relyea 2a8572a8f9 Resolves: RHEL-103353
rebase NSS to 3.112
Include mlkem1024 support and ml-dsa support for tls
2025-07-14 09:01:26 -07:00

26586 lines
879 KiB
Diff

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -15,8 +15,13 @@ GTAGS
#*
.#*
.ycm_extra_conf.py*
fuzz/libFuzzer/*
fuzz/corpus
fuzz/out
.chk
cmd/dbtool/sdb.c
+lib/freebl/oqs/liboqs/**
+lib/freebl/oqs/*extract_defines*
+lib/freebl/leancrypto/leancrypto/**
+lib/freebl/oqs/**
+mldsa_samples/**
diff --git a/cmd/certutil/certutil.c b/cmd/certutil/certutil.c
--- a/cmd/certutil/certutil.c
+++ b/cmd/certutil/certutil.c
@@ -296,16 +296,21 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
rv = SECOID_SetAlgorithmID(arena, &signAlg,
SEC_OID_PKCS1_RSA_PSS_SIGNATURE, params);
if (rv != SECSuccess) {
PORT_FreeArena(arena, PR_FALSE);
SECU_PrintError(progName, "unable to set algorithm ID");
return SECFailure;
}
} else {
+ /* sigh, we need to create a new SEC_GetSignatureAlgorithOidTag()
+ * that takes a public key and one that takes a private key */
+ if (keyType == mldsaKey) {
+ hashAlgTag = pubk->u.mldsa.params;
+ }
signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
if (signAlgTag == SEC_OID_UNKNOWN) {
PORT_FreeArena(arena, PR_FALSE);
SECU_PrintError(progName, "unknown Key or Hash type");
return SECFailure;
}
rv = SECOID_SetAlgorithmID(arena, &signAlg, signAlgTag, 0);
if (rv != SECSuccess) {
@@ -858,18 +863,20 @@ SECItemToHex(const SECItem *item, char *
unsigned int len = item->len;
for (; len > 0; --len, dst += 2) {
snprintf(dst, 3, "%02x", *src++);
}
*dst = '\0';
}
}
+/* must be ordered to match KeyType in keythi.h */
static const char *const keyTypeName[] = {
- "null", "rsa", "dsa", "fortezza", "dh", "kea", "ec", "rsaPss", "rsaOaep"
+ "null", "rsa", "dsa", "fortezza", "dh", "kea", "ec", "rsaPss", "rsaOaep",
+ "kyberKey", "edKey", "ecMontKey", "mldsaKey"
};
#define MAX_CKA_ID_BIN_LEN 20
#define MAX_CKA_ID_STR_LEN 40
/* output human readable key ID in buffer, which should have at least
* MAX_CKA_ID_STR_LEN + 3 octets (quotations and a null terminator) */
static void
@@ -899,27 +906,31 @@ formatPrivateKeyID(SECKEYPrivateKey *pri
/* print key number, key ID (in hex or ASCII), key label (nickname) */
static SECStatus
PrintKey(PRFileDesc *out, const char *nickName, int count,
SECKEYPrivateKey *key, void *pwarg)
{
char ckaIDbuf[MAX_CKA_ID_STR_LEN + 4];
CERTCertificate *cert;
KeyType keyType;
+ const char *thisKeyTypeName = "unknown";
formatPrivateKeyID(key, ckaIDbuf);
cert = PK11_GetCertFromPrivateKey(key);
if (cert) {
keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
CERT_DestroyCertificate(cert);
} else {
keyType = key->keyType;
}
+ if (keyType < PR_ARRAY_SIZE(keyTypeName)) {
+ thisKeyTypeName = keyTypeName[keyType];
+ }
PR_fprintf(out, "<%2d> %-8.8s %-42.42s %s\n", count,
- keyTypeName[keyType], ckaIDbuf, nickName);
+ thisKeyTypeName, ckaIDbuf, nickName);
return SECSuccess;
}
/* returns SECSuccess if ANY keys are found, SECFailure otherwise. */
static SECStatus
ListKeysInSlot(PK11SlotInfo *slot, const char *nickName, KeyType keyType,
void *pwarg)
@@ -1185,17 +1196,19 @@ PrintSyntax()
FPS "\t%s -F -k key-id [-d certdir] [-P dbprefix]\n",
progName);
FPS "\t%s -G -n key-name [-h token-name] [-k rsa] [-g key-size] [-y exp]\n"
"\t\t [-f pwfile] [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
FPS "\t%s -G [-h token-name] -k dsa [-q pqgfile -g key-size] [-f pwfile]\n"
"\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
FPS "\t%s -G [-h token-name] -k ec -q curve [-f pwfile]\n"
"\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
- FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|ec|rsa|all]\n",
+ FPS "\t%s -G [-h token-name] -k mldsa -q paramset [-f pwfile]\n"
+ "\t\t [-z noisefile] [-d certdir] [-P dbprefix]\n", progName);
+ FPS "\t%s -K [-n key-name] [-h token-name] [-k dsa|ec|rsa|mldsa|all]\n",
progName);
FPS "\t\t [-f pwfile] [-X] [-d certdir] [-P dbprefix]\n");
FPS "\t%s --upgrade-merge --source-dir upgradeDir --upgrade-id uniqueID\n",
progName);
FPS "\t\t [--upgrade-token-name tokenName] [-d targetDBDir]\n");
FPS "\t\t [-P targetDBPrefix] [--source-prefix upgradeDBPrefix]\n");
FPS "\t\t [-f targetPWfile] [-@ upgradePWFile]\n");
FPS "\t%s --merge --source-dir sourceDBDir [-d targetDBdir]\n",
@@ -1419,16 +1432,19 @@ luG(enum usage_level ul, const char *com
FPS "%-20s prime239v1, prime239v2, prime239v3, c2pnb163v1, \n", "");
FPS "%-20s c2pnb163v2, c2pnb163v3, c2pnb176v1, c2tnb191v1, \n", "");
FPS "%-20s c2tnb191v2, c2tnb191v3, \n", "");
FPS "%-20s c2pnb208w1, c2tnb239v1, c2tnb239v2, c2tnb239v3, \n", "");
FPS "%-20s c2pnb272w1, c2pnb304w1, \n", "");
FPS "%-20s c2tnb359w1, c2pnb368w1, c2tnb431r1, secp112r1, \n", "");
FPS "%-20s secp112r2, secp128r1, secp128r2, sect113r1, sect113r2\n", "");
FPS "%-20s sect131r1, sect131r2\n", "");
+ FPS "%-20s ML-DSA parameter set (mldsa only)\n",
+ " -q paramset");
+ FPS "%-20s valid values are ml-dsa-44, ml-dsa-65, ml-dsa-87:\n", "");
FPS "%-20s Key database directory (default is ~/.netscape)\n",
" -d keydir");
FPS "%-20s Cert & Key database prefix\n",
" -P dbprefix");
FPS "%-20s\n"
"%-20s PKCS #11 key Attributes.\n",
" --keyAttrFlags attrflags", "");
FPS "%-20s Comma separated list of key attribute attribute flags,\n", "");
@@ -1511,16 +1527,17 @@ luK(enum usage_level ul, const char *com
"-K");
if (ul == usage_selected && !is_my_command)
return;
FPS "%-20s Name of token to search (\"all\" for all tokens)\n",
" -h token-name ");
FPS "%-20s Key type (\"all\" (default), \"dsa\","
" \"ec\","
+ " \"mldsa\","
" \"rsa\")\n",
" -k key-type");
FPS "%-20s The nickname of the key or associated certificate\n",
" -n name");
FPS "%-20s Specify the password file\n",
" -f password-file");
FPS "%-20s Key database directory (default is ~/.netscape)\n",
" -d keydir");
@@ -1671,16 +1688,20 @@ luR(enum usage_level ul, const char *com
FPS "%-20s Create a certificate request restricted to RSA-PSS (rsa only)\n",
" --pss");
FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
" -q pqgfile");
FPS "%-20s Elliptic curve name (ec only)\n",
" -q curve-name");
FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
"");
+ FPS "%-20s ML-DSA parameter set (mldsa only)\n",
+ " -q paramset");
+ FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
+ "");
FPS "%-20s Specify the password file\n",
" -f pwfile");
FPS "%-20s Key database directory (default is ~/.netscape)\n",
" -d keydir");
FPS "%-20s Cert & Key database prefix\n",
" -P dbprefix");
FPS "%-20s Specify the contact phone number (\"123-456-7890\")\n",
" -p phone");
@@ -1851,16 +1872,20 @@ luS(enum usage_level ul, const char *com
FPS "%-20s Create a certificate restricted to RSA-PSS (rsa only)\n",
" --pss");
FPS "%-20s Name of file containing PQG parameters (dsa only)\n",
" -q pqgfile");
FPS "%-20s Elliptic curve name (ec only)\n",
" -q curve-name");
FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
"");
+ FPS "%-20s ML-DSA parameter set (mldsa only)\n",
+ " -q paramset");
+ FPS "%-20s See the \"-G\" option for a full list of supported names.\n",
+ "");
FPS "%-20s Self sign\n",
" -x");
FPS "%-20s Sign the certificate with RSA-PSS (the issuer key must be rsa)\n",
" --pss-sign");
FPS "%-20s Cert serial number\n",
" -m serial-number");
FPS "%-20s Time Warp\n",
" -w warp-months");
@@ -2031,16 +2056,39 @@ MakeV1Cert(CERTCertDBHandle *handle,
}
if (issuerCert) {
CERT_DestroyCertificate(issuerCert);
}
return (cert);
}
+/* sigh look up the ml-dsa oid by string */
+static SECOidTag
+FindTagFromString(char *cipherString)
+{
+ SECOidTag tag;
+ SECOidData *oid;
+
+ /* future enhancement: accept dotted oid spec? */
+
+ for (tag = 1; (oid = SECOID_FindOIDByTag(tag)) != NULL; tag++) {
+ /* only interested in oids that we actually understand */
+ if (oid->mechanism == CKM_INVALID_MECHANISM) {
+ continue;
+ }
+ if (PORT_Strcasecmp(oid->desc, cipherString) != 0) {
+ continue;
+ }
+ return tag;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+
static SECStatus
SetSignatureAlgorithm(PLArenaPool *arena,
SECAlgorithmID *signAlg,
SECAlgorithmID *spkiAlg,
SECOidTag hashAlgTag,
SECKEYPrivateKey *privKey,
PRBool pssSign)
{
@@ -2071,20 +2119,71 @@ SetSignatureAlgorithm(PLArenaPool *arena
}
rv = SECOID_SetAlgorithmID(arena, signAlg,
SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
params);
if (rv != SECSuccess) {
SECU_PrintError(progName, "Could not set signature algorithm id.");
return rv;
}
+ } else if (privKey->keyType == mldsaKey) {
+ /* sigh, we need toexport SECKEY_GetParameterSet(), for now
+ * just do it inline */
+ /* this is temp code until we fix it correctly upstream. Don't
+ * push this upstream */
+ SECOidTag algID;
+ SECItem item;
+ CK_ULONG paramSet;
+
+ rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey,
+ CKA_PARAMETER_SET, &item);
+
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "missing parameter set for ml-dsa key.");
+ return SECFailure;
+ }
+ if (item.len != sizeof (paramSet)) {
+ SECU_PrintError(progName, "corrupted parameter set for ml-dsa key.");
+ PORT_Free(item.data);
+ return SECFailure;
+ }
+ paramSet = *(CK_ULONG *)item.data;
+ PORT_Free(item.data);
+ switch (paramSet) {
+ case CKP_ML_DSA_44:
+ algID = FindTagFromString("ML-DSA-44");
+ break;
+ case CKP_ML_DSA_65:
+ algID = FindTagFromString("ML-DSA-65");
+ break;
+ case CKP_ML_DSA_87:
+ algID = FindTagFromString("ML-DSA-87");
+ break;
+ default:
+ algID = SEC_OID_UNKNOWN;
+ break;
+ }
+ if (algID == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ SECU_PrintError(progName, "invalid parameter set for ml-dsa key.");
+ return SECFailure;
+ }
+
+ rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "Could not set signature algorithm id.");
+ return rv;
+ }
} else {
KeyType keyType = SECKEY_GetPrivateKeyType(privKey);
SECOidTag algID;
-
+
+ /* first, try to get the ParameterSet from the key, If the
+ * key as a parameter set, use it, otherwise fall back to
+ * SEC_GetSignatureAlgorithmoidTag */
algID = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag);
if (algID == SEC_OID_UNKNOWN) {
SECU_PrintError(progName, "Unknown key or hash type for issuer.");
return SECFailure;
}
rv = SECOID_SetAlgorithmID(arena, signAlg, algID, 0);
if (rv != SECSuccess) {
SECU_PrintError(progName, "Could not set signature algorithm id.");
@@ -2839,19 +2938,44 @@ certutil_main(int argc, char **argv, PRB
sourceDir = certutil.options[opt_SourceDir].arg;
if (certutil.options[opt_UpgradeID].activated)
upgradeID = certutil.options[opt_UpgradeID].arg;
if (certutil.options[opt_UpgradeTokenName].activated)
upgradeTokenName = certutil.options[opt_UpgradeTokenName].arg;
+ /* must be before opt_KeySize! */
+ /* -k key type */
+ if (certutil.options[opt_KeyType].activated) {
+ char *arg = certutil.options[opt_KeyType].arg;
+ if (PL_strcmp(arg, "rsa") == 0) {
+ keytype = rsaKey;
+ } else if (PL_strcmp(arg, "dsa") == 0) {
+ keytype = dsaKey;
+ } else if (PL_strcmp(arg, "ec") == 0) {
+ keytype = ecKey;
+ } else if (PL_strcmp(arg, "mldsa") == 0) {
+ keytype = mldsaKey;
+ } else if (PL_strcmp(arg, "all") == 0) {
+ keytype = nullKey;
+ } else {
+ /* use an existing private/public key pair */
+ keysource = arg;
+ }
+ } else if (certutil.commands[cmd_ListKeys].activated) {
+ keytype = nullKey;
+ }
+
if (certutil.options[opt_KeySize].activated) {
keysize = PORT_Atoi(certutil.options[opt_KeySize].arg);
- if ((keysize < MIN_KEY_BITS) || (keysize > MAX_KEY_BITS)) {
+ /* mldsa limits are much different that rsa and dsa, don't
+ * do the check here */
+ if ((keytype != mldsaKey) &&
+ ((keysize < MIN_KEY_BITS) || (keysize > MAX_KEY_BITS))) {
PR_fprintf(PR_STDERR,
"%s -g: Keysize must be between %d and %d.\n",
progName, MIN_KEY_BITS, MAX_KEY_BITS);
return 255;
}
if (keytype == ecKey) {
PR_fprintf(PR_STDERR, "%s -g: Not for ec keys.\n", progName);
return 255;
@@ -2872,34 +2996,16 @@ certutil_main(int argc, char **argv, PRB
hashAlgTag = SECU_StringToSignatureAlgTag(arg);
if (hashAlgTag == SEC_OID_UNKNOWN) {
PR_fprintf(PR_STDERR, "%s -Z: %s is not a recognized type.\n",
progName, arg);
return 255;
}
}
- /* -k key type */
- if (certutil.options[opt_KeyType].activated) {
- char *arg = certutil.options[opt_KeyType].arg;
- if (PL_strcmp(arg, "rsa") == 0) {
- keytype = rsaKey;
- } else if (PL_strcmp(arg, "dsa") == 0) {
- keytype = dsaKey;
- } else if (PL_strcmp(arg, "ec") == 0) {
- keytype = ecKey;
- } else if (PL_strcmp(arg, "all") == 0) {
- keytype = nullKey;
- } else {
- /* use an existing private/public key pair */
- keysource = arg;
- }
- } else if (certutil.commands[cmd_ListKeys].activated) {
- keytype = nullKey;
- }
if (certutil.options[opt_KeyOpFlagsOn].activated) {
keyOpFlagsOn = GetOpFlags(certutil.options[opt_KeyOpFlagsOn].arg);
}
if (certutil.options[opt_KeyOpFlagsOff].activated) {
keyOpFlagsOff = GetOpFlags(certutil.options[opt_KeyOpFlagsOff].arg);
keyOpFlagsOn &= ~keyOpFlagsOff; /* make off override on */
}
@@ -2933,19 +3039,22 @@ certutil_main(int argc, char **argv, PRB
srcCertPrefix = certutil.options[opt_SourcePrefix].arg;
} else {
Usage();
}
}
/* -q PQG file or curve name */
if (certutil.options[opt_PQGFile].activated) {
- if ((keytype != dsaKey) && (keytype != ecKey)) {
+ if ((keytype != dsaKey) && (keytype != ecKey) &&
+ (keytype != mldsaKey)) {
PR_fprintf(PR_STDERR, "%s -q: specifies a PQG file for DSA keys"
- " (-k dsa) or a named curve for EC keys (-k ec)\n)",
+ " (-k dsa)\n"
+ " or a named curve for EC keys (-k ec)\n"
+ " or a parameter set for ML-DSA keys (-k mldsa)\n",
progName);
return 255;
}
}
/* -s subject name */
if (certutil.options[opt_Subject].activated) {
subject = CERT_AsciiToName(certutil.options[opt_Subject].arg);
diff --git a/cmd/certutil/keystuff.c b/cmd/certutil/keystuff.c
--- a/cmd/certutil/keystuff.c
+++ b/cmd/certutil/keystuff.c
@@ -512,16 +512,17 @@ CERTUTIL_GeneratePrivateKey(KeyType keyt
int publicExponent, const char *noise,
SECKEYPublicKey **pubkeyp, const char *pqgFile,
PK11AttrFlags attrFlags, CK_FLAGS opFlagsOn,
CK_FLAGS opFlagsOff, secuPWData *pwdata)
{
CK_MECHANISM_TYPE mechanism;
PK11RSAGenParams rsaparams;
SECKEYPQGParams *dsaparams = NULL;
+ CK_ULONG paramSet;
void *params;
SECKEYPrivateKey *privKey = NULL;
if (slot == NULL)
return NULL;
if (PK11_Authenticate(slot, PR_TRUE, pwdata) != SECSuccess)
return NULL;
@@ -564,16 +565,58 @@ CERTUTIL_GeneratePrivateKey(KeyType keyt
}
break;
case ecKey:
mechanism = CKM_EC_KEY_PAIR_GEN;
/* For EC keys, PQGFile determines EC parameters */
if ((params = (void *)getECParams(pqgFile)) == NULL)
return NULL;
break;
+ case mldsaKey:
+ mechanism = CKM_ML_DSA_KEY_PAIR_GEN;
+ /* set paramset */
+ paramSet = 0;
+ if (pqgFile) {
+ if (PORT_Strcasecmp(pqgFile, "ML-DSA-44") == 0) {
+ paramSet = CKP_ML_DSA_44;
+ } else if (PORT_Strcasecmp(pqgFile, "ML-DSA-65") == 0) {
+ paramSet = CKP_ML_DSA_65;
+ } else if (PORT_Strcasecmp(pqgFile, "ML-DSA-87") == 0) {
+ paramSet = CKP_ML_DSA_87;
+ } else {
+ /* if we set pqgfile, it had better be right, don't
+ * fall back to key size */
+ return NULL;
+ }
+ } else switch (size) {
+ /* optionally use the size, either the actual size in bytes
+ * or the short hand ('44', '65', '87') */
+ case 44:
+ case 2560:
+ paramSet = CKP_ML_DSA_44;
+ break;
+ case 65:
+ case 4032:
+ paramSet = CKP_ML_DSA_65;
+ break;
+ case 87:
+ case 4896:
+ paramSet = CKP_ML_DSA_87;
+ break;
+ default:
+ /* force a size to be specified somewhere */
+ return NULL;
+ }
+ /* paranoia, shouldn't be able to happen logically. Code
+ * scanners will scream, but I like belt and suspenders */
+ if (paramSet == 0) {
+ return NULL;
+ }
+ params = &paramSet;
+ break;
default:
return NULL;
}
fprintf(stderr, "\n\n");
fprintf(stderr, "Generating key. This may take a few moments...\n\n");
privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, params, pubkeyp,
diff --git a/cmd/lib/secutil.c b/cmd/lib/secutil.c
--- a/cmd/lib/secutil.c
+++ b/cmd/lib/secutil.c
@@ -1157,16 +1157,30 @@ secu_PrintValidity(FILE *out, CERTValidi
{
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
SECU_PrintTimeChoice(out, &v->notBefore, "Not Before", level + 1);
SECU_PrintTimeChoice(out, &v->notAfter, "Not After ", level + 1);
return 0;
}
+void
+SECU_PrintOidTag(FILE *out, SECOidTag tag, const char *m, int level)
+{
+ const char *desc = SECOID_FindOIDTagDescription(tag);
+
+ if (desc == NULL) {
+ desc = SECOID_FindOIDTagDescription(SEC_OID_UNKNOWN);
+ }
+ SECU_Indent(out, level);
+ if (m != NULL)
+ fprintf(out, "%s: ", m);
+ fprintf(out, "%s\n", desc);
+}
+
/* This function does NOT expect a DER type and length. */
SECOidTag
SECU_PrintObjectID(FILE *out, const SECItem *oid, const char *m, int level)
{
SECOidData *oiddata;
char *oidString = NULL;
oiddata = SECOID_FindOID(oid);
@@ -1504,16 +1518,25 @@ SECU_PrintDSAPublicKey(FILE *out, SECKEY
SECU_Indent(out, level);
fprintf(out, "%s:\n", m);
SECU_PrintInteger(out, &pk->u.dsa.params.prime, "Prime", level + 1);
SECU_PrintInteger(out, &pk->u.dsa.params.subPrime, "Subprime", level + 1);
SECU_PrintInteger(out, &pk->u.dsa.params.base, "Base", level + 1);
SECU_PrintInteger(out, &pk->u.dsa.publicValue, "PublicValue", level + 1);
}
+void
+SECU_PrintMLDSAPublicKey(FILE *out, SECKEYPublicKey *pk, char *m, int level)
+{
+ SECU_Indent(out, level);
+ fprintf(out, "%s:\n", m);
+ SECU_PrintOidTag(out, pk->u.mldsa.params, "Parameter Set", level+1);
+ SECU_PrintAsHex(out, &pk->u.mldsa.publicValue, "PublicValue", level+1);
+}
+
static void
secu_PrintSubjectPublicKeyInfo(FILE *out, PLArenaPool *arena,
CERTSubjectPublicKeyInfo *i, char *msg, int level)
{
SECKEYPublicKey *pk;
SECU_Indent(out, level);
fprintf(out, "%s:\n", msg);
@@ -1529,16 +1552,20 @@ secu_PrintSubjectPublicKeyInfo(FILE *out
case dsaKey:
SECU_PrintDSAPublicKey(out, pk, "DSA Public Key", level + 1);
break;
case ecKey:
secu_PrintECPublicKey(out, pk, "EC Public Key", level + 1);
break;
+ case mldsaKey:
+ SECU_PrintMLDSAPublicKey(out, pk, "ML-DSA Public Key", level + 1);
+ break;
+
case dhKey:
case fortezzaKey:
case keaKey:
SECU_Indent(out, level);
fprintf(out, "unable to format this SPKI algorithm type\n");
goto loser;
default:
SECU_Indent(out, level);
diff --git a/cmd/pk11importtest/pk11importtest.c b/cmd/pk11importtest/pk11importtest.c
--- a/cmd/pk11importtest/pk11importtest.c
+++ b/cmd/pk11importtest/pk11importtest.c
@@ -99,16 +99,19 @@ handleEncryptedPrivateImportTest(char *p
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.dh.publicValue);
break;
case dsaKey:
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.dsa.publicValue);
break;
case ecKey:
SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.ec.publicValue);
break;
+ case mldsaKey:
+ SECITEM_CopyItem(NULL, &pubValue, &pubKey->u.mldsa.publicValue);
+ break;
default:
fprintf(stderr, "Unknown keytype = %d\n", keyType);
goto cleanup;
}
if (pubValue.data == NULL) {
SECU_PrintError(progName, "Unable to allocate memory");
goto cleanup;
}
@@ -193,16 +196,17 @@ static const char *const usageInfo[] = {
" -k keysize size of the rsa, dh, and dsa key to test (default 1024)",
" -C ecc_curve ecc curve (default )",
" -f pwFile file to fetch the password from",
" -p pwString password",
" -r skip rsa test",
" -D skip dsa test",
" -h skip dh test",
" -e skip ec test",
+ " -m skip mldsa test",
};
static int nUsageInfo = sizeof(usageInfo) / sizeof(char *);
static void
Usage(char *progName, FILE *outFile)
{
int i;
fprintf(outFile, "Usage: %s [ commands ] options\n", progName);
@@ -215,45 +219,49 @@ enum {
opt_CertDir,
opt_KeySize,
opt_ECCurve,
opt_PWFile,
opt_PWString,
opt_NoRSA,
opt_NoDSA,
opt_NoEC,
- opt_NoDH
+ opt_NoDH,
+ opt_NoMLDSA,
};
static secuCommandFlag options[] = {
{ /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE },
{ /* opt_KeySize */ 'k', PR_TRUE, 0, PR_FALSE },
{ /* opt_ECCurve */ 'C', PR_TRUE, 0, PR_FALSE },
{ /* opt_PWFile */ 'f', PR_TRUE, 0, PR_FALSE },
{ /* opt_PWString */ 'p', PR_TRUE, 0, PR_FALSE },
{ /* opt_NORSA */ 'r', PR_TRUE, 0, PR_FALSE },
{ /* opt_NoDSA */ 'D', PR_TRUE, 0, PR_FALSE },
+ { /* opt_NoEC */ 'e', PR_TRUE, 0, PR_FALSE },
{ /* opt_NoDH */ 'h', PR_TRUE, 0, PR_FALSE },
- { /* opt_NoEC */ 'e', PR_TRUE, 0, PR_FALSE },
+ { /* opt_NoMLDSA */ 'm', PR_TRUE, 0, PR_FALSE },
};
int
main(int argc, char **argv)
{
char *progName;
SECStatus rv;
secuCommand args;
PK11SlotInfo *slot = NULL;
PRBool failed = PR_FALSE;
secuPWData pwArgs = { PW_NONE, 0 };
PRBool doRSA = PR_TRUE;
PRBool doDSA = PR_TRUE;
PRBool doDH = PR_FALSE; /* NSS currently can't export wrapped DH keys */
PRBool doEC = PR_TRUE;
+ PRBool doMLDSA = PR_TRUE;
PQGParams *pqgParams = NULL;
+ CK_ULONG paramSet = CKP_ML_DSA_44;
int keySize;
args.numCommands = 0;
args.numOptions = sizeof(options) / sizeof(secuCommandFlag);
args.commands = NULL;
args.options = options;
#ifdef XP_PC
@@ -295,16 +303,19 @@ main(int argc, char **argv)
doDSA = PR_FALSE;
}
if (args.options[opt_NoDH].activated) {
doDH = PR_FALSE;
}
if (args.options[opt_NoEC].activated) {
doEC = PR_FALSE;
}
+ if (args.options[opt_NoMLDSA].activated) {
+ doMLDSA = PR_FALSE;
+ }
slot = PK11_GetInternalKeySlot();
if (slot == NULL) {
SECU_PrintError(progName, "Couldn't find the internal key slot\n");
return 255;
}
rv = PK11_Authenticate(slot, PR_TRUE, &pwArgs);
if (rv != SECSuccess) {
@@ -383,16 +394,26 @@ main(int argc, char **argv)
PORT_Free(ecParams.data);
ec_failed:
if (rv != SECSuccess) {
fprintf(stderr, "ECC Import Failed!\n");
failed = PR_TRUE;
}
}
+ if (doMLDSA) {
+ rv = handleEncryptedPrivateImportTest(progName, slot, "ML-DSA",
+ CKM_ML_DSA_KEY_PAIR_GEN,
+ &paramSet, &pwArgs);
+ if (rv != SECSuccess) {
+ fprintf(stderr, "ML-DSA Import Failed!\n");
+ failed = PR_TRUE;
+ }
+ }
+
if (pqgParams) {
PK11_PQG_DestroyParams(pqgParams);
}
if (slot) {
PK11_FreeSlot(slot);
}
diff --git a/coreconf/config.mk b/coreconf/config.mk
--- a/coreconf/config.mk
+++ b/coreconf/config.mk
@@ -174,16 +174,20 @@ endif
ifdef BUILD_LIBPKIX_TESTS
DEFINES += -DBUILD_LIBPKIX_TESTS
endif
ifdef NSS_DISABLE_LIBPKIX
DEFINES += -DNSS_DISABLE_LIBPKIX
endif
+ifdef NSS_ENABLE_ML_DSA
+DEFINES += -DNSS_ENABLE_ML_DSA
+endif
+
ifdef NSS_DISABLE_KYBER
DEFINES += -DNSS_DISABLE_KYBER
endif
ifdef NSS_DISABLE_DBM
DEFINES += -DNSS_DISABLE_DBM
endif
diff --git a/lib/certdb/certdb.c b/lib/certdb/certdb.c
--- a/lib/certdb/certdb.c
+++ b/lib/certdb/certdb.c
@@ -1226,16 +1226,17 @@ CERT_CheckKeyUsage(CERTCertificate *cert
requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
switch (keyType) {
case rsaKey:
requiredUsage |= KU_KEY_ENCIPHERMENT;
break;
case rsaPssKey:
case dsaKey:
+ case mldsaKey:
requiredUsage |= KU_DIGITAL_SIGNATURE;
break;
case dhKey:
requiredUsage |= KU_KEY_AGREEMENT;
break;
case ecKey:
/* Accept either signature or agreement. */
if (!(cert->keyUsage &
diff --git a/lib/certhigh/certvfy.c b/lib/certhigh/certvfy.c
--- a/lib/certhigh/certvfy.c
+++ b/lib/certhigh/certvfy.c
@@ -21,16 +21,17 @@
#endif /* NSS_DISABLE_LIBPKIX */
#include "nsspki.h"
#include "pkitm.h"
#include "pkim.h"
#include "pki3hack.h"
#include "base.h"
#include "keyi.h"
+#include "secmodti.h"
/*
* Check the validity times of a certificate
*/
SECStatus
CERT_CertTimesValid(CERTCertificate *c)
{
SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE);
@@ -152,16 +153,27 @@ checkKeyParams(const SECAlgorithmID *sig
return SECFailure;
}
if (len < minLen) {
return SECFailure;
}
return SECSuccess;
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ if (key->keyType != mldsaKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+ if (key->u.mldsa.params != sigAlg) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
default:
return SECSuccess;
}
}
/*
* verify the signature of a signed data object with the given DER publickey
*/
diff --git a/lib/crmf/crmfcont.c b/lib/crmf/crmfcont.c
--- a/lib/crmf/crmfcont.c
+++ b/lib/crmf/crmfcont.c
@@ -562,16 +562,19 @@ crmf_get_mechanism_from_public_key(SECKE
}
SECItem *
crmf_get_public_value(SECKEYPublicKey *pubKey, SECItem *dest)
{
SECItem *src;
switch (pubKey->keyType) {
+ case mldsaKey:
+ src = &pubKey->u.mldsa.publicValue;
+ break;
case dsaKey:
src = &pubKey->u.dsa.publicValue;
break;
case rsaKey:
src = &pubKey->u.rsa.modulus;
break;
case dhKey:
src = &pubKey->u.dh.publicValue;
@@ -700,16 +703,21 @@ crmf_encrypted_value_unwrap_priv_key(PLA
usage = dhUsage;
usageCount = sizeof(dhUsage) / sizeof(dhUsage[0]);
break;
case dsaKey:
keyType = CKK_DSA;
usage = dsaUsage;
usageCount = sizeof(dsaUsage) / sizeof(dsaUsage[0]);
break;
+ case mldsaKey:
+ keyType = CKK_ML_DSA;
+ usage = dsaUsage;
+ usageCount = sizeof(dsaUsage) / sizeof(dsaUsage[0]);
+ break;
}
PORT_Assert(usage != NULL);
PORT_Assert(usageCount != 0);
*unWrappedKey = PK11_UnwrapPrivKey(slot, wrappingKey, wrapMechType, params,
&encValue->encValue, nickname,
publicValue, PR_TRUE, PR_TRUE,
keyType, usage, usageCount, wincx);
encValue->encValue.len = origLen;
diff --git a/lib/crmf/crmfpop.c b/lib/crmf/crmfpop.c
--- a/lib/crmf/crmfpop.c
+++ b/lib/crmf/crmfpop.c
@@ -56,22 +56,26 @@ CRMF_CertReqMsgSetRAVerifiedPOP(CRMFCert
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
static SECOidTag
crmf_get_key_sign_tag(SECKEYPublicKey *inPubKey)
{
+ SECOidTag hashAlg = SEC_OID_UNKNOWN;
/* maintain backward compatibility with older
* implementations */
if (inPubKey->keyType == rsaKey) {
return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
}
- return SEC_GetSignatureAlgorithmOidTag(inPubKey->keyType, SEC_OID_UNKNOWN);
+ if (inPubKey->keyType == mldsaKey) {
+ hashAlg = inPubKey->u.mldsa.params;
+ }
+ return SEC_GetSignatureAlgorithmOidTag(inPubKey->keyType, hashAlg);
}
static SECAlgorithmID *
crmf_create_poposignkey_algid(PLArenaPool *poolp,
SECKEYPublicKey *inPubKey)
{
SECAlgorithmID *algID;
SECOidTag tag;
diff --git a/lib/cryptohi/keyi.h b/lib/cryptohi/keyi.h
--- a/lib/cryptohi/keyi.h
+++ b/lib/cryptohi/keyi.h
@@ -49,11 +49,27 @@ SECStatus sec_DecodeRSAPSSParams(PLArena
unsigned long *saltLength);
/* convert the encoded RSA-PSS parameters into PKCS #11 mechanism parameters */
SECStatus sec_DecodeRSAPSSParamsToMechanism(PLArenaPool *arena,
const SECItem *params,
CK_RSA_PKCS_PSS_PARAMS *mech,
SECOidTag *hashAlg);
+/* MLDSA mapping functions... private for now */
+/* map various mldsa parameters and lengths back and forth */
+typedef enum {
+ SECKEYPubKeyType=1,
+ SECKEYPrivKeyType,
+ SECKEYSignatureType,
+} SECKEYSizeType;
+
+SECOidTag SECKEY_MLDSAPkcs11ParamsToOidParams(CK_ML_DSA_PARAMETER_SET_TYPE paramSet);
+CK_ML_DSA_PARAMETER_SET_TYPE SECKEY_MLDSAOidParamsToPkcs11Params(SECOidTag tag);
+unsigned int SECKEY_MLDSAOidParamsToLen(SECOidTag oid, SECKEYSizeType type);
+unsigned int SECKEY_MLDSAPkcs11ParamsToLen(CK_ML_DSA_PARAMETER_SET_TYPE paramSet,
+ SECKEYSizeType type);
+SECOidTag SECKEY_MLDSAOidParamsFromLen(unsigned int len, SECKEYSizeType type);
+/* get the parameter set, converted to a key oid, only for new keys like mldsa, mlkem, and shldsa */
+SECOidTag SECKEY_GetParameterSet(const SECKEYPrivateKey *key);
SEC_END_PROTOS
#endif /* _KEYHI_H_ */
diff --git a/lib/cryptohi/keythi.h b/lib/cryptohi/keythi.h
--- a/lib/cryptohi/keythi.h
+++ b/lib/cryptohi/keythi.h
@@ -33,16 +33,17 @@ typedef enum {
dhKey = 4,
keaKey = 5, /* deprecated */
ecKey = 6,
rsaPssKey = 7,
rsaOaepKey = 8,
kyberKey = 9,
edKey = 10,
ecMontKey = 11,
+ mldsaKey = 12,
} KeyType;
/*
** Template Definitions
**/
SEC_BEGIN_PROTOS
extern const SEC_ASN1Template SECKEY_RSAPublicKeyTemplate[];
@@ -183,32 +184,39 @@ typedef struct SECKEYKEAPublicKeyStr SEC
*/
struct SECKEYKyberPublicKeyStr {
KyberParams params;
SECItem publicValue;
};
typedef struct SECKEYKyberPublicKeyStr SECKEYKyberPublicKey;
+struct SECKEYMLDSAPublicKeyStr {
+ SECOidTag params;
+ SECItem publicValue;
+};
+typedef struct SECKEYMLDSAPublicKeyStr SECKEYMLDSAPublicKey;
+
/*
** A Generic public key object.
*/
struct SECKEYPublicKeyStr {
PLArenaPool *arena;
KeyType keyType;
PK11SlotInfo *pkcs11Slot;
CK_OBJECT_HANDLE pkcs11ID;
union {
SECKEYRSAPublicKey rsa;
SECKEYDSAPublicKey dsa;
SECKEYDHPublicKey dh;
SECKEYKEAPublicKey kea;
SECKEYFortezzaPublicKey fortezza;
SECKEYECPublicKey ec;
SECKEYKyberPublicKey kyber;
+ SECKEYMLDSAPublicKey mldsa;
} u;
};
typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
/* bit flag definitions for staticflags */
#define SECKEY_Attributes_Cached 0x1 /* bit 0 states \
whether attributes are cached */
#define SECKEY_CKA_PRIVATE (1U << 1) /* bit 1 is the value of CKA_PRIVATE */
diff --git a/lib/cryptohi/seckey.c b/lib/cryptohi/seckey.c
--- a/lib/cryptohi/seckey.c
+++ b/lib/cryptohi/seckey.c
@@ -10,16 +10,17 @@
#include "secasn1.h"
#include "cert.h"
#include "pk11func.h"
#include "secerr.h"
#include "secdig.h"
#include "prtime.h"
#include "keyi.h"
#include "nss.h"
+#include "secmodti.h" /* for private oids */
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
SEC_ASN1_MKSUB(SEC_IntegerTemplate)
const SEC_ASN1Template CERT_SubjectPublicKeyInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(CERTSubjectPublicKeyInfo) },
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
@@ -158,16 +159,169 @@ SECKEY_CreateRSAPrivateKey(int keySizeIn
param.pe = 65537L;
privk = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &param, pubk,
PR_FALSE, PR_TRUE, cx);
PK11_FreeSlot(slot);
return (privk);
}
+/* MLDSA helper functions... private for now */
+SECOidTag
+SECKEY_MLDSAPkcs11ParamsToOidParams(CK_ML_DSA_PARAMETER_SET_TYPE paramSet)
+{
+ switch (paramSet) {
+ case CKP_ML_DSA_44:
+ return SEC_OID_ML_DSA_44;
+ case CKP_ML_DSA_65:
+ return SEC_OID_ML_DSA_65;
+ case CKP_ML_DSA_87:
+ return SEC_OID_ML_DSA_87;
+ default:
+ break;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+CK_ML_DSA_PARAMETER_SET_TYPE
+SECKEY_MLDSAOidParamsToPkcs11Params(SECOidTag tag)
+{
+ switch (tag) {
+ case SEC_OID_ML_DSA_44:
+ return CKP_ML_DSA_44;
+ case SEC_OID_ML_DSA_65:
+ return CKP_ML_DSA_65;
+ case SEC_OID_ML_DSA_87:
+ return CKP_ML_DSA_87;
+ default:
+ break;
+ }
+ return 0;
+}
+
+unsigned int
+SECKEY_MLDSAOidParamsToLen(SECOidTag oid, SECKEYSizeType type)
+{
+ switch (type) {
+ case SECKEYPubKeyType:
+ switch (oid) {
+ case SEC_OID_ML_DSA_44:
+ return ML_DSA_44_PUBLICKEY_LEN;
+ case SEC_OID_ML_DSA_65:
+ return ML_DSA_65_PUBLICKEY_LEN;
+ case SEC_OID_ML_DSA_87:
+ return ML_DSA_87_PUBLICKEY_LEN;
+ default:
+ break;
+ }
+ break;
+ case SECKEYPrivKeyType:
+ switch (oid) {
+ case SEC_OID_ML_DSA_44:
+ return ML_DSA_44_PRIVATEKEY_LEN;
+ case SEC_OID_ML_DSA_65:
+ return ML_DSA_65_PRIVATEKEY_LEN;
+ case SEC_OID_ML_DSA_87:
+ return ML_DSA_87_PRIVATEKEY_LEN;
+ default:
+ break;
+ }
+ break;
+ case SECKEYSignatureType:
+ switch (oid) {
+ case SEC_OID_ML_DSA_44:
+ return ML_DSA_44_SIGNATURE_LEN;
+ case SEC_OID_ML_DSA_65:
+ return ML_DSA_65_SIGNATURE_LEN;
+ case SEC_OID_ML_DSA_87:
+ return ML_DSA_87_SIGNATURE_LEN;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+unsigned int
+SECKEY_MLDSAPkcs11ParamsToLen(CK_ML_DSA_PARAMETER_SET_TYPE paramSet,
+ SECKEYSizeType type)
+{
+ SECOidTag tag = SECKEY_MLDSAPkcs11ParamsToOidParams(paramSet);
+ return SECKEY_MLDSAOidParamsToLen(tag, type);
+}
+
+SECOidTag
+SECKEY_MLDSAOidParamsFromLen(unsigned int len, SECKEYSizeType type)
+{
+ switch (type) {
+ case SECKEYPubKeyType:
+ switch (len) {
+ case ML_DSA_44_PUBLICKEY_LEN:
+ return SEC_OID_ML_DSA_44;
+ case ML_DSA_65_PUBLICKEY_LEN:
+ return SEC_OID_ML_DSA_65;
+ case ML_DSA_87_PUBLICKEY_LEN:
+ return SEC_OID_ML_DSA_87;
+ default:
+ break;
+ }
+ break;
+ case SECKEYPrivKeyType:
+ switch (len) {
+ case ML_DSA_44_PRIVATEKEY_LEN:
+ return SEC_OID_ML_DSA_44;
+ case ML_DSA_65_PRIVATEKEY_LEN:
+ return SEC_OID_ML_DSA_65;
+ case ML_DSA_87_PRIVATEKEY_LEN:
+ return SEC_OID_ML_DSA_87;
+ default:
+ break;
+ }
+ break;
+ case SECKEYSignatureType:
+ switch (len) {
+ case ML_DSA_44_SIGNATURE_LEN:
+ return SEC_OID_ML_DSA_44;
+ case ML_DSA_65_SIGNATURE_LEN:
+ return SEC_OID_ML_DSA_65;
+ case ML_DSA_87_SIGNATURE_LEN:
+ return SEC_OID_ML_DSA_87;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+/* make this function generic. multiple key types will be able to use
+ * it (ml-kem, ml=dsa, shl-dsa, fn-dsa, etc. ) */
+SECOidTag
+SECKEY_GetParameterSet(const SECKEYPrivateKey *key)
+{
+ CK_ULONG paramSet = PK11_ReadULongAttribute(key->pkcs11Slot,
+ key->pkcs11ID,
+ CKA_PARAMETER_SET);
+ if (paramSet == CK_UNAVAILABLE_INFORMATION) {
+ return SEC_OID_UNKNOWN;
+ }
+ switch (key->keyType) {
+ case mldsaKey:
+ return SECKEY_MLDSAPkcs11ParamsToOidParams(paramSet);
+ default:
+ break;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
/* Create a DH key pair in any slot able to do so,
** This is a "session" (temporary), not "token" (permanent) key.
** Because of the high probability that this key will need to be moved to
** another token, and the high cost of moving "sensitive" keys, we attempt
** to create this key pair without the "sensitive" attribute, but revert to
** creating a "sensitive" key if necessary.
*/
SECKEYPrivateKey *
@@ -558,16 +712,21 @@ seckey_GetKeyType(SECOidTag tag)
case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
keyType = rsaKey;
break;
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ keyType = mldsaKey;
+ break;
default:
keyType = nullKey;
}
return keyType;
}
/* Function used to determine what kind of cert we are dealing with. */
KeyType
@@ -734,17 +893,29 @@ seckey_ExtractPublicKey(const CERTSubjec
break;
}
pubk->u.ec.encoding = ECPoint_Undefined;
rv = seckey_HasCurveOID(pubk);
if (rv == SECSuccess) {
return pubk;
}
break;
-
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ pubk->keyType = mldsaKey;
+ pubk->u.mldsa.params = tag;
+ /* key length should match the oid */
+ if (newOs.len != SECKEY_MLDSAOidParamsToLen(tag, SECKEYPubKeyType)) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ break;
+ }
+ /* newOs is already in the arena, we can just copy the data */
+ pubk->u.mldsa.publicValue = newOs;
+ return pubk;
default:
PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
break;
}
SECKEY_DestroyPublicKey(pubk);
return NULL;
}
@@ -1110,29 +1281,34 @@ SECKEY_PublicKeyStrengthInBits(const SEC
case dhKey:
bitSize = SECKEY_BigIntegerBitLength(&pubk->u.dh.prime);
break;
case ecKey:
case edKey:
case ecMontKey:
bitSize = SECKEY_ECParamsToKeySize(&pubk->u.ec.DEREncodedParams);
break;
+ case mldsaKey:
+ bitSize = SECKEY_MLDSAOidParamsToLen(pubk->u.mldsa.params,
+ SECKEYPubKeyType)*8;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
break;
}
return bitSize;
}
unsigned
SECKEY_PrivateKeyStrengthInBits(const SECKEYPrivateKey *privk)
{
unsigned bitSize = 0;
SECItem params = { siBuffer, NULL, 0 };
SECStatus rv;
+ SECOidTag paramSet;
if (!privk) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return 0;
}
/* interpret modulus length as key strength */
switch (privk->keyType) {
@@ -1172,23 +1348,27 @@ SECKEY_PrivateKeyStrengthInBits(const SE
rv = PK11_ReadAttribute(privk->pkcs11Slot, privk->pkcs11ID,
CKA_EC_PARAMS, NULL, &params);
if ((rv != SECSuccess) || (params.data == NULL)) {
return 0;
}
bitSize = SECKEY_ECParamsToKeySize(&params);
PORT_Free(params.data);
return bitSize;
+ case mldsaKey:
+ paramSet = SECKEY_GetParameterSet(privk);
+ return SECKEY_MLDSAOidParamsToLen(paramSet, SECKEYPrivKeyType)*8;
default:
break;
}
PORT_SetError(SEC_ERROR_INVALID_KEY);
return 0;
}
+
/* returns signature length in bytes (not bits) */
unsigned
SECKEY_SignatureLen(const SECKEYPublicKey *pubk)
{
unsigned size;
switch (pubk->keyType) {
case rsaKey:
@@ -1204,16 +1384,19 @@ SECKEY_SignatureLen(const SECKEYPublicKe
return pubk->u.dsa.params.subPrime.len * 2;
case ecKey:
case edKey:
case ecMontKey:
/* Get the base point order length in bits and adjust */
size = SECKEY_ECParamsToBasePointOrderLen(
&pubk->u.ec.DEREncodedParams);
return ((size + 7) / 8) * 2;
+ case mldsaKey:
+ return SECKEY_MLDSAOidParamsToLen(pubk->u.mldsa.params,
+ SECKEYSignatureType);
default:
break;
}
PORT_SetError(SEC_ERROR_INVALID_KEY);
return 0;
}
SECKEYPrivateKey *
@@ -1349,16 +1532,21 @@ SECKEY_CopyPublicKey(const SECKEYPublicK
break;
case nullKey:
return copyk;
case kyberKey:
copyk->u.kyber.params = pubk->u.kyber.params;
rv = SECITEM_CopyItem(arena, &copyk->u.kyber.publicValue,
&pubk->u.kyber.publicValue);
break;
+ case mldsaKey:
+ copyk->u.mldsa.params = pubk->u.mldsa.params;
+ rv = SECITEM_CopyItem(arena, &copyk->u.mldsa.publicValue,
+ &pubk->u.mldsa.publicValue);
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
rv = SECFailure;
break;
}
if (rv == SECSuccess)
return copyk;
@@ -1390,16 +1578,18 @@ SECKEY_EnforceKeySize(KeyType keyType, u
case dhKey:
case keaKey:
opt = NSS_DH_MIN_KEY_SIZE;
break;
case ecKey:
opt = NSS_ECC_MIN_KEY_SIZE;
break;
case nullKey:
+ case mldsaKey:
+ return SECSuccess; /* add policy later */
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
PORT_Assert(opt != -1);
rv = NSS_OptionGet(opt, &optVal);
if (rv != SECSuccess) {
return rv;
@@ -1579,16 +1769,29 @@ SECKEY_ConvertToPublicKey(SECKEYPrivateK
rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
CKA_EC_POINT, arena, &pubk->u.ec.publicValue);
if (rv != SECSuccess) {
break;
}
}
pubk->u.ec.encoding = ECPoint_Undefined;
return pubk;
+ case mldsaKey:
+ pubKeyHandle = seckey_FindPublicKeyHandle(privk, pubk);
+ if (pubKeyHandle == CK_INVALID_HANDLE)
+ break;
+ pubk->u.mldsa.params = SECKEY_GetParameterSet(privk);
+ if (pubk->u.mldsa.params == SEC_OID_UNKNOWN) {
+ break;
+ }
+ rv = PK11_ReadAttribute(privk->pkcs11Slot, pubKeyHandle,
+ CKA_VALUE, arena, &pubk->u.mldsa.publicValue);
+ if (rv != SECSuccess)
+ break;
+ return pubk;
default:
break;
}
/* must use Destroy public key here, because some paths create temporary
* PKCS #11 objects which need to be freed */
SECKEY_DestroyPublicKey(pubk);
return NULL;
@@ -1720,16 +1923,35 @@ seckey_CreateSubjectPublicKeyInfo_helper
*/
spki->subjectPublicKey.len <<= 3;
/*
* We got a good one; return it.
*/
return spki;
}
break;
+ case mldsaKey:
+ /* params == Algid for mldsaKey */
+ rv = SECOID_SetAlgorithmID(arena, &spki->algorithm,
+ pubk->u.mldsa.params, NULL);
+ if (rv != SECSuccess) {
+ break;
+ }
+ /* and key == spki */
+ rv = SECITEM_CopyItem(arena, &spki->subjectPublicKey,
+ &pubk->u.mldsa.publicValue);
+ if (rv == SECSuccess) {
+ /*
+ * The stored value is supposed to be a BIT_STRING,
+ * so convert the length.
+ */
+ spki->subjectPublicKey.len <<= 3;
+ return spki;
+ }
+ break;
case dhKey: /* later... */
break;
default:
break;
}
} else {
PORT_SetError(SEC_ERROR_NO_MEMORY);
@@ -2109,16 +2331,28 @@ SECKEY_ImportDERPublicKey(const SECItem
rv = SEC_QuickDERDecodeItem(pubk->arena, pubk, SECKEY_DSAPublicKeyTemplate, &newDerKey);
pubk->keyType = dsaKey;
break;
case CKK_DH:
prepare_dh_pub_key_for_asn1(pubk);
rv = SEC_QuickDERDecodeItem(pubk->arena, pubk, SECKEY_DHPublicKeyTemplate, &newDerKey);
pubk->keyType = dhKey;
break;
+ case CKK_ML_DSA:
+ pubk->keyType = mldsaKey;
+ /* ml_dsa has no derencoding */
+ pubk->u.mldsa.publicValue = newDerKey;
+ pubk->u.mldsa.params = SECKEY_MLDSAOidParamsFromLen(newDerKey.len,
+ SECKEYPubKeyType);
+ if (pubk->u.mldsa.params == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ rv = SECFailure;
+ }
+ break;
+
default:
rv = SECFailure;
break;
}
finish:
if (rv != SECSuccess) {
if (arena != NULL) {
diff --git a/lib/cryptohi/secsign.c b/lib/cryptohi/secsign.c
--- a/lib/cryptohi/secsign.c
+++ b/lib/cryptohi/secsign.c
@@ -516,21 +516,28 @@ sec_DerSignData(PLArenaPool *arena, SECI
default:
algID = SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST;
break;
}
break;
case ecKey:
algID = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE;
break;
+ case mldsaKey:
+ algID = SECKEY_GetParameterSet(pk);
+ break;
default:
- PORT_SetError(SEC_ERROR_INVALID_KEY);
- return SECFailure;
+ algID = SEC_OID_UNKNOWN;
+ break;
}
}
+ if (algID == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ return SECFailure;
+ }
/* Sign input buffer */
rv = sec_SignData(&it, buf, len, pk, algID, params);
if (rv)
goto loser;
/* Fill out SignedData object */
PORT_Memset(&sd, 0, sizeof(sd));
@@ -579,32 +586,40 @@ SGN_Digest(SECKEYPrivateKey *privKey,
PLArenaPool *arena = 0;
SGNDigestInfo *di = 0;
SECOidTag enctag;
PRUint32 policyFlags;
PRInt32 optFlags;
result->data = 0;
+
if (NSS_OptionGet(NSS_KEY_SIZE_POLICY_FLAGS, &optFlags) != SECFailure) {
if (optFlags & NSS_KEY_SIZE_POLICY_SIGN_FLAG) {
rv = SECKEY_EnforceKeySize(privKey->keyType,
SECKEY_PrivateKeyStrengthInBits(privKey),
SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED);
if (rv != SECSuccess) {
return SECFailure;
}
}
}
/* check the policy on the hash algorithm */
if ((NSS_GetAlgorithmPolicy(algtag, &policyFlags) == SECFailure) ||
!(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) {
PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
+
+ if (privKey->keyType == mldsaKey) {
+ /* whatever the input hash was, we now mark it
+ * as the signature value since mldsaKey is a full
+ * hash and sign */
+ algtag = SECKEY_GetParameterSet(privKey);
+ }
/* check the policy on the encryption algorithm */
enctag = sec_GetEncAlgFromSigAlg(
SEC_GetSignatureAlgorithmOidTag(privKey->keyType, algtag));
if ((enctag == SEC_OID_UNKNOWN) ||
(NSS_GetAlgorithmPolicy(enctag, &policyFlags) == SECFailure) ||
!(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) {
PORT_SetError(SEC_ERROR_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
@@ -714,16 +729,19 @@ SEC_GetSignatureAlgorithmOidTag(KeyType
case SEC_OID_UNKNOWN: /* default for DSA if not specified */
case SEC_OID_SHA256:
sigTag = SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST;
break;
default:
break;
}
break;
+ case mldsaKey:
+ sigTag = hashAlgTag;
+ break;
case ecKey:
switch (hashAlgTag) {
case SEC_OID_SHA1:
sigTag = SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE;
break;
case SEC_OID_SHA224:
sigTag = SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE;
break;
@@ -969,17 +987,16 @@ SEC_CreateSignatureAlgorithmParameters(P
SECOidTag hashAlgTag,
const SECItem *params,
const SECKEYPrivateKey *key)
{
switch (signAlgTag) {
case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
return sec_CreateRSAPSSParameters(arena, result,
hashAlgTag, params, key);
-
default:
if (params == NULL)
return NULL;
if (result == NULL)
result = SECITEM_AllocItem(arena, NULL, 0);
if (SECITEM_CopyItem(arena, result, params) != SECSuccess)
return NULL;
return result;
diff --git a/lib/cryptohi/secvfy.c b/lib/cryptohi/secvfy.c
--- a/lib/cryptohi/secvfy.c
+++ b/lib/cryptohi/secvfy.c
@@ -13,17 +13,17 @@
#include "secoid.h"
#include "pk11func.h"
#include "pkcs1sig.h"
#include "secdig.h"
#include "secerr.h"
#include "keyi.h"
#include "nss.h"
#include "prenv.h"
-
+#include "secmodti.h"
/*
** Recover the DigestInfo from an RSA PKCS#1 signature.
**
** If givenDigestAlg != SEC_OID_UNKNOWN, copy givenDigestAlg to digestAlgOut.
** Otherwise, parse the DigestInfo structure and store the decoded digest
** algorithm into digestAlgOut.
**
** Store the encoded DigestInfo into digestInfo.
@@ -185,16 +185,19 @@ checkedSignatureLen(const SECKEYPublicKe
maxSigLen = (RSA_MAX_MODULUS_BITS + 7) / 8;
break;
case dsaKey:
maxSigLen = DSA_MAX_SIGNATURE_LEN;
break;
case ecKey:
maxSigLen = 2 * MAX_ECKEY_LEN;
break;
+ case mldsaKey:
+ maxSigLen = MAX_ML_DSA_SIGNATURE_LEN;
+ break;
default:
PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
return 0;
}
PORT_Assert(maxSigLen <= MAX_SIGNATURE_LEN);
if (sigLen > maxSigLen) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return 0;
@@ -289,16 +292,20 @@ sec_GetEncAlgFromSigAlg(SECOidTag sigAlg
case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_RECOMMENDED_DIGEST:
case SEC_OID_ANSIX962_ECDSA_SIGNATURE_SPECIFIED_DIGEST:
return SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ return sigAlg;
/* we don't implement MD4 hashes */
case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
default:
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
break;
}
return SEC_OID_UNKNOWN;
}
@@ -392,16 +399,20 @@ sec_GetCombinedMech(SECOidTag encalg, SE
case SEC_OID_PKCS1_RSA_ENCRYPTION:
return sec_RSAPKCS1GetCombinedMech(hashalg);
case SEC_OID_PKCS1_RSA_PSS_SIGNATURE:
return sec_RSAPSSGetCombinedMech(hashalg);
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
return sec_ECDSAGetCombinedMech(hashalg);
case SEC_OID_ANSIX9_DSA_SIGNATURE:
return sec_DSAGetCombinedMech(hashalg);
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ return CKM_ML_DSA; /* ignore hashalg */
default:
break;
}
return CKM_INVALID_MECHANISM;
}
/*
* Pulls the hash algorithm, signing algorithm, and key type out of a
@@ -425,16 +436,17 @@ sec_DecodeSigAlg(const SECKEYPublicKey *
const SECItem *param, SECOidTag *encalgp, SECOidTag *hashalg,
CK_MECHANISM_TYPE *mechp, SECItem *mechparamsp)
{
unsigned int len;
PLArenaPool *arena;
SECStatus rv;
SECItem oid;
SECOidTag encalg;
+ PRBool comboRequired = PR_TRUE;
char *evp;
PR_ASSERT(hashalg != NULL);
PR_ASSERT(encalgp != NULL);
PR_ASSERT(mechp != NULL);
/* Get the expected combined mechanism from the signature OID
* We'll override it in the table below if necessary */
*mechp = PK11_AlgtagToMechanism(sigAlg);
@@ -583,16 +595,23 @@ sec_DecodeSigAlg(const SECKEYPublicKey *
}
/* only accept hash algorithms */
if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) {
/* error set by HASH_GetHashTypeByOidTag */
return SECFailure;
}
*mechp = sec_ECDSAGetCombinedMech(*hashalg);
break;
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ comboRequired = PR_TRUE;
+ *hashalg = sigAlg;
+ /* decode params to get the sign context and set (mechparamsp) */
+ break;
/* we don't implement MD4 hashes */
case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION:
default:
*mechp = CKM_INVALID_MECHANISM;
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
@@ -606,26 +625,31 @@ sec_DecodeSigAlg(const SECKEYPublicKey *
/* for testing, we want to be able to turn off combo signatures to
* 1) make sure the fallback code is working correctly so we know
* we can handle cases where the fallback doesn't work.
* 2) make sure that the combo code is compatible with the non-combo
* versions.
* We know if we are signing or verifying based on the value of 'key'.
* Since key is a public key, then it's set to NULL for signing */
evp = PR_GetEnvSecure("NSS_COMBO_SIGNATURES");
- if (evp) {
+ if (evp && !comboRequired) {
if (PORT_Strcasecmp(evp, "none") == 0) {
*mechp = CKM_INVALID_MECHANISM;
} else if (key && (PORT_Strcasecmp(evp, "signonly") == 0)) {
*mechp = CKM_INVALID_MECHANISM;
} else if (!key && (PORT_Strcasecmp(evp, "vfyonly") == 0)) {
*mechp = CKM_INVALID_MECHANISM;
}
/* anything else we take as use combo, which is the default */
}
+ /* now enforce the combo requires requirement */
+ if (comboRequired && (*mechp == CKM_INVALID_MECHANISM)) {
+ SECITEM_FreeItem(mechparamsp, PR_FALSE);
+ return SECFailure;
+ }
return SECSuccess;
}
SECStatus
vfy_ImportPublicKey(VFYContext *cx)
{
PK11SlotInfo *slot;
@@ -805,17 +829,19 @@ vfy_CreateContext(const SECKEYPublicKey
rv = vfy_SetPKCS11SigFromX509Sig(cx, sig);
}
if (rv != SECSuccess) {
goto loser;
}
}
/* check hash alg again, RSA may have changed it.*/
- if (HASH_GetHashTypeByOidTag(cx->hashAlg) == HASH_AlgNULL) {
+ /* combo mechs set the hash to the sigAlg */
+ if ((cx->hashAlg != cx->encAlg) &&
+ (HASH_GetHashTypeByOidTag(cx->hashAlg) == HASH_AlgNULL)) {
/* error set by HASH_GetHashTypeByOidTag */
goto loser;
}
/* check the policy on the hash algorithm. Do this after
* the rsa decode because some uses of this function get hash implicitly
* from the RSA signature itself. */
if ((NSS_GetAlgorithmPolicy(cx->hashAlg, &policyFlags) == SECFailure) ||
!(policyFlags & NSS_USE_ALG_IN_ANY_SIGNATURE)) {
@@ -1087,16 +1113,22 @@ vfy_VerifyDigest(const SECItem *digest,
rv = SECFailure;
break;
}
rv = PK11_Verify(cx->key, &dsasig, (SECItem *)digest, cx->wincx);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
}
break;
+ case mldsaKey:
+ rv = PK11_Verify(cx->key, sig, (SECItem *)digest, cx->wincx);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ }
+ break;
default:
break;
}
VFY_DestroyContext(cx, PR_TRUE);
}
return rv;
}
diff --git a/lib/freebl/Makefile b/lib/freebl/Makefile
--- a/lib/freebl/Makefile
+++ b/lib/freebl/Makefile
@@ -603,16 +603,71 @@ ifndef NSS_DISABLE_KYBER
KYBER_PQCRYSTALS = kyber-pqcrystals-ref.c
endif
VERIFIED_SRCS += Hacl_Hash_SHA3.c Hacl_P256.c Hacl_P384.c Hacl_P521.c libcrux_mlkem768_portable.c libcrux_mlkem_portable.c libcrux_core.c
VERIFIED_SRCS += libcrux_mlkem1024_portable.c
VERIFIED_SRCS += Hacl_Ed25519.c
VERIFIED_SRCS += Hacl_Curve25519_51.c
+ifdef NSS_ENABLE_ML_DSA
+ifdef ML_DSA_OQS
+INCLUDES += -Ioqs
+ML_DSA_SRCS += ml_dsa_44_ref_ntt.c
+ML_DSA_SRCS += ml_dsa_44_ref_packing.c
+ML_DSA_SRCS += ml_dsa_44_ref_poly.c
+ML_DSA_SRCS += ml_dsa_44_ref_polyvec.c
+ML_DSA_SRCS += ml_dsa_44_ref_reduce.c
+ML_DSA_SRCS += ml_dsa_44_ref_rounding.c
+ML_DSA_SRCS += ml_dsa_44_ref_sign.c
+ML_DSA_SRCS += ml_dsa_44_ref_symmetric-shake.c
+ML_DSA_SRCS += ml_dsa_65_ref_ntt.c
+ML_DSA_SRCS += ml_dsa_65_ref_packing.c
+ML_DSA_SRCS += ml_dsa_65_ref_poly.c
+ML_DSA_SRCS += ml_dsa_65_ref_polyvec.c
+ML_DSA_SRCS += ml_dsa_65_ref_reduce.c
+ML_DSA_SRCS += ml_dsa_65_ref_rounding.c
+ML_DSA_SRCS += ml_dsa_65_ref_sign.c
+ML_DSA_SRCS += ml_dsa_65_ref_symmetric-shake.c
+ML_DSA_SRCS += ml_dsa_87_ref_ntt.c
+ML_DSA_SRCS += ml_dsa_87_ref_packing.c
+ML_DSA_SRCS += ml_dsa_87_ref_poly.c
+ML_DSA_SRCS += ml_dsa_87_ref_polyvec.c
+ML_DSA_SRCS += ml_dsa_87_ref_reduce.c
+ML_DSA_SRCS += ml_dsa_87_ref_rounding.c
+ML_DSA_SRCS += ml_dsa_87_ref_sign.c
+ML_DSA_SRCS += ml_dsa_87_ref_symmetric-shake.c
+else
+INCLUDES += -Ileancrypto
+ML_DSA_SRCS += ml_dsa_44_ntt.c
+ML_DSA_SRCS += ml_dsa_44_poly.c
+ML_DSA_SRCS += ml_dsa_44_rounding.c
+ML_DSA_SRCS += ml_dsa_44_signature_c.c
+ML_DSA_SRCS += ml_dsa_44_signature_helper.c
+ML_DSA_SRCS += ml_dsa_65_ntt.c
+ML_DSA_SRCS += ml_dsa_65_poly.c
+ML_DSA_SRCS += ml_dsa_65_rounding.c
+ML_DSA_SRCS += ml_dsa_65_signature_c.c
+ML_DSA_SRCS += ml_dsa_65_signature_helper.c
+ML_DSA_SRCS += ml_dsa_87_ntt.c
+ML_DSA_SRCS += ml_dsa_87_poly.c
+ML_DSA_SRCS += ml_dsa_87_rounding.c
+ML_DSA_SRCS += ml_dsa_87_signature_c.c
+ML_DSA_SRCS += ml_dsa_87_signature_helper.c
+ML_DSA_SRCS += signature_domain_separation.c
+ML_DSA_SRCS += mldsa_api.c
+ML_DSA_SRCS += mldsa_zetas.c
+#ML_DSA_SRCS += ml_dsa_44_debug.c
+#ML_DSA_SRCS += ml_dsa_65_debug.c
+#ML_DSA_SRCS += ml_dsa_87_debug.c
+endif
+endif
+
+
+
# Bug 1918767 / Bug 1918711 - by setting KRML_MUSTINLINE=inline here, we
# avoid it being defined to `inline __forceinline` (for msvc) or `inline
# __attribute__((always_inline))` (for gcc/clang) in
# verified/karamel/include/krml/internal/target.h. These other
# configurations can cause excessive stack usage.
DEFINES += -DKRML_MUSTINLINE=inline
ifeq (,$(filter-out x86_64 aarch64,$(CPU_ARCH)))
@@ -640,18 +695,18 @@ include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
rijndael_tables:
$(CC) -o $(OBJDIR)/make_rijndael_tab rijndael_tables.c \
$(DEFINES) $(INCLUDES) $(OBJDIR)/libfreebl.a
$(OBJDIR)/make_rijndael_tab
-vpath %.h mpi ecl verified deprecated
-vpath %.c mpi ecl verified deprecated
+vpath %.h mpi ecl verified oqs leancrypto deprecated
+vpath %.c mpi ecl verified oqs leancrypto deprecated
vpath %.S mpi ecl
vpath %.s mpi ecl
vpath %.asm mpi ecl
INCLUDES += -Impi -Iecl -Iverified -Iverified/internal -Iverified/karamel/include -Iverified/karamel/krmllib/dist/minimal -Iverified/eurydice -Ideprecated
DEFINES += -DMP_API_COMPATIBLE
diff --git a/lib/freebl/blapi.h b/lib/freebl/blapi.h
--- a/lib/freebl/blapi.h
+++ b/lib/freebl/blapi.h
@@ -443,17 +443,17 @@ JPAKE_Round2(PLArenaPool *arena, const S
/* K = (B/g^(x2*x4*s))^x2 (mod p)
*
* K will be allocated in the arena. The arena is *not* optional so do not pass
* NULL for the arena parameter. The arena should be zeroed when it is freed.
*/
SECStatus
JPAKE_Final(PLArenaPool *arena, const SECItem *p, const SECItem *q,
const SECItem *x2, const SECItem *gx4, const SECItem *x2s,
- const SECItem *B, SECItem *K);
+ const SECItem *B_, SECItem *K_);
/******************************************************
** Elliptic Curve algorithms
*/
/* Generates a public and private key, both of which are encoded
** in a single ECPrivateKey struct. Params is input, privKey are
** output.
@@ -1814,18 +1814,18 @@ PQG_ParamGenSeedLen(
* pick a default value (typically the smallest secure value for these
* variables).
*
* The verify parameters will conform to FIPS186-3 using the smallest
* permissible hash for the key strength.
*/
extern SECStatus
PQG_ParamGenV2(
- unsigned int L, /* input : determines length of P. */
- unsigned int N, /* input : determines length of Q. */
+ unsigned int L_, /* input : determines length of P. */
+ unsigned int N_, /* input : determines length of Q. */
unsigned int seedBytes, /* input : length of seed in bytes.*/
PQGParams **pParams, /* output: P Q and G returned here */
PQGVerify **pVfy); /* output: counter and seed. */
/* Test PQGParams for validity as DSS PQG values.
* If vfy is non-NULL, test PQGParams to make sure they were generated
* using the specified seed, counter, and h values.
*
@@ -1942,11 +1942,27 @@ extern SECStatus ED_VerifyMessage(ECPubl
*/
extern SECStatus ED_DerivePublicKey(const SECItem *privateKey, SECItem *publicKey);
extern SECStatus X25519_DerivePublicKey(const SECItem *privateKey, SECItem *publicKey);
/* Public key derivation is supported only for the curves supporting pt_mul method. */
extern SECStatus EC_DerivePublicKey(const SECItem *privateKey, const ECParams *ecParams, SECItem *publicKey);
+/*
+ * ML_DSA functions
+ */
+SECStatus MLDSA_NewKey(CK_ML_DSA_PARAMETER_SET_TYPE paramSet, SECItem *seed,
+ MLDSAPrivateKey *privKey, MLDSAPublicKey *pubKey);
+SECStatus MLDSA_SignInit(MLDSAPrivateKey *key, CK_HEDGE_TYPE hedgeType,
+ const SECItem *sgnCtx, MLDSAContext **ctx);
+SECStatus MLDSA_SignUpdate(MLDSAContext *ctx, const SECItem *data);
+SECStatus MLDSA_SignFinal(MLDSAContext *ctx, SECItem *signature);
+
+SECStatus MLDSA_VerifyInit(MLDSAPublicKey *key, const SECItem *sgnCtx,
+ MLDSAContext **ctx);
+SECStatus MLDSA_VerifyUpdate(MLDSAContext *ctx, const SECItem *data) ;
+SECStatus MLDSA_VerifyFinal(MLDSAContext *ctx, const SECItem *signature);
+
+
SEC_END_PROTOS
#endif /* _BLAPI_H_ */
diff --git a/lib/freebl/blapit.h b/lib/freebl/blapit.h
--- a/lib/freebl/blapit.h
+++ b/lib/freebl/blapit.h
@@ -7,16 +7,18 @@
#ifndef _BLAPIT_H_
#define _BLAPIT_H_
#include "seccomon.h"
#include "prlink.h"
#include "plarena.h"
#include "ecl-exp.h"
+#include "pkcs11t.h"
+#include "ml_dsat.h"
/* RC2 operation modes */
#define NSS_RC2 0
#define NSS_RC2_CBC 1
/* RC5 operation modes */
#define NSS_RC5 0
#define NSS_RC5_CBC 1
@@ -150,18 +152,18 @@ typedef int __BLAPI_DEPRECATED __attribu
*/
#define RSA_MIN_MODULUS_BITS 128
#define RSA_MAX_MODULUS_BITS 16384
#define RSA_MAX_EXPONENT_BITS 64
#define DH_MIN_P_BITS 128
#define DH_MAX_P_BITS 16384
/* max signature for all our supported signatures */
-/* currently RSA is the biggest */
-#define MAX_SIGNATURE_LEN ((RSA_MAX_MODULUS_BITS + 7) / 8)
+/* currently ML-DSA is the biggest */
+#define MAX_SIGNATURE_LEN MAX_ML_DSA_SIGNATURE_LEN
/*
* The FIPS 186-1 algorithm for generating primes P and Q allows only 9
* distinct values for the length of P, and only one value for the
* length of Q.
* The algorithm uses a variable j to indicate which of the 9 lengths
* of P is to be used.
* The following table relates j to the lengths of P and Q in bits.
@@ -356,16 +358,41 @@ typedef struct DSAPublicKeyStr DSAPublic
struct DSAPrivateKeyStr {
PQGParams params;
SECItem publicValue;
SECItem privateValue;
};
typedef struct DSAPrivateKeyStr DSAPrivateKey;
+
+/* ML DSA structures */
+typedef struct MLDSAPrivateKeyStr MLDSAPrivateKey;
+typedef struct MLDSAPublicKeyStr MLDSAPublicKey;
+typedef struct MLDSAContextStr MLDSAContext;
+
+/* if we don't actually have DSA support, don't expose the
+ * ML_DSA key structures which have defines in the ml_dsa
+ * headers */
+#ifdef NSS_ENABLE_ML_DSA
+struct MLDSAPrivateKeyStr {
+ CK_ML_DSA_PARAMETER_SET_TYPE paramSet;
+ unsigned char keyVal[MAX_ML_DSA_PRIVATE_KEY_LEN];
+ unsigned int keyValLen;
+ unsigned char seed[ML_DSA_SEED_LEN];
+ unsigned int seedLen;
+};
+
+struct MLDSAPublicKeyStr {
+ CK_ML_DSA_PARAMETER_SET_TYPE paramSet;
+ unsigned char keyVal[MAX_ML_DSA_PUBLIC_KEY_LEN];
+ unsigned int keyValLen;
+};
+#endif
+
/***************************************************************************
** Diffie-Hellman Public and Private Key and related structures
** Structure member names suggested by PKCS#3.
*/
struct DHParamsStr {
PLArenaPool *arena;
SECItem prime; /* p */
@@ -385,16 +412,18 @@ struct DHPrivateKeyStr {
PLArenaPool *arena;
SECItem prime;
SECItem base;
SECItem publicValue;
SECItem privateValue;
};
typedef struct DHPrivateKeyStr DHPrivateKey;
+
+
/***************************************************************************
** Data structures used for elliptic curve parameters and
** public and private keys.
*/
/*
** The ECParams data structures can encode elliptic curve
** parameters for both GFp and GF2m curves.
diff --git a/lib/freebl/ldvector.c b/lib/freebl/ldvector.c
--- a/lib/freebl/ldvector.c
+++ b/lib/freebl/ldvector.c
@@ -444,16 +444,26 @@ static const struct FREEBLVectorStr vect
ED_DerivePublicKey,
/* End of version 3.028 */
X25519_DerivePublicKey,
/* End of version 3.029 */
EC_DerivePublicKey,
/* End of version 3.030 */
+
+ MLDSA_NewKey,
+ MLDSA_SignInit,
+ MLDSA_SignUpdate,
+ MLDSA_SignFinal,
+ MLDSA_VerifyInit,
+ MLDSA_VerifyUpdate,
+ MLDSA_VerifyFinal,
+ /* End of version 3.031 */
+
};
const FREEBLVector*
FREEBL_GetVector(void)
{
#ifdef FREEBL_NO_DEPEND
SECStatus rv;
#endif
diff --git a/lib/freebl/leancrypto/alignment.h b/lib/freebl/leancrypto/alignment.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/alignment.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef ALIGNMENT_H
+#define ALIGNMENT_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__)
+#define __align(x) __attribute__((aligned(x)))
+#elif defined(_MSC_VER)
+//#define __align(x) __declspec(align(x))
+#define __align(x) __attribute__((aligned(x)))
+#elif defined(__ARMCC_VERSION)
+/* Nothing, the used macro is known to the compiler */
+#else
+#define __align(x)
+#endif
+
+#define ALIGNED_UINT8_COEFFS(N) N
+#define ALIGNED_UINT8_UINT64(N) ((N + 7) / 8)
+
+#define BUF_ALIGNED_UINT8_UINT64(N) \
+ union { \
+ uint8_t coeffs[ALIGNED_UINT8_COEFFS(N)]; \
+ uint64_t vec[ALIGNED_UINT8_UINT64(N)]; \
+ }
+
+static inline int aligned(const uint8_t *ptr, uint32_t alignmask)
+{
+ if ((uintptr_t)ptr & alignmask)
+ return 0;
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ALIGNMENT_H */
diff --git a/lib/freebl/leancrypto/atomic.h b/lib/freebl/leancrypto/atomic.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/atomic.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _ATOMIC_H
+#define _ATOMIC_H
+
+#ifndef LINUX_KERNEL
+
+/*
+ * Atomic operations only work on:
+ * GCC >= 4.1
+ * Clang / LLVM
+ */
+
+/**
+ * Atomic type and operations equivalent to the Linux kernel.
+ */
+typedef struct {
+ volatile int counter;
+} atomic_t;
+
+/**
+ * Memory barrier
+ */
+static inline void mb(void)
+{
+ __sync_synchronize();
+}
+
+#define ATOMIC_INIT(i) { (i) }
+
+/**
+ * Read atomic variable
+ * @param v atomic variable
+ * @return variable content
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+ int i;
+
+ mb();
+ i = ((v)->counter);
+ mb();
+
+ return i;
+}
+
+/**
+ * Set atomic variable
+ * @param v atomic variable
+ * @param i value to be set
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+ mb();
+ ((v)->counter) = i;
+ mb();
+}
+
+/**
+ * Atomic add operation
+ * @param i integer value to add
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_add(int i, atomic_t *v)
+{
+ return __sync_add_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic add value from variable and test for zero
+ * @param i integer value to add
+ * @param v atomic variable
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int atomic_add_and_test(int i, atomic_t *v)
+{
+ return !(__sync_add_and_fetch(&v->counter, i));
+}
+
+/**
+ * Atomic increment by 1
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_inc(atomic_t *v)
+{
+ return atomic_add(1, v);
+}
+
+/**
+ * Atomic increment and test for zero
+ * @param v pointer of type atomic_t
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int atomic_inc_and_test(atomic_t *v)
+{
+ return atomic_add_and_test(1, v);
+}
+
+/**
+ * Atomic subtract operation
+ * @param i integer value to subtract
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_sub(int i, atomic_t *v)
+{
+ return __sync_sub_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic subtract value from variable and test for zero
+ * @param i integer value to subtract
+ * @param v atomic variable
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int atomic_sub_and_test(int i, atomic_t *v)
+{
+ return !(__sync_sub_and_fetch(&v->counter, i));
+}
+
+/**
+ * Atomic decrement by 1
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_dec(atomic_t *v)
+{
+ return atomic_sub(1, v);
+}
+
+/**
+ * Atomic decrement by 1 and test for zero
+ * @param v atomic variable
+ * @return true if the result is zero, or false for all other cases.
+ */
+static inline int atomic_dec_and_test(atomic_t *v)
+{
+ return atomic_sub_and_test(1, v);
+}
+
+/**
+ * Atomic or operation
+ * @param i integer value to or
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_or(int i, atomic_t *v)
+{
+ return __sync_or_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic xor operation
+ * @param i integer value to xor
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_xor(int i, atomic_t *v)
+{
+ return __sync_xor_and_fetch(&v->counter, i);
+}
+
+/**
+ * Atomic and operation
+ * @param i integer value to and
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_and(int i, atomic_t *v)
+{
+ return __sync_and_and_fetch(&v->counter, i);
+}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsync-fetch-and-nand-semantics-changed"
+#endif
+/**
+ * Atomic nand operation
+ * @param i integer value to nand
+ * @param v atomic variable
+ * @return variable content after operation
+ */
+static inline int atomic_nand(int i, atomic_t *v)
+{
+ return __sync_nand_and_fetch(&v->counter, i);
+}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+/**
+ * Atomic compare and exchange operation (if current value of atomic
+ * variable is equal to the old value, set the new value)
+ * @param v atomic variable
+ * @param old integer value to compare with
+ * @param new integer value to set atomic variable to
+ * @return original value if comparison is successful and new was written
+ * To verify that the exchange was successful, the caller must compare
+ * the return value with the old value.
+ */
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+ return __sync_val_compare_and_swap(&v->counter, old, new);
+}
+
+/**
+ * Atomic operation with a caller-provided function to derive the new
+ * value from the old value. Note, the caller-provided function may be called
+ * multiple times.
+ *
+ * @param v atomic variable
+ * @param data parameter that is given to the check function to maintain a state
+ * @param check_func Function that returns the new value to be set. The function
+ * is invoked with the old value as input parameter.
+ */
+static inline void atomic_update_with_func(atomic_t *v, void *data,
+ int (*check_func)(void *data,
+ int old))
+{
+ int old;
+
+ do {
+ old = atomic_read(v);
+ } while (atomic_cmpxchg(v, old, check_func(data, old)) != old);
+}
+
+#endif /* LINUX_KERNEL */
+#endif /* _ATOMIC_H */
diff --git a/lib/freebl/leancrypto/atomic_bool.h b/lib/freebl/leancrypto/atomic_bool.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/atomic_bool.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _ATOMIC_BOOL_H
+#define _ATOMIC_BOOL_H
+
+#include "bool.h"
+
+/*
+ * Atomic operations only work on:
+ * GCC >= 4.1
+ * Clang / LLVM
+ */
+
+/**
+ * Atomic type and operations equivalent to the Linux kernel.
+ */
+typedef struct {
+ volatile bool counter;
+} atomic_bool_t;
+
+/**
+ * Memory barrier
+ */
+static inline void atomic_bool_mb(void)
+{
+ __sync_synchronize();
+}
+
+#define ATOMIC_BOOL_INIT(i) { (i) }
+
+/**
+ * Read atomic variable
+ * @param v atomic variable
+ * @return variable content
+ */
+static inline bool atomic_bool_read(const atomic_bool_t *v)
+{
+ bool i;
+
+ atomic_bool_mb();
+ i = ((v)->counter);
+ atomic_bool_mb();
+
+ return i;
+}
+
+/**
+ * Set atomic variable
+ * @param i value to be set
+ * @param v atomic variable
+ */
+static inline void atomic_bool_set(bool i, atomic_bool_t *v)
+{
+ atomic_bool_mb();
+ ((v)->counter) = i;
+ atomic_bool_mb();
+}
+
+/**
+ * Set atomic variable to true
+ * @param v atomic variable
+ */
+static inline void atomic_bool_set_true(atomic_bool_t *v)
+{
+ atomic_bool_set(true, v);
+}
+
+/**
+ * Set atomic variable to false
+ * @param v atomic variable
+ */
+static inline void atomic_bool_set_false(atomic_bool_t *v)
+{
+ atomic_bool_set(false, v);
+}
+
+/**
+ * Atomic compare and exchange operation (if current value of atomic
+ * variable is equal to the old value, set the new value)
+ * @param v atomic variable
+ * @param old integer value to compare with
+ * @param new integer value to set atomic variable to
+ * @return true if comparison is successful and new was written
+ */
+static inline int atomic_bool_cmpxchg(atomic_bool_t *v, bool old, bool new)
+{
+ return __sync_bool_compare_and_swap(&v->counter, old, new);
+}
+
+#endif /* _ATOMIC_BOOL_H */
diff --git a/lib/freebl/leancrypto/binhexbin.h b/lib/freebl/leancrypto/binhexbin.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/binhexbin.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _BINHEXBIN_H
+#define _BINHEXBIN_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void hex2bin(const char *hex, const size_t hexlen, uint8_t *bin,
+ const size_t binlen);
+int hex2bin_alloc(const char *hex, const size_t hexlen, uint8_t **bin,
+ size_t *binlen);
+int bin2hex_alloc(const uint8_t *bin, const size_t binlen, char **hex,
+ size_t *hexlen);
+void bin2print(const unsigned char *bin, const size_t binlen, FILE *out,
+ const char *explanation);
+void bin2hex(const uint8_t *bin, const size_t binlen, char *hex,
+ const size_t hexlen, const int u);
+
+int bin2hex_html(const char *str, const size_t strlen, char *html,
+ const size_t htmllen);
+int bin2hex_html_from_url(const char *str, const size_t strlen, char *html,
+ const size_t htmllen);
+int bin2hex_html_alloc(const char *str, const size_t strlen, char **html,
+ size_t *htmllen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BINHEXBIN_H */
diff --git a/lib/freebl/leancrypto/bitshift.h b/lib/freebl/leancrypto/bitshift.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bitshift.h
@@ -0,0 +1,83 @@
+/* Conversion of a pointer value to an integer
+ *
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BITSHIFT_H
+#define BITSHIFT_H
+
+#include "bitshift_le.h"
+#include "bitshift_be.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+static inline uint64_t ptr_to_64(const uint8_t *p)
+{
+ return ptr_to_be64(p);
+}
+
+static inline uint32_t ptr_to_32(const uint8_t *p)
+{
+ return ptr_to_be32(p);
+}
+
+static inline void val64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ be64_to_ptr(p, value);
+}
+
+static inline void val32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ be32_to_ptr(p, value);
+}
+
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+static inline uint64_t ptr_to_64(const uint8_t *p)
+{
+ return ptr_to_le64(p);
+}
+
+static inline uint32_t ptr_to_32(const uint8_t *p)
+{
+ return ptr_to_le32(p);
+}
+
+static inline void val64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ le64_to_ptr(p, value);
+}
+
+static inline void val32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ le32_to_ptr(p, value);
+}
+
+#else
+#error "Endianess not defined"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BITSHIFT_H */
diff --git a/lib/freebl/leancrypto/bitshift_be.h b/lib/freebl/leancrypto/bitshift_be.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bitshift_be.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BITSHIFT_BE_H
+#define BITSHIFT_BE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Conversion of Big-Endian representations in byte streams - the data
+ * representation in the integer values is the host representation.
+ */
+static inline uint32_t ptr_to_be32(const uint8_t *p)
+{
+ return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 |
+ (uint32_t)p[2] << 8 | (uint32_t)p[3];
+}
+
+static inline uint64_t ptr_to_be64(const uint8_t *p)
+{
+ return (uint64_t)ptr_to_be32(p) << 32 | (uint64_t)ptr_to_be32(p + 4);
+}
+
+static inline void be32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ p[0] = (uint8_t)(value >> 24);
+ p[1] = (uint8_t)(value >> 16);
+ p[2] = (uint8_t)(value >> 8);
+ p[3] = (uint8_t)(value);
+}
+
+static inline void be64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ be32_to_ptr(p, (uint32_t)(value >> 32));
+ be32_to_ptr(p + 4, (uint32_t)(value));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BITSHIFT_BE_H */
diff --git a/lib/freebl/leancrypto/bitshift_le.h b/lib/freebl/leancrypto/bitshift_le.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bitshift_le.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BITSHIFT_LE_H
+#define BITSHIFT_LE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Conversion of Little-Endian representations in byte streams - the data
+ * representation in the integer values is the host representation.
+ */
+static inline uint32_t ptr_to_le32(const uint8_t *p)
+{
+ return (uint32_t)p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 |
+ (uint32_t)p[3] << 24;
+}
+
+static inline uint64_t ptr_to_le64(const uint8_t *p)
+{
+ return (uint64_t)ptr_to_le32(p) | (uint64_t)ptr_to_le32(p + 4) << 32;
+}
+
+static inline void le32_to_ptr(uint8_t *p, const uint32_t value)
+{
+ p[0] = (uint8_t)(value);
+ p[1] = (uint8_t)(value >> 8);
+ p[2] = (uint8_t)(value >> 16);
+ p[3] = (uint8_t)(value >> 24);
+}
+
+static inline void le64_to_ptr(uint8_t *p, const uint64_t value)
+{
+ le32_to_ptr(p + 4, (uint32_t)(value >> 32));
+ le32_to_ptr(p, (uint32_t)(value));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BITSHIFT_LE_H */
diff --git a/lib/freebl/leancrypto/bool.h b/lib/freebl/leancrypto/bool.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/bool.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef BOOL_H
+#define BOOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LINUX_KERNEL
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ < 202311L && !defined(false)
+/* Boolean variable */
+enum { false, true };
+typedef _Bool bool;
+#endif
+
+#endif /* LINUX_KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BOOL_H */
diff --git a/lib/freebl/leancrypto/build_bug_on.h b/lib/freebl/leancrypto/build_bug_on.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/build_bug_on.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 - 2023, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _BUILD_BUG_ON_H
+#define _BUILD_BUG_ON_H
+
+#ifndef LINUX_KERNEL
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
+#endif
+
+#endif /* _BUILD_BUG_ON_H */
diff --git a/lib/freebl/leancrypto/compare.h b/lib/freebl/leancrypto/compare.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/compare.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef COMPARE_H
+#define COMPARE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int get_current_selftest_level(void);
+
+#ifdef LC_SELFTEST_ENABLED
+#define LC_SELFTEST_RUN(x) \
+ if (*x == get_current_selftest_level()) \
+ return; \
+ *x = get_current_selftest_level();
+#else /* LC_SELFTEST_ENABLED */
+#define LC_SELFTEST_RUN(x) \
+ (void)x; \
+ if (1) \
+ return;
+#endif /* LC_SELFTEST_ENABLED */
+
+int lc_compare(const uint8_t *act, const uint8_t *exp, const size_t len,
+ const char *info);
+void lc_compare_selftest(const uint8_t *act, const uint8_t *exp,
+ const size_t len, const char *info);
+void lc_disable_selftest(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* COMPARE_H */
diff --git a/lib/freebl/leancrypto/conv_be_le.h b/lib/freebl/leancrypto/conv_be_le.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/conv_be_le.h
@@ -0,0 +1,142 @@
+/* Conversion functions from LE to BE and vice versa
+ *
+ * Copyright (C) 2015 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef CONV_BE_LE_H
+#define CONV_BE_LE_H
+
+#include "ext_headers.h"
+#include "rotate.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(CONVERSION_TEST) && (defined(__gcc__) || defined(__clang__))
+#define __HAVE_BUILTIN_BSWAP16__
+#define __HAVE_BUILTIN_BSWAP32__
+#define __HAVE_BUILTIN_BSWAP64__
+#endif
+
+/* Byte swap for 16-bit, 32-bit and 64-bit integers. */
+#ifndef __HAVE_BUILTIN_BSWAP16__
+static inline uint16_t _lc_bswap16(uint16_t x)
+{
+ return (uint16_t)((rol16(x, 8) & 0x00ff) | (ror16(x, 8) & 0xff00));
+}
+#define _lc_swap16(x) _lc_bswap16((uint16_t)(x))
+#else
+#define _lc_swap16(x) (uint16_t)__builtin_bswap16((uint16_t)(x))
+#endif
+
+#if !defined(__HAVE_BUILTIN_BSWAP32__) || !defined(__HAVE_BUILTIN_BSWAP64__)
+static inline uint32_t _lc_bswap32(uint32_t x)
+{
+ return ((rol32(x, 8) & 0x00ff00ffL) | (ror32(x, 8) & 0xff00ff00L));
+}
+#define _lc_swap32(x) _lc_bswap32((uint32_t)(x))
+#else
+#define _lc_swap32(x) (uint32_t)__builtin_bswap32((uint32_t)(x))
+#endif
+
+#ifndef __HAVE_BUILTIN_BSWAP64__
+static inline uint64_t _lc_bswap64(uint64_t x)
+{
+ return ((uint64_t)_lc_bswap32((uint32_t)x) << 32) |
+ (_lc_bswap32((uint32_t)(x >> 32)));
+}
+#define _lc_swap64(x) _lc_bswap64((uint64_t)(x))
+#else
+#define _lc_swap64(x) (uint64_t)__builtin_bswap64((uint64_t)(x))
+#endif
+
+/* Endian dependent byte swap operations. */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+static inline uint16_t le_bswap16(uint16_t x)
+{
+ return _lc_swap16(x);
+}
+
+static inline uint16_t be_bswap16(uint16_t x)
+{
+ return x;
+}
+
+static inline uint32_t le_bswap32(uint32_t x)
+{
+ return _lc_swap32(x);
+}
+
+static inline uint32_t be_bswap32(uint32_t x)
+{
+ return x;
+}
+
+static inline uint64_t le_bswap64(uint64_t x)
+{
+ return _lc_swap64(x);
+}
+
+static inline uint64_t be_bswap64(uint64_t x)
+{
+ return x;
+}
+
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+static inline uint16_t le_bswap16(uint16_t x)
+{
+ return x;
+}
+
+static inline uint16_t be_bswap16(uint16_t x)
+{
+ return _lc_swap16(x);
+}
+
+static inline uint32_t le_bswap32(uint32_t x)
+{
+ return x;
+}
+
+static inline uint32_t be_bswap32(uint32_t x)
+{
+ return _lc_swap32(x);
+}
+
+static inline uint64_t le_bswap64(uint64_t x)
+{
+ return x;
+}
+
+static inline uint64_t be_bswap64(uint64_t x)
+{
+ return _lc_swap64(x);
+}
+
+#else
+#error "Endianess not defined"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONV_BE_LE_H */
diff --git a/lib/freebl/leancrypto/dilithium_debug.h b/lib/freebl/leancrypto/dilithium_debug.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_debug.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_DEBUG_H
+#define DILITHIUM_DEBUG_H
+
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LC_DILITHIUM_DEBUG
+
+/* Disable selftests */
+#define LC_DILITHIUM_TEST_INIT 1
+
+void dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation);
+void dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation);
+void dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation);
+void dilithium_print_polyveck(polyveck *polyvec, const char *explanation);
+void dilithium_print_poly(poly *vec, const char *explanation);
+
+#else /* LC_DILITHIUM_DEBUG */
+
+/* Enable selftests */
+#define LC_DILITHIUM_TEST_INIT 0
+
+static inline void dilithium_print_buffer(const uint8_t *buffer,
+ const size_t bufferlen,
+ const char *explanation)
+{
+ (void)buffer;
+ (void)bufferlen;
+ (void)explanation;
+}
+
+static inline void dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ (void)mat;
+ (void)explanation;
+}
+
+static inline void dilithium_print_polyvecl(polyvecl *polyvec,
+ const char *explanation)
+{
+ (void)polyvec;
+ (void)explanation;
+}
+
+static inline void dilithium_print_polyveck(polyveck *polyvec,
+ const char *explanation)
+{
+ (void)polyvec;
+ (void)explanation;
+}
+
+static inline void dilithium_print_poly(poly *vec, const char *explanation)
+{
+ (void)vec;
+ (void)explanation;
+}
+
+#endif /* LC_DILITHIUM_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_DEBUG_H */
diff --git a/lib/freebl/leancrypto/dilithium_ntt.h b/lib/freebl/leancrypto/dilithium_ntt.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_ntt.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_NTT_H
+#define DILITHIUM_NTT_H
+
+#include "ext_headers.h"
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ntt(int32_t a[LC_DILITHIUM_N]);
+void invntt_tomont(int32_t a[LC_DILITHIUM_N]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_NTT_H */
diff --git a/lib/freebl/leancrypto/dilithium_pack.h b/lib/freebl/leancrypto/dilithium_pack.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_pack.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_PACK_H
+#define DILITHIUM_PACK_H
+
+#include "build_bug_on.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*******************************************************************************
+ * Pack / Unpack public key
+ ******************************************************************************/
+static inline void pack_pk_rho(struct lc_dilithium_pk *pk,
+ const uint8_t rho[LC_DILITHIUM_SEEDBYTES])
+{
+ memcpy(pk->pk, rho, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void pack_pk_t1(struct lc_dilithium_pk *pk, const polyveck *t1)
+{
+ unsigned int i;
+ uint8_t *pubkey = pk->pk + LC_DILITHIUM_SEEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt1_pack(pubkey + i * LC_DILITHIUM_POLYT1_PACKEDBYTES,
+ &t1->vec[i]);
+}
+
+static inline void unpack_pk_rho(uint8_t rho[LC_DILITHIUM_SEEDBYTES],
+ const struct lc_dilithium_pk *pk)
+{
+ memcpy(rho, pk->pk, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void unpack_pk_t1(polyveck *t1, const struct lc_dilithium_pk *pk)
+{
+ unsigned int i;
+ const uint8_t *pubkey = pk->pk + LC_DILITHIUM_SEEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt1_unpack(&t1->vec[i],
+ pubkey + i * LC_DILITHIUM_POLYT1_PACKEDBYTES);
+}
+
+/*******************************************************************************
+ * Pack / Unpack secret key
+ ******************************************************************************/
+static inline void pack_sk_rho(struct lc_dilithium_sk *sk,
+ const uint8_t rho[LC_DILITHIUM_SEEDBYTES])
+{
+ memcpy(sk->sk, rho, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void pack_sk_key(struct lc_dilithium_sk *sk,
+ const uint8_t key[LC_DILITHIUM_SEEDBYTES])
+{
+ memcpy(sk->sk + LC_DILITHIUM_SEEDBYTES, key, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void pack_sk_tr(struct lc_dilithium_sk *sk,
+ const uint8_t tr[LC_DILITHIUM_TRBYTES])
+{
+ memcpy(sk->sk + 2 * LC_DILITHIUM_SEEDBYTES, tr, LC_DILITHIUM_TRBYTES);
+}
+
+static inline void pack_sk_s1(struct lc_dilithium_sk *sk, const polyvecl *s1)
+{
+ unsigned int i;
+ uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyeta_pack(seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES,
+ &s1->vec[i]);
+}
+
+static inline void pack_sk_s2(struct lc_dilithium_sk *sk, const polyveck *s2)
+{
+ unsigned int i;
+ uint8_t *seckey = sk->sk + 2 * LC_DILITHIUM_SEEDBYTES +
+ LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyeta_pack(seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES,
+ &s2->vec[i]);
+}
+
+static inline void pack_sk_t0(struct lc_dilithium_sk *sk, const polyveck *t0)
+{
+ unsigned int i;
+ uint8_t *seckey = sk->sk + 2 * LC_DILITHIUM_SEEDBYTES +
+ LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES +
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt0_pack(seckey + i * LC_DILITHIUM_POLYT0_PACKEDBYTES,
+ &t0->vec[i]);
+}
+
+static inline void unpack_sk_rho(uint8_t rho[LC_DILITHIUM_SEEDBYTES],
+ const struct lc_dilithium_sk *sk)
+{
+ memcpy(rho, sk->sk, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void unpack_sk_key(uint8_t key[LC_DILITHIUM_SEEDBYTES],
+ const struct lc_dilithium_sk *sk)
+{
+ memcpy(key, sk->sk + LC_DILITHIUM_SEEDBYTES, LC_DILITHIUM_SEEDBYTES);
+}
+
+static inline void unpack_sk_tr(uint8_t tr[LC_DILITHIUM_TRBYTES],
+ const struct lc_dilithium_sk *sk)
+{
+ memcpy(tr, sk->sk + 2 * LC_DILITHIUM_SEEDBYTES, LC_DILITHIUM_TRBYTES);
+}
+
+static inline void unpack_sk_s1(polyvecl *s1, const struct lc_dilithium_sk *sk)
+{
+ unsigned int i;
+ const uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyeta_unpack(&s1->vec[i],
+ seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES);
+}
+
+static inline void unpack_sk_s2(polyveck *s2, const struct lc_dilithium_sk *sk)
+{
+ unsigned int i;
+ const uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyeta_unpack(&s2->vec[i],
+ seckey + i * LC_DILITHIUM_POLYETA_PACKEDBYTES);
+}
+
+static inline void unpack_sk_t0(polyveck *t0, const struct lc_dilithium_sk *sk)
+{
+ unsigned int i;
+ const uint8_t *seckey =
+ sk->sk + 2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES +
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyt0_unpack(&t0->vec[i],
+ seckey + i * LC_DILITHIUM_POLYT0_PACKEDBYTES);
+}
+
+/**
+ * @brief pack_sig - Bit-pack signature sig = (c, z, h).
+ *
+ * NOTE: A signature is the concatenation of sig = (c || packed z || packed h).
+ * As c is already present in the first bytes of sig, this function does
+ * not need to copy it yet again to the right location. This implies that
+ * this function does not process c.
+ *
+ * @param [out] sig signature
+ * @param [in] z pointer to vector z
+ * @param [in] h pointer to hint vector h
+ */
+static inline void pack_sig(struct lc_dilithium_sig *sig, const polyvecl *z,
+ const polyveck *h)
+{
+ unsigned int i, j, k;
+ /* Skip c */
+ uint8_t *signature = sig->sig + LC_DILITHIUM_CTILDE_BYTES;
+
+ BUILD_BUG_ON((1ULL << (sizeof(j) << 3)) < LC_DILITHIUM_N);
+ BUILD_BUG_ON((1ULL << (sizeof(k) << 3)) < LC_DILITHIUM_N);
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyz_pack(signature + i * LC_DILITHIUM_POLYZ_PACKEDBYTES,
+ &z->vec[i]);
+ signature += LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES;
+
+ /* Encode h */
+ memset(signature, 0, LC_DILITHIUM_OMEGA + LC_DILITHIUM_K);
+
+ k = 0;
+ for (i = 0; i < LC_DILITHIUM_K; ++i) {
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ if (h->vec[i].coeffs[j] != 0)
+ signature[k++] = (uint8_t)j;
+
+ signature[LC_DILITHIUM_OMEGA + i] = (uint8_t)k;
+ }
+}
+
+/**
+ * @brief unpack_sig_z - Unpack z part of signature sig = (c, z, h).
+ *
+ * NOTE: The c value is not unpacked as it can be used right from the signature.
+ * To access it, a caller simply needs to use the first
+ * LC_DILITHIUM_CTILDE_BYTES of the signature.
+ *
+ * @param [out] z pointer to output vector z
+ * @param [in] sig signature
+ */
+static inline void unpack_sig_z(polyvecl *z, const struct lc_dilithium_sig *sig)
+{
+ unsigned int i;
+ /* Skip c */
+ const uint8_t *signature = sig->sig + LC_DILITHIUM_CTILDE_BYTES;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ polyz_unpack(&z->vec[i],
+ signature + i * LC_DILITHIUM_POLYZ_PACKEDBYTES);
+}
+
+/**
+ * @brief unpack_sig - Unpack h value of signature sig = (c, z, h).
+ *
+ * NOTE: The c value is not unpacked as it can be used right from the signature.
+ * To access it, a caller simply needs to use the first
+ * LC_DILITHIUM_CTILDE_BYTES of the signature.
+ *
+ * @param [out] h pointer to output hint vector h
+ * @param [in] sig signature
+ *
+ * @return 1 in case of malformed signature; otherwise 0.
+ */
+static inline int unpack_sig_h(polyveck *h, const struct lc_dilithium_sig *sig)
+{
+ unsigned int i, j, k;
+ /* Skip c */
+ const uint8_t *signature =
+ sig->sig + LC_DILITHIUM_CTILDE_BYTES +
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES;
+
+ /* Decode h */
+ k = 0;
+ for (i = 0; i < LC_DILITHIUM_K; ++i) {
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ h->vec[i].coeffs[j] = 0;
+
+ if (signature[LC_DILITHIUM_OMEGA + i] < k ||
+ signature[LC_DILITHIUM_OMEGA + i] > LC_DILITHIUM_OMEGA)
+ return 1;
+
+ for (j = k; j < signature[LC_DILITHIUM_OMEGA + i]; ++j) {
+ /* Coefficients are ordered for strong unforgeability */
+ if (j > k && signature[j] <= signature[j - 1])
+ return 1;
+ h->vec[i].coeffs[signature[j]] = 1;
+ }
+
+ k = signature[LC_DILITHIUM_OMEGA + i];
+ }
+
+ /* Extra indices are zero for strong unforgeability */
+ for (j = k; j < LC_DILITHIUM_OMEGA; ++j)
+ if (signature[j])
+ return 1;
+
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_PACK_H */
diff --git a/lib/freebl/leancrypto/dilithium_pct.h b/lib/freebl/leancrypto/dilithium_pct.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_pct.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_PCT_H
+#define DILITHIUM_PCT_H
+
+#include "fips_mode.h"
+#include "ret_checkers.h"
+#include "small_stack_support.h"
+#include "visibility.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline int _lc_dilithium_pct_fips(const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk)
+{
+ struct workspace {
+ uint8_t m[32];
+ struct lc_dilithium_sig sig;
+ };
+ int ret;
+ LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t));
+
+ CKINT(lc_dilithium_sign(&ws->sig, ws->m, sizeof(ws->m), sk,
+ lc_seeded_rng));
+ CKINT(lc_dilithium_verify(&ws->sig, ws->m, sizeof(ws->m), pk));
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static inline int lc_dilithium_pct_fips(const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk)
+{
+ FIPS140_PCT_LOOP(_lc_dilithium_pct_fips(pk, sk))
+
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_PCT_H */
diff --git a/lib/freebl/leancrypto/dilithium_poly.h b/lib/freebl/leancrypto/dilithium_poly.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_poly.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_POLY_H
+#define DILITHIUM_POLY_H
+
+#include "dilithium_type.h"
+#include "dilithium_reduce.h"
+#include "dilithium_rounding.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ int32_t coeffs[LC_DILITHIUM_N];
+} poly;
+
+/**
+ * @brief poly_add - Add polynomials. No modular reduction is performed.
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] a pointer to first summand
+ * @param [in] b pointer to second summand
+ */
+static inline void poly_add(poly *c, const poly *a, const poly *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = a->coeffs[i] + b->coeffs[i];
+}
+
+/**
+ * @brief poly_sub - Subtract polynomials. No modular reduction is
+ * performed.
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] a pointer to first input polynomial
+ * @param [in] b pointer to second input polynomial to be subtraced from first
+ * input polynomial
+ */
+static inline void poly_sub(poly *c, const poly *a, const poly *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = a->coeffs[i] - b->coeffs[i];
+}
+
+/**
+ * @brief poly_shiftl - Multiply polynomial by 2^D without modular reduction.
+ * Assumes input coefficients to be less than 2^{31-D} in
+ * absolute value.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void poly_shiftl(poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a->coeffs[i] <<= LC_DILITHIUM_D;
+}
+
+/**
+ * @brief poly_decompose - For all coefficients c of the input polynomial,
+ * compute high and low bits c0, c1 such
+ * c mod Q = c1*ALPHA + c0 with
+ * -ALPHA/2 < c0 <= ALPHA/2 except c1 = (Q-1)/ALPHA
+ * where we set c1 = 0 and
+ * -ALPHA/2 <= c0 = c mod Q - Q < 0.
+ * Assumes coefficients to be standard representatives.
+ *
+ * @param [out] a1 pointer to output polynomial with coefficients c1
+ * @param [out] a0 pointer to output polynomial with coefficients c0
+ * @param [in] a pointer to input polynomial
+ */
+static inline void poly_decompose(poly *a1, poly *a0, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a1->coeffs[i] = decompose(&a0->coeffs[i], a->coeffs[i]);
+}
+
+/**
+ * @brief poly_make_hint - Compute hint polynomial. The coefficients of which
+ * indicate whether the low bits of the corresponding
+ * coefficient of the input polynomial overflow into the
+ * high bits.
+ *
+ * @param [out] h pointer to output hint polynomial
+ * @param [in] a0 pointer to low part of input polynomial
+ * @param [in] a1 pointer to high part of input polynomial
+ *
+ * @return number of 1 bits.
+ */
+static inline unsigned int poly_make_hint(poly *h, const poly *a0,
+ const poly *a1)
+{
+ unsigned int i, s = 0;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ h->coeffs[i] = make_hint(a0->coeffs[i], a1->coeffs[i]);
+ s += (unsigned int)h->coeffs[i];
+ }
+
+ return s;
+}
+
+/**
+ * @brief poly_use_hint - Use hint polynomial to correct the high bits of a
+ * polynomial.
+ *
+ * @param [out] b pointer to output polynomial with corrected high bits
+ * @param [in] a pointer to input polynomial
+ * @param [in] h pointer to input hint polynomial
+ */
+static inline void poly_use_hint(poly *b, const poly *a, const poly *h)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ b->coeffs[i] = use_hint(a->coeffs[i], h->coeffs[i]);
+}
+
+int poly_chknorm(const poly *a, int32_t B);
+
+#define POLY_UNIFORM_NBLOCKS \
+ ((768 + LC_SHAKE_128_SIZE_BLOCK - 1) / LC_SHAKE_128_SIZE_BLOCK)
+void poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf);
+
+#if LC_DILITHIUM_ETA == 2
+#define POLY_UNIFORM_ETA_NBLOCKS \
+ ((136 + LC_SHAKE_256_SIZE_BLOCK - 1) / LC_SHAKE_256_SIZE_BLOCK)
+#elif LC_DILITHIUM_ETA == 4
+#define POLY_UNIFORM_ETA_NBLOCKS \
+ ((227 + LC_SHAKE_256_SIZE_BLOCK - 1) / LC_SHAKE_256_SIZE_BLOCK)
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+#define POLY_UNIFORM_ETA_BYTES POLY_UNIFORM_ETA_NBLOCKS *LC_SHAKE_256_SIZE_BLOCK
+void poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf);
+
+#define POLY_UNIFORM_GAMMA1_NBLOCKS \
+ ((LC_DILITHIUM_POLYZ_PACKEDBYTES + LC_SHAKE_256_SIZE_BLOCK - 1) / \
+ LC_SHAKE_256_SIZE_BLOCK)
+#define POLY_UNIFORM_GAMMA1_BYTES \
+ POLY_UNIFORM_GAMMA1_NBLOCKS *LC_SHAKE_256_SIZE_BLOCK
+
+#define POLY_CHALLENGE_BYTES LC_SHAKE_256_SIZE_BLOCK
+void poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf);
+
+void polyeta_pack(uint8_t *r, const poly *a);
+void polyeta_unpack(poly *r, const uint8_t *a);
+
+void polyt1_pack(uint8_t *r, const poly *a);
+
+void polyt0_pack(uint8_t *r, const poly *a);
+void polyt0_unpack(poly *r, const uint8_t *a);
+
+void polyz_pack(uint8_t *r, const poly *a);
+void polyz_unpack(poly *r, const uint8_t *a);
+
+void polyw1_pack(uint8_t *r, const poly *a);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLY_H */
diff --git a/lib/freebl/leancrypto/dilithium_poly_c.h b/lib/freebl/leancrypto/dilithium_poly_c.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_poly_c.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_POLY_C_H
+#define DILITHIUM_POLY_C_H
+
+#include "dilithium_ntt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief poly_reduce - Inplace reduction of all coefficients of polynomial to
+ * representative in [-6283009,6283007].
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void poly_reduce(poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a->coeffs[i] = reduce32(a->coeffs[i]);
+}
+
+/**
+ * @brief poly_caddq - For all coefficients of in/out polynomial add Q if
+ * coefficient is negative.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void poly_caddq(poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a->coeffs[i] = caddq(a->coeffs[i]);
+}
+
+/**
+ * @brief poly_pointwise_montgomery - Pointwise multiplication of polynomials in
+ * NTT domain representation and
+ * multiplication of resulting polynomial
+ * by 2^{-32}.
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] a pointer to first input polynomial
+ * @param [in] b pointer to second input polynomial
+ */
+static inline void poly_pointwise_montgomery(poly *c, const poly *a,
+ const poly *b)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] =
+ montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]);
+}
+
+/**
+ * @brief poly_power2round - For all coefficients c of the input polynomial,
+ * compute c0, c1 such that c mod Q = c1*2^D + c0
+ * with -2^{D-1} < c0 <= 2^{D-1}. Assumes coefficients
+ * to be standard representatives.
+ *
+ * @param [out] a1 pointer to output polynomial with coefficients c1
+ * @param [out] a0 pointer to output polynomial with coefficients c0
+ * @param [in] a pointer to input polynomial
+ */
+static inline void poly_power2round(poly *a1, poly *a0, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ a1->coeffs[i] = power2round(&a0->coeffs[i], a->coeffs[i]);
+}
+
+/**
+ * @brief polyt1_unpack - Unpack polynomial t1 with 10-bit coefficients.
+ * Output coefficients are standard representatives.
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+static inline void polyt1_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] =
+ ((a[5 * i + 0] >> 0) | ((uint32_t)a[5 * i + 1] << 8)) &
+ 0x3FF;
+ r->coeffs[4 * i + 1] =
+ ((a[5 * i + 1] >> 2) | ((uint32_t)a[5 * i + 2] << 6)) &
+ 0x3FF;
+ r->coeffs[4 * i + 2] =
+ ((a[5 * i + 2] >> 4) | ((uint32_t)a[5 * i + 3] << 4)) &
+ 0x3FF;
+ r->coeffs[4 * i + 3] =
+ ((a[5 * i + 3] >> 6) | ((uint32_t)a[5 * i + 4] << 2)) &
+ 0x3FF;
+ }
+}
+
+/**
+ * @brief poly_ntt - Inplace forward NTT. Coefficients can grow by
+ * 8*Q in absolute value.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void poly_ntt(poly *a)
+{
+ ntt(a->coeffs);
+}
+
+/**
+ * @brief poly_invntt_tomont - Inplace inverse NTT and multiplication by 2^{32}.
+ * Input coefficients need to be less than Q in
+ * absolute value and output coefficients are again
+ * bounded by Q.
+ *
+ * @param [in,out] a pointer to input/output polynomial
+ */
+static inline void poly_invntt_tomont(poly *a)
+{
+ invntt_tomont(a->coeffs);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLY_C_H */
diff --git a/lib/freebl/leancrypto/dilithium_poly_common.h b/lib/freebl/leancrypto/dilithium_poly_common.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_poly_common.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_POLY_COMMON_H
+#define DILITHIUM_POLY_COMMON_H
+
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLY_COMMON_H */
diff --git a/lib/freebl/leancrypto/dilithium_polyvec.h b/lib/freebl/leancrypto/dilithium_polyvec.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_polyvec.h
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_POLYVEC_H
+#define DILITHIUM_POLYVEC_H
+
+#include "conv_be_le.h"
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ poly vec[LC_DILITHIUM_L];
+} polyvecl;
+
+/* Vectors of polynomials of length K */
+typedef struct {
+ poly vec[LC_DILITHIUM_K];
+} polyveck;
+
+/**************************************************************/
+/************ Vectors of polynomials of length L **************/
+/**************************************************************/
+
+static inline void polyvecl_reduce(polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_reduce(&v->vec[i]);
+}
+
+/**
+ * @brief polyvecl_add - Add vectors of polynomials of length L.
+ * No modular reduction is performed.
+ *
+ * @param [out] w pointer to output vector
+ * @param [in] u pointer to first summand
+ * @param [in] v pointer to second summand
+ */
+static inline void polyvecl_add(polyvecl *w, const polyvecl *u,
+ const polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_add(&w->vec[i], &u->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyvecl_ntt - Forward NTT of all polynomials in vector of length L.
+ * Output coefficients can be up to 16*Q larger than input
+ * coefficients.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void polyvecl_ntt(polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_ntt(&v->vec[i]);
+}
+
+static inline void polyvecl_invntt_tomont(polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_invntt_tomont(&v->vec[i]);
+}
+
+static inline void polyvecl_pointwise_poly_montgomery(polyvecl *r,
+ const poly *a,
+ const polyvecl *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]);
+}
+
+/**
+ * @brief polyvecl_chknorm - Check infinity norm of polynomials in vector of
+ * length L. Assumes input polyvecl to be reduced by
+ * polyvecl_reduce().
+ *
+ * @param [in] v pointer to vector
+ * @param [in] bound norm bound
+ *
+ * @return 0 if norm of all polynomials is strictly smaller than B <= (Q-1)/8
+ * and 1 otherwise.
+ */
+static inline int polyvecl_chknorm(const polyvecl *v, int32_t bound)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ if (poly_chknorm(&v->vec[i], bound))
+ return 1;
+
+ return 0;
+}
+
+/**************************************************************/
+/************ Vectors of polynomials of length K **************/
+/**************************************************************/
+
+/**
+ * @brief polyveck_reduce - Reduce coefficients of polynomials in vector of
+ * length LC_DILITHIUM_K to representatives in
+ * [-6283009,6283007].
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void polyveck_reduce(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_reduce(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_caddq - For all coefficients of polynomials in vector of
+ * length LC_DILITHIUM_K add LC_DILITHIUM_Q if
+ * coefficient is negative.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void polyveck_caddq(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_caddq(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_add - Add vectors of polynomials of length LC_DILITHIUM_K.
+ * No modular reduction is performed.
+ *
+ * @param [out] w pointer to output vector
+ * @param [in] u pointer to first summand
+ * @param [in] v pointer to second summand
+ */
+static inline void polyveck_add(polyveck *w, const polyveck *u,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_add(&w->vec[i], &u->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief olyveck_sub - Subtract vectors of polynomials of length
+ * LC_DILITHIUM_K. No modular reduction is performed.
+ *
+ * @param [out] w pointer to output vector
+ * @param [in] u pointer to first input vector
+ * @param [in] v pointer to second input vector to be subtracted from first
+ * input vector
+ */
+static inline void polyveck_sub(polyveck *w, const polyveck *u,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_sub(&w->vec[i], &u->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_shiftl - Multiply vector of polynomials of Length K by
+ * 2^D without modular reduction. Assumes input
+ * coefficients to be less than 2^{31-D}.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void polyveck_shiftl(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_shiftl(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_ntt - Forward NTT of all polynomials in vector of length K.
+ * Output coefficients can be up to 16*Q larger than input
+ * coefficients.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void polyveck_ntt(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_ntt(&v->vec[i]);
+}
+
+/**
+ * @brief polyveck_invntt_tomont - Inverse NTT and multiplication by 2^{32} of
+ * polynomials in vector of length K. Input
+ * coefficients need to be less than 2*Q.
+ *
+ * @param [in,out] v pointer to input/output vector
+ */
+static inline void polyveck_invntt_tomont(polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_invntt_tomont(&v->vec[i]);
+}
+
+static inline void polyveck_pointwise_poly_montgomery(polyveck *r,
+ const poly *a,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_pointwise_montgomery(&r->vec[i], a, &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_chknorm - Check infinity norm of polynomials in vector of
+ * length K. Assumes input polyveck to be reduced by
+ * polyveck_reduce().
+ *
+ * @param [in] v pointer to vector
+ * @param [in] bound norm bound
+ *
+ * @return 0 if norm of all polynomials are strictly smaller than B <= (Q-1)/8
+ * and 1 otherwise.
+ */
+static inline int polyveck_chknorm(const polyveck *v, int32_t bound)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ if (poly_chknorm(&v->vec[i], bound))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief polyveck_power2round - For all coefficients a of polynomials in vector
+ * of length K, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with
+ * -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients
+ * to be standard representatives.
+ *
+ * @param [out] v1 pointer to output vector of polynomials with coefficients a1
+ * @param [in] v0 pointer to output vector of polynomials with coefficients a0
+ * @param [in] v pointer to input vector
+ */
+static inline void polyveck_power2round(polyveck *v1, polyveck *v0,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_power2round(&v1->vec[i], &v0->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_decompose - For all coefficients a of polynomials in vector
+ * of length K, compute high and low bits a0, a1
+ * such a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except a1 = (Q-1)/ALPHA
+ * where we set a1 = 0 and
+ * -ALPHA/2 <= a0 = a mod Q - Q < 0. Assumes
+ * coefficients to be standard representatives.
+ *
+ * @param [out] v1 pointer to output vector of polynomials with coefficients a1
+ * @param [in] v0 pointer to output vector of polynomials with coefficients a0
+ * @param [in] v pointer to input vector
+ */
+static inline void polyveck_decompose(polyveck *v1, polyveck *v0,
+ const polyveck *v)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_decompose(&v1->vec[i], &v0->vec[i], &v->vec[i]);
+}
+
+/**
+ * @brief polyveck_make_hint - Compute hint vector.
+ *
+ * @param [out] h pointer to output vector
+ * @param [in] v0 pointer to low part of input vector
+ * @param [in] v1 pointer to high part of input vector
+ *
+ * @return number of 1 bits.
+ */
+static inline unsigned int polyveck_make_hint(polyveck *h, const polyveck *v0,
+ const polyveck *v1)
+{
+ unsigned int i, s = 0;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ s += poly_make_hint(&h->vec[i], &v0->vec[i], &v1->vec[i]);
+
+ return s;
+}
+
+/**
+ * @brief polyveck_use_hint - Use hint vector to correct the high bits of input
+ * vector.
+ *
+ * @param [out] w pointer to output vector of polynomials with corrected high
+ * bits
+ * @param [in] u pointer to input vector
+ * @param [in] h pointer to input hint vector
+ */
+static inline void polyveck_use_hint(polyveck *w, const polyveck *u,
+ const polyveck *h)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_use_hint(&w->vec[i], &u->vec[i], &h->vec[i]);
+}
+
+static inline void
+polyveck_pack_w1(uint8_t r[LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES],
+ const polyveck *w1)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyw1_pack(&r[i * LC_DILITHIUM_POLYW1_PACKEDBYTES],
+ &w1->vec[i]);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLYVEC_H */
diff --git a/lib/freebl/leancrypto/dilithium_polyvec_c.h b/lib/freebl/leancrypto/dilithium_polyvec_c.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_polyvec_c.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_POLYVEC_C_H
+#define DILITHIUM_POLYVEC_C_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_uniform_eta(&v->vec[i], seed, le_bswap16(nonce++), ws_buf);
+}
+
+static inline void
+polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_L; ++i)
+ poly_uniform_gamma1(
+ &v->vec[i], seed,
+ le_bswap16((uint16_t)(LC_DILITHIUM_L * nonce + i)),
+ ws_buf);
+}
+
+static inline void
+polyveck_uniform_eta(polyveck *v, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ poly_uniform_eta(&v->vec[i], seed, le_bswap16(nonce++), ws_buf);
+}
+
+/**
+ * @brief expand_mat - Implementation of ExpandA. Generates matrix A with
+ * uniformly random coefficients a_{i,j} by performing
+ * rejection sampling on the output stream of
+ * SHAKE128(rho|j|i).
+ *
+ * @param [out] mat output matrix
+ * @param [in] rho byte array containing seed rho
+ */
+static inline void
+polyvec_matrix_expand(polyvecl mat[LC_DILITHIUM_K],
+ const uint8_t rho[LC_DILITHIUM_SEEDBYTES], void *ws_buf)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ for (j = 0; j < LC_DILITHIUM_L; ++j)
+ poly_uniform(
+ &mat[i].vec[j], rho,
+ le_bswap16((uint16_t)(i << 8) + (uint16_t)j),
+ ws_buf);
+}
+
+/**
+ * @brief polyvecl_pointwise_acc_montgomery -
+ * Pointwise multiply vectors of polynomials of length L, multiply
+ * resulting vector by 2^{-32} and add (accumulate) polynomials
+ * in it. Input/output vectors are in NTT domain representation.
+ *
+ * @param [out] w output polynomial
+ * @param [in] u pointer to first input vector
+ * @param [in] v pointer to second input vector
+ */
+static inline void polyvecl_pointwise_acc_montgomery(poly *w, const polyvecl *u,
+ const polyvecl *v,
+ void *ws_buf)
+{
+ unsigned int i;
+ poly *t = ws_buf;
+
+ poly_pointwise_montgomery(w, &u->vec[0], &v->vec[0]);
+ for (i = 1; i < LC_DILITHIUM_L; ++i) {
+ poly_pointwise_montgomery(t, &u->vec[i], &v->vec[i]);
+ poly_add(w, w, t);
+ }
+}
+
+static inline void
+polyvec_matrix_pointwise_montgomery(polyveck *t,
+ const polyvecl mat[LC_DILITHIUM_K],
+ const polyvecl *v, void *ws_buf)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_K; ++i)
+ polyvecl_pointwise_acc_montgomery(&t->vec[i], &mat[i], v,
+ ws_buf);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_POLYVEC_C_H */
diff --git a/lib/freebl/leancrypto/dilithium_reduce.h b/lib/freebl/leancrypto/dilithium_reduce.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_reduce.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_REDUCE_H
+#define DILITHIUM_REDUCE_H
+
+#include "dilithium_type.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MONT -4186625 // 2^32 % Q
+#define QINV 58728449 // q^(-1) mod 2^32
+
+/**
+ * @brief montgomery_reduce - For finite field element a with
+ * -2^{31}Q <= a <= Q*2^31,
+ * compute r \equiv a*2^{-32} (mod Q) such that
+ * -Q < r < Q.
+ *
+ * @param [in] a finite field element
+ *
+ * @return r
+ */
+static inline int32_t montgomery_reduce(int64_t a)
+{
+ int32_t t;
+
+ t = (int32_t)a * QINV;
+ t = (int32_t)((a - (int64_t)t * LC_DILITHIUM_Q) >> 32);
+ return t;
+}
+
+/**
+ * @brief reduce32 - For finite field element a with a <= 2^{31} - 2^{22} - 1,
+ * compute r \equiv a (mod Q) such that
+ * -6283009 <= r <= 6283007.
+ *
+ * @param [in] a finite field element
+ *
+ * @return r
+ */
+static inline int32_t reduce32(int32_t a)
+{
+ int32_t t;
+
+ t = (a + (1 << 22)) >> 23;
+ t = a - t * LC_DILITHIUM_Q;
+ return t;
+}
+
+/**
+ * @brief caddq - Add Q if input coefficient is negative.
+ *
+ * @param [in] a finite field element
+ *
+ * @return r
+ */
+static inline int32_t caddq(int32_t a)
+{
+ a += (a >> 31) & LC_DILITHIUM_Q;
+ return a;
+}
+
+/**
+ * @brief freeze - For finite field element a, compute standard representative
+ * r = a mod^+ Q.
+ *
+ * @param [in] a finite field element a
+ *
+ * @return r
+ */
+static inline int32_t freeze(int32_t a)
+{
+ a = reduce32(a);
+ a = caddq(a);
+ return a;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_REDUCE_H */
diff --git a/lib/freebl/leancrypto/dilithium_rounding.h b/lib/freebl/leancrypto/dilithium_rounding.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_rounding.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_ROUNDING_H
+#define DILITHIUM_ROUNDING_H
+
+#include "dilithium_type.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int32_t power2round(int32_t *a0, int32_t a);
+int32_t decompose(int32_t *a0, int32_t a);
+int32_t make_hint(int32_t a0, int32_t a1);
+int32_t use_hint(int32_t a, int32_t hint);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_ROUNDING_H */
diff --git a/lib/freebl/leancrypto/dilithium_service_helpers.h b/lib/freebl/leancrypto/dilithium_service_helpers.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_service_helpers.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_SERVICE_HELPERS_H
+#define DILITHIUM_SERVICE_HELPERS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief rej_uniform - Sample uniformly random coefficients in [0, Q-1] by
+ * performing rejection sampling on array of random bytes.
+ *
+ * @param [out] a pointer to output array (allocated)
+ * @param [in] len number of coefficients to be sampled
+ * @param [in] buf array of random bytes
+ * @param [in] buflen length of array of random bytes
+ *
+ * @return number of sampled coefficients. Can be smaller than len if not enough
+ * random bytes were given.
+ */
+static inline unsigned int rej_uniform(int32_t *a, unsigned int len,
+ const uint8_t *buf, unsigned int buflen)
+{
+ unsigned int ctr, pos;
+ uint32_t t;
+
+ ctr = pos = 0;
+ while (ctr < len && pos + 3 <= buflen) {
+ t = buf[pos++];
+ t |= (uint32_t)buf[pos++] << 8;
+ t |= (uint32_t)buf[pos++] << 16;
+ t &= 0x7FFFFF;
+
+ if (t < LC_DILITHIUM_Q)
+ a[ctr++] = (int32_t)t;
+ }
+
+ return ctr;
+}
+
+/**
+ * @brief rej_eta - Sample uniformly random coefficients in [-ETA, ETA] by
+ * performing rejection sampling on array of random bytes.
+ *
+ * @param [out] a pointer to output array (allocated)
+ * @param [in] len number of coefficients to be sampled
+ * @param [in] buf array of random bytes
+ * @param [in] buflen length of array of random bytes
+ *
+ * @return number of sampled coefficients. Can be smaller than len if not enough
+ * random bytes were given.
+ */
+static inline unsigned int rej_eta(int32_t *a, unsigned int len,
+ const uint8_t *buf, unsigned int buflen)
+{
+ unsigned int ctr, pos;
+ int32_t t0, t1;
+
+ ctr = pos = 0;
+ while (ctr < len && pos < buflen) {
+ t0 = buf[pos] & 0x0F;
+ t1 = buf[pos++] >> 4;
+
+#if LC_DILITHIUM_ETA == 2
+ if (t0 < 15) {
+ t0 = t0 - (205 * t0 >> 10) * 5;
+ a[ctr++] = 2 - t0;
+ }
+ if (t1 < 15 && ctr < len) {
+ t1 = t1 - (205 * t1 >> 10) * 5;
+ a[ctr++] = 2 - t1;
+ }
+#elif LC_DILITHIUM_ETA == 4
+ if (t0 < 9)
+ a[ctr++] = 4 - t0;
+ if (t1 < 9 && ctr < len)
+ a[ctr++] = 4 - t1;
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+ }
+
+ return ctr;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_SERVICE_HELPERS_H */
diff --git a/lib/freebl/leancrypto/dilithium_signature_c.h b/lib/freebl/leancrypto/dilithium_signature_c.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_signature_c.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_SIGNATURE_C_H
+#define DILITHIUM_SIGNATURE_C_H
+
+#include "dilithium_type.h"
+#include "lc_rng.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int lc_dilithium_keypair_c(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+int lc_dilithium_keypair_from_seed_c(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_sign_c(struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+int lc_dilithium_sign_ctx_c(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+int lc_dilithium_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk);
+int lc_dilithium_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_sign_final_c(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_verify_c(const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+int lc_dilithium_verify_ctx_c(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+int lc_dilithium_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+int lc_dilithium_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_verify_final_c(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_SIGNATURE_C_H */
diff --git a/lib/freebl/leancrypto/dilithium_signature_impl.h b/lib/freebl/leancrypto/dilithium_signature_impl.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_signature_impl.h
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef DILITHIUM_SIGNATURE_IMPL_H
+#define DILITHIUM_SIGNATURE_IMPL_H
+
+#include "alignment.h"
+#include "build_bug_on.h"
+#include "dilithium_type.h"
+#include "dilithium_debug.h"
+#include "dilithium_pack.h"
+#include "lc_hash.h"
+#include "lc_memcmp_secure.h"
+#include "lc_sha3.h"
+#include "ret_checkers.h"
+#include "signature_domain_separation.h"
+#include "small_stack_support.h"
+#include "static_rng.h"
+#include "timecop.h"
+#include "visibility.h"
+#include "binhexbin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Enable this macro to report the rejection code paths taken with the
+ * signature generation operation. When disabled, the compiler should
+ * eliminate this code which means that the counting code is folded away.
+ */
+#undef REJECTION_TEST_SAMPLING
+
+#define _WS_POLY_UNIFORM_BUF_SIZE \
+ (POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK + 2)
+
+#ifndef LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER
+#error "LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER is not defined"
+#endif
+
+#define WS_POLY_UNIFORM_BUF_SIZE \
+ (_WS_POLY_UNIFORM_BUF_SIZE * LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER)
+
+static int lc_dilithium_keypair_from_seed_impl(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed,
+ size_t seedlen);
+
+static int lc_dilithium_keypair_impl(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ struct workspace {
+ union {
+ polyvecl s1, s1hat;
+ } s1;
+ union {
+ polyvecl mat[LC_DILITHIUM_K];
+ polyveck t0;
+ } matrix;
+ polyveck s2, t1;
+ uint8_t seedbuf[2 * LC_DILITHIUM_SEEDBYTES +
+ LC_DILITHIUM_CRHBYTES];
+ union {
+ poly polyvecl_pointwise_acc_montgomery_buf;
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ uint8_t poly_uniform_eta_buf[POLY_UNIFORM_ETA_BYTES];
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ } tmp;
+ };
+ static const uint8_t dimension[2] = { LC_DILITHIUM_K, LC_DILITHIUM_L };
+ const uint8_t *rho, *rhoprime, *key;
+ int ret;
+ LC_HASH_CTX_ON_STACK(shake256_ctx, lc_shake256);
+ LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t));
+
+ CKNULL(pk, -EINVAL);
+ CKNULL(sk, -EINVAL);
+
+ lc_rng_check(&rng_ctx);
+
+ /* Get randomness for rho, rhoprime and key */
+ CKINT(lc_rng_generate(rng_ctx, NULL, 0, ws->seedbuf,
+ LC_DILITHIUM_SEEDBYTES));
+ dilithium_print_buffer(ws->seedbuf, LC_DILITHIUM_SEEDBYTES,
+ "Keygen - Seed");
+
+ lc_hash_init(shake256_ctx);
+ lc_hash_update(shake256_ctx, ws->seedbuf, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(shake256_ctx, dimension, sizeof(dimension));
+ lc_hash_set_digestsize(shake256_ctx, sizeof(ws->seedbuf));
+ lc_hash_final(shake256_ctx, ws->seedbuf);
+ lc_hash_zero(shake256_ctx);
+
+ rho = ws->seedbuf;
+ dilithium_print_buffer(ws->seedbuf, LC_DILITHIUM_SEEDBYTES,
+ "Keygen - RHO");
+ pack_pk_rho(pk, rho);
+ pack_sk_rho(sk, rho);
+
+ /*
+ * Timecop: RHO' is a random number which is enlarged to sample the
+ * vectors S1 and S2 from. The sampling operation is not considered
+ * relevant for the side channel operation as (a) an attacker does not
+ * have access to the random number and (b) only the result after the
+ * sampling operation of S1 and S2 is released.
+ */
+ rhoprime = rho + LC_DILITHIUM_SEEDBYTES;
+ dilithium_print_buffer(rhoprime, LC_DILITHIUM_CRHBYTES,
+ "Keygen - RHOPrime");
+
+ key = rhoprime + LC_DILITHIUM_CRHBYTES;
+ dilithium_print_buffer(key, LC_DILITHIUM_SEEDBYTES, "Keygen - Key");
+
+ /* Timecop: key goes into the secret key */
+ poison(key, LC_DILITHIUM_SEEDBYTES);
+
+ pack_sk_key(sk, key);
+
+ /* Sample short vectors s1 and s2 */
+
+ polyvecl_uniform_eta(&ws->s1.s1, rhoprime, 0,
+ ws->tmp.poly_uniform_eta_buf);
+ polyveck_uniform_eta(&ws->s2, rhoprime, LC_DILITHIUM_L,
+ ws->tmp.poly_uniform_eta_buf);
+
+ /* Timecop: s1 and s2 are secret */
+ poison(&ws->s1.s1, sizeof(polyvecl));
+ poison(&ws->s2, sizeof(polyveck));
+
+ dilithium_print_polyvecl(&ws->s1.s1,
+ "Keygen - S1 L x N matrix after ExpandS:");
+ dilithium_print_polyveck(&ws->s2,
+ "Keygen - S2 K x N matrix after ExpandS:");
+
+ pack_sk_s1(sk, &ws->s1.s1);
+ pack_sk_s2(sk, &ws->s2);
+
+ polyvecl_ntt(&ws->s1.s1hat);
+ dilithium_print_polyvecl(&ws->s1.s1hat,
+ "Keygen - S1 L x N matrix after NTT:");
+
+ /* Expand matrix */
+ polyvec_matrix_expand(ws->matrix.mat, rho, ws->tmp.poly_uniform_buf);
+ dilithium_print_polyvecl_k(
+ ws->matrix.mat, "Keygen - MAT K x L x N matrix after ExpandA:");
+
+ polyvec_matrix_pointwise_montgomery(
+ &ws->t1, ws->matrix.mat, &ws->s1.s1hat,
+ &ws->tmp.polyvecl_pointwise_acc_montgomery_buf);
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T K x N matrix after A*NTT(s1):");
+
+ polyveck_reduce(&ws->t1);
+ dilithium_print_polyveck(
+ &ws->t1, "Keygen - T K x N matrix reduce after A*NTT(s1):");
+
+ polyveck_invntt_tomont(&ws->t1);
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T K x N matrix after NTT-1:");
+
+ /* Add error vector s2 */
+ polyveck_add(&ws->t1, &ws->t1, &ws->s2);
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T K x N matrix after add S2:");
+
+ /* Extract t1 and write public key */
+ polyveck_caddq(&ws->t1);
+ dilithium_print_polyveck(&ws->t1, "Keygen - T K x N matrix caddq:");
+
+ polyveck_power2round(&ws->t1, &ws->matrix.t0, &ws->t1);
+ dilithium_print_polyveck(&ws->matrix.t0,
+ "Keygen - T0 K x N matrix after power2round:");
+ dilithium_print_polyveck(&ws->t1,
+ "Keygen - T1 K x N matrix after power2round:");
+
+ pack_sk_t0(sk, &ws->matrix.t0);
+ pack_pk_t1(pk, &ws->t1);
+ dilithium_print_buffer(pk->pk, LC_DILITHIUM_PUBLICKEYBYTES,
+ "Keygen - PK after pkEncode:");
+
+ /* Compute H(rho, t1) and write secret key */
+ lc_xof(lc_shake256, pk->pk, sizeof(pk->pk), ws->tmp.tr,
+ sizeof(ws->tmp.tr));
+ dilithium_print_buffer(ws->tmp.tr, sizeof(ws->tmp.tr), "Keygen - TR:");
+ pack_sk_tr(sk, ws->tmp.tr);
+
+ dilithium_print_buffer(sk->sk, LC_DILITHIUM_SECRETKEYBYTES,
+ "Keygen - SK:");
+
+ /* Timecop: pk and sk are not relevant for side-channels any more. */
+ unpoison(pk->pk, sizeof(pk->pk));
+ unpoison(sk->sk, sizeof(sk->sk));
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int lc_dilithium_keypair_from_seed_impl(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed,
+ size_t seedlen)
+{
+ struct lc_static_rng_data s_rng_state;
+ LC_STATIC_DRNG_ON_STACK(s_drng, &s_rng_state);
+ int ret;
+
+ if (seedlen != LC_DILITHIUM_SEEDBYTES)
+ return -EINVAL;
+
+ /* Set the seed that the key generation can pull via the RNG. */
+ s_rng_state.seed = seed;
+ s_rng_state.seedlen = seedlen;
+
+ /* Generate the key pair from the seed. */
+ CKINT(lc_dilithium_keypair_impl(pk, sk, &s_drng));
+
+out:
+ return ret;
+}
+
+static int lc_dilithium_sign_internal_ahat(struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx,
+ struct lc_rng_ctx *rng_ctx)
+{
+ struct workspace_sign {
+ polyvecl s1, y, z;
+ polyveck t0, s2, w1, w0, h;
+ poly cp;
+ uint8_t seedbuf[LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_RNDBYTES +
+ LC_DILITHIUM_CRHBYTES];
+ union {
+ uint8_t poly_uniform_gamma1_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ uint8_t poly_challenge_buf[POLY_CHALLENGE_BYTES];
+ } tmp;
+ };
+ unsigned int n;
+ uint8_t *key, *mu, *rhoprime, *rnd;
+ const polyvecl *mat = ctx->ahat;
+ uint16_t nonce = 0;
+ int ret = 0;
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+ uint8_t __maybe_unused rej_total = 0;
+ LC_DECLARE_MEM(ws, struct workspace_sign, sizeof(uint64_t));
+
+ /* AHat must be present at this time */
+ CKNULL(mat, -EINVAL);
+
+ key = ws->seedbuf;
+ rnd = key + LC_DILITHIUM_SEEDBYTES;
+ mu = rnd + LC_DILITHIUM_RNDBYTES;
+
+ /*
+ * If the external mu is provided, use this verbatim, otherwise
+ * calculate the mu value.
+ */
+ if (ctx->external_mu) {
+ if (ctx->external_mu_len != LC_DILITHIUM_CRHBYTES)
+ return -EINVAL;
+ memcpy(mu, ctx->external_mu, LC_DILITHIUM_CRHBYTES);
+ } else {
+ /*
+ * Set the digestsize - for SHA512 this is a noop, for SHAKE256,
+ * it sets the value. The BUILD_BUG_ON is to check that the
+ * SHA-512 output size is identical to the expected length.
+ */
+ BUILD_BUG_ON(LC_DILITHIUM_CRHBYTES != LC_SHA3_512_SIZE_DIGEST);
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CRHBYTES);
+ lc_hash_final(hash_ctx, mu);
+ }
+ dilithium_print_buffer(mu, LC_DILITHIUM_CRHBYTES, "Siggen - MU:");
+
+ if (rng_ctx) {
+ CKINT(lc_rng_generate(rng_ctx, NULL, 0, rnd,
+ LC_DILITHIUM_RNDBYTES));
+ } else {
+ memset(rnd, 0, LC_DILITHIUM_RNDBYTES);
+ }
+ dilithium_print_buffer(rnd, LC_DILITHIUM_RNDBYTES, "Siggen - RND:");
+
+ unpack_sk_key(key, sk);
+
+ /* Timecop: key is secret */
+ poison(key, LC_DILITHIUM_SEEDBYTES);
+
+ /* Re-use the ws->seedbuf, but making sure that mu is unchanged */
+ BUILD_BUG_ON(LC_DILITHIUM_CRHBYTES >
+ LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_RNDBYTES);
+ rhoprime = key;
+
+ lc_xof(lc_shake256, key,
+ LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_RNDBYTES +
+ LC_DILITHIUM_CRHBYTES,
+ rhoprime, LC_DILITHIUM_CRHBYTES);
+ dilithium_print_buffer(rhoprime, LC_DILITHIUM_CRHBYTES,
+ "Siggen - RHOPrime:");
+
+ /*
+ * Timecop: RHO' is the hash of the secret value of key which is
+ * enlarged to sample the intermediate vector y from. Due to the hashing
+ * any side channel on RHO' cannot allow the deduction of the original
+ * key.
+ */
+ unpoison(rhoprime, LC_DILITHIUM_CRHBYTES);
+
+ unpack_sk_s1(&ws->s1, sk);
+
+ /* Timecop: s1 is secret */
+ poison(&ws->s1, sizeof(polyvecl));
+
+ polyvecl_ntt(&ws->s1);
+ dilithium_print_polyvecl(&ws->s1,
+ "Siggen - S1 L x N matrix after NTT:");
+
+ unpack_sk_s2(&ws->s2, sk);
+
+ /* Timecop: s2 is secret */
+ poison(&ws->s2, sizeof(polyveck));
+
+ polyveck_ntt(&ws->s2);
+ dilithium_print_polyveck(&ws->s2,
+ "Siggen - S2 K x N matrix after NTT:");
+
+ unpack_sk_t0(&ws->t0, sk);
+ polyveck_ntt(&ws->t0);
+ dilithium_print_polyveck(&ws->t0,
+ "Siggen - T0 K x N matrix after NTT:");
+
+rej:
+ /* Sample intermediate vector y */
+ polyvecl_uniform_gamma1(&ws->y, rhoprime, nonce++,
+ ws->tmp.poly_uniform_gamma1_buf);
+ dilithium_print_polyvecl(
+ &ws->y,
+ "Siggen - Y L x N matrix after ExpandMask - start of loop");
+
+ /* Timecop: s2 is secret */
+ poison(&ws->y, sizeof(polyvecl));
+
+ /* Matrix-vector multiplication */
+ ws->z = ws->y;
+ polyvecl_ntt(&ws->z);
+
+ /* Use the cp for this operation as it is not used here so far. */
+ polyvec_matrix_pointwise_montgomery(&ws->w1, mat, &ws->z, &ws->cp);
+ polyveck_reduce(&ws->w1);
+ polyveck_invntt_tomont(&ws->w1);
+ dilithium_print_polyveck(&ws->w1,
+ "Siggen - W K x N matrix after NTT-1");
+
+ /* Decompose w and call the random oracle */
+ polyveck_caddq(&ws->w1);
+ polyveck_decompose(&ws->w1, &ws->w0, &ws->w1);
+
+ /* Timecop: the signature component w1 is not sensitive any more. */
+ unpoison(&ws->w1, sizeof(polyveck));
+ polyveck_pack_w1(sig->sig, &ws->w1);
+ dilithium_print_buffer(sig->sig,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES,
+ "Siggen - w1Encode of W1");
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, mu, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, sig->sig,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES);
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_final(hash_ctx, sig->sig);
+ lc_hash_zero(hash_ctx);
+ dilithium_print_buffer(sig->sig, LC_DILITHIUM_CTILDE_BYTES,
+ "Siggen - ctilde");
+
+ poly_challenge(&ws->cp, sig->sig, ws->tmp.poly_challenge_buf);
+ dilithium_print_poly(&ws->cp, "Siggen - c after SampleInBall");
+ poly_ntt(&ws->cp);
+ dilithium_print_poly(&ws->cp, "Siggen - c after NTT");
+
+ /* Compute z, reject if it reveals secret */
+ polyvecl_pointwise_poly_montgomery(&ws->z, &ws->cp, &ws->s1);
+ polyvecl_invntt_tomont(&ws->z);
+ polyvecl_add(&ws->z, &ws->z, &ws->y);
+ dilithium_print_polyvecl(&ws->z, "Siggen - z <- y + cs1");
+
+ polyvecl_reduce(&ws->z);
+ dilithium_print_polyvecl(&ws->z, "Siggen - z reduction");
+
+ /* Timecop: the signature component z is not sensitive any more. */
+ unpoison(&ws->z, sizeof(polyvecl));
+
+ if (polyvecl_chknorm(&ws->z, LC_DILITHIUM_GAMMA1 - LC_DILITHIUM_BETA)) {
+ dilithium_print_polyvecl(&ws->z, "Siggen - z rejection");
+ rej_total |= 1 << 0;
+ goto rej;
+ }
+
+ /*
+ * Check that subtracting cs2 does not change high bits of w and low
+ * bits do not reveal secret information.
+ */
+ polyveck_pointwise_poly_montgomery(&ws->h, &ws->cp, &ws->s2);
+ polyveck_invntt_tomont(&ws->h);
+ polyveck_sub(&ws->w0, &ws->w0, &ws->h);
+ polyveck_reduce(&ws->w0);
+
+ /* Timecop: verification data w0 is not sensitive any more. */
+ unpoison(&ws->w0, sizeof(polyveck));
+
+ if (polyveck_chknorm(&ws->w0,
+ LC_DILITHIUM_GAMMA2 - LC_DILITHIUM_BETA)) {
+ dilithium_print_polyveck(&ws->w0, "Siggen - r0 rejection");
+ rej_total |= 1 << 1;
+ goto rej;
+ }
+
+ /* Compute hints for w1 */
+ polyveck_pointwise_poly_montgomery(&ws->h, &ws->cp, &ws->t0);
+ polyveck_invntt_tomont(&ws->h);
+ polyveck_reduce(&ws->h);
+
+ /* Timecop: the signature component h is not sensitive any more. */
+ unpoison(&ws->h, sizeof(polyveck));
+
+ if (polyveck_chknorm(&ws->h, LC_DILITHIUM_GAMMA2)) {
+ dilithium_print_polyveck(&ws->h, "Siggen - ct0 rejection");
+ rej_total |= 1 << 2;
+ goto rej;
+ }
+
+ polyveck_add(&ws->w0, &ws->w0, &ws->h);
+
+ n = polyveck_make_hint(&ws->h, &ws->w0, &ws->w1);
+ if (n > LC_DILITHIUM_OMEGA) {
+ dilithium_print_polyveck(&ws->w0, "Siggen - h rejection");
+ rej_total |= 1 << 3;
+ goto rej;
+ }
+
+ /* Write signature */
+ dilithium_print_buffer(sig->sig, LC_DILITHIUM_CTILDE_BYTES,
+ "Siggen - Ctilde:");
+ dilithium_print_polyvecl(&ws->z, "Siggen - Z L x N matrix:");
+ dilithium_print_polyveck(&ws->h, "Siggen - H K x N matrix:");
+
+ pack_sig(sig, &ws->z, &ws->h);
+
+ dilithium_print_buffer(sig->sig, LC_DILITHIUM_CRYPTO_BYTES,
+ "Siggen - Signature:");
+
+out:
+ LC_RELEASE_MEM(ws);
+#ifdef REJECTION_TEST_SAMPLING
+ return ret ? ret : rej_total;
+#else
+ return ret;
+#endif
+}
+
+static int lc_dilithium_sign_internal_noahat(struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx,
+ struct lc_rng_ctx *rng_ctx)
+{
+ struct workspace_sign {
+ polyvecl mat[LC_DILITHIUM_K];
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = sk->sk;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_sign, LC_DILITHIUM_AHAT_ALIGNMENT);
+
+ polyvec_matrix_expand(ws->mat, rho, ws->poly_uniform_buf);
+
+ /* Temporarily set the pointer */
+ ctx->ahat = ws->mat;
+
+ CKINT(lc_dilithium_sign_internal_ahat(sig, sk, ctx, rng_ctx));
+
+out:
+ ctx->ahat = NULL;
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int lc_dilithium_sk_expand_impl(const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_sign {
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = sk->sk;
+ polyvecl *mat = ctx->ahat;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_sign, sizeof(uint64_t));
+
+ /*
+ * The compile time sanity check links API header file with
+ * Dilithium-internal definitions.
+ *
+ * Runtime sanity check ensures that the allocated context has
+ * sufficient size (e.g. not that caller used, say,
+ * LC_DILITHIUM_44_CTX_ON_STACK_AHAT with a ML-DSA 65 or 87 key)
+ */
+#if LC_DILITHIUM_MODE == 2
+ BUILD_BUG_ON(LC_DILITHIUM_44_AHAT_SIZE !=
+ sizeof(polyvecl) * LC_DILITHIUM_K);
+ if (ctx->ahat_size < LC_DILITHIUM_44_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 3
+ BUILD_BUG_ON(LC_DILITHIUM_65_AHAT_SIZE !=
+ sizeof(polyvecl) * LC_DILITHIUM_K);
+ if (ctx->ahat_size < LC_DILITHIUM_65_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 5
+ BUILD_BUG_ON(LC_DILITHIUM_87_AHAT_SIZE !=
+ sizeof(polyvecl) * LC_DILITHIUM_K);
+ if (ctx->ahat_size < LC_DILITHIUM_87_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#else
+#error "Undefined LC_DILITHIUM_MODE"
+#endif
+
+ polyvec_matrix_expand(mat, rho, ws->poly_uniform_buf);
+ dilithium_print_polyvecl_k(mat,
+ "AHAT - A K x L x N matrix after ExpandA:");
+
+ ctx->ahat_expanded = 1;
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int lc_dilithium_sign_internal(struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_sk *sk,
+ struct lc_dilithium_ctx *ctx,
+ struct lc_rng_ctx *rng_ctx)
+{
+ int ret;
+
+ if (!ctx->ahat)
+ return lc_dilithium_sign_internal_noahat(sig, sk, ctx, rng_ctx);
+
+ if (!ctx->ahat_expanded)
+ CKINT(lc_dilithium_sk_expand_impl(sk, ctx));
+
+ CKINT(lc_dilithium_sign_internal_ahat(sig, sk, ctx, rng_ctx));
+
+out:
+ return ret;
+}
+
+static int lc_dilithium_sign_ctx_impl(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ int ret = 0;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!sig || !sk || !ctx)
+ return -EINVAL;
+ /* Either the message or the external mu must be provided */
+ if (!m && !ctx->external_mu)
+ return -EINVAL;
+
+ dilithium_print_buffer(m, mlen, "Siggen - Message");
+
+ unpack_sk_tr(tr, sk);
+
+ if (m) {
+ /* Compute mu = CRH(tr, msg) */
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, tr, LC_DILITHIUM_TRBYTES);
+
+ CKINT(signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx,
+ ctx->userctxlen, m, mlen, ctx->randomizer,
+ ctx->randomizerlen, LC_DILITHIUM_NIST_CATEGORY));
+ }
+
+ ret = lc_dilithium_sign_internal(sig, sk, ctx, rng_ctx);
+
+out:
+ lc_memset_secure(tr, 0, sizeof(tr));
+ return ret;
+}
+
+static int lc_dilithium_sign_impl(struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ LC_DILITHIUM_CTX_ON_STACK(dilithium_ctx);
+ int ret = lc_dilithium_sign_ctx_impl(sig, dilithium_ctx, m, mlen, sk,
+ rng_ctx);
+
+ lc_dilithium_ctx_zero(dilithium_ctx);
+ return ret;
+}
+
+static int lc_dilithium_sign_init_impl(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ struct lc_hash_ctx *hash_ctx;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!ctx || !sk)
+ return -EINVAL;
+
+ hash_ctx = &ctx->dilithium_hash_ctx;
+
+ /* Require the use of SHAKE256 */
+ if (hash_ctx->hash != lc_shake256)
+ return -EOPNOTSUPP;
+
+ unpack_sk_tr(tr, sk);
+
+ /* Compute mu = CRH(tr, msg) */
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, tr, LC_DILITHIUM_TRBYTES);
+ lc_memset_secure(tr, 0, sizeof(tr));
+
+ return signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx, ctx->userctxlen,
+ NULL, 0, ctx->randomizer, ctx->randomizerlen,
+ LC_DILITHIUM_NIST_CATEGORY);
+}
+
+static int lc_dilithium_sign_update_impl(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen)
+{
+ if (!ctx || !m)
+ return -EINVAL;
+
+ /* Compute CRH(tr, msg) */
+ lc_hash_update(&ctx->dilithium_hash_ctx, m, mlen);
+
+ return 0;
+}
+
+static int lc_dilithium_sign_final_impl(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ int ret = 0;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!sig || !ctx || !sk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lc_dilithium_sign_internal(sig, sk, ctx, rng_ctx);
+
+out:
+ lc_dilithium_ctx_zero(ctx);
+ return ret;
+}
+
+static int lc_dilithium_verify_internal_ahat(const struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_verify {
+ union {
+ poly cp;
+ } matrix;
+ polyveck w1;
+ union {
+ polyveck t1, h;
+ polyvecl z;
+ uint8_t mu[LC_DILITHIUM_CRHBYTES];
+ BUF_ALIGNED_UINT8_UINT64(LC_DILITHIUM_CTILDE_BYTES) c2;
+ } buf;
+
+ union {
+ poly polyvecl_pointwise_acc_montgomery_buf;
+ uint8_t buf[LC_DILITHIUM_K *
+ LC_DILITHIUM_POLYW1_PACKEDBYTES];
+ uint8_t poly_challenge_buf[POLY_CHALLENGE_BYTES];
+ } tmp;
+ };
+ /* The first bytes of the signature is c~ and thus contains c1. */
+ const uint8_t *c1 = sig->sig;
+ const polyvecl *mat = ctx->ahat;
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_verify, sizeof(uint64_t));
+
+ /* AHat must be present at this time */
+ CKNULL(mat, -EINVAL);
+
+ unpack_sig_z(&ws->buf.z, sig);
+ if (polyvecl_chknorm(&ws->buf.z,
+ LC_DILITHIUM_GAMMA1 - LC_DILITHIUM_BETA)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ polyvecl_ntt(&ws->buf.z);
+ polyvec_matrix_pointwise_montgomery(
+ &ws->w1, mat, &ws->buf.z,
+ &ws->tmp.polyvecl_pointwise_acc_montgomery_buf);
+
+ /* Matrix-vector multiplication; compute Az - c2^dt1 */
+ poly_challenge(&ws->matrix.cp, c1, ws->tmp.poly_challenge_buf);
+ poly_ntt(&ws->matrix.cp);
+
+ unpack_pk_t1(&ws->buf.t1, pk);
+ polyveck_shiftl(&ws->buf.t1);
+ polyveck_ntt(&ws->buf.t1);
+ polyveck_pointwise_poly_montgomery(&ws->buf.t1, &ws->matrix.cp,
+ &ws->buf.t1);
+
+ polyveck_sub(&ws->w1, &ws->w1, &ws->buf.t1);
+ polyveck_reduce(&ws->w1);
+ polyveck_invntt_tomont(&ws->w1);
+
+ /* Reconstruct w1 */
+ polyveck_caddq(&ws->w1);
+ dilithium_print_polyveck(&ws->w1,
+ "Sigver - W K x N matrix before hint:");
+
+ if (unpack_sig_h(&ws->buf.h, sig))
+ return -EINVAL;
+ dilithium_print_polyveck(&ws->buf.h, "Siggen - H K x N matrix:");
+
+ polyveck_use_hint(&ws->w1, &ws->w1, &ws->buf.h);
+ dilithium_print_polyveck(&ws->w1,
+ "Sigver - W K x N matrix after hint:");
+ polyveck_pack_w1(ws->tmp.buf, &ws->w1);
+ dilithium_print_buffer(ws->tmp.buf,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES,
+ "Sigver - W after w1Encode");
+
+ if (ctx->external_mu) {
+ if (ctx->external_mu_len != LC_DILITHIUM_CRHBYTES)
+ return -EINVAL;
+
+ /* Call random oracle and verify challenge */
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, ctx->external_mu,
+ LC_DILITHIUM_CRHBYTES);
+ } else {
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CRHBYTES);
+ lc_hash_final(hash_ctx, ws->buf.mu);
+
+ /* Call random oracle and verify challenge */
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, ws->buf.mu, LC_DILITHIUM_CRHBYTES);
+ }
+
+ lc_hash_update(hash_ctx, ws->tmp.buf,
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYW1_PACKEDBYTES);
+ lc_hash_set_digestsize(hash_ctx, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_final(hash_ctx, ws->buf.c2.coeffs);
+ lc_hash_zero(hash_ctx);
+
+ /* Signature verification operation */
+ if (lc_memcmp_secure(c1, LC_DILITHIUM_CTILDE_BYTES, ws->buf.c2.coeffs,
+ LC_DILITHIUM_CTILDE_BYTES))
+ ret = -EBADMSG;
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int
+lc_dilithium_verify_internal_noahat(const struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_verify {
+ polyvecl mat[LC_DILITHIUM_K];
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = pk->pk;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_verify, sizeof(uint64_t));
+
+ polyvec_matrix_expand(ws->mat, rho, ws->poly_uniform_buf);
+
+ /* Temporarily set the pointer */
+ ctx->ahat = ws->mat;
+
+ CKINT(lc_dilithium_verify_internal_ahat(sig, pk, ctx));
+
+out:
+ ctx->ahat = NULL;
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int lc_dilithium_pk_expand_impl(const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ struct workspace_verify {
+ uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE];
+ };
+ /* The first bytes of the key is rho. */
+ const uint8_t *rho = pk->pk;
+ polyvecl *mat = ctx->ahat;
+ int ret = 0;
+ LC_DECLARE_MEM(ws, struct workspace_verify, sizeof(uint64_t));
+
+ /*
+ * Runtime sanity check ensures that the allocated context has
+ * sufficient size (e.g. not that caller used, say,
+ * LC_DILITHIUM_44_CTX_ON_STACK_AHAT with a ML-DSA 65 or 87 key)
+ */
+#if LC_DILITHIUM_MODE == 2
+ if (ctx->ahat_size < LC_DILITHIUM_44_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 3
+ if (ctx->ahat_size < LC_DILITHIUM_65_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#elif LC_DILITHIUM_MODE == 5
+ if (ctx->ahat_size < LC_DILITHIUM_87_AHAT_SIZE) {
+ ret = -EOVERFLOW;
+ goto out;
+ }
+#else
+#error "Undefined LC_DILITHIUM_MODE"
+#endif
+
+ polyvec_matrix_expand(mat, rho, ws->poly_uniform_buf);
+ ctx->ahat_expanded = 1;
+
+out:
+ LC_RELEASE_MEM(ws);
+ return ret;
+}
+
+static int lc_dilithium_verify_internal(const struct lc_dilithium_sig *sig,
+ const struct lc_dilithium_pk *pk,
+ struct lc_dilithium_ctx *ctx)
+{
+ int ret;
+
+ if (!ctx->ahat)
+ return lc_dilithium_verify_internal_noahat(sig, pk, ctx);
+
+ if (!ctx->ahat_expanded)
+ CKINT(lc_dilithium_pk_expand_impl(pk, ctx));
+
+ CKINT(lc_dilithium_verify_internal_ahat(sig, pk, ctx));
+
+out:
+ return ret;
+}
+
+static int lc_dilithium_verify_ctx_impl(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_pk *pk)
+{
+ uint8_t tr[LC_DILITHIUM_TRBYTES];
+ int ret = 0;
+
+ if (!sig || !pk || !ctx)
+ return -EINVAL;
+
+ /* Either the message or the external mu must be provided */
+ if (!m && !ctx->external_mu)
+ return -EINVAL;
+
+ /* Make sure that ->mu is large enough for ->tr */
+ BUILD_BUG_ON(LC_DILITHIUM_TRBYTES > LC_DILITHIUM_CRHBYTES);
+
+ /* Compute CRH(H(rho, t1), msg) */
+ lc_xof(lc_shake256, pk->pk, LC_DILITHIUM_PUBLICKEYBYTES, tr,
+ LC_DILITHIUM_TRBYTES);
+
+ if (m) {
+ struct lc_hash_ctx *hash_ctx = &ctx->dilithium_hash_ctx;
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, tr, LC_DILITHIUM_TRBYTES);
+ CKINT(signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx,
+ ctx->userctxlen, m, mlen, ctx->randomizer,
+ ctx->randomizerlen, LC_DILITHIUM_NIST_CATEGORY));
+ }
+
+ ret = lc_dilithium_verify_internal(sig, pk, ctx);
+
+out:
+ lc_memset_secure(tr, 0, sizeof(tr));
+ return ret;
+}
+
+static int lc_dilithium_verify_impl(const struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_pk *pk)
+{
+ LC_DILITHIUM_CTX_ON_STACK(ctx);
+ int ret = lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+
+ lc_dilithium_ctx_zero(ctx);
+ return ret;
+}
+
+static int lc_dilithium_verify_init_impl(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ uint8_t mu[LC_DILITHIUM_TRBYTES];
+ struct lc_hash_ctx *hash_ctx;
+
+ /* rng_ctx is allowed to be NULL as handled below */
+ if (!ctx || !pk)
+ return -EINVAL;
+
+ hash_ctx = &ctx->dilithium_hash_ctx;
+
+ /* Require the use of SHAKE256 */
+ if (hash_ctx->hash != lc_shake256)
+ return -EOPNOTSUPP;
+
+ /* Compute CRH(H(rho, t1), msg) */
+ lc_xof(lc_shake256, pk->pk, LC_DILITHIUM_PUBLICKEYBYTES, mu,
+ LC_DILITHIUM_TRBYTES);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, mu, LC_DILITHIUM_TRBYTES);
+ lc_memset_secure(mu, 0, sizeof(mu));
+
+ return signature_domain_separation(
+ &ctx->dilithium_hash_ctx, ctx->ml_dsa_internal,
+ ctx->dilithium_prehash_type, ctx->userctx, ctx->userctxlen,
+ NULL, 0, ctx->randomizer, ctx->randomizerlen,
+ LC_DILITHIUM_NIST_CATEGORY);
+}
+
+static int lc_dilithium_verify_update_impl(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen)
+{
+ struct lc_hash_ctx *hash_ctx;
+
+ if (!ctx || !m)
+ return -EINVAL;
+
+ /* Compute CRH(H(rho, t1), msg) */
+ hash_ctx = &ctx->dilithium_hash_ctx;
+ lc_hash_update(hash_ctx, m, mlen);
+
+ return 0;
+}
+
+static int lc_dilithium_verify_final_impl(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ int ret = 0;
+
+ if (!sig || !ctx || !pk) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = lc_dilithium_verify_internal(sig, pk, ctx);
+
+out:
+ lc_dilithium_ctx_zero(ctx);
+ return ret;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_SIGNATURE_IMPL_H */
diff --git a/lib/freebl/leancrypto/dilithium_type.h b/lib/freebl/leancrypto/dilithium_type.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_type.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_TYPE_H
+#define DILITHIUM_TYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Prevent Dilithium macros from getting undefined */
+#define LC_DILITHIUM_INTERNAL
+
+/*
+ * This define replaces all symbol names accordingly to allow double compilation
+ * of the same code base.
+ *
+ * Due to the replacement operation, this header file must be included as the
+ * first header file in the entire stack.
+ *
+ * This file can easily be replaced with lc_dilithium.h to achieve the common
+ * functionality without symbol duplication. But in this case, only the
+ * Dilithium security strength is compiled defined in lc_dilithium.h. Duplicate
+ * compilation different sizes would not be possible.
+ */
+#ifdef LC_DILITHIUM_TYPE_65
+#define DILITHIUM_F(name) lc_dilithium_65_##name
+#define lc_dilithium_pk lc_dilithium_65_pk
+#define lc_dilithium_sk lc_dilithium_65_sk
+#define lc_dilithium_sig lc_dilithium_65_sig
+#define lc_dilithium_ed25519_pk lc_dilithium_65_ed25519_pk
+#define lc_dilithium_ed25519_sk lc_dilithium_65_ed25519_sk
+#define lc_dilithium_ed25519_sig lc_dilithium_65_ed25519_sig
+#define lc_dilithium_ed25519_ctx lc_dilithium_65_ed25519_ctx
+#define lc_dilithium_ed448_pk lc_dilithium_65_ed448_pk
+#define lc_dilithium_ed448_sk lc_dilithium_65_ed448_sk
+#define lc_dilithium_ed448_sig lc_dilithium_65_ed448_sig
+#define lc_dilithium_ed448_ctx lc_dilithium_65_ed448_ctx
+
+#include "lc_dilithium_65.h"
+
+#elif defined LC_DILITHIUM_TYPE_44
+#define DILITHIUM_F(name) lc_dilithium_44_##name
+#define lc_dilithium_pk lc_dilithium_44_pk
+#define lc_dilithium_sk lc_dilithium_44_sk
+#define lc_dilithium_sig lc_dilithium_44_sig
+#define lc_dilithium_ed25519_pk lc_dilithium_44_ed25519_pk
+#define lc_dilithium_ed25519_sk lc_dilithium_44_ed25519_sk
+#define lc_dilithium_ed25519_sig lc_dilithium_44_ed25519_sig
+#define lc_dilithium_ed25519_ctx lc_dilithium_44_ed25519_ctx
+#define lc_dilithium_ed448_pk lc_dilithium_44_ed448_pk
+#define lc_dilithium_ed448_sk lc_dilithium_44_ed448_sk
+#define lc_dilithium_ed448_sig lc_dilithium_44_ed448_sig
+#define lc_dilithium_ed448_ctx lc_dilithium_44_ed448_ctx
+
+#include "lc_dilithium_44.h"
+
+#else
+#define DILITHIUM_F(name) lc_dilithium_87_##name
+#define lc_dilithium_pk lc_dilithium_87_pk
+#define lc_dilithium_sk lc_dilithium_87_sk
+#define lc_dilithium_sig lc_dilithium_87_sig
+#define lc_dilithium_ed25519_pk lc_dilithium_87_ed25519_pk
+#define lc_dilithium_ed25519_sk lc_dilithium_87_ed25519_sk
+#define lc_dilithium_ed25519_sig lc_dilithium_87_ed25519_sig
+#define lc_dilithium_ed25519_ctx lc_dilithium_87_ed25519_ctx
+#define lc_dilithium_ed448_pk lc_dilithium_87_ed448_pk
+#define lc_dilithium_ed448_sk lc_dilithium_87_ed448_sk
+#define lc_dilithium_ed448_sig lc_dilithium_87_ed448_sig
+#define lc_dilithium_ed448_ctx lc_dilithium_87_ed448_ctx
+
+#include "lc_dilithium_87.h"
+
+#endif
+
+/*
+ * The following defines simply allow duplicate compilation of the
+ * respective functions.
+ */
+#define lc_dilithium_keypair DILITHIUM_F(keypair)
+#define lc_dilithium_keypair_from_seed DILITHIUM_F(keypair_from_seed)
+#define lc_dilithium_sign DILITHIUM_F(sign)
+#define lc_dilithium_sign_ctx DILITHIUM_F(sign_ctx)
+#define lc_dilithium_sign_init DILITHIUM_F(sign_init)
+#define lc_dilithium_sign_update DILITHIUM_F(sign_update)
+#define lc_dilithium_sign_final DILITHIUM_F(sign_final)
+#define lc_dilithium_verify DILITHIUM_F(verify)
+#define lc_dilithium_verify_ctx DILITHIUM_F(verify_ctx)
+#define lc_dilithium_verify_init DILITHIUM_F(verify_init)
+#define lc_dilithium_verify_update DILITHIUM_F(verify_update)
+#define lc_dilithium_verify_final DILITHIUM_F(verify_final)
+#define lc_dilithium_ctx_alloc DILITHIUM_F(ctx_alloc)
+#define lc_dilithium_ctx_alloc_ahat DILITHIUM_F(ctx_alloc_ahat)
+#define lc_dilithium_ctx_zero_free DILITHIUM_F(ctx_zero_free)
+#define lc_dilithium_ctx_zero DILITHIUM_F(ctx_zero)
+
+#define lc_dilithium_keypair_c DILITHIUM_F(keypair_c)
+#define lc_dilithium_keypair_from_seed_c DILITHIUM_F(keypair_from_seed_c)
+#define lc_dilithium_sign_c DILITHIUM_F(sign_c)
+#define lc_dilithium_sign_ctx_c DILITHIUM_F(sign_ctx_c)
+#define lc_dilithium_sign_init_c DILITHIUM_F(sign_init_c)
+#define lc_dilithium_sign_update_c DILITHIUM_F(sign_update_c)
+#define lc_dilithium_sign_final_c DILITHIUM_F(sign_final_c)
+#define lc_dilithium_verify_c DILITHIUM_F(verify_c)
+#define lc_dilithium_verify_ctx_c DILITHIUM_F(verify_ctx_c)
+#define lc_dilithium_verify_init_c DILITHIUM_F(verify_init_c)
+#define lc_dilithium_verify_update_c DILITHIUM_F(verify_update_c)
+#define lc_dilithium_verify_final_c DILITHIUM_F(verify_final_c)
+
+#define lc_dilithium_ed25519_keypair DILITHIUM_F(ed25519_keypair)
+#define lc_dilithium_ed25519_sign DILITHIUM_F(ed25519_sign)
+#define lc_dilithium_ed25519_sign_ctx DILITHIUM_F(ed25519_sign_ctx)
+#define lc_dilithium_ed25519_sign_init DILITHIUM_F(ed25519_sign_init)
+#define lc_dilithium_ed25519_sign_update DILITHIUM_F(ed25519_sign_update)
+#define lc_dilithium_ed25519_sign_final DILITHIUM_F(ed25519_sign_final)
+#define lc_dilithium_ed25519_verify DILITHIUM_F(ed25519_verify)
+#define lc_dilithium_ed25519_verify_ctx DILITHIUM_F(ed25519_verify_ctx)
+#define lc_dilithium_ed25519_verify_init DILITHIUM_F(ed25519_verify_init)
+#define lc_dilithium_ed25519_verify_update DILITHIUM_F(ed25519_verify_update)
+#define lc_dilithium_ed25519_verify_final DILITHIUM_F(ed25519_verify_final)
+#define lc_dilithium_ed25519_ctx_alloc DILITHIUM_F(ed25519_ctx_alloc)
+#define lc_dilithium_ed25519_ctx_zero_free DILITHIUM_F(ed25519_ctx_zero_free)
+#define lc_dilithium_ed25519_ctx_zero DILITHIUM_F(ed25519_ctx_zero)
+
+#define lc_dilithium_ed448_keypair DILITHIUM_F(ed448_keypair)
+#define lc_dilithium_ed448_sign DILITHIUM_F(ed448_sign)
+#define lc_dilithium_ed448_sign_ctx DILITHIUM_F(ed448_sign_ctx)
+#define lc_dilithium_ed448_sign_init DILITHIUM_F(ed448_sign_init)
+#define lc_dilithium_ed448_sign_update DILITHIUM_F(ed448_sign_update)
+#define lc_dilithium_ed448_sign_final DILITHIUM_F(ed448_sign_final)
+#define lc_dilithium_ed448_verify DILITHIUM_F(ed448_verify)
+#define lc_dilithium_ed448_verify_ctx DILITHIUM_F(ed448_verify_ctx)
+#define lc_dilithium_ed448_verify_init DILITHIUM_F(ed448_verify_init)
+#define lc_dilithium_ed448_verify_update DILITHIUM_F(ed448_verify_update)
+#define lc_dilithium_ed448_verify_final DILITHIUM_F(ed448_verify_final)
+#define lc_dilithium_ed448_ctx_alloc DILITHIUM_F(ed448_ctx_alloc)
+#define lc_dilithium_ed448_ctx_zero_free DILITHIUM_F(ed448_ctx_zero_free)
+#define lc_dilithium_ed448_ctx_zero DILITHIUM_F(ed448_ctx_zero)
+
+#define dilithium_keypair_tester DILITHIUM_F(keypair_tester)
+#define dilithium_siggen_tester DILITHIUM_F(siggen_tester)
+#define dilithium_sigver_tester DILITHIUM_F(sigver_tester)
+
+#define ntt DILITHIUM_F(ntt)
+#define invntt_tomont DILITHIUM_F(invntt_tomont)
+#define poly_chknorm DILITHIUM_F(poly_chknorm)
+#define poly_uniform DILITHIUM_F(poly_uniform)
+#define poly_uniform_eta DILITHIUM_F(poly_uniform_eta)
+#define poly_uniform_gamma1 DILITHIUM_F(poly_uniform_gamma1)
+#define polyz_unpack DILITHIUM_F(polyz_unpack)
+#define poly_challenge DILITHIUM_F(poly_challenge)
+#define polyeta_pack DILITHIUM_F(polyeta_pack)
+#define polyeta_unpack DILITHIUM_F(polyeta_unpack)
+#define polyt1_pack DILITHIUM_F(polyt1_pack)
+#define polyt0_pack DILITHIUM_F(polyt0_pack)
+#define polyt0_unpack DILITHIUM_F(polyt0_unpack)
+#define polyz_pack DILITHIUM_F(polyz_pack)
+#define polyw1_pack DILITHIUM_F(polyw1_pack)
+#define power2round DILITHIUM_F(power2round)
+#define decompose DILITHIUM_F(decompose)
+#define make_hint DILITHIUM_F(make_hint)
+#define use_hint DILITHIUM_F(use_hint)
+
+#define dilithium_print_buffer DILITHIUM_F(print_buffer)
+#define dilithium_print_polyvecl_k DILITHIUM_F(print_polyvecl_k)
+#define dilithium_print_polyvecl DILITHIUM_F(print_polyvecl)
+#define dilithium_print_polyveck DILITHIUM_F(print_polyveck)
+#define dilithium_print_poly DILITHIUM_F(print_poly)
+
+/* AVX2 Implementation */
+#define dilithium_invntt_avx DILITHIUM_F(invntt_avx)
+#define dilithium_ntt_avx DILITHIUM_F(ntt_avx)
+#define dilithium_nttunpack_avx DILITHIUM_F(nttunpack_avx)
+#define dilithium_pointwise_avx DILITHIUM_F(pointwise_avx)
+#define dilithium_pointwise_acc_avx DILITHIUM_F(pointwise_acc_avx)
+#define poly_reduce_avx DILITHIUM_F(poly_reduce_avx)
+#define poly_caddq_avx DILITHIUM_F(poly_caddq_avx)
+#define poly_add_avx DILITHIUM_F(poly_add_avx)
+#define poly_sub_avx DILITHIUM_F(poly_sub_avx)
+#define poly_shiftl_avx DILITHIUM_F(poly_shiftl_avx)
+#define poly_chknorm_avx DILITHIUM_F(poly_chknorm_avx)
+#define poly_uniform_4x_avx DILITHIUM_F(poly_uniform_4x_avx)
+#define poly_uniform_eta_4x_avx DILITHIUM_F(poly_uniform_eta_4x_avx)
+#define poly_uniform_gamma1_4x_avx DILITHIUM_F(poly_uniform_gamma1_4x_avx)
+#define polyz_unpack_avx DILITHIUM_F(polyz_unpack_avx)
+#define poly_challenge_avx DILITHIUM_F(poly_challenge_avx)
+#define polyeta_pack_avx DILITHIUM_F(polyeta_pack_avx)
+#define polyeta_unpack_avx DILITHIUM_F(polyeta_unpack_avx)
+#define polyt1_pack_avx DILITHIUM_F(polyt1_pack_avx)
+#define polyt1_unpack_avx DILITHIUM_F(polyt1_unpack_avx)
+#define polyt0_pack_avx DILITHIUM_F(polyt0_pack_avx)
+#define polyt0_unpack_avx DILITHIUM_F(polyt0_unpack_avx)
+#define polyz_pack_avx DILITHIUM_F(polyz_pack_avx)
+#define polyw1_pack_avx DILITHIUM_F(polyw1_pack_avx)
+#define polyvec_matrix_expand DILITHIUM_F(polyvec_matrix_expand)
+#define polyvec_matrix_expand_row0 DILITHIUM_F(polyvec_matrix_expand_row0)
+#define polyvec_matrix_expand_row1 DILITHIUM_F(polyvec_matrix_expand_row1)
+#define polyvec_matrix_expand_row2 DILITHIUM_F(polyvec_matrix_expand_row2)
+#define polyvec_matrix_expand_row3 DILITHIUM_F(polyvec_matrix_expand_row3)
+#define polyvec_matrix_expand_row4 DILITHIUM_F(polyvec_matrix_expand_row4)
+#define polyvec_matrix_expand_row5 DILITHIUM_F(polyvec_matrix_expand_row5)
+#define polyvec_matrix_expand_row6 DILITHIUM_F(polyvec_matrix_expand_row6)
+#define polyvec_matrix_expand_row7 DILITHIUM_F(polyvec_matrix_expand_row7)
+#define rej_uniform_avx DILITHIUM_F(rej_uniform_avx)
+#define rej_eta_avx DILITHIUM_F(rej_eta_avx)
+#define idxlut DILITHIUM_F(idxlut)
+#define power2round_avx DILITHIUM_F(power2round_avx)
+#define decompose_avx DILITHIUM_F(decompose_avx)
+#define make_hint_avx DILITHIUM_F(make_hint_avx)
+#define use_hint_avx DILITHIUM_F(use_hint_avx)
+#define lc_dilithium_keypair_avx2 DILITHIUM_F(keypair_avx2)
+#define lc_dilithium_keypair_from_seed_avx2 DILITHIUM_F(keypair_from_seed_avx2)
+#define lc_dilithium_sign_avx2 DILITHIUM_F(sign_avx2)
+#define lc_dilithium_sign_ctx_avx2 DILITHIUM_F(sign_ctx_avx2)
+#define lc_dilithium_sign_init_avx2 DILITHIUM_F(sign_init_avx2)
+#define lc_dilithium_sign_update_avx2 DILITHIUM_F(sign_update_avx2)
+#define lc_dilithium_sign_final_avx2 DILITHIUM_F(sign_final_avx2)
+#define lc_dilithium_verify_avx2 DILITHIUM_F(verify_avx2)
+#define lc_dilithium_verify_ctx_avx2 DILITHIUM_F(verify_ctx_avx2)
+#define lc_dilithium_verify_init_avx2 DILITHIUM_F(verify_init_avx2)
+#define lc_dilithium_verify_update_avx2 DILITHIUM_F(verify_update_avx2)
+#define lc_dilithium_verify_final_avx2 DILITHIUM_F(verify_final_avx2)
+
+/* ARMv8 Implementation */
+#define intt_SIMD_top_armv8 DILITHIUM_F(intt_SIMD_top_armv8)
+#define intt_SIMD_bot_armv8 DILITHIUM_F(intt_SIMD_bot_armv8)
+#define ntt_SIMD_top_armv8 DILITHIUM_F(ntt_SIMD_top_armv8)
+#define ntt_SIMD_bot_armv8 DILITHIUM_F(ntt_SIMD_bot_armv8)
+#define poly_uniformx2 DILITHIUM_F(poly_uniformx2)
+#define poly_uniform_etax2 DILITHIUM_F(poly_uniform_etax2)
+#define poly_uniform_gamma1x2 DILITHIUM_F(poly_uniform_gamma1x2)
+#define armv8_10_to_32 DILITHIUM_F(armv8_10_to_32)
+#define poly_reduce_armv8 DILITHIUM_F(poly_reduce_armv8)
+#define poly_caddq_armv8 DILITHIUM_F(poly_caddq_armv8)
+#define poly_power2round_armv8 DILITHIUM_F(poly_power2round_armv8)
+#define poly_pointwise_montgomery_armv8 \
+ DILITHIUM_F(poly_pointwise_montgomery_armv8)
+#define polyvecl_pointwise_acc_montgomery_armv8 \
+ DILITHIUM_F(polyvecl_pointwise_acc_montgomery_armv8)
+#define lc_dilithium_keypair_armv8 DILITHIUM_F(keypair_armv8)
+#define lc_dilithium_keypair_from_seed_armv8 \
+ DILITHIUM_F(keypair_from_seed_armv8)
+#define lc_dilithium_sign_armv8 DILITHIUM_F(sign_armv8)
+#define lc_dilithium_sign_ctx_armv8 DILITHIUM_F(sign_ctx_armv8)
+#define lc_dilithium_sign_init_armv8 DILITHIUM_F(sign_init_armv8)
+#define lc_dilithium_sign_update_armv8 DILITHIUM_F(sign_update_armv8)
+#define lc_dilithium_sign_final_armv8 DILITHIUM_F(sign_final_armv8)
+#define lc_dilithium_verify_armv8 DILITHIUM_F(verify_armv8)
+#define lc_dilithium_verify_ctx_armv8 DILITHIUM_F(verify_ctx_armv8)
+#define lc_dilithium_verify_init_armv8 DILITHIUM_F(verify_init_armv8)
+#define lc_dilithium_verify_update_armv8 DILITHIUM_F(verify_update_armv8)
+#define lc_dilithium_verify_final_armv8 DILITHIUM_F(verify_final_armv8)
+
+/* ARMv7 Implementation */
+#define armv7_ntt_asm_smull DILITHIUM_F(armv7_ntt_asm_smull)
+#define armv7_inv_ntt_asm_smull DILITHIUM_F(armv7_inv_ntt_asm_smull)
+#define armv7_poly_pointwise_invmontgomery_asm_smull \
+ DILITHIUM_F(armv7_poly_pointwise_invmontgomery_asm_smull)
+#define armv7_poly_pointwise_acc_invmontgomery_asm_smull \
+ DILITHIUM_F(armv7_poly_pointwise_acc_invmontgomery_asm_smull)
+#define poly_uniform_armv7 DILITHIUM_F(poly_uniform_armv7)
+#define armv7_poly_reduce_asm DILITHIUM_F(armv7_poly_reduce_asm)
+#define armv7_rej_uniform_asm DILITHIUM_F(armv7_rej_uniform_asm)
+#define lc_dilithium_keypair_armv7 DILITHIUM_F(keypair_armv7)
+#define lc_dilithium_keypair_from_seed_armv7 \
+ DILITHIUM_F(keypair_from_seed_armv7)
+#define lc_dilithium_sign_armv7 DILITHIUM_F(sign_armv7)
+#define lc_dilithium_sign_ctx_armv7 DILITHIUM_F(sign_ctx_armv7)
+#define lc_dilithium_sign_init_armv7 DILITHIUM_F(sign_init_armv7)
+#define lc_dilithium_sign_update_armv7 DILITHIUM_F(sign_update_armv7)
+#define lc_dilithium_sign_final_armv7 DILITHIUM_F(sign_final_armv7)
+#define lc_dilithium_verify_armv7 DILITHIUM_F(verify_armv7)
+#define lc_dilithium_verify_ctx_armv7 DILITHIUM_F(verify_ctx_armv7)
+#define lc_dilithium_verify_init_armv7 DILITHIUM_F(verify_init_armv7)
+#define lc_dilithium_verify_update_armv7 DILITHIUM_F(verify_update_armv7)
+#define lc_dilithium_verify_final_armv7 DILITHIUM_F(verify_final_armv7)
+
+/* RISCV 64 ASM Implementation */
+#define lc_dilithium_keypair_riscv64 DILITHIUM_F(keypair_riscv64)
+#define lc_dilithium_keypair_from_seed_riscv64 \
+ DILITHIUM_F(keypair_from_seed_riscv64)
+#define lc_dilithium_sign_riscv64 DILITHIUM_F(sign_riscv64)
+#define lc_dilithium_sign_ctx_riscv64 DILITHIUM_F(sign_ctx_riscv64)
+#define lc_dilithium_sign_init_riscv64 DILITHIUM_F(sign_init_riscv64)
+#define lc_dilithium_sign_update_riscv64 DILITHIUM_F(sign_update_riscv64)
+#define lc_dilithium_sign_final_riscv64 DILITHIUM_F(sign_final_riscv64)
+#define lc_dilithium_verify_riscv64 DILITHIUM_F(verify_riscv64)
+#define lc_dilithium_verify_ctx_riscv64 DILITHIUM_F(verify_ctx_riscv64)
+#define lc_dilithium_verify_init_riscv64 DILITHIUM_F(verify_init_riscv64)
+#define lc_dilithium_verify_update_riscv64 DILITHIUM_F(verify_update_riscv64)
+#define lc_dilithium_verify_final_riscv64 DILITHIUM_F(verify_final_riscv64)
+#define dilithium_ntt_8l_rv64im DILITHIUM_F(ntt_8l_rv64im)
+#define dilithium_intt_8l_rv64im DILITHIUM_F(intt_8l_rv64im)
+#define dilithium_poly_basemul_8l_init_rv64im \
+ DILITHIUM_F(poly_basemul_8l_init_rv64im)
+#define dilithium_poly_basemul_8l_acc_rv64im \
+ DILITHIUM_F(poly_basemul_8l_acc_rv64im)
+#define dilithium_poly_basemul_8l_acc_end_rv64im \
+ DILITHIUM_F(poly_basemul_8l_acc_end_rv64im)
+#define dilithium_poly_basemul_8l_rv64im DILITHIUM_F(poly_basemul_8l_rv64im)
+#define dilithium_poly_reduce_rv64im DILITHIUM_F(poly_reduce_rv64im)
+
+/* RISCV 64 RVV Implementation */
+#define lc_dilithium_keypair_riscv64_rvv DILITHIUM_F(keypair_riscv64_rvv)
+#define lc_dilithium_keypair_from_seed_riscv64_rvv \
+ DILITHIUM_F(keypair_from_seed_riscv64_rvv)
+#define lc_dilithium_sign_riscv64_rvv DILITHIUM_F(sign_riscv64_rvv)
+#define lc_dilithium_sign_ctx_riscv64_rvv DILITHIUM_F(sign_ctx_riscv64_rvv)
+#define lc_dilithium_sign_init_riscv64_rvv DILITHIUM_F(sign_init_riscv64_rvv)
+#define lc_dilithium_sign_update_riscv64_rvv \
+ DILITHIUM_F(sign_update_riscv64_rvv)
+#define lc_dilithium_sign_final_riscv64_rvv DILITHIUM_F(sign_final_riscv64_rvv)
+#define lc_dilithium_verify_riscv64_rvv DILITHIUM_F(verify_riscv64_rvv)
+#define lc_dilithium_verify_ctx_riscv64_rvv DILITHIUM_F(verify_ctx_riscv64_rvv)
+#define lc_dilithium_verify_init_riscv64_rvv \
+ DILITHIUM_F(verify_init_riscv64_rvv)
+#define lc_dilithium_verify_update_riscv64_rvv \
+ DILITHIUM_F(verify_update_riscv64_rvv)
+#define lc_dilithium_verify_final_riscv64_rvv \
+ DILITHIUM_F(verify_final_riscv64_rvv)
+#define dilithium_ntt_8l_rvv DILITHIUM_F(ntt_8l_rvv)
+#define dilithium_intt_8l_rvv DILITHIUM_F(intt_8l_rvv)
+#define dilithium_poly_basemul_8l_rvv DILITHIUM_F(poly_basemul_8l_rvv)
+#define dilithium_poly_basemul_acc_8l_rvv DILITHIUM_F(poly_basemul_acc_8l_rvv)
+#define dilithium_ntt2normal_order_8l_rvv DILITHIUM_F(ntt2normal_order_8l_rvv)
+#define dilithium_normal2ntt_order_8l_rvv DILITHIUM_F(normal2ntt_order_8l_rvv)
+#define dilithium_poly_reduce_rvv DILITHIUM_F(poly_reduce_rvv)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_TYPE_H */
diff --git a/lib/freebl/leancrypto/dilithium_zetas.h b/lib/freebl/leancrypto/dilithium_zetas.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/dilithium_zetas.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef DILITHIUM_ZETAS_H
+#define DILITHIUM_ZETAS_H
+
+#include "dilithium_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const int32_t dilithium_zetas[LC_DILITHIUM_N];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DILITHIUM_ZETAS_H */
diff --git a/lib/freebl/leancrypto/errno_private-base.h b/lib/freebl/leancrypto/errno_private-base.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/errno_private-base.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_GENERIC_ERRNO_BASE_H
+#define _ASM_GENERIC_ERRNO_BASE_H
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Argument list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+
+#endif
diff --git a/lib/freebl/leancrypto/errno_private.h b/lib/freebl/leancrypto/errno_private.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/errno_private.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_GENERIC_ERRNO_H
+#define _ASM_GENERIC_ERRNO_H
+
+#include "errno_private-base.h"
+
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+
+/*
+ * This error code is special: arch syscall entry code will return
+ * -ENOSYS if users try to call a syscall that doesn't exist. To keep
+ * failures of syscalls that really do exist distinguishable from
+ * failures due to attempts to use a nonexistent syscall, syscall
+ * implementations should refrain from returning -ENOSYS.
+ */
+#define ENOSYS 38 /* Invalid system call number */
+
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+
+#define EDEADLOCK EDEADLK
+
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+
+#define ENOMEDIUM 123 /* No medium found */
+#define EMEDIUMTYPE 124 /* Wrong medium type */
+#define ECANCELED 125 /* Operation Canceled */
+#define ENOKEY 126 /* Required key not available */
+#define EKEYEXPIRED 127 /* Key has expired */
+#define EKEYREVOKED 128 /* Key has been revoked */
+#define EKEYREJECTED 129 /* Key was rejected by service */
+
+/* for robust mutexes */
+#define EOWNERDEAD 130 /* Owner died */
+#define ENOTRECOVERABLE 131 /* State not recoverable */
+
+#define ERFKILL 132 /* Operation not possible due to RF-kill */
+
+#define EHWPOISON 133 /* Memory page has hardware error */
+
+#endif
diff --git a/lib/freebl/leancrypto/ext_headers.h b/lib/freebl/leancrypto/ext_headers.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ext_headers.h
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef EXT_HEADERS_H
+#define EXT_HEADERS_H
+
+/******************************************************************************
+ * Generic Definitions
+ ******************************************************************************/
+#define LC_PURE __attribute__((pure))
+
+/**
+ * @brief Return the size of a member variable of a data structure
+ *
+ * @param [in] struct data structure containing the member variable
+ * @param [in] member member variable name whose size shall be obtained
+ *
+ * @return size of the variable
+ */
+#define lc_member_size(struct, member) (sizeof(((struct *)0)->member))
+
+#ifdef LINUX_KERNEL
+/******************************************************************************
+ * Linux Kernel
+ ******************************************************************************/
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+/* POSIX Support */
+unsigned long getauxval(unsigned long type);
+
+static inline int mlock(const void *ptr, size_t len)
+{
+ (void)ptr;
+ (void)len;
+ return 0;
+}
+
+extern const int errno;
+
+static inline pid_t getpid(void)
+{
+ return 0;
+}
+
+#define restrict
+
+#define printf printk
+
+#ifndef assert
+#define assert(x) WARN_ON(!(x))
+#endif
+
+#define PRIu64 "lu"
+
+#define LC_DEFINE_CONSTRUCTOR(_func) void _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void _func(void)
+
+#define SYSV_ABI
+
+typedef s64 time64_t;
+
+static inline int lc_get_time(time64_t *time_since_epoch)
+{
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ *time_since_epoch = (time64_t)(jiffies / HZ);
+
+ return 0;
+}
+
+#define LC_FIPS_RODATA_SECTION
+
+#elif (defined(LC_EFI_ENVIRONMENT))
+/******************************************************************************
+ * UEFI support
+ ******************************************************************************/
+
+/* POSIX Support */
+#include <efi/efi.h>
+#include <efi/efilib.h>
+
+#include "errno_private.h"
+
+#define LC_DEFINE_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void __attribute__((destructor)) _func(void)
+
+#if !defined __ILP32__
+#define __WORDSIZE 64
+#else
+#define __WORDSIZE 32
+#endif
+
+typedef int pid_t;
+typedef long time_t;
+typedef long long time64_t;
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
+#endif
+
+#if __WORDSIZE == 64
+
+typedef unsigned long uintptr_t;
+
+#ifndef _SIZE_T
+typedef unsigned long size_t;
+#define _SIZE_T
+#endif
+
+#ifndef _SSIZE_T
+typedef long ssize_t;
+#define _SSIZE_T
+#endif
+
+#elif __WORDSIZE == 32
+
+#ifndef _UINTPTR_T
+typedef unsigned int uintptr_t;
+#define _UINTPTR_T
+#endif
+
+#error
+#ifndef _SIZE_T
+typedef unsigned int size_t;
+#define _SIZE_T
+#endif
+
+#ifndef _SSIZE_T
+typedef int ssize_t;
+#define _SSIZE_T
+#endif
+
+#endif
+
+#include "lc_memcpy_secure.h"
+
+void *memset(void *d, int c, unsigned long long n);
+
+static inline int mlock(const void *ptr, size_t len)
+{
+ (void)ptr;
+ (void)len;
+ return 0;
+}
+
+static inline pid_t getpid(void)
+{
+ return 0;
+}
+
+static inline int snprintf(char *restrict str, size_t size,
+ const char *restrict format, ...)
+{
+ (void)format;
+ if (size) {
+ memset(str, 0, size);
+ return (int)size - 1;
+ }
+ return 0;
+}
+
+static inline void *memcpy(void *d, const void *s, size_t n)
+{
+ return lc_memcpy_secure(d, n, s, n);
+}
+
+static inline size_t strlen(const char *str)
+{
+ size_t len = 0;
+
+ while (*str != '\0') {
+ str++;
+ len++;
+ }
+
+ return len;
+}
+
+static inline int lc_get_time(time64_t *time_since_epoch)
+{
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ *time_since_epoch = -1;
+
+ return -EOPNOTSUPP;
+}
+
+#define SYSV_ABI __attribute__((sysv_abi))
+
+/*
+ * See https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
+ */
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough \
+ do { \
+ } while (0)
+#endif
+
+#ifndef assert
+#define assert(x) \
+ if (x) { \
+ Exit(EFI_ABORTED, 0, NULL); \
+ }
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+#define stdout NULL
+
+#define printf(...) Print(L##__VA_ARGS__)
+
+#undef errno
+#define errno errno_private
+static const int errno_private = 0;
+
+#define LC_FIPS_RODATA_SECTION
+
+#define noinline __attribute__((__noinline__))
+
+#elif (defined(__CYGWIN__) || defined(_WIN32))
+/******************************************************************************
+ * Windows
+ ******************************************************************************/
+
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 16
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define LC_DEFINE_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void __attribute__((destructor)) _func(void)
+
+#else
+
+#error "Constructor / destructor not defined for compiler"
+
+#endif
+
+/*
+ * Replace GCC-specific alternative keywords
+ * see https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
+ */
+#ifndef __GNUC__
+#define __asm__ asm
+#define __volatile__ volatile
+#endif
+
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200112L
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static inline int mlock(const void *ptr, size_t len)
+{
+ (void)ptr;
+ (void)len;
+ return 0;
+}
+
+#define SYSV_ABI __attribute__((sysv_abi))
+
+/*
+ * See https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
+ */
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough \
+ do { \
+ } while (0)
+#endif
+
+typedef int64_t time64_t;
+
+static inline int lc_get_time(time64_t *time_since_epoch)
+{
+ struct timespec tp = { 0 };
+
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ if (clock_gettime(CLOCK_REALTIME, &tp) == 0) {
+ *time_since_epoch = tp.tv_sec;
+ return 0;
+ }
+
+ *time_since_epoch = (time64_t)-1;
+ return -errno;
+}
+
+#define LC_FIPS_RODATA_SECTION
+
+#define noinline __attribute__((__noinline__))
+
+#else /* LINUX_KERNEL */
+/******************************************************************************
+ * POSIX
+ ******************************************************************************/
+
+#ifndef MB_LEN_MAX
+#define MB_LEN_MAX 16
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
+
+#define LC_DEFINE_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void)
+#define LC_DEFINE_DESTRUCTOR(_func) void __attribute__((destructor)) _func(void)
+
+#else
+
+#error "Constructor / destructor not defined for compiler"
+
+#endif
+
+/*
+ * Replace GCC-specific alternative keywords
+ * see https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
+ */
+#ifndef __GNUC__
+#define __asm__ asm
+#define __volatile__ volatile
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#define SYSV_ABI
+
+/*
+ * See https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes
+ */
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough \
+ do { \
+ } while (0)
+#endif
+
+typedef int64_t time64_t;
+
+static inline int lc_get_time(time64_t *time_since_epoch)
+{
+ struct timespec tp = { 0 };
+
+ if (!time_since_epoch)
+ return -EINVAL;
+
+ if (clock_gettime(CLOCK_REALTIME, &tp) == 0) {
+ *time_since_epoch = tp.tv_sec;
+ return 0;
+ }
+
+ *time_since_epoch = (time64_t)-1;
+ return -errno;
+}
+
+/*
+ * FIPS 140 integrity check cannot check the .rodata section. Thus move all
+ * relevant data to teh fips_rodata section.
+ */
+#if defined __ELF__
+#define LC_FIPS_RODATA_SECTION_NAME_START __start_fips_rodata
+#define LC_FIPS_RODATA_SECTION_NAME_STOP __stop_fips_rodata
+#define LC_FIPS_RODATA_SECTION_NAME "fips_rodata"
+#define LC_FIPS_RODATA_SECTION \
+ __attribute__((section(LC_FIPS_RODATA_SECTION_NAME)))
+#else
+#define LC_FIPS_RODATA_SECTION
+#endif
+
+#define noinline __attribute__((__noinline__))
+
+#endif /* LINUX_KERNEL */
+
+/******************************************************************************
+ * Generic Definitions after all includes are present
+ ******************************************************************************/
+
+#ifndef ENOPKG
+#define ENOPKG 254 /* Package not installed */
+#endif
+#ifndef EKEYREJECTED
+#define EKEYREJECTED 253 /* Key was rejected by service */
+#endif
+#ifndef ENOKEY
+#define ENOKEY 252 /* Key not found */
+#endif
+
+#endif /* EXT_HEADERS_H */
diff --git a/lib/freebl/leancrypto/fetch_ml_dsa.sh b/lib/freebl/leancrypto/fetch_ml_dsa.sh
new file mode 100755
--- /dev/null
+++ b/lib/freebl/leancrypto/fetch_ml_dsa.sh
@@ -0,0 +1,451 @@
+#!/bin/sh
+declare -a versions
+declare -a modes
+declare -a type
+versions=(ml_dsa_44 ml_dsa_65 ml_dsa_87)
+modes=(2 3 5)
+types=(44 65 87)
+keep=0
+clean=0
+update=0
+clean_gen=0
+branch="master"
+
+library=leancrypto
+git_url=https://github.com/smuellerDD/leancrypto
+ml_dsa_src=ml-dsa/src
+#cpufeatures.h
+#test_helper.h
+
+header_gen_list=( lc_dilithium_44.h lc_dilithium_65.h lc_dilithium_87.h)
+header_gen_internal_list=( lc_memory_support.h )
+header_internal_list=( alignment.h atomic_bool.h atomic.h binhexbin.h
+ bitshift_be.h bitshift.h bitshift_le.h bool.h build_bug_on.h
+ compare.h conv_be_le.h errno_private-base.h errno_private.h
+ ext_headers.h fips_mode.h helper.h initialization.h lc_init.h
+ lc_memcmp_secure.h lc_memcpy_secure.h lc_memory_support.h.in
+ lc_memset_secure.h lc_status.h left_encode.h math_helper.h
+ mutex_w.h null_buffer.h ret_checkers.h rotate.h
+ sidechannel_resistantce.h signature_domain_separation.h
+ small_stack_support.h timecop.h visibility.h xor256.h xor.h )
+header_api_list=(dilithium_type.h lc_dilithium.h)
+header_list=( dilithium_debug.h dilithium_ntt.h dilithium_pack.h dilithium_pct.h
+ dilithium_poly.h dilithium_poly_c.h dilithium_poly_common.h
+ dilithium_polyvec.h dilithium_polyvec_c.h dilithium_reduce.h
+ dilithium_rounding.h dilithium_service_helpers.h
+ dilithium_signature_c.h dilithium_signature_impl.h dilithium_zetas.h)
+internal_list=(signature_domain_separation.c)
+common_list=(dilithium_api.c dilithium_zetas.c)
+specific_list=( dilithium_ntt.c dilithium_poly.c dilithium_rounding.c
+ dilithium_signature_c.c dilithium_signature_helper.c)
+
+
+process_versions()
+{
+ echo "process_version($3)" >&2
+ local -n vers=$1
+ local -n mods=$2
+ declare -p vers
+ declare -p mods
+ vers=()
+ mods=()
+ for i in ${3//;/ }
+ do
+ ver=${i%%:*}
+ mod=${i##*:}
+ echo "spec=/$i/ ver=/$ver/ mod=/$mod/" >&2
+ if [[ "$ver" = "" || "$mod" = "" ]]; then
+ echo "invalid version spec \"$i\"" >&2
+ return 1
+ fi
+ vers+=($ver)
+ mods+=($mod)
+ done
+ return 0
+}
+
+usage()
+{
+ echo "usage: ${0##*/} [--keep_intermediate] [--clean_library]" >&2
+ echo " [--version list_of_versions_and_modes]" >&2
+ echo " [--update] [--branch]" >&2
+ echo "--keep_intermediate don't delete intermediate files used to generate headers" >&2
+ echo "--clean_library remove old library directory before starting" >&2
+ echo "--clean_generated remove generated files before starting" >&2
+ echo "--versions ';' separated list of versions and modes. of the form:" >&2
+ echo " version:mode. example:" >&2
+ echo " \"ml_dsa_44_ref:2;ml_dsa_65_avx:3;ml_dsa_65_ref:3\"" >&2
+ echo "--update if liboq directory exists, update it" >&2
+ echo "--branch select the git branch to use" >&2
+ exit 1
+}
+
+
+while true ; do
+ case "$1" in
+ --keep_intermediate|-k) keep=1; shift
+ ;;
+ --clean_leancrypto|-C)
+ clean=1; shift
+ ;;
+ --clean_generated|-c)
+ clean_gen=1; shift
+ ;;
+ -versions|-v)
+ process_versions versions modes "$2"
+ if (( $? != 0 || ${#modes[@]} != ${#versions[@]} )); then
+ echo "not all versions have a mode \"$2\"" >&2
+ echo "number modes=${#modes}, number versions=${#versions}" >&2
+ echo "versions:${versions[*]}" >&2
+ echo "modes:${modes[*]}" >&2
+ usage
+ fi
+ shift 2
+ ;;
+ --update|-u)
+ update=1; shift
+ ;;
+ --branch|-b)
+ branch="$2"; shift 2
+ ;;
+ "")
+ break
+ ;;
+ *)
+ echo "$0: Unknown option: \"$1\"" >&2
+ usage
+ esac
+done
+
+
+# first fetch and builds leancrypto
+top=$(pwd)
+echo "--------------- fetching $library"
+if [ -d $library ]; then
+ if (( $clean == 1 )); then
+ rm -rf $library
+ git clone -b main $giturl
+ elif (( $update == 1 )); then
+ (cd $library ; git checkout main ; git pull)
+ fi
+else
+ git clone -b main $giturl
+fi
+cd $library
+git checkout $branch
+if [ $? != 0 ]; then
+ echo "branch \"$branch\" not found" >&2
+ echo "valid branches and tags are: " >&2
+ git branch -l >&2
+ git tag -l >&2
+ exit 1
+fi
+libdir=$(pwd)
+ml_dsa_dir=${libdir}/${ml_dsa_src}
+if [ ! -d $ml_dsa_dir ]; then
+ echo "ml_dsa not avaliable on this branch or tag ${branch}" >&2
+ exit 1
+fi
+
+# now set up everything
+if (( clean_gen == 1 )); then
+ (cd $top ; rm -f ml_dsa_* fips202.h randombytes.h $library_git_version.txt )
+fi
+rm -rf build
+mkdir build
+meson setup build -Ddilithium_ed25519=disabled -Ddilithium_ed448=disabled
+cd build
+mkdir sed_scripts
+mkdir generated_code
+build=$(pwd)
+sed_scripts=${build}/sed_scripts
+generated_code=${build}/generated_code
+cd ${libdir}
+
+# these are used to allow is to create 'max' defines
+# we find the value from each of the different lengths and then we
+# keep the maximum, so we can define our max value in terms of that value
+pub_size=0
+pub_define="UNKNOWN"
+priv_size=0
+priv_define="UNKNOWN"
+sig_size=0
+sig_define="UNKNOWN"
+seed_size=0
+seed_define="UNKNOWN"
+
+# ml_dsa_api.h is a completely generated header. It basically resolves the defines in the various
+# xxxx_sign.h headers, which uses macros redefine all the functions, but they use the same macro and
+# function names, so the we can't have a single file that includes all the definitions. library itself
+# handles this by creating their own c stubs for each of the functions. We've already name deconflicted
+# the filenames, so since we have this program we can safely create a proper header file.
+#cat > ${generated_code}/ml_dsa_api.h << __EoF__
+#ifndef ML_DSA_API_H
+#define ML_DSA_API_H
+#// This is a generated file from the various XXX_sign.h files
+#include <stddef.h>
+#include "ml_dsa_apit.h"
+#__EoF__
+cat > ${generated_code}/ml_dsa_apit.h << __EoF__
+// This is a generated file from the various XXX_sign.h files
+#ifndef ML_DSA_APIT_H
+#define ML_DSA_APIT_H
+
+// to make the function defines work
+#ifndef RNDBYTES
+#define RNDBYTES 32
+#endif
+__EoF__
+
+for i in ${commonlist[@]}
+do
+ cp ${ml_dsa_dir}/$i ${generated_code}/
+done
+for file in ${header_internal_list[@]}
+do
+ cp ${libdir}/internal/api/${file} ${generated_code}/${file}
+done
+for file in ${header_gen_internal_list=[@]}
+do
+ cp ${libdir}/build/internal/api/${file} ${generated_code}/${file}
+done
+for file in ${header_gen_list[@]}
+do
+ cp ${libdir}/build/ml-dsa/api/${file} ${generated_code}/${file}
+done
+for file in ${header_api_list[@]}
+do
+ cp ${ml_dsa_dir}/../api/${file} ${generated_code}/${file}
+done
+for file in ${header_list[@]}
+do
+ cp ${ml_dsa_dir}/${file} ${generated_code}/${file}
+done
+for file in ${internal_list[@]}
+do
+ cp ${libdir}/internal/src/${file} ${generated_code}/${file}
+done
+for file in ${common_list[@]}
+do
+ target_file=${file#dilithium}
+ cp ${ml_dsa_dir}/${file} ${generated_code}/mldsa${target_file}
+done
+# now process the each of the versions we are woring on.
+for i in ${!versions[@]}
+do
+ sig=${versions[$i]}
+ mode=${modes[$i]}
+ #ls *.h | sed "s/\(.*\)/s;\1;${sig}_\1;/" > ${sed_scripts}/header_rename_${sig}.sed
+ #echo "s;<oqs/oqs.h>;\"blapi.h\";" >> ${sed_scripts}/header_rename_${sig}.sed
+ #echo "s;OQS_API;;g" >> ${sed_scripts}/header_rename_${sig}.sed
+ echo "#define LC_DILITHIUM_TYPE_${types[$i]} 1" > ${generated_code}/${sig}_def_header.h
+
+ echo "------------------------- Processing $sig mode=$mode ... "
+ for file in ${specific_list[@]}
+ do
+ target_file=${file#dilithium}
+ echo -n -e "fixup ${file} to ${sig}_${file}\r"
+ tmp=${generated_code}/${sig}${file}_tmp
+ cat ${generated_code}/${sig}_def_header.h ${ml_dsa_dir}/${file} > ${tmp}
+ #sed -f ${sed_scripts}/header_rename_${sig}.sed ${file} > ${tmp}
+ #unifdef -DLC_DILITHIUM_MODE=${mode} ${tmp} > ${generated_code}/${sig}_${file}
+ cp ${tmp} ${generated_code}/${sig}${target_file}
+ rm ${tmp}
+ done
+ echo ""
+ cd ${generated_code}
+ #process ${sig}_sign.h to get all the function defines into our ml_dsa_api.h
+# echo "" >> ${generated_code}/ml_dsa_api.h
+# echo "// from ${sig}_sign.h " >> ${generated_code}/ml_dsa_api.h
+#grep -v '^#' ${sig}_sign.h | grep -v "^$" | sed -e "s/crypto_sign/pqcrystals_${sig}/" >> ${generated_code}/ml_dsa_api.h
+
+ # now let's get unrolled defines for the various sizes (keys, signatures, seeds). We use a C program
+ # and make C evaluate the final value of several defines which are calculated on the fly.
+# echo "#include \"${sig}_params.h\"" > ./${sig}_extract_defines.c
+# echo "const char *sign_ver=\"${sig^^}\";" >> ./${sig}_extract_defines.c
+# echo "const char *l_sign_ver=\"${sig}\";" >> ./${sig}_extract_defines.c
+# cat >> ./${sig}_extract_defines.c << __EoF__
+##include <stdio.h>
+#int main(int argc, char **arv) {
+# printf("\n");
+# printf("// from %s_sign.h\n", l_sign_ver);
+# printf("#define %s_PUBLICKEY_BYTES %d\n", sign_ver, CRYPTO_PUBLICKEYBYTES);
+# printf("#define %s_PRIVATEKEY_BYTES %d\n", sign_ver, CRYPTO_SECRETKEYBYTES);
+# printf("#define %s_SIGNATURE_BYTES %d\n", sign_ver, CRYPTO_BYTES);
+# printf("#define %s_SEED_BYTES %d\n", sign_ver, SEEDBYTES);
+#}
+#__EoF__
+# cc -o ${sig}_extract_defines ${sig}_extract_defines.c
+# ./${sig}_extract_defines >> ${generated_code}/ml_dsa_apit.h
+# new_size=$(./${sig}_extract_defines | grep PUBLICKEY | awk '{ print $NF }')
+# if (( new_size > pub_size )); then
+# pub_size=$new_size
+# pub_define=${sig^^}
+# fi
+# new_size=$(./${sig}_extract_defines | grep PRIVATEKEY | awk '{ print $NF }')
+# if (( new_size > priv_size )); then
+# priv_size=$new_size
+# priv_define=${sig^^}
+# fi
+# new_size=$(./${sig}_extract_defines | grep SIGNATURE | awk '{ print $NF }')
+# if (( new_size > sig_size )); then
+# sig_size=$new_size
+# sig_define=${sig^^}
+# fi
+# new_size=$(./${sig}_extract_defines | grep SEED | awk '{ print $NF }')
+# if (( new_size > seed_size )); then
+# seed_size=$new_size
+# seed_define=${sig^^}
+# fi
+# if (( $keep != 1 )); then
+# rm ${sig}_extract_defines*
+# fi
+ # sigh clang20 knows about type c, but we only have cpp in our
+ # .clang-format file, tell clang to use .cpp
+ for file in *.[c]
+ do
+ base=$(basename $file .c)
+ echo clang-format --assume-filename=${base}.cpp --sort-includes=false -i ${file}
+ cat ${file} | clang-format --assume-filename=${base}.cpp --sort-includes=false >${file}.clang
+ mv ${file}.clang ${file}
+ done
+ for file in *.[h]
+ do
+ clang-format --sort-includes=false -i ${file}
+ done
+ tar cf - . | (cd ${top} ; tar xf -)
+ cd ${libarary}
+done
+
+#if (( $version_found == 0 )); then
+# echo "no requested versions (${versions[*]}) found in this branch ($branch)" >&2
+# echo "valid versions are:" >&2
+# ls ${ml_dsa_dir} | grep pqcrystals | sed 's;pqcrystals-dilithium-standard[_|-];;' >&2
+# exit 1
+#fi
+
+echo "" >> ${generated_code}/ml_dsa_apit.h
+#echo "// create the max defines" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_PRIVATE_KEY_LEN ${priv_define}_PRIVATEKEY_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_PUBLIC_KEY_LEN ${priv_define}_PUBLICKEY_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_SIGNATURE_LEN ${priv_define}_SIGNATURE_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#define MAX_MLDSA_REF_SEED_LEN ${priv_define}_SEED_BYTES" >> ${generated_code}/ml_dsa_apit.h
+#echo "#endif /* ML_DSA_API_H */" >> ${generated_code}/ml_dsa_api.h
+echo "#endif /* ML_DSA_APIT_H */" >> ${generated_code}/ml_dsa_apit.h
+
+cp ${generated_code}/ml_dsa_api.h ${top}
+cp ${generated_code}/ml_dsa_apit.h ${top}
+cd ${top}
+echo "------------------------- wrote ml_dsa_apit.h : ... "
+# remember what version we are using
+(cd ${library}; git branch -v) > leancrypto_git_version.txt
+# Now write out the fixed files
+cat > fips202.h << __EoF__
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA liboqs
+
+#ifndef FIPS202_H
+#define FIPS202_H
+
+#include <blapi.h>
+
+#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf
+
+#ifdef NOT_SUPPORTED
+#define SHA3_256_RATE SHA3_256_BLOCK_LENGTH
+#define sha3_256 OQS_SHA3_sha3_256
+#define sha3_256_inc_init OQS_SHA3_sha3_256_inc_init
+#define sha3_256_inc_absorb OQS_SHA3_sha3_256_inc_absorb
+#define sha3_256_inc_finalize OQS_SHA3_sha3_256_inc_finalize
+#define sha3_256_inc_ctx_clone OQS_SHA3_sha3_256_inc_ctx_clone
+#define sha3_256_inc_ctx_release OQS_SHA3_sha3_256_inc_ctx_release
+
+#define SHA3_384_RATE SHA3_384_BLOCK_LENGTH
+#define sha3_384 OQS_SHA3_sha3_384
+#define sha3_384_inc_init OQS_SHA3_sha3_384_inc_init
+#define sha3_384_inc_absorb OQS_SHA3_sha3_384_inc_absorb
+#define sha3_384_inc_finalize OQS_SHA3_sha3_384_inc_finalize
+#define sha3_384_inc_ctx_clone OQS_SHA3_sha3_384_inc_ctx_clone
+#define sha3_384_inc_ctx_release OQS_SHA3_sha3_384_inc_ctx_release
+
+#define SHA3_512_RATE SHA3_512_BLOCK_LENGTH
+#define sha3_512 OQS_SHA3_sha3_512
+#define sha3_512_inc_init OQS_SHA3_sha3_512_inc_init
+#define sha3_512_inc_absorb OQS_SHA3_sha3_512_inc_absorb
+#define sha3_512_inc_finalize OQS_SHA3_sha3_512_inc_finalize
+#define sha3_512_inc_ctx_clone OQS_SHA3_sha3_512_inc_ctx_clone
+#define sha3_512_inc_ctx_release OQS_SHA3_sha3_512_inc_ctx_release
+#endif
+
+typedef SHAKE_128Context *shake128incctx;
+#define shake128_inc_init(ptr) \
+ (*(ptr))=SHAKE_128_NewContext(); \
+ SHAKE_128_Begin(*(ptr))
+#define shake128_inc_absorb(ptr, input, inlen) \
+ SHAKE_128_Absorb(*(ptr), input, inlen)
+#define shake128_inc_finalize(ptr)
+#define shake128_inc_squeeze(output, outlen, ptr) \
+ SHAKE_128_SqueezeEnd(*(ptr), output, outlen)
+#define shake128_inc_ctx_release(ptr) \
+ SHAKE_128_DestroyContext(*(ptr), PR_TRUE)
+#define shake128_inc_ctx_reset(ptr) \
+ SHAKE_128_Begin(ptr)
+#ifdef NOT_SUPPORTED
+#define shake128_inc_ctx_clone OQS_SHA3_shake128_inc_ctx_clone
+#endif
+
+typedef SHAKE_256Context *shake256incctx;
+#define shake256_inc_init(ptr) \
+ (*(ptr))=SHAKE_256_NewContext(); \
+ SHAKE_256_Begin(*(ptr))
+#define shake256_inc_absorb(ptr, input, inlen) \
+ SHAKE_256_Absorb(*(ptr), input, inlen)
+#define shake256_inc_finalize(ptr)
+#define shake256_inc_squeeze(output, outlen, ptr) \
+ SHAKE_256_SqueezeEnd(*(ptr), output, outlen)
+#define shake256_inc_ctx_release(ptr) \
+ SHAKE_256_DestroyContext(*(ptr), PR_TRUE)
+#define shake256_inc_ctx_reset(ptr) \
+ SHAKE_256_Begin(*ptr)
+
+#ifdef NOT_SUPPORTED
+#define shake128_absorb_once OQS_SHA3_shake128_absorb_once
+void OQS_SHA3_shake128_absorb_once(shake128incctx *state, const uint8_t *in, size_t inlen);
+
+#define shake256_absorb_once OQS_SHA3_shake256_absorb_once
+void OQS_SHA3_shake256_absorb_once(shake256incctx *state, const uint8_t *in, size_t inlen);
+#endif
+
+#define shake128_squeezeblocks(OUT, NBLOCKS, STATE) shake128_inc_squeeze(OUT, (NBLOCKS)*SHAKE128_RATE, STATE)
+
+#define shake256_squeezeblocks(OUT, NBLOCKS, STATE) shake256_inc_squeeze(OUT, (NBLOCKS)*SHAKE256_RATE, STATE)
+
+#endif
+__EoF__
+cat > randombytes.h << __EoF__
+// SPDX-License-Identifier: MIT
+// NSS stub for liboqs randombytes.h
+
+#ifndef RANDOMBYTES_H
+#define RANDOMBYTES_H
+
+// run the random number generator through our mldsa code so we can support
+// CKA_SEED (both acquiring it and generating keys from it) and
+// DETERMINISTIC signatures (by returning zeros from the RNG)
+void mldsa_GetRandomBytes(unsigned char *rdn, int bytes);
+#define randombytes mldsa_GetRandomBytes
+
+#endif
+__EoF__
+clang-format -sort-includes=false -i randombytes.h fips202.h ml_dsa_apit.h ml_dsa_api.h
+#output our generated files for review
+echo "ml_dsa_abit.h -----------------"
+cat ml_dsa_apit.h
+echo "git version used -----------------"
+cat oqs_git_version.txt
+#rm -rf ${liboqs}
+exit 0
diff --git a/lib/freebl/leancrypto/fips202.h b/lib/freebl/leancrypto/fips202.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/fips202.h
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA liboqs
+
+#ifndef FIPS202_H
+#define FIPS202_H
+
+#include <blapi.h>
+
+#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf
+
+#ifdef NOT_SUPPORTED
+#define SHA3_256_RATE SHA3_256_BLOCK_LENGTH
+#define sha3_256 OQS_SHA3_sha3_256
+#define sha3_256_inc_init OQS_SHA3_sha3_256_inc_init
+#define sha3_256_inc_absorb OQS_SHA3_sha3_256_inc_absorb
+#define sha3_256_inc_finalize OQS_SHA3_sha3_256_inc_finalize
+#define sha3_256_inc_ctx_clone OQS_SHA3_sha3_256_inc_ctx_clone
+#define sha3_256_inc_ctx_release OQS_SHA3_sha3_256_inc_ctx_release
+
+#define SHA3_384_RATE SHA3_384_BLOCK_LENGTH
+#define sha3_384 OQS_SHA3_sha3_384
+#define sha3_384_inc_init OQS_SHA3_sha3_384_inc_init
+#define sha3_384_inc_absorb OQS_SHA3_sha3_384_inc_absorb
+#define sha3_384_inc_finalize OQS_SHA3_sha3_384_inc_finalize
+#define sha3_384_inc_ctx_clone OQS_SHA3_sha3_384_inc_ctx_clone
+#define sha3_384_inc_ctx_release OQS_SHA3_sha3_384_inc_ctx_release
+
+#define SHA3_512_RATE SHA3_512_BLOCK_LENGTH
+#define sha3_512 OQS_SHA3_sha3_512
+#define sha3_512_inc_init OQS_SHA3_sha3_512_inc_init
+#define sha3_512_inc_absorb OQS_SHA3_sha3_512_inc_absorb
+#define sha3_512_inc_finalize OQS_SHA3_sha3_512_inc_finalize
+#define sha3_512_inc_ctx_clone OQS_SHA3_sha3_512_inc_ctx_clone
+#define sha3_512_inc_ctx_release OQS_SHA3_sha3_512_inc_ctx_release
+#endif
+
+typedef SHAKE_128Context *shake128incctx;
+#define shake128_inc_init(ptr) \
+ (*(ptr)) = SHAKE_128_NewContext(); \
+ SHAKE_128_Begin(*(ptr))
+#define shake128_inc_absorb(ptr, input, inlen) SHAKE_128_Absorb(*(ptr), input, inlen)
+#define shake128_inc_finalize(ptr)
+#define shake128_inc_squeeze(output, outlen, ptr) SHAKE_128_SqueezeEnd(*(ptr), output, outlen)
+#define shake128_inc_ctx_release(ptr) SHAKE_128_DestroyContext(*(ptr), PR_TRUE)
+#define shake128_inc_ctx_reset(ptr) SHAKE_128_Begin(ptr)
+#ifdef NOT_SUPPORTED
+#define shake128_inc_ctx_clone OQS_SHA3_shake128_inc_ctx_clone
+#endif
+
+typedef SHAKE_256Context *shake256incctx;
+#define shake256_inc_init(ptr) \
+ (*(ptr)) = SHAKE_256_NewContext(); \
+ SHAKE_256_Begin(*(ptr))
+#define shake256_inc_absorb(ptr, input, inlen) SHAKE_256_Absorb(*(ptr), input, inlen)
+#define shake256_inc_finalize(ptr)
+#define shake256_inc_squeeze(output, outlen, ptr) SHAKE_256_SqueezeEnd(*(ptr), output, outlen)
+#define shake256_inc_ctx_release(ptr) SHAKE_256_DestroyContext(*(ptr), PR_TRUE)
+#define shake256_inc_ctx_reset(ptr) SHAKE_256_Begin(*ptr)
+
+#ifdef NOT_SUPPORTED
+#define shake128_absorb_once OQS_SHA3_shake128_absorb_once
+void OQS_SHA3_shake128_absorb_once(shake128incctx *state, const uint8_t *in, size_t inlen);
+
+#define shake256_absorb_once OQS_SHA3_shake256_absorb_once
+void OQS_SHA3_shake256_absorb_once(shake256incctx *state, const uint8_t *in, size_t inlen);
+#endif
+
+#define shake128_squeezeblocks(OUT, NBLOCKS, STATE) shake128_inc_squeeze(OUT, (NBLOCKS) * SHAKE128_RATE, STATE)
+
+#define shake256_squeezeblocks(OUT, NBLOCKS, STATE) shake256_inc_squeeze(OUT, (NBLOCKS) * SHAKE256_RATE, STATE)
+
+#endif
diff --git a/lib/freebl/leancrypto/fips_mode.h b/lib/freebl/leancrypto/fips_mode.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/fips_mode.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef FIPS_MODE_H
+#define FIPS_MODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Is FIPS 140 Mode enabled?
+ *
+ * return 0 == false, 1 == true
+ */
+int fips140_mode_enabled(void);
+
+#define FIPS140_PCT_LOOP(func) \
+ if (fips140_mode_enabled()) { \
+ unsigned int __i; \
+ int __ret; \
+ \
+ for (__i = 0; __i < 5; __i++) { \
+ __ret = func; \
+ if (!__ret) \
+ return __ret; \
+ } \
+ assert(0); \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FIPS_MODE_H */
diff --git a/lib/freebl/leancrypto/helper.h b/lib/freebl/leancrypto/helper.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/helper.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef HELPER_H
+#define HELPER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __not_used
+#define __not_used __attribute__((__unused__))
+#endif
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((__unused__))
+#endif
+#ifndef __always_inline
+#define __always_inline __inline __attribute__((__always_inline__))
+#endif
+
+#ifndef likely
+#define likely(x) __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
+#ifndef LINUX_KERNEL
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/**
+ * @brief Obtain pointer to data structure when having a pointer to one of
+ * its members.
+ *
+ * Example:
+ * struct foo *val = member_to_struct(&struct->list_entry, struct foo, list_entry);
+ *
+ * @param member the pointer to the member.
+ * @param data_type the data type of the struct the member is part of.
+ * @param member_var the member variable of the struct the member is
+ * referenced with
+ */
+#define member_to_struct(member, data_type, member_var) \
+ (data_type *)((char *)(member) - (char *)&((data_type *)0)->member_var)
+
+/**
+ * @brief Obtain size of a member of a data structure
+ *
+ * @param struct Data structure
+ * @param member Member variable to obtain size from
+ */
+#define member_size(struct, member) (sizeof(((struct *)0)->member))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HELPER_H */
diff --git a/lib/freebl/leancrypto/initialization.h b/lib/freebl/leancrypto/initialization.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/initialization.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef INITIALIZATION_H
+#define INITIALIZATION_H
+
+#include "visibility.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ascon_fastest_impl(void);
+void sha256_fastest_impl(void);
+void sha512_fastest_impl(void);
+void sha3_fastest_impl(void);
+void aes_fastest_impl(void);
+void kyber_riscv_rvv_selector(void);
+void secure_execution_linux(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INITIALIZATION_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium.h b/lib/freebl/leancrypto/lc_dilithium.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium.h
@@ -0,0 +1,1853 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_H
+#define LC_DILITHIUM_H
+
+#include "ext_headers.h"
+
+#if defined __has_include
+#if __has_include("lc_dilithium_87.h")
+#include "lc_dilithium_87.h"
+#define LC_DILITHIUM_87_ENABLED
+#endif
+#if __has_include("lc_dilithium_65.h")
+#include "lc_dilithium_65.h"
+#define LC_DILITHIUM_65_ENABLED
+#endif
+#if __has_include("lc_dilithium_44.h")
+#include "lc_dilithium_44.h"
+#define LC_DILITHIUM_44_ENABLED
+#endif
+#else
+#error "Compiler misses __has_include"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum lc_dilithium_type {
+ /** Unknown key type */
+ LC_DILITHIUM_UNKNOWN,
+ /** Dilithium 87 */
+ LC_DILITHIUM_87,
+ /** Dilithium 65 */
+ LC_DILITHIUM_65,
+ /** Dilithium 44 */
+ LC_DILITHIUM_44,
+};
+
+/** @defgroup Dilithium ML-DSA / CRYSTALS-Dilithium Signature Mechanism
+ *
+ * \note Although the API uses the term "dilithium", the implementation complies
+ * with FIPS 204. Thus the terms Dilithium and ML-DSA are used interchangeably.
+ *
+ * Dilithium API concept
+ *
+ * The Dilithium API is accessible via the following header files with the
+ * mentioned purpose.
+ *
+ * * lc_dilithium.h: This API is the generic API allowing the caller to select
+ * which Dilithium type (Dilithium 87, 65 or 44) are to be used. The selection
+ * is made either with the flag specified during key generation or by matching
+ * the size of the imported data with the different lc_dilithium_*_load API
+ * calls. All remaining APIs take the information about the Dilithium type
+ * from the provided input data.
+ *
+ * This header file only provides inline functions which selectively call
+ * the API provided with the header files below.
+ *
+ * * lc_dilithium_87.h: Direct access to Dilithium 87.
+ *
+ * * lc_dilithium_65.h: Direct access to Dilithium 65.
+ *
+ * * lc_dilithium_44.h: Direct access to Dilithium 44.
+ *
+ * To support the stream mode of the Dilithium signature operation, a
+ * context structure is required. This context structure can be allocated either
+ * on the stack or heap with \p LC_DILITHIUM_CTX_ON_STACK or
+ * \p lc_dilithium_ctx_alloc. The context should be zeroized
+ * and freed (only for heap) with \p lc_dilithium_ctx_zero or
+ * \p lc_dilithium_ctx_zero_free.
+ */
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_sk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_sk sk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_sk sk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_sk sk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_pk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_pk pk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_pk pk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_pk pk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_sig {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_sig sig_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_sig sig_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_sig sig_44;
+#endif
+ } sig;
+};
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * The provided context size is sufficiently large to support all ML-DSA key
+ * sizes this library version offers support for.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * param [in] name Name of the stack variable
+ */
+#ifdef LC_DILITHIUM_87_ENABLED
+#define LC_DILITHIUM_CTX_ON_STACK_AHAT(name) \
+ LC_DILITHIUM_87_CTX_ON_STACK_AHAT(name)
+#elif defined(LC_DILITHIUM_65_ENABLED)
+LC_DILITHIUM_CTX_ON_STACK_AHAT(name)
+LC_DILITHIUM_65_CTX_ON_STACK_AHAT(name)
+#elif defined(LC_DILITHIUM_44_ENABLED)
+LC_DILITHIUM_CTX_ON_STACK_AHAT(name)
+LC_DILITHIUM_44_CTX_ON_STACK_AHAT(name)
+#endif
+
+/**
+ * @ingroup Dilithium
+ * @brief Allocates Dilithium context on heap
+ *
+ * @param [out] ctx Dilithium context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Allocates Dilithium context on heap with support to keep the internal
+ * representation of the key.
+ *
+ * \note See \p LC_DILITHIUM_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Dilithium context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Zeroizes and frees Dilithium context on heap
+ *
+ * @param [out] ctx Dilithium context pointer
+ */
+void lc_dilithium_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Zeroizes Dilithium context either on heap or on stack
+ *
+ * @param [out] ctx Dilithium context pointer
+ */
+void lc_dilithium_ctx_zero(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Mark the Dilithium context to execute ML-DSA.Sign_internal /
+ * ML-DSA.Verify_internal.
+ *
+ * @param [in] ctx Dilithium context
+ */
+void lc_dilithium_ctx_internal(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Set the hash type that was used for pre-hashing the message. The
+ * message digest is used with the HashML-DSA. The message digest
+ * is to be provided via the message pointer in the sign/verify APIs.
+ *
+ * @param [in] ctx Dilithium context
+ * @param [in] hash Hash context referencing the used hash for pre-hashing the
+ * message
+ */
+void lc_dilithium_ctx_hash(struct lc_dilithium_ctx *ctx,
+ const struct lc_hash *hash);
+
+/**
+ * @ingroup Dilithium
+ * @brief Specify the optional user context string to be applied with the
+ * Dilithium signature operation.
+ *
+ * @param [in] ctx Dilithium context
+ * @param [in] userctx User context string
+ * @param [in] userctxlen Size of the user context string
+ */
+void lc_dilithium_ctx_userctx(struct lc_dilithium_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen);
+
+/**
+ * @ingroup Dilithium
+ * @brief Specify the optional external mu value.
+ *
+ * \note If the external mu is specified, the signature generation /
+ * verification APIs do not require a message. In this case, the message buffer
+ * can be set to NULL.
+ *
+ * \note If both a message and an external mu are provided, the external mu
+ * takes precedence.
+ *
+ * @param [in] ctx Dilithium context
+ * @param [in] external_mu User context string
+ * @param [in] external_mu_len Size of the user context string
+ */
+void lc_dilithium_ctx_external_mu(struct lc_dilithium_ctx *ctx,
+ const uint8_t *external_mu,
+ size_t external_mu_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Invalidate the expanded key that potentially is stored in the context.
+ *
+ * This call can be executed on a context irrespective it was allocated with
+ * space for the expanded representation or not. Thus, the caller does not need
+ * to track whether the context supports the expanded key.
+ *
+ * @param [in] ctx Dilithium context
+ */
+void lc_dilithium_ctx_drop_ahat(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain Dilithium type from secret key
+ *
+ * @param [in] sk Secret key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type lc_dilithium_sk_type(const struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain Dilithium type from public key
+ *
+ * @param [in] pk Public key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type lc_dilithium_pk_type(const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain Dilithium type from signature
+ *
+ * @param [in] sig Signature from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_sig_type(const struct lc_dilithium_sig *sig);
+
+/**
+ * @ingroup Dilithium
+ * @brief Return the size of the Dilithium secret key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_sk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Return the size of the Dilithium public key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_pk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Return the size of the Dilithium signature.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_sig_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Load a Dilithium secret key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sk Secret key to be filled (the caller must have it allocated)
+ * @param [in] src_key Buffer that holds the key to be imported
+ * @param [in] src_key_len Buffer length that holds the key to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_sk_load(struct lc_dilithium_sk *sk, const uint8_t *src_key,
+ size_t src_key_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Load a Dilithium public key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] pk Secret key to be filled (the caller must have it allocated)
+ * @param [in] src_key Buffer that holds the key to be imported
+ * @param [in] src_key_len Buffer length that holds the key to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_pk_load(struct lc_dilithium_pk *pk, const uint8_t *src_key,
+ size_t src_key_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Load a Dilithium signature provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sig Secret key to be filled (the caller must have it allocated)
+ * @param [in] src_sig Buffer that holds the signature to be imported
+ * @param [in] src_sig_len Buffer length that holds the signature to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_sig_load(struct lc_dilithium_sig *sig, const uint8_t *src_sig,
+ size_t src_sig_len);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [in] sk Dilithium secret key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_sk_ptr(uint8_t **dilithium_key, size_t *dilithium_key_len,
+ struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [in] pk Dilithium publi key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_pk_ptr(uint8_t **dilithium_key, size_t *dilithium_key_len,
+ struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Obtain the reference to the Dilithium signature and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto signature,
+ * too.
+ *
+ * @param [out] dilithium_sig Dilithium signature pointer
+ * @param [out] dilithium_sig_len Length of the signature buffer
+ * @param [in] sig Dilithium signature from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_sig_ptr(uint8_t **dilithium_sig, size_t *dilithium_sig_len,
+ struct lc_dilithium_sig *sig);
+
+/**
+ * @ingroup Dilithium
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_keypair(struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup Dilithium
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_keypair_from_seed(struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @brief Pairwise consistency check as per FIPS 140 IG
+ *
+ * This call should be invoked after generating a key pair in FIPS mode
+ *
+ * @param [in] pk Public key
+ * @param [in] sk Secret key
+ *
+ * @return 0 on success, < 0 on error
+ */
+int lc_dilithium_pct(const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign(struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Computes signature woth user context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * Using the ctx structure, the caller can select 3 different types of ML-DSA:
+ *
+ * * ctx->dilithium_prehash_type set to a hash type, HashML-DSA is assumed which
+ * implies that the message m must be exactly digest size (FIPS 204 section
+ * 5.4)
+ *
+ * * ctx->ml_dsa_internal set to 1, the ML-DSA.Sign_internal and
+ * .Verify_internal are executed (FIPS 204 chapter 6)
+ *
+ * * both aforementioned parameter set to NULL / 0, ML-DSA.Sign and
+ * ML-DSA.Verify are executed (FIPS 204 sections 5.2 and 5.3)
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign_ctx(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @ingroup Dilithium
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_sign_final(struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup Dilithium
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_verify(const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_verify_ctx(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+
+/**
+ * @ingroup Dilithium
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_verify_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @ingroup Dilithium
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_verify_final(const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+/** @defgroup HybridDilithium ML-DSA / CRYSTALS-Dilithium Hybrid Signature Mechanism
+ *
+ * The Dilithium hybrid API performs signature operations with Dilithium and
+ * the classic ED25519 algorithm at the same time. The API is identical to
+ * the Dilithium API and can be used as a drop-in replacement.
+ *
+ * ED25519ph is used for the hybrid signature operation compliant to
+ * RFC8032 using a NULL context. This approach is taken to support the
+ * stream mode operation with init / update / final.
+ *
+ * To support the stream mode of the Dilithium signature operation, a
+ * context structure is required. This context structure can be allocated either
+ * on the stack or heap with \p LC_DILITHIUM_ED25519_CTX_ON_STACK or
+ * \p lc_dilithium_ed25519_ctx_alloc. The context should be zeroized
+ * and freed (only for heap) with \p lc_dilithium_ed25519_ctx_zero or
+ * \p lc_dilithium_ed25519_ctx_zero_free.
+ */
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_ed25519_sk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed25519_sk sk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed25519_sk sk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed25519_sk sk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_ed25519_pk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed25519_pk pk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed25519_pk pk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed25519_pk pk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_ed25519_sig {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed25519_sig sig_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed25519_sig sig_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed25519_sig sig_44;
+#endif
+ } sig;
+};
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Allocates Dilithium-ED25519 context on heap
+ *
+ * @param [out] ctx Dilithium-ED25519 context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes and frees Dilithium-ED25519 context on heap
+ *
+ * @param [out] ctx Dilithium-ED25519 context pointer
+ */
+void lc_dilithium_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes Dilithium-ED25519 context either on heap or on stack
+ *
+ * @param [out] ctx Dilithium-ED25519 context pointer
+ */
+void lc_dilithium_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Set the hash type that was used for pre-hashing the message. The
+ * message digest ist used with the HashML-DSA. The message digest
+ * is to be provided via the message pointer in the sign/verify APIs.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] hash Hash context referencing the used hash for pre-hashing the
+ * message
+ */
+void lc_dilithium_ed25519_ctx_hash(struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_hash *hash);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Mark the Dilithium context to execute ML-DSA.Sign_internal /
+ * ML-DSA.Verify_internal.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ */
+void lc_dilithium_ed25519_ctx_internal(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional user context string to be applied with the
+ * Dilithium-ED25519 signature operation.
+ *
+ * \warning The operation of the HashComposite-ML-DSA operation clears out
+ * this context during processing. If this context is reused, the caller MUST
+ * set the cotext again.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] userctx User context string
+ * @param [in] userctxlen Size of the user context string
+ */
+void lc_dilithium_ed25519_ctx_userctx(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *userctx,
+ size_t userctxlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional randomizer to be applied with the
+ * Dilithium-ED25519 signature operation.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] randomizer Randomizer
+ * @param [in] randomizerlen Size of randomizer
+ */
+void lc_dilithium_ed25519_ctx_randomizer(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *randomizer,
+ size_t randomizerlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from secret key
+ *
+ * @param [in] sk Secret key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed25519_sk_type(const struct lc_dilithium_ed25519_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from public key
+ *
+ * @param [in] pk Public key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed25519_pk_type(const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from signature
+ *
+ * @param [in] sig Signature from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed25519_sig_type(const struct lc_dilithium_ed25519_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium secret key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed25519_sk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium public key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed25519_pk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium signature.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed25519_sig_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium secret key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed25519_src_key Buffer that holds the ED25519 key to be imported
+ * @param [in] ed25519_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed25519_sk_load(struct lc_dilithium_ed25519_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium public key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] pk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed25519_src_key Buffer that holds the ED25519 key to be imported
+ * @param [in] ed25519_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed25519_pk_load(struct lc_dilithium_ed25519_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium signature provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sig Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_sig Buffer that holds the Dilithium signature to be
+ * imported
+ * @param [in] dilithium_src_sig_len Buffer length that holds the Dilithium
+ * signature to be imported
+ * @param [in] ed25519_src_sig Buffer that holds the ED25519 signature to be
+ * imported
+ * @param [in] ed25519_src_sig_len Buffer length that holds the ED25519
+ * signature to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed25519_sig_load(struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed25519_src_sig,
+ size_t ed25519_src_sig_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed25519_key ED25519 key pointer
+ * @param [out] ed25519_key_len ED25519 of the key buffer
+ * @param [in] sk Dilithium secret key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed25519_sk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len,
+ uint8_t **ed25519_key, size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed25519_key ED25519 key pointer
+ * @param [out] ed25519_key_len ED25519 of the key buffer
+ * @param [in] pk Dilithium publi key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed25519_pk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len,
+ uint8_t **ed25519_key, size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium signature and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto signature,
+ * too.
+ *
+ * @param [out] dilithium_sig Dilithium signature pointer
+ * @param [out] dilithium_sig_len Length of the signature buffer
+ * @param [out] ed25519_sig ED25519 signature pointer
+ * @param [out] ed25519_sig_len ED25519 of the signature buffer
+ * @param [in] sig Dilithium signature from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed25519_sig_ptr(uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len,
+ uint8_t **ed25519_sig, size_t *ed25519_sig_len,
+ struct lc_dilithium_ed25519_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_keypair(struct lc_dilithium_ed25519_pk *pk,
+ struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign(struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_ctx(struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED25519 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_init(struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature in stream mode
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in stream mode
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_sign_final(struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed25519_verify(const struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed25519_verify_ctx(const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature verification operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED25519 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_verify_init(struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature verification in stream mode
+ *
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in stream mode
+ *
+ * @param [in] sig pointer to input signatur
+ * @param [in] ctx Dilithium-ED25519 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed25519_verify_final(const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED448_SIG
+
+/** @defgroup HybridDilithium ML-DSA / CRYSTALS-Dilithium Hybrid Signature Mechanism
+ *
+ * The Dilithium hybrid API performs signature operations with Dilithium and
+ * the classic ED448 algorithm at the same time. The API is identical to
+ * the Dilithium API and can be used as a drop-in replacement.
+ *
+ * ED448ph is used for the hybrid signature operation compliant to
+ * RFC8032 using a NULL context. This approach is taken to support the
+ * stream mode operation with init / update / final.
+ *
+ * To support the stream mode of the Dilithium signature operation, a
+ * context structure is required. This context structure can be allocated either
+ * on the stack or heap with \p LC_DILITHIUM_ED448_CTX_ON_STACK or
+ * \p lc_dilithium_ed448_ctx_alloc. The context should be zeroized
+ * and freed (only for heap) with \p lc_dilithium_ed448_ctx_zero or
+ * \p lc_dilithium_ed448_ctx_zero_free.
+ *
+ * \note The APIs for ML-DSA ED25519 and ED448 are identical. You can switch
+ * between both by simply applying a global search and replace of 25519 <-> 448.
+ */
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_ed448_sk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed448_sk sk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed448_sk sk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed448_sk sk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_ed448_pk {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed448_pk pk_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed448_pk pk_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed448_pk pk_44;
+#endif
+ } key;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_ed448_sig {
+ enum lc_dilithium_type dilithium_type;
+ union {
+#ifdef LC_DILITHIUM_87_ENABLED
+ struct lc_dilithium_87_ed448_sig sig_87;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ struct lc_dilithium_65_ed448_sig sig_65;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ struct lc_dilithium_44_ed448_sig sig_44;
+#endif
+ } sig;
+};
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Allocates Dilithium-ED448 context on heap
+ *
+ * @param [out] ctx Dilithium-ED448 context pointer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes and frees Dilithium-ED448 context on heap
+ *
+ * @param [out] ctx Dilithium-ED448 context pointer
+ */
+void lc_dilithium_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Zeroizes Dilithium-ED448 context either on heap or on stack
+ *
+ * @param [out] ctx Dilithium-ED448 context pointer
+ */
+void lc_dilithium_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Set the hash type that was used for pre-hashing the message. The
+ * message digest ist used with the HashML-DSA. The message digest
+ * is to be provided via the message pointer in the sign/verify APIs.
+ *
+ * @param [in] ctx Dilithium-ED448 context
+ * @param [in] hash Hash context referencing the used hash for pre-hashing the
+ * message
+ */
+void lc_dilithium_ed448_ctx_hash(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_hash *hash);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Mark the Dilithium context to execute ML-DSA.Sign_internal /
+ * ML-DSA.Verify_internal.
+ *
+ * @param [in] ctx Dilithium-ED448 context
+ */
+void lc_dilithium_ed448_ctx_internal(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional user context string to be applied with the
+ * Dilithium-ED448 signature operation.
+ *
+ * \warning The operation of the HashComposite-ML-DSA operation clears out
+ * this context during processing. If this context is reused, the caller MUST
+ * set the cotext again.
+ *
+ * @param [in] ctx Dilithium-ED448 context
+ * @param [in] userctx User context string
+ * @param [in] userctxlen Size of the user context string
+ */
+void lc_dilithium_ed448_ctx_userctx(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Specify the optional randomizer to be applied with the
+ * Dilithium-ED25519 signature operation.
+ *
+ * @param [in] ctx Dilithium-ED25519 context
+ * @param [in] randomizer Randomizer
+ * @param [in] randomizerlen Size of randomizer
+ */
+void lc_dilithium_ed448_ctx_randomizer(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *randomizer,
+ size_t randomizerlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from secret key
+ *
+ * @param [in] sk Secret key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed448_sk_type(const struct lc_dilithium_ed448_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from public key
+ *
+ * @param [in] pk Public key from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed448_pk_type(const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain Dilithium type from signature
+ *
+ * @param [in] sig Signature from which the type is to be obtained
+ *
+ * @return key type
+ */
+enum lc_dilithium_type
+lc_dilithium_ed448_sig_type(const struct lc_dilithium_ed448_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium secret key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed448_sk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium public key.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed448_pk_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Return the size of the Dilithium signature.
+ *
+ * @param [in] dilithium_type Dilithium type for which the size is requested
+ *
+ * @return requested size
+ */
+LC_PURE unsigned int
+lc_dilithium_ed448_sig_size(enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium secret key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed448_src_key Buffer that holds the ED448 key to be imported
+ * @param [in] ed448_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed448_sk_load(struct lc_dilithium_ed448_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key,
+ size_t ed448_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium public key provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] pk Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_key Buffer that holds the Dilithium key to be
+ * imported
+ * @param [in] dilithium_src_key_len Buffer length that holds the key to be
+ * imported
+ * @param [in] ed448_src_key Buffer that holds the ED448 key to be imported
+ * @param [in] ed448_src_key_len Buffer length that holds the key to be
+ * imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed448_pk_load(struct lc_dilithium_ed448_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key,
+ size_t ed448_src_key_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Load a Dilithium signature provided with a buffer into the leancrypto
+ * data structure.
+ *
+ * @param [out] sig Secret key to be filled (the caller must have it allocated)
+ * @param [in] dilithium_src_sig Buffer that holds the Dilithium signature to be
+ * imported
+ * @param [in] dilithium_src_sig_len Buffer length that holds the Dilithium
+ * signature to be imported
+ * @param [in] ed448_src_sig Buffer that holds the ED448 signature to be
+ * imported
+ * @param [in] ed448_src_sig_len Buffer length that holds the ED448
+ * signature to be imported
+ *
+ * @return 0 on success or < 0 on error
+ */
+int lc_dilithium_ed448_sig_load(struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed448_src_sig,
+ size_t ed448_src_sig_len);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed448_key ED448 key pointer
+ * @param [out] ed448_key_len ED448 of the key buffer
+ * @param [in] sk Dilithium secret key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed448_sk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len,
+ struct lc_dilithium_ed448_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium key and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto key, too.
+ *
+ * @param [out] dilithium_key Dilithium key pointer
+ * @param [out] dilithium_key_len Length of the key buffer
+ * @param [out] ed448_key ED448 key pointer
+ * @param [out] ed448_key_len ED448 of the key buffer
+ * @param [in] pk Dilithium publi key from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed448_pk_ptr(uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len,
+ struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Obtain the reference to the Dilithium signature and its length
+ *
+ * \note Only pointer references into the leancrypto data structure are returned
+ * which implies that any modification will modify the leancrypto signature,
+ * too.
+ *
+ * @param [out] dilithium_sig Dilithium signature pointer
+ * @param [out] dilithium_sig_len Length of the signature buffer
+ * @param [out] ed448_sig ED448 signature pointer
+ * @param [out] ed448_sig_len ED448 of the signature buffer
+ * @param [in] sig Dilithium signature from which the references are obtained
+ *
+ * @return 0 on success, != 0 on error
+ */
+int lc_dilithium_ed448_sig_ptr(uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len, uint8_t **ed448_sig,
+ size_t *ed448_sig_len,
+ struct lc_dilithium_ed448_sig *sig);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ * @param [in] dilithium_type type of the Dilithium key to generate
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_keypair(struct lc_dilithium_ed448_pk *pk,
+ struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign(struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_ctx(struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED448 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature in stream mode
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Computes signature in stream mode
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_sign_final(struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in one shot
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed448_verify(const struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * \note The one-shot API provides the algorithm of Composite-ML-DSA as outlined
+ * in https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html
+ * If the caller specifies a hash algorithm as pre-hash algorithm in the context
+ * via \p lc_dilithium_ctx_hash then *only* the ML-DSA part is affected and
+ * changed into a HashML-DSA which implies that the resulting operation is still
+ * Composite-ML-DSA but with a HashML-DSA used internally - i.e. the resulting
+ * algorithm does not comply to any standard. Therefore, it is best to not
+ * use this method.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed448_verify_ctx(const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Initializes signature verification operation in stream mode
+ *
+ * \note The stream API provides the algorithm of HashComposite-ML-DSA as
+ * outlined in
+ * https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html.
+ * The reason is that ED448 cannot operate in stream mode and thus must be
+ * turned into using a pre-hashed message.
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Updates signature verification in stream mode
+ *
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @ingroup HybridDilithium
+ * @brief Verifies signature in stream mode
+ *
+ * @param [in] sig pointer to input signatur
+ * @param [in] ctx Dilithium-ED448 context pointer
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_ed448_verify_final(const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium_44.h b/lib/freebl/leancrypto/lc_dilithium_44.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium_44.h
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_44_H
+#define LC_DILITHIUM_44_H
+
+#ifndef __ASSEMBLER__
+
+#include "ext_headers.h"
+#include "lc_hash.h"
+#include "lc_rng.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+
+#endif /* __ASSEMBLER__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+/*
+ * Dilithium Security Levels
+ * 2 -> 192 bits of security strength
+ * 3 -> 225 bits of security strength
+ * 5 -> 257 bits of security strength
+ */
+#define LC_DILITHIUM_MODE 2
+
+#define LC_DILITHIUM_SEEDBYTES 32
+#define LC_DILITHIUM_CRHBYTES 64
+#define LC_DILITHIUM_TRBYTES 64
+#define LC_DILITHIUM_RNDBYTES 32
+#define LC_DILITHIUM_N 256
+#define LC_DILITHIUM_Q 8380417
+#define LC_DILITHIUM_D 13
+#define LC_DILITHIUM_ROOT_OF_UNITY 1753
+
+#if LC_DILITHIUM_MODE == 2
+#define LC_DILITHIUM_NIST_CATEGORY 1
+#define LC_DILITHIUM_LAMBDA 128
+#define LC_DILITHIUM_K 4
+#define LC_DILITHIUM_L 4
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 39
+#define LC_DILITHIUM_BETA 78
+#define LC_DILITHIUM_GAMMA1 (1 << 17)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 88)
+#define LC_DILITHIUM_OMEGA 80
+
+#elif LC_DILITHIUM_MODE == 3
+#define LC_DILITHIUM_NIST_CATEGORY 3
+#define LC_DILITHIUM_LAMBDA 192
+#define LC_DILITHIUM_K 6
+#define LC_DILITHIUM_L 5
+#define LC_DILITHIUM_ETA 4
+#define LC_DILITHIUM_TAU 49
+#define LC_DILITHIUM_BETA 196
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 55
+
+#elif LC_DILITHIUM_MODE == 5
+#define LC_DILITHIUM_NIST_CATEGORY 5
+#define LC_DILITHIUM_LAMBDA 256
+#define LC_DILITHIUM_K 8
+#define LC_DILITHIUM_L 7
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 60
+#define LC_DILITHIUM_BETA 120
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 75
+
+#endif
+
+#define LC_DILITHIUM_CTILDE_BYTES (LC_DILITHIUM_LAMBDA * 2 / 8)
+#define LC_DILITHIUM_POLYT1_PACKEDBYTES 320
+#define LC_DILITHIUM_POLYT0_PACKEDBYTES 416
+#define LC_DILITHIUM_POLYVECH_PACKEDBYTES (LC_DILITHIUM_OMEGA + LC_DILITHIUM_K)
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 576
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 640
+#endif
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 192
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 128
+#endif
+
+#if LC_DILITHIUM_ETA == 2
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 96
+#elif LC_DILITHIUM_ETA == 4
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 128
+#endif
+
+/*
+ * Sizes of the different Dilithium buffer types.
+ *
+ * WARNING: Do not use these defines in your code. If you need the sizes of
+ * the different variable sizes, use sizeof of the different variable structs or
+ * use the different *_size functions documented below to retrieve the data size
+ * of a particular Dilithium component.
+ */
+#define LC_DILITHIUM_PUBLICKEYBYTES \
+ (LC_DILITHIUM_SEEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT1_PACKEDBYTES)
+#define LC_DILITHIUM_SECRETKEYBYTES \
+ (2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT0_PACKEDBYTES)
+
+#define LC_DILITHIUM_CRYPTO_BYTES \
+ (LC_DILITHIUM_CTILDE_BYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES + \
+ LC_DILITHIUM_POLYVECH_PACKEDBYTES)
+/// \endcond
+
+#ifndef __ASSEMBLER__
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_44_sk {
+ uint8_t sk[LC_DILITHIUM_SECRETKEYBYTES];
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_44_pk {
+ uint8_t pk[LC_DILITHIUM_PUBLICKEYBYTES];
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_44_sig {
+ uint8_t sig[LC_DILITHIUM_CRYPTO_BYTES];
+};
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+struct lc_dilithium_ctx {
+ /**
+ * @brief Hash context used internally to the library - it should not
+ * be touched by the user
+ */
+ struct lc_hash_ctx dilithium_hash_ctx;
+
+ /**
+ * @brief State memory of the hash context used internally to the
+ * library - it should not be touched by the user
+ */
+ uint8_t shake_state[LC_SHA3_STATE_SIZE_ALIGN(LC_SHA3_256_CTX_SIZE)];
+
+ /**
+ * @brief When using HashML-DSA, set the hash reference used for the
+ * hash operation. Allowed values are lc_sha256, lc_sha512, lc_sha3_256,
+ * lc_sha3_384, lc_sha3_512, lc_shake128 and lc_shake256. Note, the
+ * actual message digest operation can be performed external to
+ * leancrypto. This parameter only shall indicate the used hash
+ * operation.
+ *
+ * \note Use \p lc_dilithium_ctx_hash or
+ * \p lc_dilithium_ed25519_ctx_hash to set this value.
+ */
+ const struct lc_hash *dilithium_prehash_type;
+
+ /**
+ * @brief length of the user context (allowed range between 0 and 255
+ * bytes)
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ size_t userctxlen;
+
+ /**
+ * @brief buffer with a caller-specified context string
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ const uint8_t *userctx;
+
+ /**
+ * @brief Pointer to the AHat buffer. This can be provided by the caller
+ * or it must be NULL otherwise.
+ *
+ * \note Use \p LC_DILITHIUM_CTX_ON_STACK_AHAT to provide memory for
+ * storing AHat in the caller context and thus make the signature
+ * operation much faster starting with the 2nd use of the key (pair).
+ */
+ void *ahat;
+ unsigned short ahat_size;
+
+ /**
+ * @brief Pointer to the external mu.
+ *
+ * If set, the signature operation will use the provided mu instead of
+ * the message. In this case, the message pointer to the signature
+ * generation or verification can be NULL.
+ */
+ const uint8_t *external_mu;
+ size_t external_mu_len;
+
+ /**
+ * @brief Pointer to the randomizer
+ *
+ * This is used for the Composite signature: For the discussion of the
+ * randomizer, see https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ const uint8_t *randomizer;
+ size_t randomizerlen;
+
+ /**
+ * @brief NIST category required for composite signatures
+ *
+ * The domain separation logic depends on the selection of the right
+ * OID for the "Domain" data.
+ */
+ unsigned int nist_category;
+
+ /**
+ * @brief When set to true, only the ML-DSA.Sign_internal or
+ * ML-DSA.Verify_internal are performed (see FIPS 204 chapter 6).
+ * Otherwise the ML-DSA.Sign / ML-DSA.Verify (see FIPS chapter 5) is
+ * applied.
+ *
+ * \note Use \p lc_dilithium_ctx_internal or
+ * \p lc_dilithium_ed25519_ctx_internal to set this value.
+ *
+ * \warning Only set this value to true if you exactly know what you are
+ * doing!.
+ */
+ unsigned int ml_dsa_internal : 1;
+
+ /**
+ * @brief Was aHat already filled? This is used and set internally.
+ */
+ unsigned int ahat_expanded : 1;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+
+/*
+ * The alignment is based on largest alignment of a polyvecl typedef - this is
+ * the AVX2 definition.
+ */
+#define LC_DILITHIUM_AHAT_ALIGNMENT (32)
+
+/*
+ * Padding between struct lc_dilithium_ctx and AHat buffer to ensure AHat buffer
+ * is aligned to LC_DILITHIUM_AHAT_ALIGNMENT
+ */
+#define LC_DILITHIUM_44_AHAT_PAD \
+ (LC_DILITHIUM_AHAT_ALIGNMENT - \
+ (sizeof(struct lc_dilithium_ctx) % LC_DILITHIUM_AHAT_ALIGNMENT))
+
+/* Size of the AHat matrix for ML-DSA 87 */
+#define LC_DILITHIUM_44_AHAT_SIZE \
+ (256 * sizeof(int32_t) * LC_DILITHIUM_K * LC_DILITHIUM_L)
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_SIZE sizeof(struct lc_dilithium_ctx)
+
+#define LC_DILITHIUM_CTX_INIT_HASH(name) \
+ LC_SHAKE_256_CTX((&(name)->dilithium_hash_ctx))
+
+#define LC_DILITHIUM_SET_CTX(name) \
+ LC_DILITHIUM_CTX_INIT_HASH(name); \
+ (name)->dilithium_prehash_type = NULL; \
+ (name)->ml_dsa_internal = 0; \
+ (name)->userctxlen = 0; \
+ (name)->userctx = NULL; \
+ (name)->ahat = NULL; \
+ (name)->ahat_size = 0; \
+ (name)->external_mu = NULL; \
+ (name)->external_mu_len = 0; \
+ (name)->randomizer = NULL; \
+ (name)->randomizerlen = 0; \
+ (name)->nist_category = 0;
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context or additional
+ * parameter relevant for the signature operation.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#define LC_DILITHIUM_44_CTX_ON_STACK_AHAT(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_44_AHAT_PAD + \
+ LC_DILITHIUM_44_AHAT_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ name->ahat = (uint8_t *)name + LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_44_AHAT_PAD; \
+ name->ahat_expanded = 0; \
+ name->ahat_size = LC_DILITHIUM_44_AHAT_SIZE; \
+ _Pragma("GCC diagnostic pop")
+
+/**
+ * @brief Zeroize Dilithium context allocated with
+ * LC_DILITHIUM_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium context to be zeroized
+ */
+static inline void lc_dilithium_44_ctx_zero(struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_hash_zero(&ctx->dilithium_hash_ctx);
+ if (ctx->ahat) {
+ lc_memset_secure(ctx->ahat, 0, ctx->ahat_size);
+ ctx->ahat_expanded = 0;
+ }
+}
+
+/**
+ * @brief Allocate Dilithium stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Allocate Dilithium stream context on heap including additional
+ * parameter relevant for the signature operation.
+ *
+ * \note See \p LC_DILITHIUM_44_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium stream context
+ *
+ * @param [in] ctx Dilithium stream context to be zeroized and freed
+ */
+void lc_dilithium_44_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @brief Return the size of the Dilithium secret key.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_44_sk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_44_sk, sk);
+}
+
+/**
+ * @brief Return the size of the Dilithium public key.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_44_pk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_44_pk, pk);
+}
+
+/**
+ * @brief Return the size of the Dilithium signature.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_44_sig_size(void)
+{
+ return lc_member_size(struct lc_dilithium_44_sig, sig);
+}
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_keypair(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_keypair_from_seed(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+/**
+ * @brief Computes ML-DSA signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign(struct lc_dilithium_44_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign_ctx(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_44_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_sign_final(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies ML-DSA signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_verify(const struct lc_dilithium_44_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_pk *pk);
+
+/**
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_verify_ctx(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen,
+ const struct lc_dilithium_44_pk *pk);
+
+/**
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_44_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_verify_update(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_verify_final(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED25519_SIG
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+#include "lc_ed25519.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_44_ed25519_sk {
+ struct lc_dilithium_44_sk sk;
+ struct lc_ed25519_sk sk_ed25519;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_44_ed25519_pk {
+ struct lc_dilithium_44_pk pk;
+ struct lc_ed25519_pk pk_ed25519;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_44_ed25519_sig {
+ struct lc_dilithium_44_sig sig;
+ struct lc_ed25519_sig sig_ed25519;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED25519 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+struct lc_dilithium_ed25519_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_SIZE sizeof(struct lc_dilithium_ed25519_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED25519 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_ED25519_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed25519_ctx *name = \
+ (struct lc_dilithium_ed25519_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED25519 context allocated with
+ * LC_DILITHIUM_ED25519_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium-ED25519 context to be zeroized
+ */
+static inline void
+lc_dilithium_44_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_44_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED25519 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED25519 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED25519 stream context
+ *
+ * @param [in] ctx Dilithium-ED25519 stream context to be zeroized and freed
+ */
+void lc_dilithium_44_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed25519_keypair(struct lc_dilithium_44_ed25519_pk *pk,
+ struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed25519_sign(struct lc_dilithium_44_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed25519_sign_ctx(struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_ed25519_sign_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_sk *sk);
+
+int lc_dilithium_44_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_44_ed25519_sign_final(
+ struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed25519_verify(const struct lc_dilithium_44_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed25519_verify_ctx(
+ const struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+
+int lc_dilithium_44_ed25519_verify_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+int lc_dilithium_44_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_44_ed25519_verify_final(
+ const struct lc_dilithium_44_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_44_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED448_SIG
+#ifdef LC_DILITHIUM_ED448_SIG
+
+#include "lc_ed448.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_44_ed448_sk {
+ struct lc_dilithium_44_sk sk;
+ struct lc_ed448_sk sk_ed448;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_44_ed448_pk {
+ struct lc_dilithium_44_pk pk;
+ struct lc_ed448_pk pk_ed448;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_44_ed448_sig {
+ struct lc_dilithium_44_sig sig;
+ struct lc_ed448_sig sig_ed448;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED448 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+struct lc_dilithium_ed448_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_SIZE sizeof(struct lc_dilithium_ed448_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED448 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_ED448_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed448_ctx *name = \
+ (struct lc_dilithium_ed448_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED448 context allocated with
+ * LC_DILITHIUM_ED448_CTX_ON_STACK lc_dilithium_ed448_alloc
+ *
+ * @param [in] ctx Dilithium-ED448 context to be zeroized
+ */
+static inline void
+lc_dilithium_44_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_44_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED448 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED448 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_44_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED448 stream context
+ *
+ * @param [in] ctx Dilithium-ED448 stream context to be zeroized and freed
+ */
+void lc_dilithium_44_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed448_keypair(struct lc_dilithium_44_ed448_pk *pk,
+ struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed448_sign(struct lc_dilithium_44_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_44_ed448_sign_ctx(struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_sk *sk);
+
+int lc_dilithium_44_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_44_ed448_sign_final(struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed448_verify(const struct lc_dilithium_44_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_44_ed448_verify_ctx(const struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_44_ed448_pk *pk);
+
+int lc_dilithium_44_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_pk *pk);
+int lc_dilithium_44_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_44_ed448_verify_final(
+ const struct lc_dilithium_44_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_44_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#endif /* __ASSEMBLER__ */
+
+/*
+ * To allow including the different lc_dilithium_*.h files, these macros need to
+ * be undefined. Only during compilation of leancrypto, these macros remain
+ * defined as this header file is not included multiple times.
+ */
+#ifndef LC_DILITHIUM_INTERNAL
+#undef LC_DILITHIUM_MODE
+#undef LC_DILITHIUM_NIST_CATEGORY
+#undef LC_DILITHIUM_SEEDBYTES
+#undef LC_DILITHIUM_CRHBYTES
+#undef LC_DILITHIUM_TRBYTES
+#undef LC_DILITHIUM_RNDBYTES
+#undef LC_DILITHIUM_N
+#undef LC_DILITHIUM_Q
+#undef LC_DILITHIUM_D
+#undef LC_DILITHIUM_ROOT_OF_UNITY
+#undef LC_DILITHIUM_LAMBDA
+#undef LC_DILITHIUM_K
+#undef LC_DILITHIUM_L
+#undef LC_DILITHIUM_ETA
+#undef LC_DILITHIUM_TAU
+#undef LC_DILITHIUM_BETA
+#undef LC_DILITHIUM_GAMMA1
+#undef LC_DILITHIUM_GAMMA2
+#undef LC_DILITHIUM_OMEGA
+#undef LC_DILITHIUM_CTILDE_BYTES
+#undef LC_DILITHIUM_POLYT1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYT0_PACKEDBYTES
+#undef LC_DILITHIUM_POLYVECH_PACKEDBYTES
+#undef LC_DILITHIUM_POLYZ_PACKEDBYTES
+#undef LC_DILITHIUM_POLYW1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYETA_PACKEDBYTES
+#undef LC_DILITHIUM_PUBLICKEYBYTES
+#undef LC_DILITHIUM_SECRETKEYBYTES
+#undef LC_DILITHIUM_CRYPTO_BYTES
+#endif /* LC_DILITHIUM_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_44_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium_65.h b/lib/freebl/leancrypto/lc_dilithium_65.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium_65.h
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_65_H
+#define LC_DILITHIUM_65_H
+
+#ifndef __ASSEMBLER__
+
+#include "ext_headers.h"
+#include "lc_hash.h"
+#include "lc_rng.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+
+#endif /* __ASSEMBLER__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+/*
+ * Dilithium Security Levels
+ * 2 -> 192 bits of security strength
+ * 3 -> 225 bits of security strength
+ * 5 -> 257 bits of security strength
+ */
+#define LC_DILITHIUM_MODE 3
+
+#define LC_DILITHIUM_SEEDBYTES 32
+#define LC_DILITHIUM_CRHBYTES 64
+#define LC_DILITHIUM_TRBYTES 64
+#define LC_DILITHIUM_RNDBYTES 32
+#define LC_DILITHIUM_N 256
+#define LC_DILITHIUM_Q 8380417
+#define LC_DILITHIUM_D 13
+#define LC_DILITHIUM_ROOT_OF_UNITY 1753
+
+#if LC_DILITHIUM_MODE == 2
+#define LC_DILITHIUM_NIST_CATEGORY 1
+#define LC_DILITHIUM_LAMBDA 128
+#define LC_DILITHIUM_K 4
+#define LC_DILITHIUM_L 4
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 39
+#define LC_DILITHIUM_BETA 78
+#define LC_DILITHIUM_GAMMA1 (1 << 17)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 88)
+#define LC_DILITHIUM_OMEGA 80
+
+#elif LC_DILITHIUM_MODE == 3
+#define LC_DILITHIUM_NIST_CATEGORY 3
+#define LC_DILITHIUM_LAMBDA 192
+#define LC_DILITHIUM_K 6
+#define LC_DILITHIUM_L 5
+#define LC_DILITHIUM_ETA 4
+#define LC_DILITHIUM_TAU 49
+#define LC_DILITHIUM_BETA 196
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 55
+
+#elif LC_DILITHIUM_MODE == 5
+#define LC_DILITHIUM_NIST_CATEGORY 5
+#define LC_DILITHIUM_LAMBDA 256
+#define LC_DILITHIUM_K 8
+#define LC_DILITHIUM_L 7
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 60
+#define LC_DILITHIUM_BETA 120
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 75
+
+#endif
+
+#define LC_DILITHIUM_CTILDE_BYTES (LC_DILITHIUM_LAMBDA * 2 / 8)
+#define LC_DILITHIUM_POLYT1_PACKEDBYTES 320
+#define LC_DILITHIUM_POLYT0_PACKEDBYTES 416
+#define LC_DILITHIUM_POLYVECH_PACKEDBYTES (LC_DILITHIUM_OMEGA + LC_DILITHIUM_K)
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 576
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 640
+#endif
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 192
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 128
+#endif
+
+#if LC_DILITHIUM_ETA == 2
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 96
+#elif LC_DILITHIUM_ETA == 4
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 128
+#endif
+
+/*
+ * Sizes of the different Dilithium buffer types.
+ *
+ * WARNING: Do not use these defines in your code. If you need the sizes of
+ * the different variable sizes, use sizeof of the different variable structs or
+ * use the different *_size functions documented below to retrieve the data size
+ * of a particular Dilithium component.
+ */
+#define LC_DILITHIUM_PUBLICKEYBYTES \
+ (LC_DILITHIUM_SEEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT1_PACKEDBYTES)
+#define LC_DILITHIUM_SECRETKEYBYTES \
+ (2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT0_PACKEDBYTES)
+
+#define LC_DILITHIUM_CRYPTO_BYTES \
+ (LC_DILITHIUM_CTILDE_BYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES + \
+ LC_DILITHIUM_POLYVECH_PACKEDBYTES)
+/// \endcond
+
+#ifndef __ASSEMBLER__
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_65_sk {
+ uint8_t sk[LC_DILITHIUM_SECRETKEYBYTES];
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_65_pk {
+ uint8_t pk[LC_DILITHIUM_PUBLICKEYBYTES];
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_65_sig {
+ uint8_t sig[LC_DILITHIUM_CRYPTO_BYTES];
+};
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+struct lc_dilithium_ctx {
+ /**
+ * @brief Hash context used internally to the library - it should not
+ * be touched by the user
+ */
+ struct lc_hash_ctx dilithium_hash_ctx;
+
+ /**
+ * @brief State memory of the hash context used internally to the
+ * library - it should not be touched by the user
+ */
+ uint8_t shake_state[LC_SHA3_STATE_SIZE_ALIGN(LC_SHA3_256_CTX_SIZE)];
+
+ /**
+ * @brief When using HashML-DSA, set the hash reference used for the
+ * hash operation. Allowed values are lc_sha256, lc_sha512, lc_sha3_256,
+ * lc_sha3_384, lc_sha3_512, lc_shake128 and lc_shake256. Note, the
+ * actual message digest operation can be performed external to
+ * leancrypto. This parameter only shall indicate the used hash
+ * operation.
+ *
+ * \note Use \p lc_dilithium_ctx_hash or
+ * \p lc_dilithium_ed25519_ctx_hash to set this value.
+ */
+ const struct lc_hash *dilithium_prehash_type;
+
+ /**
+ * @brief length of the user context (allowed range between 0 and 255
+ * bytes)
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ size_t userctxlen;
+
+ /**
+ * @brief buffer with a caller-specified context string
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ const uint8_t *userctx;
+
+ /**
+ * @brief Pointer to the AHat buffer. This can be provided by the caller
+ * or it must be NULL otherwise.
+ *
+ * \note Use \p LC_DILITHIUM_CTX_ON_STACK_AHAT to provide memory for
+ * storing AHat in the caller context and thus make the signature
+ * operation much faster starting with the 2nd use of the key (pair).
+ */
+ void *ahat;
+ unsigned short ahat_size;
+
+ /**
+ * @brief Pointer to the external mu.
+ *
+ * If set, the signature operation will use the provided mu instead of
+ * the message. In this case, the message pointer to the signature
+ * generation or verification can be NULL.
+ */
+ const uint8_t *external_mu;
+ size_t external_mu_len;
+
+ /**
+ * @brief Pointer to the randomizer
+ *
+ * This is used for the Composite signature: For the discussion of the
+ * randomizer, see https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ const uint8_t *randomizer;
+ size_t randomizerlen;
+
+ /**
+ * @brief NIST category required for composite signatures
+ *
+ * The domain separation logic depends on the selection of the right
+ * OID for the "Domain" data.
+ */
+ unsigned int nist_category;
+
+ /**
+ * @brief When set to true, only the ML-DSA.Sign_internal or
+ * ML-DSA.Verify_internal are performed (see FIPS 204 chapter 6).
+ * Otherwise the ML-DSA.Sign / ML-DSA.Verify (see FIPS chapter 5) is
+ * applied.
+ *
+ * \note Use \p lc_dilithium_ctx_internal or
+ * \p lc_dilithium_ed25519_ctx_internal to set this value.
+ *
+ * \warning Only set this value to true if you exactly know what you are
+ * doing!.
+ */
+ unsigned int ml_dsa_internal : 1;
+
+ /**
+ * @brief Was aHat already filled? This is used and set internally.
+ */
+ unsigned int ahat_expanded : 1;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+
+/*
+ * The alignment is based on largest alignment of a polyvecl typedef - this is
+ * the AVX2 definition.
+ */
+#define LC_DILITHIUM_AHAT_ALIGNMENT (32)
+
+/*
+ * Padding between struct lc_dilithium_ctx and AHat buffer to ensure AHat buffer
+ * is aligned to LC_DILITHIUM_AHAT_ALIGNMENT
+ */
+#define LC_DILITHIUM_65_AHAT_PAD \
+ (LC_DILITHIUM_AHAT_ALIGNMENT - \
+ (sizeof(struct lc_dilithium_ctx) % LC_DILITHIUM_AHAT_ALIGNMENT))
+
+/* Size of the AHat matrix for ML-DSA 87 */
+#define LC_DILITHIUM_65_AHAT_SIZE \
+ (256 * sizeof(int32_t) * LC_DILITHIUM_K * LC_DILITHIUM_L)
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_SIZE sizeof(struct lc_dilithium_ctx)
+
+#define LC_DILITHIUM_CTX_INIT_HASH(name) \
+ LC_SHAKE_256_CTX((&(name)->dilithium_hash_ctx))
+
+#define LC_DILITHIUM_SET_CTX(name) \
+ LC_DILITHIUM_CTX_INIT_HASH(name); \
+ (name)->dilithium_prehash_type = NULL; \
+ (name)->ml_dsa_internal = 0; \
+ (name)->userctxlen = 0; \
+ (name)->userctx = NULL; \
+ (name)->ahat = NULL; \
+ (name)->ahat_size = 0; \
+ (name)->external_mu = NULL; \
+ (name)->external_mu_len = 0; \
+ (name)->randomizer = NULL; \
+ (name)->randomizerlen = 0; \
+ (name)->nist_category = 0;
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context or additional
+ * parameter relevant for the signature operation.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#define LC_DILITHIUM_65_CTX_ON_STACK_AHAT(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_65_AHAT_PAD + \
+ LC_DILITHIUM_65_AHAT_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ name->ahat = (uint8_t *)name + LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_65_AHAT_PAD; \
+ name->ahat_expanded = 0; \
+ name->ahat_size = LC_DILITHIUM_65_AHAT_SIZE; \
+ _Pragma("GCC diagnostic pop")
+
+/**
+ * @brief Zeroize Dilithium context allocated with
+ * LC_DILITHIUM_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium context to be zeroized
+ */
+static inline void lc_dilithium_65_ctx_zero(struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_hash_zero(&ctx->dilithium_hash_ctx);
+ if (ctx->ahat) {
+ lc_memset_secure(ctx->ahat, 0, ctx->ahat_size);
+ ctx->ahat_expanded = 0;
+ }
+}
+
+/**
+ * @brief Allocate Dilithium stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Allocate Dilithium stream context on heap including additional
+ * parameter relevant for the signature operation.
+ *
+ * \note See \p LC_DILITHIUM_65_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium stream context
+ *
+ * @param [in] ctx Dilithium stream context to be zeroized and freed
+ */
+void lc_dilithium_65_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @brief Return the size of the Dilithium secret key.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_65_sk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_65_sk, sk);
+}
+
+/**
+ * @brief Return the size of the Dilithium public key.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_65_pk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_65_pk, pk);
+}
+
+/**
+ * @brief Return the size of the Dilithium signature.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_65_sig_size(void)
+{
+ return lc_member_size(struct lc_dilithium_65_sig, sig);
+}
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_keypair(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_keypair_from_seed(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+/**
+ * @brief Computes ML-DSA signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign(struct lc_dilithium_65_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign_ctx(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_65_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_sign_final(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies ML-DSA signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_verify(const struct lc_dilithium_65_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_pk *pk);
+
+/**
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_verify_ctx(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen,
+ const struct lc_dilithium_65_pk *pk);
+
+/**
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_65_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_verify_update(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_verify_final(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED25519_SIG
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+#include "lc_ed25519.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_65_ed25519_sk {
+ struct lc_dilithium_65_sk sk;
+ struct lc_ed25519_sk sk_ed25519;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_65_ed25519_pk {
+ struct lc_dilithium_65_pk pk;
+ struct lc_ed25519_pk pk_ed25519;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_65_ed25519_sig {
+ struct lc_dilithium_65_sig sig;
+ struct lc_ed25519_sig sig_ed25519;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED25519 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+struct lc_dilithium_ed25519_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_SIZE sizeof(struct lc_dilithium_ed25519_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED25519 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_ED25519_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed25519_ctx *name = \
+ (struct lc_dilithium_ed25519_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED25519 context allocated with
+ * LC_DILITHIUM_ED25519_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium-ED25519 context to be zeroized
+ */
+static inline void
+lc_dilithium_65_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_65_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED25519 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED25519 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED25519 stream context
+ *
+ * @param [in] ctx Dilithium-ED25519 stream context to be zeroized and freed
+ */
+void lc_dilithium_65_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed25519_keypair(struct lc_dilithium_65_ed25519_pk *pk,
+ struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed25519_sign(struct lc_dilithium_65_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed25519_sign_ctx(struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_ed25519_sign_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_sk *sk);
+
+int lc_dilithium_65_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_65_ed25519_sign_final(
+ struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed25519_verify(const struct lc_dilithium_65_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed25519_verify_ctx(
+ const struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+
+int lc_dilithium_65_ed25519_verify_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+int lc_dilithium_65_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_65_ed25519_verify_final(
+ const struct lc_dilithium_65_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_65_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED448_SIG
+#ifdef LC_DILITHIUM_ED448_SIG
+
+#include "lc_ed448.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_65_ed448_sk {
+ struct lc_dilithium_65_sk sk;
+ struct lc_ed448_sk sk_ed448;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_65_ed448_pk {
+ struct lc_dilithium_65_pk pk;
+ struct lc_ed448_pk pk_ed448;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_65_ed448_sig {
+ struct lc_dilithium_65_sig sig;
+ struct lc_ed448_sig sig_ed448;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED448 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+struct lc_dilithium_ed448_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_SIZE sizeof(struct lc_dilithium_ed448_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED448 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_ED448_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed448_ctx *name = \
+ (struct lc_dilithium_ed448_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED448 context allocated with
+ * LC_DILITHIUM_ED448_CTX_ON_STACK lc_dilithium_ed448_alloc
+ *
+ * @param [in] ctx Dilithium-ED448 context to be zeroized
+ */
+static inline void
+lc_dilithium_65_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_65_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED448 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED448 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_65_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED448 stream context
+ *
+ * @param [in] ctx Dilithium-ED448 stream context to be zeroized and freed
+ */
+void lc_dilithium_65_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed448_keypair(struct lc_dilithium_65_ed448_pk *pk,
+ struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed448_sign(struct lc_dilithium_65_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_65_ed448_sign_ctx(struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_sk *sk);
+
+int lc_dilithium_65_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_65_ed448_sign_final(struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed448_verify(const struct lc_dilithium_65_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_65_ed448_verify_ctx(const struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_65_ed448_pk *pk);
+
+int lc_dilithium_65_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_pk *pk);
+int lc_dilithium_65_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_65_ed448_verify_final(
+ const struct lc_dilithium_65_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_65_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#endif /* __ASSEMBLER__ */
+
+/*
+ * To allow including the different lc_dilithium_*.h files, these macros need to
+ * be undefined. Only during compilation of leancrypto, these macros remain
+ * defined as this header file is not included multiple times.
+ */
+#ifndef LC_DILITHIUM_INTERNAL
+#undef LC_DILITHIUM_MODE
+#undef LC_DILITHIUM_NIST_CATEGORY
+#undef LC_DILITHIUM_SEEDBYTES
+#undef LC_DILITHIUM_CRHBYTES
+#undef LC_DILITHIUM_TRBYTES
+#undef LC_DILITHIUM_RNDBYTES
+#undef LC_DILITHIUM_N
+#undef LC_DILITHIUM_Q
+#undef LC_DILITHIUM_D
+#undef LC_DILITHIUM_ROOT_OF_UNITY
+#undef LC_DILITHIUM_LAMBDA
+#undef LC_DILITHIUM_K
+#undef LC_DILITHIUM_L
+#undef LC_DILITHIUM_ETA
+#undef LC_DILITHIUM_TAU
+#undef LC_DILITHIUM_BETA
+#undef LC_DILITHIUM_GAMMA1
+#undef LC_DILITHIUM_GAMMA2
+#undef LC_DILITHIUM_OMEGA
+#undef LC_DILITHIUM_CTILDE_BYTES
+#undef LC_DILITHIUM_POLYT1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYT0_PACKEDBYTES
+#undef LC_DILITHIUM_POLYVECH_PACKEDBYTES
+#undef LC_DILITHIUM_POLYZ_PACKEDBYTES
+#undef LC_DILITHIUM_POLYW1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYETA_PACKEDBYTES
+#undef LC_DILITHIUM_PUBLICKEYBYTES
+#undef LC_DILITHIUM_SECRETKEYBYTES
+#undef LC_DILITHIUM_CRYPTO_BYTES
+#endif /* LC_DILITHIUM_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_65_H */
diff --git a/lib/freebl/leancrypto/lc_dilithium_87.h b/lib/freebl/leancrypto/lc_dilithium_87.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_dilithium_87.h
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#ifndef LC_DILITHIUM_87_H
+#define LC_DILITHIUM_87_H
+
+#ifndef __ASSEMBLER__
+
+#include "ext_headers.h"
+#include "lc_hash.h"
+#include "lc_rng.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+
+#endif /* __ASSEMBLER__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+/*
+ * Dilithium Security Levels
+ * 2 -> 192 bits of security strength
+ * 3 -> 225 bits of security strength
+ * 5 -> 257 bits of security strength
+ */
+#define LC_DILITHIUM_MODE 5
+
+#define LC_DILITHIUM_SEEDBYTES 32
+#define LC_DILITHIUM_CRHBYTES 64
+#define LC_DILITHIUM_TRBYTES 64
+#define LC_DILITHIUM_RNDBYTES 32
+#define LC_DILITHIUM_N 256
+#define LC_DILITHIUM_Q 8380417
+#define LC_DILITHIUM_D 13
+#define LC_DILITHIUM_ROOT_OF_UNITY 1753
+
+#if LC_DILITHIUM_MODE == 2
+#define LC_DILITHIUM_NIST_CATEGORY 1
+#define LC_DILITHIUM_LAMBDA 128
+#define LC_DILITHIUM_K 4
+#define LC_DILITHIUM_L 4
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 39
+#define LC_DILITHIUM_BETA 78
+#define LC_DILITHIUM_GAMMA1 (1 << 17)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 88)
+#define LC_DILITHIUM_OMEGA 80
+
+#elif LC_DILITHIUM_MODE == 3
+#define LC_DILITHIUM_NIST_CATEGORY 3
+#define LC_DILITHIUM_LAMBDA 192
+#define LC_DILITHIUM_K 6
+#define LC_DILITHIUM_L 5
+#define LC_DILITHIUM_ETA 4
+#define LC_DILITHIUM_TAU 49
+#define LC_DILITHIUM_BETA 196
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 55
+
+#elif LC_DILITHIUM_MODE == 5
+#define LC_DILITHIUM_NIST_CATEGORY 5
+#define LC_DILITHIUM_LAMBDA 256
+#define LC_DILITHIUM_K 8
+#define LC_DILITHIUM_L 7
+#define LC_DILITHIUM_ETA 2
+#define LC_DILITHIUM_TAU 60
+#define LC_DILITHIUM_BETA 120
+#define LC_DILITHIUM_GAMMA1 (1 << 19)
+#define LC_DILITHIUM_GAMMA2 ((LC_DILITHIUM_Q - 1) / 32)
+#define LC_DILITHIUM_OMEGA 75
+
+#endif
+
+#define LC_DILITHIUM_CTILDE_BYTES (LC_DILITHIUM_LAMBDA * 2 / 8)
+#define LC_DILITHIUM_POLYT1_PACKEDBYTES 320
+#define LC_DILITHIUM_POLYT0_PACKEDBYTES 416
+#define LC_DILITHIUM_POLYVECH_PACKEDBYTES (LC_DILITHIUM_OMEGA + LC_DILITHIUM_K)
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 576
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+#define LC_DILITHIUM_POLYZ_PACKEDBYTES 640
+#endif
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 192
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+#define LC_DILITHIUM_POLYW1_PACKEDBYTES 128
+#endif
+
+#if LC_DILITHIUM_ETA == 2
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 96
+#elif LC_DILITHIUM_ETA == 4
+#define LC_DILITHIUM_POLYETA_PACKEDBYTES 128
+#endif
+
+/*
+ * Sizes of the different Dilithium buffer types.
+ *
+ * WARNING: Do not use these defines in your code. If you need the sizes of
+ * the different variable sizes, use sizeof of the different variable structs or
+ * use the different *_size functions documented below to retrieve the data size
+ * of a particular Dilithium component.
+ */
+#define LC_DILITHIUM_PUBLICKEYBYTES \
+ (LC_DILITHIUM_SEEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT1_PACKEDBYTES)
+#define LC_DILITHIUM_SECRETKEYBYTES \
+ (2 * LC_DILITHIUM_SEEDBYTES + LC_DILITHIUM_TRBYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYETA_PACKEDBYTES + \
+ LC_DILITHIUM_K * LC_DILITHIUM_POLYT0_PACKEDBYTES)
+
+#define LC_DILITHIUM_CRYPTO_BYTES \
+ (LC_DILITHIUM_CTILDE_BYTES + \
+ LC_DILITHIUM_L * LC_DILITHIUM_POLYZ_PACKEDBYTES + \
+ LC_DILITHIUM_POLYVECH_PACKEDBYTES)
+/// \endcond
+
+#ifndef __ASSEMBLER__
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_87_sk {
+ uint8_t sk[LC_DILITHIUM_SECRETKEYBYTES];
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_87_pk {
+ uint8_t pk[LC_DILITHIUM_PUBLICKEYBYTES];
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_87_sig {
+ uint8_t sig[LC_DILITHIUM_CRYPTO_BYTES];
+};
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+struct lc_dilithium_ctx {
+ /**
+ * @brief Hash context used internally to the library - it should not
+ * be touched by the user
+ */
+ struct lc_hash_ctx dilithium_hash_ctx;
+
+ /**
+ * @brief State memory of the hash context used internally to the
+ * library - it should not be touched by the user
+ */
+ uint8_t shake_state[LC_SHA3_STATE_SIZE_ALIGN(LC_SHA3_256_CTX_SIZE)];
+
+ /**
+ * @brief When using HashML-DSA, set the hash reference used for the
+ * hash operation. Allowed values are lc_sha256, lc_sha512, lc_sha3_256,
+ * lc_sha3_384, lc_sha3_512, lc_shake128 and lc_shake256. Note, the
+ * actual message digest operation can be performed external to
+ * leancrypto. This parameter only shall indicate the used hash
+ * operation.
+ *
+ * \note Use \p lc_dilithium_ctx_hash or
+ * \p lc_dilithium_ed25519_ctx_hash to set this value.
+ */
+ const struct lc_hash *dilithium_prehash_type;
+
+ /**
+ * @brief length of the user context (allowed range between 0 and 255
+ * bytes)
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ size_t userctxlen;
+
+ /**
+ * @brief buffer with a caller-specified context string
+ *
+ * \note Use \p lc_dilithium_ctx_userctx or
+ * \p lc_dilithium_ed25519_ctx_userctx to set this value.
+ */
+ const uint8_t *userctx;
+
+ /**
+ * @brief Pointer to the AHat buffer. This can be provided by the caller
+ * or it must be NULL otherwise.
+ *
+ * \note Use \p LC_DILITHIUM_CTX_ON_STACK_AHAT to provide memory for
+ * storing AHat in the caller context and thus make the signature
+ * operation much faster starting with the 2nd use of the key (pair).
+ */
+ void *ahat;
+ unsigned short ahat_size;
+
+ /**
+ * @brief Pointer to the external mu.
+ *
+ * If set, the signature operation will use the provided mu instead of
+ * the message. In this case, the message pointer to the signature
+ * generation or verification can be NULL.
+ */
+ const uint8_t *external_mu;
+ size_t external_mu_len;
+
+ /**
+ * @brief Pointer to the randomizer
+ *
+ * This is used for the Composite signature: For the discussion of the
+ * randomizer, see https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ const uint8_t *randomizer;
+ size_t randomizerlen;
+
+ /**
+ * @brief NIST category required for composite signatures
+ *
+ * The domain separation logic depends on the selection of the right
+ * OID for the "Domain" data.
+ */
+ unsigned int nist_category;
+
+ /**
+ * @brief When set to true, only the ML-DSA.Sign_internal or
+ * ML-DSA.Verify_internal are performed (see FIPS 204 chapter 6).
+ * Otherwise the ML-DSA.Sign / ML-DSA.Verify (see FIPS chapter 5) is
+ * applied.
+ *
+ * \note Use \p lc_dilithium_ctx_internal or
+ * \p lc_dilithium_ed25519_ctx_internal to set this value.
+ *
+ * \warning Only set this value to true if you exactly know what you are
+ * doing!.
+ */
+ unsigned int ml_dsa_internal : 1;
+
+ /**
+ * @brief Was aHat already filled? This is used and set internally.
+ */
+ unsigned int ahat_expanded : 1;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+
+/*
+ * The alignment is based on largest alignment of a polyvecl typedef - this is
+ * the AVX2 definition.
+ */
+#define LC_DILITHIUM_AHAT_ALIGNMENT (32)
+
+/*
+ * Padding between struct lc_dilithium_ctx and AHat buffer to ensure AHat buffer
+ * is aligned to LC_DILITHIUM_AHAT_ALIGNMENT
+ */
+#define LC_DILITHIUM_87_AHAT_PAD \
+ (LC_DILITHIUM_AHAT_ALIGNMENT - \
+ (sizeof(struct lc_dilithium_ctx) % LC_DILITHIUM_AHAT_ALIGNMENT))
+
+/* Size of the AHat matrix for ML-DSA 87 */
+#define LC_DILITHIUM_87_AHAT_SIZE \
+ (256 * sizeof(int32_t) * LC_DILITHIUM_K * LC_DILITHIUM_L)
+
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_SIZE sizeof(struct lc_dilithium_ctx)
+
+#define LC_DILITHIUM_CTX_INIT_HASH(name) \
+ LC_SHAKE_256_CTX((&(name)->dilithium_hash_ctx))
+
+#define LC_DILITHIUM_SET_CTX(name) \
+ LC_DILITHIUM_CTX_INIT_HASH(name); \
+ (name)->dilithium_prehash_type = NULL; \
+ (name)->ml_dsa_internal = 0; \
+ (name)->userctxlen = 0; \
+ (name)->userctx = NULL; \
+ (name)->ahat = NULL; \
+ (name)->ahat_size = 0; \
+ (name)->external_mu = NULL; \
+ (name)->external_mu_len = 0; \
+ (name)->randomizer = NULL; \
+ (name)->randomizerlen = 0; \
+ (name)->nist_category = 0;
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context or additional
+ * parameter relevant for the signature operation.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_CTX_ON_STACK
+#define LC_DILITHIUM_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Allocate stack memory for the Dilithium stream context and additional
+ * parameter relevant for the signature operation.
+ *
+ * In addition, the memory buffer returned by this allocation contains the space
+ * for an expanded representation of the public key which is required in both,
+ * signature generation and verification. When using this memory, the first
+ * signature operation expands the key and any subsequent operation using this
+ * context will re-use the expanded key which improves performance of the
+ * signature operation significantly.
+ *
+ * As the same expanded structure is used for signature generation and
+ * verification and the structure can be expanded by either operation, it
+ * is perfectly legal to use one context for both operations as the expanded
+ * key can (a) be generated from either the public or the secret key and (b)
+ * it applies to both operations and (c) is identical irrespective it was
+ * generated from the public or secret key.
+ *
+ * \note: ML-DSA AVX2 signature operation uses a completely different
+ * algorithm which does not use a pre-pcomputed expanded key. Thus, if you know
+ * you have AVX2 support, you *may* not need this larger buffer and you *can*
+ * use \p LC_DILITHIUM_CTX_ON_STACK instead.
+ *
+ * \note: The expanded representation only uses public key data. Even when
+ * deriving the expanded representation from a secret key, this data is only
+ * obtained from a part that is considered public. Thus, this memory does not
+ * require special protections. See FIPS 204 section 3.6.3 on the properties
+ * and handling requirements of the  matrix. Further, see the FIPS 204
+ * ML-DSA.Sign_internal and ML-DSA.Verify_internal algorithm specification on
+ * how this  matrix is generated and that the input to the generation is public
+ * data.
+ *
+ * \warning: One instance of the expanded key representation can only ever apply
+ * to one given key (pair). If you want to reuse the context with multiple keys,
+ * you MUST invalidate the potentially present expanded key representation. Such
+ * invalidation is invoked with the method \p lc_dilithium_ctx_drop_ahat. Only
+ * after this invalidation you can use the context with a different key.
+ *
+ * @param [in] name Name of the stack variable
+ */
+#define LC_DILITHIUM_87_CTX_ON_STACK_AHAT(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_87_AHAT_PAD + \
+ LC_DILITHIUM_87_AHAT_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ctx *name = \
+ (struct lc_dilithium_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(name); \
+ name->ahat = (uint8_t *)name + LC_DILITHIUM_CTX_SIZE + \
+ LC_DILITHIUM_87_AHAT_PAD; \
+ name->ahat_expanded = 0; \
+ name->ahat_size = LC_DILITHIUM_87_AHAT_SIZE; \
+ _Pragma("GCC diagnostic pop")
+
+/**
+ * @brief Zeroize Dilithium context allocated with
+ * LC_DILITHIUM_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium context to be zeroized
+ */
+static inline void lc_dilithium_87_ctx_zero(struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_hash_zero(&ctx->dilithium_hash_ctx);
+ if (ctx->ahat) {
+ lc_memset_secure(ctx->ahat, 0, ctx->ahat_size);
+ ctx->ahat_expanded = 0;
+ }
+}
+
+/**
+ * @brief Allocate Dilithium stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ctx_alloc(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Allocate Dilithium stream context on heap including additional
+ * parameter relevant for the signature operation.
+ *
+ * \note See \p LC_DILITHIUM_87_CTX_ON_STACK_AHAT for details.
+ *
+ * @param [out] ctx Allocated Dilithium stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ctx_alloc_ahat(struct lc_dilithium_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium stream context
+ *
+ * @param [in] ctx Dilithium stream context to be zeroized and freed
+ */
+void lc_dilithium_87_ctx_zero_free(struct lc_dilithium_ctx *ctx);
+
+/**
+ * @brief Return the size of the Dilithium secret key.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_87_sk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_87_sk, sk);
+}
+
+/**
+ * @brief Return the size of the Dilithium public key.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_87_pk_size(void)
+{
+ return lc_member_size(struct lc_dilithium_87_pk, pk);
+}
+
+/**
+ * @brief Return the size of the Dilithium signature.
+ */
+LC_PURE
+static inline unsigned int lc_dilithium_87_sig_size(void)
+{
+ return lc_member_size(struct lc_dilithium_87_sig, sig);
+}
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_keypair(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Generates Dilithium public and private key from a given seed.
+ *
+ * The idea of the function is the allowance of FIPS 204 to maintain the seed
+ * used to generate a key pair in lieu of maintaining a private key or the
+ * key pair (which used much more memory). The seed must be treated equally
+ * sensitive as a private key.
+ *
+ * The seed is generated by simply obtaining 32 bytes from a properly seeded
+ * DRNG, i.e. the same way as a symmetric key would be generated.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] seed buffer with the seed data which must be exactly 32 bytes
+ * in size
+ * @param [in] seedlen length of the seed buffer
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_keypair_from_seed(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+/**
+ * @brief Computes ML-DSA signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign(struct lc_dilithium_87_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign_ctx(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Initializes a signature operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_update and lc_dilithium_sign_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] sk pointer to bit-packed secret key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_87_sign_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_sign_init and lc_dilithium_sign_final.
+ *
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign_update(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+
+/**
+ * @brief Computes signature
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_sign_final(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies ML-DSA signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_verify(const struct lc_dilithium_87_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_pk *pk);
+
+/**
+ * @brief Verifies signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_verify_ctx(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen,
+ const struct lc_dilithium_87_pk *pk);
+
+/**
+ * @brief Initializes a signature verification operation
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_update and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to an allocated Dilithium context
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 (success) or < 0 on error; -EOPNOTSUPP is returned if a different
+ * hash than lc_shake256 is used.
+ */
+int lc_dilithium_87_verify_init(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+
+/**
+ * @brief Add more data to an already initialized signature state
+ *
+ * This call is intended to support messages that are located in non-contiguous
+ * places and even becomes available at different times. This call is to be
+ * used together with the lc_dilithium_verify_init and
+ * lc_dilithium_verify_final.
+ *
+ * @param [in,out] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_verify_update(struct lc_dilithium_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+/**
+ * @brief Verifies signature
+ *
+ * @param [in] sig pointer to output signature
+ * @param [in] ctx pointer to Dilithium context that was initialized with
+ * lc_dilithium_sign_init and filled with
+ * lc_dilithium_sign_update
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_verify_final(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED25519_SIG
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+#include "lc_ed25519.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_87_ed25519_sk {
+ struct lc_dilithium_87_sk sk;
+ struct lc_ed25519_sk sk_ed25519;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_87_ed25519_pk {
+ struct lc_dilithium_87_pk pk;
+ struct lc_ed25519_pk pk_ed25519;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_87_ed25519_sig {
+ struct lc_dilithium_87_sig sig;
+ struct lc_ed25519_sig sig_ed25519;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED25519 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+struct lc_dilithium_ed25519_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_SIZE sizeof(struct lc_dilithium_ed25519_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED25519 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED25519_CTX_ON_STACK
+#define LC_DILITHIUM_ED25519_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, \
+ LC_DILITHIUM_ED25519_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed25519_ctx *name = \
+ (struct lc_dilithium_ed25519_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED25519 context allocated with
+ * LC_DILITHIUM_ED25519_CTX_ON_STACK lc_dilithium_ed25519_alloc
+ *
+ * @param [in] ctx Dilithium-ED25519 context to be zeroized
+ */
+static inline void
+lc_dilithium_87_ed25519_ctx_zero(struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_87_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED25519 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED25519 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ed25519_ctx_alloc(struct lc_dilithium_ed25519_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED25519 stream context
+ *
+ * @param [in] ctx Dilithium-ED25519 stream context to be zeroized and freed
+ */
+void lc_dilithium_87_ed25519_ctx_zero_free(struct lc_dilithium_ed25519_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed25519_keypair(struct lc_dilithium_87_ed25519_pk *pk,
+ struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed25519_sign(struct lc_dilithium_87_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed25519_sign_ctx(struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_ed25519_sign_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_sk *sk);
+
+int lc_dilithium_87_ed25519_sign_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_87_ed25519_sign_final(
+ struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed25519_verify(const struct lc_dilithium_87_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed25519_verify_ctx(
+ const struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+
+int lc_dilithium_87_ed25519_verify_init(
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+int lc_dilithium_87_ed25519_verify_update(struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_87_ed25519_verify_final(
+ const struct lc_dilithium_87_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_87_ed25519_pk *pk);
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+/* Macro set during leancrypto compile time for target platform */
+#undef LC_DILITHIUM_ED448_SIG
+#ifdef LC_DILITHIUM_ED448_SIG
+
+#include "lc_ed448.h"
+
+/**
+ * @brief Dilithium secret key
+ */
+struct lc_dilithium_87_ed448_sk {
+ struct lc_dilithium_87_sk sk;
+ struct lc_ed448_sk sk_ed448;
+};
+
+/**
+ * @brief Dilithium public key
+ */
+struct lc_dilithium_87_ed448_pk {
+ struct lc_dilithium_87_pk pk;
+ struct lc_ed448_pk pk_ed448;
+};
+
+/**
+ * @brief Dilithium signature
+ */
+struct lc_dilithium_87_ed448_sig {
+ struct lc_dilithium_87_sig sig;
+ struct lc_ed448_sig sig_ed448;
+};
+
+/**
+ * @brief Dilithium stream context
+ *
+ * This structure is used for the init/update/final operation of the
+ * Dilithium-ED448 hybrid.
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+struct lc_dilithium_ed448_ctx {
+ struct lc_dilithium_ctx dilithium_ctx;
+};
+#endif
+
+/// \cond DO_NOT_DOCUMENT
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_SIZE sizeof(struct lc_dilithium_ed448_ctx)
+#endif
+/// \endcond
+
+/**
+ * @brief Allocate stack memory for the Dilithium-ED448 stream context
+ *
+ * @param [in] name Name of the stack variable
+ */
+#ifndef LC_DILITHIUM_ED448_CTX_ON_STACK
+#define LC_DILITHIUM_ED448_CTX_ON_STACK(name) \
+ _Pragma("GCC diagnostic push") _Pragma( \
+ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \
+ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_DILITHIUM_ED448_CTX_SIZE, \
+ LC_HASH_COMMON_ALIGNMENT); \
+ struct lc_dilithium_ed448_ctx *name = \
+ (struct lc_dilithium_ed448_ctx *)name##_ctx_buf; \
+ LC_DILITHIUM_SET_CTX(&(name)->dilithium_ctx); \
+ _Pragma("GCC diagnostic pop")
+#endif
+
+/**
+ * @brief Zeroize Dilithium-ED448 context allocated with
+ * LC_DILITHIUM_ED448_CTX_ON_STACK lc_dilithium_ed448_alloc
+ *
+ * @param [in] ctx Dilithium-ED448 context to be zeroized
+ */
+static inline void
+lc_dilithium_87_ed448_ctx_zero(struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ lc_dilithium_87_ctx_zero(&ctx->dilithium_ctx);
+}
+
+/**
+ * @brief Allocate Dilithium-ED448 stream context on heap
+ *
+ * @param [out] ctx Allocated Dilithium-ED448 stream context
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int lc_dilithium_87_ed448_ctx_alloc(struct lc_dilithium_ed448_ctx **ctx);
+
+/**
+ * @brief Zeroize and free Dilithium-ED448 stream context
+ *
+ * @param [in] ctx Dilithium-ED448 stream context to be zeroized and freed
+ */
+void lc_dilithium_87_ed448_ctx_zero_free(struct lc_dilithium_ed448_ctx *ctx);
+
+/**
+ * @brief Generates Dilithium public and private key.
+ *
+ * @param [out] pk pointer to allocated output public key
+ * @param [out] sk pointer to allocated output private key
+ * @param [in] rng_ctx pointer to seeded random number generator context
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed448_keypair(struct lc_dilithium_87_ed448_pk *pk,
+ struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature in one shot
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed448_sign(struct lc_dilithium_87_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Computes signature with Dilithium context in one shot
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [out] sig pointer to output signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message to be signed
+ * @param [in] mlen length of message
+ * @param [in] sk pointer to bit-packed secret key
+ * @param [in] rng_ctx pointer to seeded random number generator context - when
+ * pointer is non-NULL, perform a randomized signing.
+ * Otherwise use deterministic signing.
+ *
+ * @return 0 (success) or < 0 on error
+ */
+int lc_dilithium_87_ed448_sign_ctx(struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_ed448_sign_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_sk *sk);
+
+int lc_dilithium_87_ed448_sign_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+
+int lc_dilithium_87_ed448_sign_final(struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+/**
+ * @brief Verifies signature in one shot
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed448_verify(const struct lc_dilithium_87_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_pk *pk);
+
+/**
+ * @brief Verifies signature in one shot with Dilithium context
+ *
+ * This API allows the caller to provide an arbitrary context buffer which
+ * is hashed together with the message to form the message digest to be signed.
+ *
+ * @param [in] sig pointer to input signature
+ * @param [in] ctx reference to the allocated Dilithium context handle
+ * @param [in] m pointer to message
+ * @param [in] mlen length of message
+ * @param [in] pk pointer to bit-packed public key
+ *
+ * @return 0 if signature could be verified correctly and -EBADMSG when
+ * signature cannot be verified, < 0 on other errors
+ */
+int lc_dilithium_87_ed448_verify_ctx(const struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_87_ed448_pk *pk);
+
+int lc_dilithium_87_ed448_verify_init(struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_pk *pk);
+int lc_dilithium_87_ed448_verify_update(struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *m, size_t mlen);
+int lc_dilithium_87_ed448_verify_final(
+ const struct lc_dilithium_87_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_87_ed448_pk *pk);
+
+#endif /* LC_DILITHIUM_ED448_SIG */
+
+#endif /* __ASSEMBLER__ */
+
+/*
+ * To allow including the different lc_dilithium_*.h files, these macros need to
+ * be undefined. Only during compilation of leancrypto, these macros remain
+ * defined as this header file is not included multiple times.
+ */
+#ifndef LC_DILITHIUM_INTERNAL
+#undef LC_DILITHIUM_MODE
+#undef LC_DILITHIUM_NIST_CATEGORY
+#undef LC_DILITHIUM_SEEDBYTES
+#undef LC_DILITHIUM_CRHBYTES
+#undef LC_DILITHIUM_TRBYTES
+#undef LC_DILITHIUM_RNDBYTES
+#undef LC_DILITHIUM_N
+#undef LC_DILITHIUM_Q
+#undef LC_DILITHIUM_D
+#undef LC_DILITHIUM_ROOT_OF_UNITY
+#undef LC_DILITHIUM_LAMBDA
+#undef LC_DILITHIUM_K
+#undef LC_DILITHIUM_L
+#undef LC_DILITHIUM_ETA
+#undef LC_DILITHIUM_TAU
+#undef LC_DILITHIUM_BETA
+#undef LC_DILITHIUM_GAMMA1
+#undef LC_DILITHIUM_GAMMA2
+#undef LC_DILITHIUM_OMEGA
+#undef LC_DILITHIUM_CTILDE_BYTES
+#undef LC_DILITHIUM_POLYT1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYT0_PACKEDBYTES
+#undef LC_DILITHIUM_POLYVECH_PACKEDBYTES
+#undef LC_DILITHIUM_POLYZ_PACKEDBYTES
+#undef LC_DILITHIUM_POLYW1_PACKEDBYTES
+#undef LC_DILITHIUM_POLYETA_PACKEDBYTES
+#undef LC_DILITHIUM_PUBLICKEYBYTES
+#undef LC_DILITHIUM_SECRETKEYBYTES
+#undef LC_DILITHIUM_CRYPTO_BYTES
+#endif /* LC_DILITHIUM_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_DILITHIUM_87_H */
diff --git a/lib/freebl/leancrypto/lc_hash.h b/lib/freebl/leancrypto/lc_hash.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_hash.h
@@ -0,0 +1,17 @@
+#include "hasht.h"
+#include "blapit.h"
+#include "lc_memset_secure.h" /* sigh the original included it,
+ * so some files expect it to be there already */
+#include "lc_memory_support.h"
+/* sigh Stephen doesn seem to believe in typedef, so
+ * just stuff our context pointer into a struct */
+#define LC_SHA3_256_CTX_SIZE (SHA3_256_BLOCK_LENGTH)
+#define LC_SHA3_STATE_SIZE_ALIGN(x) (x)
+
+#define LC_SHA3_512_SIZE_DIGEST SHA3_512_LENGTH
+
+#ifndef LC_HASH_COMMON_ALIGNMENT
+#define LC_HASH_COMMON_ALIGNMENT 64
+#endif
+
+
diff --git a/lib/freebl/leancrypto/lc_init.h b/lib/freebl/leancrypto/lc_init.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_init.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_INIT_H
+#define LC_INIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialization of leancrypto
+ *
+ * This function invokes all necessary initialization functions required at
+ * the loading time of leancrypto. However, this function is only needed for
+ * environments without a constructor functionality such as the Linux kernel
+ * or the EFI environment.
+ *
+ * For regular environments such as Linux, this function is not required to be
+ * called. But it does not hurt to be called.
+ *
+ * \note If this function is called, no other leancrypto service must be offered
+ * as this function may alter the global leancrypto state.
+ *
+ * @param [in] flags currently unused
+ *
+ * @return 0 on success, < 0 on error
+ */
+int lc_init(unsigned int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_INIT_H */
diff --git a/lib/freebl/leancrypto/lc_memcmp_secure.h b/lib/freebl/leancrypto/lc_memcmp_secure.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memcmp_secure.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef MEMCMP_SECURE_H
+#define MEMCMP_SECURE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Constant-time memcmp implementation
+ *
+ * @param s1 First string
+ * @param s1n Size of first string
+ * @param s2 Second string
+ * @param s2n Size of second string
+ *
+ * @return 0 on string match, != 0 when strings do not match
+ */
+int lc_memcmp_secure(const void *s1, size_t s1n, const void *s2, size_t s2n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMCMP_SECURE_H */
diff --git a/lib/freebl/leancrypto/lc_memcpy_secure.h b/lib/freebl/leancrypto/lc_memcpy_secure.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memcpy_secure.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef MEMCPY_SECURE_H
+#define MEMCPY_SECURE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Leancrypto-internal memcpy
+ *
+ * @param d Destination buffer
+ * @param dn Size of destination buffer
+ * @param s Source buffer
+ * @param sn Size of source buffer
+ */
+void *lc_memcpy_secure(void *d, size_t dn, const void *s, size_t sn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MEMCPY_SECURE_H */
diff --git a/lib/freebl/leancrypto/lc_memory_support.h b/lib/freebl/leancrypto/lc_memory_support.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memory_support.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_MEMORY_SUPPORT_H
+#define LC_MEMORY_SUPPORT_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Default memory alignment */
+#define LC_MEM_COMMON_ALIGNMENT (8)
+
+/**
+ * @brief Allocate aligned stack memory
+ *
+ * The variable can be casted to any structure
+ *
+ * @param name variable name
+ * @param size size of the buffer
+ * @param alignment alignment of the buffer
+ */
+#define LC_ALIGNED_BUFFER(name, size, alignment) \
+ uint64_t name[(size + sizeof(uint64_t) - 1) / sizeof(uint64_t)] \
+ __attribute__((aligned(alignment)))
+
+/* Helpers to align a pointer */
+#define LC_ALIGNMENT_MASK(alignment) (alignment - 1)
+#define LC_ALIGN_APPLY(x, mask) (((x) + (mask)) & ~(mask))
+#define LC_ALIGN(x, a) LC_ALIGN_APPLY((x), (uintptr_t)(a))
+
+/**
+ * @brief Align pointer interpreted as 64 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_64(p, a) ((uint64_t *)LC_ALIGN((uintptr_t)(p), (a)));
+
+/**
+ * @brief Align pointer interpreted as 32 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_32(p, a) ((uint32_t *)LC_ALIGN((uintptr_t)(p), (a)))
+
+/**
+ * @brief Align pointer interpreted as 16 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_16(p, a) ((uint16_t *)LC_ALIGN((uintptr_t)(p), (a)))
+
+/**
+ * @brief Align pointer interpreted as 8 bit variable
+ *
+ * @param p pointer
+ * @param a alignment
+ */
+#define LC_ALIGN_PTR_8(p, a) ((uint8_t *)LC_ALIGN((uintptr_t)(p), (a)))
+
+/**
+ * Proper memory alignment value when using XOR
+ */
+#define LC_XOR_MIN_ALIGNMENT(min, requested) \
+ ((min < requested) ? (requested) : (min))
+
+/* Macros set during leancrypto compile time for target platform */
+#define LC_DEF_HOST_X86_64
+#undef LC_DEF_HOST_ARM32_NEON
+#undef LC_DEF_HOST_AARCH64
+
+#ifdef LC_DEF_HOST_X86_64
+
+/*
+ * The load of data into __m256i does not require alignment, the store
+ * requires 64 bit alignment by using _mm_storel_pd / _mm_storeh_pd.
+ */
+#define LC_XOR_AVX2_ALIGNMENT (sizeof(uint64_t))
+#define LC_XOR_ALIGNMENT(min) LC_XOR_MIN_ALIGNMENT(min, LC_XOR_AVX2_ALIGNMENT)
+
+#elif (defined(LC_DEF_HOST_ARM32_NEON) || defined(LC_DEF_HOST_AARCH64)) && \
+ !defined(LINUX_KERNEL)
+
+/*
+ * The load of data into uint64x2_t requires 64 bit alignment, the store
+ * requires 64 bit alignment.
+ */
+#define LC_XOR_NEON_ALIGNMENT (sizeof(uint64_t))
+#define LC_XOR_ALIGNMENT(min) LC_XOR_MIN_ALIGNMENT(min, LC_XOR_NEON_ALIGNMENT)
+
+#else
+
+#define LC_XOR_ALIGNMENT(min) LC_XOR_MIN_ALIGNMENT(min, (sizeof(uint64_t)))
+
+#endif
+
+/**
+ * @brief allocate aligned memory up to 8 bytes alignment
+ *
+ * @param [out] memptr pointer to the newly allocated memory
+ * @param [in] alignment alignment of the memory
+ * @param [in] size size of the memory buffer
+ */
+int lc_alloc_aligned(void **memptr, size_t alignment, size_t size);
+
+/**
+ * @brief allocate aligned memory up to 8 bytes alignment with additional
+ * security precautions
+ *
+ * @param [out] memptr pointer to the newly allocated memory
+ * @param [in] alignment alignment of the memory
+ * @param [in] size size of the memory buffer
+ */
+int lc_alloc_aligned_secure(void **memptr, size_t alignment, size_t size);
+
+/**
+ * @brief allocate aligned memory with arbitrary alignment
+ *
+ * @param [out] memptr pointer to the newly allocated memory
+ * @param [in] alignment alignment of the memory
+ * @param [in] size size of the memory buffer
+ */
+int lc_alloc_high_aligned(void **memptr, size_t alignment, size_t size);
+
+/**
+ * @brief free the memory allocated with lc_alloc_aligned
+ *
+ * The memory is NOT zeroized.
+ *
+ * @param [in] ptr memory pointer to free
+ */
+void lc_free(void *ptr);
+
+/**
+ * @brief free the memory allocated with lc_alloc_high_aligned
+ *
+ * The memory is NOT zeroized.
+ *
+ * @param [in] ptr memory pointer to free
+ * @param [in] size size of the memory to free
+ */
+void lc_free_high_aligned(void *ptr, size_t size);
+
+/**
+ * @brief Check if memory pointer is aligned to given alignment mask
+ *
+ * @param [in] ptr memory pointer to check
+ * @param [in] alignmask alignment mask to check for
+ *
+ * @return 1 if pointer is aligned, 0 if not aligned
+ */
+static inline int lc_mem_aligned(const uint8_t *ptr, uint32_t alignmask)
+{
+ if ((uintptr_t)ptr & alignmask)
+ return 0;
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_MEMORY_SUPPORT_H */
diff --git a/lib/freebl/leancrypto/lc_memset_secure.h b/lib/freebl/leancrypto/lc_memset_secure.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_memset_secure.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_MEMSET_SECURE_H
+#define LC_MEMSET_SECURE_H
+
+#include "ext_headers.h"
+
+/*
+ * Tested following code:
+ *
+ * (1) __asm__ __volatile__("" : "=r" (s) : "0" (s));
+ * (2) __asm__ __volatile__("": : :"memory");
+ * (3) __asm__ __volatile__("" : "=r" (s) : "0" (s) : "memory");
+ * (4) __asm__ __volatile__("" : : "r" (s) : "memory");
+ *
+ * Requred result:
+ *
+ * gcc -O3: objdump -d shows the following:
+ *
+ * 0000000000400440 <main>:
+ * ...
+ * 400469: 48 c7 04 24 00 00 00 movq $0x0,(%rsp)
+ * 400470: 00
+ * 400471: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp)
+ * 400478: 00 00
+ * 40047a: c7 44 24 10 00 00 00 movl $0x0,0x10(%rsp)
+ * 400481: 00
+ *
+ * clang -O3: objdump -d shows the following:
+ *
+ * 0000000000400590 <main>:
+ * ...
+ * 4005c3: c7 44 24 10 00 00 00 movl $0x0,0x10(%rsp)
+ * 4005ca: 00
+ *
+ *
+ * Test results:
+ *
+ * The following table marks an X when the aforementioned movq/movl code is
+ * present (or an invocation of memset@plt) in the object code
+ * (i.e. the code we want). Contrary, the table marks - where the code is not
+ * present (i.e. the code we do not want):
+ *
+ * | BARRIER | (1) | (2) | (3) | (4)
+ * ---------+----------+ | | |
+ * Compiler | | | | |
+ * =========+==========+=======================
+ * | | | |
+ * gcc -O0 | X | X | X | X
+ * | | | |
+ * gcc -O2 | - | X | X | X
+ * | | | |
+ * gcc -O3 | - | X | X | X
+ * | | | |
+ * clang -00 | X | X | X | X
+ * | | | |
+ * clang -02 | X | - | X | X
+ * | | | |
+ * clang -03 | - | - | X | X
+ */
+
+static inline void lc_memset_secure(void *s, int c, size_t n)
+{
+ memset(s, c, n);
+ __asm__ __volatile__("" : : "r"(s) : "memory");
+}
+
+#if 0
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ char buf[20];
+
+ snprintf(buf, sizeof(buf) - 1, "test");
+ printf("%s\n", buf);
+
+ memset_secure(buf, 0, sizeof(buf));
+ return 0;
+}
+#endif
+
+#endif /* LC_MEMSET_SECURE_H */
diff --git a/lib/freebl/leancrypto/lc_rng.h b/lib/freebl/leancrypto/lc_rng.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_rng.h
@@ -0,0 +1,56 @@
+#ifndef LC_RNG_H
+#define LC_RNG_H 1
+#include <blapi.h>
+#include <secrng.h>
+
+struct lc_rng_ctx;
+extern struct lc_rng_ctx *lc_seeded_rng;
+
+/* just enough of the rng_context to make the code happy.
+ * in the end, we just use our NSS internal RNG */
+struct lc_static_rng_data {
+ const unsigned char *seed;
+ size_t seedlen;
+};
+
+struct lc_rng_ctx {
+ struct lc_static_rng_data *dummy;
+};
+
+static inline int lc_rng_generate(struct lc_rng_ctx *rng,
+ unsigned char *addinput,
+ size_t addlen,
+ unsigned char *out,
+ size_t outlen)
+{
+ size_t len;
+ if (rng->dummy != NULL) {
+ if (outlen > rng->dummy->seedlen) {
+ return -1;
+ }
+ PORT_Memcpy(out, rng->dummy->seed, outlen);
+ return 0;
+ }
+ if (addlen != 0) {
+ RNG_RandomUpdate(addinput, addlen);
+ }
+ len= RNG_SystemRNG(out, outlen);
+ if (len != outlen) {
+ return -1;
+ }
+ return 0;
+}
+
+#define lc_rng_seed(rng, seed, seedlen, pers, perslen) {\
+ if (pers_len != 0) {\
+ RNG_RandomUpdate(pers, perslen); \
+ } \
+ RNG_SystemRNG(seed, seedlen); \
+}
+#define lc_rng_check(rng)
+
+#define LC_STATIC_DRNG_ON_STACK(sdrng, state)\
+ struct lc_rng_ctx sdrng; \
+ sdrng.dummy = state;
+
+#endif
diff --git a/lib/freebl/leancrypto/lc_sha256.h b/lib/freebl/leancrypto/lc_sha256.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/lc_sha3.h b/lib/freebl/leancrypto/lc_sha3.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_sha3.h
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA leancrypto
+
+#ifndef LC_SHA3__H
+#define LC_SHA3__H
+#include <blapi.h>
+
+#define LC_SHA3_SIZE_RATE(bits) ((1600 - 2 * bits) >> 3)
+
+#define LC_SHAKE_128_SIZE_BLOCK LC_SHA3_SIZE_RATE(128)
+#define LC_SHAKE_256_SIZE_BLOCK LC_SHA3_SIZE_RATE(256)
+
+/*#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf */
+
+#define LC_HASH_CTX_ON_STACK(name, type_) \
+ struct lc_hash_ctx _##name ; \
+ _##name.hash= type_; \
+ _##name.buf= NULL; \
+ _##name.stream = false; \
+ _##name.u.ctx_ptr = NULL; \
+ struct lc_hash_ctx *name= &_##name;
+
+#define LC_HASH_SET_CTX(name, type_) \
+ name->hash= type_;
+
+#define LC_SHAKE_256_CTX(name) \
+ LC_HASH_SET_CTX(name, lc_shake256);
+
+typedef enum {
+ lc_shake128,
+ lc_shake256,
+} sha3Type;
+
+
+/* sigh, we buffer eKEverything because we can't correctly do multiple
+ * finals correctly. In cases where we know we are not going to
+ * do multiple finals, set the streaming bool */
+struct lc_hash_ctx {
+ sha3Type hash;
+ bool stream;
+ union {
+ SHAKE_256Context *shake256_ctx;
+ SHAKE_128Context *shake128_ctx;
+ void *ctx_ptr;
+ }u;
+ size_t digestSize;
+ size_t current_input;
+ size_t current_output;
+ unsigned char *buf;
+ size_t buf_size;
+ unsigned char buf_space[2048];
+ unsigned char buf2_space[2048];
+};
+
+#define lc_xof(type, in, inlen, out, outlen) \
+ switch(type) { \
+ case lc_shake128: \
+ SHAKE_128_HashBuf(out, outlen, in, inlen); \
+ break; \
+ case lc_shake256: \
+ SHAKE_256_HashBuf(out, outlen, in, inlen); \
+ break; \
+ default: \
+ assert(0); \
+ }
+
+static inline void
+lc_hash_init(struct lc_hash_ctx *ptr) {
+ if (ptr->stream) {
+ /* if we already have a context, just reset it, This is
+ * what the caller wanted, saving a destroy and create */
+ if (ptr->u.ctx_ptr == NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ ptr->u.shake128_ctx = SHAKE_128_NewContext();
+ break;
+ case lc_shake256:
+ ptr->u.shake256_ctx = SHAKE_256_NewContext();
+ break;
+ }
+ }
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Begin(ptr->u.shake128_ctx);
+ break;
+ case lc_shake256:
+ SHAKE_256_Begin(ptr->u.shake256_ctx);
+ break;
+ }
+ return;
+ }
+ /* we can be called with an active buffer, do and implicit reset here
+ * and free that buffer before we set up the next one */
+ if (ptr->buf && ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->current_input);
+ free(ptr->buf);
+ }
+ ptr->digestSize = 0;
+ ptr->current_input = 0;
+ ptr->current_output = 0;
+ ptr->buf_size = sizeof(ptr->buf_space);
+ ptr->buf = &ptr->buf_space[0];
+}
+
+static inline void
+lc_hash_update(struct lc_hash_ctx *ptr, const unsigned char *input, size_t inLen) {
+ if (inLen ==0) { return; } /* why were we even called with a NULL buffer? */
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Absorb(ptr->u.shake128_ctx, input, inLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_Absorb(ptr->u.shake256_ctx, input, inLen);
+ break;
+ }
+ return;
+ }
+ if (ptr->current_input + inLen > ptr->buf_size) {
+ int len = ptr->current_input + inLen + 2048;
+ unsigned char *newBuf;
+ if (ptr->buf_size == sizeof(ptr->buf_space)) {
+ newBuf = calloc(1, len);
+ if (newBuf) {
+ memcpy(newBuf, ptr->buf, ptr->buf_size);
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ }
+ } else {
+ newBuf = reallocarray(ptr->buf, 1, len);
+ }
+ if (!newBuf) {
+ return;
+ }
+ ptr->buf = newBuf;
+ ptr->buf_size = len;
+ }
+ memcpy(ptr->buf + ptr->current_input, input, inLen);
+ ptr->current_input+=inLen;
+}
+
+#define lc_hash_set_digestsize(ptr, len) ((ptr)->digestSize = (len))
+static inline void
+lc_hash_final(struct lc_hash_ctx *ptr, unsigned char *output)
+{
+ size_t outLen= ptr->digestSize;
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_SqueezeEnd(ptr->u.shake128_ctx, output, outLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_SqueezeEnd(ptr->u.shake256_ctx, output, outLen);
+ break;
+ }
+ return;
+ }
+ int len= ptr->current_output+outLen;
+ if (ptr->current_output == 0) {
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, output, outLen);
+ ptr->current_output += outLen;
+ return;
+ }
+ if (len > sizeof(ptr->buf2_space)) {
+ unsigned char *newBuf = calloc(1,len);
+ if (!newBuf) {
+ memset(output, 0, outLen);
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, newBuf, len);
+ memcpy(output, newBuf+ptr->current_output, outLen);
+ memset(newBuf, 0, len);
+ free(newBuf);
+ ptr->current_output += outLen;
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, ptr->buf2_space, len);
+ memcpy(output, &ptr->buf2_space[ptr->current_output], outLen);
+ memset(ptr->buf2_space, 0,len);
+ ptr->current_output += outLen;
+ return;
+}
+
+static inline void lc_hash_zero(struct lc_hash_ctx *ptr)
+{
+ if (ptr->stream) {
+ if (ptr->u.ctx_ptr != NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_DestroyContext(ptr->u.shake128_ctx, PR_TRUE);
+ ptr->u.shake128_ctx = NULL;
+ break;
+ case lc_shake256:
+ SHAKE_128_DestroyContext(ptr->u.shake256_ctx, PR_TRUE);
+ ptr->u.shake256_ctx = NULL;
+ break;
+ }
+ }
+ return;
+ }
+ memset(ptr->buf2_space, 0, sizeof(ptr->buf2_space));
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ if (ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->buf_size);
+ free(ptr->buf);
+ ptr->buf = NULL;
+ }
+ lc_hash_init(ptr);
+}
+#endif
diff --git a/lib/freebl/leancrypto/lc_sha512.h b/lib/freebl/leancrypto/lc_sha512.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/lc_status.h b/lib/freebl/leancrypto/lc_status.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/lc_status.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LC_STATUS_H
+#define LC_STATUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief (Re-)run the self tests
+ *
+ * If the self tests were already executed for a given algorithm, they are
+ * triggered again.
+ */
+void lc_rerun_selftests(void);
+
+/**
+ * @brief Re-run the FIPS 140 integrity test
+ *
+ * \note This API is only present in the FIPS module instance of leancrypto.
+ */
+void lc_fips_integrity_checker(void);
+
+/**
+ * @brief Status information about leancrypto
+ *
+ * @param [in] outbuf Buffer to be filled with status information, allocated by
+ * caller
+ * @param [in] outlen Size of the output buffer
+ */
+void lc_status(char *outbuf, size_t outlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LC_STATUS_H */
diff --git a/lib/freebl/leancrypto/leap_git_version.txt b/lib/freebl/leancrypto/leap_git_version.txt
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/leap_git_version.txt
@@ -0,0 +1,1 @@
+* master 70cabeec Poly1305: move function delcarations to internal header
diff --git a/lib/freebl/leancrypto/left_encode.h b/lib/freebl/leancrypto/left_encode.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/left_encode.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef LEFT_ENCODE_H
+#define LEFT_ENCODE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief - left_encode operation defined in SP800-185
+ */
+unsigned int lc_left_encode(uint8_t *buf, size_t val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LEFT_ENCODE_H */
diff --git a/lib/freebl/leancrypto/math_helper.h b/lib/freebl/leancrypto/math_helper.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/math_helper.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef MATH_HELPER_H
+#define MATH_HELPER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LINUX_KERNEL
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#endif /* LINUX_KERNEL */
+
+static inline uint8_t min_uint8(uint8_t a, uint8_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline uint32_t min_uint32(uint32_t a, uint32_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline uint64_t min_uint64(uint64_t a, uint64_t b)
+{
+ return a < b ? a : b;
+}
+
+static inline size_t min_size(size_t a, size_t b)
+{
+ return a < b ? a : b;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MATH_HELPER_H */
diff --git a/lib/freebl/leancrypto/ml_dsa_44_debug.c b/lib/freebl/leancrypto/ml_dsa_44_debug.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_debug.c
@@ -0,0 +1,92 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "binhexbin.h"
+
+/* This code is only tuned to the C implementation */
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+
+#include "dilithium_debug.h"
+
+void dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation)
+{
+ bin2print(buffer, bufferlen, stdout, explanation);
+}
+
+void dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ unsigned int i, j, k;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ for (j = 0; j < LC_DILITHIUM_L; j++) {
+ printf("\nK(%u) x L(%u) x N: ", i, j);
+ for (k = 0; k < LC_DILITHIUM_N; k++)
+ printf("0x%.8x ", mat[i].vec[j].coeffs[k]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_L; i++) {
+ printf("\nL(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_polyveck(polyveck *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ printf("\nK(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_poly(poly *vec, const char *explanation)
+{
+ unsigned int i;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_N; i++) {
+ printf("%d ", vec->coeffs[i]);
+ }
+ printf("\n");
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_def_header.h b/lib/freebl/leancrypto/ml_dsa_44_def_header.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_def_header.h
@@ -0,0 +1,1 @@
+#define LC_DILITHIUM_TYPE_44 1
diff --git a/lib/freebl/leancrypto/ml_dsa_44_ntt.c b/lib/freebl/leancrypto/ml_dsa_44_ntt.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_ntt.c
@@ -0,0 +1,92 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_ntt.h"
+#include "dilithium_reduce.h"
+#include "dilithium_zetas.h"
+
+/**
+ * @brief ntt - Forward NTT, in-place. No modular reduction is performed after
+ * additions or subtractions. Output vector is in bitreversed
+ * order.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void ntt(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int len, start, j, k;
+ int32_t zeta, t;
+
+ k = 0;
+
+ for (len = 128; len > 0; len >>= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = dilithium_zetas[++k];
+ for (j = start; j < start + len; ++j) {
+ t = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ a[j + len] = a[j] - t;
+ a[j] = a[j] + t;
+ }
+ }
+ }
+}
+
+/**
+ * @brief invntt_tomont - Inverse NTT and multiplication by Montgomery factor
+ * 2^32. In-place. No modular reductions after additions
+ * or subtractions; input coefficients need to be smaller
+ * than Q in absolute value. Output coefficient are
+ * smaller than Q in absolute value.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void invntt_tomont(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int start, len, j, k;
+ int32_t t, zeta;
+ const int32_t f = 41978; // mont^2/256
+
+ k = 256;
+
+ for (len = 1; len < LC_DILITHIUM_N; len <<= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = -dilithium_zetas[--k];
+ for (j = start; j < start + len; ++j) {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ }
+ }
+ }
+
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ a[j] = montgomery_reduce((int64_t)f * a[j]);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_poly.c b/lib/freebl/leancrypto/ml_dsa_44_poly.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_poly.c
@@ -0,0 +1,595 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_service_helpers.h"
+#include "lc_sha3.h"
+#include "timecop.h"
+
+/**
+ * @brief poly_chknorm - Check infinity norm of polynomial against given bound.
+ * Assumes input coefficients were reduced by reduce32().
+ *
+ * @param [in] a pointer to polynomial
+ * @param [in] B norm bound
+ *
+ * @return 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
+ */
+int poly_chknorm(const poly *a, int32_t B)
+{
+ unsigned int i;
+ int32_t t;
+
+ if (B > (LC_DILITHIUM_Q - 1) / 8)
+ return 1;
+
+ /*
+ * It is ok to leak which coefficient violates the bound since
+ * the probability for each coefficient *is independent of secret
+ * data but we must not leak the sign of the centralized representative.
+ */
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ /* Absolute value */
+ t = a->coeffs[i] >> 31;
+ t = a->coeffs[i] - (t & 2 * a->coeffs[i]);
+
+ if (t >= B)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief poly_uniform - Sample polynomial with uniformly random coefficients
+ * in [0,Q-1] by performing rejection sampling on the
+ * output stream of SHAKE128(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_SEEDBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i, ctr, off;
+ unsigned int buflen = POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake128);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, buflen);
+ lc_hash_final(hash_ctx, buf);
+
+ lc_hash_set_digestsize(hash_ctx, LC_SHAKE_128_SIZE_BLOCK);
+
+ ctr = rej_uniform(a->coeffs, LC_DILITHIUM_N, buf, buflen);
+
+ while (ctr < LC_DILITHIUM_N) {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ buf[i] = buf[buflen - off + i];
+
+ lc_hash_final(hash_ctx, buf + off);
+ buflen = LC_DILITHIUM_SEEDBYTES + off;
+ ctr += rej_uniform(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ buflen);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_eta - Sample polynomial with uniformly random
+ * coefficients in [-ETA,ETA] by performing rejection
+ * sampling on the output stream from
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int ctr;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_ETA_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ ctr = rej_eta(a->coeffs, LC_DILITHIUM_N, buf, POLY_UNIFORM_ETA_BYTES);
+
+ while (ctr < LC_DILITHIUM_N) {
+ lc_hash_final(hash_ctx, buf);
+
+ ctr += rej_eta(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ LC_SHAKE_256_SIZE_BLOCK);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_gamma1 - Sample polynomial with uniformly random
+ * coefficients in [-(GAMMA1 - 1), GAMMA1] by
+ * unpacking output stream of
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed: byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param nonce 16-bit nonce
+ */
+void poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_GAMMA1_BYTES);
+ lc_hash_final(hash_ctx, ws_buf);
+ lc_hash_zero(hash_ctx);
+
+ polyz_unpack(a, ws_buf);
+}
+
+/**
+ * @brief poly_challenge - Implementation of H. Samples polynomial with TAU
+ * nonzero coefficients in {-1,1} using the output
+ * stream of SHAKE256(seed).
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] mu byte array containing seed of length LC_DILITHIUM_CTILDE_BYTES
+ */
+void poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf)
+{
+ unsigned int i, b, pos;
+ uint64_t signs;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_set_digestsize(hash_ctx, POLY_CHALLENGE_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ signs |= (uint64_t)buf[i] << 8 * i;
+ pos = 8;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = 0;
+
+ for (i = LC_DILITHIUM_N - LC_DILITHIUM_TAU; i < LC_DILITHIUM_N; ++i) {
+ do {
+ if (pos >= LC_SHAKE_256_SIZE_BLOCK) {
+ lc_hash_final(hash_ctx, buf);
+ pos = 0;
+ }
+
+ b = buf[pos++];
+ } while (b > i);
+
+ c->coeffs[i] = c->coeffs[b];
+ c->coeffs[b] = 1 - (int32_t)(2 * (signs & 1));
+ signs >>= 1;
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief polyeta_pack - Bit-pack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYETA_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyeta_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint8_t t[8];
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 1]);
+ t[2] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 2]);
+ t[3] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 3]);
+ t[4] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 4]);
+ t[5] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 5]);
+ t[6] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 6]);
+ t[7] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 7]);
+
+ r[3 * i + 0] =
+ (uint8_t)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[3 * i + 1] = (uint8_t)((t[2] >> 2) | (t[3] << 1) |
+ (t[4] << 4) | (t[5] << 7));
+ r[3 * i + 2] =
+ (uint8_t)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 1]);
+ r[i] = (uint8_t)(t[0] | (t[1] << 4));
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyeta_unpack - Unpack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyeta_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7;
+ r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7;
+ r->coeffs[8 * i + 2] =
+ ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7;
+ r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7;
+ r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7;
+ r->coeffs[8 * i + 5] =
+ ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7;
+ r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7;
+ r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7;
+
+ r->coeffs[8 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 7];
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[i] & 0x0F;
+ r->coeffs[2 * i + 1] = a[i] >> 4;
+ r->coeffs[2 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyt1_pack - Bit-pack polynomial t1 with coefficients fitting in 10
+ * bits. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyt1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0));
+ r[5 * i + 1] = (uint8_t)((a->coeffs[4 * i + 0] >> 8) |
+ (a->coeffs[4 * i + 1] << 2));
+ r[5 * i + 2] = (uint8_t)((a->coeffs[4 * i + 1] >> 6) |
+ (a->coeffs[4 * i + 2] << 4));
+ r[5 * i + 3] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) |
+ (a->coeffs[4 * i + 3] << 6));
+ r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2));
+ }
+}
+
+/**
+ * @brief polyt0_pack - Bit-pack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT0_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyt0_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[8];
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 0]);
+ t[1] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 1]);
+ t[2] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 2]);
+ t[3] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 3]);
+ t[4] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 4]);
+ t[5] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 5]);
+ t[6] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 6]);
+ t[7] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 7]);
+
+ r[13 * i + 0] = (uint8_t)(t[0]);
+ r[13 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[13 * i + 1] |= (uint8_t)(t[1] << 5);
+ r[13 * i + 2] = (uint8_t)(t[1] >> 3);
+ r[13 * i + 3] = (uint8_t)(t[1] >> 11);
+ r[13 * i + 3] |= (uint8_t)(t[2] << 2);
+ r[13 * i + 4] = (uint8_t)(t[2] >> 6);
+ r[13 * i + 4] |= (uint8_t)(t[3] << 7);
+ r[13 * i + 5] = (uint8_t)(t[3] >> 1);
+ r[13 * i + 6] = (uint8_t)(t[3] >> 9);
+ r[13 * i + 6] |= (uint8_t)(t[4] << 4);
+ r[13 * i + 7] = (uint8_t)(t[4] >> 4);
+ r[13 * i + 8] = (uint8_t)(t[4] >> 12);
+ r[13 * i + 8] |= (uint8_t)(t[5] << 1);
+ r[13 * i + 9] = (uint8_t)(t[5] >> 7);
+ r[13 * i + 9] |= (uint8_t)(t[6] << 6);
+ r[13 * i + 10] = (uint8_t)(t[6] >> 2);
+ r[13 * i + 11] = (uint8_t)(t[6] >> 10);
+ r[13 * i + 11] |= (uint8_t)(t[7] << 3);
+ r[13 * i + 12] = (uint8_t)(t[7] >> 5);
+ }
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyt0_unpack - Unpack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyt0_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = a[13 * i + 0];
+ r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
+ r->coeffs[8 * i + 0] &= 0x1FFF;
+
+ r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
+ r->coeffs[8 * i + 1] &= 0x1FFF;
+
+ r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
+ r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
+ r->coeffs[8 * i + 2] &= 0x1FFF;
+
+ r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
+ r->coeffs[8 * i + 3] &= 0x1FFF;
+
+ r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
+ r->coeffs[8 * i + 4] &= 0x1FFF;
+
+ r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
+ r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
+ r->coeffs[8 * i + 5] &= 0x1FFF;
+
+ r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
+ r->coeffs[8 * i + 6] &= 0x1FFF;
+
+ r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
+ r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
+ r->coeffs[8 * i + 7] &= 0x1FFF;
+
+ r->coeffs[8 * i + 0] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 7];
+ }
+}
+
+/**
+ * @param polyz_pack - Bit-pack polynomial with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYZ_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyz_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[4];
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 1]);
+ t[2] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 2]);
+ t[3] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 3]);
+
+ r[9 * i + 0] = (uint8_t)(t[0]);
+ r[9 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[9 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[9 * i + 2] |= (uint8_t)(t[1] << 2);
+ r[9 * i + 3] = (uint8_t)(t[1] >> 6);
+ r[9 * i + 4] = (uint8_t)(t[1] >> 14);
+ r[9 * i + 4] |= (uint8_t)(t[2] << 4);
+ r[9 * i + 5] = (uint8_t)(t[2] >> 4);
+ r[9 * i + 6] = (uint8_t)(t[2] >> 12);
+ r[9 * i + 6] |= (uint8_t)(t[3] << 6);
+ r[9 * i + 7] = (uint8_t)(t[3] >> 2);
+ r[9 * i + 8] = (uint8_t)(t[3] >> 10);
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 1]);
+
+ r[5 * i + 0] = (uint8_t)(t[0]);
+ r[5 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[5 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[5 * i + 2] |= (uint8_t)(t[1] << 4);
+ r[5 * i + 3] = (uint8_t)(t[1] >> 4);
+ r[5 * i + 4] = (uint8_t)(t[1] >> 12);
+ }
+#else
+#error "Undefined Gamma"
+#endif
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyz_unpack - Unpack polynomial z with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyz_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] = a[9 * i + 0];
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 1] << 8;
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 2] << 16;
+ r->coeffs[4 * i + 0] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 3] << 6;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 4] << 14;
+ r->coeffs[4 * i + 1] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 5] << 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 6] << 12;
+ r->coeffs[4 * i + 2] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 7] << 2;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 8] << 10;
+ r->coeffs[4 * i + 3] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 0];
+ r->coeffs[4 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 1];
+ r->coeffs[4 * i + 2] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 2];
+ r->coeffs[4 * i + 3] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 3];
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[5 * i + 0];
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 1] << 8;
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 2] << 16;
+ r->coeffs[2 * i + 0] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 3] << 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 4] << 12;
+ r->coeffs[2 * i + 1] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined Gamma"
+#endif
+}
+
+/**
+ * @brief polyw1_pack - Bit-pack polynomial w1 with coefficients in [0,15] or
+ * [0,43]. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYW1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyw1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[3 * i + 0] = (uint8_t)(a->coeffs[4 * i + 0]);
+ r[3 * i + 0] |= (uint8_t)(a->coeffs[4 * i + 1] << 6);
+ r[3 * i + 1] = (uint8_t)(a->coeffs[4 * i + 1] >> 2);
+ r[3 * i + 1] |= (uint8_t)(a->coeffs[4 * i + 2] << 4);
+ r[3 * i + 2] = (uint8_t)(a->coeffs[4 * i + 2] >> 4);
+ r[3 * i + 2] |= (uint8_t)(a->coeffs[4 * i + 3] << 2);
+ }
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i)
+ r[i] = (uint8_t)(a->coeffs[2 * i + 0] |
+ (a->coeffs[2 * i + 1] << 4));
+#else
+#error "Undefined Gamma"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_rounding.c b/lib/freebl/leancrypto/ml_dsa_44_rounding.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_rounding.c
@@ -0,0 +1,130 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_rounding.h"
+
+/**
+ * @brief power2round - For finite field element a, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t power2round(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + (1 << (LC_DILITHIUM_D - 1)) - 1) >> LC_DILITHIUM_D;
+ *a0 = a - (a1 << LC_DILITHIUM_D);
+ return a1;
+}
+
+/**
+ * @brief decompose - For finite field element a, compute high and low bits a0,
+ * a1 such that a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except if a1 = (Q-1)/ALPHA where
+ * we set a1 = 0 and -ALPHA/2 <= a0 = a mod^+ Q - Q < 0.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t decompose(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + 127) >> 7;
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+#else
+#error "Uknown GAMMA2"
+#endif
+
+ *a0 = a - a1 * 2 * LC_DILITHIUM_GAMMA2;
+ *a0 -= (((LC_DILITHIUM_Q - 1) / 2 - *a0) >> 31) & LC_DILITHIUM_Q;
+
+ return a1;
+}
+
+/**
+ * @brief make_hint - Compute hint bit indicating whether the low bits of the
+ * input element overflow into the high bits.
+ *
+ * @param a0 [in] low bits of input element
+ * @param a1 [in] high bits of input element
+ *
+ * @return 1 if overflow.
+ */
+int32_t make_hint(int32_t a0, int32_t a1)
+{
+ if (a0 > LC_DILITHIUM_GAMMA2 || a0 < -LC_DILITHIUM_GAMMA2 ||
+ (a0 == -LC_DILITHIUM_GAMMA2 && a1 != 0))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief use_hint - Correct high bits according to hint.
+ *
+ * @param [in] a input element
+ * @param [in] hint hint bit
+ *
+ * @return corrected high bits.
+ */
+int32_t use_hint(int32_t a, int32_t hint)
+{
+ int32_t a0, a1;
+
+ a1 = decompose(&a0, a);
+ if (hint == 0)
+ return a1;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ if (a0 > 0)
+ return (a1 + 1) & 15;
+ else
+ return (a1 - 1) & 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ if (a0 > 0)
+ return (a1 == 43) ? 0 : a1 + 1;
+ else
+ return (a1 == 0) ? 43 : a1 - 1;
+#else
+#error "Uknown GAMMA2"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_signature_c.c b/lib/freebl/leancrypto/ml_dsa_44_signature_c.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_signature_c.c
@@ -0,0 +1,131 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_signature_c.h"
+#include "visibility.h"
+
+/* We need once the buffer size to handle the hashing */
+#define LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER 1
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+#include "dilithium_pack.h"
+#include "dilithium_signature_impl.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_from_seed_c,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen)
+{
+ return lc_dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_c, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_keypair_impl(pk, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_c, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_impl(sig, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_ctx_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_ctx_impl(sig, ctx, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ return lc_dilithium_sign_init_impl(ctx, sk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_sign_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_final_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_final_impl(sig, ctx, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_c,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_impl(sig, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_ctx_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_init_impl(ctx, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_verify_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_final_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_final_impl(sig, ctx, pk);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_44_signature_helper.c b/lib/freebl/leancrypto/ml_dsa_44_signature_helper.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_44_signature_helper.c
@@ -0,0 +1,103 @@
+#define LC_DILITHIUM_TYPE_44 1
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "dilithium_type.h"
+#include "visibility.h"
+
+#include "lc_sha3.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE);
+ if (ret)
+ return -ret;
+
+ LC_DILITHIUM_SET_CTX(out_ctx);
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+#if LC_DILITHIUM_MODE == 2
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD +
+ LC_DILITHIUM_44_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_44_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 3
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD +
+ LC_DILITHIUM_65_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_65_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 5
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD +
+ LC_DILITHIUM_87_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_87_AHAT_SIZE;
+#endif
+
+ LC_SHAKE_256_CTX((&(out_ctx)->dilithium_hash_ctx));
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ lc_dilithium_ctx_zero(ctx);
+ lc_free(ctx);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_debug.c b/lib/freebl/leancrypto/ml_dsa_65_debug.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_debug.c
@@ -0,0 +1,92 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "binhexbin.h"
+
+/* This code is only tuned to the C implementation */
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+
+#include "dilithium_debug.h"
+
+void dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation)
+{
+ bin2print(buffer, bufferlen, stdout, explanation);
+}
+
+void dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ unsigned int i, j, k;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ for (j = 0; j < LC_DILITHIUM_L; j++) {
+ printf("\nK(%u) x L(%u) x N: ", i, j);
+ for (k = 0; k < LC_DILITHIUM_N; k++)
+ printf("0x%.8x ", mat[i].vec[j].coeffs[k]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_L; i++) {
+ printf("\nL(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_polyveck(polyveck *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ printf("\nK(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_poly(poly *vec, const char *explanation)
+{
+ unsigned int i;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_N; i++) {
+ printf("%d ", vec->coeffs[i]);
+ }
+ printf("\n");
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_def_header.h b/lib/freebl/leancrypto/ml_dsa_65_def_header.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_def_header.h
@@ -0,0 +1,1 @@
+#define LC_DILITHIUM_TYPE_65 1
diff --git a/lib/freebl/leancrypto/ml_dsa_65_ntt.c b/lib/freebl/leancrypto/ml_dsa_65_ntt.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_ntt.c
@@ -0,0 +1,92 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_ntt.h"
+#include "dilithium_reduce.h"
+#include "dilithium_zetas.h"
+
+/**
+ * @brief ntt - Forward NTT, in-place. No modular reduction is performed after
+ * additions or subtractions. Output vector is in bitreversed
+ * order.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void ntt(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int len, start, j, k;
+ int32_t zeta, t;
+
+ k = 0;
+
+ for (len = 128; len > 0; len >>= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = dilithium_zetas[++k];
+ for (j = start; j < start + len; ++j) {
+ t = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ a[j + len] = a[j] - t;
+ a[j] = a[j] + t;
+ }
+ }
+ }
+}
+
+/**
+ * @brief invntt_tomont - Inverse NTT and multiplication by Montgomery factor
+ * 2^32. In-place. No modular reductions after additions
+ * or subtractions; input coefficients need to be smaller
+ * than Q in absolute value. Output coefficient are
+ * smaller than Q in absolute value.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void invntt_tomont(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int start, len, j, k;
+ int32_t t, zeta;
+ const int32_t f = 41978; // mont^2/256
+
+ k = 256;
+
+ for (len = 1; len < LC_DILITHIUM_N; len <<= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = -dilithium_zetas[--k];
+ for (j = start; j < start + len; ++j) {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ }
+ }
+ }
+
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ a[j] = montgomery_reduce((int64_t)f * a[j]);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_poly.c b/lib/freebl/leancrypto/ml_dsa_65_poly.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_poly.c
@@ -0,0 +1,595 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_service_helpers.h"
+#include "lc_sha3.h"
+#include "timecop.h"
+
+/**
+ * @brief poly_chknorm - Check infinity norm of polynomial against given bound.
+ * Assumes input coefficients were reduced by reduce32().
+ *
+ * @param [in] a pointer to polynomial
+ * @param [in] B norm bound
+ *
+ * @return 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
+ */
+int poly_chknorm(const poly *a, int32_t B)
+{
+ unsigned int i;
+ int32_t t;
+
+ if (B > (LC_DILITHIUM_Q - 1) / 8)
+ return 1;
+
+ /*
+ * It is ok to leak which coefficient violates the bound since
+ * the probability for each coefficient *is independent of secret
+ * data but we must not leak the sign of the centralized representative.
+ */
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ /* Absolute value */
+ t = a->coeffs[i] >> 31;
+ t = a->coeffs[i] - (t & 2 * a->coeffs[i]);
+
+ if (t >= B)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief poly_uniform - Sample polynomial with uniformly random coefficients
+ * in [0,Q-1] by performing rejection sampling on the
+ * output stream of SHAKE128(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_SEEDBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i, ctr, off;
+ unsigned int buflen = POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake128);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, buflen);
+ lc_hash_final(hash_ctx, buf);
+
+ lc_hash_set_digestsize(hash_ctx, LC_SHAKE_128_SIZE_BLOCK);
+
+ ctr = rej_uniform(a->coeffs, LC_DILITHIUM_N, buf, buflen);
+
+ while (ctr < LC_DILITHIUM_N) {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ buf[i] = buf[buflen - off + i];
+
+ lc_hash_final(hash_ctx, buf + off);
+ buflen = LC_DILITHIUM_SEEDBYTES + off;
+ ctr += rej_uniform(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ buflen);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_eta - Sample polynomial with uniformly random
+ * coefficients in [-ETA,ETA] by performing rejection
+ * sampling on the output stream from
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int ctr;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_ETA_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ ctr = rej_eta(a->coeffs, LC_DILITHIUM_N, buf, POLY_UNIFORM_ETA_BYTES);
+
+ while (ctr < LC_DILITHIUM_N) {
+ lc_hash_final(hash_ctx, buf);
+
+ ctr += rej_eta(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ LC_SHAKE_256_SIZE_BLOCK);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_gamma1 - Sample polynomial with uniformly random
+ * coefficients in [-(GAMMA1 - 1), GAMMA1] by
+ * unpacking output stream of
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed: byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param nonce 16-bit nonce
+ */
+void poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_GAMMA1_BYTES);
+ lc_hash_final(hash_ctx, ws_buf);
+ lc_hash_zero(hash_ctx);
+
+ polyz_unpack(a, ws_buf);
+}
+
+/**
+ * @brief poly_challenge - Implementation of H. Samples polynomial with TAU
+ * nonzero coefficients in {-1,1} using the output
+ * stream of SHAKE256(seed).
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] mu byte array containing seed of length LC_DILITHIUM_CTILDE_BYTES
+ */
+void poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf)
+{
+ unsigned int i, b, pos;
+ uint64_t signs;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_set_digestsize(hash_ctx, POLY_CHALLENGE_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ signs |= (uint64_t)buf[i] << 8 * i;
+ pos = 8;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = 0;
+
+ for (i = LC_DILITHIUM_N - LC_DILITHIUM_TAU; i < LC_DILITHIUM_N; ++i) {
+ do {
+ if (pos >= LC_SHAKE_256_SIZE_BLOCK) {
+ lc_hash_final(hash_ctx, buf);
+ pos = 0;
+ }
+
+ b = buf[pos++];
+ } while (b > i);
+
+ c->coeffs[i] = c->coeffs[b];
+ c->coeffs[b] = 1 - (int32_t)(2 * (signs & 1));
+ signs >>= 1;
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief polyeta_pack - Bit-pack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYETA_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyeta_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint8_t t[8];
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 1]);
+ t[2] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 2]);
+ t[3] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 3]);
+ t[4] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 4]);
+ t[5] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 5]);
+ t[6] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 6]);
+ t[7] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 7]);
+
+ r[3 * i + 0] =
+ (uint8_t)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[3 * i + 1] = (uint8_t)((t[2] >> 2) | (t[3] << 1) |
+ (t[4] << 4) | (t[5] << 7));
+ r[3 * i + 2] =
+ (uint8_t)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 1]);
+ r[i] = (uint8_t)(t[0] | (t[1] << 4));
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyeta_unpack - Unpack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyeta_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7;
+ r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7;
+ r->coeffs[8 * i + 2] =
+ ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7;
+ r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7;
+ r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7;
+ r->coeffs[8 * i + 5] =
+ ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7;
+ r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7;
+ r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7;
+
+ r->coeffs[8 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 7];
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[i] & 0x0F;
+ r->coeffs[2 * i + 1] = a[i] >> 4;
+ r->coeffs[2 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyt1_pack - Bit-pack polynomial t1 with coefficients fitting in 10
+ * bits. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyt1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0));
+ r[5 * i + 1] = (uint8_t)((a->coeffs[4 * i + 0] >> 8) |
+ (a->coeffs[4 * i + 1] << 2));
+ r[5 * i + 2] = (uint8_t)((a->coeffs[4 * i + 1] >> 6) |
+ (a->coeffs[4 * i + 2] << 4));
+ r[5 * i + 3] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) |
+ (a->coeffs[4 * i + 3] << 6));
+ r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2));
+ }
+}
+
+/**
+ * @brief polyt0_pack - Bit-pack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT0_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyt0_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[8];
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 0]);
+ t[1] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 1]);
+ t[2] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 2]);
+ t[3] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 3]);
+ t[4] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 4]);
+ t[5] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 5]);
+ t[6] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 6]);
+ t[7] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 7]);
+
+ r[13 * i + 0] = (uint8_t)(t[0]);
+ r[13 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[13 * i + 1] |= (uint8_t)(t[1] << 5);
+ r[13 * i + 2] = (uint8_t)(t[1] >> 3);
+ r[13 * i + 3] = (uint8_t)(t[1] >> 11);
+ r[13 * i + 3] |= (uint8_t)(t[2] << 2);
+ r[13 * i + 4] = (uint8_t)(t[2] >> 6);
+ r[13 * i + 4] |= (uint8_t)(t[3] << 7);
+ r[13 * i + 5] = (uint8_t)(t[3] >> 1);
+ r[13 * i + 6] = (uint8_t)(t[3] >> 9);
+ r[13 * i + 6] |= (uint8_t)(t[4] << 4);
+ r[13 * i + 7] = (uint8_t)(t[4] >> 4);
+ r[13 * i + 8] = (uint8_t)(t[4] >> 12);
+ r[13 * i + 8] |= (uint8_t)(t[5] << 1);
+ r[13 * i + 9] = (uint8_t)(t[5] >> 7);
+ r[13 * i + 9] |= (uint8_t)(t[6] << 6);
+ r[13 * i + 10] = (uint8_t)(t[6] >> 2);
+ r[13 * i + 11] = (uint8_t)(t[6] >> 10);
+ r[13 * i + 11] |= (uint8_t)(t[7] << 3);
+ r[13 * i + 12] = (uint8_t)(t[7] >> 5);
+ }
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyt0_unpack - Unpack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyt0_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = a[13 * i + 0];
+ r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
+ r->coeffs[8 * i + 0] &= 0x1FFF;
+
+ r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
+ r->coeffs[8 * i + 1] &= 0x1FFF;
+
+ r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
+ r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
+ r->coeffs[8 * i + 2] &= 0x1FFF;
+
+ r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
+ r->coeffs[8 * i + 3] &= 0x1FFF;
+
+ r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
+ r->coeffs[8 * i + 4] &= 0x1FFF;
+
+ r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
+ r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
+ r->coeffs[8 * i + 5] &= 0x1FFF;
+
+ r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
+ r->coeffs[8 * i + 6] &= 0x1FFF;
+
+ r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
+ r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
+ r->coeffs[8 * i + 7] &= 0x1FFF;
+
+ r->coeffs[8 * i + 0] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 7];
+ }
+}
+
+/**
+ * @param polyz_pack - Bit-pack polynomial with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYZ_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyz_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[4];
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 1]);
+ t[2] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 2]);
+ t[3] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 3]);
+
+ r[9 * i + 0] = (uint8_t)(t[0]);
+ r[9 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[9 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[9 * i + 2] |= (uint8_t)(t[1] << 2);
+ r[9 * i + 3] = (uint8_t)(t[1] >> 6);
+ r[9 * i + 4] = (uint8_t)(t[1] >> 14);
+ r[9 * i + 4] |= (uint8_t)(t[2] << 4);
+ r[9 * i + 5] = (uint8_t)(t[2] >> 4);
+ r[9 * i + 6] = (uint8_t)(t[2] >> 12);
+ r[9 * i + 6] |= (uint8_t)(t[3] << 6);
+ r[9 * i + 7] = (uint8_t)(t[3] >> 2);
+ r[9 * i + 8] = (uint8_t)(t[3] >> 10);
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 1]);
+
+ r[5 * i + 0] = (uint8_t)(t[0]);
+ r[5 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[5 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[5 * i + 2] |= (uint8_t)(t[1] << 4);
+ r[5 * i + 3] = (uint8_t)(t[1] >> 4);
+ r[5 * i + 4] = (uint8_t)(t[1] >> 12);
+ }
+#else
+#error "Undefined Gamma"
+#endif
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyz_unpack - Unpack polynomial z with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyz_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] = a[9 * i + 0];
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 1] << 8;
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 2] << 16;
+ r->coeffs[4 * i + 0] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 3] << 6;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 4] << 14;
+ r->coeffs[4 * i + 1] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 5] << 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 6] << 12;
+ r->coeffs[4 * i + 2] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 7] << 2;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 8] << 10;
+ r->coeffs[4 * i + 3] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 0];
+ r->coeffs[4 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 1];
+ r->coeffs[4 * i + 2] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 2];
+ r->coeffs[4 * i + 3] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 3];
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[5 * i + 0];
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 1] << 8;
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 2] << 16;
+ r->coeffs[2 * i + 0] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 3] << 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 4] << 12;
+ r->coeffs[2 * i + 1] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined Gamma"
+#endif
+}
+
+/**
+ * @brief polyw1_pack - Bit-pack polynomial w1 with coefficients in [0,15] or
+ * [0,43]. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYW1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyw1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[3 * i + 0] = (uint8_t)(a->coeffs[4 * i + 0]);
+ r[3 * i + 0] |= (uint8_t)(a->coeffs[4 * i + 1] << 6);
+ r[3 * i + 1] = (uint8_t)(a->coeffs[4 * i + 1] >> 2);
+ r[3 * i + 1] |= (uint8_t)(a->coeffs[4 * i + 2] << 4);
+ r[3 * i + 2] = (uint8_t)(a->coeffs[4 * i + 2] >> 4);
+ r[3 * i + 2] |= (uint8_t)(a->coeffs[4 * i + 3] << 2);
+ }
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i)
+ r[i] = (uint8_t)(a->coeffs[2 * i + 0] |
+ (a->coeffs[2 * i + 1] << 4));
+#else
+#error "Undefined Gamma"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_rounding.c b/lib/freebl/leancrypto/ml_dsa_65_rounding.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_rounding.c
@@ -0,0 +1,130 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_rounding.h"
+
+/**
+ * @brief power2round - For finite field element a, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t power2round(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + (1 << (LC_DILITHIUM_D - 1)) - 1) >> LC_DILITHIUM_D;
+ *a0 = a - (a1 << LC_DILITHIUM_D);
+ return a1;
+}
+
+/**
+ * @brief decompose - For finite field element a, compute high and low bits a0,
+ * a1 such that a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except if a1 = (Q-1)/ALPHA where
+ * we set a1 = 0 and -ALPHA/2 <= a0 = a mod^+ Q - Q < 0.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t decompose(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + 127) >> 7;
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+#else
+#error "Uknown GAMMA2"
+#endif
+
+ *a0 = a - a1 * 2 * LC_DILITHIUM_GAMMA2;
+ *a0 -= (((LC_DILITHIUM_Q - 1) / 2 - *a0) >> 31) & LC_DILITHIUM_Q;
+
+ return a1;
+}
+
+/**
+ * @brief make_hint - Compute hint bit indicating whether the low bits of the
+ * input element overflow into the high bits.
+ *
+ * @param a0 [in] low bits of input element
+ * @param a1 [in] high bits of input element
+ *
+ * @return 1 if overflow.
+ */
+int32_t make_hint(int32_t a0, int32_t a1)
+{
+ if (a0 > LC_DILITHIUM_GAMMA2 || a0 < -LC_DILITHIUM_GAMMA2 ||
+ (a0 == -LC_DILITHIUM_GAMMA2 && a1 != 0))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief use_hint - Correct high bits according to hint.
+ *
+ * @param [in] a input element
+ * @param [in] hint hint bit
+ *
+ * @return corrected high bits.
+ */
+int32_t use_hint(int32_t a, int32_t hint)
+{
+ int32_t a0, a1;
+
+ a1 = decompose(&a0, a);
+ if (hint == 0)
+ return a1;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ if (a0 > 0)
+ return (a1 + 1) & 15;
+ else
+ return (a1 - 1) & 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ if (a0 > 0)
+ return (a1 == 43) ? 0 : a1 + 1;
+ else
+ return (a1 == 0) ? 43 : a1 - 1;
+#else
+#error "Uknown GAMMA2"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_signature_c.c b/lib/freebl/leancrypto/ml_dsa_65_signature_c.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_signature_c.c
@@ -0,0 +1,131 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_signature_c.h"
+#include "visibility.h"
+
+/* We need once the buffer size to handle the hashing */
+#define LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER 1
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+#include "dilithium_pack.h"
+#include "dilithium_signature_impl.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_from_seed_c,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen)
+{
+ return lc_dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_c, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_keypair_impl(pk, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_c, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_impl(sig, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_ctx_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_ctx_impl(sig, ctx, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ return lc_dilithium_sign_init_impl(ctx, sk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_sign_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_final_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_final_impl(sig, ctx, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_c,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_impl(sig, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_ctx_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_init_impl(ctx, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_verify_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_final_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_final_impl(sig, ctx, pk);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_65_signature_helper.c b/lib/freebl/leancrypto/ml_dsa_65_signature_helper.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_65_signature_helper.c
@@ -0,0 +1,103 @@
+#define LC_DILITHIUM_TYPE_65 1
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "dilithium_type.h"
+#include "visibility.h"
+
+#include "lc_sha3.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE);
+ if (ret)
+ return -ret;
+
+ LC_DILITHIUM_SET_CTX(out_ctx);
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+#if LC_DILITHIUM_MODE == 2
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD +
+ LC_DILITHIUM_44_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_44_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 3
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD +
+ LC_DILITHIUM_65_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_65_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 5
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD +
+ LC_DILITHIUM_87_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_87_AHAT_SIZE;
+#endif
+
+ LC_SHAKE_256_CTX((&(out_ctx)->dilithium_hash_ctx));
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ lc_dilithium_ctx_zero(ctx);
+ lc_free(ctx);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_debug.c b/lib/freebl/leancrypto/ml_dsa_87_debug.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_debug.c
@@ -0,0 +1,92 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2023 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "binhexbin.h"
+
+/* This code is only tuned to the C implementation */
+#include "dilithium_type.h"
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+
+#include "dilithium_debug.h"
+
+void dilithium_print_buffer(const uint8_t *buffer, const size_t bufferlen,
+ const char *explanation)
+{
+ bin2print(buffer, bufferlen, stdout, explanation);
+}
+
+void dilithium_print_polyvecl_k(polyvecl mat[LC_DILITHIUM_K],
+ const char *explanation)
+{
+ unsigned int i, j, k;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ for (j = 0; j < LC_DILITHIUM_L; j++) {
+ printf("\nK(%u) x L(%u) x N: ", i, j);
+ for (k = 0; k < LC_DILITHIUM_N; k++)
+ printf("0x%.8x ", mat[i].vec[j].coeffs[k]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_polyvecl(polyvecl *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_L; i++) {
+ printf("\nL(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_polyveck(polyveck *polyvec, const char *explanation)
+{
+ unsigned int i, j;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_K; i++) {
+ printf("\nK(%u) x N: ", i);
+ for (j = 0; j < LC_DILITHIUM_N; j++) {
+ printf("%d ", polyvec->vec[i].coeffs[j]);
+ }
+ }
+ printf("\n");
+}
+
+void dilithium_print_poly(poly *vec, const char *explanation)
+{
+ unsigned int i;
+
+ printf("%s", explanation);
+ for (i = 0; i < LC_DILITHIUM_N; i++) {
+ printf("%d ", vec->coeffs[i]);
+ }
+ printf("\n");
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_def_header.h b/lib/freebl/leancrypto/ml_dsa_87_def_header.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_def_header.h
@@ -0,0 +1,1 @@
+#define LC_DILITHIUM_TYPE_87 1
diff --git a/lib/freebl/leancrypto/ml_dsa_87_ntt.c b/lib/freebl/leancrypto/ml_dsa_87_ntt.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_ntt.c
@@ -0,0 +1,92 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_ntt.h"
+#include "dilithium_reduce.h"
+#include "dilithium_zetas.h"
+
+/**
+ * @brief ntt - Forward NTT, in-place. No modular reduction is performed after
+ * additions or subtractions. Output vector is in bitreversed
+ * order.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void ntt(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int len, start, j, k;
+ int32_t zeta, t;
+
+ k = 0;
+
+ for (len = 128; len > 0; len >>= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = dilithium_zetas[++k];
+ for (j = start; j < start + len; ++j) {
+ t = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ a[j + len] = a[j] - t;
+ a[j] = a[j] + t;
+ }
+ }
+ }
+}
+
+/**
+ * @brief invntt_tomont - Inverse NTT and multiplication by Montgomery factor
+ * 2^32. In-place. No modular reductions after additions
+ * or subtractions; input coefficients need to be smaller
+ * than Q in absolute value. Output coefficient are
+ * smaller than Q in absolute value.
+ *
+ * @param [in,out] p input/output coefficient array
+ */
+void invntt_tomont(int32_t a[LC_DILITHIUM_N])
+{
+ unsigned int start, len, j, k;
+ int32_t t, zeta;
+ const int32_t f = 41978; // mont^2/256
+
+ k = 256;
+
+ for (len = 1; len < LC_DILITHIUM_N; len <<= 1) {
+ for (start = 0; start < LC_DILITHIUM_N; start = j + len) {
+ zeta = -dilithium_zetas[--k];
+ for (j = start; j < start + len; ++j) {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = montgomery_reduce((int64_t)zeta *
+ a[j + len]);
+ }
+ }
+ }
+
+ for (j = 0; j < LC_DILITHIUM_N; ++j)
+ a[j] = montgomery_reduce((int64_t)f * a[j]);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_poly.c b/lib/freebl/leancrypto/ml_dsa_87_poly.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_poly.c
@@ -0,0 +1,595 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_service_helpers.h"
+#include "lc_sha3.h"
+#include "timecop.h"
+
+/**
+ * @brief poly_chknorm - Check infinity norm of polynomial against given bound.
+ * Assumes input coefficients were reduced by reduce32().
+ *
+ * @param [in] a pointer to polynomial
+ * @param [in] B norm bound
+ *
+ * @return 0 if norm is strictly smaller than B <= (Q-1)/8 and 1 otherwise.
+ */
+int poly_chknorm(const poly *a, int32_t B)
+{
+ unsigned int i;
+ int32_t t;
+
+ if (B > (LC_DILITHIUM_Q - 1) / 8)
+ return 1;
+
+ /*
+ * It is ok to leak which coefficient violates the bound since
+ * the probability for each coefficient *is independent of secret
+ * data but we must not leak the sign of the centralized representative.
+ */
+ for (i = 0; i < LC_DILITHIUM_N; ++i) {
+ /* Absolute value */
+ t = a->coeffs[i] >> 31;
+ t = a->coeffs[i] - (t & 2 * a->coeffs[i]);
+
+ if (t >= B)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief poly_uniform - Sample polynomial with uniformly random coefficients
+ * in [0,Q-1] by performing rejection sampling on the
+ * output stream of SHAKE128(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_SEEDBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void poly_uniform(poly *a, const uint8_t seed[LC_DILITHIUM_SEEDBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int i, ctr, off;
+ unsigned int buflen = POLY_UNIFORM_NBLOCKS * LC_SHAKE_128_SIZE_BLOCK;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake128);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_SEEDBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, buflen);
+ lc_hash_final(hash_ctx, buf);
+
+ lc_hash_set_digestsize(hash_ctx, LC_SHAKE_128_SIZE_BLOCK);
+
+ ctr = rej_uniform(a->coeffs, LC_DILITHIUM_N, buf, buflen);
+
+ while (ctr < LC_DILITHIUM_N) {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ buf[i] = buf[buflen - off + i];
+
+ lc_hash_final(hash_ctx, buf + off);
+ buflen = LC_DILITHIUM_SEEDBYTES + off;
+ ctr += rej_uniform(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ buflen);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_eta - Sample polynomial with uniformly random
+ * coefficients in [-ETA,ETA] by performing rejection
+ * sampling on the output stream from
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param [in] nonce 2-byte nonce
+ */
+void poly_uniform_eta(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ unsigned int ctr;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_ETA_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ ctr = rej_eta(a->coeffs, LC_DILITHIUM_N, buf, POLY_UNIFORM_ETA_BYTES);
+
+ while (ctr < LC_DILITHIUM_N) {
+ lc_hash_final(hash_ctx, buf);
+
+ ctr += rej_eta(a->coeffs + ctr, LC_DILITHIUM_N - ctr, buf,
+ LC_SHAKE_256_SIZE_BLOCK);
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief poly_uniform_gamma1 - Sample polynomial with uniformly random
+ * coefficients in [-(GAMMA1 - 1), GAMMA1] by
+ * unpacking output stream of
+ * SHAKE256(seed|nonce).
+ *
+ * @param [out] a pointer to output polynomial
+ * @param [in] seed: byte array with seed of length LC_DILITHIUM_CRHBYTES
+ * @param nonce 16-bit nonce
+ */
+void poly_uniform_gamma1(poly *a, const uint8_t seed[LC_DILITHIUM_CRHBYTES],
+ uint16_t nonce, void *ws_buf)
+{
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CRHBYTES);
+ lc_hash_update(hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
+ lc_hash_set_digestsize(hash_ctx, POLY_UNIFORM_GAMMA1_BYTES);
+ lc_hash_final(hash_ctx, ws_buf);
+ lc_hash_zero(hash_ctx);
+
+ polyz_unpack(a, ws_buf);
+}
+
+/**
+ * @brief poly_challenge - Implementation of H. Samples polynomial with TAU
+ * nonzero coefficients in {-1,1} using the output
+ * stream of SHAKE256(seed).
+ *
+ * @param [out] c pointer to output polynomial
+ * @param [in] mu byte array containing seed of length LC_DILITHIUM_CTILDE_BYTES
+ */
+void poly_challenge(poly *c, const uint8_t seed[LC_DILITHIUM_CTILDE_BYTES],
+ void *ws_buf)
+{
+ unsigned int i, b, pos;
+ uint64_t signs;
+ uint8_t *buf = ws_buf;
+ LC_HASH_CTX_ON_STACK(hash_ctx, lc_shake256);
+
+ lc_hash_init(hash_ctx);
+ lc_hash_update(hash_ctx, seed, LC_DILITHIUM_CTILDE_BYTES);
+ lc_hash_set_digestsize(hash_ctx, POLY_CHALLENGE_BYTES);
+ lc_hash_final(hash_ctx, buf);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ signs |= (uint64_t)buf[i] << 8 * i;
+ pos = 8;
+
+ for (i = 0; i < LC_DILITHIUM_N; ++i)
+ c->coeffs[i] = 0;
+
+ for (i = LC_DILITHIUM_N - LC_DILITHIUM_TAU; i < LC_DILITHIUM_N; ++i) {
+ do {
+ if (pos >= LC_SHAKE_256_SIZE_BLOCK) {
+ lc_hash_final(hash_ctx, buf);
+ pos = 0;
+ }
+
+ b = buf[pos++];
+ } while (b > i);
+
+ c->coeffs[i] = c->coeffs[b];
+ c->coeffs[b] = 1 - (int32_t)(2 * (signs & 1));
+ signs >>= 1;
+ }
+
+ lc_hash_zero(hash_ctx);
+}
+
+/**
+ * @brief polyeta_pack - Bit-pack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYETA_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyeta_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint8_t t[8];
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 1]);
+ t[2] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 2]);
+ t[3] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 3]);
+ t[4] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 4]);
+ t[5] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 5]);
+ t[6] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 6]);
+ t[7] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[8 * i + 7]);
+
+ r[3 * i + 0] =
+ (uint8_t)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[3 * i + 1] = (uint8_t)((t[2] >> 2) | (t[3] << 1) |
+ (t[4] << 4) | (t[5] << 7));
+ r[3 * i + 2] =
+ (uint8_t)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 0]);
+ t[1] = (uint8_t)(LC_DILITHIUM_ETA - a->coeffs[2 * i + 1]);
+ r[i] = (uint8_t)(t[0] | (t[1] << 4));
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyeta_unpack - Unpack polynomial with coefficients in [-ETA,ETA].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyeta_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_ETA == 2
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = (a[3 * i + 0] >> 0) & 7;
+ r->coeffs[8 * i + 1] = (a[3 * i + 0] >> 3) & 7;
+ r->coeffs[8 * i + 2] =
+ ((a[3 * i + 0] >> 6) | (a[3 * i + 1] << 2)) & 7;
+ r->coeffs[8 * i + 3] = (a[3 * i + 1] >> 1) & 7;
+ r->coeffs[8 * i + 4] = (a[3 * i + 1] >> 4) & 7;
+ r->coeffs[8 * i + 5] =
+ ((a[3 * i + 1] >> 7) | (a[3 * i + 2] << 1)) & 7;
+ r->coeffs[8 * i + 6] = (a[3 * i + 2] >> 2) & 7;
+ r->coeffs[8 * i + 7] = (a[3 * i + 2] >> 5) & 7;
+
+ r->coeffs[8 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] = LC_DILITHIUM_ETA - r->coeffs[8 * i + 7];
+ }
+#elif LC_DILITHIUM_ETA == 4
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[i] & 0x0F;
+ r->coeffs[2 * i + 1] = a[i] >> 4;
+ r->coeffs[2 * i + 0] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] = LC_DILITHIUM_ETA - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined LC_DILITHIUM_ETA"
+#endif
+}
+
+/**
+ * @brief polyt1_pack - Bit-pack polynomial t1 with coefficients fitting in 10
+ * bits. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyt1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0));
+ r[5 * i + 1] = (uint8_t)((a->coeffs[4 * i + 0] >> 8) |
+ (a->coeffs[4 * i + 1] << 2));
+ r[5 * i + 2] = (uint8_t)((a->coeffs[4 * i + 1] >> 6) |
+ (a->coeffs[4 * i + 2] << 4));
+ r[5 * i + 3] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) |
+ (a->coeffs[4 * i + 3] << 6));
+ r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2));
+ }
+}
+
+/**
+ * @brief polyt0_pack - Bit-pack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYT0_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyt0_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[8];
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ t[0] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 0]);
+ t[1] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 1]);
+ t[2] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 2]);
+ t[3] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 3]);
+ t[4] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 4]);
+ t[5] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 5]);
+ t[6] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 6]);
+ t[7] = (uint32_t)((1 << (LC_DILITHIUM_D - 1)) -
+ a->coeffs[8 * i + 7]);
+
+ r[13 * i + 0] = (uint8_t)(t[0]);
+ r[13 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[13 * i + 1] |= (uint8_t)(t[1] << 5);
+ r[13 * i + 2] = (uint8_t)(t[1] >> 3);
+ r[13 * i + 3] = (uint8_t)(t[1] >> 11);
+ r[13 * i + 3] |= (uint8_t)(t[2] << 2);
+ r[13 * i + 4] = (uint8_t)(t[2] >> 6);
+ r[13 * i + 4] |= (uint8_t)(t[3] << 7);
+ r[13 * i + 5] = (uint8_t)(t[3] >> 1);
+ r[13 * i + 6] = (uint8_t)(t[3] >> 9);
+ r[13 * i + 6] |= (uint8_t)(t[4] << 4);
+ r[13 * i + 7] = (uint8_t)(t[4] >> 4);
+ r[13 * i + 8] = (uint8_t)(t[4] >> 12);
+ r[13 * i + 8] |= (uint8_t)(t[5] << 1);
+ r[13 * i + 9] = (uint8_t)(t[5] >> 7);
+ r[13 * i + 9] |= (uint8_t)(t[6] << 6);
+ r[13 * i + 10] = (uint8_t)(t[6] >> 2);
+ r[13 * i + 11] = (uint8_t)(t[6] >> 10);
+ r[13 * i + 11] |= (uint8_t)(t[7] << 3);
+ r[13 * i + 12] = (uint8_t)(t[7] >> 5);
+ }
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyt0_unpack - Unpack polynomial t0 with coefficients in
+ * ]-2^{D-1}, 2^{D-1}].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyt0_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+ for (i = 0; i < LC_DILITHIUM_N / 8; ++i) {
+ r->coeffs[8 * i + 0] = a[13 * i + 0];
+ r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
+ r->coeffs[8 * i + 0] &= 0x1FFF;
+
+ r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
+ r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
+ r->coeffs[8 * i + 1] &= 0x1FFF;
+
+ r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
+ r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
+ r->coeffs[8 * i + 2] &= 0x1FFF;
+
+ r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
+ r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
+ r->coeffs[8 * i + 3] &= 0x1FFF;
+
+ r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
+ r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
+ r->coeffs[8 * i + 4] &= 0x1FFF;
+
+ r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
+ r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
+ r->coeffs[8 * i + 5] &= 0x1FFF;
+
+ r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
+ r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
+ r->coeffs[8 * i + 6] &= 0x1FFF;
+
+ r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
+ r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
+ r->coeffs[8 * i + 7] &= 0x1FFF;
+
+ r->coeffs[8 * i + 0] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 0];
+ r->coeffs[8 * i + 1] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 1];
+ r->coeffs[8 * i + 2] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 2];
+ r->coeffs[8 * i + 3] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 3];
+ r->coeffs[8 * i + 4] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 4];
+ r->coeffs[8 * i + 5] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 5];
+ r->coeffs[8 * i + 6] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 6];
+ r->coeffs[8 * i + 7] =
+ (1 << (LC_DILITHIUM_D - 1)) - r->coeffs[8 * i + 7];
+ }
+}
+
+/**
+ * @param polyz_pack - Bit-pack polynomial with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYZ_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyz_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+ uint32_t t[4];
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 1]);
+ t[2] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 2]);
+ t[3] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[4 * i + 3]);
+
+ r[9 * i + 0] = (uint8_t)(t[0]);
+ r[9 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[9 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[9 * i + 2] |= (uint8_t)(t[1] << 2);
+ r[9 * i + 3] = (uint8_t)(t[1] >> 6);
+ r[9 * i + 4] = (uint8_t)(t[1] >> 14);
+ r[9 * i + 4] |= (uint8_t)(t[2] << 4);
+ r[9 * i + 5] = (uint8_t)(t[2] >> 4);
+ r[9 * i + 6] = (uint8_t)(t[2] >> 12);
+ r[9 * i + 6] |= (uint8_t)(t[3] << 6);
+ r[9 * i + 7] = (uint8_t)(t[3] >> 2);
+ r[9 * i + 8] = (uint8_t)(t[3] >> 10);
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ t[0] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 0]);
+ t[1] = (uint32_t)(LC_DILITHIUM_GAMMA1 - a->coeffs[2 * i + 1]);
+
+ r[5 * i + 0] = (uint8_t)(t[0]);
+ r[5 * i + 1] = (uint8_t)(t[0] >> 8);
+ r[5 * i + 2] = (uint8_t)(t[0] >> 16);
+ r[5 * i + 2] |= (uint8_t)(t[1] << 4);
+ r[5 * i + 3] = (uint8_t)(t[1] >> 4);
+ r[5 * i + 4] = (uint8_t)(t[1] >> 12);
+ }
+#else
+#error "Undefined Gamma"
+#endif
+
+ lc_memset_secure(t, 0, sizeof(t));
+}
+
+/**
+ * @brief polyz_unpack - Unpack polynomial z with coefficients
+ * in [-(GAMMA1 - 1), GAMMA1].
+ *
+ * @param [out] r pointer to output polynomial
+ * @param [in] a byte array with bit-packed polynomial
+ */
+void polyz_unpack(poly *r, const uint8_t *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA1 == (1 << 17)
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r->coeffs[4 * i + 0] = a[9 * i + 0];
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 1] << 8;
+ r->coeffs[4 * i + 0] |= (int32_t)a[9 * i + 2] << 16;
+ r->coeffs[4 * i + 0] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 1] = a[9 * i + 2] >> 2;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 3] << 6;
+ r->coeffs[4 * i + 1] |= (int32_t)a[9 * i + 4] << 14;
+ r->coeffs[4 * i + 1] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 2] = a[9 * i + 4] >> 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 5] << 4;
+ r->coeffs[4 * i + 2] |= (int32_t)a[9 * i + 6] << 12;
+ r->coeffs[4 * i + 2] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 3] = a[9 * i + 6] >> 6;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 7] << 2;
+ r->coeffs[4 * i + 3] |= (int32_t)a[9 * i + 8] << 10;
+ r->coeffs[4 * i + 3] &= 0x3FFFF;
+
+ r->coeffs[4 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 0];
+ r->coeffs[4 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 1];
+ r->coeffs[4 * i + 2] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 2];
+ r->coeffs[4 * i + 3] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[4 * i + 3];
+ }
+#elif LC_DILITHIUM_GAMMA1 == (1 << 19)
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i) {
+ r->coeffs[2 * i + 0] = a[5 * i + 0];
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 1] << 8;
+ r->coeffs[2 * i + 0] |= (int32_t)a[5 * i + 2] << 16;
+ r->coeffs[2 * i + 0] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 1] = a[5 * i + 2] >> 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 3] << 4;
+ r->coeffs[2 * i + 1] |= (int32_t)a[5 * i + 4] << 12;
+ r->coeffs[2 * i + 1] &= 0xFFFFF;
+
+ r->coeffs[2 * i + 0] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 0];
+ r->coeffs[2 * i + 1] =
+ LC_DILITHIUM_GAMMA1 - r->coeffs[2 * i + 1];
+ }
+#else
+#error "Undefined Gamma"
+#endif
+}
+
+/**
+ * @brief polyw1_pack - Bit-pack polynomial w1 with coefficients in [0,15] or
+ * [0,43]. Input coefficients are assumed to be standard
+ * representatives.
+ *
+ * @param [out] r pointer to output byte array with at least
+ * LC_DILITHIUM_POLYW1_PACKEDBYTES bytes
+ * @param [in] a pointer to input polynomial
+ */
+void polyw1_pack(uint8_t *r, const poly *a)
+{
+ unsigned int i;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ for (i = 0; i < LC_DILITHIUM_N / 4; ++i) {
+ r[3 * i + 0] = (uint8_t)(a->coeffs[4 * i + 0]);
+ r[3 * i + 0] |= (uint8_t)(a->coeffs[4 * i + 1] << 6);
+ r[3 * i + 1] = (uint8_t)(a->coeffs[4 * i + 1] >> 2);
+ r[3 * i + 1] |= (uint8_t)(a->coeffs[4 * i + 2] << 4);
+ r[3 * i + 2] = (uint8_t)(a->coeffs[4 * i + 2] >> 4);
+ r[3 * i + 2] |= (uint8_t)(a->coeffs[4 * i + 3] << 2);
+ }
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ for (i = 0; i < LC_DILITHIUM_N / 2; ++i)
+ r[i] = (uint8_t)(a->coeffs[2 * i + 0] |
+ (a->coeffs[2 * i + 1] << 4));
+#else
+#error "Undefined Gamma"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_rounding.c b/lib/freebl/leancrypto/ml_dsa_87_rounding.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_rounding.c
@@ -0,0 +1,130 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_rounding.h"
+
+/**
+ * @brief power2round - For finite field element a, compute a0, a1 such that
+ * a mod^+ Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t power2round(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + (1 << (LC_DILITHIUM_D - 1)) - 1) >> LC_DILITHIUM_D;
+ *a0 = a - (a1 << LC_DILITHIUM_D);
+ return a1;
+}
+
+/**
+ * @brief decompose - For finite field element a, compute high and low bits a0,
+ * a1 such that a mod^+ Q = a1*ALPHA + a0 with
+ * -ALPHA/2 < a0 <= ALPHA/2 except if a1 = (Q-1)/ALPHA where
+ * we set a1 = 0 and -ALPHA/2 <= a0 = a mod^+ Q - Q < 0.
+ * Assumes a to be standard representative.
+ *
+ * @param [in] a input element
+ * @param [out] a0 pointer to output element a0
+ *
+ * @return a1.
+ */
+int32_t decompose(int32_t *a0, int32_t a)
+{
+ int32_t a1;
+
+ a1 = (a + 127) >> 7;
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+#else
+#error "Uknown GAMMA2"
+#endif
+
+ *a0 = a - a1 * 2 * LC_DILITHIUM_GAMMA2;
+ *a0 -= (((LC_DILITHIUM_Q - 1) / 2 - *a0) >> 31) & LC_DILITHIUM_Q;
+
+ return a1;
+}
+
+/**
+ * @brief make_hint - Compute hint bit indicating whether the low bits of the
+ * input element overflow into the high bits.
+ *
+ * @param a0 [in] low bits of input element
+ * @param a1 [in] high bits of input element
+ *
+ * @return 1 if overflow.
+ */
+int32_t make_hint(int32_t a0, int32_t a1)
+{
+ if (a0 > LC_DILITHIUM_GAMMA2 || a0 < -LC_DILITHIUM_GAMMA2 ||
+ (a0 == -LC_DILITHIUM_GAMMA2 && a1 != 0))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * @brief use_hint - Correct high bits according to hint.
+ *
+ * @param [in] a input element
+ * @param [in] hint hint bit
+ *
+ * @return corrected high bits.
+ */
+int32_t use_hint(int32_t a, int32_t hint)
+{
+ int32_t a0, a1;
+
+ a1 = decompose(&a0, a);
+ if (hint == 0)
+ return a1;
+
+#if LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 32
+ if (a0 > 0)
+ return (a1 + 1) & 15;
+ else
+ return (a1 - 1) & 15;
+#elif LC_DILITHIUM_GAMMA2 == (LC_DILITHIUM_Q - 1) / 88
+ if (a0 > 0)
+ return (a1 == 43) ? 0 : a1 + 1;
+ else
+ return (a1 == 0) ? 43 : a1 - 1;
+#else
+#error "Uknown GAMMA2"
+#endif
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_signature_c.c b/lib/freebl/leancrypto/ml_dsa_87_signature_c.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_signature_c.c
@@ -0,0 +1,131 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_signature_c.h"
+#include "visibility.h"
+
+/* We need once the buffer size to handle the hashing */
+#define LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER 1
+
+#include "dilithium_poly.h"
+#include "dilithium_poly_common.h"
+#include "dilithium_poly_c.h"
+#include "dilithium_polyvec.h"
+#include "dilithium_polyvec_c.h"
+#include "dilithium_pack.h"
+#include "dilithium_signature_impl.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_from_seed_c,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen)
+{
+ return lc_dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_c, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_keypair_impl(pk, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_c, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_impl(sig, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_ctx_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_ctx_impl(sig, ctx, m, mlen, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ return lc_dilithium_sign_init_impl(ctx, sk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_sign_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_final_c,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ return lc_dilithium_sign_final_impl(sig, ctx, sk, rng_ctx);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_c,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_impl(sig, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_ctx_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_ctx_impl(sig, ctx, m, mlen, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_init_c,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_init_impl(ctx, pk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_update_c,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ return lc_dilithium_verify_update_impl(ctx, m, mlen);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_final_c,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ return lc_dilithium_verify_final_impl(sig, ctx, pk);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_87_signature_helper.c b/lib/freebl/leancrypto/ml_dsa_87_signature_helper.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_87_signature_helper.c
@@ -0,0 +1,103 @@
+#define LC_DILITHIUM_TYPE_87 1
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "dilithium_type.h"
+#include "visibility.h"
+
+#include "lc_sha3.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE);
+ if (ret)
+ return -ret;
+
+ LC_DILITHIUM_SET_CTX(out_ctx);
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ struct lc_dilithium_ctx *out_ctx = NULL;
+ int ret;
+
+ if (!ctx)
+ return -EINVAL;
+
+#if LC_DILITHIUM_MODE == 2
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD +
+ LC_DILITHIUM_44_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_44_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_44_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 3
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD +
+ LC_DILITHIUM_65_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_65_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_65_AHAT_SIZE;
+#elif LC_DILITHIUM_MODE == 5
+ ret = lc_alloc_aligned((void **)&out_ctx, LC_HASH_COMMON_ALIGNMENT,
+ LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD +
+ LC_DILITHIUM_87_AHAT_SIZE);
+ if (ret)
+ return -ret;
+ out_ctx->ahat = (uint8_t *)out_ctx + LC_DILITHIUM_CTX_SIZE +
+ LC_DILITHIUM_87_AHAT_PAD;
+ out_ctx->ahat_size = LC_DILITHIUM_87_AHAT_SIZE;
+#endif
+
+ LC_SHAKE_256_CTX((&(out_ctx)->dilithium_hash_ctx));
+
+ *ctx = out_ctx;
+
+ return 0;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ lc_dilithium_ctx_zero(ctx);
+ lc_free(ctx);
+}
diff --git a/lib/freebl/leancrypto/ml_dsa_api.h b/lib/freebl/leancrypto/ml_dsa_api.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_api.h
@@ -0,0 +1,71 @@
+#ifndef ML_DSA_API_H
+#define ML_DSA_API_H
+// This is a generated file from the various XXX_sign.h files
+#include <stddef.h>
+#include "ml_dsa_apit.h"
+
+// from ml_dsa_44_sign.h
+int lc_dilithium_44_keypair_from_seed_c(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_44_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk);
+int lc_dilithium_44_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_sign_final_c(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+int lc_dilithium_44_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_verify_final_c(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+// from ml_dsa_65_sign.h
+int lc_dilithium_65_keypair_from_seed_c(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_65_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk);
+int lc_dilithium_65_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_sign_final_c(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+int lc_dilithium_65_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_verify_final_c(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+// from ml_dsa_87_sign.h
+int lc_dilithium_87_keypair_from_seed_c(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_87_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk);
+int lc_dilithium_87_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_sign_final_c(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+int lc_dilithium_87_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_verify_final_c(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+#endif /* ML_DSA_API_H */
diff --git a/lib/freebl/leancrypto/ml_dsa_apit.h b/lib/freebl/leancrypto/ml_dsa_apit.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ml_dsa_apit.h
@@ -0,0 +1,10 @@
+// This is a generated file from the various XXX_sign.h files
+#ifndef ML_DSA_APIT_H
+#define ML_DSA_APIT_H
+
+// to make the function defines work
+#ifndef RNDBYTES
+#define RNDBYTES 32
+#endif
+
+#endif /* ML_DSA_APIT_H */
diff --git a/lib/freebl/leancrypto/mldsa_api.c b/lib/freebl/leancrypto/mldsa_api.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/mldsa_api.c
@@ -0,0 +1,2535 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "ext_headers.h"
+#include "lc_dilithium.h"
+#include "dilithium_pct.h"
+#include "visibility.h"
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc,
+ struct lc_dilithium_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ctx_alloc(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ctx_alloc_ahat,
+ struct lc_dilithium_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ctx_alloc_ahat(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ctx_alloc_ahat(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ctx_alloc_ahat(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero_free,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ctx_zero_free(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_zero, struct lc_dilithium_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ctx_zero(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_internal,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (ctx)
+ ctx->ml_dsa_internal = 1;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_hash, struct lc_dilithium_ctx *ctx,
+ const struct lc_hash *hash)
+{
+ if (ctx)
+ ctx->dilithium_prehash_type = hash;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_userctx,
+ struct lc_dilithium_ctx *ctx, const uint8_t *userctx,
+ size_t userctxlen)
+{
+ if (ctx) {
+ ctx->userctx = userctx;
+ ctx->userctxlen = userctxlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_external_mu,
+ struct lc_dilithium_ctx *ctx, const uint8_t *external_mu,
+ size_t external_mu_len)
+{
+ if (ctx) {
+ ctx->external_mu = external_mu;
+ ctx->external_mu_len = external_mu_len;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ctx_drop_ahat,
+ struct lc_dilithium_ctx *ctx)
+{
+ if (ctx)
+ ctx->ahat_expanded = 0;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_sk_type,
+ const struct lc_dilithium_sk *sk)
+{
+ if (!sk)
+ return LC_DILITHIUM_UNKNOWN;
+ return sk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_pk_type,
+ const struct lc_dilithium_pk *pk)
+{
+ if (!pk)
+ return LC_DILITHIUM_UNKNOWN;
+ return pk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_sig_type,
+ const struct lc_dilithium_sig *sig)
+{
+ if (!sig)
+ return LC_DILITHIUM_UNKNOWN;
+ return sig->dilithium_type;
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_sk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_sk, key.sk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_sk, key.sk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_sk, key.sk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_pk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_pk, key.pk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_pk, key.pk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_pk, key.pk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_sig_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_sig, sig.sig_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_sig, sig.sig_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_sig, sig.sig_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sk_load, struct lc_dilithium_sk *sk,
+ const uint8_t *src_key, size_t src_key_len)
+{
+ if (!sk || !src_key || src_key_len == 0) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (src_key_len == lc_dilithium_sk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_sk *_sk = &sk->key.sk_87;
+
+ memcpy(_sk->sk, src_key, src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (src_key_len == lc_dilithium_sk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_sk *_sk = &sk->key.sk_65;
+
+ memcpy(_sk->sk, src_key, src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (src_key_len == lc_dilithium_sk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_sk *_sk = &sk->key.sk_44;
+
+ memcpy(_sk->sk, src_key, src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_pk_load, struct lc_dilithium_pk *pk,
+ const uint8_t *src_key, size_t src_key_len)
+{
+ if (!pk || !src_key || src_key_len == 0) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (src_key_len == lc_dilithium_pk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_pk *_pk = &pk->key.pk_87;
+
+ memcpy(_pk->pk, src_key, src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (src_key_len == lc_dilithium_pk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_pk *_pk = &pk->key.pk_65;
+
+ memcpy(_pk->pk, src_key, src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (src_key_len == lc_dilithium_pk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_pk *_pk = &pk->key.pk_44;
+
+ memcpy(_pk->pk, src_key, src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sig_load, struct lc_dilithium_sig *sig,
+ const uint8_t *src_sig, size_t src_sig_len)
+{
+ if (!sig || !src_sig || src_sig_len == 0) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (src_sig_len == lc_dilithium_sig_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_sig *_sig = &sig->sig.sig_87;
+
+ memcpy(_sig->sig, src_sig, src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (src_sig_len == lc_dilithium_sig_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_sig *_sig = &sig->sig.sig_65;
+
+ memcpy(_sig->sig, src_sig, src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (src_sig_len == lc_dilithium_sig_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_sig *_sig = &sig->sig.sig_44;
+
+ memcpy(_sig->sig, src_sig, src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, struct lc_dilithium_sk *sk)
+{
+ if (!sk || !dilithium_key || !dilithium_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_sk *_sk = &sk->key.sk_87;
+
+ *dilithium_key = _sk->sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_sk *_sk = &sk->key.sk_65;
+
+ *dilithium_key = _sk->sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_sk *_sk = &sk->key.sk_44;
+
+ *dilithium_key = _sk->sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_pk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, struct lc_dilithium_pk *pk)
+{
+ if (!pk || !dilithium_key || !dilithium_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_pk *_pk = &pk->key.pk_87;
+
+ *dilithium_key = _pk->pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_pk *_pk = &pk->key.pk_65;
+
+ *dilithium_key = _pk->pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_pk *_pk = &pk->key.pk_44;
+
+ *dilithium_key = _pk->pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sig_ptr, uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len, struct lc_dilithium_sig *sig)
+{
+ if (!sig || !dilithium_sig || !dilithium_sig_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_sig *_sig = &sig->sig.sig_87;
+
+ *dilithium_sig = _sig->sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_sig *_sig = &sig->sig.sig_65;
+
+ *dilithium_sig = _sig->sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_sig *_sig = &sig->sig.sig_44;
+
+ *dilithium_sig = _sig->sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair, struct lc_dilithium_pk *pk,
+ struct lc_dilithium_sk *sk, struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_keypair(&pk->key.pk_87, &sk->key.sk_87,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_keypair(&pk->key.pk_65, &sk->key.sk_65,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_keypair(&pk->key.pk_44, &sk->key.sk_44,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_keypair_from_seed,
+ struct lc_dilithium_pk *pk, struct lc_dilithium_sk *sk,
+ const uint8_t *seed, size_t seedlen,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_keypair_from_seed(
+ &pk->key.pk_87, &sk->key.sk_87, seed, seedlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_keypair_from_seed(
+ &pk->key.pk_65, &sk->key.sk_65, seed, seedlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_keypair_from_seed(
+ &pk->key.pk_44, &sk->key.sk_44, seed, seedlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_pct, const struct lc_dilithium_pk *pk,
+ const struct lc_dilithium_sk *sk)
+{
+ return _lc_dilithium_pct_fips(pk, sk);
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign, struct lc_dilithium_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_sign(&sig->sig.sig_87, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_sign(&sig->sig.sig_65, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_sign(&sig->sig.sig_44, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_ctx, struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_sign_ctx(&sig->sig.sig_87, ctx, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_sign_ctx(&sig->sig.sig_65, ctx, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_sign_ctx(&sig->sig.sig_44, ctx, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_init, struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk)
+{
+ if (!sk)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_sign_init(ctx, &sk->key.sk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_sign_init(ctx, &sk->key.sk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_sign_init(ctx, &sk->key.sk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_update,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_sign_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_sign_final,
+ struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_sign_final(&sig->sig.sig_87, ctx,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_sign_final(&sig->sig.sig_65, ctx,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_sign_final(&sig->sig.sig_44, ctx,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify,
+ const struct lc_dilithium_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify(&sig->sig.sig_87, m, mlen,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify(&sig->sig.sig_65, m, mlen,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify(&sig->sig.sig_44, m, mlen,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_ctx,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_ctx(&sig->sig.sig_87, ctx, m,
+ mlen, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify_ctx(&sig->sig.sig_65, ctx, m,
+ mlen, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify_ctx(&sig->sig.sig_44, ctx, m,
+ mlen, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_init,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ if (!pk)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_init(ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify_init(ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify_init(ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_update,
+ struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_verify_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_verify_final,
+ const struct lc_dilithium_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_verify_final(&sig->sig.sig_87, ctx,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_verify_final(&sig->sig.sig_65, ctx,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_verify_final(&sig->sig.sig_44, ctx,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED25519_SIG
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_ctx_alloc,
+ struct lc_dilithium_ed25519_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed25519_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed25519_ctx_alloc(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_zero_free,
+ struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed25519_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed25519_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed25519_ctx_zero_free(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_zero,
+ struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed25519_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed25519_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed25519_ctx_zero(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_hash,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_hash *hash)
+{
+ if (ctx)
+ ctx->dilithium_ctx.dilithium_prehash_type = hash;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_internal,
+ struct lc_dilithium_ed25519_ctx *ctx)
+{
+ if (ctx)
+ ctx->dilithium_ctx.ml_dsa_internal = 1;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_userctx,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.userctx = userctx;
+ ctx->dilithium_ctx.userctxlen = userctxlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed25519_ctx_randomizer,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const uint8_t *randomizer, size_t randomizerlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.randomizer = randomizer;
+ ctx->dilithium_ctx.randomizerlen = randomizerlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed25519_sk_type,
+ const struct lc_dilithium_ed25519_sk *sk)
+{
+ if (!sk)
+ return LC_DILITHIUM_UNKNOWN;
+ return sk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed25519_pk_type,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk)
+ return LC_DILITHIUM_UNKNOWN;
+ return pk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed25519_sig_type,
+ const struct lc_dilithium_ed25519_sig *sig)
+{
+ if (!sig)
+ return LC_DILITHIUM_UNKNOWN;
+ return sig->dilithium_type;
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed25519_sk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sk,
+ key.sk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sk,
+ key.sk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sk,
+ key.sk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed25519_pk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_pk,
+ key.pk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_pk,
+ key.pk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_pk,
+ key.pk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed25519_sig_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sig,
+ sig.sig_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sig,
+ sig.sig_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed25519_sig,
+ sig.sig_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sk_load,
+ struct lc_dilithium_ed25519_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len)
+{
+ if (!sk || !dilithium_src_key || !ed25519_src_key ||
+ ed25519_src_key_len != LC_ED25519_SECRETKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed25519_sk *_sk = &sk->key.sk_87;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed25519.sk, ed25519_src_key,
+ ed25519_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed25519_sk *_sk = &sk->key.sk_65;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed25519.sk, ed25519_src_key,
+ ed25519_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed25519_sk *_sk = &sk->key.sk_44;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed25519.sk, ed25519_src_key,
+ ed25519_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_pk_load,
+ struct lc_dilithium_ed25519_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed25519_src_key,
+ size_t ed25519_src_key_len)
+{
+ if (!pk || !dilithium_src_key || !ed25519_src_key ||
+ ed25519_src_key_len != LC_ED25519_PUBLICKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed25519_pk *_pk = &pk->key.pk_87;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed25519.pk, ed25519_src_key,
+ ed25519_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed25519_pk *_pk = &pk->key.pk_65;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed25519.pk, ed25519_src_key,
+ ed25519_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed25519_pk *_pk = &pk->key.pk_44;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed25519.pk, ed25519_src_key,
+ ed25519_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sig_load,
+ struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed25519_src_sig,
+ size_t ed25519_src_sig_len)
+{
+ if (!sig || !dilithium_src_sig || !ed25519_src_sig ||
+ ed25519_src_sig_len != LC_ED25519_SIGBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed25519_sig *_sig = &sig->sig.sig_87;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed25519.sig, ed25519_src_sig,
+ ed25519_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed25519_sig *_sig = &sig->sig.sig_65;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed25519.sig, ed25519_src_sig,
+ ed25519_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed25519_sig *_sig = &sig->sig.sig_44;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed25519.sig, ed25519_src_sig,
+ ed25519_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed25519_key,
+ size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_sk *sk)
+{
+ if (!sk || !dilithium_key || !dilithium_key_len || !ed25519_key ||
+ !ed25519_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed25519_sk *_sk = &sk->key.sk_87;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed25519_key = _sk->sk_ed25519.sk;
+ *ed25519_key_len = LC_ED25519_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed25519_sk *_sk = &sk->key.sk_65;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed25519_key = _sk->sk_ed25519.sk;
+ *ed25519_key_len = LC_ED25519_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed25519_sk *_sk = &sk->key.sk_44;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed25519_key = _sk->sk_ed25519.sk;
+ *ed25519_key_len = LC_ED25519_SECRETKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_pk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed25519_key,
+ size_t *ed25519_key_len,
+ struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !dilithium_key || !dilithium_key_len || !ed25519_key ||
+ !ed25519_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed25519_pk *_pk = &pk->key.pk_87;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed25519_key = _pk->pk_ed25519.pk;
+ *ed25519_key_len = LC_ED25519_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed25519_pk *_pk = &pk->key.pk_65;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed25519_key = _pk->pk_ed25519.pk;
+ *ed25519_key_len = LC_ED25519_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed25519_pk *_pk = &pk->key.pk_44;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed25519_key = _pk->pk_ed25519.pk;
+ *ed25519_key_len = LC_ED25519_PUBLICKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sig_ptr,
+ uint8_t **dilithium_sig, size_t *dilithium_sig_len,
+ uint8_t **ed25519_sig, size_t *ed25519_sig_len,
+ struct lc_dilithium_ed25519_sig *sig)
+{
+ if (!sig || !dilithium_sig || !dilithium_sig_len || !ed25519_sig ||
+ !ed25519_sig_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed25519_sig *_sig = &sig->sig.sig_87;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed25519_sig = _sig->sig_ed25519.sig;
+ *ed25519_sig_len = LC_ED25519_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed25519_sig *_sig = &sig->sig.sig_65;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed25519_sig = _sig->sig_ed25519.sig;
+ *ed25519_sig_len = LC_ED25519_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed25519_sig *_sig = &sig->sig.sig_44;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed25519_sig = _sig->sig_ed25519.sig;
+ *ed25519_sig_len = LC_ED25519_SIGBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_keypair,
+ struct lc_dilithium_ed25519_pk *pk,
+ struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_ed25519_keypair(&pk->key.pk_87,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_ed25519_keypair(&pk->key.pk_65,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_ed25519_keypair(&pk->key.pk_44,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign,
+ struct lc_dilithium_ed25519_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed25519_sign(&sig->sig.sig_87, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed25519_sign(&sig->sig.sig_65, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed25519_sign(&sig->sig.sig_44, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_ctx,
+ struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed25519_sign_ctx(&sig->sig.sig_87, ctx,
+ m, mlen, &sk->key.sk_87,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed25519_sign_ctx(&sig->sig.sig_65, ctx,
+ m, mlen, &sk->key.sk_65,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed25519_sign_ctx(&sig->sig.sig_44, ctx,
+ m, mlen, &sk->key.sk_44,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_init,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk)
+{
+ if (!ctx || !sk)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_sign_init(ctx, &sk->key.sk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_sign_init(ctx, &sk->key.sk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_sign_init(ctx, &sk->key.sk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_update,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed25519_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed25519_sign_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_sign_final,
+ struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig || !ctx)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed25519_sign_final(
+ &sig->sig.sig_87, ctx, &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed25519_sign_final(
+ &sig->sig.sig_65, ctx, &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed25519_sign_final(
+ &sig->sig.sig_44, ctx, &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify,
+ const struct lc_dilithium_ed25519_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify(&sig->sig.sig_87, m, mlen,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify(&sig->sig.sig_65, m, mlen,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify(&sig->sig.sig_44, m, mlen,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_ctx,
+ const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_ctx(
+ &sig->sig.sig_87, ctx, m, mlen, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify_ctx(
+ &sig->sig.sig_65, ctx, m, mlen, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify_ctx(
+ &sig->sig.sig_44, ctx, m, mlen, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_init,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!pk || !ctx)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_init(ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify_init(ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify_init(ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_update,
+ struct lc_dilithium_ed25519_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed25519_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed25519_verify_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed25519_verify_final,
+ const struct lc_dilithium_ed25519_sig *sig,
+ struct lc_dilithium_ed25519_ctx *ctx,
+ const struct lc_dilithium_ed25519_pk *pk)
+{
+ if (!ctx || !pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed25519_verify_final(
+ &sig->sig.sig_87, ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed25519_verify_final(
+ &sig->sig.sig_65, ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed25519_verify_final(
+ &sig->sig.sig_44, ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+#endif /* LC_DILITHIUM_ED25519_SIG */
+
+/****************************** Dilithium ED25510 *****************************/
+
+#ifdef LC_DILITHIUM_ED448_SIG
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_ctx_alloc,
+ struct lc_dilithium_ed448_ctx **ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed448_ctx_alloc(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed448_ctx_alloc(ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_zero_free,
+ struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed448_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed448_ctx_zero_free(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed448_ctx_zero_free(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_zero,
+ struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ lc_dilithium_87_ed448_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ lc_dilithium_65_ed448_ctx_zero(ctx);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ lc_dilithium_44_ed448_ctx_zero(ctx);
+#endif
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_hash,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_hash *hash)
+{
+ if (ctx)
+ ctx->dilithium_ctx.dilithium_prehash_type = hash;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_internal,
+ struct lc_dilithium_ed448_ctx *ctx)
+{
+ if (ctx)
+ ctx->dilithium_ctx.ml_dsa_internal = 1;
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_userctx,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *userctx, size_t userctxlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.userctx = userctx;
+ ctx->dilithium_ctx.userctxlen = userctxlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(void, lc_dilithium_ed448_ctx_randomizer,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const uint8_t *randomizer, size_t randomizerlen)
+{
+ if (ctx) {
+ ctx->dilithium_ctx.randomizer = randomizer;
+ ctx->dilithium_ctx.randomizerlen = randomizerlen;
+ }
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed448_sk_type,
+ const struct lc_dilithium_ed448_sk *sk)
+{
+ if (!sk)
+ return LC_DILITHIUM_UNKNOWN;
+ return sk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed448_pk_type,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk)
+ return LC_DILITHIUM_UNKNOWN;
+ return pk->dilithium_type;
+}
+
+LC_INTERFACE_FUNCTION(enum lc_dilithium_type, lc_dilithium_ed448_sig_type,
+ const struct lc_dilithium_ed448_sig *sig)
+{
+ if (!sig)
+ return LC_DILITHIUM_UNKNOWN;
+ return sig->dilithium_type;
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed448_sk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sk, key.sk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sk, key.sk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sk, key.sk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed448_pk_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_pk, key.pk_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_pk, key.pk_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_pk, key.pk_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_PURE LC_INTERFACE_FUNCTION(unsigned int, lc_dilithium_ed448_sig_size,
+ enum lc_dilithium_type dilithium_type)
+{
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sig,
+ sig.sig_87);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sig,
+ sig.sig_65);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_member_size(struct lc_dilithium_ed448_sig,
+ sig.sig_44);
+#else
+ return 0;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return 0;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sk_load,
+ struct lc_dilithium_ed448_sk *sk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key, size_t ed448_src_key_len)
+{
+ if (!sk || !dilithium_src_key || !ed448_src_key ||
+ ed448_src_key_len != LC_ED448_SECRETKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed448_sk *_sk = &sk->key.sk_87;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed448.sk, ed448_src_key, ed448_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed448_sk *_sk = &sk->key.sk_65;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed448.sk, ed448_src_key, ed448_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_sk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed448_sk *_sk = &sk->key.sk_44;
+
+ memcpy(_sk->sk.sk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_sk->sk_ed448.sk, ed448_src_key, ed448_src_key_len);
+ sk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_pk_load,
+ struct lc_dilithium_ed448_pk *pk,
+ const uint8_t *dilithium_src_key,
+ size_t dilithium_src_key_len,
+ const uint8_t *ed448_src_key, size_t ed448_src_key_len)
+{
+ if (!pk || !dilithium_src_key || !ed448_src_key ||
+ ed448_src_key_len != LC_ED448_PUBLICKEYBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed448_pk *_pk = &pk->key.pk_87;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed448.pk, ed448_src_key, ed448_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed448_pk *_pk = &pk->key.pk_65;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed448.pk, ed448_src_key, ed448_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_key_len ==
+ lc_dilithium_pk_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed448_pk *_pk = &pk->key.pk_44;
+
+ memcpy(_pk->pk.pk, dilithium_src_key, dilithium_src_key_len);
+ memcpy(_pk->pk_ed448.pk, ed448_src_key, ed448_src_key_len);
+ pk->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sig_load,
+ struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *dilithium_src_sig,
+ size_t dilithium_src_sig_len,
+ const uint8_t *ed448_src_sig, size_t ed448_src_sig_len)
+{
+ if (!sig || !dilithium_src_sig || !ed448_src_sig ||
+ ed448_src_sig_len != LC_ED448_SIGBYTES) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_87)) {
+ struct lc_dilithium_87_ed448_sig *_sig = &sig->sig.sig_87;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed448.sig, ed448_src_sig, ed448_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_65)) {
+ struct lc_dilithium_65_ed448_sig *_sig = &sig->sig.sig_65;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed448.sig, ed448_src_sig, ed448_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (dilithium_src_sig_len ==
+ lc_dilithium_sig_size(LC_DILITHIUM_44)) {
+ struct lc_dilithium_44_ed448_sig *_sig = &sig->sig.sig_44;
+
+ memcpy(_sig->sig.sig, dilithium_src_sig, dilithium_src_sig_len);
+ memcpy(_sig->sig_ed448.sig, ed448_src_sig, ed448_src_sig_len);
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len, struct lc_dilithium_ed448_sk *sk)
+{
+ if (!sk || !dilithium_key || !dilithium_key_len || !ed448_key ||
+ !ed448_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed448_sk *_sk = &sk->key.sk_87;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed448_key = _sk->sk_ed448.sk;
+ *ed448_key_len = LC_ED448_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed448_sk *_sk = &sk->key.sk_65;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed448_key = _sk->sk_ed448.sk;
+ *ed448_key_len = LC_ED448_SECRETKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed448_sk *_sk = &sk->key.sk_44;
+
+ *dilithium_key = _sk->sk.sk;
+ *dilithium_key_len = lc_dilithium_sk_size(sk->dilithium_type);
+ *ed448_key = _sk->sk_ed448.sk;
+ *ed448_key_len = LC_ED448_SECRETKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_pk_ptr, uint8_t **dilithium_key,
+ size_t *dilithium_key_len, uint8_t **ed448_key,
+ size_t *ed448_key_len, struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !dilithium_key || !dilithium_key_len || !ed448_key ||
+ !ed448_key_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed448_pk *_pk = &pk->key.pk_87;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed448_key = _pk->pk_ed448.pk;
+ *ed448_key_len = LC_ED448_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed448_pk *_pk = &pk->key.pk_65;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed448_key = _pk->pk_ed448.pk;
+ *ed448_key_len = LC_ED448_PUBLICKEYBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (pk->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed448_pk *_pk = &pk->key.pk_44;
+
+ *dilithium_key = _pk->pk.pk;
+ *dilithium_key_len = lc_dilithium_pk_size(pk->dilithium_type);
+ *ed448_key = _pk->pk_ed448.pk;
+ *ed448_key_len = LC_ED448_PUBLICKEYBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sig_ptr, uint8_t **dilithium_sig,
+ size_t *dilithium_sig_len, uint8_t **ed448_sig,
+ size_t *ed448_sig_len, struct lc_dilithium_ed448_sig *sig)
+{
+ if (!sig || !dilithium_sig || !dilithium_sig_len || !ed448_sig ||
+ !ed448_sig_len) {
+ return -EINVAL;
+#ifdef LC_DILITHIUM_87_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_87) {
+ struct lc_dilithium_87_ed448_sig *_sig = &sig->sig.sig_87;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed448_sig = _sig->sig_ed448.sig;
+ *ed448_sig_len = LC_ED448_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_65_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_65) {
+ struct lc_dilithium_65_ed448_sig *_sig = &sig->sig.sig_65;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed448_sig = _sig->sig_ed448.sig;
+ *ed448_sig_len = LC_ED448_SIGBYTES;
+ return 0;
+#endif
+#ifdef LC_DILITHIUM_44_ENABLED
+ } else if (sig->dilithium_type == LC_DILITHIUM_44) {
+ struct lc_dilithium_44_ed448_sig *_sig = &sig->sig.sig_44;
+
+ *dilithium_sig = _sig->sig.sig;
+ *dilithium_sig_len = lc_dilithium_sig_size(sig->dilithium_type);
+ *ed448_sig = _sig->sig_ed448.sig;
+ *ed448_sig_len = LC_ED448_SIGBYTES;
+ return 0;
+#endif
+ } else {
+ return -EINVAL;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_keypair,
+ struct lc_dilithium_ed448_pk *pk,
+ struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx,
+ enum lc_dilithium_type dilithium_type)
+{
+ if (!pk || !sk)
+ return -EINVAL;
+
+ switch (dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_87_ed448_keypair(&pk->key.pk_87,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_65_ed448_keypair(&pk->key.pk_65,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ pk->dilithium_type = dilithium_type;
+ sk->dilithium_type = dilithium_type;
+ return lc_dilithium_44_ed448_keypair(&pk->key.pk_44,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign,
+ struct lc_dilithium_ed448_sig *sig, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed448_sign(&sig->sig.sig_87, m, mlen,
+ &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed448_sign(&sig->sig.sig_65, m, mlen,
+ &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed448_sign(&sig->sig.sig_44, m, mlen,
+ &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_ctx,
+ struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed448_sign_ctx(&sig->sig.sig_87, ctx, m,
+ mlen, &sk->key.sk_87,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed448_sign_ctx(&sig->sig.sig_65, ctx, m,
+ mlen, &sk->key.sk_65,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed448_sign_ctx(&sig->sig.sig_44, ctx, m,
+ mlen, &sk->key.sk_44,
+ rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_init,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk)
+{
+ if (!ctx || !sk)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_sign_init(ctx, &sk->key.sk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_sign_init(ctx, &sk->key.sk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_sign_init(ctx, &sk->key.sk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_update,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed448_sign_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed448_sign_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_sign_final,
+ struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_sk *sk,
+ struct lc_rng_ctx *rng_ctx)
+{
+ if (!sk || !sig || !ctx)
+ return -EINVAL;
+
+ switch (sk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_87;
+ return lc_dilithium_87_ed448_sign_final(
+ &sig->sig.sig_87, ctx, &sk->key.sk_87, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_65;
+ return lc_dilithium_65_ed448_sign_final(
+ &sig->sig.sig_65, ctx, &sk->key.sk_65, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ sig->dilithium_type = LC_DILITHIUM_44;
+ return lc_dilithium_44_ed448_sign_final(
+ &sig->sig.sig_44, ctx, &sk->key.sk_44, rng_ctx);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify,
+ const struct lc_dilithium_ed448_sig *sig,
+ const uint8_t *m, size_t mlen,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify(&sig->sig.sig_87, m, mlen,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify(&sig->sig.sig_65, m, mlen,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify(&sig->sig.sig_44, m, mlen,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_ctx,
+ const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen, const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_ctx(
+ &sig->sig.sig_87, ctx, m, mlen, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify_ctx(
+ &sig->sig.sig_65, ctx, m, mlen, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify_ctx(
+ &sig->sig.sig_44, ctx, m, mlen, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_init,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!pk || !ctx)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_init(ctx, &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify_init(ctx, &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify_init(ctx, &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_update,
+ struct lc_dilithium_ed448_ctx *ctx, const uint8_t *m,
+ size_t mlen)
+{
+ if (!ctx)
+ return -EINVAL;
+
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_65_ENABLED)
+ return lc_dilithium_65_ed448_verify_update(ctx, m, mlen);
+#elif defined(LC_DILITHIUM_44_ENABLED)
+ return lc_dilithium_44_ed448_verify_update(ctx, m, mlen);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+LC_INTERFACE_FUNCTION(int, lc_dilithium_ed448_verify_final,
+ const struct lc_dilithium_ed448_sig *sig,
+ struct lc_dilithium_ed448_ctx *ctx,
+ const struct lc_dilithium_ed448_pk *pk)
+{
+ if (!ctx || !pk || !sig || sig->dilithium_type != pk->dilithium_type)
+ return -EINVAL;
+
+ switch (pk->dilithium_type) {
+ case LC_DILITHIUM_87:
+#ifdef LC_DILITHIUM_87_ENABLED
+ return lc_dilithium_87_ed448_verify_final(&sig->sig.sig_87, ctx,
+ &pk->key.pk_87);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_65:
+#ifdef LC_DILITHIUM_65_ENABLED
+ return lc_dilithium_65_ed448_verify_final(&sig->sig.sig_65, ctx,
+ &pk->key.pk_65);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_44:
+#ifdef LC_DILITHIUM_44_ENABLED
+ return lc_dilithium_44_ed448_verify_final(&sig->sig.sig_44, ctx,
+ &pk->key.pk_44);
+#else
+ return -EOPNOTSUPP;
+#endif
+ case LC_DILITHIUM_UNKNOWN:
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+#endif /* LC_DILITHIUM_ED448_SIG */
diff --git a/lib/freebl/leancrypto/mldsa_zetas.c b/lib/freebl/leancrypto/mldsa_zetas.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/mldsa_zetas.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/dilithium
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/);
+ * or Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0.html).
+ */
+
+#include "dilithium_zetas.h"
+#include "ext_headers.h"
+
+LC_FIPS_RODATA_SECTION
+const int32_t dilithium_zetas[LC_DILITHIUM_N] = {
+ 0, 25847, -2608894, -518909, 237124, -777960, -876248,
+ 466468, 1826347, 2353451, -359251, -2091905, 3119733, -2884855,
+ 3111497, 2680103, 2725464, 1024112, -1079900, 3585928, -549488,
+ -1119584, 2619752, -2108549, -2118186, -3859737, -1399561, -3277672,
+ 1757237, -19422, 4010497, 280005, 2706023, 95776, 3077325,
+ 3530437, -1661693, -3592148, -2537516, 3915439, -3861115, -3043716,
+ 3574422, -2867647, 3539968, -300467, 2348700, -539299, -1699267,
+ -1643818, 3505694, -3821735, 3507263, -2140649, -1600420, 3699596,
+ 811944, 531354, 954230, 3881043, 3900724, -2556880, 2071892,
+ -2797779, -3930395, -1528703, -3677745, -3041255, -1452451, 3475950,
+ 2176455, -1585221, -1257611, 1939314, -4083598, -1000202, -3190144,
+ -3157330, -3632928, 126922, 3412210, -983419, 2147896, 2715295,
+ -2967645, -3693493, -411027, -2477047, -671102, -1228525, -22981,
+ -1308169, -381987, 1349076, 1852771, -1430430, -3343383, 264944,
+ 508951, 3097992, 44288, -1100098, 904516, 3958618, -3724342,
+ -8578, 1653064, -3249728, 2389356, -210977, 759969, -1316856,
+ 189548, -3553272, 3159746, -1851402, -2409325, -177440, 1315589,
+ 1341330, 1285669, -1584928, -812732, -1439742, -3019102, -3881060,
+ -3628969, 3839961, 2091667, 3407706, 2316500, 3817976, -3342478,
+ 2244091, -2446433, -3562462, 266997, 2434439, -1235728, 3513181,
+ -3520352, -3759364, -1197226, -3193378, 900702, 1859098, 909542,
+ 819034, 495491, -1613174, -43260, -522500, -655327, -3122442,
+ 2031748, 3207046, -3556995, -525098, -768622, -3595838, 342297,
+ 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044,
+ 2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353,
+ 1595974, -3767016, 1250494, 2635921, -3548272, -2994039, 1869119,
+ 1903435, -1050970, -1333058, 1237275, -3318210, -1430225, -451100,
+ 1312455, 3306115, -1962642, -1279661, 1917081, -2546312, -1374803,
+ 1500165, 777191, 2235880, 3406031, -542412, -2831860, -1671176,
+ -1846953, -2584293, -3724270, 594136, -3776993, -2013608, 2432395,
+ 2454455, -164721, 1957272, 3369112, 185531, -1207385, -3183426,
+ 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107,
+ -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735,
+ 472078, -426683, 1723600, -1803090, 1910376, -1667432, -1104333,
+ -260646, -3833893, -2939036, -2235985, -420899, -2286327, 183443,
+ -976891, 1612842, -3545687, -554416, 3919660, -48306, -1362209,
+ 3937738, 1400424, -846154, 1976782
+};
diff --git a/lib/freebl/leancrypto/mutex_w.h b/lib/freebl/leancrypto/mutex_w.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/mutex_w.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see COPYING file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef _MUTEX_W_H
+#define _MUTEX_W_H
+
+#include "atomic.h"
+#include "atomic_bool.h"
+
+/**
+ * @brief Writer mutex with a polling mechanism
+ *
+ * This mutex and its implementation below is intended to cover the needs of
+ * leancrypto and having no dependencies whatsoever. Thus, the implementation
+ * is below is not intended for general-purpose use! Yet, it serves its purpose
+ * for leancrypto.
+ */
+typedef struct {
+ atomic_bool_t lock;
+ atomic_t writer_pending;
+} mutex_w_t;
+
+/* 1 microsecond when using nanosleep */
+#define MUTEX_DEFAULT_SLEEP_TIME_NS (1 << 10)
+/** 1 << (MUTEX_DEFAULT_SLEEP_TIME_NS + MUTEX_MAX_INC_BITS) */
+#define MUTEX_MAX_INC_BITS 14
+
+#define __MUTEX_W_INITIALIZER(locked) \
+ { \
+ .lock = ATOMIC_BOOL_INIT(locked), \
+ .writer_pending = ATOMIC_INIT(0), \
+ }
+
+#define DEFINE_MUTEX_W_UNLOCKED(name) \
+ mutex_w_t name = __MUTEX_W_INITIALIZER(false)
+
+#define DEFINE_MUTEX_W_LOCKED(name) mutex_w_t name = __MUTEX_W_INITIALIZER(true)
+
+/*
+ * Instead of using a environment-dependent nanosleep implementation, we use
+ * a small busy-wait loop which should serve the purpose of the short-duration
+ * contentions possible in leancrypto.
+ */
+#if 0
+#include <time.h>
+static inline void mutex_w_sleep(mutex_w_t *mutex)
+{
+ struct timespec sleeptime = { .tv_sec = 0,
+ .tv_nsec = MUTEX_DEFAULT_SLEEP_TIME_NS};
+ int pending = atomic_read(&mutex->writer_pending);
+
+ /* Increase wait time exponentially depending on waiters */
+ if (pending > MUTEX_MAX_INC_BITS)
+ pending = MUTEX_MAX_INC_BITS;
+ if (pending < 0)
+ pending = 0;
+ sleeptime.tv_nsec <<= pending;
+ nanosleep(&sleeptime, NULL);
+}
+#else
+static inline void mutex_w_sleep(mutex_w_t *mutex)
+{
+ uint64_t tv_nsec = MUTEX_DEFAULT_SLEEP_TIME_NS;
+ /*
+ * Use volatile to ensure the compiler does not optimize the busyloop
+ * away.
+ */
+ volatile uint64_t i;
+ int pending = atomic_read(&mutex->writer_pending);
+
+ /* Increase wait time exponentially depending on waiters */
+ if (pending > MUTEX_MAX_INC_BITS)
+ pending = MUTEX_MAX_INC_BITS;
+ if (pending < 0)
+ pending = 0;
+ tv_nsec <<= pending;
+
+ /* Busy-loop for sleeping */
+ for (i = 0; i < tv_nsec; i++)
+ ;
+}
+#endif
+
+/**
+ * @brief Initialize a mutex
+ * @param mutex [in] Lock variable to initialize.
+ * @param locked [in] Specify whether the lock shall already be locked (true)
+ * or unlocked (false).
+ */
+static inline void mutex_w_init(mutex_w_t *mutex, bool locked)
+{
+ atomic_bool_set(locked, &mutex->lock);
+ atomic_set(&mutex->writer_pending, 0);
+}
+
+static inline void mutex_w_destroy(mutex_w_t *mutex)
+{
+ (void)mutex;
+}
+
+/**
+ * Mutual exclusion lock (covering also the reader lock use case).
+ * @param mutex [in] lock variable to lock
+ */
+static inline void mutex_w_lock(mutex_w_t *mutex)
+{
+ atomic_inc(&mutex->writer_pending);
+
+ /* Take the writer lock only if no writer lock is taken. */
+ while (!atomic_bool_cmpxchg(&mutex->lock, false, true))
+ mutex_w_sleep(mutex);
+
+ atomic_dec(&mutex->writer_pending);
+}
+
+/**
+ * Mutual exclusion lock: Attempt to take the lock. The function will never
+ * block but return whether the lock was successfully taken or not.
+ *
+ * @param mutex [in] lock variable to lock
+ * @return true if lock was taken, false if lock was not taken
+ */
+static inline bool mutex_w_trylock(mutex_w_t *mutex)
+{
+ return atomic_bool_cmpxchg(&mutex->lock, false, true);
+}
+
+static inline bool mutex_w_islocked(mutex_w_t *mutex)
+{
+ return atomic_bool_read(&mutex->lock);
+}
+
+/**
+ * Unlock the lock
+ * @param mutex [in] lock variable to lock
+ */
+static inline void mutex_w_unlock(mutex_w_t *mutex)
+{
+ /* Release the writer lock. */
+ atomic_bool_cmpxchg(&mutex->lock, true, false);
+}
+
+#endif /* _MUTEX_W_H */
diff --git a/lib/freebl/leancrypto/new_headers/lc_hash.h b/lib/freebl/leancrypto/new_headers/lc_hash.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/lc_hash.h
@@ -0,0 +1,17 @@
+#include "hasht.h"
+#include "blapit.h"
+#include "lc_memset_secure.h" /* sigh the original included it,
+ * so some files expect it to be there already */
+#include "lc_memory_support.h"
+/* sigh Stephen doesn seem to believe in typedef, so
+ * just stuff our context pointer into a struct */
+#define LC_SHA3_256_CTX_SIZE (SHA3_256_BLOCK_LENGTH)
+#define LC_SHA3_STATE_SIZE_ALIGN(x) (x)
+
+#define LC_SHA3_512_SIZE_DIGEST SHA3_512_LENGTH
+
+#ifndef LC_HASH_COMMON_ALIGNMENT
+#define LC_HASH_COMMON_ALIGNMENT 64
+#endif
+
+
diff --git a/lib/freebl/leancrypto/new_headers/lc_rng.h b/lib/freebl/leancrypto/new_headers/lc_rng.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/lc_rng.h
@@ -0,0 +1,56 @@
+#ifndef LC_RNG_H
+#define LC_RNG_H 1
+#include <blapi.h>
+#include <secrng.h>
+
+struct lc_rng_ctx;
+extern struct lc_rng_ctx *lc_seeded_rng;
+
+/* just enough of the rng_context to make the code happy.
+ * in the end, we just use our NSS internal RNG */
+struct lc_static_rng_data {
+ const unsigned char *seed;
+ size_t seedlen;
+};
+
+struct lc_rng_ctx {
+ struct lc_static_rng_data *dummy;
+};
+
+static inline int lc_rng_generate(struct lc_rng_ctx *rng,
+ unsigned char *addinput,
+ size_t addlen,
+ unsigned char *out,
+ size_t outlen)
+{
+ size_t len;
+ if (rng->dummy != NULL) {
+ if (outlen > rng->dummy->seedlen) {
+ return -1;
+ }
+ PORT_Memcpy(out, rng->dummy->seed, outlen);
+ return 0;
+ }
+ if (addlen != 0) {
+ RNG_RandomUpdate(addinput, addlen);
+ }
+ len= RNG_SystemRNG(out, outlen);
+ if (len != outlen) {
+ return -1;
+ }
+ return 0;
+}
+
+#define lc_rng_seed(rng, seed, seedlen, pers, perslen) {\
+ if (pers_len != 0) {\
+ RNG_RandomUpdate(pers, perslen); \
+ } \
+ RNG_SystemRNG(seed, seedlen); \
+}
+#define lc_rng_check(rng)
+
+#define LC_STATIC_DRNG_ON_STACK(sdrng, state)\
+ struct lc_rng_ctx sdrng; \
+ sdrng.dummy = state;
+
+#endif
diff --git a/lib/freebl/leancrypto/new_headers/lc_sha3.h b/lib/freebl/leancrypto/new_headers/lc_sha3.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/lc_sha3.h
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: MIT
+// NSS SHA3 bindings for ML-DSA leancrypto
+
+#ifndef LC_SHA3__H
+#define LC_SHA3__H
+#include <blapi.h>
+
+#define LC_SHA3_SIZE_RATE(bits) ((1600 - 2 * bits) >> 3)
+
+#define LC_SHAKE_128_SIZE_BLOCK LC_SHA3_SIZE_RATE(128)
+#define LC_SHAKE_256_SIZE_BLOCK LC_SHA3_SIZE_RATE(256)
+
+/*#define SHAKE128_RATE 168
+#define shake128 SHAKE_128_HashBuf
+
+#define SHAKE256_RATE SHA3_256_BLOCK_LENGTH
+#define shake256 SHAKE_256_HashBuf */
+
+#define LC_HASH_CTX_ON_STACK(name, type_) \
+ struct lc_hash_ctx _##name ; \
+ _##name.hash= type_; \
+ _##name.buf= NULL; \
+ _##name.stream = false; \
+ _##name.u.ctx_ptr = NULL; \
+ struct lc_hash_ctx *name= &_##name;
+
+#define LC_HASH_SET_CTX(name, type_) \
+ name->hash= type_;
+
+#define LC_SHAKE_256_CTX(name) \
+ LC_HASH_SET_CTX(name, lc_shake256);
+
+typedef enum {
+ lc_shake128,
+ lc_shake256,
+} sha3Type;
+
+
+/* sigh, we buffer eKEverything because we can't correctly do multiple
+ * finals correctly. In cases where we know we are not going to
+ * do multiple finals, set the streaming bool */
+struct lc_hash_ctx {
+ sha3Type hash;
+ bool stream;
+ union {
+ SHAKE_256Context *shake256_ctx;
+ SHAKE_128Context *shake128_ctx;
+ void *ctx_ptr;
+ }u;
+ size_t digestSize;
+ size_t current_input;
+ size_t current_output;
+ unsigned char *buf;
+ size_t buf_size;
+ unsigned char buf_space[2048];
+ unsigned char buf2_space[2048];
+};
+
+#define lc_xof(type, in, inlen, out, outlen) \
+ switch(type) { \
+ case lc_shake128: \
+ SHAKE_128_HashBuf(out, outlen, in, inlen); \
+ break; \
+ case lc_shake256: \
+ SHAKE_256_HashBuf(out, outlen, in, inlen); \
+ break; \
+ default: \
+ assert(0); \
+ }
+
+static inline void
+lc_hash_init(struct lc_hash_ctx *ptr) {
+ if (ptr->stream) {
+ /* if we already have a context, just reset it, This is
+ * what the caller wanted, saving a destroy and create */
+ if (ptr->u.ctx_ptr == NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ ptr->u.shake128_ctx = SHAKE_128_NewContext();
+ break;
+ case lc_shake256:
+ ptr->u.shake256_ctx = SHAKE_256_NewContext();
+ break;
+ }
+ }
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Begin(ptr->u.shake128_ctx);
+ break;
+ case lc_shake256:
+ SHAKE_256_Begin(ptr->u.shake256_ctx);
+ break;
+ }
+ return;
+ }
+ /* we can be called with an active buffer, do and implicit reset here
+ * and free that buffer before we set up the next one */
+ if (ptr->buf && ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->current_input);
+ free(ptr->buf);
+ }
+ ptr->digestSize = 0;
+ ptr->current_input = 0;
+ ptr->current_output = 0;
+ ptr->buf_size = sizeof(ptr->buf_space);
+ ptr->buf = &ptr->buf_space[0];
+}
+
+static inline void
+lc_hash_update(struct lc_hash_ctx *ptr, const unsigned char *input, size_t inLen) {
+ if (inLen ==0) { return; } /* why were we even called with a NULL buffer? */
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_Absorb(ptr->u.shake128_ctx, input, inLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_Absorb(ptr->u.shake256_ctx, input, inLen);
+ break;
+ }
+ return;
+ }
+ if (ptr->current_input + inLen > ptr->buf_size) {
+ int len = ptr->current_input + inLen + 2048;
+ unsigned char *newBuf;
+ if (ptr->buf_size == sizeof(ptr->buf_space)) {
+ newBuf = calloc(1, len);
+ if (newBuf) {
+ memcpy(newBuf, ptr->buf, ptr->buf_size);
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ }
+ } else {
+ newBuf = reallocarray(ptr->buf, 1, len);
+ }
+ if (!newBuf) {
+ return;
+ }
+ ptr->buf = newBuf;
+ ptr->buf_size = len;
+ }
+ memcpy(ptr->buf + ptr->current_input, input, inLen);
+ ptr->current_input+=inLen;
+}
+
+#define lc_hash_set_digestsize(ptr, len) ((ptr)->digestSize = (len))
+static inline void
+lc_hash_final(struct lc_hash_ctx *ptr, unsigned char *output)
+{
+ size_t outLen= ptr->digestSize;
+ if (ptr->stream) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_SqueezeEnd(ptr->u.shake128_ctx, output, outLen);
+ break;
+ case lc_shake256:
+ SHAKE_256_SqueezeEnd(ptr->u.shake256_ctx, output, outLen);
+ break;
+ }
+ return;
+ }
+ int len= ptr->current_output+outLen;
+ if (ptr->current_output == 0) {
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, output, outLen);
+ ptr->current_output += outLen;
+ return;
+ }
+ if (len > sizeof(ptr->buf2_space)) {
+ unsigned char *newBuf = calloc(1,len);
+ if (!newBuf) {
+ memset(output, 0, outLen);
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, newBuf, len);
+ memcpy(output, newBuf+ptr->current_output, outLen);
+ memset(newBuf, 0, len);
+ free(newBuf);
+ ptr->current_output += outLen;
+ return;
+ }
+ lc_xof(ptr->hash, ptr->buf, ptr->current_input, ptr->buf2_space, len);
+ memcpy(output, &ptr->buf2_space[ptr->current_output], outLen);
+ memset(ptr->buf2_space, 0,len);
+ ptr->current_output += outLen;
+ return;
+}
+
+static inline void lc_hash_zero(struct lc_hash_ctx *ptr)
+{
+ if (ptr->stream) {
+ if (ptr->u.ctx_ptr != NULL) {
+ switch (ptr->hash) {
+ case lc_shake128:
+ SHAKE_128_DestroyContext(ptr->u.shake128_ctx, PR_TRUE);
+ ptr->u.shake128_ctx = NULL;
+ break;
+ case lc_shake256:
+ SHAKE_128_DestroyContext(ptr->u.shake256_ctx, PR_TRUE);
+ ptr->u.shake256_ctx = NULL;
+ break;
+ }
+ }
+ return;
+ }
+ memset(ptr->buf2_space, 0, sizeof(ptr->buf2_space));
+ memset(ptr->buf_space, 0, sizeof(ptr->buf_space));
+ if (ptr->buf != ptr->buf_space) {
+ memset(ptr->buf, 0, ptr->buf_size);
+ free(ptr->buf);
+ ptr->buf = NULL;
+ }
+ lc_hash_init(ptr);
+}
+#endif
diff --git a/lib/freebl/leancrypto/new_headers/lc_sha512.h b/lib/freebl/leancrypto/new_headers/lc_sha512.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/new_headers/ml_dsa_api.h b/lib/freebl/leancrypto/new_headers/ml_dsa_api.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/new_headers/ml_dsa_api.h
@@ -0,0 +1,71 @@
+#ifndef ML_DSA_API_H
+#define ML_DSA_API_H
+// This is a generated file from the various XXX_sign.h files
+#include <stddef.h>
+#include "ml_dsa_apit.h"
+
+// from ml_dsa_44_sign.h
+int lc_dilithium_44_keypair_from_seed_c(struct lc_dilithium_44_pk *pk,
+ struct lc_dilithium_44_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_44_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk);
+int lc_dilithium_44_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_sign_final_c(struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_44_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+int lc_dilithium_44_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_44_verify_final_c(const struct lc_dilithium_44_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_44_pk *pk);
+
+// from ml_dsa_65_sign.h
+int lc_dilithium_65_keypair_from_seed_c(struct lc_dilithium_65_pk *pk,
+ struct lc_dilithium_65_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_65_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk);
+int lc_dilithium_65_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_sign_final_c(struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_65_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+int lc_dilithium_65_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_65_verify_final_c(const struct lc_dilithium_65_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_65_pk *pk);
+// from ml_dsa_87_sign.h
+int lc_dilithium_87_keypair_from_seed_c(struct lc_dilithium_87_pk *pk,
+ struct lc_dilithium_87_sk *sk,
+ const uint8_t *seed, size_t seedlen);
+
+int lc_dilithium_87_sign_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk);
+int lc_dilithium_87_sign_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_sign_final_c(struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_sk *sk,
+ struct lc_rng_ctx *rng_ctx);
+
+int lc_dilithium_87_verify_init_c(struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+int lc_dilithium_87_verify_update_c(struct lc_dilithium_ctx *ctx, const uint8_t *m,
+ size_t mlen);
+int lc_dilithium_87_verify_final_c(const struct lc_dilithium_87_sig *sig,
+ struct lc_dilithium_ctx *ctx,
+ const struct lc_dilithium_87_pk *pk);
+#endif /* ML_DSA_API_H */
diff --git a/lib/freebl/leancrypto/new_headers/static_rng.h b/lib/freebl/leancrypto/new_headers/static_rng.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/null_buffer.h b/lib/freebl/leancrypto/null_buffer.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/null_buffer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef NULL_BUFFER_H
+#define NULL_BUFFER_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LC_NULL_BUFFER_SIZE 64
+extern const uint8_t null_buffer[];
+
+extern volatile int16_t optimization_blocker_int16;
+extern volatile int8_t optimization_blocker_int8;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NULL_BUFFER_H */
diff --git a/lib/freebl/leancrypto/randombytes.h b/lib/freebl/leancrypto/randombytes.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/randombytes.h
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+// NSS stub for liboqs randombytes.h
+
+#ifndef RANDOMBYTES_H
+#define RANDOMBYTES_H
+
+// run the random number generator through our mldsa code so we can support
+// CKA_SEED (both acquiring it and generating keys from it) and
+// DETERMINISTIC signatures (by returning zeros from the RNG)
+void mldsa_GetRandomBytes(unsigned char *rdn, int bytes);
+#define randombytes mldsa_GetRandomBytes
+
+#endif
diff --git a/lib/freebl/leancrypto/ret_checkers.h b/lib/freebl/leancrypto/ret_checkers.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/ret_checkers.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef RET_CHECKERS_H
+#define RET_CHECKERS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#define ret_t int __attribute__((warn_unused_result))
+
+#ifdef LC_DEBUG
+#define CKERROR_LOG \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \
+ printf("Error %d at %s:%s:%u\n", ret, __FILE__, \
+ __FUNCTION__, __LINE__); \
+ _Pragma("GCC diagnostic pop")
+#else
+#define CKERROR_LOG
+#endif
+
+#define CKINT(x) \
+ { \
+ ret = x; \
+ if (ret < 0) { \
+ CKERROR_LOG \
+ goto out; \
+ } \
+ }
+
+#define CKINT_LOG(x, ...) \
+ { \
+ ret = x; \
+ if (ret < 0) { \
+ CKERROR_LOG \
+ printf(__VA_ARGS__); \
+ goto out; \
+ } \
+ }
+
+#define CKNULL(v, r) \
+ { \
+ if (!v) { \
+ ret = r; \
+ CKERROR_LOG \
+ goto out; \
+ } \
+ }
+
+#define CKNULL_LOG(v, r, ...) \
+ { \
+ if (!v) { \
+ printf(__VA_ARGS__); \
+ ret = r; \
+ CKERROR_LOG \
+ goto out; \
+ } \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RET_CHECKERS_H */
diff --git a/lib/freebl/leancrypto/rotate.h b/lib/freebl/leancrypto/rotate.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/rotate.h
@@ -0,0 +1,90 @@
+/* Rotate left / right functions
+ *
+ * Copyright (C) 2015 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef ROTATE_H
+#define ROTATE_H
+
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LINUX_KERNEL
+
+/*
+ * Rotate 8 bit unsigned integer X by N bits left/right
+ */
+static inline uint8_t rol8(uint16_t x, uint8_t n)
+{
+ return (uint8_t)((x << (n & (8 - 1))) | (x >> ((8 - n) & (8 - 1))));
+}
+
+static inline uint8_t ror8(uint16_t x, uint8_t n)
+{
+ return (uint8_t)((x >> (n & (8 - 1))) | (x << ((8 - n) & (8 - 1))));
+}
+
+/*
+ * Rotate 16 bit unsigned integer X by N bits left/right
+ */
+static inline uint16_t rol16(uint16_t x, uint8_t n)
+{
+ return (uint16_t)((x << (n & (16 - 1))) | (x >> ((16 - n) & (16 - 1))));
+}
+
+static inline uint16_t ror16(uint16_t x, uint8_t n)
+{
+ return (uint16_t)((x >> (n & (16 - 1))) | (x << ((16 - n) & (16 - 1))));
+}
+
+/*
+ * Rotate 32 bit unsigned integer X by N bits left/right
+ */
+static inline uint32_t rol32(uint32_t x, uint8_t n)
+{
+ return ((x << (n & (32 - 1))) | (x >> ((32 - n) & (32 - 1))));
+}
+
+static inline uint32_t ror32(uint32_t x, uint8_t n)
+{
+ return ((x >> (n & (32 - 1))) | (x << ((32 - n) & (32 - 1))));
+}
+
+/*
+ * Rotate 64 bit unsigned integer X by N bits left/right
+ */
+static inline uint64_t rol64(uint64_t x, uint8_t n)
+{
+ return ((x << (n & (64 - 1))) | (x >> ((64 - n) & (64 - 1))));
+}
+
+static inline uint64_t ror64(uint64_t x, uint8_t n)
+{
+ return ((x >> (n & (64 - 1))) | (x << ((64 - n) & (64 - 1))));
+}
+
+#endif /* LINUX_KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ROTATE_H */
diff --git a/lib/freebl/leancrypto/sidechannel_resistantce.h b/lib/freebl/leancrypto/sidechannel_resistantce.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/sidechannel_resistantce.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived in parts from the code distribution provided with
+ * https://github.com/pq-crystals/kyber
+ *
+ * That code is released under Public Domain
+ * (https://creativecommons.org/share-your-work/public-domain/cc0/).
+ */
+
+#ifndef SIDECHANNEL_RESISTANCE_H
+#define SIDECHANNEL_RESISTANCE_H
+
+#include "ext_headers.h"
+#include "null_buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief cmov - Copy len bytes from x to r if b is 1;
+ * don't modify x if b is 0. Requires b to be in {0,1};
+ * assumes two's complement representation of negative integers.
+ * Runs in constant time.
+ *
+ * @param [out] r pointer to output byte array
+ * @param [in] x pointer to input byte array
+ * @param [in] len Amount of bytes to be copied
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void cmov(uint8_t *r, const uint8_t *x, size_t len, uint8_t b)
+{
+ size_t i;
+ uint8_t opt_blocker;
+
+ /*
+ * Goal: copy data only depending on a given condition without
+ * the use of a branching operation which alters the timing behavior
+ * depending on the condition. As the condition here depends on
+ * secret data, the code has to ensure that no branching is used to have
+ * time-invariant code. This solution below also shall ensure that the
+ * compiler cannot optimize this code such that it brings back the
+ * branching.
+ *
+ * (condition ^ opt_blocker) can be any value at run-time to the
+ * compiler, making it impossible to skip the computation (except the
+ * compiler would care to create a branch for opt_blocker to be either
+ * 0 or 1, which would be extremely unlikely). Yet the volatile
+ * variable has to be loaded only once at the beginning of the function
+ * call.
+ *
+ * Note, the opt_blocker is not required in most instances, but in the
+ * ARMv8 Neon implementation of SLH-DSA the compiler managed to still
+ * create time-variant code without the optimization blocker.
+ */
+ opt_blocker = (uint8_t)optimization_blocker_int8;
+
+ b = -b;
+ for (i = 0; i < len; i++)
+ r[i] ^= (b & (r[i] ^ x[i])) ^ opt_blocker;
+}
+
+/**
+ * @brief cmov_int16 - Copy input v to *r if b is 1, don't modify *r if b is 0.
+ * Requires b to be in {0,1}; Runs in constant time.
+ *
+ * @param [out] r pointer to output int16_t
+ * @param [in] v input int16_t
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void cmov_int16(int16_t *r, int16_t v, uint16_t b)
+{
+ b = -b;
+ *r ^= (int16_t)(b & ((*r) ^ v));
+}
+
+/**
+ * @brief cmov_uint32 - Copy input v to *r if b is 1, don't modify *r if b is 0.
+ * Requires b to be in {0,1}; Runs in constant time.
+ *
+ * @param [out] r pointer to output int16_t
+ * @param [in] v input int16_t
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void cmov_uint32(uint32_t *r, uint32_t v, uint32_t b)
+{
+ b = -b;
+ *r ^= (uint32_t)(b & ((*r) ^ v));
+}
+
+/**
+ * @brief cmov_int - Copy input v to *r if b is 1, don't modify *r if b is 0.
+ * Requires b to be in {0,1}; Runs in constant time.
+ *
+ * @param [out] r pointer to output int16_t
+ * @param [in] v input int16_t
+ * @param [in] b Condition bit; has to be in {0,1}
+ */
+static inline void cmov_int(int *r, int v, uint16_t b)
+{
+ b = -b;
+ *r ^= (int)(b & ((*r) ^ v));
+}
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIDECHANNEL_RESISTANCE_H */
diff --git a/lib/freebl/leancrypto/signature_domain_separation.c b/lib/freebl/leancrypto/signature_domain_separation.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/signature_domain_separation.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#include "signature_domain_separation.h"
+#include "lc_sha256.h"
+#include "lc_sha3.h"
+#include "lc_sha512.h"
+#include "ret_checkers.h"
+
+/* RFC4055 2.16.840.1.101.3.4.2.1 */
+static const uint8_t sha256_oid_der[] __maybe_unused = { 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x01 };
+/* RFC4055 2.16.840.1.101.3.4.2.2 */
+static const uint8_t sha384_oid_der[] __maybe_unused = { 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x02 };
+/* RFC4055 2.16.840.1.101.3.4.2.3 */
+static const uint8_t sha512_oid_der[] __maybe_unused = { 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x03 };
+
+/*
+ * https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+static const uint8_t sha3_256_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08
+};
+static const uint8_t sha3_384_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09
+};
+static const uint8_t sha3_512_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a
+};
+
+/* RFC8692 2.16.840.1.101.3.4.2.11 */
+static const uint8_t shake128_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B
+};
+
+/* RFC8692 2.16.840.1.101.3.4.2.11 */
+static const uint8_t shake256_oid_der[] __maybe_unused = {
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0C
+};
+
+/* OIDs from https://www.ietf.org/archive/id/draft-ietf-lamps-pq-composite-sigs-03.html */
+const uint8_t lc_x509_composite_sig_prefix[] = {
+ 0x43, 0x6F, 0x6D, 0x70, 0x6F, 0x73, 0x69, 0x74, 0x65, 0x41, 0x6C,
+ 0x67, 0x6F, 0x72, 0x69, 0x74, 0x68, 0x6D, 0x53, 0x69, 0x67, 0x6E,
+ 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x32, 0x30, 0x32, 0x35
+};
+
+static const uint8_t lc_x509_test_dom_sep[] = { 0x06, 0x0b, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xfa, 0x6b, 0x50,
+ 0x09, 0x01, 0x08 };
+
+/* id-HashMLDSA44-Ed25519-SHA512 */
+static const uint8_t lc_x509_mldsa44_ed25519_sha512_dom_sep[] = {
+ 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x02
+};
+
+/* id-HashMLDSA65-Ed25519-SHA512 */
+static const uint8_t lc_x509_mldsa65_ed25519_sha512_dom_sep[] = {
+ 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0B
+};
+
+/* id-HashMLDSA87-Ed448-SHAKE256 */
+static const uint8_t lc_x509_mldsa87_ed448_sha512_dom_sep[] = {
+ 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0E
+};
+
+int signature_ph_oids(struct lc_hash_ctx *hash_ctx,
+ const struct lc_hash *signature_prehash_type, size_t mlen,
+ unsigned int nist_category)
+{
+ /* If no hash is supplied, we have no HashML-DSA */
+ if (!signature_prehash_type)
+ return 0;
+
+ /*
+ * The signature init/update/final operation will not work with the
+ * check of mlen, as only when _final is invoked, the message length
+ * is known.
+ *
+ * As defined in FIPS 204, section 5.4 requires
+ * "... the digest that is signed needs to be generated using an
+ * approved hash function or XOF (e.g., from FIPS 180 or FIPS 202) that
+ * provides at least λ bits of classical security strength against both
+ * collision and second preimage attacks ... Obtaining at least λ bits
+ * of classical security strength against collision attacks requires
+ * that the digest to be signed be at least 2λ bits in length."
+ * This requirement implies in the following definitions.
+ */
+ (void)mlen;
+
+ switch (nist_category) {
+ case 1:
+#ifdef LC_SHA2_256
+ if (signature_prehash_type == lc_sha256) {
+ // if (mlen != LC_SHA256_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha256_oid_der,
+ sizeof(sha256_oid_der));
+ return 0;
+ }
+#endif
+#ifdef LC_SHA3
+ if (signature_prehash_type == lc_sha3_256) {
+ // if (mlen != LC_SHA3_256_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha3_256_oid_der,
+ sizeof(sha3_256_oid_der));
+ return 0;
+ }
+ if (signature_prehash_type == lc_shake128) {
+ /* FIPS 204 section 5.4.1 */
+ // if (mlen != 32)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, shake128_oid_der,
+ sizeof(shake128_oid_der));
+ return 0;
+ }
+#endif
+ /* FALLTHROUGH - Dilithium44 allows the following, too */
+ fallthrough;
+ case 3:
+#ifdef LC_SHA3
+ if (signature_prehash_type == lc_sha3_384) {
+ // if (mlen != LC_SHA3_384_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha3_384_oid_der,
+ sizeof(sha3_384_oid_der));
+ return 0;
+ }
+#endif
+#ifdef LC_SHA2_512
+ if (signature_prehash_type == lc_sha384) {
+ // if (mlen != LC_SHA384_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha384_oid_der,
+ sizeof(sha384_oid_der));
+ return 0;
+ }
+#endif
+ /* FALLTHROUGH - Dilithium[44|65] allows the following, too */
+ fallthrough;
+ case 5:
+#ifdef LC_SHA2_512
+ if (signature_prehash_type == lc_sha512) {
+ // if (mlen != LC_SHA512_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha512_oid_der,
+ sizeof(sha512_oid_der));
+ return 0;
+ }
+#endif
+#ifdef LC_SHA3
+ if (signature_prehash_type == lc_sha3_512) {
+ // if (mlen != LC_SHA3_512_SIZE_DIGEST)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, sha3_512_oid_der,
+ sizeof(sha3_512_oid_der));
+ return 0;
+ } else if (signature_prehash_type == lc_shake256) {
+ /* FIPS 204 section 5.4.1 */
+ /*
+ * TODO: mlen must be >= 64 to comply with the
+ * aforementioned requirement - unfortunately we can
+ * only check mlen at the end of the signature
+ * operation - shall this be implemented?
+ */
+ // if (mlen != 64)
+ // return -EOPNOTSUPP;
+ lc_hash_update(hash_ctx, shake256_oid_der,
+ sizeof(shake256_oid_der));
+ return 0;
+ }
+#endif
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int composite_signature_set_domain(const uint8_t **domain,
+ size_t *domainlen,
+ unsigned int nist_category)
+{
+ /* Set Domain */
+ switch (nist_category) {
+ case 0:
+ *domain = lc_x509_test_dom_sep;
+ *domainlen = sizeof(lc_x509_test_dom_sep);
+ break;
+ case 1:
+ *domain = lc_x509_mldsa44_ed25519_sha512_dom_sep;
+ *domainlen = sizeof(lc_x509_mldsa44_ed25519_sha512_dom_sep);
+ break;
+ case 3:
+ *domain = lc_x509_mldsa65_ed25519_sha512_dom_sep;
+ *domainlen = sizeof(lc_x509_mldsa65_ed25519_sha512_dom_sep);
+ break;
+ case 5:
+ *domain = lc_x509_mldsa87_ed448_sha512_dom_sep;
+ *domainlen = sizeof(lc_x509_mldsa87_ed448_sha512_dom_sep);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int composite_signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ const uint8_t *userctx,
+ size_t userctxlen,
+ const uint8_t *randomizer,
+ size_t randomizerlen,
+ unsigned int nist_category)
+{
+ const uint8_t *domain;
+ size_t domainlen;
+ uint8_t userctxlen_small = (uint8_t)userctxlen;
+ int ret;
+
+ CKINT(composite_signature_set_domain(&domain, &domainlen,
+ nist_category));
+
+ /*
+ * M' = Prefix || Domain || len(ctx) || ctx || r
+ *
+ * See for details: https://lamps-wg.github.io/draft-composite-sigs/draft-ietf-lamps-pq-composite-sigs.html
+ */
+ lc_hash_update(hash_ctx, lc_x509_composite_sig_prefix,
+ sizeof(lc_x509_composite_sig_prefix));
+ lc_hash_update(hash_ctx, domain, domainlen);
+ lc_hash_update(hash_ctx, &userctxlen_small, sizeof(userctxlen_small));
+ lc_hash_update(hash_ctx, userctx, userctxlen);
+ lc_hash_update(hash_ctx, randomizer, randomizerlen);
+
+out:
+ return ret;
+}
+
+static int standalone_signature_domain_separation(
+ struct lc_hash_ctx *hash_ctx,
+ const struct lc_hash *signature_prehash_type, const uint8_t *userctx,
+ size_t userctxlen, size_t mlen, unsigned int nist_category)
+{
+ int ret;
+ uint8_t domainseparation[2];
+
+ domainseparation[0] = signature_prehash_type ? 1 : 0;
+ domainseparation[1] = (uint8_t)userctxlen;
+
+ lc_hash_update(hash_ctx, domainseparation, sizeof(domainseparation));
+ lc_hash_update(hash_ctx, userctx, userctxlen);
+
+ CKINT(signature_ph_oids(hash_ctx, signature_prehash_type, mlen,
+ nist_category));
+
+out:
+ return ret;
+}
+
+int signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ unsigned int ml_dsa_internal,
+ const struct lc_hash *signature_prehash_type,
+ const uint8_t *userctx, size_t userctxlen,
+ const uint8_t *m, size_t mlen,
+ const uint8_t *randomizer, size_t randomizerlen,
+ unsigned int nist_category)
+{
+ int ret = 0;
+
+ /* The internal operation skips the domain separation code */
+ if (ml_dsa_internal)
+ goto out;
+
+ if (userctxlen > 255)
+ return -EINVAL;
+
+ /* If Composite ML-DSA is requested, use domain as userctx */
+ if (randomizer) {
+ const uint8_t *domain;
+ size_t domainlen;
+
+ CKINT(composite_signature_set_domain(&domain, &domainlen,
+ nist_category));
+
+ /* Add the composite signature domain as context */
+ CKINT(standalone_signature_domain_separation(
+ hash_ctx, signature_prehash_type, domain, domainlen,
+ mlen, nist_category));
+
+ CKINT(composite_signature_domain_separation(
+ hash_ctx, userctx, userctxlen, randomizer,
+ randomizerlen, nist_category));
+ } else {
+ CKINT(standalone_signature_domain_separation(
+ hash_ctx, signature_prehash_type, userctx, userctxlen,
+ mlen, nist_category));
+ }
+
+out:
+ lc_hash_update(hash_ctx, m, mlen);
+ return ret;
+}
diff --git a/lib/freebl/leancrypto/signature_domain_separation.h b/lib/freebl/leancrypto/signature_domain_separation.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/signature_domain_separation.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef SIGNATURE_DOMAIN_SEPARATION_H
+#define SIGNATURE_DOMAIN_SEPARATION_H
+
+#include "dilithium_type.h"
+#include "helper.h"
+#include "lc_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ unsigned int ml_dsa_internal,
+ const struct lc_hash *signature_prehash_type,
+ const uint8_t *userctx, size_t userctxlen,
+ const uint8_t *m, size_t mlen,
+ const uint8_t *randomizer, size_t randomizerlen,
+ unsigned int nist_category);
+int signature_ph_oids(struct lc_hash_ctx *hash_ctx,
+ const struct lc_hash *signature_prehash_type, size_t mlen,
+ unsigned int nist_category);
+int composite_signature_domain_separation(struct lc_hash_ctx *hash_ctx,
+ const uint8_t *userctx,
+ size_t userctxlen,
+ const uint8_t *randomizer,
+ size_t randomizerlen,
+ unsigned int nist_category);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIGNATURE_DOMAIN_SEPARATION_H */
diff --git a/lib/freebl/leancrypto/small_stack_support.h b/lib/freebl/leancrypto/small_stack_support.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/small_stack_support.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef SMALL_STACK_SUPPORT_H
+#define SMALL_STACK_SUPPORT_H
+
+#include "ext_headers.h"
+#include "lc_memory_support.h"
+#include "lc_memset_secure.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LC_ALIGNED_BUFFER_ALIGNMENTSIZE(name, size, alignment) \
+ uint64_t name[(size + sizeof(uint64_t) - 1) / sizeof(uint64_t)] \
+ __attribute__((aligned(alignment)))
+
+/* Allocate memory on stack */
+#define __LC_DECLARE_MEM_STACK(name, type, alignment) \
+ LC_ALIGNED_BUFFER_ALIGNMENTSIZE(name##_buf, sizeof(type), alignment); \
+ lc_memset_secure(name##_buf, 0, sizeof(type)); \
+ type *name = (type *)name##_buf
+#define __LC_RELEASE_MEM_STACK(name) lc_memset_secure(name, 0, sizeof(*name))
+
+/* Allocate memory on heap */
+#define __LC_DECLARE_MEM_HEAP(name, type, alignment) \
+ type *name = NULL; \
+ int __ret = \
+ lc_alloc_high_aligned((void *)&name, alignment, sizeof(type)); \
+ if (__ret || !name) \
+ return __ret; \
+ lc_memset_secure(name, 0, sizeof(type))
+
+#define __LC_RELEASE_MEM_HEAP(name) \
+ lc_memset_secure(name, 0, sizeof(*name)); \
+ lc_free_high_aligned(name, sizeof(*name))
+
+/* Define macro LC_MEM_ON_HEAP if stack is less than 256KiB in size */
+#ifdef LC_MEM_ON_HEAP
+
+#define noinline_stack noinline
+
+#define LC_DECLARE_MEM(name, type, alignment) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \
+ __LC_DECLARE_MEM_HEAP(name, type, alignment); \
+ _Pragma("GCC diagnostic pop")
+#define LC_RELEASE_MEM(name) __LC_RELEASE_MEM_HEAP(name)
+
+#else
+
+#define noinline_stack
+
+#define LC_DECLARE_MEM(name, type, alignment) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \
+ __LC_DECLARE_MEM_STACK(name, type, alignment); \
+ _Pragma("GCC diagnostic pop")
+#define LC_RELEASE_MEM(name) __LC_RELEASE_MEM_STACK(name)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SMALL_STACK_SUPPORT_H */
diff --git a/lib/freebl/leancrypto/static_rng.h b/lib/freebl/leancrypto/static_rng.h
new file mode 100644
diff --git a/lib/freebl/leancrypto/timecop.h b/lib/freebl/leancrypto/timecop.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/timecop.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+/*
+ * This code is derived from
+ * https://www.post-apocalyptic-crypto.org/timecop/#source-code
+ *
+ * The license is: this code is released into the public domain
+ */
+
+#ifndef TIMECOP_H
+#define TIMECOP_H
+
+#if defined __has_include
+#if __has_include(<valgrind/memcheck.h>)
+#define LC_HAS_TIMECOP
+#endif
+#endif
+
+#if defined(LC_USE_TIMECOP) && !defined(LC_HAS_TIMECOP)
+#error "Compilation with TIMECOP requested, but valgrind's memcheck.h missing."
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LC_USE_TIMECOP
+
+/*
+ * This code requires header files from valgrind. They can be installed with
+ * packages like valgrind-client-headers.
+ */
+#include <valgrind/memcheck.h>
+
+/**
+ * Poisons a memory region of len bytes, starting at addr, indicating that
+ * execution time must not depend on the content of this memory region.
+ * Use this function to mark any memory regions containing secret data.
+ */
+#define poison(addr, len) VALGRIND_MAKE_MEM_UNDEFINED(addr, len)
+
+/**
+ * Use this function to indicate that the specified memory region does no longer
+ * contain data that must not affect execution time.
+ */
+#define unpoison(addr, len) VALGRIND_MAKE_MEM_DEFINED(addr, len)
+
+/**
+ * Checks whether the memory region of len bytes, starting at addr,
+ * contains any poisoned bits.
+ * Returns 0 if the code is running natively, rather than within valgrind.
+ * If valgrind is running, it returns the first address containing poisoned
+ * data, or 0 if there is no poisoned data in the specified memory region.
+ * You can use RUNNING_ON_VALGRIND from valgrind.h to check whether the code
+ * is being executed within valgrind.
+ */
+#define is_poisoned(addr, len) VALGRIND_CHECK_MEM_IS_DEFINED(addr, len)
+
+#else /* LC_USE_TIMECOP */
+
+#define poison(addr, len)
+#define unpoison(addr, len)
+#define is_poisoned(addr, len)
+
+#endif /* LC_USE_TIMECOP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TIMECOP_H */
diff --git a/lib/freebl/leancrypto/visibility.h b/lib/freebl/leancrypto/visibility.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/visibility.h
@@ -0,0 +1,107 @@
+/*
+ * see https://gcc.gnu.org/wiki/Visibility
+ *
+ * use -fvisibility=hidden to mark all symbols hidden per default
+ *
+ * It is sufficient to use the macros in the declarations only. The
+ * definitions do not need to be instrumented.
+ */
+
+#ifndef VISIBILITY_H
+#define VISIBILITY_H
+
+#ifdef LINUX_KERNEL
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+#define LC_INTERFACE_FUNCTION(ret, symbol, param...) \
+ ret symbol(param); \
+ EXPORT_SYMBOL(symbol); \
+ ret symbol(param)
+
+#define LC_INIT_FUNCTION(ret, symbol, param...) \
+ ret __init symbol(param); \
+ ret __init symbol(param)
+
+#define LC_TEST_FUNC(ret, symbol, param...) \
+ static ret symbol(param); \
+ static int __init symbol##_init(void) \
+ { \
+ int __ret; \
+ \
+ pr_info("%s: Starting test case\n", KBUILD_MODNAME); \
+ __ret = symbol(0, NULL); \
+ pr_info("%s: Test case completed with return code %d\n", \
+ KBUILD_MODNAME, __ret); \
+ return __ret ? -EFAULT : 0; \
+ } \
+ static void __exit symbol##_exit(void) \
+ { \
+ } \
+ module_init(symbol##_init); \
+ module_exit(symbol##_exit); \
+ MODULE_LICENSE("Dual BSD/GPL"); \
+ MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>"); \
+ MODULE_DESCRIPTION("leancrypto test case"); \
+ static ret symbol(param)
+
+#pragma GCC diagnostic pop
+
+#define LC_INTERFACE_SYMBOL(ret, symbol) \
+ ret symbol; \
+ EXPORT_SYMBOL(symbol); \
+ ret symbol
+
+#define LC_CONSTRUCTOR(_func) \
+ void __init _func(void); \
+ void __init _func(void)
+
+#else /* LINUX_KERNEL */
+
+#define DSO_PUBLIC __attribute__((visibility("default")))
+#define DSO_LOCAL __attribute__((visibility("hidden")))
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvariadic-macros"
+
+#define LC_INTERFACE_FUNCTION(ret, symbol, param...) \
+ DSO_PUBLIC ret symbol(param)
+
+#define LC_INIT_FUNCTION(ret, symbol, param...) DSO_PUBLIC ret symbol(param)
+
+#ifdef LC_STATIC
+#define LC_TEST_FUNC(ret, symbol, param...) \
+ int lc_init(unsigned int flags); \
+ static ret __##symbol(param); \
+ ret symbol(param) \
+ { \
+ lc_init(0); \
+ return __##symbol(argc, argv); \
+ } \
+ static ret __##symbol(param)
+#else
+#define LC_TEST_FUNC(ret, symbol, param...) ret symbol(param)
+#endif
+
+#pragma GCC diagnostic pop
+
+#define LC_INTERFACE_SYMBOL(ret, symbol) DSO_PUBLIC ret symbol
+
+#ifdef LC_EFI
+/*
+ * EFI does not have the constructor logic. Thus, mark the constructor functions
+ * as regular non-static functions as they are intended to be called by lc_init.
+ */
+#define LC_CONSTRUCTOR(_func) \
+ void _func(void); \
+ void _func(void)
+#else /* LC_EFI */
+
+#define LC_CONSTRUCTOR(_func) \
+ void __attribute__((constructor)) _func(void); \
+ void _func(void)
+#endif /* LC_EFI */
+
+#endif /* LINUX_KERNEL */
+
+#endif /* VISIBILITY_H */
diff --git a/lib/freebl/leancrypto/xor.h b/lib/freebl/leancrypto/xor.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/xor.h
@@ -0,0 +1,189 @@
+/* Efficient XOR implementation
+ *
+ * Copyright (C) 2022 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef XOR_H
+#define XOR_H
+
+#include "alignment.h"
+#include "ext_headers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void xor_8(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ for (; size; size--)
+ *dst++ ^= *src++;
+}
+
+static inline void xor_32_aligned(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint32_t *dst_word = (uint32_t *)dst;
+ uint32_t *src_word = (uint32_t *)src;
+#pragma GCC diagnostic pop
+
+ for (; size >= sizeof(*src_word); size -= sizeof(*src_word))
+ *dst_word++ ^= *src_word++;
+
+ xor_8((uint8_t *)dst_word, (uint8_t *)src_word, size);
+}
+
+static inline void xor_32(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ if (aligned(src, sizeof(uint32_t) - 1) &&
+ aligned(dst, sizeof(uint32_t) - 1))
+ xor_32_aligned(dst, src, size);
+ else
+ xor_8(dst, src, size);
+}
+
+static inline void xor_64_aligned(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint64_t *dst_dword = (uint64_t *)dst;
+ uint64_t *src_dword = (uint64_t *)src;
+#pragma GCC diagnostic pop
+
+#ifdef __LP64__
+ for (; size >= sizeof(*src_dword); size -= sizeof(*src_dword))
+ *dst_dword++ ^= *src_dword++;
+#endif /* __LP64__ */
+
+ xor_32_aligned((uint8_t *)dst_dword, (uint8_t *)src_dword, size);
+}
+
+/**
+ * @brief Perform XOR operation efficiently
+ *
+ * @param [in,out] dst Data in which the source data is XORed into
+ * @param [in] src Source data which is XORed into the destination
+ * @param [in] size Buffer lengths of both, dst and src
+ */
+static inline void xor_64(uint8_t *dst, const uint8_t *src, size_t size)
+{
+#ifdef __LP64__
+ if (aligned(src, sizeof(uint64_t) - 1) &&
+ aligned(dst, sizeof(uint64_t) - 1))
+ xor_64_aligned(dst, src, size);
+ else
+#endif
+ xor_32(dst, src, size);
+}
+
+static inline void xor_8_3(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ for (; size; size--)
+ *dst++ = *src1++ ^ *src2++;
+}
+
+static inline void xor_32_3_aligned(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint32_t *dst_word = (uint32_t *)dst;
+ uint32_t *src1_word = (uint32_t *)src1;
+ uint32_t *src2_word = (uint32_t *)src2;
+#pragma GCC diagnostic pop
+
+ for (; size >= sizeof(*src1_word); size -= sizeof(*src1_word))
+ *dst_word++ = *src1_word++ ^ *src2_word++;
+
+ xor_8_3((uint8_t *)dst_word, (uint8_t *)src1_word, (uint8_t *)src2_word,
+ size);
+}
+
+static inline void xor_32_3(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ if (aligned(src1, sizeof(uint32_t) - 1) &&
+ aligned(src2, sizeof(uint32_t) - 1) &&
+ aligned(dst, sizeof(uint32_t) - 1))
+ xor_32_3_aligned(dst, src1, src2, size);
+ else
+ xor_8_3(dst, src1, src2, size);
+}
+
+#ifdef __LP64__
+static inline void xor_64_3_aligned(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ uint64_t *dst_dword = (uint64_t *)dst;
+ uint64_t *src1_dword = (uint64_t *)src1;
+ uint64_t *src2_dword = (uint64_t *)src2;
+#pragma GCC diagnostic pop
+
+ for (; size >= sizeof(*src1_dword); size -= sizeof(*src1_dword))
+ *dst_dword++ = *src1_dword++ ^ *src2_dword++;
+
+ xor_32_3_aligned((uint8_t *)dst_dword, (uint8_t *)src1_dword,
+ (uint8_t *)src2_dword, size);
+}
+#endif
+
+/**
+ * @brief Perform XOR operation efficiently
+ *
+ * @param [out] dst Data in which the source data is XORed into
+ * @param [in] src1 1st source data which is XORed into the destination
+ * @param [in] src2 2nd source data which is XORed into the destination
+ * @param [in] size Buffer lengths all buffers dst, src1, and src2
+ */
+static inline void xor_64_3(uint8_t *dst, const uint8_t *src1,
+ const uint8_t *src2, size_t size)
+{
+#ifdef __LP64__
+ if (aligned(src1, sizeof(uint64_t) - 1) &&
+ aligned(src2, sizeof(uint64_t) - 1) &&
+ aligned(dst, sizeof(uint64_t) - 1))
+ xor_64_3_aligned(dst, src1, src2, size);
+ else
+#endif
+ xor_32_3(dst, src1, src2, size);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XOR_H */
diff --git a/lib/freebl/leancrypto/xor256.h b/lib/freebl/leancrypto/xor256.h
new file mode 100644
--- /dev/null
+++ b/lib/freebl/leancrypto/xor256.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2024 - 2025, Stephan Mueller <smueller@chronox.de>
+ *
+ * License: see LICENSE file in root directory
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef XOR256_H
+#define XOR256_H
+
+#include "build_bug_on.h"
+#include "cpufeatures.h"
+#include "xor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LC_HOST_X86_64
+
+/*
+ * AVX2 implementation of XOR (processing 256 bit chunks)
+ */
+#include "ext_headers_x86.h"
+static inline void xor_256_aligned(uint8_t *dst, const uint8_t *src,
+ size_t size)
+{
+ __m256i dst_256, src_256;
+
+ LC_FPU_ENABLE;
+ for (; size >= sizeof(src_256); size -= sizeof(src_256),
+ dst += sizeof(dst_256),
+ src += sizeof(src_256)) {
+ __m128d t;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+ dst_256 = _mm256_set_epi8(dst[31], dst[30], dst[29], dst[28],
+ dst[27], dst[26], dst[25], dst[24],
+ dst[23], dst[22], dst[21], dst[20],
+ dst[19], dst[18], dst[17], dst[16],
+ dst[15], dst[14], dst[13], dst[12],
+ dst[11], dst[10], dst[9], dst[8],
+ dst[7], dst[6], dst[5], dst[4],
+ dst[3], dst[2], dst[1], dst[0]);
+ src_256 = _mm256_set_epi8(src[31], src[30], src[29], src[28],
+ src[27], src[26], src[25], src[24],
+ src[23], src[22], src[21], src[20],
+ src[19], src[18], src[17], src[16],
+ src[15], src[14], src[13], src[12],
+ src[11], src[10], src[9], src[8],
+ src[7], src[6], src[5], src[4],
+ src[3], src[2], src[1], src[0]);
+#pragma GCC diagnostic pop
+
+ dst_256 = _mm256_xor_si256(dst_256, src_256);
+
+ /*
+ * We can ignore the alignment warning as we checked
+ * for proper alignment.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ t = _mm_castsi128_pd(_mm256_castsi256_si128(dst_256));
+ _mm_storel_pd((__attribute__((__may_alias__)) double *)&dst[0],
+ t);
+ _mm_storeh_pd((__attribute__((__may_alias__)) double *)&dst[8],
+ t);
+ t = _mm_castsi128_pd(_mm256_extracti128_si256(dst_256, 1));
+ _mm_storel_pd((__attribute__((__may_alias__)) double *)&dst[16],
+ t);
+ _mm_storeh_pd((__attribute__((__may_alias__)) double *)&dst[24],
+ t);
+#pragma GCC diagnostic pop
+ }
+ LC_FPU_DISABLE;
+
+ /*
+ * As we skip the xor_64 alignment check, guarantee it at compile time.
+ */
+ BUILD_BUG_ON(LC_XOR_AVX2_ALIGNMENT < sizeof(uint64_t));
+ xor_64_aligned(dst, src, size);
+}
+
+static inline void xor_256(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ enum lc_cpu_features feat =
+ lc_cpu_feature_available() & LC_CPU_FEATURE_INTEL_AVX2;
+
+ if (!aligned(src, LC_XOR_AVX2_ALIGNMENT - 1) ||
+ !aligned(dst, LC_XOR_AVX2_ALIGNMENT - 1) || !feat) {
+ xor_64(dst, src, size);
+ } else {
+ xor_256_aligned(dst, src, size);
+ }
+}
+
+#elif (defined(LC_HOST_ARM32_NEON) || defined(LC_HOST_AARCH64)) && \
+ !defined(LINUX_KERNEL)
+
+/*
+ * ARM Neon implementation of XOR (processing 128 bit chunks)
+ */
+/* This code cannot be compiled for the Linux kernel as of now */
+#include <arm_neon.h>
+#include "ext_headers_arm.h"
+static inline void xor_256_aligned(uint8_t *dst, const uint8_t *src,
+ size_t size)
+{
+ uint64x2_t dst_128, src_128;
+
+ if (!aligned(src, sizeof(uint64x2_t) - 1) ||
+ !aligned(dst, sizeof(uint64x2_t) - 1)) {
+ xor_64(dst, src, size);
+ return;
+ }
+
+ LC_NEON_ENABLE;
+ for (; size >= sizeof(src_128); size -= sizeof(src_128),
+ dst += sizeof(dst_128),
+ src += sizeof(src_128)) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ src_128 = vld1q_u64((uint64_t *)src);
+ dst_128 = vld1q_u64((uint64_t *)dst);
+#pragma GCC diagnostic pop
+
+ dst_128 = veorq_u64(dst_128, src_128);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ vst1q_u64((uint64_t *)dst, dst_128);
+#pragma GCC diagnostic pop
+ }
+ LC_NEON_DISABLE;
+
+ /*
+ * As we skip the xor_64 alignment check, guarantee it at compile time.
+ */
+ BUILD_BUG_ON(LC_XOR_NEON_ALIGNMENT < sizeof(uint64_t));
+ xor_64_aligned(dst, src, size);
+}
+
+static inline void xor_256(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ enum lc_cpu_features feat =
+ lc_cpu_feature_available() & LC_CPU_FEATURE_ARM_NEON;
+
+ if (!aligned(src, LC_XOR_NEON_ALIGNMENT - 1) ||
+ !aligned(dst, LC_XOR_NEON_ALIGNMENT - 1) || !feat) {
+ xor_64(dst, src, size);
+ } else {
+ xor_256_aligned(dst, src, size);
+ }
+}
+
+#else /* LC_HOST_X86_64 */
+
+static inline void xor_256(uint8_t *dst, const uint8_t *src, size_t size)
+{
+ xor_64(dst, src, size);
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* XOR256_H */
diff --git a/lib/freebl/loader.c b/lib/freebl/loader.c
--- a/lib/freebl/loader.c
+++ b/lib/freebl/loader.c
@@ -2897,8 +2897,59 @@ X25519_DerivePublicKey(const SECItem *pr
SECStatus
EC_DerivePublicKey(const SECItem *privateKey, const ECParams *ecParams, SECItem *publicKey)
{
if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
return SECFailure;
return (vector->p_EC_DerivePublicKey)(privateKey, ecParams, publicKey);
}
+
+/* ============== New for 3.0031 =============================== */
+
+SECStatus MLDSA_NewKey(CK_ML_DSA_PARAMETER_SET_TYPE paramSet, SECItem *seed,
+ MLDSAPrivateKey *privKey, MLDSAPublicKey *pubKey)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_NewKey)(paramSet, seed, privKey, pubKey);
+}
+SECStatus MLDSA_SignInit(MLDSAPrivateKey *key, CK_HEDGE_TYPE hedgeType,
+ const SECItem *sgnCtx, MLDSAContext **ctx)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_SignInit)(key, hedgeType, sgnCtx, ctx);
+}
+SECStatus MLDSA_SignUpdate(MLDSAContext *ctx, const SECItem *data)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_SignUpdate)(ctx, data);
+}
+SECStatus MLDSA_SignFinal(MLDSAContext *ctx, SECItem *signature)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_SignFinal)(ctx, signature);
+}
+
+SECStatus MLDSA_VerifyInit(MLDSAPublicKey *key, const SECItem *sgnCtx,
+ MLDSAContext **ctx)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_VerifyInit)(key, sgnCtx, ctx);
+}
+SECStatus MLDSA_VerifyUpdate(MLDSAContext *ctx, const SECItem *data)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_VerifyUpdate)(ctx, data);
+}
+SECStatus MLDSA_VerifyFinal(MLDSAContext *ctx, const SECItem *signature)
+{
+ if (!vector && PR_SUCCESS != freebl_RunLoaderOnce())
+ return SECFailure;
+ return (vector->p_MLDSA_VerifyFinal)(ctx, signature);
+}
+
+
diff --git a/lib/freebl/loader.h b/lib/freebl/loader.h
--- a/lib/freebl/loader.h
+++ b/lib/freebl/loader.h
@@ -5,17 +5,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _LOADER_H_
#define _LOADER_H_ 1
#include "blapi.h"
-#define FREEBL_VERSION 0x0327
+#define FREEBL_VERSION 0x0331
struct FREEBLVectorStr {
unsigned short length; /* of this struct in bytes */
unsigned short version; /* of this struct. */
RSAPrivateKey *(*p_RSA_NewKey)(int keySizeInBits,
SECItem *publicExponent);
@@ -926,16 +926,25 @@ struct FREEBLVectorStr {
/* Version 3.028 came to here */
SECStatus (*p_X25519_DerivePublicKey)(const SECItem *privateKey, SECItem *publicKey);
/* Version 3.029 came to here */
SECStatus (*p_EC_DerivePublicKey)(const SECItem *privateKey, const ECParams *ecParams, SECItem *publicKey);
/* Version 3.030 came to here */
+ SECStatus (*p_MLDSA_NewKey)(CK_ML_DSA_PARAMETER_SET_TYPE paramSet, SECItem *seed, MLDSAPrivateKey *privKey, MLDSAPublicKey *pubKey);
+ SECStatus (*p_MLDSA_SignInit)(MLDSAPrivateKey *key, CK_HEDGE_TYPE hedgType, const SECItem *sgnCtx, MLDSAContext **ctx);
+ SECStatus (*p_MLDSA_SignUpdate)(MLDSAContext *ctx, const SECItem *data);
+ SECStatus (*p_MLDSA_SignFinal)(MLDSAContext *ctx, SECItem *signature);
+ SECStatus (*p_MLDSA_VerifyInit)(MLDSAPublicKey *key, const SECItem *sgnCtx, MLDSAContext **ctx);
+ SECStatus (*p_MLDSA_VerifyUpdate)(MLDSAContext *ctx, const SECItem *data) ;
+ SECStatus (*p_MLDSA_VerifyFinal)(MLDSAContext *ctx, const SECItem *signature);
+ /* Version 3.031 came to here */
+
/* Add new function pointers at the end of this struct and bump
* FREEBL_VERSION at the beginning of this file. */
};
typedef struct FREEBLVectorStr FREEBLVector;
#ifdef FREEBL_LOWHASH
#include "nsslowhash.h"
@@ -1034,8 +1043,11 @@ typedef SECStatus (*F_RC2_InitContext)(R
typedef RC2Context *(*F_RC2_AllocateContext)(void);
#endif
typedef SECStatus (*F_Kyber_NewKey)(KyberParams params, const SECItem *seed, SECItem *privKey, SECItem *pubKey);
typedef SECStatus (*F_Kyber_Encapsulate)(KyberParams params, const SECItem *seed, const SECItem *pubKey, SECItem *ciphertext, SECItem *secret);
typedef SECStatus (*F_Kyber_Decapsulate)(KyberParams params, const SECItem *privKey, const SECItem *ciphertext, SECItem *secret);
+
+
+
diff --git a/lib/freebl/manifest.mn b/lib/freebl/manifest.mn
--- a/lib/freebl/manifest.mn
+++ b/lib/freebl/manifest.mn
@@ -82,16 +82,17 @@ DEFINES += -DKYBER_K=3
REQUIRES =
EXPORTS = \
blapit.h \
shsign.h \
ecl-exp.h \
$(LOWHASH_EXPORTS) \
+ $(ML_DSA_EXPORTS) \
$(NULL)
PRIVATE_EXPORTS = \
cmac.h \
alghmac.h \
blake2b.h \
blapi.h \
chacha20poly1305.h \
@@ -108,16 +109,18 @@ MPI_HDRS = mpi-config.h mpi.h mpi-priv.h
MPI_SRCS = mpprime.c mpmontg.c mplogic.c mpi.c mp_gf2m.c
ECL_HDRS = ecl-exp.h ecl.h ecl-priv.h
ECL_SRCS = ecp_25519.c ecp_secp256r1.c ecp_secp384r1.c ecp_secp521r1.c
SHA_SRCS = sha_fast.c
MPCPU_SRCS = mpcpucache.c
VERIFIED_SRCS = $(NULL)
+ML_DSA_SRCS = $(NULL)
+
CSRCS = \
freeblver.c \
ldvector.c \
sysrand.c \
$(SHA_SRCS) \
md2.c \
md5.c \
@@ -152,21 +155,23 @@ CSRCS = \
rsa.c \
rsa_blind.c \
rsapkcs.c \
shvfy.c \
tlsprfalg.c \
jpake.c \
secmpi.c \
kyber.c \
+ ml_dsa.c \
$(KYBER_PQCRYSTALS) \
$(MPI_SRCS) \
$(MPCPU_SRCS) \
$(ECL_SRCS) \
$(VERIFIED_SRCS) \
+ $(ML_DSA_SRCS) \
$(STUBS_SRCS) \
$(LOWHASH_SRCS) \
$(EXTRA_SRCS) \
$(NULL)
ifndef NSS_DISABLE_DEPRECATED_SEED
CSRCS += deprecated/seed.c
endif
diff --git a/lib/freebl/ml_dsa.c b/lib/freebl/ml_dsa.c
new file mode 100644
--- /dev/null
+++ b/lib/freebl/ml_dsa.c
@@ -0,0 +1,552 @@
+/*
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef FREEBL_NO_DEPEND
+#include "stubs.h"
+#endif
+
+#include "prerror.h"
+#include "secerr.h"
+
+#include "prtypes.h"
+#include "prinit.h"
+#include "blapi.h"
+#include "secitem.h"
+#include "blapit.h"
+#include "secport.h"
+#include "nssilock.h"
+#include "secrng.h"
+
+#include "lc_dilithium.h"
+#include "ml_dsa_api.h"
+
+
+/*
+ * some missing utilities, just use the nss implementations
+ */
+int
+lc_memcmp_secure(const void *s1, size_t s1n, const void *s2, size_t s2n)
+{
+ /* NSS's secure function takes on one len, get the min length
+ * to pass to it the point here is to be constant time... so
+ * we do the select checks in constant time: NOTE:this is really
+ * only constant time is the min value is constant, but we can't
+ * overrun the min buffer */
+ PRUint32 s1n_, s2n_, min, res;
+ s1n_ = s1n; s2n_= s2n;
+ min = PORT_CT_SEL(PORT_CT_LT(s1n_,s2n_), s1n_, s2n_);
+ res = NSS_SecureMemcmp(s1, s2, min);
+ return (int)PORT_CT_SEL(PORT_CT_EQ(s1n_,s2n_), res, 1);
+}
+
+#ifdef notdef
+void bin2print(const unsigned char *bin, const size_t binlen, FILE *out,
+ const char *explanation) {
+ if (explanation)
+ fprintf(out, "%s = ", explanation);
+
+ for (int i=0; i < binlen; i++)
+ fprintf(out, "%02x",bin[i]);
+ fprintf(out,"\n");
+}
+#endif
+
+struct MLDSAContextStr {
+ PLArenaPool *arena;
+ MLDSAPrivateKey *privKey;
+ MLDSAPublicKey *pubKey;
+ CK_HEDGE_TYPE hedgeType;
+ CK_ML_DSA_PARAMETER_SET_TYPE paramSet;
+ struct lc_dilithium_ctx lc_dilithium;
+};
+
+#ifdef notdef
+void
+hexprint(const char *label, const unsigned char *buf,
+ unsigned int len, unsigned int max)
+{
+ int i;
+ fprintf(stderr,"%s(%d): ",label, len);
+ len = len > max ? max : len;
+ if (len == 0) {
+ fprintf(stderr, "null\n");
+ return;
+ }
+
+ for (i=0; i < len; i++) {
+ fprintf(stderr,"%02x",buf[i]);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+
+#ifdef NSS_ENABLE_ML_DSA
+static void
+mldsa_DestroyContext(MLDSAContext *ctx)
+{
+ PLArenaPool *arena = ctx->arena;
+
+ /* free up any dangling hashes. Can happen in certain signature
+ * failure cases */
+ lc_hash_zero(&ctx->lc_dilithium.dilithium_hash_ctx);
+
+ /* this zeros out all the arena allocated data, so we don't have to
+ * do any expicit freeing */
+ PORT_FreeArena(arena, PR_TRUE);
+}
+
+static MLDSAContext *
+mldsa_NewContext(const MLDSAPrivateKey *privKey, const MLDSAPublicKey *pubKey)
+{
+ PLArenaPool *arena = NULL;
+ MLDSAContext *ctx = NULL;
+
+ /* must have one and only one of the keys */
+ if (!privKey && !pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ }
+ if (privKey && pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ }
+
+ arena = PORT_NewArena(1024);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ ctx = PORT_ArenaZNew(arena, MLDSAContext);
+ if (!ctx) {
+ goto loser;
+ }
+ ctx->arena = arena;
+ ctx->lc_dilithium.dilithium_hash_ctx.hash = lc_shake256;
+ ctx->lc_dilithium.dilithium_hash_ctx.stream = true;
+ /* we ZNew'd the context, so this is not necessary, but
+ * document it here or hashing won't work if we don't
+ * have a zero'ed context:
+ lctx->lc_dilithium.dilithium_hash_ctx.u.ctx_ptr = NULL; */
+ if (privKey) {
+ ctx->privKey = PORT_ArenaNew(arena, MLDSAPrivateKey);
+ if (ctx->privKey == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(ctx->privKey, privKey, sizeof(MLDSAPrivateKey));
+ }
+ if (pubKey) {
+ ctx->pubKey = PORT_ArenaNew(arena, MLDSAPublicKey);
+ if (ctx->pubKey == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(ctx->pubKey, pubKey, sizeof(MLDSAPublicKey));
+ }
+ return ctx;
+
+loser:
+ if (ctx) {
+ arena = 0;
+ mldsa_DestroyContext(ctx);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return NULL;
+}
+
+static const MLDSAPrivateKey *
+mldsa_ContextGetPrivateKey(const MLDSAContext *ctx)
+{
+ return ctx->privKey;
+}
+
+static const MLDSAPublicKey *
+mldsa_ContextGetPublicKey(const MLDSAContext *ctx)
+{
+ return ctx->pubKey;
+}
+#endif
+
+/*
+** Generate and return a new DSA public and private key pair,
+** both of which are encoded into a single DSAPrivateKey struct.
+** "params" is a pointer to the PQG parameters for the domain
+** Uses a random seed.
+*/
+SECStatus
+MLDSA_NewKey(CK_ML_DSA_PARAMETER_SET_TYPE paramSet, SECItem *seed,
+ MLDSAPrivateKey *privKey, MLDSAPublicKey *pubKey)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ int ret = -1;
+
+ /* make sure we can set the keys first */
+ if (!privKey || !pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ privKey->seedLen = ML_DSA_SEED_LEN;
+ privKey->paramSet = paramSet;
+ pubKey->paramSet = paramSet;
+ if (seed != NULL) {
+ if ((seed->data == NULL) || (seed->len != ML_DSA_SEED_LEN)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ PORT_Memcpy(privKey->seed, seed->data, ML_DSA_SEED_LEN);
+ } else {
+ RNG_SystemRNG(privKey->seed, ML_DSA_SEED_LEN);
+ }
+ privKey->seedLen = ML_DSA_SEED_LEN;
+
+ switch (paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_keypair_from_seed_c(
+ (struct lc_dilithium_44_pk *)pubKey->keyVal,
+ (struct lc_dilithium_44_sk *)privKey->keyVal,
+ privKey->seed, privKey->seedLen);
+ pubKey->keyValLen = ML_DSA_44_PUBLICKEY_LEN;
+ privKey->keyValLen = ML_DSA_44_PRIVATEKEY_LEN;
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_keypair_from_seed_c(
+ (struct lc_dilithium_65_pk *)pubKey->keyVal,
+ (struct lc_dilithium_65_sk *)privKey->keyVal,
+ privKey->seed, privKey->seedLen);
+ pubKey->keyValLen = ML_DSA_65_PUBLICKEY_LEN;
+ privKey->keyValLen = ML_DSA_65_PRIVATEKEY_LEN;
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_keypair_from_seed_c(
+ (struct lc_dilithium_87_pk *)pubKey->keyVal,
+ (struct lc_dilithium_87_sk *)privKey->keyVal,
+ privKey->seed, privKey->seedLen);
+ pubKey->keyValLen = ML_DSA_87_PUBLICKEY_LEN;
+ privKey->keyValLen = ML_DSA_87_PRIVATEKEY_LEN;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret != 0 ) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ return SECSuccess;
+
+loser:
+ PORT_SafeZero(privKey, sizeof(privKey));
+ PORT_SafeZero(pubKey, sizeof(pubKey));
+ return SECFailure;
+#endif
+}
+
+/*
+ * we don't have a streaming interace, so use our own local context
+ * to keep track of things */
+SECStatus
+MLDSA_SignInit(MLDSAPrivateKey *key, CK_HEDGE_TYPE hedgeType,
+ const SECItem *sgnCtx, MLDSAContext **ctx)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ int ret = -1;
+ MLDSAContext *lctx = NULL;
+ if (!ctx || !key || sgnCtx->len > 255) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ lctx = mldsa_NewContext(key, NULL);
+ if (lctx == NULL ) {
+ return SECFailure;
+ }
+ lctx->hedgeType = hedgeType;
+ if (sgnCtx && sgnCtx->len != 0) {
+ lctx->lc_dilithium.userctx = sgnCtx->data;
+ lctx->lc_dilithium.userctxlen = sgnCtx->len;
+ }
+ lctx->paramSet = key->paramSet;
+
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_sign_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_44_sk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_sign_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_65_sk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_sign_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_87_sk *)key->keyVal);
+ break;
+ }
+
+ if (ret < 0) {
+ mldsa_DestroyContext(lctx);
+ return SECFailure;
+ }
+ *ctx = lctx;
+ return SECSuccess;
+#endif
+}
+
+SECStatus
+MLDSA_SignUpdate(MLDSAContext *ctx, const SECItem *data)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ int ret = -1;
+ switch (ctx->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_sign_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_sign_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_sign_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ }
+
+ if (ret < 0) {
+ return SECFailure;
+ }
+ return SECSuccess;
+#endif
+}
+
+
+SECStatus
+MLDSA_SignFinal(MLDSAContext *ctx, SECItem *signature)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ /* make sure we have all the parameters */
+ if (!ctx || !signature) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ const MLDSAPrivateKey *key = mldsa_ContextGetPrivateKey(ctx);
+ int ret = -1;
+ size_t len = signature->len;
+ struct lc_rng_ctx system_rng = {NULL};
+ struct lc_rng_ctx *fake_rng = &system_rng;
+
+ if (!key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (ctx->hedgeType == CKH_DETERMINISTIC_REQUIRED) {
+ fake_rng = NULL;
+ }
+
+ ret = -1;
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ /* handle the case where we are trying to get the signature length
+ * or we supplied a length that was too short */
+ len = ML_DSA_44_SIGNATURE_LEN;
+ if (!signature->data ||
+ (signature->len < len)) {
+ signature->len = len;
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ break;
+ }
+ ret = lc_dilithium_44_sign_final_c(
+ (struct lc_dilithium_44_sig *)signature->data,
+ &ctx->lc_dilithium,
+ (struct lc_dilithium_44_sk *)key->keyVal,
+ fake_rng);
+ break;
+ case CKP_ML_DSA_65:
+ /* handle the case where we are trying to get the signature length
+ * or we supplied a length that was too short */
+ len = ML_DSA_65_SIGNATURE_LEN;
+ if (!signature->data ||
+ (signature->len < len)) {
+ signature->len = len;
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ break;
+ }
+ ret = lc_dilithium_65_sign_final_c(
+ (struct lc_dilithium_65_sig *)signature->data,
+ &ctx->lc_dilithium,
+ (struct lc_dilithium_65_sk *)key->keyVal,
+ fake_rng);
+ break;
+ case CKP_ML_DSA_87:
+ /* handle the case where we are trying to get the signature length
+ * or we supplied a length that was too short */
+ len = ML_DSA_87_SIGNATURE_LEN;
+ if (!signature->data ||
+ (signature->len < len)) {
+ signature->len = len;
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ break;
+ }
+ ret = lc_dilithium_87_sign_final_c(
+ (struct lc_dilithium_87_sig *)signature->data,
+ &ctx->lc_dilithium,
+ (struct lc_dilithium_87_sk *)key->keyVal,
+ fake_rng);
+ break;
+ default:
+ ret = -1;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ break;
+ }
+ if (ret != 0 ) {
+ /* error code already set */
+ return SECFailure;
+ }
+ signature->len = len;
+ mldsa_DestroyContext(ctx);
+ return SECSuccess;
+#endif
+}
+
+/*
+ * we don't have a streaming interace, so use our own local context
+ * to keep track of things */
+SECStatus
+MLDSA_VerifyInit(MLDSAPublicKey *key, const SECItem *sgnCtx, MLDSAContext **ctx)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ MLDSAContext *lctx;
+ int ret = -1;
+ if (!ctx || !key || sgnCtx->len > 255) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ lctx = mldsa_NewContext(NULL, key);
+ if (!lctx) {
+ return SECFailure;
+ }
+ if (sgnCtx && sgnCtx->len != 0) {
+ lctx->lc_dilithium.userctx = sgnCtx->data;
+ lctx->lc_dilithium.userctxlen = sgnCtx->len;
+ }
+ lctx->paramSet = key->paramSet;
+
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_verify_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_44_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_verify_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_65_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_verify_init_c(&lctx->lc_dilithium,
+ (struct lc_dilithium_87_pk *)key->keyVal);
+ break;
+ }
+
+ if (ret < 0) {
+ mldsa_DestroyContext(lctx);
+ return SECFailure;
+ }
+ *ctx = lctx;
+ return SECSuccess;
+#endif
+}
+
+SECStatus
+MLDSA_VerifyUpdate(MLDSAContext *ctx, const SECItem *data)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ int ret = -1;
+ switch (ctx->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_verify_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_verify_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_verify_update_c(&ctx->lc_dilithium,
+ data->data, data->len);
+ break;
+ }
+
+ if (ret < 0) {
+ return SECFailure;
+ }
+ return SECSuccess;
+#endif
+}
+
+SECStatus
+MLDSA_VerifyFinal(MLDSAContext *ctx, const SECItem *signature)
+{
+#ifndef NSS_ENABLE_ML_DSA
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+#else
+ const MLDSAPublicKey *key = mldsa_ContextGetPublicKey(ctx);
+ int ret = -1;
+
+ if (key == NULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ ret = -1;
+ switch (key->paramSet) {
+ case CKP_ML_DSA_44:
+ ret = lc_dilithium_44_verify_final_c(
+ (struct lc_dilithium_44_sig *)signature->data,
+ &ctx->lc_dilithium, (struct lc_dilithium_44_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_65:
+ ret = lc_dilithium_65_verify_final_c(
+ (struct lc_dilithium_65_sig *)signature->data,
+ &ctx->lc_dilithium, (struct lc_dilithium_65_pk *)key->keyVal);
+ break;
+ case CKP_ML_DSA_87:
+ ret = lc_dilithium_87_verify_final_c(
+ (struct lc_dilithium_87_sig *)signature->data,
+ &ctx->lc_dilithium, (struct lc_dilithium_87_pk *)key->keyVal);
+ break;
+ default:
+ ret = -1;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ break;
+ }
+ if (ret != 0 ) {
+ /* in Verify we close the context on an invalid signature as well
+ * as success */
+ mldsa_DestroyContext(ctx);
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ return SECFailure;
+ }
+ mldsa_DestroyContext(ctx);
+ return SECSuccess;
+#endif
+}
+
diff --git a/lib/freebl/stubs.c b/lib/freebl/stubs.c
--- a/lib/freebl/stubs.c
+++ b/lib/freebl/stubs.c
@@ -135,16 +135,17 @@
if (fn) { \
return fn(a1, a2, a3, a4, a5, a6); \
}
#endif
STUB_DECLARE(void *, PORT_Alloc_Util, (size_t len));
STUB_DECLARE(void *, PORT_ArenaAlloc_Util, (PLArenaPool * arena, size_t size));
STUB_DECLARE(void *, PORT_ArenaZAlloc_Util, (PLArenaPool * arena, size_t size));
+STUB_DECLARE(void *, PORT_ArenaGrow_Util, (PLArenaPool * arena, void *ptr, size_t oldsize, size_t newsize));
STUB_DECLARE(void, PORT_Free_Util, (void *ptr));
STUB_DECLARE(void, PORT_FreeArena_Util, (PLArenaPool * arena, PRBool zero));
STUB_DECLARE(int, PORT_GetError_Util, (void));
STUB_DECLARE(PLArenaPool *, PORT_NewArena_Util, (unsigned long chunksize));
STUB_DECLARE(void, PORT_SafeZero, (void *p, size_t n));
STUB_DECLARE(void, PORT_SetError_Util, (int value));
STUB_DECLARE(void *, PORT_ZAlloc_Util, (size_t len));
STUB_DECLARE(void *, PORT_ZAllocAligned_Util, (size_t bytes, size_t alignment, void **mem));
@@ -326,16 +327,25 @@ extern void *
PORT_ArenaZAlloc_stub(PLArenaPool *arena, size_t size)
{
STUB_SAFE_CALL2(PORT_ArenaZAlloc_Util, arena, size);
abort();
return NULL;
}
+extern void *
+PORT_ArenaGrow_stub(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize)
+{
+
+ STUB_SAFE_CALL4(PORT_ArenaGrow_Util, arena, ptr, oldsize, newsize);
+ abort();
+ return NULL;
+}
+
extern void
PORT_FreeArena_stub(PLArenaPool *arena, PRBool zero)
{
STUB_SAFE_CALL2(PORT_FreeArena_Util, arena, zero);
abort();
}
@@ -808,16 +818,17 @@ freebl_InitNSSUtil(void *lib)
{
STUB_FETCH_FUNCTION(PORT_Alloc_Util);
STUB_FETCH_FUNCTION(PORT_Free_Util);
STUB_FETCH_FUNCTION(PORT_ZAlloc_Util);
STUB_FETCH_FUNCTION(PORT_ZFree_Util);
STUB_FETCH_FUNCTION(PORT_NewArena_Util);
STUB_FETCH_FUNCTION(PORT_ArenaAlloc_Util);
STUB_FETCH_FUNCTION(PORT_ArenaZAlloc_Util);
+ STUB_FETCH_FUNCTION(PORT_ArenaGrow_Util);
STUB_FETCH_FUNCTION(PORT_FreeArena_Util);
STUB_FETCH_FUNCTION(PORT_GetError_Util);
STUB_FETCH_FUNCTION(PORT_SetError_Util);
STUB_FETCH_FUNCTION(SECITEM_FreeItem_Util);
STUB_FETCH_FUNCTION(SECITEM_AllocItem_Util);
STUB_FETCH_FUNCTION(SECITEM_CompareItem_Util);
STUB_FETCH_FUNCTION(SECITEM_CopyItem_Util);
STUB_FETCH_FUNCTION(SECITEM_ZfreeItem_Util);
diff --git a/lib/freebl/stubs.h b/lib/freebl/stubs.h
--- a/lib/freebl/stubs.h
+++ b/lib/freebl/stubs.h
@@ -18,16 +18,17 @@
#endif
/* hide libutil rename */
#define _LIBUTIL_H_ 1
#define PORT_Alloc PORT_Alloc_stub
#define PORT_ArenaAlloc PORT_ArenaAlloc_stub
#define PORT_ArenaZAlloc PORT_ArenaZAlloc_stub
+#define PORT_ArenaGrow PORT_ArenaGrow_stub
#define PORT_Free PORT_Free_stub
#define PORT_FreeArena PORT_FreeArena_stub
#define PORT_GetError PORT_GetError_stub
#define PORT_NewArena PORT_NewArena_stub
#define PORT_SafeZero PORT_SafeZero_stub
#define PORT_SetError PORT_SetError_stub
#define PORT_ZAlloc PORT_ZAlloc_stub
#define PORT_ZFree PORT_ZFree_stub
diff --git a/lib/pk11wrap/pk11akey.c b/lib/pk11wrap/pk11akey.c
--- a/lib/pk11wrap/pk11akey.c
+++ b/lib/pk11wrap/pk11akey.c
@@ -21,21 +21,21 @@
#include "secasn1.h"
#include "secoid.h"
#include "secerr.h"
#include "sechash.h"
#include "secpkcs5.h"
#include "blapit.h"
-static SECItem *
-pk11_MakeIDFromPublicKey(SECKEYPublicKey *pubKey)
+const SECItem *
+pk11_GetPublicKeyComponent(SECKEYPublicKey *pubKey)
{
/* set the ID to the public key so we can find it again */
- SECItem *pubKeyIndex = NULL;
+ const SECItem *pubKeyIndex = NULL;
switch (pubKey->keyType) {
case rsaKey:
pubKeyIndex = &pubKey->u.rsa.modulus;
break;
case dsaKey:
pubKeyIndex = &pubKey->u.dsa.publicValue;
break;
case dhKey:
@@ -44,24 +44,67 @@ pk11_MakeIDFromPublicKey(SECKEYPublicKey
case edKey:
case ecKey:
case ecMontKey:
pubKeyIndex = &pubKey->u.ec.publicValue;
break;
case kyberKey:
pubKeyIndex = &pubKey->u.kyber.publicValue;
break;
+ case mldsaKey:
+ pubKeyIndex = &pubKey->u.mldsa.publicValue;
+ break;
default:
return NULL;
}
PORT_Assert(pubKeyIndex != NULL);
+ return pubKeyIndex;
+}
+static SECItem *
+pk11_MakeIDFromPublicKey(SECKEYPublicKey *pubKey)
+{
+ const SECItem *pubKeyIndex = pk11_GetPublicKeyComponent(pubKey);
+ if (pubKeyIndex == NULL) {
+ return NULL;
+ }
return PK11_MakeIDFromPubKey(pubKeyIndex);
}
+KeyType
+pk11_GetKeyTypeFromPKCS11KeyType(CK_KEY_TYPE pk11KeyType)
+{
+ switch (pk11KeyType) {
+ case CKK_RSA:
+ return rsaKey;
+ case CKK_DSA:
+ return dsaKey;
+ case CKK_DH:
+ return dhKey;
+ case CKK_EC:
+ return ecKey;
+ case CKK_EC_MONTGOMERY:
+ return ecMontKey;
+ case CKK_EC_EDWARDS:
+ return edKey;
+#ifndef NSS_DISABLE_KYBER
+ case CKK_NSS_KYBER:
+#endif
+ case CKK_NSS_ML_KEM:
+ case CKK_ML_KEM:
+ return kyberKey;
+ case CKK_ML_DSA:
+ return mldsaKey;
+ default:
+ break;
+ }
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return nullKey;
+}
+
/*
* import a public key into the desired slot
*
* This function takes a public key structure and creates a public key in a
* given slot. If isToken is set, then a persistant public key is created.
*
* Note: it is possible for this function to return a handle for a key which
* is persistant, even if isToken is not set.
@@ -73,17 +116,17 @@ PK11_ImportPublicKey(PK11SlotInfo *slot,
CK_BBOOL cktrue = CK_TRUE;
CK_BBOOL ckfalse = CK_FALSE;
CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
CK_OBJECT_HANDLE objectID;
CK_ATTRIBUTE theTemplate[11];
CK_ATTRIBUTE *signedattr = NULL;
CK_ATTRIBUTE *attrs = theTemplate;
- CK_NSS_KEM_PARAMETER_SET_TYPE kemParams;
+ CK_ULONG paramSet;
SECItem *ckaId = NULL;
SECItem *pubValue = NULL;
int signedcount = 0;
unsigned int templateCount = 0;
SECStatus rv;
/* if we already have an object in the desired slot, use it */
if (!isToken && pubKey->pkcs11Slot == slot) {
@@ -244,62 +287,74 @@ PK11_ImportPublicKey(PK11SlotInfo *slot,
return CK_INVALID_HANDLE;
}
PK11_SETATTRS(attrs, CKA_EC_POINT,
pubValue->data, pubValue->len);
attrs++;
}
break;
case kyberKey:
- /*fprintf(stderr, "PK11_ImportPublic key kyber, params=%d\n",
- pubKey->u.kyber.params);*/
switch (pubKey->u.kyber.params) {
#ifndef NSS_DISABLE_KYBER
case params_kyber768_round3:
case params_kyber768_round3_test_mode:
keyType = CKK_NSS_KYBER;
- kemParams = CKP_NSS_KYBER_768_ROUND3;
+ paramSet = CKP_NSS_KYBER_768_ROUND3;
break;
#endif
case params_ml_kem768:
case params_ml_kem768_test_mode:
keyType = CKK_ML_KEM;
- kemParams = CKP_ML_KEM_768;
+ paramSet = CKP_ML_KEM_768;
break;
case params_ml_kem1024:
case params_ml_kem1024_test_mode:
keyType = CKK_ML_KEM;
- kemParams = CKP_ML_KEM_1024;
+ paramSet = CKP_ML_KEM_1024;
break;
default:
- kemParams = CKP_INVALID_ID;
- break;
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return CK_INVALID_HANDLE;
}
- /*fprintf(stderr, "PK11_ImportPublic KEY_TYPE=0x%08lx, kem_params=0x%08lxd\n",
- keyType, kemParams); */
PK11_SETATTRS(attrs, CKA_PARAMETER_SET,
- &kemParams,
+ &paramSet,
sizeof(CK_ML_KEM_PARAMETER_SET_TYPE));
attrs++;
PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.kyber.publicValue.data,
pubKey->u.kyber.publicValue.len);
attrs++;
break;
+ case mldsaKey:
+ keyType = CKK_ML_DSA;
+ paramSet = SECKEY_MLDSAOidParamsToPkcs11Params(pubKey->u.mldsa.params);
+ if (paramSet == 0) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return CK_INVALID_HANDLE;
+ }
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE,
+ pubKey->u.mldsa.publicValue.data,
+ pubKey->u.mldsa.publicValue.len);
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_PARAMETER_SET, &paramSet,
+ sizeof(CK_ML_DSA_PARAMETER_SET_TYPE));
+ attrs++;
+ break;
default:
if (ckaId) {
SECITEM_FreeItem(ckaId, PR_TRUE);
}
PORT_SetError(SEC_ERROR_BAD_KEY);
return CK_INVALID_HANDLE;
}
templateCount = attrs - theTemplate;
PORT_Assert(templateCount <= (sizeof(theTemplate) / sizeof(CK_ATTRIBUTE)));
- if (pubKey->keyType != ecKey && pubKey->keyType != kyberKey && pubKey->keyType != edKey &&
- pubKey->keyType != ecMontKey) {
+ if (signedattr) {
PORT_Assert(signedattr);
signedcount = attrs - signedattr;
for (attrs = signedattr; signedcount; attrs++, signedcount--) {
pk11_SignedToUnsigned(attrs);
}
}
rv = PK11_CreateNewObject(slot, CK_INVALID_HANDLE, theTemplate,
templateCount, isToken, &objectID);
@@ -664,54 +719,28 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot
PLArenaPool *tmp_arena;
SECKEYPublicKey *pubKey;
unsigned int templateCount = 0;
CK_KEY_TYPE pk11KeyType;
CK_RV crv;
CK_ATTRIBUTE template[8];
CK_ATTRIBUTE *attrs = template;
CK_ATTRIBUTE *modulus, *exponent, *base, *prime, *subprime, *value;
- CK_ATTRIBUTE *ecparams, *kemParams;
+ CK_ATTRIBUTE *ecparams, *paramSet;
/* if we didn't know the key type, get it */
if (keyType == nullKey) {
pk11KeyType = PK11_ReadULongAttribute(slot, id, CKA_KEY_TYPE);
if (pk11KeyType == CK_UNAVAILABLE_INFORMATION) {
return NULL;
}
- switch (pk11KeyType) {
- case CKK_RSA:
- keyType = rsaKey;
- break;
- case CKK_DSA:
- keyType = dsaKey;
- break;
- case CKK_DH:
- keyType = dhKey;
- break;
- case CKK_EC:
- keyType = ecKey;
- break;
- case CKK_EC_MONTGOMERY:
- keyType = ecMontKey;
- break;
- case CKK_EC_EDWARDS:
- keyType = edKey;
- break;
-#ifndef NSS_DISABLE_KYBER
- case CKK_NSS_KYBER:
-#endif
- case CKK_NSS_ML_KEM:
- case CKK_ML_KEM:
- keyType = kyberKey;
- break;
- default:
- PORT_SetError(SEC_ERROR_BAD_KEY);
- return NULL;
+ keyType = pk11_GetKeyTypeFromPKCS11KeyType(pk11KeyType);
+ if (keyType == nullKey) {
+ return NULL;
}
}
/* now we need to create space for the public key */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL)
return NULL;
tmp_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
@@ -862,27 +891,27 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot
crv = pk11_get_Decoded_ECPoint(arena,
&pubKey->u.ec.DEREncodedParams, value,
&pubKey->u.ec.publicValue);
break;
case kyberKey:
value = attrs;
PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0);
attrs++;
- kemParams = attrs;
+ paramSet = attrs;
PK11_SETATTRS(attrs, CKA_PARAMETER_SET, NULL, 0);
attrs++;
templateCount = attrs - template;
PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE));
crv = PK11_GetAttributes(arena, slot, id, template, templateCount);
if (crv != CKR_OK) {
/* try to fetch with the vendor specific
* CKA_NSS_PARAMETER_SET */
- kemParams->type = CKA_NSS_PARAMETER_SET;
+ paramSet->type = CKA_NSS_PARAMETER_SET;
crv = PK11_GetAttributes(arena, slot, id, template,
templateCount);
if (crv != CKR_OK) {
break;
}
}
if (keyClass != CKO_PUBLIC_KEY) {
crv = CKR_OBJECT_HANDLE_INVALID;
@@ -898,21 +927,21 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot
default:
crv = CKR_OBJECT_HANDLE_INVALID;
break;
}
if (crv != CKR_OK) {
break;
}
- if (kemParams->ulValueLen != sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) {
+ if (paramSet->ulValueLen != sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) {
crv = CKR_OBJECT_HANDLE_INVALID;
break;
}
- CK_ML_KEM_PARAMETER_SET_TYPE *pPK11Params = kemParams->pValue;
+ CK_ML_KEM_PARAMETER_SET_TYPE *pPK11Params = paramSet->pValue;
switch (*pPK11Params) {
#ifdef NSS_DISABLE_KYBER
case CKP_NSS_KYBER_768_ROUND3:
pubKey->u.kyber.params = params_kyber768_round3;
break;
#endif
case CKP_NSS_ML_KEM_768:
case CKP_ML_KEM_768:
@@ -922,16 +951,43 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot
pubKey->u.kyber.params = params_ml_kem1024;
break;
default:
pubKey->u.kyber.params = params_kyber_invalid;
break;
}
crv = pk11_Attr2SecItem(arena, value, &pubKey->u.kyber.publicValue);
break;
+ case mldsaKey:
+ value = attrs;
+ PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0);
+ attrs++;
+ paramSet = attrs;
+ PK11_SETATTRS(attrs, CKA_PARAMETER_SET, NULL, 0);
+ attrs++;
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE));
+
+ crv = PK11_GetAttributes(arena, slot, id, template, templateCount);
+ if (crv != CKR_OK) {
+ break;
+ }
+ if (keyClass != CKO_PUBLIC_KEY) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+
+ if (paramSet->ulValueLen != sizeof(CK_ML_DSA_PARAMETER_SET_TYPE)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ pubKey->u.mldsa.params = SECKEY_MLDSAPkcs11ParamsToOidParams(
+ *(CK_ML_DSA_PARAMETER_SET_TYPE *)paramSet->pValue);
+ crv = pk11_Attr2SecItem(arena, value, &pubKey->u.mldsa.publicValue);
+ break;
case fortezzaKey:
case nullKey:
default:
crv = CKR_OBJECT_HANDLE_INVALID;
break;
}
PORT_FreeArena(tmp_arena, PR_FALSE);
@@ -959,47 +1015,19 @@ PK11_MakePrivKey(PK11SlotInfo *slot, Key
SECStatus rv;
/* don't know? look it up */
if (keyType == nullKey) {
CK_KEY_TYPE pk11Type = CKK_RSA;
pk11Type = PK11_ReadULongAttribute(slot, privID, CKA_KEY_TYPE);
isTemp = (PRBool)!PK11_HasAttributeSet(slot, privID, CKA_TOKEN, PR_FALSE);
- switch (pk11Type) {
- case CKK_RSA:
- keyType = rsaKey;
- break;
- case CKK_DSA:
- keyType = dsaKey;
- break;
- case CKK_DH:
- keyType = dhKey;
- break;
- case CKK_KEA:
- keyType = fortezzaKey;
- break;
- case CKK_EC:
- keyType = ecKey;
- break;
- case CKK_EC_MONTGOMERY:
- keyType = ecMontKey;
- break;
- case CKK_EC_EDWARDS:
- keyType = edKey;
- break;
-#ifndef NSS_DISABLE_KYBER
- case CKK_NSS_KYBER:
-#endif
- case CKK_NSS_ML_KEM:
- case CKK_ML_KEM:
- keyType = kyberKey;
- break;
- default:
- break;
+ keyType = pk11_GetKeyTypeFromPKCS11KeyType(pk11Type);
+ if (keyType == nullKey) {
+ return NULL;
}
}
/* if the key is private, make sure we are authenticated to the
* token before we try to use it */
isPrivate = (PRBool)PK11_HasAttributeSet(slot, privID, CKA_PRIVATE, PR_FALSE);
if (isPrivate) {
rv = PK11_Authenticate(slot, PR_TRUE, wincx);
@@ -1105,16 +1133,17 @@ pk11_loadPrivKeyWithFlags(PK11SlotInfo *
{ CKA_EXPONENT_1, NULL, 0 },
{ CKA_EXPONENT_2, NULL, 0 },
{ CKA_COEFFICIENT, NULL, 0 },
{ CKA_DECRYPT, NULL, 0 },
{ CKA_DERIVE, NULL, 0 },
{ CKA_SIGN, NULL, 0 },
{ CKA_SIGN_RECOVER, NULL, 0 },
{ CKA_UNWRAP, NULL, 0 },
+ { CKA_ENCAPSULATE, NULL, 0 },
/* reserve space for the attributes that may be
* specified in attrFlags */
{ CKA_TOKEN, NULL, 0 },
{ CKA_PRIVATE, NULL, 0 },
{ CKA_MODIFIABLE, NULL, 0 },
{ CKA_SENSITIVE, NULL, 0 },
{ CKA_EXTRACTABLE, NULL, 0 },
#define NUM_RESERVED_ATTRS 5 /* number of reserved attributes above */
@@ -1214,16 +1243,34 @@ pk11_loadPrivKeyWithFlags(PK11SlotInfo *
extra_count++;
}
ap->type = CKA_SIGN;
ap++;
count++;
extra_count++;
break;
+ case mldsaKey:
+ ap->type = CKA_VALUE;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_SEED;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_PARAMETER_SET;
+ ap++;
+ count++;
+ extra_count++;
+ ap->type = CKA_SIGN;
+ ap++;
+ count++;
+ extra_count++;
+ break;
default:
count = 0;
extra_count = 0;
break;
}
if (count == 0) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
@@ -1400,17 +1447,17 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot
/*CK_ULONG key_size = 0;*/
CK_ATTRIBUTE *pubTemplate;
int privCount = 0;
int pubCount = 0;
PK11RSAGenParams *rsaParams;
SECKEYPQGParams *dsaParams;
SECKEYDHParams *dhParams;
- CK_NSS_KEM_PARAMETER_SET_TYPE *kemParams;
+ CK_ULONG *paramSet;
CK_MECHANISM mechanism;
CK_MECHANISM test_mech;
CK_MECHANISM test_mech2;
CK_SESSION_HANDLE session_handle;
CK_RV crv;
CK_OBJECT_HANDLE privID, pubID;
SECKEYPrivateKey *privKey;
KeyType keyType;
@@ -1610,25 +1657,36 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot
goto ml_kem_gen;
#endif
case CKM_NSS_ML_KEM_KEY_PAIR_GEN:
test_mech.mechanism = CKM_NSS_ML_KEM;
goto ml_kem_gen;
case CKM_ML_KEM_KEY_PAIR_GEN:
test_mech.mechanism = CKM_ML_KEM;
ml_kem_gen:
- kemParams = (CK_NSS_KEM_PARAMETER_SET_TYPE *)param;
+ paramSet = (CK_ULONG *)param;
attrs = kyberPubTemplate;
- PK11_SETATTRS(attrs, CKA_NSS_PARAMETER_SET,
- kemParams,
- sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE));
+ PK11_SETATTRS(attrs, CKA_PARAMETER_SET,
+ paramSet,
+ sizeof(CK_ML_KEM_PARAMETER_SET_TYPE));
attrs++;
pubTemplate = kyberPubTemplate;
keyType = kyberKey;
break;
+ case CKM_ML_DSA_KEY_PAIR_GEN:
+ test_mech.mechanism = CKM_ML_DSA;
+ paramSet = (CK_ULONG *)param;
+ attrs = kyberPubTemplate;
+ PK11_SETATTRS(attrs, CKA_PARAMETER_SET,
+ paramSet,
+ sizeof(CK_ML_DSA_PARAMETER_SET_TYPE));
+ attrs++;
+ pubTemplate = kyberPubTemplate;
+ keyType = mldsaKey;
+ break;
case CKM_EC_MONTGOMERY_KEY_PAIR_GEN:
ecParams = (SECKEYECParams *)param;
attrs = ecPubTemplate;
PK11_SETATTRS(attrs, CKA_EC_PARAMS, ecParams->data,
ecParams->len);
attrs++;
pubTemplate = ecPubTemplate;
keyType = ecMontKey;
@@ -1705,16 +1763,19 @@ ml_kem_gen:
break;
#ifndef NSS_DISABLE_KYBER
case CKM_NSS_KYBER:
#endif
case CKM_NSS_ML_KEM:
case CKM_ML_KEM:
mechanism_info.flags = CKF_ENCAPSULATE|CKF_DECAPSULATE;
break;
+ case CKM_ML_DSA:
+ mechanism_info.flags = CKF_SIGN|CKF_VERIFY;
+ break;
default:
break;
}
}
/* now adjust our flags according to the user's key usage passed to us */
mechanism_info.flags = (mechanism_info.flags & (~opFlagsMask)) | opFlags;
/* set the public key attributes */
attrs += pk11_AttrFlagsToAttributes(pubKeyAttrFlags, attrs,
@@ -1938,16 +1999,17 @@ PK11_MakeKEAPubKey(unsigned char *keyDat
SECStatus
SECKEY_SetPublicValue(SECKEYPrivateKey *privKey, SECItem *publicValue)
{
SECStatus rv;
SECKEYPublicKey pubKey;
PLArenaPool *arena;
PK11SlotInfo *slot;
CK_OBJECT_HANDLE privKeyID;
+ CK_ULONG paramSet;
if (privKey == NULL || publicValue == NULL ||
publicValue->data == NULL || publicValue->len == 0) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
pubKey.arena = NULL;
@@ -2004,16 +2066,31 @@ SECKEY_SetPublicValue(SECKEYPrivateKey *
case edKey:
case ecMontKey:
pubKey.u.ec.publicValue = *publicValue;
pubKey.u.ec.encoding = ECPoint_Undefined;
pubKey.u.ec.size = 0;
rv = PK11_ReadAttribute(slot, privKeyID, CKA_EC_PARAMS,
arena, &pubKey.u.ec.DEREncodedParams);
break;
+ case mldsaKey:
+ pubKey.u.mldsa.publicValue = *publicValue;
+ paramSet = PK11_ReadULongAttribute(slot, privKeyID,
+ CKA_PARAMETER_SET);
+ if (paramSet == CK_UNAVAILABLE_INFORMATION) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ }
+ pubKey.u.mldsa.params = SECKEY_MLDSAPkcs11ParamsToOidParams(paramSet);
+ if (pubKey.u.mldsa.params == SEC_OID_UNKNOWN) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ }
+ rv = SECSuccess;
+ break;
}
if (rv == SECSuccess) {
rv = PK11_ImportPublicKey(slot, &pubKey, PR_TRUE);
}
/* Even though pubKey is stored on the stack, we've allocated
* some of it's data from the arena. SECKEY_DestroyPublicKey
* destroys keys by freeing the arena, so this will clean up all
* the data we allocated specifically for the key above. It will
@@ -2132,16 +2209,21 @@ PK11_ImportEncryptedPrivateKeyInfoAndRet
usage = edUsage;
usageCount = 1;
break;
case ecMontKey:
key_type = CKK_EC_MONTGOMERY;
usage = dhUsage;
usageCount = 1;
break;
+ case mldsaKey:
+ key_type = CKK_ML_DSA;
+ usage = dsaUsage;
+ usageCount = sizeof(dsaUsage) / sizeof(dsaUsage[0]);
+ break;
}
try_faulty_3des:
key = PK11_PBEKeyGen(slot, &epki->algorithm, pwitem, faulty3DES, wincx);
if (key == NULL) {
rv = SECFailure;
goto done;
@@ -2751,17 +2833,17 @@ PK11_FindKeyByKeyID(PK11SlotInfo *slot,
}
/*
* Generate a CKA_ID from the relevant public key data. The CKA_ID is generated
* from the pubKeyData by SHA1_Hashing it to produce a smaller CKA_ID (to make
* smart cards happy.
*/
SECItem *
-PK11_MakeIDFromPubKey(SECItem *pubKeyData)
+PK11_MakeIDFromPubKey(const SECItem *pubKeyData)
{
PK11Context *context;
SECItem *certCKA_ID;
SECStatus rv;
if (pubKeyData->len <= SHA1_LENGTH) {
/* probably an already hashed value. The strongest known public
* key values <= 160 bits would be less than 40 bit symetric in
diff --git a/lib/pk11wrap/pk11cert.c b/lib/pk11wrap/pk11cert.c
--- a/lib/pk11wrap/pk11cert.c
+++ b/lib/pk11wrap/pk11cert.c
@@ -13,16 +13,17 @@
#include "secmodi.h"
#include "secmodti.h"
#include "pkcs11.h"
#include "pk11func.h"
#include "cert.h"
#include "certi.h"
#include "secitem.h"
#include "keyhi.h"
+#include "keyi.h"
#include "secoid.h"
#include "pkcs7t.h"
#include "cmsreclist.h"
#include "certdb.h"
#include "secerr.h"
#include "sslerr.h"
@@ -150,16 +151,17 @@ PK11_IsUserCert(PK11SlotInfo *slot, CERT
SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(cert);
CK_ATTRIBUTE theTemplate;
if (pubKey == NULL) {
return PR_FALSE;
}
PK11_SETATTRS(&theTemplate, 0, NULL, 0);
+
switch (pubKey->keyType) {
case rsaKey:
case rsaPssKey:
case rsaOaepKey:
PK11_SETATTRS(&theTemplate, CKA_MODULUS, pubKey->u.rsa.modulus.data,
pubKey->u.rsa.modulus.len);
break;
case dsaKey:
@@ -172,16 +174,20 @@ PK11_IsUserCert(PK11SlotInfo *slot, CERT
break;
case ecKey:
case edKey:
case ecMontKey:
PK11_SETATTRS(&theTemplate, CKA_EC_POINT,
pubKey->u.ec.publicValue.data,
pubKey->u.ec.publicValue.len);
break;
+ case mldsaKey:
+ PK11_SETATTRS(&theTemplate, CKA_VALUE, pubKey->u.mldsa.publicValue.data,
+ pubKey->u.mldsa.publicValue.len);
+ break;
case keaKey:
case fortezzaKey:
case kyberKey:
case nullKey:
/* fall through and return false */
break;
}
@@ -1093,40 +1099,25 @@ PK11_FindCertsFromNickname(const char *n
* extract a key ID for a certificate...
* NOTE: We call this function from PKCS11.c If we ever use
* pkcs11 to extract the public key (we currently do not), this will break.
*/
SECItem *
PK11_GetPubIndexKeyID(CERTCertificate *cert)
{
SECKEYPublicKey *pubk;
+ const SECItem *oldItem;
SECItem *newItem = NULL;
pubk = CERT_ExtractPublicKey(cert);
if (pubk == NULL)
return NULL;
-
- switch (pubk->keyType) {
- case rsaKey:
- newItem = SECITEM_DupItem(&pubk->u.rsa.modulus);
- break;
- case dsaKey:
- newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue);
- break;
- case dhKey:
- newItem = SECITEM_DupItem(&pubk->u.dh.publicValue);
- break;
- case ecKey:
- case edKey:
- case ecMontKey:
- newItem = SECITEM_DupItem(&pubk->u.ec.publicValue);
- break;
- case fortezzaKey:
- default:
- newItem = NULL; /* Fortezza Fix later... */
+ oldItem = pk11_GetPublicKeyComponent(pubk);
+ if (oldItem) {
+ newItem = SECITEM_DupItem(oldItem);
}
SECKEY_DestroyPublicKey(pubk);
/* make hash of it */
return newItem;
}
/*
* generate a CKA_ID from a certificate.
diff --git a/lib/pk11wrap/pk11mech.c b/lib/pk11wrap/pk11mech.c
--- a/lib/pk11wrap/pk11mech.c
+++ b/lib/pk11wrap/pk11mech.c
@@ -1934,16 +1934,18 @@ PK11_MapSignKeyType(KeyType keyType)
return CKM_RSA_PKCS;
case fortezzaKey:
case dsaKey:
return CKM_DSA;
case ecKey:
return CKM_ECDSA;
case edKey:
return CKM_EDDSA;
+ case mldsaKey:
+ return CKM_ML_DSA;
case dhKey:
default:
break;
}
return CKM_INVALID_MECHANISM;
}
CK_MECHANISM_TYPE
diff --git a/lib/pk11wrap/pk11obj.c b/lib/pk11wrap/pk11obj.c
--- a/lib/pk11wrap/pk11obj.c
+++ b/lib/pk11wrap/pk11obj.c
@@ -11,16 +11,17 @@
#include "secder.h"
#include "secmod.h"
#include "secmodi.h"
#include "secmodti.h"
#include "pkcs11.h"
#include "pkcs11t.h"
#include "pk11func.h"
#include "keyhi.h"
+#include "keyi.h"
#include "secitem.h"
#include "secerr.h"
#include "sslerr.h"
#define PK11_SEARCH_CHUNKSIZE 10
/*
* Build a block big enough to hold the data
@@ -547,16 +548,17 @@ pk11_backupGetSignLength(SECKEYPrivateKe
*/
int
PK11_SignatureLen(SECKEYPrivateKey *key)
{
int val;
SECItem attributeItem = { siBuffer, NULL, 0 };
SECStatus rv;
int length;
+ SECOidTag paramSet;
switch (key->keyType) {
case rsaKey:
case rsaPssKey:
val = PK11_GetPrivateModulusLen(key);
if (val == -1) {
return pk11_backupGetSignLength(key);
}
@@ -585,16 +587,20 @@ PK11_SignatureLen(SECKEYPrivateKey *key)
length = SECKEY_ECParamsToBasePointOrderLen(&attributeItem);
PORT_Free(attributeItem.data);
if (length != 0) {
length = ((length + 7) / 8) * 2;
return length;
}
}
return pk11_backupGetSignLength(key);
+ case mldsaKey:
+ paramSet = SECKEY_GetParameterSet(key);
+ return SECKEY_MLDSAOidParamsToLen(paramSet, SECKEYSignatureType);
+
default:
break;
}
PORT_SetError(SEC_ERROR_INVALID_KEY);
return 0;
}
/*
diff --git a/lib/pk11wrap/pk11pars.c b/lib/pk11wrap/pk11pars.c
--- a/lib/pk11wrap/pk11pars.c
+++ b/lib/pk11wrap/pk11pars.c
@@ -472,16 +472,22 @@ static const oidValDef signOptList[] = {
{ CIPHER_NAME("RSA-PKCS"), SEC_OID_PKCS1_RSA_ENCRYPTION,
NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
{ CIPHER_NAME("RSA-PSS"), SEC_OID_PKCS1_RSA_PSS_SIGNATURE,
NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
{ CIPHER_NAME("ECDSA"), SEC_OID_ANSIX962_EC_PUBLIC_KEY,
NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
{ CIPHER_NAME("ED25519"), SEC_OID_ED25519_PUBLIC_KEY,
NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("ML-DSA-44"), SEC_OID_ML_DSA_44,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("ML-DSA-65"), SEC_OID_ML_DSA_65,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
+ { CIPHER_NAME("ML-DSA-87"), SEC_OID_ML_DSA_87,
+ NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE },
};
typedef struct {
const oidValDef *list;
PRUint32 entries;
const char *description;
PRBool allowEmpty;
} algListsDef;
diff --git a/lib/pk11wrap/pk11pk12.c b/lib/pk11wrap/pk11pk12.c
--- a/lib/pk11wrap/pk11pk12.c
+++ b/lib/pk11wrap/pk11pk12.c
@@ -12,16 +12,17 @@
#include "secmod.h"
#include "secmodi.h"
#include "secmodti.h"
#include "secmodt.h"
#include "pkcs11.h"
#include "pk11func.h"
#include "secitem.h"
#include "keyhi.h"
+#include "keyi.h"
#include "secoid.h"
#include "secasn1.h"
#include "secerr.h"
#include "prerror.h"
/* These data structures should move to a common .h file shared between the
* wrappers and the pkcs 12 code. */
@@ -74,27 +75,35 @@ struct SECKEYECPrivateKeyStr {
PLArenaPool *arena;
SECItem version;
SECItem curveOID; /* optional/ignored */
SECItem publicValue; /* required (for now) */
SECItem privateValue;
};
typedef struct SECKEYECPrivateKeyStr SECKEYECPrivateKey;
+struct SECKEYMLDSAPrivateKeyStr {
+ SECOidTag params;
+ SECItem privateValue;
+ SECItem seed;
+};
+typedef struct SECKEYMLDSAPrivateKeyStr SECKEYMLDSAPrivateKey;
+
/*
** raw private key object
*/
struct SECKEYRawPrivateKeyStr {
PLArenaPool *arena;
KeyType keyType;
union {
SECKEYRSAPrivateKey rsa;
SECKEYDSAPrivateKey dsa;
SECKEYDHPrivateKey dh;
SECKEYECPrivateKey ec;
+ SECKEYMLDSAPrivateKey mldsa;
} u;
};
typedef struct SECKEYRawPrivateKeyStr SECKEYRawPrivateKey;
SEC_ASN1_MKSUB(SEC_AnyTemplate)
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
/* ASN1 Templates for new decoder/encoder */
@@ -150,16 +159,38 @@ const SEC_ASN1Template SECKEY_DSAPrivate
};
const SEC_ASN1Template SECKEY_DHPrivateKeyExportTemplate[] = {
{ SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.privateValue) },
{ SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.base) },
{ SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.dh.prime) },
};
+const SEC_ASN1Template SECKEY_MLDSAPrivateKeyBothExportTemplate[] = {
+ { SEC_ASN1_CHOICE, 0, NULL, sizeof(SECKEYRawPrivateKey)},
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SECKEYRawPrivateKey, u.mldsa.seed) },
+ { SEC_ASN1_OCTET_STRING, offsetof(SECKEYRawPrivateKey, u.mldsa.privateValue) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_MLDSAPrivateKeySeedExportTemplate[] = {
+ { SEC_ASN1_CHOICE, 0, NULL, sizeof(SECKEYRawPrivateKey)},
+ { SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SECKEYRawPrivateKey, u.mldsa.seed),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template SECKEY_MLDSAPrivateKeyKeyExportTemplate[] = {
+ { SEC_ASN1_CHOICE, 0, NULL, sizeof(SECKEYRawPrivateKey)},
+ { SEC_ASN1_OCTET_STRING, offsetof(SECKEYRawPrivateKey, u.mldsa.privateValue) },
+ { 0 }
+};
+
SEC_ASN1_MKSUB(SEC_BitStringTemplate)
SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
const SEC_ASN1Template SECKEY_ECPrivateKeyExportTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYRawPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(SECKEYRawPrivateKey, u.ec.version) },
{ SEC_ASN1_OCTET_STRING,
offsetof(SECKEYRawPrivateKey, u.ec.privateValue) },
@@ -330,16 +361,17 @@ PK11_ImportAndReturnPrivateKey(PK11SlotI
CK_ATTRIBUTE theTemplate[20];
int templateCount = 0;
SECStatus rv = SECFailure;
CK_ATTRIBUTE *attrs;
CK_ATTRIBUTE *signedattr = NULL;
int signedcount = 0;
CK_ATTRIBUTE *ap;
SECItem *ck_id = NULL;
+ CK_ULONG paramSet;
attrs = theTemplate;
PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
attrs++;
PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
attrs++;
PK11_SETATTRS(attrs, CKA_TOKEN, isPerm ? &cktrue : &ckfalse,
@@ -423,17 +455,17 @@ PK11_ImportAndReturnPrivateKey(PK11SlotI
}
if (PK11_IsInternal(slot)) {
PK11_SETATTRS(attrs, CKA_NSS_DB,
publicValue->data, publicValue->len);
attrs++;
}
PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL));
attrs++;
- PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL));
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &ckfalse, sizeof(CK_BBOOL));
attrs++;
if (nickname) {
PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
attrs++;
}
ck_id = PK11_MakeIDFromPubKey(publicValue);
if (ck_id == NULL) {
goto loser;
@@ -497,19 +529,17 @@ PK11_ImportAndReturnPrivateKey(PK11SlotI
lpk->u.ec.publicValue.len);
attrs++;
}
}
PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
- PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
- (keyUsage & KU_DIGITAL_SIGNATURE) ? &cktrue
- : &ckfalse,
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &ckfalse,
sizeof(CK_BBOOL));
attrs++;
PK11_SETATTRS(attrs, CKA_DERIVE, (keyUsage & KU_KEY_AGREEMENT) ? &cktrue : &ckfalse,
sizeof(CK_BBOOL));
attrs++;
if (nickname) {
PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
attrs++;
@@ -566,24 +596,76 @@ PK11_ImportAndReturnPrivateKey(PK11SlotI
PK11_SETATTRS(attrs, CKA_EC_PARAMS, lpk->u.ec.curveOID.data,
lpk->u.ec.curveOID.len);
attrs++;
PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.ec.privateValue.data,
lpk->u.ec.privateValue.len);
attrs++;
break;
+ case mldsaKey:
+ keyType = CKK_ML_DSA;
+ /* we need at least one of these to import into PKCS #11.
+ * if we have only one, it may still fail, but that is up
+ * to the token */
+ if ((lpk->u.mldsa.seed.len == 0) &&
+ (lpk->u.mldsa.privateValue.len == 0)) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &ckfalse, sizeof(CK_BBOOL));
+ attrs++;
+ /* if we have the public value, we can do more, without it
+ * we won't be able to set the ck_id properly, which will make
+ * this key effectively invisible. The application will need
+ * to update the ID before it looses it's handle */
+ if (publicValue != NULL) {
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NSS_DB,
+ publicValue->data, publicValue->len);
+ attrs++;
+ }
+ ck_id = PK11_MakeIDFromPubKey(publicValue);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len);
+ attrs++;
+ }
+ if (nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ paramSet = SECKEY_MLDSAOidParamsToPkcs11Params(lpk->u.mldsa.params);
+ PK11_SETATTRS(attrs, CKA_PARAMETER_SET, (unsigned char *)&paramSet,
+ sizeof(CK_ML_DSA_PARAMETER_SET_TYPE));
+ attrs++;
+ if (lpk->u.mldsa.seed.len) {
+ PK11_SETATTRS(attrs, CKA_SEED, lpk->u.mldsa.seed.data,
+ lpk->u.mldsa.seed.len);
+ attrs++;
+ }
+ if (lpk->u.mldsa.privateValue.len) {
+ PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.mldsa.privateValue.data,
+ lpk->u.mldsa.privateValue.len);
+ attrs++;
+ }
+ break;
default:
PORT_SetError(SEC_ERROR_BAD_KEY);
goto loser;
}
templateCount = attrs - theTemplate;
PORT_Assert(templateCount <= sizeof(theTemplate) / sizeof(CK_ATTRIBUTE));
- if (lpk->keyType != ecKey && lpk->keyType != edKey && lpk->keyType != ecMontKey) {
- PORT_Assert(signedattr);
+ /* we used to assert unless the key didn't need signedattrs, but that's
+ * now true of almost all modern keys, so now if the key has signedattrs
+ * the just need to set the value */
+ if (signedattr) {
signedcount = attrs - signedattr;
for (ap = signedattr; signedcount; ap++, signedcount--) {
pk11_SignedToUnsigned(ap);
}
}
rv = PK11_CreateNewObject(slot, CK_INVALID_HANDLE,
theTemplate, templateCount, isPerm, &objectID);
@@ -608,31 +690,33 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK
PRBool isPerm, PRBool isPrivate, unsigned int keyUsage,
SECKEYPrivateKey **privk, void *wincx)
{
SECStatus rv = SECFailure;
SECKEYRawPrivateKey *lpk = NULL;
const SEC_ASN1Template *keyTemplate, *paramTemplate;
void *paramDest = NULL;
PLArenaPool *arena = NULL;
+ SECOidTag algTag;
arena = PORT_NewArena(2048);
if (!arena) {
return SECFailure;
}
/* need to change this to use RSA/DSA keys */
lpk = (SECKEYRawPrivateKey *)PORT_ArenaZAlloc(arena,
sizeof(SECKEYRawPrivateKey));
if (lpk == NULL) {
goto loser;
}
lpk->arena = arena;
- switch (SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ algTag = SECOID_GetAlgorithmTag(&pki->algorithm);
+ switch (algTag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
prepare_rsa_priv_key_export_for_asn1(lpk);
keyTemplate = SECKEY_RSAPrivateKeyExportTemplate;
paramTemplate = NULL;
paramDest = NULL;
lpk->keyType = rsaKey;
break;
case SEC_OID_ANSIX9_DSA_SIGNATURE:
@@ -666,17 +750,40 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK
break;
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
prepare_ec_priv_key_export_for_asn1(lpk);
keyTemplate = SECKEY_ECPrivateKeyExportTemplate;
paramTemplate = NULL;
paramDest = NULL;
lpk->keyType = ecKey;
break;
-
+ case SEC_OID_ML_DSA_44:
+ case SEC_OID_ML_DSA_65:
+ case SEC_OID_ML_DSA_87:
+ /* choice */
+ switch (pki->privateKey.data[0]) {
+ case SEC_ASN1_CONTEXT_SPECIFIC|0:
+ keyTemplate = SECKEY_MLDSAPrivateKeySeedExportTemplate;
+ break;
+ case SEC_ASN1_OCTET_STRING:
+ keyTemplate = SECKEY_MLDSAPrivateKeyKeyExportTemplate;
+ break;
+ case SEC_ASN1_CONSTRUCTED|SEC_ASN1_SEQUENCE:
+ keyTemplate = SECKEY_MLDSAPrivateKeyBothExportTemplate;
+ break;
+ default:
+ keyTemplate = NULL;
+ PORT_SetError(SEC_ERROR_BAD_DER);
+ break;
+ }
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = mldsaKey;
+ lpk->u.mldsa.params = algTag;
+ break;
default:
keyTemplate = NULL;
paramTemplate = NULL;
paramDest = NULL;
break;
}
if (!keyTemplate) {
diff --git a/lib/pk11wrap/pk11pub.h b/lib/pk11wrap/pk11pub.h
--- a/lib/pk11wrap/pk11pub.h
+++ b/lib/pk11wrap/pk11pub.h
@@ -681,17 +681,17 @@ PK11SymKey *PK11_ConvertSessionSymKeyToT
SECKEYPrivateKey *PK11_ConvertSessionPrivKeyToTokenPrivKey(
SECKEYPrivateKey *privk, void *wincx);
SECKEYPrivateKey *PK11_CopyTokenPrivKeyToSessionPrivKey(PK11SlotInfo *destSlot,
SECKEYPrivateKey *privKey);
/**********************************************************************
* Certs
**********************************************************************/
-SECItem *PK11_MakeIDFromPubKey(SECItem *pubKeyData);
+SECItem *PK11_MakeIDFromPubKey(const SECItem *pubKeyData);
SECStatus PK11_TraverseSlotCerts(
SECStatus (*callback)(CERTCertificate *, SECItem *, void *),
void *arg, void *wincx);
CERTCertificate *PK11_FindCertFromNickname(const char *nickname, void *wincx);
CERTCertificate *PK11_FindCertFromURI(const char *uri, void *wincx);
CERTCertList *PK11_FindCertsFromURI(const char *uri, void *wincx);
CERTCertList *PK11_FindCertsFromEmailAddress(const char *email, void *wincx);
CERTCertList *PK11_FindCertsFromNickname(const char *nickname, void *wincx);
diff --git a/lib/pk11wrap/pk11skey.c b/lib/pk11wrap/pk11skey.c
--- a/lib/pk11wrap/pk11skey.c
+++ b/lib/pk11wrap/pk11skey.c
@@ -2123,23 +2123,17 @@ PK11_PubDerive(SECKEYPrivateKey *privKey
/* Message is or'd with a real Attribute (CKA_ENCRYPT, CKA_DECRYPT),
* etc. Strip out the real attribute here */
operation &= ~CKA_NSS_MESSAGE_MASK;
}
symKey->origin = PK11_OriginDerive;
switch (privKey->keyType) {
- case rsaKey:
- case rsaPssKey:
- case rsaOaepKey:
- case kyberKey:
- case nullKey:
- case edKey:
- case ecMontKey:
+ default:
PORT_SetError(SEC_ERROR_BAD_KEY);
break;
case dsaKey:
case keaKey:
case fortezzaKey: {
static unsigned char rb_email[128] = { 0 };
CK_KEA_DERIVE_PARAMS param;
param.isSender = (CK_BBOOL)isSender;
diff --git a/lib/pk11wrap/secmodi.h b/lib/pk11wrap/secmodi.h
--- a/lib/pk11wrap/secmodi.h
+++ b/lib/pk11wrap/secmodi.h
@@ -165,12 +165,13 @@ SECKEYPrivateKey *PK11_MakePrivKey(PK11S
PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx);
CERTCertificate *PK11_MakeCertFromHandle(PK11SlotInfo *slot,
CK_OBJECT_HANDLE certID, CK_ATTRIBUTE *privateLabel);
SECItem *pk11_GenerateNewParamWithKeyLen(CK_MECHANISM_TYPE type, int keyLen);
SECItem *pk11_ParamFromIVWithLen(CK_MECHANISM_TYPE type,
SECItem *iv, int keyLen);
SECItem *pk11_mkcertKeyID(CERTCertificate *cert);
+const SECItem *pk11_GetPublicKeyComponent(SECKEYPublicKey *pubKey);
SEC_END_PROTOS
#endif
diff --git a/lib/pk11wrap/secmodti.h b/lib/pk11wrap/secmodti.h
--- a/lib/pk11wrap/secmodti.h
+++ b/lib/pk11wrap/secmodti.h
@@ -204,10 +204,13 @@ struct PK11GenericObjectStr {
/*
* private header file, so we can set real names for oids that aren't upstream
* yet, so we applications don't try to use them and get hosed when they change
*/
#define SEC_OID_X25519MLKEM768 SEC_OID_MLKEM768X25519
#define SEC_OID_SECP256R1MLKEM768 SEC_OID_PRIVATE_1
#define SEC_OID_SECP384R1MLKEM1024 SEC_OID_PRIVATE_2
+#define SEC_OID_ML_DSA_44 SEC_OID_PRIVATE_3
+#define SEC_OID_ML_DSA_65 SEC_OID_PRIVATE_4
+#define SEC_OID_ML_DSA_87 SEC_OID_PRIVATE_5
#endif /* _SECMODTI_H_ */
diff --git a/lib/pkcs12/p12d.c b/lib/pkcs12/p12d.c
--- a/lib/pkcs12/p12d.c
+++ b/lib/pkcs12/p12d.c
@@ -2887,16 +2887,19 @@ sec_pkcs12_get_public_value_and_type(SEC
if (!type || !pubKey) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return NULL;
}
*type = pubKey->keyType;
switch (pubKey->keyType) {
+ case mldsaKey:
+ pubValue = &pubKey->u.mldsa.publicValue;
+ break;
case dsaKey:
pubValue = &pubKey->u.dsa.publicValue;
break;
case dhKey:
pubValue = &pubKey->u.dh.publicValue;
break;
case rsaKey:
pubValue = &pubKey->u.rsa.modulus;
diff --git a/lib/smime/cmssiginfo.c b/lib/smime/cmssiginfo.c
--- a/lib/smime/cmssiginfo.c
+++ b/lib/smime/cmssiginfo.c
@@ -136,16 +136,17 @@ NSS_CMSSignerInfo_GetSignatureAlgorithmO
SECOidTag pubkAlgTag,
SECOidTag signAlgTag)
{
switch (keyType) {
case rsaKey:
return pubkAlgTag;
case rsaPssKey:
case dsaKey:
+ case mldsaKey: /* pubkAlgTag and signAlgTag are the same */
case ecKey:
return signAlgTag;
default:
return SEC_OID_UNKNOWN;
}
}
/*
diff --git a/lib/softoken/lowkey.c b/lib/softoken/lowkey.c
--- a/lib/softoken/lowkey.c
+++ b/lib/softoken/lowkey.c
@@ -87,16 +87,34 @@ const SEC_ASN1Template nsslowkey_RSAPriv
const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dsa.publicValue) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dsa.privateValue) },
{ 0 }
};
+const SEC_ASN1Template nsslowkey_PQBothSeedAndPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_OCTET_STRING, offsetof(NSSLOWKEYPrivateKey, u.genpq.seedItem) },
+ { SEC_ASN1_OCTET_STRING, offsetof(NSSLOWKEYPrivateKey, u.genpq.keyItem) },
+ { 0 }
+};
+
+const SEC_ASN1Template nsslowkey_PQSeedTemplate[] = {
+ { SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSLOWKEYPrivateKey, u.genpq.seedItem),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+ { 0 }
+};
+const SEC_ASN1Template nsslowkey_PQPrivateKeyTemplate[] = {
+ { SEC_ASN1_OCTET_STRING, offsetof(NSSLOWKEYPrivateKey, u.genpq.keyItem) },
+ { 0 }
+};
+
const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[] = {
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dsa.privateValue) },
};
const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dh.publicValue) },
{ SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey, u.dh.privateValue) },
@@ -418,16 +436,56 @@ nsslowkey_ConvertToPublicKey(NSSLOWKEYPr
pubk->u.ec.ecParams.arena = arena;
/* Copy the rest of the params */
rv = EC_CopyParams(arena, &(pubk->u.ec.ecParams),
&(privk->u.ec.ecParams));
if (rv == SECSuccess)
return pubk;
}
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case NSSLOWKEYMLDSAKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+ SECItem seed= {siBuffer, NULL, 0};
+ MLDSAPrivateKey newPrivKey;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+
+ /* privatekey value is encoded (rho, K, tr, s1, s2, t0) */
+ /* publickey value is encoded (rho, t1) */
+ /* Future, we can calculate public key directly from
+ * privatekey value as follows :
+ * A^ = ExpandA(rho);
+ * t = NTT-1(A& o NSS(s1)) + s2
+ * (t1, t0) = Power2Round(t)
+ * we now have rho and t1 so we can encode public key.
+ * these functions are all specified in FIPS-204. For now
+ * we just use the seed if it's a available and regenerate
+ * both keys, and discard the private key. */
+ if (privk->u.mldsa.seedLen == 0) {
+ PORT_SetError(SEC_ERROR_PKCS11_FUNCTION_FAILED);
+ rv = SECFailure;
+ break;
+ }
+ seed.data = privk->u.mldsa.seed;
+ seed.len = privk->u.mldsa.seedLen;
+ rv = MLDSA_NewKey(privk->u.mldsa.paramSet, &seed,
+ &newPrivKey, &pubk->u.mldsa);
+ if (rv != SECSuccess) {
+ break;
+ }
+ PORT_SafeZero(&newPrivKey, sizeof(newPrivKey));
+ return pubk;
+ }
+ break;
+#endif
/* No Fortezza in Low Key implementations (Fortezza keys aren't
* stored in our data base */
default:
break;
}
PORT_FreeArena(arena, PR_TRUE);
return NULL;
@@ -554,16 +612,22 @@ nsslowkey_CopyPrivateKey(NSSLOWKEYPrivat
break;
returnKey->u.ec.ecParams.arena = poolp;
/* Copy the rest of the params */
rv = EC_CopyParams(poolp, &(returnKey->u.ec.ecParams),
&(privKey->u.ec.ecParams));
if (rv != SECSuccess)
break;
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case NSSLOWKEYMLDSAKey:
+ returnKey->u.mldsa = privKey->u.mldsa;
+ rv = SECSuccess;
+ break;
+#endif
default:
rv = SECFailure;
}
loser:
if (rv != SECSuccess) {
PORT_FreeArena(poolp, PR_TRUE);
diff --git a/lib/softoken/lowkeyti.h b/lib/softoken/lowkeyti.h
--- a/lib/softoken/lowkeyti.h
+++ b/lib/softoken/lowkeyti.h
@@ -17,16 +17,19 @@
extern const SEC_ASN1Template nsslowkey_PQGParamsTemplate[];
extern const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[];
extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[];
extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[];
extern const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[];
extern const SEC_ASN1Template nsslowkey_DHPrivateKeyExportTemplate[];
#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION 1 /* as per SECG 1 C.4 */
extern const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_PQBothSeedAndPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_PQSeedTemplate[];
+extern const SEC_ASN1Template nsslowkey_PQPrivateKeyTemplate[];
extern const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[];
extern const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[];
extern const SEC_ASN1Template nsslowkey_SubjectPublicKeyInfoTemplate[];
extern const SEC_ASN1Template nsslowkey_RSAPublicKeyTemplate[];
/*
* PKCS #8 attributes
@@ -57,44 +60,58 @@ struct NSSLOWKEYSubjectPublicKeyInfoStr
};
typedef struct NSSLOWKEYSubjectPublicKeyInfoStr NSSLOWKEYSubjectPublicKeyInfo;
typedef enum {
NSSLOWKEYNullKey = 0,
NSSLOWKEYRSAKey = 1,
NSSLOWKEYDSAKey = 2,
NSSLOWKEYDHKey = 4,
- NSSLOWKEYECKey = 5
+ NSSLOWKEYECKey = 5,
+ NSSLOWKEYMLDSAKey = 6,
} NSSLOWKEYType;
/*
** An RSA public key object.
*/
struct NSSLOWKEYPublicKeyStr {
PLArenaPool *arena;
NSSLOWKEYType keyType;
union {
RSAPublicKey rsa;
DSAPublicKey dsa;
DHPublicKey dh;
ECPublicKey ec;
+#ifdef NSS_ENABLE_ML_DSA
+ MLDSAPublicKey mldsa;
+#endif
} u;
};
typedef struct NSSLOWKEYPublicKeyStr NSSLOWKEYPublicKey;
+typedef struct GenPostQuantumPrivateKeyStr GenPostQuantumPrivateKey;
+struct GenPostQuantumPrivateKeyStr {
+ SECItem seedItem;
+ SECItem keyItem;
+};
+
/*
** Low Level private key object
** This is only used by the raw Crypto engines (crypto), keydb (keydb),
** and PKCS #11. Everyone else uses the high level key structure.
*/
struct NSSLOWKEYPrivateKeyStr {
PLArenaPool *arena;
NSSLOWKEYType keyType;
union {
RSAPrivateKey rsa;
DSAPrivateKey dsa;
DHPrivateKey dh;
ECPrivateKey ec;
+ GenPostQuantumPrivateKey genpq; /* used to decode post quantum keys */
+#ifdef NSS_ENABLE_ML_DSA
+ MLDSAPrivateKey mldsa;
+#endif
} u;
};
typedef struct NSSLOWKEYPrivateKeyStr NSSLOWKEYPrivateKey;
#endif /* _LOWKEYTI_H_ */
diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c
--- a/lib/softoken/pkcs11.c
+++ b/lib/softoken/pkcs11.c
@@ -655,16 +655,18 @@ static const struct mechanismList mechan
#ifndef NSS_DISABLE_KYBER
{ CKM_NSS_KYBER_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
{ CKM_NSS_KYBER, { 0, 0, CKF_KEM }, PR_TRUE },
#endif
{ CKM_NSS_ML_KEM_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
{ CKM_NSS_ML_KEM, { 0, 0, CKF_KEM }, PR_TRUE },
{ CKM_ML_KEM_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE },
{ CKM_ML_KEM, { 0, 0, CKF_KEM }, PR_TRUE },
+ { CKM_ML_DSA_KEY_PAIR_GEN, { ML_DSA_44_PUBLICKEY_LEN, ML_DSA_87_PUBLICKEY_LEN, CKF_GENERATE }, PR_TRUE },
+ { CKM_ML_DSA, { ML_DSA_44_PUBLICKEY_LEN, ML_DSA_87_PUBLICKEY_LEN, CKF_SN_VR }, PR_TRUE },
};
static const CK_ULONG mechanismCount = sizeof(mechanisms) / sizeof(mechanisms[0]);
/* sigh global so fipstokn can read it */
PRBool nsc_init = PR_FALSE;
#if defined(CHECK_FORK_PTHREAD) || defined(CHECK_FORK_MIXED)
@@ -1037,16 +1039,17 @@ sftk_handlePublicKeyObject(SFTKSession *
CK_KEY_TYPE key_type)
{
CK_BBOOL encrypt = CK_TRUE;
CK_BBOOL recover = CK_TRUE;
CK_BBOOL wrap = CK_TRUE;
CK_BBOOL derive = CK_FALSE;
CK_BBOOL verify = CK_TRUE;
CK_BBOOL encapsulate = CK_FALSE;
+ CK_ULONG paramSet = 0;
CK_RV crv;
switch (key_type) {
case CKK_RSA:
crv = sftk_ConstrainAttribute(object, CKA_MODULUS,
RSA_MIN_MODULUS_BITS, 0, 0);
if (crv != CKR_OK) {
return crv;
@@ -1127,16 +1130,37 @@ sftk_handlePublicKeyObject(SFTKSession *
}
derive = CK_FALSE;
verify = CK_FALSE;
encrypt = CK_FALSE;
recover = CK_FALSE;
wrap = CK_FALSE;
encapsulate = CK_TRUE;
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ crv = sftk_GetULongAttribute (object, CKA_PARAMETER_SET,
+ &paramSet);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ if (sftk_MLDSAGetSigLen(paramSet) == 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ derive = CK_FALSE;
+ verify = CK_TRUE;
+ encrypt = CK_FALSE;
+ recover = CK_FALSE;
+ wrap = CK_FALSE;
+ encapsulate = CK_FALSE;
+ break;
+#endif
default:
return CKR_ATTRIBUTE_VALUE_INVALID;
}
/* make sure the required fields exist */
crv = sftk_defaultAttribute(object, CKA_SUBJECT, NULL, 0);
if (crv != CKR_OK)
return crv;
@@ -1207,20 +1231,21 @@ static CK_RV
sftk_handlePrivateKeyObject(SFTKSession *session, SFTKObject *object, CK_KEY_TYPE key_type)
{
CK_BBOOL cktrue = CK_TRUE;
CK_BBOOL encrypt = CK_TRUE;
CK_BBOOL sign = CK_FALSE;
CK_BBOOL recover = CK_TRUE;
CK_BBOOL wrap = CK_TRUE;
CK_BBOOL derive = CK_TRUE;
- CK_BBOOL encapsulate = CK_FALSE;
+ CK_BBOOL decapsulate = CK_FALSE;
CK_BBOOL ckfalse = CK_FALSE;
PRBool createObjectInfo = PR_TRUE;
PRBool fillPrivateKey = PR_FALSE;
+ CK_ULONG paramSet = 0;
int missing_rsa_mod_component = 0;
int missing_rsa_exp_component = 0;
int missing_rsa_crt_component = 0;
SECItem mod;
CK_RV crv;
SECStatus rv;
@@ -1352,18 +1377,97 @@ sftk_handlePrivateKeyObject(SFTKSession
return CKR_TEMPLATE_INCOMPLETE;
}
if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) {
if (!sftk_hasAttribute(object, CKA_NSS_PARAMETER_SET)) {
return CKR_TEMPLATE_INCOMPLETE;
}
}
encrypt = sign = recover = wrap = CK_FALSE;
- encapsulate = CK_TRUE;
+ decapsulate = CK_TRUE;
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ if (!sftk_hasAttribute(object, CKA_KEY_TYPE)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ /* make sure we have a CKA_PARAMETER_SET */
+ if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ /* make sure it's one we understand */
+ crv = sftk_GetULongAttribute (object, CKA_PARAMETER_SET,
+ &paramSet);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ if (sftk_MLDSAGetSigLen(paramSet) == 0) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ /*
+ * if we have a seed deal with making sure seed and
+ * CKA_VALUE . We skip this step if the SEED and VALUE
+ * was generated together by us. */
+ if (sftk_hasAttribute(object, CKA_SEED)) {
+ PRBool seedOK = sftk_hasAttribute(object, CKA_NSS_SEED_OK);
+ SFTKAttribute *seedAttribute = sftk_FindAttribute(object,
+ CKA_SEED);
+ PORT_Assert(seedAttribute);
+ crv = CKR_OK;
+ if (seedAttribute->attrib.ulValueLen != 0) {
+ SFTKAttribute *valueAttribute =
+ sftk_FindAttribute(object, CKA_VALUE);
+ unsigned int valueLen = valueAttribute ?
+ valueAttribute->attrib.ulValueLen : 0;
+ if (!seedOK || valueLen == 0) {
+ MLDSAPrivateKey privKey;
+ MLDSAPublicKey pubKey;
+ SECItem seedItem;
+
+ seedItem.data = seedAttribute->attrib.pValue;
+ seedItem.len = seedAttribute->attrib.ulValueLen;
+ rv = MLDSA_NewKey(paramSet, &seedItem, &privKey,
+ &pubKey);
+ if (rv != SECSuccess) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ } else if (valueLen == 0) {
+ crv = sftk_forceAttribute(object, CKA_VALUE,
+ privKey.keyVal,
+ privKey.keyValLen);
+ } else {
+ /* we have the value, so we must need to
+ * verify it */
+ PORT_Assert(!seedOK);
+ if ((privKey.keyValLen != valueLen) ||
+ (PORT_Memcmp(valueAttribute->attrib.pValue,
+ privKey.keyVal, valueLen) != 0)){
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ }
+ PORT_SafeZero(&privKey, sizeof(privKey));
+ PORT_SafeZero(&pubKey, sizeof(pubKey));
+ }
+ if (valueAttribute)
+ sftk_FreeAttribute(valueAttribute);
+ }
+ sftk_FreeAttribute(seedAttribute);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ }
+ sftk_DeleteAttributeType(object, CKA_NSS_SEED_OK);
+ /* if we got this far, we should have a CKA_VALUE, either but
+ * one given to us, or by it being generated above */
+ if (!sftk_hasAttribute(object, CKA_VALUE)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ encrypt = decapsulate = recover = wrap = CK_FALSE;
+ sign = CK_TRUE;
+ break;
+#endif
default:
return CKR_ATTRIBUTE_VALUE_INVALID;
}
crv = sftk_defaultAttribute(object, CKA_SUBJECT, NULL, 0);
if (crv != CKR_OK)
return crv;
crv = sftk_defaultAttribute(object, CKA_SENSITIVE, &cktrue, sizeof(CK_BBOOL));
if (crv != CKR_OK)
@@ -1382,17 +1486,17 @@ sftk_handlePrivateKeyObject(SFTKSession
if (crv != CKR_OK)
return crv;
crv = sftk_defaultAttribute(object, CKA_UNWRAP, &wrap, sizeof(CK_BBOOL));
if (crv != CKR_OK)
return crv;
crv = sftk_defaultAttribute(object, CKA_DERIVE, &derive, sizeof(CK_BBOOL));
if (crv != CKR_OK)
return crv;
- crv = sftk_defaultAttribute(object, CKA_DECAPSULATE, &encapsulate, sizeof(CK_BBOOL));
+ crv = sftk_defaultAttribute(object, CKA_DECAPSULATE, &decapsulate, sizeof(CK_BBOOL));
if (crv != CKR_OK)
return crv;
/* the next two bits get modified only in the key gen and token cases */
crv = sftk_forceAttribute(object, CKA_ALWAYS_SENSITIVE,
&ckfalse, sizeof(CK_BBOOL));
if (crv != CKR_OK)
return crv;
crv = sftk_forceAttribute(object, CKA_NEVER_EXTRACTABLE,
@@ -2047,16 +2151,31 @@ sftk_GetPubKey(SFTKObject *object, CK_KE
break;
#ifndef NSS_DISABLE_KYBER
case CKK_NSS_KYBER:
#endif
case CKK_NSS_ML_KEM:
case CKK_ML_KEM:
crv = CKR_OK;
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ pubKey->keyType = NSSLOWKEYMLDSAKey;
+ crv = sftk_ReadAttribute(object, CKA_VALUE,
+ pubKey->u.mldsa.keyVal,
+ sizeof(pubKey->u.mldsa.keyVal),
+ &pubKey->u.mldsa.keyValLen);
+ if (crv != CKR_OK) {
+ break;
+ }
+ crv = sftk_GetULongAttribute (object, CKA_PARAMETER_SET,
+ &pubKey->u.mldsa.paramSet);
+
+ break;
+#endif
default:
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
*crvp = crv;
if (crv != CKR_OK) {
PORT_FreeArena(arena, PR_TRUE);
return NULL;
@@ -2220,16 +2339,41 @@ sftk_mkPrivKey(SFTKObject *object, CK_KE
#ifndef NSS_DISABLE_KYBER
case CKK_NSS_KYBER:
#endif
case CKK_NSS_ML_KEM:
case CKK_ML_KEM:
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ privKey->keyType = NSSLOWKEYMLDSAKey;
+ crv = sftk_ReadAttribute(object, CKA_VALUE,
+ privKey->u.mldsa.keyVal,
+ sizeof(privKey->u.mldsa.keyVal),
+ &privKey->u.mldsa.keyValLen);
+ if (crv != CKR_OK) {
+ break;
+ }
+ crv = sftk_ReadAttribute(object, CKA_SEED,
+ privKey->u.mldsa.seed,
+ sizeof(privKey->u.mldsa.seed),
+ &privKey->u.mldsa.seedLen);
+ if (crv != CKR_OK) {
+ /* no seed value, just set it to zero. The seed
+ * has been lost or discarded for this key */
+ privKey->u.mldsa.seedLen = 0;
+ }
+ crv = sftk_GetULongAttribute(object, CKA_PARAMETER_SET,
+ &privKey->u.mldsa.paramSet);
+
+ break;
+#endif
+
default:
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
if (crv == CKR_OK && itemTemplateCount != 0) {
PORT_Assert(itemTemplateCount > 0);
PORT_Assert(itemTemplateCount <= SFTK_MAX_ITEM_TEMPLATE);
crv = sftk_MultipleAttribute2SecItem(arena, object, itemTemplate,
@@ -2470,16 +2614,31 @@ sftk_PutPubKey(SFTKObject *publicKey, SF
crv = sftk_AddAttributeType(publicKey, CKA_BASE,
sftk_item_expand(&pubKey->u.dsa.params.base));
if (crv != CKR_OK) {
break;
}
crv = sftk_AddAttributeType(publicKey, CKA_VALUE,
sftk_item_expand(&pubKey->u.dsa.publicValue));
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ sftk_DeleteAttributeType(publicKey, CKA_VALUE);
+ sftk_DeleteAttributeType(publicKey, CKA_PARAMETER_SET);
+ crv = sftk_AddAttributeType(publicKey, CKA_VALUE,
+ pubKey->u.mldsa.keyVal,
+ pubKey->u.mldsa.keyValLen);
+ if (crv != CKR_OK) {
+ break;
+ }
+ crv = sftk_AddAttributeType(publicKey, CKA_PARAMETER_SET,
+ (unsigned char *)&pubKey->u.mldsa.paramSet,
+ sizeof(pubKey->u.mldsa.paramSet));
+ break;
+#endif
case CKK_DH:
sftk_DeleteAttributeType(publicKey, CKA_PRIME);
sftk_DeleteAttributeType(publicKey, CKA_BASE);
crv = sftk_AddAttributeType(publicKey, CKA_PRIME,
sftk_item_expand(&pubKey->u.dh.prime));
if (crv != CKR_OK) {
break;
}
@@ -2534,16 +2693,22 @@ sftk_PutPubKey(SFTKObject *publicKey, SF
}
}
if (sftk_isTrue(privateKey, CKA_SIGN_RECOVER)) {
crv = sftk_forceAttribute(publicKey, CKA_VERIFY_RECOVER, &cktrue, sizeof(CK_BBOOL));
if (crv != CKR_OK) {
return crv;
}
}
+ if (sftk_isTrue(privateKey, CKA_DECAPSULATE)) {
+ crv = sftk_forceAttribute(publicKey, CKA_ENCAPSULATE, &cktrue, sizeof(CK_BBOOL));
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ }
if (sftk_isTrue(privateKey, CKA_DERIVE)) {
crv = sftk_forceAttribute(publicKey, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));
if (crv != CKR_OK) {
return crv;
}
}
return crv;
}
diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c
--- a/lib/softoken/pkcs11c.c
+++ b/lib/softoken/pkcs11c.c
@@ -76,16 +76,25 @@ typedef struct {
} SSL3RSAPreMasterSecret;
static void
sftk_Null(void *data, PRBool freeit)
{
return;
}
+/* fake hash end, the hashed data is already in the signature context,
+ * return a NULL hash, which will be passed to the sign final and ignored */
+void
+sftk_NullHashEnd(void *info, unsigned char *data, unsigned int *lenp,
+ unsigned int maxlen)
+{
+ *lenp = 0;
+}
+
#ifdef EC_DEBUG
#define SEC_PRINT(str1, str2, num, sitem) \
printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
str1, str2, num, sitem->len); \
for (i = 0; i < sitem->len; i++) { \
printf("%02x:", sitem->data[i]); \
} \
printf("\n")
@@ -2850,16 +2859,85 @@ nsc_EDDSASignStub(void *ctx, unsigned ch
SECStatus rv = ED_SignMessage(&(key->u.ec), &signature, &digest);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
*sigLen = signature.len;
return rv;
}
+#ifdef NSS_ENABLE_ML_DSA
+void
+sftk_MLDSASignUpdate(void *info, const unsigned char *data, unsigned int len)
+{
+ MLDSAContext *ctptr = (MLDSAContext *)info;
+ const SECItem inData = {siBuffer, (unsigned char *)data, len};
+ (void) MLDSA_SignUpdate(ctptr, &inData);
+}
+
+void
+sftk_MLDSAVerifyUpdate(void *info, const unsigned char *data, unsigned int len)
+{
+ MLDSAContext *ctptr = (MLDSAContext *)info;
+ const SECItem inData = {siBuffer, (unsigned char *)data, len};
+ (void) MLDSA_VerifyUpdate(ctptr, &inData);
+}
+
+SECStatus
+sftk_MLDSASignFinal(void *info, unsigned char *sig, unsigned int *sigLen,
+ unsigned int maxLen, const unsigned char *data,
+ unsigned int len)
+{
+ MLDSAContext *ctptr = (MLDSAContext *)info;
+ SECItem sigOut = {siBuffer, sig, maxLen};
+ SECStatus rv;
+
+ if (len !=0 ) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ rv = MLDSA_SignFinal(ctptr, &sigOut);
+ *sigLen = sigOut.len;
+ return rv;
+}
+
+SECStatus
+sftk_MLDSAVerifyFinal(void *info, const unsigned char *sig, unsigned int sigLen,
+ const unsigned char *data, unsigned int len)
+{
+ MLDSAContext *ctptr = (MLDSAContext *)info;
+ const SECItem sigIn= {siBuffer, (unsigned char *)sig, sigLen};
+
+ if (len !=0 ) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ return MLDSA_VerifyFinal(ctptr, &sigIn);
+}
+
+unsigned int
+sftk_MLDSAGetSigLen(CK_ML_DSA_PARAMETER_SET_TYPE paramSet)
+{
+ switch (paramSet) {
+ case CKP_ML_DSA_44:
+ return ML_DSA_44_SIGNATURE_LEN;
+ case CKP_ML_DSA_65:
+ return ML_DSA_65_SIGNATURE_LEN;
+ case CKP_ML_DSA_87:
+ return ML_DSA_87_SIGNATURE_LEN;
+ }
+ /* this is a programming error if we get a valid DSA key with an unknown
+ * parmaSet */
+ PORT_Assert( /* unknown param set */ 0);
+ return 0;
+}
+#endif
+
/* NSC_SignInit setups up the signing operations. There are three basic
* types of signing:
* (1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied
* to data in a single Sign operation (which often looks a lot like an
* encrypt, with data coming in and data going out).
* (2) Hash based signing, where we continually hash the data, then apply
* some sort of signature to the end.
* (3) Block Encryption CBC MAC's, where the Data is encrypted with a key,
@@ -3040,16 +3118,80 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
}
context->cipherInfo = privKey;
context->update = nsc_DSA_Sign_Stub;
context->destroy = (privKey == key->objectInfo) ? sftk_Null : sftk_FreePrivKey;
context->maxLen = DSA_MAX_SIGNATURE_LEN;
break;
#endif
+#ifdef NSS_ENABLE_ML_DSA
+ case CKM_ML_DSA:
+ {
+ /* set our defaults */
+ CK_HEDGE_TYPE hedgeType = CKH_HEDGE_PREFERRED;
+ SECItem signCtx = { siBuffer, NULL, 0 };
+ MLDSAContext *ctptr = NULL;
+ SECStatus rv;
+
+ /* make sure we have the right key type */
+ if (key_type != CKK_ML_DSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ /* fill in our parameters from the mechanism parameters if
+ * supplied */
+ if (pMechanism->ulParameterLen != 0) {
+ CK_SIGN_ADDITIONAL_CONTEXT *param;
+ if (pMechanism->ulParameterLen !=
+ sizeof(CK_SIGN_ADDITIONAL_CONTEXT)) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+ param = (CK_SIGN_ADDITIONAL_CONTEXT *)pMechanism->pParameter;
+ hedgeType = param->hedgeVariant;
+ signCtx.data = param->pContext;
+ signCtx.len = param->ulContextLen;
+ }
+ /* fetch the key */
+ privKey = sftk_GetPrivKey(key, key_type, &crv);
+ if (privKey == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ /* now initialize it the signature */
+ rv = MLDSA_SignInit(&privKey->u.mldsa, hedgeType, &signCtx, &ctptr);
+ if (rv != SECSuccess) {
+ crv = sftk_MapCryptError(PORT_GetError());
+ if (privKey != key->objectInfo) {
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ break;
+ }
+ /* set up our cipher info. MLDSA is only a combined hash/sign
+ * so the hash update is our sign update, the hash end is a null
+ * function returning a zero length value, and the final gets our
+ * signature based on the context. Both the cipher context and the
+ * hash Info is the same. The MLDSA_SignFinal frees the context,
+ * so we don't have to */
+ context->multi = PR_TRUE;
+ context->cipherInfo = ctptr;
+ context->hashInfo = ctptr;
+ context->hashUpdate = sftk_MLDSASignUpdate;
+ context->end = sftk_NullHashEnd;
+ context->hashdestroy = sftk_Null;
+ context->destroy = sftk_Null;
+ context->update = sftk_MLDSASignFinal;
+ context->maxLen = sftk_MLDSAGetSigLen(privKey->u.mldsa.paramSet);
+ if (privKey != key->objectInfo) {
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ break;
+ }
+#endif
#define INIT_ECDSA_SIG_MECH(mmm) \
case CKM_ECDSA_##mmm: \
context->multi = PR_TRUE; \
crv = sftk_doSub##mmm(context); \
if (crv != CKR_OK) \
break; \
goto finish_ecdsa;
@@ -3849,16 +3991,72 @@ NSC_VerifyInit(CK_SESSION_HANDLE hSessio
if (pubKey == NULL) {
break;
}
context->cipherInfo = pubKey;
context->verify = nsc_DSA_Verify_Stub;
context->destroy = sftk_Null;
break;
#endif
+#ifdef NSS_ENABLE_ML_DSA
+ case CKM_ML_DSA:
+ {
+ /* set our defaults */
+ SECItem signCtx = { siBuffer, NULL, 0 };
+ MLDSAContext *ctptr = NULL;
+ SECStatus rv;
+
+ /* make sure we have the right key type */
+ if (key_type != CKK_ML_DSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ /* fill in our parameters from the mechanism parameters if
+ * supplied */
+ if (pMechanism->ulParameterLen != 0) {
+ CK_SIGN_ADDITIONAL_CONTEXT *param;
+ if (pMechanism->ulParameterLen !=
+ sizeof(CK_SIGN_ADDITIONAL_CONTEXT)) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+ param = (CK_SIGN_ADDITIONAL_CONTEXT *)pMechanism->pParameter;
+ signCtx.data = param->pContext;
+ signCtx.len = param->ulContextLen;
+ }
+ /* fetch the key */
+ pubKey = sftk_GetPubKey(key, key_type, &crv);
+ if (pubKey == NULL) {
+ /* crv already set */
+ break;
+ }
+ /* now initialize it the signature */
+ rv = MLDSA_VerifyInit(&(pubKey->u.mldsa), &signCtx, &ctptr);
+ if (rv != SECSuccess) {
+ crv = sftk_MapVerifyError(PORT_GetError());
+ break;
+ }
+ /* set up our cipher info. MLDSA is only a combined hash/sign
+ * so the hash update is our sign update, the hash end is a null
+ * function returning a zero length value, and the final gets our
+ * signature based on the context. Both the cipher context and the
+ * hash Info is the same. The MLDSA_VerifyFinal frees the context,
+ * so we don't have to */
+ context->multi = PR_TRUE;
+ context->cipherInfo = ctptr;
+ context->hashInfo = ctptr;
+ context->hashUpdate = sftk_MLDSAVerifyUpdate;
+ context->end = sftk_NullHashEnd;
+ context->hashdestroy = sftk_Null;
+ context->destroy = sftk_Null;
+ context->verify = sftk_MLDSAVerifyFinal;
+ context->maxLen = sftk_MLDSAGetSigLen(pubKey->u.mldsa.paramSet);
+ break;
+ }
+#endif
INIT_ECDSA_SIG_MECH(SHA1)
INIT_ECDSA_SIG_MECH(SHA224)
INIT_ECDSA_SIG_MECH(SHA256)
INIT_ECDSA_SIG_MECH(SHA384)
INIT_ECDSA_SIG_MECH(SHA512)
case CKM_ECDSA:
finish_ecdsa:
@@ -5325,16 +5523,20 @@ sftk_PairwiseConsistencyCheck(CK_SESSION
pairwise_digest_length = subPrimeLen;
mech.mechanism = CKM_DSA;
break;
#endif
case CKK_EC:
signature_length = MAX_ECKEY_LEN * 2;
mech.mechanism = CKM_ECDSA;
break;
+ case CKK_ML_DSA:
+ signature_length = MAX_ML_DSA_SIGNATURE_LEN;
+ mech.mechanism = CKM_ML_DSA;
+ break;
case CKK_EC_EDWARDS:
signature_length = ED25519_SIGN_LEN;
mech.mechanism = CKM_EDDSA;
break;
default:
return CKR_DEVICE_ERROR;
}
@@ -5648,27 +5850,31 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS
RSAPrivateKey *rsaPriv;
DHParams dhParam;
#ifndef NSS_DISABLE_DSA
/* DSA */
PQGParams pqgParam;
DSAPrivateKey *dsaPriv;
#endif
+#ifdef NSS_ENABLE_ML_DSA
+ MLDSAPrivateKey mldsaPriv;
+ MLDSAPublicKey mldsaPub;
+#endif
/* Diffie Hellman */
DHPrivateKey *dhPriv;
/* Elliptic Curve Cryptography */
SECItem ecEncodedParams; /* DER Encoded parameters */
ECPrivateKey *ecPriv;
ECParams *ecParams;
- /* Kyber */
- CK_NSS_KEM_PARAMETER_SET_TYPE ckKyberParamSet;
+ /* PKCS $v3.2 algorithms */
+ CK_ULONG ckParamSet;
CHECK_FORK();
if (!slot) {
return CKR_SESSION_HANDLE_INVALID;
}
/*
* now lets create an object to hang the attributes off of
@@ -5683,21 +5889,21 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS
*/
for (i = 0; i < (int)ulPublicKeyAttributeCount; i++) {
if (pPublicKeyTemplate[i].type == CKA_MODULUS_BITS) {
public_modulus_bits = *(CK_ULONG *)pPublicKeyTemplate[i].pValue;
continue;
}
if (pPublicKeyTemplate[i].type == CKA_NSS_PARAMETER_SET) {
- ckKyberParamSet = *(CK_NSS_KEM_PARAMETER_SET_TYPE *)pPublicKeyTemplate[i].pValue;
+ ckParamSet = *(CK_ULONG *)pPublicKeyTemplate[i].pValue;
continue;
}
if (pPublicKeyTemplate[i].type == CKA_PARAMETER_SET) {
- ckKyberParamSet = *(CK_ML_KEM_PARAMETER_SET_TYPE *)pPublicKeyTemplate[i].pValue;
+ ckParamSet = *(CK_ULONG *)pPublicKeyTemplate[i].pValue;
continue;
}
crv = sftk_AddAttributeType(publicKey,
sftk_attr_expand(&pPublicKeyTemplate[i]));
if (crv != CKR_OK)
break;
}
@@ -5940,17 +6146,16 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS
crv = sftk_AddAttributeType(privateKey, CKA_VALUE,
sftk_item_expand(&dsaPriv->privateValue));
dsagn_done:
/* should zeroize, since this function doesn't. */
PORT_FreeArena(dsaPriv->params.arena, PR_TRUE);
break;
#endif
-
case CKM_DH_PKCS_KEY_PAIR_GEN:
sftk_DeleteAttributeType(privateKey, CKA_PRIME);
sftk_DeleteAttributeType(privateKey, CKA_BASE);
sftk_DeleteAttributeType(privateKey, CKA_VALUE);
sftk_DeleteAttributeType(privateKey, CKA_NSS_DB);
key_type = CKK_DH;
/* extract the necessary parameters and copy them to private keys */
@@ -6103,17 +6308,17 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS
goto generate_mlkem;
case CKM_ML_KEM_KEY_PAIR_GEN:
key_type = CKK_ML_KEM;
generate_mlkem:
sftk_DeleteAttributeType(privateKey, CKA_NSS_DB);
SECItem privKey = { siBuffer, NULL, 0 };
SECItem pubKey = { siBuffer, NULL, 0 };
- KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(ckKyberParamSet);
+ KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(ckParamSet);
if (!sftk_kyber_AllocPrivKeyItem(kyberParams, &privKey)) {
crv = CKR_HOST_MEMORY;
goto kyber_done;
}
if (!sftk_kyber_AllocPubKeyItem(kyberParams, &pubKey)) {
crv = CKR_HOST_MEMORY;
goto kyber_done;
}
@@ -6123,37 +6328,117 @@ generate_mlkem:
goto kyber_done;
}
crv = sftk_AddAttributeType(publicKey, CKA_VALUE, sftk_item_expand(&pubKey));
if (crv != CKR_OK) {
goto kyber_done;
}
crv = sftk_AddAttributeType(publicKey, CKA_PARAMETER_SET,
- &ckKyberParamSet, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE));
+ &ckParamSet, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE));
if (crv != CKR_OK) {
goto kyber_done;
}
crv = sftk_AddAttributeType(privateKey, CKA_VALUE,
sftk_item_expand(&privKey));
if (crv != CKR_OK) {
goto kyber_done;
}
crv = sftk_AddAttributeType(privateKey, CKA_PARAMETER_SET,
- &ckKyberParamSet, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE));
+ &ckParamSet, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE));
if (crv != CKR_OK) {
goto kyber_done;
}
crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB,
sftk_item_expand(&pubKey));
kyber_done:
SECITEM_ZfreeItem(&privKey, PR_FALSE);
SECITEM_FreeItem(&pubKey, PR_FALSE);
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKM_ML_DSA_KEY_PAIR_GEN:
+ sftk_DeleteAttributeType(publicKey, CKA_VALUE);
+ sftk_DeleteAttributeType(privateKey, CKA_NSS_DB);
+ sftk_DeleteAttributeType(privateKey, CKA_SEED);
+ key_type = CKK_ML_DSA;
+
+
+ /*
+ * the parameters are recognized by us
+ */
+ bitSize = sftk_MLDSAGetSigLen(ckParamSet);
+ if (bitSize == 0) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ }
+
+ /* Generate the key */
+ rv = MLDSA_NewKey(ckParamSet, NULL, &mldsaPriv, &mldsaPub);
+
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ crv = sftk_MapCryptError(PORT_GetError());
+ break;
+ }
+
+ /* store the generated key into the attributes */
+ crv = sftk_AddAttributeType(publicKey, CKA_VALUE,
+ mldsaPub.keyVal, mldsaPub.keyValLen);
+ if (crv != CKR_OK)
+ goto mldsagn_done;
+ crv = sftk_AddAttributeType(publicKey, CKA_PARAMETER_SET,
+ &ckParamSet, sizeof(CK_ML_DSA_PARAMETER_SET_TYPE));
+ if (crv != CKR_OK) {
+ goto mldsagn_done;
+ }
+
+ /* now fill in the RSA dependent paramenters in the private key */
+ crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB,
+ mldsaPub.keyVal, mldsaPub.keyValLen);
+ if (crv != CKR_OK)
+ goto mldsagn_done;
+
+ crv = sftk_AddAttributeType(privateKey, CKA_VALUE,
+ mldsaPriv.keyVal,
+ mldsaPriv.keyValLen);
+ if (crv != CKR_OK)
+ goto mldsagn_done;
+ crv = sftk_AddAttributeType(privateKey, CKA_PARAMETER_SET,
+ &ckParamSet, sizeof(CK_ML_DSA_PARAMETER_SET_TYPE));
+ if (crv != CKR_OK) {
+ goto mldsagn_done;
+ }
+
+ if (mldsaPriv.seedLen != 0) {
+ crv = sftk_AddAttributeType(privateKey, CKA_SEED,
+ mldsaPriv.seed, mldsaPriv.seedLen);
+ if (crv != CKR_OK) {
+ goto mldsagn_done;
+ }
+ /* pseudo attribute that says the seed came with the key
+ * so don't try to regenerate thekey in handleObject.
+ * it will be removed before the object sees the light of
+ * day. */
+ crv = sftk_AddAttributeType(privateKey, CKA_NSS_SEED_OK,
+ NULL, 0);
+ /* it was either this or a comment 'fall through' which would
+ * be cryptic to some users */
+ if (crv != CKR_OK) {
+ goto mldsagn_done;
+ }
+ }
+ mldsagn_done:
+ PORT_SafeZero(&mldsaPriv, sizeof(mldsaPriv));
+ PORT_SafeZero(&mldsaPub, sizeof(mldsaPub));
+ break;
+#endif
+
case CKM_EC_MONTGOMERY_KEY_PAIR_GEN:
case CKM_EC_EDWARDS_KEY_PAIR_GEN:
sftk_DeleteAttributeType(privateKey, CKA_EC_PARAMS);
sftk_DeleteAttributeType(privateKey, CKA_VALUE);
sftk_DeleteAttributeType(privateKey, CKA_NSS_DB);
key_type = (pMechanism->mechanism == CKM_EC_EDWARDS_KEY_PAIR_GEN) ? CKK_EC_EDWARDS : CKK_EC_MONTGOMERY;
@@ -6464,16 +6749,77 @@ sftk_PackagePrivateKey(SFTKObject *key,
SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKey", lk->keyType,
fordebug);
#endif
param = SECITEM_DupItem(&lk->u.ec.ecParams.DEREncoding);
algorithm = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
break;
+ case NSSLOWKEYMLDSAKey:
+ {
+ SECItem seed = {siBuffer, NULL, 0};
+ SECItem keyVal = {siBuffer, NULL, 0};
+ dummy = NULL;
+
+ /* paramSet sets the algorithm */
+ switch (lk->u.mldsa.paramSet) {
+ case CKP_ML_DSA_44:
+ algorithm = SEC_OID_ML_DSA_44;
+ break;
+ case CKP_ML_DSA_65:
+ algorithm = SEC_OID_ML_DSA_65;
+ break;
+ case CKP_ML_DSA_87:
+ algorithm = SEC_OID_ML_DSA_87;
+ break;
+ default:
+ algorithm = SEC_OID_UNKNOWN;
+ break;
+ }
+ if (algorithm == SEC_OID_UNKNOWN) {
+ break;
+ }
+
+ /* if we have the seed, copy it */
+ if (lk->u.mldsa.seedLen != 0) {
+ rv = SECITEM_MakeItem(arena, &seed, lk->u.mldsa.seed,
+ lk->u.mldsa.seedLen);
+ if (rv != SECSuccess) {
+ break;
+ }
+ }
+ rv = SECITEM_MakeItem(arena, &keyVal, lk->u.mldsa.keyVal,
+ lk->u.mldsa.keyValLen);
+ if (rv != SECSuccess) {
+ break;
+ }
+ if (lk == key->objectInfo) {
+ /* we are used a cached key, and we are about to
+ * overwrite it, let's get a duplicate first */
+ lk = nsslowkey_CopyPrivateKey(lk);
+ if (lk == NULL) {
+ break;
+ }
+ }
+ /* this overwrites the mldsa data, but we don't need it any
+ * more because we are discarding lk once we encode */
+ lk->u.genpq.seedItem = seed;
+ lk->u.genpq.keyItem = keyVal;
+
+ if (seed.len) {
+ dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+ nsslowkey_PQBothSeedAndPrivateKeyTemplate);
+ } else {
+ dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+ nsslowkey_PQPrivateKeyTemplate);
+ }
+ }
+ break;
+
case NSSLOWKEYDHKey:
default:
dummy = NULL;
break;
}
if (!dummy || ((lk->keyType == NSSLOWKEYDSAKey) && !param)) {
*crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
@@ -6672,24 +7018,26 @@ NSC_WrapKey(CK_SESSION_HANDLE hSession,
/*
* import a pprivate key info into the desired slot
*/
static SECStatus
sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
{
CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
CK_KEY_TYPE keyType = CKK_RSA;
SECStatus rv = SECFailure;
const SEC_ASN1Template *keyTemplate, *paramTemplate;
void *paramDest = NULL;
PLArenaPool *arena;
NSSLOWKEYPrivateKey *lpk = NULL;
NSSLOWKEYPrivateKeyInfo *pki = NULL;
CK_RV crv = CKR_KEY_TYPE_INCONSISTENT;
+ CK_ULONG paramSet = 0;
arena = PORT_NewArena(2048);
if (!arena) {
return SECFailure;
}
pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(arena,
sizeof(NSSLOWKEYPrivateKeyInfo));
@@ -6731,16 +7079,45 @@ sftk_unwrapPrivateKey(SFTKObject *key, S
case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
keyTemplate = nsslowkey_ECPrivateKeyTemplate;
paramTemplate = NULL;
paramDest = &(lpk->u.ec.ecParams.DEREncoding);
lpk->keyType = NSSLOWKEYECKey;
prepare_low_ec_priv_key_for_asn1(lpk);
prepare_low_ecparams_for_asn1(&lpk->u.ec.ecParams);
break;
+ case SEC_OID_ML_DSA_44:
+ paramSet = CKP_ML_DSA_44;
+ goto mldsa_next;
+ case SEC_OID_ML_DSA_65:
+ paramSet = CKP_ML_DSA_65;
+ goto mldsa_next;
+ case SEC_OID_ML_DSA_87:
+ paramSet = CKP_ML_DSA_87;
+mldsa_next:
+ switch (pki->privateKey.data[0]) {
+ case SEC_ASN1_CONTEXT_SPECIFIC|0:
+ keyTemplate = nsslowkey_PQSeedTemplate;
+ break;
+ case SEC_ASN1_OCTET_STRING:
+ keyTemplate = nsslowkey_PQPrivateKeyTemplate;
+ break;
+ case SEC_ASN1_CONSTRUCTED|SEC_ASN1_SEQUENCE:
+ keyTemplate = nsslowkey_PQBothSeedAndPrivateKeyTemplate;
+ break;
+ default:
+ keyTemplate = NULL;
+ break;
+ }
+
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = NSSLOWKEYMLDSAKey;
+ /* genpq encodes ocect, not integer, so no need to prep it */
+ break;
default:
keyTemplate = NULL;
paramTemplate = NULL;
paramDest = NULL;
break;
}
if (!keyTemplate) {
@@ -6839,17 +7216,17 @@ sftk_unwrapPrivateKey(SFTKObject *key, S
crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType,
sizeof(keyType));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue,
sizeof(CK_BBOOL));
if (crv != CKR_OK)
break;
- crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue,
+ crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &ckfalse,
sizeof(CK_BBOOL));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_PRIME,
sftk_item_expand(&lpk->u.dsa.params.prime));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_SUBPRIME,
@@ -6860,16 +7237,58 @@ sftk_unwrapPrivateKey(SFTKObject *key, S
sftk_item_expand(&lpk->u.dsa.params.base));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_VALUE,
sftk_item_expand(&lpk->u.dsa.privateValue));
if (crv != CKR_OK)
break;
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case NSSLOWKEYMLDSAKey:
+ keyType = CKK_ML_DSA;
+ crv = (sftk_hasAttribute(key, CKA_NSS_DB)) ? CKR_OK : CKR_KEY_TYPE_INCONSISTENT;
+ if (crv != CKR_OK)
+ break;
+ crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType,
+ sizeof(keyType));
+ if (crv != CKR_OK)
+ break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue,
+ sizeof(CK_BBOOL));
+ if (crv != CKR_OK)
+ break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &ckfalse,
+ sizeof(CK_BBOOL));
+ if (crv != CKR_OK)
+ break;
+ crv = sftk_AddAttributeType(key, CKA_PARAMETER_SET, &paramSet,
+ sizeof(CK_ML_DSA_PARAMETER_SET_TYPE));
+ if (crv != CKR_OK)
+ break;
+ if (lpk->u.genpq.seedItem.len != 0) {
+ crv = sftk_AddAttributeType(key, CKA_SEED,
+ sftk_item_expand(&lpk->u.genpq.seedItem));
+ if (crv != CKR_OK)
+ break;
+ }
+
+ /* if we were given just the seed, we'll regenerate the key
+ * from the seed in handleObject */
+ if (lpk->u.genpq.keyItem.len != 0) {
+ crv = sftk_AddAttributeType(key, CKA_VALUE,
+ sftk_item_expand(&lpk->u.genpq.keyItem));
+ /* I know, this is redundant, but it would be too easy
+ * for someone to add another sftk_AddAttributeType after
+ * this without adding this check back because of the if */
+ if (crv != CKR_OK)
+ break;
+ }
+ break;
+#endif
#ifdef notdef
case NSSLOWKEYDHKey:
template = dhTemplate;
templateCount = sizeof(dhTemplate) / sizeof(CK_ATTRIBUTE);
keyType = CKK_DH;
break;
#endif
/* what about fortezza??? */
@@ -6881,17 +7300,17 @@ sftk_unwrapPrivateKey(SFTKObject *key, S
crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType,
sizeof(keyType));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue,
sizeof(CK_BBOOL));
if (crv != CKR_OK)
break;
- crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue,
+ crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &ckfalse,
sizeof(CK_BBOOL));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_DERIVE, &cktrue,
sizeof(CK_BBOOL));
if (crv != CKR_OK)
break;
crv = sftk_AddAttributeType(key, CKA_EC_PARAMS,
diff --git a/lib/softoken/pkcs11i.h b/lib/softoken/pkcs11i.h
--- a/lib/softoken/pkcs11i.h
+++ b/lib/softoken/pkcs11i.h
@@ -68,16 +68,20 @@
#define TIME_ATTRIBUTE_HASH_SIZE 32
#define TIME_SESSION_OBJECT_HASH_SIZE 1024
#define TIME_SESSION_HASH_SIZE 1024
#define MAX_OBJECT_LIST_SIZE 800
/* how many objects to keep on the free list
* before we start freeing them */
#define MAX_KEY_LEN 256 /* maximum symmetric key length in bytes */
+#define SEC_OID_ML_DSA_44 SEC_OID_PRIVATE_3
+#define SEC_OID_ML_DSA_65 SEC_OID_PRIVATE_4
+#define SEC_OID_ML_DSA_87 SEC_OID_PRIVATE_5
+
/*
* LOG2_BUCKETS_PER_SESSION_LOCK must be a prime number.
* With SESSION_HASH_SIZE=1024, LOG2 can be 9, 5, 1, or 0.
* With SESSION_HASH_SIZE=4096, LOG2 can be 11, 9, 5, 1, or 0.
*
* HASH_SIZE LOG2_BUCKETS_PER BUCKETS_PER_LOCK NUMBER_OF_BUCKETS
* 1024 9 512 2
* 1024 5 32 32
@@ -764,16 +768,19 @@ extern CK_RV sftk_Attribute2SSecItem(PLA
CK_ATTRIBUTE_TYPE type);
extern SFTKModifyType sftk_modifyType(CK_ATTRIBUTE_TYPE type,
CK_OBJECT_CLASS inClass);
extern PRBool sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass);
extern char *sftk_getString(SFTKObject *object, CK_ATTRIBUTE_TYPE type);
extern void sftk_nullAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type);
extern CK_RV sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
CK_ULONG *longData);
+extern CK_RV sftk_ReadAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ unsigned char *data, unsigned int maxlen,
+ unsigned int *lenp);
extern CK_RV sftk_forceAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
const void *value, unsigned int len);
extern CK_RV sftk_defaultAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
const void *value, unsigned int len);
extern unsigned int sftk_MapTrust(CK_TRUST trust, PRBool clientAuth);
extern SFTKObject *sftk_NewObject(SFTKSlot *slot);
extern CK_RV sftk_CopyObject(SFTKObject *destObject, SFTKObject *srcObject);
@@ -979,11 +986,15 @@ CK_FLAGS sftk_AttributeToFlags(CK_ATTRIB
/* check the FIPS table to determine if this current operation is allowed by
* FIPS security policy */
PRBool sftk_operationIsFIPS(SFTKSlot *slot, CK_MECHANISM *mech,
CK_ATTRIBUTE_TYPE op, SFTKObject *source,
CK_ULONG targetKeySize);
/* add validation objects to the slot */
CK_RV sftk_CreateValidationObjects(SFTKSlot *slot);
+/* get the length of an MLDSASignature based on the PKCS #11 parameter set */
+unsigned int sftk_MLDSAGetSigLen(CK_ML_DSA_PARAMETER_SET_TYPE paramSet);
+
+
SEC_END_PROTOS
#endif /* _PKCS11I_H_ */
diff --git a/lib/softoken/pkcs11u.c b/lib/softoken/pkcs11u.c
--- a/lib/softoken/pkcs11u.c
+++ b/lib/softoken/pkcs11u.c
@@ -742,16 +742,18 @@ sftk_modifyType(CK_ATTRIBUTE_TYPE type,
case CKA_PRIME_2:
case CKA_EXPONENT_1:
case CKA_EXPONENT_2:
case CKA_COEFFICIENT:
case CKA_VALUE_LEN:
case CKA_ALWAYS_SENSITIVE:
case CKA_NEVER_EXTRACTABLE:
case CKA_NSS_DB:
+ case CKA_SEED:
+ case CKA_PARAMETER_SET:
mtype = SFTK_NEVER;
break;
/* ONCOPY */
case CKA_TOKEN:
case CKA_PRIVATE:
case CKA_MODIFIABLE:
mtype = SFTK_ONCOPY;
@@ -809,16 +811,17 @@ sftk_isSensitive(CK_ATTRIBUTE_TYPE type,
switch (type) {
/* ALWAYS */
case CKA_PRIVATE_EXPONENT:
case CKA_PRIME_1:
case CKA_PRIME_2:
case CKA_EXPONENT_1:
case CKA_EXPONENT_2:
case CKA_COEFFICIENT:
+ case CKA_SEED:
return PR_TRUE;
/* DEPENDS ON CLASS */
case CKA_VALUE:
/* PRIVATE and SECRET KEYS have SENSITIVE values */
return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY));
default:
@@ -872,16 +875,38 @@ sftk_GetULongAttribute(SFTKObject *objec
return CKR_ATTRIBUTE_VALUE_INVALID;
}
*longData = *(CK_ULONG *)attribute->attrib.pValue;
sftk_FreeAttribute(attribute);
return CKR_OK;
}
+CK_RV
+sftk_ReadAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ unsigned char *data, unsigned int maxLen, unsigned int *lenp)
+{
+ SFTKAttribute *attribute;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL)
+ return CKR_TEMPLATE_INCOMPLETE;
+
+ *lenp = attribute->attrib.ulValueLen;
+ if (*lenp > maxLen) {
+ /* normally would be CKR_BUFFER_TOO_SMALL, but
+ * it used with internal buffers, so if the value is
+ * to long, the original attribute was invalid */
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ PORT_Memcpy(data, attribute->attrib.pValue, *lenp);
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
void
sftk_DeleteAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
{
SFTKAttribute *attribute;
attribute = sftk_FindAttribute(object, type);
if (attribute == NULL)
return;
sftk_DeleteAttribute(object, attribute);
@@ -1401,16 +1426,21 @@ static const CK_ATTRIBUTE_TYPE dhPubKeyA
static const CK_ULONG dhPubKeyAttrsCount =
sizeof(dhPubKeyAttrs) / sizeof(dhPubKeyAttrs[0]);
static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = {
CKA_EC_PARAMS, CKA_EC_POINT
};
static const CK_ULONG ecPubKeyAttrsCount =
sizeof(ecPubKeyAttrs) / sizeof(ecPubKeyAttrs[0]);
+static const CK_ATTRIBUTE_TYPE mldsaPubKeyAttrs[] = {
+ CKA_PARAMETER_SET, CKA_VALUE
+};
+static const CK_ULONG mldsaPubKeyAttrsCount = PR_ARRAY_SIZE(mldsaPubKeyAttrs);
+
static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = {
CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT,
CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NSS_DB, CKA_PUBLIC_KEY_INFO
};
static const CK_ULONG commonPrivKeyAttrsCount =
sizeof(commonPrivKeyAttrs) / sizeof(commonPrivKeyAttrs[0]);
static const CK_ATTRIBUTE_TYPE rsaPrivKeyAttrs[] = {
@@ -1438,16 +1468,21 @@ static const CK_ULONG ecPrivKeyAttrsCoun
sizeof(ecPrivKeyAttrs) / sizeof(ecPrivKeyAttrs[0]);
static const CK_ATTRIBUTE_TYPE certAttrs[] = {
CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER
};
static const CK_ULONG certAttrsCount =
sizeof(certAttrs) / sizeof(certAttrs[0]);
+static const CK_ATTRIBUTE_TYPE mldsaPrivKeyAttrs[] = {
+ CKA_PARAMETER_SET, CKA_VALUE, CKA_SEED
+};
+static const CK_ULONG mldsaPrivKeyAttrsCount = PR_ARRAY_SIZE(mldsaPrivKeyAttrs);
+
static const CK_ATTRIBUTE_TYPE trustAttrs[] = {
CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_EMAIL_PROTECTION,
CKA_TRUST_CODE_SIGNING, CKA_TRUST_STEP_UP_APPROVED
};
static const CK_ULONG trustAttrsCount =
sizeof(trustAttrs) / sizeof(trustAttrs[0]);
@@ -1531,16 +1566,22 @@ stfk_CopyTokenPrivateKey(SFTKObject *des
case CKK_RSA:
crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPrivKeyAttrs,
rsaPrivKeyAttrsCount);
break;
case CKK_DSA:
crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPrivKeyAttrs,
dsaPrivKeyAttrsCount);
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, mldsaPrivKeyAttrs,
+ mldsaPrivKeyAttrsCount);
+ break;
+#endif
case CKK_DH:
crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs,
dhPrivKeyAttrsCount);
break;
case CKK_EC:
crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs,
ecPrivKeyAttrsCount);
break;
@@ -1591,16 +1632,22 @@ stfk_CopyTokenPublicKey(SFTKObject *dest
case CKK_RSA:
crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPubKeyAttrs,
rsaPubKeyAttrsCount);
break;
case CKK_DSA:
crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPubKeyAttrs,
dsaPubKeyAttrsCount);
break;
+#ifdef NSS_ENABLE_ML_DSA
+ case CKK_ML_DSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, mldsaPubKeyAttrs,
+ mldsaPubKeyAttrsCount);
+ break;
+#endif
case CKK_DH:
crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs,
dhPubKeyAttrsCount);
break;
case CKK_EC:
crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs,
ecPubKeyAttrsCount);
break;
diff --git a/lib/softoken/sdb.c b/lib/softoken/sdb.c
--- a/lib/softoken/sdb.c
+++ b/lib/softoken/sdb.c
@@ -108,32 +108,32 @@ static const CK_ATTRIBUTE_TYPE known_att
CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_HW_FEATURE_TYPE,
CKA_RESET_ON_INIT, CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y,
CKA_RESOLUTION, CKA_CHAR_ROWS, CKA_CHAR_COLUMNS, CKA_COLOR,
CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, CKA_ENCODING_METHODS, CKA_MIME_TYPES,
CKA_MECHANISM_TYPE, CKA_REQUIRED_CMS_ATTRIBUTES,
- CKA_ENCAPSULATE, CKA_DECAPSULATE, CKA_PARAMETER_SET,
+ CKA_ENCAPSULATE, CKA_DECAPSULATE, CKA_PARAMETER_SET, CKA_SEED,
CKA_DEFAULT_CMS_ATTRIBUTES, CKA_SUPPORTED_CMS_ATTRIBUTES,
CKA_WRAP_TEMPLATE, CKA_UNWRAP_TEMPLATE, CKA_NSS_TRUST, CKA_NSS_URL,
CKA_NSS_EMAIL, CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP,
CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES,
CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED,
CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC,
CKA_NSS_OVERRIDE_EXTENSIONS, CKA_NSS_SERVER_DISTRUST_AFTER,
CKA_NSS_EMAIL_DISTRUST_AFTER, CKA_TRUST_DIGITAL_SIGNATURE,
CKA_TRUST_NON_REPUDIATION, CKA_TRUST_KEY_ENCIPHERMENT,
CKA_TRUST_DATA_ENCIPHERMENT, CKA_TRUST_KEY_AGREEMENT,
CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN, CKA_TRUST_SERVER_AUTH,
CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
CKA_TRUST_IPSEC_END_SYSTEM, CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
CKA_TRUST_TIME_STAMPING, CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH,
- CKA_CERT_MD5_HASH, CKA_NSS_DB
+ CKA_CERT_MD5_HASH, CKA_NSS_DB,
};
static const int known_attributes_size = PR_ARRAY_SIZE(known_attributes);
/*
* Note on use of sqlReadDB: Only one thread at a time may have an actual
* operation going on given sqlite3 * database. An operation is defined as
* the time from a sqlite3_prepare() until the sqlite3_finalize().
@@ -1937,16 +1937,111 @@ sdb_attributeComparator(const void *a, c
return -1;
}
if (*(CK_ATTRIBUTE_TYPE *)a > *(CK_ATTRIBUTE_TYPE *)b) {
return 1;
}
return 0;
}
+static const char ADD_COLUMN_CMD[] = "ALTER TABLE %s ADD COLUMN %s";
+static CK_RV
+sdb_add_column(sqlite3 *sqlDB, const char *table, sdbDataType type,
+ const char *typeString)
+{
+ char *newStr = sqlite3_mprintf(ADD_COLUMN_CMD, table, typeString);
+ int sqlerr;
+
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ return sdb_mapSQLError(type, sqlerr);
+ }
+ return CKR_OK;
+}
+
+static const char COLUMN_QUERY_CMD[] =
+ "SELECT * FROM %s";
+
+/*
+ * if our current database does not have all the attributes in the columns,
+ * we need to add those columns or attempts to add objects with those
+ * attributes will fail. We only need this on R/W databases because we only
+ * add objects to R/W databases.
+ */
+static CK_RV
+sdb_update_column(sqlite3 *sqlDB, const char *table, sdbDataType type)
+{
+ char *newStr = sqlite3_mprintf(COLUMN_QUERY_CMD, table);
+ sqlite3_stmt *stmt;
+ int columnCount;
+ int sqlerr;
+ CK_RV error;
+
+ if (!newStr) {
+ return CKR_HOST_MEMORY;
+ }
+
+ sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ return sdb_mapSQLError(type, sqlerr);
+ }
+
+ columnCount= sqlite3_column_count(stmt);
+ /* columns include the first column, which is id, which is not
+ * and attribute. check to make sure we have at least as many attributes
+ * as there are id in out list. This assumes we never add some
+ * attributes in some NSS version and not others, which is generally
+ * true. */
+ if (columnCount >= known_attributes_size+1) {
+ sqlite3_finalize(stmt);
+ return CKR_OK;
+ }
+ /* we have more attributes than in the database, so we know things
+ * are missing, find what was missing */
+ for (int i=0; i < known_attributes_size; i++) {
+ char *typeString = sqlite3_mprintf("a%x", known_attributes[i]);
+ PRBool found=PR_FALSE;
+ /* this one index is important, we skip the first column (id), since
+ * it will never match, starting at zero isn't a bug,
+ * just inefficient */
+ for (int j=1; j < columnCount; j++) {
+ const char *columnName = sqlite3_column_name(stmt, j);
+ if (columnName == NULL) {
+ sqlite3_free(typeString);
+ sqlite3_finalize(stmt);
+ /* if we couldnt' get the colmun name, it's only because
+ * we couldn't get the memory */
+ return CKR_HOST_MEMORY;
+ }
+ if (PORT_Strcmp(typeString, columnName) == 0) {
+ /* we found this one, no need to add it */
+ found=PR_TRUE;
+ break;
+ }
+ }
+ if (found) {
+ sqlite3_free(typeString);
+ continue;
+ }
+ /* we didn't find the attribute, so now add it */
+ error = sdb_add_column(sqlDB, table, type, typeString);
+ if (error != CKR_OK) {
+ sqlite3_free(typeString);
+ sqlite3_finalize(stmt);
+ return error;
+ }
+ sqlite3_free(typeString);
+ }
+ sqlite3_finalize(stmt);
+ return CKR_OK;
+}
+
+
/*
* initialize a single database
*/
static const char INIT_CMD[] =
"CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)";
CK_RV
sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
@@ -2081,17 +2176,26 @@ sdb_init(char *dbname, char *table, sdbD
goto loser;
}
sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
sqlite3_free(newStr);
if (sqlerr != SQLITE_OK) {
error = sdb_mapSQLError(type, sqlerr);
goto loser;
}
+ } else if (flags != SDB_RDONLY) {
+ /* check to see if we need to update the scheme, only need to
+ * do this if we open r/w, since that's the only case where
+ * it's a problem is attribute colmumn are missing */
+ error = sdb_update_column(sqlDB, table, type);
+ if (error != CKR_OK) {
+ goto loser;
+ }
}
+
/*
* detect the case where we have created the database, but have
* not yet updated it.
*
* We only check the Key database because only the key database has
* a metaData table. The metaData table is created when a password
* is set, or in the case of update, when a password is supplied.
* If no key database exists, then the update would have happened immediately
diff --git a/lib/softoken/sftkdb.c b/lib/softoken/sftkdb.c
--- a/lib/softoken/sftkdb.c
+++ b/lib/softoken/sftkdb.c
@@ -91,16 +91,17 @@ sftkdb_isPrivateAttribute(CK_ATTRIBUTE_T
switch (type) {
case CKA_VALUE:
case CKA_PRIVATE_EXPONENT:
case CKA_PRIME_1:
case CKA_PRIME_2:
case CKA_EXPONENT_1:
case CKA_EXPONENT_2:
case CKA_COEFFICIENT:
+ case CKA_SEED:
return PR_TRUE;
default:
break;
}
return PR_FALSE;
}
/* These attributes must be authenticated with an hmac. */
diff --git a/lib/util/manifest.mn b/lib/util/manifest.mn
--- a/lib/util/manifest.mn
+++ b/lib/util/manifest.mn
@@ -5,16 +5,17 @@
CORE_DEPTH = ../..
EXPORTS = \
base64.h \
ciferfam.h \
eccutil.h \
hasht.h \
kyber.h \
+ ml_dsat.h \
nssb64.h \
nssb64t.h \
nsshash.h \
nsslocks.h \
nssilock.h \
nssilckt.h \
nssrwlk.h \
nssrwlkt.h \
diff --git a/lib/util/ml_dsat.h b/lib/util/ml_dsat.h
new file mode 100644
--- /dev/null
+++ b/lib/util/ml_dsat.h
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ML_DSAT_H
+#define ML_DSAT_H
+
+// ML_DSA Key and signature sizes, independent of implementaion
+
+// ml_dsa_44
+#define ML_DSA_44_PUBLICKEY_LEN 1312
+#define ML_DSA_44_PRIVATEKEY_LEN 2560
+#define ML_DSA_44_SIGNATURE_LEN 2420
+
+// ml_dsa_65
+#define ML_DSA_65_PUBLICKEY_LEN 1952
+#define ML_DSA_65_PRIVATEKEY_LEN 4032
+#define ML_DSA_65_SIGNATURE_LEN 3309
+
+// ml_dsa_87
+#define ML_DSA_87_PUBLICKEY_LEN 2592
+#define ML_DSA_87_PRIVATEKEY_LEN 4896
+#define ML_DSA_87_SIGNATURE_LEN 4627
+
+// the max defines and common defines
+#define MAX_ML_DSA_PRIVATE_KEY_LEN ML_DSA_87_PRIVATEKEY_LEN
+#define MAX_ML_DSA_PUBLIC_KEY_LEN ML_DSA_87_PUBLICKEY_LEN
+#define MAX_ML_DSA_SIGNATURE_LEN ML_DSA_87_SIGNATURE_LEN
+#define ML_DSA_SEED_LEN 32
+
+#endif /* ML_DSAT_H */
diff --git a/lib/util/pkcs11n.h b/lib/util/pkcs11n.h
--- a/lib/util/pkcs11n.h
+++ b/lib/util/pkcs11n.h
@@ -108,16 +108,19 @@
#define CKA_NSS_EMAIL_DISTRUST_AFTER (CKA_NSS + 36)
#define CKA_NSS_VALIDATION_TYPE (CKA_NSS + 36)
#define CKA_NSS_VALIDATION_VERSION (CKA_NSS + 37)
#define CKA_NSS_VALIDATION_LEVEL (CKA_NSS + 38)
#define CKA_NSS_VALIDATION_MODULE_ID (CKA_NSS + 39)
#define CKA_NSS_PARAMETER_SET (CKA_NSS + 40)
+/* this is an intern NSS signalling attribute, you'll
+ * never see it in an application accessible object */
+#define CKA_NSS_SEED_OK (CKA_NSS + 41)
/*
* Trust attributes:
*
* If trust goes standard, these probably will too. So I'll
* put them all in one place.
*/
diff --git a/lib/util/pkcs11t.h b/lib/util/pkcs11t.h
--- a/lib/util/pkcs11t.h
+++ b/lib/util/pkcs11t.h
@@ -764,16 +764,35 @@ typedef CK_ULONG CK_ML_KEM_PARAMETER_SET
#define CKP_ML_KEM_512 0x00000001UL
#define CKP_ML_KEM_768 0x00000002UL
#define CKP_ML_KEM_1024 0x00000003UL
#define CKA_PARAMETER_SET 0x0000061dUL
#define CKA_ENCAPSULATE 0x00000633UL
#define CKA_DECAPSULATE 0x00000634UL
#define CKF_DECAPSULATE 0x20000000UL
#define CKF_ENCAPSULATE 0x10000000UL
+/* mldsa support */
+#define CKK_ML_DSA 0x0000004aUL
+#define CKM_ML_DSA_KEY_PAIR_GEN 0x0000001cUL
+#define CKM_ML_DSA 0x0000001dUL
+typedef CK_ULONG CK_ML_DSA_PARAMETER_SET_TYPE;
+#define CKP_ML_DSA_44 0x00000001UL
+#define CKP_ML_DSA_65 0x00000002UL
+#define CKP_ML_DSA_87 0x00000003UL
+#define CKA_SEED 0x00000637UL
+typedef CK_ULONG CK_HEDGE_TYPE;
+#define CKH_HEDGE_PREFERRED 0x00000000UL
+#define CKH_HEDGE_REQUIRED 0x00000001UL
+#define CKH_DETERMINISTIC_REQUIRED 0x00000002UL
+typedef struct CK_SIGN_ADDITIONAL_CONTEXT {
+ CK_HEDGE_TYPE hedgeVariant;
+ CK_BYTE_PTR pContext;
+ CK_ULONG ulContextLen;
+} CK_SIGN_ADDITIONAL_CONTEXT;
+
#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL
#define CKM_DH_PKCS_DERIVE 0x00000021UL
/* CKM_X9_42_DH_KEY_PAIR_GEN, CKM_X9_42_DH_DERIVE,
* CKM_X9_42_DH_HYBRID_DERIVE, and CKM_X9_42_MQV_DERIVE are new for
* v2.11 */
#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030UL
diff --git a/lib/util/secoid.c b/lib/util/secoid.c
--- a/lib/util/secoid.c
+++ b/lib/util/secoid.c
@@ -34,17 +34,17 @@ const char __nss_util_version[] = "Versi
#define MISSI_KEA_DSS MISSI, 0x14
#define MISSI_DSS MISSI, 0x13
#define MISSI_KEA MISSI, 0x0a
#define MISSI_ALT_KEA MISSI, 0x16
#define NISTALGS USGOV, 3, 4
#define AES NISTALGS, 1
#define SHAXXX NISTALGS, 2
-#define DSA2 NISTALGS, 3
+#define NISTSIGALGS NISTALGS, 3
/**
** The Netscape OID space is allocated by Terry Hayes. If you need
** a piece of the space, contact him at thayes@netscape.com.
**/
/* Netscape Communications Corporation Object ID space */
/* { 2 16 840 1 113730 } */
@@ -420,18 +420,21 @@ CONST_OID pkcs12V1SafeContentsBag[] = {
/* The following encoding is INCORRECT, but correcting it would create a
* duplicate OID in the table. So, we will leave it alone.
*/
CONST_OID pkcs12KeyUsageAttr[] = { 2, 5, 29, 15 };
CONST_OID ansix9DSASignature[] = { ANSI_X9_ALGORITHM, 0x01 };
CONST_OID ansix9DSASignaturewithSHA1Digest[] = { ANSI_X9_ALGORITHM, 0x03 };
-CONST_OID nistDSASignaturewithSHA224Digest[] = { DSA2, 0x01 };
-CONST_OID nistDSASignaturewithSHA256Digest[] = { DSA2, 0x02 };
+CONST_OID nistDSASignaturewithSHA224Digest[] = { NISTSIGALGS, 0x01 };
+CONST_OID nistDSASignaturewithSHA256Digest[] = { NISTSIGALGS, 0x02 };
+CONST_OID nistMLDSASignatureParm44[] = { NISTSIGALGS, 17 };
+CONST_OID nistMLDSASignatureParm65[] = { NISTSIGALGS, 18 };
+CONST_OID nistMLDSASignatureParm87[] = { NISTSIGALGS, 19 };
/* verisign OIDs */
CONST_OID verisignUserNotices[] = { VERISIGN, 1, 7, 1, 1 };
/* pkix OIDs */
CONST_OID pkixCPSPointerQualifier[] = { PKIX_POLICY_QUALIFIERS, 1 };
CONST_OID pkixUserNoticeQualifier[] = { PKIX_POLICY_QUALIFIERS, 2 };
@@ -1901,17 +1904,22 @@ const static SECOidData oids[SEC_OID_TOT
ODE(SEC_OID_TLS_REQUIRE_EMS,
"TLS Require EMS", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION),
/* this will change upstream. for now apps shouldn't use it */
/* we need it for the policy code. */
ODE(SEC_OID_PRIVATE_1,
"ML-KEM-768+SECP256 key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION),
ODE(SEC_OID_PRIVATE_2,
"ML-KEM-1024+SECP256 key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION),
-
+ OD(nistMLDSASignatureParm44, SEC_OID_PRIVATE_3, "ML-DSA-44",
+ CKM_ML_DSA, INVALID_CERT_EXTENSION),
+ OD(nistMLDSASignatureParm65, SEC_OID_PRIVATE_4, "ML-DSA-65",
+ CKM_ML_DSA, INVALID_CERT_EXTENSION),
+ OD(nistMLDSASignatureParm87, SEC_OID_PRIVATE_5, "ML-DSA-87",
+ CKM_ML_DSA, INVALID_CERT_EXTENSION),
};
/* PRIVATE EXTENDED SECOID Table
* This table is private. Its structure is opaque to the outside.
* It is indexed by the same SECOidTag as the oids table above.
* Every member of this struct must have accessor functions (set, get)
* and those functions must operate by value, not by reference.
* The addresses of the contents of this table must not be exposed
diff --git a/lib/util/secoidt.h b/lib/util/secoidt.h
--- a/lib/util/secoidt.h
+++ b/lib/util/secoidt.h
@@ -536,16 +536,19 @@ typedef enum {
SEC_OID_TLS_REQUIRE_EMS = 390,
/* these will change upstream. for now apps shouldn't use it */
/* give it an obscure name here */
SEC_OID_PRIVATE_1 = 391,
SEC_OID_PRIVATE_2 = 392,
+ SEC_OID_PRIVATE_3 = 393,
+ SEC_OID_PRIVATE_4 = 394,
+ SEC_OID_PRIVATE_5 = 395,
SEC_OID_TOTAL
} SECOidTag;
#define SEC_OID_SECG_EC_SECP192R1 SEC_OID_ANSIX962_EC_PRIME192V1
#define SEC_OID_SECG_EC_SECP256R1 SEC_OID_ANSIX962_EC_PRIME256V1
#define SEC_OID_PKCS12_KEY_USAGE SEC_OID_X509_KEY_USAGE
diff --git a/tests/cert/cert.sh b/tests/cert/cert.sh
--- a/tests/cert/cert.sh
+++ b/tests/cert/cert.sh
@@ -291,24 +291,43 @@ cert_create_cert()
if [ -z "$NSS_DISABLE_DSA" ]; then
CU_ACTION="Import DSA Root CA for $CERTNAME"
certu -A -n "TestCA-dsa" -t "TC,TC,TC" -f "${R_PWFILE}" \
-d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-dsa.ca.cert" 2>&1
if [ "$RET" -ne 0 ]; then
return $RET
fi
fi
-
-
- CU_ACTION="Import EC Root CA for $CERTNAME"
- certu -A -n "TestCA-ec" -t "TC,TC,TC" -f "${R_PWFILE}" \
- -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ec.ca.cert" 2>&1
+ if [ -n "$NSS_ENABLE_ML_DSA" ]; then
+ CU_ACTION="Import ML-DSA-44 Root CA for $CERTNAME"
+ certu -A -n "TestCA-ml-dsa-44" -t "TC,TC,TC" -f "${R_PWFILE}" \
+ -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ml-dsa-44.ca.cert" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+ CU_ACTION="Import ML-DSA-65 Root CA for $CERTNAME"
+ certu -A -n "TestCA-ml-dsa-65" -t "TC,TC,TC" -f "${R_PWFILE}" \
+ -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ml-dsa-65.ca.cert" 2>&1
if [ "$RET" -ne 0 ]; then
return $RET
fi
+ CU_ACTION="Import ML-DSA-87 Root CA for $CERTNAME"
+ certu -A -n "TestCA-ml-dsa-87" -t "TC,TC,TC" -f "${R_PWFILE}" \
+ -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ml-dsa-87.ca.cert" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+ fi
+
+ CU_ACTION="Import EC Root CA for $CERTNAME"
+ certu -A -n "TestCA-ec" -t "TC,TC,TC" -f "${R_PWFILE}" \
+ -d "${PROFILEDIR}" -i "${R_CADIR}/TestCA-ec.ca.cert" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
cert_add_cert "$5"
return $?
}
############################# cert_add_cert ############################
# local shell function to add client certs to an existing CERT DB
# generate request
@@ -391,16 +410,87 @@ cert_add_cert()
CU_ACTION="Import $CERTNAME's mixed DSA Cert"
certu -A -n "${CERTNAME}-dsamixed" -t "u,u,u" -d "${PROFILEDIR}" \
-f "${R_PWFILE}" -i "${CERTNAME}-dsamixed.cert" 2>&1
if [ "$RET" -ne 0 ]; then
return $RET
fi
cert_log "SUCCESS: $CERTNAME's mixed DSA Cert Created"
fi
+ if [ -n "$NSS_ENABLE_ML_DSA" ]; then
+ CU_ACTION="Generate ML-DSA-44 Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}-ml-dsa-44@example.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -k mldsa -q ml-dsa-44 -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -z "${R_NOISE_FILE}" -o req 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+
+ CU_ACTION="Sign ${CERTNAME}'s ML-DSA-44 Request"
+ certu -C -c "TestCA-ml-dsa-44" -m "$CERTSERIAL" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}-ml-dsa-44.cert" -f "${R_PWFILE}" "$1" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+
+ CU_ACTION="Import $CERTNAME's ML-DSA-44 Cert"
+ certu -A -n "${CERTNAME}-ml-dsa-44" -t "u,u,u" -d "${PROFILEDIR}" \
+ -f "${R_PWFILE}" -i "${CERTNAME}-ml-dsa-44.cert" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+ cert_log "SUCCESS: $CERTNAME's ML-DSA-44 Cert Created"
+
+ CU_ACTION="Generate ML-DSA-65 Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}-ml-dsa-65@example.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ certu -R -k mldsa -q ml-dsa-65 -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -z "${R_NOISE_FILE}" -o req 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+
+ CU_ACTION="Sign ${CERTNAME}'s ML-DSA-65 Request"
+ certu -C -c "TestCA-ml-dsa-65" -m "$CERTSERIAL" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}-ml-dsa-65.cert" -f "${R_PWFILE}" "$1" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+
+ CU_ACTION="Import $CERTNAME's ML-DSA-65 Cert"
+ certu -A -n "${CERTNAME}-ml-dsa-65" -t "u,u,u" -d "${PROFILEDIR}" \
+ -f "${R_PWFILE}" -i "${CERTNAME}-ml-dsa-65.cert" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+ cert_log "SUCCESS: $CERTNAME's ML-DSA-65 Cert Created"
+
+ CU_ACTION="Generate ML-DSA-87 Cert Request for $CERTNAME"
+ CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}-ml-dsa-87@example.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ cert_log "SUCCESS: $CERTNAME's ML-DSA-87 Cert Created"
+ certu -R -k mldsa -q ml-dsa-87 -d "${PROFILEDIR}" -f "${R_PWFILE}" \
+ -z "${R_NOISE_FILE}" -o req 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+
+ CU_ACTION="Sign ${CERTNAME}'s ML-DSA-87 Request"
+ certu -C -c "TestCA-ml-dsa-87" -m "$CERTSERIAL" -v 60 -d "${P_R_CADIR}" \
+ -i req -o "${CERTNAME}-ml-dsa-87.cert" -f "${R_PWFILE}" "$1" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+
+ CU_ACTION="Import $CERTNAME's ML-DSA-87 Cert"
+ certu -A -n "${CERTNAME}-ml-dsa-87" -t "u,u,u" -d "${PROFILEDIR}" \
+ -f "${R_PWFILE}" -i "${CERTNAME}-ml-dsa-87.cert" 2>&1
+ if [ "$RET" -ne 0 ]; then
+ return $RET
+ fi
+ cert_log "SUCCESS: $CERTNAME's ML-DSA-87 Cert Created"
+ fi
#
# Generate and add EC cert
#
CURVE="secp384r1"
CU_ACTION="Generate EC Cert Request for $CERTNAME"
CU_SUBJECT="CN=$CERTNAME, E=${CERTNAME}-ec@example.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US"
certu -R -k ec -q "${CURVE}" -d "${PROFILEDIR}" -f "${R_PWFILE}" \
@@ -528,16 +618,52 @@ cert_all_CA()
cert_dsa_CA $CLIENT_CADIR chain-1-clientCA-dsa "-c clientCA-dsa" "u,u,u" ${D_CLIENT_CA} "6"
ALL_CU_SUBJECT="CN=NSS Chain2 Client Test CA (DSA), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
cert_dsa_CA $CLIENT_CADIR chain-2-clientCA-dsa "-c chain-1-clientCA-dsa" "u,u,u" ${D_CLIENT_CA} "7"
rm $CLIENT_CADIR/dsaroot.cert $SERVER_CADIR/dsaroot.cert
# dsaroot.cert in $CLIENT_CADIR and in $SERVER_CADIR is one of the last
# in the chain
fi
+ if [ -n "$NSS_ENABLE_ML_DSA" ]; then
+#
+# Create ML-DSA-44 version of TestCA
+ ALL_CU_SUBJECT="CN=NSS Test CA (ML-DSA-44), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ cert_ml_dsa_CA ml-dsa-44 $CADIR TestCA-ml-dsa-44 -x "CTu,CTu,CTu" ${D_CA} "1"
+#
+# Create ML-DSA-44 versions of the intermediate CA certs
+ ALL_CU_SUBJECT="CN=NSS Server Test CA (ML-DSA-44), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
+ cert_ml_dsa_CA ml-dsa-44 $SERVER_CADIR serverCA-ml-dsa-44 -x "Cu,Cu,Cu" ${D_SERVER_CA} "2"
+
+ ALL_CU_SUBJECT="CN=NSS Client Test CA (ML-DSA-44), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
+ cert_m_ldsa_CA ml-dsa-44 $CLIENT_CADIR clientCA-dsa -x "Tu,Cu,Cu" ${D_CLIENT_CA} "5"
+#
+# Create ML-DSA-65 version of TestCA
+ ALL_CU_SUBJECT="CN=NSS Test CA (ML-DSA-65), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ cert_ml_dsa_CA ml-dsa-65 $CADIR TestCA-ml-dsa-65 -x "CTu,CTu,CTu" ${D_CA} "1"
+#
+# Create ML-DSA-65 versions of the intermediate CA certs
+ ALL_CU_SUBJECT="CN=NSS Server Test CA (ML-DSA-65), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
+ cert_ml_dsa_CA ml-dsa-65 $SERVER_CADIR serverCA-ml-dsa-65 -x "Cu,Cu,Cu" ${D_SERVER_CA} "2"
+
+ ALL_CU_SUBJECT="CN=NSS Client Test CA (ML-DSA-65), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
+ cert_m_ldsa_CA ml-dsa-65 $CLIENT_CADIR clientCA-dsa -x "Tu,Cu,Cu" ${D_CLIENT_CA} "5"
+#
+# Create ML-DSA-87 version of TestCA
+ ALL_CU_SUBJECT="CN=NSS Test CA (ML-DSA-87), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
+ cert_ml_dsa_CA ml-dsa-87 $CADIR TestCA-ml-dsa-87 -x "CTu,CTu,CTu" ${D_CA} "1"
+#
+# Create ML-DSA-87 versions of the intermediate CA certs
+ ALL_CU_SUBJECT="CN=NSS Server Test CA (ML-DSA-87), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
+ cert_ml_dsa_CA ml-dsa-87 $SERVER_CADIR serverCA-ml-dsa-87 -x "Cu,Cu,Cu" ${D_SERVER_CA} "2"
+
+ ALL_CU_SUBJECT="CN=NSS Client Test CA (ML-DSA-87), O=BOGUS NSS, L=Santa Clara, ST=California, C=US"
+ cert_m_ldsa_CA ml-dsa-87 $CLIENT_CADIR clientCA-dsa -x "Tu,Cu,Cu" ${D_CLIENT_CA} "5"
+
+ fi
#
# Create RSA-PSS version of TestCA
ALL_CU_SUBJECT="CN=NSS Test CA (RSA-PSS), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
cert_rsa_pss_CA $CADIR TestCA-rsa-pss -x "CTu,CTu,CTu" ${D_CA} "1" SHA256
rm $CADIR/rsapssroot.cert
ALL_CU_SUBJECT="CN=NSS Test CA (RSA-PSS-SHA1), O=BOGUS NSS, L=Mountain View, ST=California, C=US"
@@ -713,16 +839,79 @@ CERTSCRIPT
CU_ACTION="Exporting DSA Root Cert"
certu -L -n $NICKNAME -r -d ${LPROFILE} -o dsaroot.cert
if [ "$RET" -ne 0 ]; then
Exit 7 "Fatal - failed to export dsa root cert"
fi
cp dsaroot.cert ${NICKNAME}.ca.cert
}
+################################ cert_ml_dsa_CA #############################
+# local shell function to build the Temp. Certificate Authority (CA)
+# used for testing purposes, creating a CA Certificate and a root cert
+# This is the ML-DSA version of cert_CA.
+##########################################################################
+cert_ml_dsa_CA()
+{
+ PARAM_SET=$1
+ CUR_CADIR=$2
+ NICKNAME=$3
+ SIGNER=$4
+ TRUSTARG=$5
+ DOMAIN=$6
+ CERTSERIAL=$7
+
+ echo "$SCRIPTNAME: Creating a ML-DSA ($PARAM_SET) CA Certificate $NICKNAME =========================="
+
+ if [ ! -d "${CUR_CADIR}" ]; then
+ mkdir -p "${CUR_CADIR}"
+ fi
+ cd ${CUR_CADIR}
+ pwd
+
+ LPROFILE=.
+ if [ -n "${MULTIACCESS_DBM}" ]; then
+ LPROFILE="multiaccess:${DOMAIN}"
+ fi
+
+ ################# Creating a ML-DSA CA Cert ###############################
+ #
+ CU_ACTION="Creating ML-DSA ($PARAM_SET) CA Cert $NICKNAME "
+ CU_SUBJECT=$ALL_CU_SUBJECT
+ certu -S -n $NICKNAME -k mldsa -q $PARAM_SET -t $TRUSTARG -v 600 $SIGNER \
+ -d ${LPROFILE} -1 -2 -5 -f ${R_PWFILE} -z ${R_NOISE_FILE} \
+ -m $CERTSERIAL 2>&1 <<CERTSCRIPT
+5
+6
+9
+n
+y
+-1
+n
+5
+6
+7
+9
+n
+CERTSCRIPT
+
+ if [ "$RET" -ne 0 ]; then
+ echo "return value is $RET"
+ Exit 6 "Fatal - failed to create ML-DSA ($PARAM_SET) CA cert"
+ fi
+
+ ################# Exporting ML-DSA Root Cert ###############################
+ #
+ CU_ACTION="Exporting ML-DSA ($PARAM_SET) Root Cert"
+ certu -L -n $NICKNAME -r -d ${LPROFILE} -o ${NICKNAME}.ca.cert
+ if [ "$RET" -ne 0 ]; then
+ Exit 7 "Fatal - failed to export $PARAM_SET root cert"
+ fi
+}
+
################################ cert_rsa_pss_CA #############################
# local shell function to build the Temp. Certificate Authority (CA)
# used for testing purposes, creating a CA Certificate and a root cert
# This is the RSA-PSS version of cert_CA.
@@ -1283,16 +1472,25 @@ cert_ssl()
CU_ACTION="Modify trust attributes of Root CA -t TC,TC,TC"
certu -M -n "TestCA" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
if [ -z "$NSS_DISABLE_DSA" ]; then
CU_ACTION="Modify trust attributes of DSA Root CA -t TC,TC,TC"
certu -M -n "TestCA-dsa" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
fi
+ if [ -n "$NSS_ENABLE_ML_DSA" ]; then
+ CU_ACTION="Modify trust attributes of ML-DSA-44 Root CA -t TC,TC,TC"
+ certu -M -n "TestCA-ml-dsa-44" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
+ CU_ACTION="Modify trust attributes of ML-DSA-65 Root CA -t TC,TC,TC"
+ certu -M -n "TestCA-ml-dsa-65" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
+ CU_ACTION="Modify trust attributes of ML-DSA-87 Root CA -t TC,TC,TC"
+ certu -M -n "TestCA-ml-dsa-87" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
+ fi
+
CU_ACTION="Modify trust attributes of EC Root CA -t TC,TC,TC"
certu -M -n "TestCA-ec" -t "TC,TC,TC" -d ${PROFILEDIR} -f "${R_PWFILE}"
# cert_init_cert ${SERVERDIR} "${HOSTADDR}" 1 ${D_SERVER}
# echo "************* Copying CA files to ${SERVERDIR}"
# cp ${CADIR}/*.db .
# hw_acc
# CU_ACTION="Creating ${CERTNAME}'s Server Cert"
# CU_SUBJECT="CN=${CERTNAME}, O=BOGUS Netscape, L=Mountain View, ST=California, C=US"
@@ -1358,17 +1556,17 @@ cert_stresscerts()
}
############################## cert_fips #####################################
# local shell function to create certificates for FIPS tests
##############################################################################
cert_fips()
{
CERTFAILED=0
- echo "$SCRIPTNAME: Creating FIPS 140 DSA Certificates =============="
+ echo "$SCRIPTNAME: Creating FIPS 140 Certificates =============="
cert_init_cert "${FIPSDIR}" "FIPS PUB 140 Test Certificate" 1000 "${D_FIPS}"
CU_ACTION="Initializing ${CERTNAME}'s Cert DB"
certu -N -d "${PROFILEDIR}" -f "${R_FIPSPWFILE}" 2>&1
CU_ACTION="Loading root cert module to ${CERTNAME}'s Cert DB (ext.)"
modu -add "RootCerts" -libfile "${ROOTCERTSFILE}" -dbdir "${PROFILEDIR}" 2>&1
@@ -1392,16 +1590,18 @@ MODSCRIPT
CU_ACTION="Attempt to generate a key with exponent of 3 (too small)"
certu -G -k rsa -g 2048 -y 3 -d "${PROFILEDIR}" -z ${R_NOISE_FILE} -f "${R_FIPSPWFILE}"
CU_ACTION="Attempt to generate a key with exponent of 17 (too small)"
certu -G -k rsa -g 2048 -y 17 -d "${PROFILEDIR}" -z ${R_NOISE_FILE} -f "${R_FIPSPWFILE}"
RETEXPECTED=0
if [ -z "$NSS_DISABLE_DSA" ]; then
FIPS_KEY="-k dsa"
+ elif [ -n "$NSS_ENABLE_ML_DSA" ]; then
+ FIPS_KEY="-k mldsa -q ml-dsa-44"
else
FIPS_KEY="-k ec -q nistp256"
fi
CU_ACTION="Generate Certificate for ${CERTNAME}"
CU_SUBJECT="CN=${CERTNAME}, E=fips@example.com, O=BOGUS NSS, OU=FIPS PUB 140, L=Mountain View, ST=California, C=US"
certu -S -n ${FIPSCERTNICK} -x -t "Cu,Cu,Cu" -d "${PROFILEDIR}" -f "${R_FIPSPWFILE}" ${FIPS_KEY} -v 600 -m 500 -z "${R_NOISE_FILE}" 2>&1
if [ "$RET" -eq 0 ]; then
cert_log "SUCCESS: FIPS passed"
diff --git a/tests/tools/tools.sh b/tests/tools/tools.sh
--- a/tests/tools/tools.sh
+++ b/tests/tools/tools.sh
@@ -123,16 +123,25 @@ tools_init()
cp ${QADIR}/tools/PKCS5WithImplicitKDF.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/pbmac1-valid-sha256.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/pbmac1-valid-sha256-sha512.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/pbmac1-valid-sha512.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/pbmac1-invalid-bad-iter.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/pbmac1-invalid-bad-salt.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/pbmac1-invalid-no-length.p12 ${TOOLSDIR}/data
cp ${QADIR}/tools/corrupted_cert_bag.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/ietf-ml-dsa-44-both.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/ietf-ml-dsa-44-key.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/ietf-ml-dsa-65-both.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/ietf-ml-dsa-65-key.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/ietf-ml-dsa-87-both.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/ietf-ml-dsa-87-key.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/openssl-ml-dsa-44.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/openssl-ml-dsa-65.p12 ${TOOLSDIR}/data
+ cp ${QADIR}/tools/openssl-ml-dsa-87.p12 ${TOOLSDIR}/data
cd ${TOOLSDIR}
}
########################## list_p12_file ###############################
# List the key and cert in the specified p12 file
########################################################################
list_p12_file()
@@ -476,16 +485,40 @@ tools_p12_export_list_import_with_defaul
check_tmpfile
echo "$SCRIPTNAME: Listing Alice's pk12 EC file with long pw ------------"
echo "pk12util -l Alice-ec-long.p12 -w ${R_LONGPWFILE}"
${BINDIR}/pk12util -l Alice-ec-long.p12 -w ${R_LONGPWFILE} 2>&1
ret=$?
html_msg $ret 0 "Listing Alice's pk12 EC file with long pw (pk12util -l)"
check_tmpfile
+
+ echo "$SCRIPTNAME: Exporting Alice's ML-DSA cert & key---------------"
+ echo "pk12util -o Alice-mldsa.p12 -n \"Alice-ml-dsa-44\" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \\"
+ echo " -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -o Alice-mldsa.p12 -n "Alice-ml-dsa-44" -d ${P_R_ALICEDIR} -k ${R_PWFILE} \
+ -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Exporting Alice's ML-DSA cert & key (pk12util -o)"
+ check_tmpfile
+ verify_p12 Alice-mldsa.p12 "default" "default" "default"
+
+ echo "$SCRIPTNAME: Importing Alice's ML-DSA cert & key --------------"
+ echo "pk12util -i Alice-mldsa.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -i Alice-mldsa.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Importing Alice's ML-DSA cert & key (pk12util -i)"
+ check_tmpfile
+
+ echo "$SCRIPTNAME: Listing Alice's pk12 ML-DSA file -----------------"
+ echo "pk12util -l Alice-mldsa.p12 -w ${R_PWFILE}"
+ ${BINDIR}/pk12util -l Alice-mldsa.p12 -w ${R_PWFILE} 2>&1
+ ret=$?
+ html_msg $ret 0 "Listing Alice's pk12 ML-DSA file (pk12util -l)"
+ check_tmpfile
}
tools_p12_import_old_files()
{
echo "$SCRIPTNAME: Importing PKCS#12 files created with older NSS --------------"
echo "pk12util -i TestOldCA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE}"
${BINDIR}/pk12util -i ${TOOLSDIR}/data/TestOldCA.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -w ${R_PWFILE} 2>&1
ret=$?
@@ -524,16 +557,43 @@ tools_p12_import_rsa_pss_private_key()
${BINDIR}/certutil -d ${P_R_COPYDIR} -K -f ${R_PWFILE} | grep '^<[0-9 ]*> *rsaPss'
ret=$?
html_msg $ret 0 "Listing RSA-PSS private key imported from PKCS#12 file"
check_tmpfile
return $ret
}
+tools_p12_ml_dsa_import()
+{
+ echo "$SCRIPTNAME: Testing ml-dsa compatibility with pkcs12 --------------"
+ for i in 44 65 87
+ do
+ echo "${BINDIR}/pk12util -i ${TOOLSDIR}/data/openssl-ml-dsa-$i.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -W 'test' 2>&1"
+ ${BINDIR}/pk12util -i ${TOOLSDIR}/data/openssl-ml-dsa-$i.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -W 'test' 2>&1
+ ret=$?
+ html_msg $ret 0 "Importing openssl encoded ml-dsa-$i private key from PKCS#12 file"
+ check_tmpfile
+ for j in 'key' 'both'
+ do
+ echo "${BINDIR}/pk12util -i ${TOOLSDIR}/data/ietf-ml-dsa-$i-$j.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -W 'test' 2>&1"
+ ${BINDIR}/pk12util -i ${TOOLSDIR}/data/ietf-ml-dsa-$i-$j.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -W 'test' 2>&1
+ ret=$?
+ html_msg $ret 0 "Importing openssl encoded ml-dsa-$i private key from PKCS#12 file"
+ check_tmpfile
+ html_msg $ret 0 "Importing ietf sample ml-dsa-$i-$j private key from PKCS#12 file"
+
+ # each cert has the same issuer/sn, so we can't hold more than one in
+ # the data base
+ echo "${BINDIR}/certutil -F -n "ietf ml-dsa-$i-$j sample" -d ${P_R_COPYDIR} -f ${R_PWFILE}"
+ ${BINDIR}/certutil -F -n "ietf ml-dsa-$i-$j sample" -d ${P_R_COPYDIR} -f ${R_PWFILE}
+ done
+ done
+}
+
tools_p12_import_pbmac1_samples()
{
echo "$SCRIPTNAME: Importing private key pbmac1 PKCS#12 file --------------"
echo "${BINDIR}/pk12util -i ${TOOLSDIR}/data/pbmac1-valid-sha256.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -W '1234'"
${BINDIR}/pk12util -i ${TOOLSDIR}/data/pbmac1-valid-sha256.p12 -d ${P_R_COPYDIR} -k ${R_PWFILE} -W '1234' 2>&1
ret=$?
html_msg $ret 0 "Importing private key pbmac1 hmac-sha-256 from PKCS#12 file"
check_tmpfile
@@ -578,28 +638,29 @@ tools_p12()
{
tools_p12_export_list_import_with_default_ciphers
# optimized builds have a larger iterator, so they can't run as many
# pkcs12 tests and complete in a reasonable time. Use the iterateration
# count from the previous tests to determine how many tests
# we can run.
iteration_count=$(pp -t p12 -i Alice-ec.p12 | grep "Iterations: " | sed -e 's;.*Iterations: ;;' -e 's;(.*).*;;')
echo "Iteration count=${iteration_count}"
- if [ -n "${iteration_count}" -a ${iteration_count} -le 10000 ]; then
+ if [-z "${NSS_PK12_SHORT_TESTS}" -a -n "${iteration_count}" -a ${iteration_count} -le 10000 ]; then
tools_p12_export_list_import_all_pkcs5v2_ciphers
tools_p12_export_list_import_all_pkcs12v2pbe_ciphers
else
tools_p12_export_list_import_most_ciphers
fi
tools_p12_export_with_none_ciphers
tools_p12_export_with_invalid_ciphers
tools_p12_import_old_files
tools_p12_import_pbmac1_samples
if using_sql; then
tools_p12_import_rsa_pss_private_key
+ tools_p12_ml_dsa_import
tools_p12_policy
fi
}
############################## tools_sign ##############################
# local shell function pk12util uses a hardcoded tmp file, if this exists
# and is owned by another user we don't get reasonable errormessages
########################################################################
@@ -870,10 +931,8 @@ tools_cleanup()
################## main #################################################
tools_init
tools_p12
tools_sign
tools_modutil
tools_cleanup
-
-