Skip to content
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,43 @@ wolfssl verify -CAfile A.cert B.cert
wolfssl verify -CAfile A.cert C.cert
```

### Creating Chimera Certificates

Following is a scenario creating Chimera (dual algorithms) certificates for PQC(Post Quantum Cryptography).

The following demonstrates how to create a root CA and use it to sign other certificates. This example uses ECC and ML-DSA. In this scenario there are three entities A, B, and C, where A is meant to function as a root CA.

The following steps demonstrate how to generate keys and certificates for A, B, and C, where A is self-signed and B and C are signed by A

1. Create private ECC and ML-DSA keys for A, B, and C
```
wolfssl genkey -ecc -out ecc-key-A -output priv -outform PEM
wolfssl genkey -ecc -out ecc-key-B -output priv -outform PEM
wolfssl genkey -ecc -out ecc-key-C -output priv -outform PEM
wolfssl genkey -ml-dsa -out ml-dsa-key-A -output keypair -outform PEM
wolfssl genkey -ml-dsa -out ml-dsa-key-B -output keypair -outform PEM
wolfssl genkey -ml-dsa -out ml-dsa-key-C -output keypair -outform PEM
```

2. Create a self-signed conventional certificate for A, root CA certificate.
```
wolfssl req -new -key ecc-key-A.priv -subj O=org-A/C=US/ST=WA/L=Seattle/CN=A/OU=org-unit-A -x509 -out A.cert -outform PEM
wolfssl ca -altextend -in A.cert -keyfile ecc-key-A.priv -altkey ml-dsa-key-A.priv -altpub ml-dsa-key-A.pub -out A-chimera.cert
```

3. Create certificates for B and C.
```
# first create conventional certificate signing request (CSR) for B and C
wolfssl req -new -key ecc-key-B.priv -subj O=org-B/C=US/ST=WA/L=Seattle/CN=B/OU=org-unit-B -out B.csr -outform PEM
wolfssl req -new -key ecc-key-C.priv -subj O=org-C/C=US/ST=WA/L=Seattle/CN=C/OU=org-unit-C -out C.csr -outform PEM

