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 = ¶mSet; + 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, + ¶mSet, &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, ¶m, 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, ¶ms); if ((rv != SECSuccess) || (params.data == NULL)) { return 0; } bitSize = SECKEY_ECParamsToKeySize(¶ms); 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, ©k->u.kyber.publicValue, &pubk->u.kyber.publicValue); break; + case mldsaKey: + copyk->u.mldsa.params = pubk->u.mldsa.params; + rv = SECITEM_CopyItem(arena, ©k->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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 +#include +#include +#include + +/* 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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#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;;\"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 +#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 + +#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 + +#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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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
: + * ... + * 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
: + * ... + * 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 + +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 +#include + +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 + +#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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 +#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 + * + * 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 + * + * 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 + * + * 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 +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 +#include + +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 + +#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 +#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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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 + * + * 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() +#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 + +/** + * 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 "); \ + 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 + * + * 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 + * + * 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 +#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, + ¶mSet, 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, ¶mSet, + 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 *)¶mSet, + 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, + ¶mSet); + 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, + ¶mSet); + 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, ¶mSet, + 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 <&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 - -