# now have conventional signed certs, then add a pub key and Chimera signs the B and C to generate Chimera certificates
wolfssl ca -in B.csr -keyfile ecc-key-A.priv -cert A.cert -out B.cert
wolfssl ca -in C.csr -keyfile ecc-key-B.priv -cert B.cert -out C.cert
wolfssl ca -altextend -in B.cert -keyfile ecc-key-A.priv -altkey ml-dsa-key-A.priv -altpub ml-dsa-key-B.pub -subjkey ecc-key-B.priv -cert A-chimera.cert -out B-chimera.cert
wolfssl ca -altextend -in C.cert -keyfile ecc-key-B.priv -altkey ml-dsa-key-B.priv -altpub ml-dsa-key-C.pub -subjkey ecc-key-C.priv -cert B-chimera.cert -out C-chimera.cert
```

## Contacts

Please contact [email protected] with any questions or comments.
Expand Down
241 changes: 241 additions & 0 deletions src/genkey/clu_genkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,247 @@ int wolfCLU_genKey_Dilithium(WC_RNG* rng, char* fName, int directive, int fmt,
#endif /* HAVE_DILITHIUM */
}

int wolfCLU_genKey_ML_DSA(WC_RNG* rng, char* fName, int directive, int fmt,
int keySz, int level, int withAlg)
{
#ifdef HAVE_DILITHIUM
int ret = WOLFCLU_SUCCESS;

XFILE file = NULL;
int fNameSz = 0;
int fExtSz = 6; // size of ".priv\0" or ".pub\0\0"
char fExtPriv[6] = ".priv\0";
char fExtPub[6] = ".pub\0\0";
char* fOutNameBuf = NULL;

#ifdef NO_AES
/* use 16 bytes for AES block size */
size_t maxDerBufSz = 4 * keySz * 16;
#else
size_t maxDerBufSz = 4 * keySz * AES_BLOCK_SIZE;
#endif /* NO_AES */

byte* derBuf = NULL;
byte* pemBuf = NULL;
byte* outBuf = NULL;
int derBufSz = -1;
int pemBufSz = 0;
int outBufSz = 0;

#ifdef WOLFSSL_SMALL_STACK
MlDsaKey* key;
key = (MlDsaKey*)XMALLOC(sizeof(MlDsaKey), HEAP_HINT,
DYNAMIC_TYPE_DILITHIUM);
if (key == NULL) {
return MEMORY_E;
}
#else
MlDsaKey key[1];
#endif

if (rng == NULL || fName == NULL) {
return BAD_FUNC_ARG;
}

/* init the ML-DSA key */
if (wc_MlDsaKey_Init(key, NULL, 0) != 0) {
wolfCLU_LogError("Failed to initialize ML-DSA Key.\nRET: %d", ret);
#ifdef WOLFSSL_SMALL_STACK
XFREE(key, HEAP_HINT, DYNAMIC_TYPE_DILITHIUM);
#endif
return ret;
}
XMEMSET(key, 0, sizeof(MlDsaKey));

/* set the level of the ML-DSA key */
if (wc_MlDsaKey_SetParams(key, level) != 0) {
wc_MlDsaKey_Free(key);
#ifdef WOLFSSL_SMALL_STACK
XFREE(key, HEAP_HINT, DYNAMIC_TYPE_DILITHIUM);
#endif
return WOLFCLU_FAILURE;
}

/* make the ML-DSA key */
if (wc_MlDsaKey_MakeKey(key, rng) != 0) {
wc_MlDsaKey_Free(key);
#ifdef WOLFSSL_SMALL_STACK
XFREE(key, HEAP_HINT, DYNAMIC_TYPE_DILITHIUM);
#endif
return WOLFCLU_FAILURE;
}

/* set up the file name output buffer */
if (ret == WOLFCLU_SUCCESS) {
fNameSz = (int)XSTRLEN(fName);
fOutNameBuf = (char*)XMALLOC(fNameSz + fExtSz + 1, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER);
if (fOutNameBuf == NULL) {
ret = MEMORY_E;
}
}

if (ret == WOLFCLU_SUCCESS) {
XMEMSET(fOutNameBuf, 0, fNameSz + fExtSz);
XMEMCPY(fOutNameBuf, fName, fNameSz);

derBuf = (byte*)XMALLOC(maxDerBufSz, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER);
if (derBuf == NULL) {
ret = MEMORY_E;
}
}

if (ret == WOLFCLU_SUCCESS) {
switch (directive) {
case PRIV_AND_PUB_FILES:
/* Fall through to PRIV_ONLY_FILE */
FALL_THROUGH;
case PRIV_ONLY_FILE:
/* add on the final part of the file name ".priv" */
XMEMCPY(fOutNameBuf + fNameSz, fExtPriv, fExtSz);
WOLFCLU_LOG(WOLFCLU_L0, "Private key file = %s", fOutNameBuf);

/* Private key to der */
derBufSz = wc_MlDsaKey_PrivateKeyToDer(key,
derBuf, (word32)maxDerBufSz);
if (derBufSz < 0) {
ret = derBufSz;
}
outBuf = derBuf;
outBufSz = derBufSz;

/* check if should convert to PEM format */
if (ret == WOLFCLU_SUCCESS && fmt == PEM_FORM) {
pemBufSz = wolfCLU_KeyDerToPem(derBuf, derBufSz, &pemBuf,
PKCS8_PRIVATEKEY_TYPE, DYNAMIC_TYPE_TMP_BUFFER);
if (pemBufSz <= 0 || pemBuf == NULL) {
ret = WOLFCLU_FAILURE;
}
outBuf = pemBuf;
outBufSz = pemBufSz;
}

/* open file and write Private key */
if (ret == WOLFCLU_SUCCESS) {
file = XFOPEN(fOutNameBuf, "wb");
if (file == XBADFILE) {
wolfCLU_LogError("unable to open file %s",
fOutNameBuf);
ret = OUTPUT_FILE_ERROR;
}
}

if (ret == WOLFCLU_SUCCESS) {
if ((int)XFWRITE(outBuf, 1, outBufSz, file) <= 0) {
ret = OUTPUT_FILE_ERROR;
}
}

if (directive != PRIV_AND_PUB_FILES) {
break;
}

XFCLOSE(file);
file = NULL;
XFREE(derBuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
derBuf = NULL;
XFREE(pemBuf, HEAP_HINT, DYNAMIC_TYPE_PRIVATE_KEY);
pemBuf = NULL;

FALL_THROUGH;
case PUB_ONLY_FILE:
/* add on the final part of the file name ".priv" */
XMEMCPY(fOutNameBuf + fNameSz, fExtPub, fExtSz);
WOLFCLU_LOG(WOLFCLU_L0, "Public key file = %s", fOutNameBuf);

derBuf = (byte*)XMALLOC(maxDerBufSz, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER);
if (derBuf == NULL) {
ret = MEMORY_E;
}

derBufSz = wc_MlDsaKey_PublicKeyToDer(key, derBuf,
(word32)maxDerBufSz, withAlg);
if (derBufSz < 0) {
ret = derBufSz;
}
else {
outBuf = derBuf;
outBufSz = derBufSz;
}

/* check if should convert to PEM format */
if (ret == WOLFCLU_SUCCESS && fmt == PEM_FORM) {
pemBufSz = wolfCLU_KeyDerToPem(derBuf, derBufSz, &pemBuf,
PUBLICKEY_TYPE, DYNAMIC_TYPE_TMP_BUFFER);
if (pemBufSz <= 0 || pemBuf == NULL) {
ret = WOLFCLU_FAILURE;
}
outBuf = pemBuf;
outBufSz = pemBufSz;
}

/* open file and write Public key */
if (ret == WOLFCLU_SUCCESS) {
file = XFOPEN(fOutNameBuf, "wb");
if (file == XBADFILE) {
wolfCLU_LogError("unable to open file %s",
fOutNameBuf);
ret = OUTPUT_FILE_ERROR;
}
}

if (ret == WOLFCLU_SUCCESS) {
if ((int)XFWRITE(outBuf, 1, outBufSz, file) <= 0) {
ret = OUTPUT_FILE_ERROR;
}
}

break;
default:
wolfCLU_LogError("Invalid directive");
ret = BAD_FUNC_ARG;
}
}

if (file != NULL) {
XFCLOSE(file);
}

if (derBuf != NULL) {
wolfCLU_ForceZero(derBuf, (unsigned int)maxDerBufSz);
XFREE(derBuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
}

if (pemBuf != NULL) {
wolfCLU_ForceZero(pemBuf, pemBufSz);
XFREE(pemBuf, HEAP_HINT, DYNAMIC_TYPE_PRIVATE_KEY);
}

if (fOutNameBuf != NULL) {
XFREE(fOutNameBuf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
}

wc_MlDsaKey_Free(key);
#ifdef WOLFSSL_SMALL_STACK
XFREE(key, HEAP_HINT, DYNAMIC_TYPE_DILITHIUM);
#endif

return ret;
#else
(void)rng;
(void)fName;
(void)directive;
(void)fmt;
(void)keySz;
(void)level;
(void)withAlg;

return NOT_COMPILED_IN;
#endif /* HAVE_DILITHIUM */
}

/* The call back function of the writting xmss key */
#ifdef WOLFSSL_HAVE_XMSS
enum wc_XmssRc wolfCLU_XmssKey_WriteCb(const byte * priv,
Expand Down
70 changes: 70 additions & 0 deletions src/genkey/clu_genkey_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,76 @@ int wolfCLU_genKeySetup(int argc, char** argv)
return NOT_COMPILED_IN;
#endif /* HAVE_DILITHIUM */
}
else if (XSTRNCMP(keyType, "ml-dsa", 6) == 0) {
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_KEY_GEN)
int directiveArg = PRIV_AND_PUB_FILES;
int keySz = DILITHIUM_ML_DSA_44_PRV_KEY_SIZE;
int level = WC_ML_DSA_44;
int withAlg = DILITHIUM_LEVEL2k;
const char* levelStr;

WOLFCLU_LOG(WOLFCLU_L0, "Generate ML-DSA Key");

/* get the level argument */
ret = wolfCLU_checkForArg("-level", 6, argc, argv);
if (ret > 0) {
level = XATOI(argv[ret+1]);
switch (level) {
case WC_ML_DSA_44:
keySz = DILITHIUM_ML_DSA_44_PRV_KEY_SIZE;
withAlg = ML_DSA_LEVEL2k;
break;
case WC_ML_DSA_65:
keySz = DILITHIUM_ML_DSA_65_PRV_KEY_SIZE;
withAlg = ML_DSA_LEVEL3k;
break;
case WC_ML_DSA_87:
keySz = DILITHIUM_ML_DSA_87_PRV_KEY_SIZE;
withAlg = ML_DSA_LEVEL5k;
break;
default:
WOLFCLU_LOG(WOLFCLU_L0, "Invalid -level (%s), using level%d",
argv[ret+1], level);
break;
}
}
else {
/* no option -level */
WOLFCLU_LOG(WOLFCLU_L0, "No -level [ 2 | 3 | 5 ]");
WOLFCLU_LOG(WOLFCLU_L0, "DEFAULT: use Level %d", level);
}

/* get the directive argument */
ret = wolfCLU_checkForArg("-output", 7, argc, argv);
if (ret > 0) {
if (argv[ret+1] != NULL) {
if (XSTRNCMP(argv[ret+1], "pub", 3) == 0)
directiveArg = PUB_ONLY_FILE;
else if (XSTRNCMP(argv[ret+1], "priv", 4) == 0)
directiveArg = PRIV_ONLY_FILE;
else if (XSTRNCMP(argv[ret+1], "keypair", 7) == 0)
directiveArg = PRIV_AND_PUB_FILES;
}
}
else {
WOLFCLU_LOG(WOLFCLU_L0, "No -output <PUB/PRIV/KEYPAIR>");
WOLFCLU_LOG(WOLFCLU_L0, "DEFAULT: output public and private key pair");
}

levelStr = (level == WC_ML_DSA_44) ? "44" :
(level == WC_ML_DSA_65) ? "65" :
(level == WC_ML_DSA_87) ? "87" : "Unknown";
WOLFCLU_LOG(WOLFCLU_L0, "using ML-DSA-%s", levelStr);
ret = wolfCLU_genKey_ML_DSA(&rng, keyOutFName, directiveArg,
formatArg, keySz, level, withAlg);
#else
wolfCLU_LogError("Invalid option, ML-DSA not enabled.");
WOLFCLU_LOG(WOLFCLU_L0, "Please re-configure wolfSSL with "
"--enable-dilithium, --enable-experimental and try again");
wc_FreeRng(&rng);
return NOT_COMPILED_IN;
#endif /* HAVE_DILITHIUM */
}
else if (XSTRNCMP(keyType, "xmssmt", 6) == 0) {
#if defined(WOLFSSL_HAVE_XMSS) && defined(WOLFSSL_KEY_GEN)
int directiveArg = PRIV_AND_PUB_FILES;
Expand Down
3 changes: 3 additions & 0 deletions src/tools/clu_funcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ void wolfCLU_genKeyHelp(void)
#endif
#ifdef HAVE_DILITHIUM
,"dilithium"
,"ml-dsa"
#endif
#ifdef WOLFSSL_HAVE_XMSS
,"xmss"
Expand All @@ -489,6 +490,8 @@ void wolfCLU_genKeyHelp(void)
#ifdef HAVE_DILITHIUM
WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey dilithium -level "
"[2|3|5] -out mykey -outform der -output KEYPAIR");
WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey ml-dsa -level "
"[2|3|5] -out mykey -outform der -output KEYPAIR");
#endif
#ifdef WOLFSSL_HAVE_XMSS
WOLFCLU_LOG(WOLFCLU_L0, "wolfssl -genkey xmss -height [10|16|20] -out mykey -outform raw"
Expand Down
Loading
Loading