mirror of https://github.com/wolfSSL/wolfssh.git
Add ML-DSA44-Ed25519 composite signature support
parent
b2b32cca51
commit
866f7392cd
|
|
@ -53,6 +53,7 @@
|
|||
#include <wolfssl/wolfcrypt/wc_port.h>
|
||||
#include <wolfssl/wolfcrypt/error-crypt.h>
|
||||
#include <wolfssl/wolfcrypt/coding.h>
|
||||
#include <wolfssl/wolfcrypt/asn_public.h>
|
||||
|
||||
#ifdef WOLFSSL_FPKI
|
||||
#include <wolfssl/wolfcrypt/asn.h>
|
||||
|
|
@ -160,14 +161,41 @@ struct WOLFSSHD_AUTH {
|
|||
#endif
|
||||
|
||||
#ifndef MAX_LINE_SZ
|
||||
/* Sized to hold the largest authorized_keys entry. */
|
||||
/* sized for the largest authorized_keys entry, composite pubkeys
|
||||
* included */
|
||||
#ifndef WOLFSSH_NO_MLDSA
|
||||
#ifndef WOLFSSH_NO_MLDSA87
|
||||
#define MAX_LINE_SZ ((WC_MLDSA_87_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
|
||||
#if defined(WOLFSSH_CERTS)
|
||||
/* x509v3-ssh-mldsa-87: ML-DSA-87 pubkey + CA sig + DER
|
||||
* overhead, base64-encoded */
|
||||
#define MAX_LINE_SZ \
|
||||
((WC_MLDSA_87_PUB_KEY_SIZE + WC_MLDSA_87_SIG_SIZE + \
|
||||
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
|
||||
#else
|
||||
#define MAX_LINE_SZ \
|
||||
((WC_MLDSA_87_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
|
||||
2) / 3 * 4 + 640)
|
||||
#endif
|
||||
#elif !defined(WOLFSSH_NO_MLDSA65)
|
||||
#define MAX_LINE_SZ ((WC_MLDSA_65_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
|
||||
#if defined(WOLFSSH_CERTS)
|
||||
#define MAX_LINE_SZ \
|
||||
((WC_MLDSA_65_PUB_KEY_SIZE + WC_MLDSA_65_SIG_SIZE + \
|
||||
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
|
||||
#else
|
||||
#define MAX_LINE_SZ \
|
||||
((WC_MLDSA_65_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
|
||||
2) / 3 * 4 + 640)
|
||||
#endif
|
||||
#else
|
||||
#define MAX_LINE_SZ ((WC_MLDSA_44_PUB_KEY_SIZE + 2) / 3 * 4 + 640)
|
||||
#if defined(WOLFSSH_CERTS)
|
||||
#define MAX_LINE_SZ \
|
||||
((WC_MLDSA_44_PUB_KEY_SIZE + WC_MLDSA_44_SIG_SIZE + \
|
||||
COMPOSITE_MAX_TRAD_PUB_SZ + 1024 + 2) / 3 * 4 + 640)
|
||||
#else
|
||||
#define MAX_LINE_SZ \
|
||||
((WC_MLDSA_44_PUB_KEY_SIZE + COMPOSITE_MAX_TRAD_PUB_SZ + \
|
||||
2) / 3 * 4 + 640)
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#define MAX_LINE_SZ 900
|
||||
|
|
@ -270,9 +298,10 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
|
|||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519)
|
||||
"ssh-mldsa44-ed25519@openssh.com",
|
||||
#endif
|
||||
};
|
||||
const int NUM_ALLOWED_TYPES =
|
||||
(int)(sizeof(allowedTypes) / sizeof(allowedTypes[0]));
|
||||
int typeOk = 0;
|
||||
int i;
|
||||
|
||||
|
|
@ -289,8 +318,9 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
|
|||
}
|
||||
}
|
||||
if (ret == WSSHD_AUTH_SUCCESS) {
|
||||
for (i = 0; i < NUM_ALLOWED_TYPES; ++i) {
|
||||
if (WSTRCMP(type, allowedTypes[i]) == 0) {
|
||||
for (i = 0; i < (int)(sizeof(allowedTypes) / sizeof(allowedTypes[0]));
|
||||
++i) {
|
||||
if (allowedTypes[i] != NULL && WSTRCMP(type, allowedTypes[i]) == 0) {
|
||||
typeOk = 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1342,6 +1372,78 @@ static int SearchKeysFile(const char* keysFilePath, const byte* key,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Detects OpenSSH vs ASN1/DER format of a raw host private key buffer.
|
||||
*
|
||||
* Uses wc_KeyPemToDer(), not wc_PemToDer(..., PRIVATEKEY_TYPE, ...): the
|
||||
* latter also unwraps PKCS#8 via ToTraditional(), which mangles key types
|
||||
* with no traditional DER form (e.g. ML-DSA).
|
||||
*
|
||||
* On a PEM buffer, *keyDer is a WMALLOC'd (heap, DYNTYPE_SSHD) buffer the
|
||||
* caller must WS_FORCEZERO + WFREE; NULL if data was passed through as-is
|
||||
* (raw DER or OpenSSH). privBuf/privBufSz are set to the buffer to actually
|
||||
* load. Returns WOLFSSH_FORMAT_ASN1/WOLFSSH_FORMAT_OPENSSH, or negative on
|
||||
* error. */
|
||||
int wolfSSHD_DetectPrivKeyFormat(byte* data, word32 dataSz, void* heap,
|
||||
byte** keyDer, byte** privBuf, word32* privBufSz)
|
||||
{
|
||||
int keyFormat = WOLFSSH_FORMAT_ASN1;
|
||||
byte* der;
|
||||
int derSz;
|
||||
|
||||
if (keyDer != NULL) {
|
||||
*keyDer = NULL;
|
||||
}
|
||||
if (privBuf != NULL) {
|
||||
*privBuf = NULL;
|
||||
}
|
||||
if (privBufSz != NULL) {
|
||||
*privBufSz = 0;
|
||||
}
|
||||
|
||||
if (data == NULL || dataSz == 0 || keyDer == NULL || privBuf == NULL ||
|
||||
privBufSz == NULL) {
|
||||
return WS_BAD_ARGUMENT;
|
||||
}
|
||||
|
||||
der = (byte*)WMALLOC(dataSz, heap, DYNTYPE_SSHD);
|
||||
if (der == NULL) {
|
||||
return WS_MEMORY_E;
|
||||
}
|
||||
|
||||
derSz = wc_KeyPemToDer(data, (int)dataSz, der, (int)dataSz, NULL);
|
||||
if (derSz <= 0) {
|
||||
WFREE(der, heap, DYNTYPE_SSHD);
|
||||
|
||||
*privBuf = data;
|
||||
*privBufSz = dataSz;
|
||||
|
||||
/* wstrnstr() stops at the first NUL, so binary buffers fall
|
||||
* through to the WMEMCMP magic check below. */
|
||||
if (WSTRNSTR((const char*)*privBuf,
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----", *privBufSz) != NULL) {
|
||||
keyFormat = WOLFSSH_FORMAT_OPENSSH;
|
||||
}
|
||||
else if (*privBufSz >= sizeof("openssh-key-v1") &&
|
||||
WMEMCMP(*privBuf, "openssh-key-v1",
|
||||
sizeof("openssh-key-v1")) == 0) {
|
||||
/* sizeof() includes the magic's trailing NUL */
|
||||
keyFormat = WOLFSSH_FORMAT_OPENSSH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*keyDer = der;
|
||||
*privBuf = der;
|
||||
*privBufSz = (word32)derSz;
|
||||
/* PEM-decoded result may still be an OpenSSH binary blob */
|
||||
if (*privBufSz >= sizeof("openssh-key-v1") &&
|
||||
WMEMCMP(*privBuf, "openssh-key-v1",
|
||||
sizeof("openssh-key-v1")) == 0) {
|
||||
keyFormat = WOLFSSH_FORMAT_OPENSSH;
|
||||
}
|
||||
}
|
||||
|
||||
return keyFormat;
|
||||
}
|
||||
|
||||
WOLFSSHD_STATIC int SearchForPubKey(const char* path,
|
||||
const char* authKeysFile, const char* user,
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ int wolfSSHD_GetHomeDirectory(WOLFSSHD_AUTH* auth, WOLFSSH* ssh, WCHAR* out, int
|
|||
int wolfSSHD_OpenSecureFile(const char* path, WUID_T ownerUid,
|
||||
int rejectReadable, void* heap, WFILE** out);
|
||||
|
||||
/* classifies a loaded host private key buffer as OpenSSH or ASN1/DER */
|
||||
int wolfSSHD_DetectPrivKeyFormat(byte* data, word32 dataSz, void* heap,
|
||||
byte** keyDer, byte** privBuf, word32* privBufSz);
|
||||
|
||||
#ifdef WOLFSSHD_UNIT_TEST
|
||||
#ifndef _WIN32
|
||||
extern int (*wsshd_setregid_cb)(WGID_T, WGID_T);
|
||||
|
|
|
|||
|
|
@ -2318,18 +2318,19 @@ static int test_ScanShadowFile_truncatedLine(void)
|
|||
#endif /* WOLFSSH_HAVE_LIBCRYPT || WOLFSSH_HAVE_LIBLOGIN */
|
||||
|
||||
#ifdef WOLFSSL_BASE64_ENCODE
|
||||
/* Build a mutable "ssh-rsa <base64(key)>" line; WSTRTOK mutates in place. */
|
||||
static int BuildAuthKeysLine(const byte* key, word32 keySz,
|
||||
char* lineOut, word32 lineOutSz)
|
||||
/* Build a mutable "<type> <base64(key)>" line; WSTRTOK mutates in place. */
|
||||
static int BuildAuthKeysLineType(const char* type, const byte* key,
|
||||
word32 keySz, char* lineOut, word32 lineOutSz)
|
||||
{
|
||||
static const char prefix[] = "ssh-rsa ";
|
||||
word32 prefixLen = (word32)(sizeof(prefix) - 1);
|
||||
word32 typeLen = (word32)WSTRLEN(type);
|
||||
word32 prefixLen = typeLen + 1;
|
||||
word32 b64Sz;
|
||||
|
||||
if (lineOutSz <= prefixLen) {
|
||||
return WS_BUFFER_E;
|
||||
}
|
||||
WMEMCPY(lineOut, prefix, prefixLen);
|
||||
WMEMCPY(lineOut, type, typeLen);
|
||||
lineOut[typeLen] = ' ';
|
||||
b64Sz = lineOutSz - prefixLen;
|
||||
if (Base64_Encode_NoNl(key, keySz, (byte*)lineOut + prefixLen, &b64Sz)
|
||||
!= 0) {
|
||||
|
|
@ -2343,7 +2344,111 @@ static int BuildAuthKeysLine(const byte* key, word32 keySz,
|
|||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Negative-path coverage for CheckAuthKeysLine's ConstantCompare clause. */
|
||||
static int BuildAuthKeysLine(const byte* key, word32 keySz,
|
||||
char* lineOut, word32 lineOutSz)
|
||||
{
|
||||
return BuildAuthKeysLineType("ssh-rsa", key, keySz, lineOut, lineOutSz);
|
||||
}
|
||||
|
||||
/* Confirms every key-type string in CheckAuthKeysLine's allowedTypes[] table
|
||||
* is recognized, guarding against allowedTypes[]/NUM_ALLOWED_TYPES drifting
|
||||
* out of sync as the ML-DSA/composite/cert #ifdef branches change. */
|
||||
static int test_CheckAuthKeysLineTypes(void)
|
||||
{
|
||||
static const char* types[] = {
|
||||
"ssh-rsa",
|
||||
"ssh-ed25519",
|
||||
"ecdsa-sha2-nistp256",
|
||||
"ecdsa-sha2-nistp384",
|
||||
"ecdsa-sha2-nistp521",
|
||||
#ifdef WOLFSSH_CERTS
|
||||
"x509v3-ssh-rsa",
|
||||
"x509v3-ecdsa-sha2-nistp256",
|
||||
"x509v3-ecdsa-sha2-nistp384",
|
||||
"x509v3-ecdsa-sha2-nistp521",
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_MLDSA
|
||||
#ifndef WOLFSSH_NO_MLDSA44
|
||||
"ssh-mldsa-44",
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_MLDSA65
|
||||
"ssh-mldsa-65",
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_MLDSA87
|
||||
"ssh-mldsa-87",
|
||||
#endif
|
||||
#ifdef WOLFSSH_CERTS
|
||||
#ifndef WOLFSSH_NO_MLDSA44
|
||||
"x509v3-ssh-mldsa-44",
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_MLDSA65
|
||||
"x509v3-ssh-mldsa-65",
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_MLDSA87
|
||||
"x509v3-ssh-mldsa-87",
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519)
|
||||
"ssh-mldsa44-ed25519@openssh.com",
|
||||
#endif
|
||||
};
|
||||
static const char keyAStr[] = "wolfssh-auth-key-test-A-AAAAAAA";
|
||||
static const char keyBStr[] = "wolfssh-auth-key-test-B-BBBBBBB";
|
||||
const byte* keyA = (const byte*)keyAStr;
|
||||
const byte* keyB = (const byte*)keyBStr;
|
||||
const word32 keySz = (word32)(sizeof(keyAStr) - 1);
|
||||
char line[256];
|
||||
char lineCopy[256];
|
||||
word32 i;
|
||||
int ret = WS_SUCCESS;
|
||||
int rc;
|
||||
|
||||
for (i = 0; i < (word32)(sizeof(types) / sizeof(types[0])); i++) {
|
||||
ret = BuildAuthKeysLineType(types[i], keyA, keySz, line, sizeof(line));
|
||||
if (ret != WS_SUCCESS) {
|
||||
Log(" CheckAuthKeysLine type %s: build failed.\n", types[i]);
|
||||
return ret;
|
||||
}
|
||||
Log(" Testing scenario: known type %s reaches key comparison.",
|
||||
types[i]);
|
||||
WMEMCPY(lineCopy, line, WSTRLEN(line) + 1);
|
||||
/* Non-matching key: a recognized type must proceed to the key
|
||||
* comparison and report a plain auth failure, not a negative error. */
|
||||
rc = CheckAuthKeysLine(lineCopy, (word32)WSTRLEN(lineCopy),
|
||||
keyB, keySz);
|
||||
if (rc == WSSHD_AUTH_FAILURE) {
|
||||
Log(" PASSED.\n");
|
||||
}
|
||||
else {
|
||||
Log(" FAILED (rc=%d).\n", rc);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* An unknown type must be rejected with a negative status. */
|
||||
ret = BuildAuthKeysLineType("ssh-bogus-type", keyA, keySz, line,
|
||||
sizeof(line));
|
||||
if (ret != WS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
Log(" Testing scenario: unknown type is rejected.");
|
||||
WMEMCPY(lineCopy, line, WSTRLEN(line) + 1);
|
||||
rc = CheckAuthKeysLine(lineCopy, (word32)WSTRLEN(lineCopy), keyA, keySz);
|
||||
if (rc < 0) {
|
||||
Log(" PASSED.\n");
|
||||
}
|
||||
else {
|
||||
Log(" FAILED (rc=%d).\n", rc);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Negative-path coverage for CheckAuthKeysLine so mutation of the
|
||||
* ConstantCompare clause (the only substantive bytewise check after the
|
||||
* length comparison) does not survive the test suite. */
|
||||
static int test_CheckAuthKeysLine(void)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
|
|
@ -5346,6 +5451,148 @@ static int test_ResolveAuthKeysPath(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* read an entire file into a heap buffer; *outSz is set to the file size.
|
||||
* returns NULL on any failure */
|
||||
static byte* ReadWholeFile(const char* path, word32* outSz)
|
||||
{
|
||||
FILE* f;
|
||||
byte* buf = NULL;
|
||||
long sz;
|
||||
|
||||
f = fopen(path, "rb");
|
||||
if (f == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (fseek(f, 0, SEEK_END) != 0 || (sz = ftell(f)) < 0 ||
|
||||
fseek(f, 0, SEEK_SET) != 0) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
buf = (byte*)malloc((size_t)sz);
|
||||
if (buf != NULL) {
|
||||
if (fread(buf, 1, (size_t)sz, f) != (size_t)sz) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
if (buf != NULL) {
|
||||
*outSz = (word32)sz;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* locate the repo's keys/ directory regardless of whether this binary is run
|
||||
* from the repo root or from apps/wolfsshd/test/ */
|
||||
static int BuildKeyPath(const char* name, char* out, size_t outSz)
|
||||
{
|
||||
static const char* candidates[] = { "keys/", "../../../keys/" };
|
||||
word32 i;
|
||||
FILE* f;
|
||||
|
||||
for (i = 0; i < (word32)(sizeof(candidates) / sizeof(candidates[0]));
|
||||
i++) {
|
||||
snprintf(out, outSz, "%s%s", candidates[i], name);
|
||||
f = fopen(out, "rb");
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
}
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* Regression coverage for wolfSSHD_DetectPrivKeyFormat(), the host-key
|
||||
* format auto-detection SetupCTX() relies on to load PEM-armored OpenSSH
|
||||
* keys, raw binary openssh-key-v1 blobs (including composite ML-DSA host
|
||||
* keys, which are only ever stored in that raw form), and traditional
|
||||
* PEM/DER keys. */
|
||||
static int test_DetectPrivKeyFormat(void)
|
||||
{
|
||||
typedef struct {
|
||||
const char* desc;
|
||||
const char* file;
|
||||
int wantFormat;
|
||||
} DPK_CASE;
|
||||
static const DPK_CASE cases[] = {
|
||||
{ "PEM-armored OpenSSH key", "id_ecdsa", WOLFSSH_FORMAT_OPENSSH },
|
||||
{ "raw binary openssh-key-v1 composite ML-DSA key",
|
||||
"server-key-mldsa44ed25519", WOLFSSH_FORMAT_OPENSSH },
|
||||
{ "PEM traditional key decodes to DER/ASN1", "server-key-ecc.pem",
|
||||
WOLFSSH_FORMAT_ASN1 },
|
||||
};
|
||||
word32 i;
|
||||
int ret = WS_SUCCESS;
|
||||
byte dummy = 0;
|
||||
byte* badKeyDer = NULL;
|
||||
byte* badPrivBuf = NULL;
|
||||
word32 badPrivBufSz = 0;
|
||||
int badGot;
|
||||
|
||||
/* A 0-byte host key file (or otherwise bad arguments) must be rejected
|
||||
* without touching the out-params, matching the empty-file case
|
||||
* getBufferFromFile() can hand back. */
|
||||
badGot = wolfSSHD_DetectPrivKeyFormat(&dummy, 0, NULL, &badKeyDer,
|
||||
&badPrivBuf, &badPrivBufSz);
|
||||
Log(" Testing scenario: 0-length buffer. %s\n",
|
||||
(badGot == WS_BAD_ARGUMENT && badPrivBuf == NULL && badPrivBufSz == 0)
|
||||
? "PASSED" : "FAILED");
|
||||
if (badGot != WS_BAD_ARGUMENT || badPrivBuf != NULL ||
|
||||
badPrivBufSz != 0) {
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
badGot = wolfSSHD_DetectPrivKeyFormat(NULL, sizeof(dummy), NULL,
|
||||
&badKeyDer, &badPrivBuf, &badPrivBufSz);
|
||||
Log(" Testing scenario: NULL data pointer. %s\n",
|
||||
(badGot == WS_BAD_ARGUMENT) ? "PASSED" : "FAILED");
|
||||
if (badGot != WS_BAD_ARGUMENT) {
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < (word32)(sizeof(cases) / sizeof(cases[0])); i++) {
|
||||
char path[128];
|
||||
byte* data;
|
||||
word32 dataSz = 0;
|
||||
byte* keyDer = NULL;
|
||||
byte* privBuf = NULL;
|
||||
word32 privBufSz = 0;
|
||||
int gotFormat;
|
||||
|
||||
if (BuildKeyPath(cases[i].file, path, sizeof(path)) != WS_SUCCESS) {
|
||||
Log(" Testing scenario: %s. FAILED (couldn't locate %s)\n",
|
||||
cases[i].desc, cases[i].file);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
data = ReadWholeFile(path, &dataSz);
|
||||
if (data == NULL) {
|
||||
Log(" Testing scenario: %s. FAILED (couldn't read %s)\n",
|
||||
cases[i].desc, path);
|
||||
return WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
gotFormat = wolfSSHD_DetectPrivKeyFormat(data, dataSz, NULL, &keyDer,
|
||||
&privBuf, &privBufSz);
|
||||
|
||||
Log(" Testing scenario: %s. %s\n", cases[i].desc,
|
||||
(gotFormat == cases[i].wantFormat) ? "PASSED" : "FAILED");
|
||||
if (gotFormat != cases[i].wantFormat) {
|
||||
ret = WS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
if (keyDer != NULL) {
|
||||
WFREE(keyDer, NULL, DYNTYPE_SSHD);
|
||||
}
|
||||
free(data);
|
||||
if (ret != WS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_ConfigDefaults),
|
||||
TEST_DECL(test_PermitRootProhibitPassword),
|
||||
|
|
@ -5374,8 +5621,10 @@ const TEST_CASE testCases[] = {
|
|||
TEST_DECL(test_OpenSecureFile),
|
||||
TEST_DECL(test_ConfigSavePID),
|
||||
#endif
|
||||
TEST_DECL(test_DetectPrivKeyFormat),
|
||||
#ifdef WOLFSSL_BASE64_ENCODE
|
||||
TEST_DECL(test_CheckAuthKeysLine),
|
||||
TEST_DECL(test_CheckAuthKeysLineTypes),
|
||||
#endif
|
||||
#if defined(WOLFSSL_BASE64_ENCODE) && !defined(_WIN32)
|
||||
TEST_DECL(test_SearchForPubKey),
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
|
|||
byte** banner)
|
||||
{
|
||||
int ret = WS_SUCCESS;
|
||||
byte* keyDer = NULL;
|
||||
byte* privBuf = NULL;
|
||||
word32 privBufSz = 0;
|
||||
void* heap = NULL;
|
||||
|
|
@ -415,20 +416,13 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
|
|||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
/* Host keys may be PEM or DER. Detect by content: a DER key is
|
||||
* an ASN.1 SEQUENCE (leading 0x30); anything else is treated as
|
||||
* PEM text and decoded with wc_KeyPemToDer(), which handles
|
||||
* PKCS#1, SEC1 and PKCS#8 "PRIVATE KEY" bodies.
|
||||
*
|
||||
* The previous code used wc_PemToDer(..., PRIVATEKEY_TYPE, ...),
|
||||
* which only recognizes the classic "RSA/EC PRIVATE KEY" PEM
|
||||
* headers. On a PKCS#8 body (how ML-DSA host keys are emitted)
|
||||
* it returns *success* but yields a malformed body (leading
|
||||
* 0x04, not a 0x30 SEQUENCE), which
|
||||
* wolfSSH_CTX_UsePrivateKey_buffer() then rejects with
|
||||
* WS_BAD_FILETYPE_E, so ML-DSA PEM host keys could not load. */
|
||||
byte* keyDer = NULL;
|
||||
|
||||
/* Host keys may be OpenSSH, PEM, or DER.
|
||||
* wolfSSHD_DetectPrivKeyFormat() uses wc_KeyPemToDer() (not
|
||||
* wc_PemToDer(..., PRIVATEKEY_TYPE, ...), which unwraps
|
||||
* PKCS#8 via ToTraditional() and mangles key types with no
|
||||
* traditional DER form, e.g. ML-DSA) and also detects the
|
||||
* OpenSSH private-key format, used by composite ML-DSA host
|
||||
* keys generated in that format. */
|
||||
if (dataSz == 0) {
|
||||
/* An empty (0-byte) file passes the NULL check above but
|
||||
* carries no key material. Handle it explicitly as a file
|
||||
|
|
@ -438,36 +432,31 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
|
|||
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Host key file is empty.");
|
||||
ret = WS_BAD_FILE_E;
|
||||
}
|
||||
else if (data[0] == 0x30) {
|
||||
privBuf = data;
|
||||
privBufSz = dataSz;
|
||||
}
|
||||
else {
|
||||
keyDer = (byte*)WMALLOC(dataSz, heap, DYNTYPE_SSHD);
|
||||
if (keyDer == NULL) {
|
||||
ret = WS_MEMORY_E;
|
||||
int keyFormat = wolfSSHD_DetectPrivKeyFormat(data, dataSz,
|
||||
heap, &keyDer, &privBuf, &privBufSz);
|
||||
|
||||
if (keyFormat < 0) {
|
||||
wolfSSH_Log(WS_LOG_ERROR,
|
||||
"[SSHD] Host private key file is invalid.");
|
||||
ret = WS_BAD_FILE_E;
|
||||
}
|
||||
else if (keyFormat == WOLFSSH_FORMAT_OPENSSH) {
|
||||
wolfSSH_Log(WS_LOG_DEBUG, "[SSHD] Loading host private "
|
||||
"key as OpenSSH format.");
|
||||
}
|
||||
else {
|
||||
int keyDerSz = wc_KeyPemToDer(data, dataSz, keyDer,
|
||||
(int)dataSz, NULL);
|
||||
if (keyDerSz <= 0) {
|
||||
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to convert "
|
||||
"host private key from PEM.");
|
||||
ret = WS_BAD_FILE_E;
|
||||
}
|
||||
else {
|
||||
privBuf = keyDer;
|
||||
privBufSz = (word32)keyDerSz;
|
||||
}
|
||||
wolfSSH_Log(WS_LOG_DEBUG, "[SSHD] Loading host private "
|
||||
"key as DER format.");
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS
|
||||
&& wolfSSH_CTX_UsePrivateKey_buffer(*ctx, privBuf,
|
||||
privBufSz, WOLFSSH_FORMAT_ASN1) < 0) {
|
||||
wolfSSH_Log(WS_LOG_ERROR,
|
||||
"[SSHD] Failed to use host private key.");
|
||||
ret = WS_BAD_ARGUMENT;
|
||||
if (ret == WS_SUCCESS &&
|
||||
wolfSSH_CTX_UsePrivateKey_buffer(*ctx, privBuf,
|
||||
privBufSz, keyFormat) < 0) {
|
||||
wolfSSH_Log(WS_LOG_ERROR,
|
||||
"[SSHD] Failed to use host private key.");
|
||||
ret = WS_BAD_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyDer != NULL) {
|
||||
|
|
|
|||
|
|
@ -1861,8 +1861,70 @@ static int load_key_mldsa87(byte* buf, word32 bufSz)
|
|||
}
|
||||
#endif /* WOLFSSH_NO_MLDSA87 */
|
||||
|
||||
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519)
|
||||
/* buf NULL: *bufSz set to file size; else file read into buf (bufSz max) */
|
||||
static int load_key_mldsa44_ed25519(byte* buf, word32* bufSz)
|
||||
{
|
||||
word32 sz = 0;
|
||||
#ifndef NO_FILESYSTEM
|
||||
sz = load_file("./keys/server-key-mldsa44ed25519", buf, bufSz);
|
||||
#else
|
||||
(void)buf; (void)bufSz;
|
||||
#endif
|
||||
return sz;
|
||||
}
|
||||
#endif /* !WOLFSSH_NO_MLDSA44 && !WOLFSSH_NO_ED25519 */
|
||||
|
||||
#ifndef WOLFSSH_NO_MLDSA
|
||||
/* composite key buffer must be sized from the file, not a fixed constant */
|
||||
static int LoadMlDsaCompositeHostKey(WOLFSSH_CTX* ctx,
|
||||
int (*loadFn)(byte*, word32*), const char* label)
|
||||
{
|
||||
byte* compBuf = NULL;
|
||||
word32 compBufSz = 0;
|
||||
word32 compSz;
|
||||
|
||||
loadFn(NULL, &compBufSz);
|
||||
if (compBufSz == 0) {
|
||||
fprintf(stderr, "Couldn't find size of %s key file.\n", label);
|
||||
return -1;
|
||||
}
|
||||
compBuf = (byte*)WMALLOC(compBufSz, NULL, 0);
|
||||
if (compBuf == NULL) {
|
||||
fprintf(stderr, "Couldn't allocate %s key buffer.\n", label);
|
||||
return -1;
|
||||
}
|
||||
compSz = loadFn(compBuf, &compBufSz);
|
||||
if (compSz == 0) {
|
||||
WFREE(compBuf, NULL, 0);
|
||||
fprintf(stderr, "Couldn't load %s key file.\n", label);
|
||||
return -1;
|
||||
}
|
||||
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, compBuf, compSz,
|
||||
WOLFSSH_FORMAT_OPENSSH) < 0) {
|
||||
WFREE(compBuf, NULL, 0);
|
||||
fprintf(stderr, "Couldn't use %s key buffer.\n", label);
|
||||
return -1;
|
||||
}
|
||||
WFREE(compBuf, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* substr;
|
||||
int (*loadFn)(byte*, word32*);
|
||||
const char* label;
|
||||
} MlDsaCompositeEntry;
|
||||
|
||||
/* NULL-terminated so the table is never empty if ECDSA and Ed25519/Ed448
|
||||
* are both disabled */
|
||||
static const MlDsaCompositeEntry mldsaCompositeEntries[] = {
|
||||
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519)
|
||||
{ "mldsa44-ed25519", load_key_mldsa44_ed25519, "ML-DSA-44+Ed25519" },
|
||||
#endif
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static int LoadMlDsaHostKeys(WOLFSSH_CTX* ctx, const char* keyList)
|
||||
{
|
||||
byte* mldsaBuf;
|
||||
|
|
@ -3414,13 +3476,59 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
|
|||
* unconditionally would force mldsa negotiation on non-mldsa tests. */
|
||||
#ifndef WOLFSSH_NO_MLDSA
|
||||
if (keyList != NULL && WSTRSTR(keyList, "mldsa") != NULL) {
|
||||
if (LoadMlDsaHostKeys(ctx, keyList) != 0) {
|
||||
int mldsaErr = 0;
|
||||
int mldsaMatched = 0;
|
||||
|
||||
/* skip LoadMlDsaHostKeys() for a purely composite keyList; it
|
||||
* only knows plain "mldsa-NN" names and would abort */
|
||||
if (WSTRSTR(keyList, "mldsa-44") != NULL ||
|
||||
WSTRSTR(keyList, "mldsa-65") != NULL ||
|
||||
WSTRSTR(keyList, "mldsa-87") != NULL) {
|
||||
mldsaMatched = 1;
|
||||
if (LoadMlDsaHostKeys(ctx, keyList) != 0) {
|
||||
mldsaErr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mldsaErr) {
|
||||
#ifdef WOLFSSH_SMALL_STACK
|
||||
wc_ForceZero(keyLoadBuf, EXAMPLE_KEYLOAD_BUFFER_SZ);
|
||||
WFREE(keyLoadBuf, NULL, 0);
|
||||
#endif
|
||||
ES_ERROR("Error loading ML-DSA host keys.\n");
|
||||
}
|
||||
else {
|
||||
word32 mldsaIdx;
|
||||
|
||||
for (mldsaIdx = 0;
|
||||
mldsaCompositeEntries[mldsaIdx].substr != NULL;
|
||||
mldsaIdx++) {
|
||||
const MlDsaCompositeEntry* entry =
|
||||
&mldsaCompositeEntries[mldsaIdx];
|
||||
|
||||
if (WSTRSTR(keyList, entry->substr) != NULL) {
|
||||
mldsaMatched = 1;
|
||||
if (LoadMlDsaCompositeHostKey(ctx, entry->loadFn,
|
||||
entry->label) != 0) {
|
||||
#ifdef WOLFSSH_SMALL_STACK
|
||||
wc_ForceZero(keyLoadBuf, EXAMPLE_KEYLOAD_BUFFER_SZ);
|
||||
WFREE(keyLoadBuf, NULL, 0);
|
||||
#endif
|
||||
ES_ERROR("Error loading %s host key.\n",
|
||||
entry->label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mldsaMatched) {
|
||||
#ifdef WOLFSSH_SMALL_STACK
|
||||
wc_ForceZero(keyLoadBuf, EXAMPLE_KEYLOAD_BUFFER_SZ);
|
||||
WFREE(keyLoadBuf, NULL, 0);
|
||||
#endif
|
||||
ES_ERROR("ML-DSA key list '%s' matched no supported "
|
||||
"level.\n", keyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* WOLFSSH_NO_MLDSA */
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ EXTRA_DIST+= \
|
|||
keys/renewcerts.sh keys/renewcerts.cnf \
|
||||
keys/server-key-ed25519.der keys/server-key-ed25519.pem \
|
||||
keys/server-key-mldsa44.der keys/server-key-mldsa65.der \
|
||||
keys/server-key-mldsa87.der \
|
||||
keys/server-key-mldsa87.der keys/server-key-mldsa44ed25519 \
|
||||
keys/renew-ossh-certs.sh \
|
||||
keys/ossh-ca keys/ossh-ca.pub \
|
||||
keys/ossh-ca-rsa keys/ossh-ca-rsa.pub \
|
||||
|
|
|
|||
Binary file not shown.
1476
src/internal.c
1476
src/internal.c
File diff suppressed because it is too large
Load Diff
328
src/keygen.c
328
src/keygen.c
|
|
@ -47,7 +47,15 @@
|
|||
#ifndef WOLFSSH_NO_ECDSA
|
||||
#include <wolfssl/wolfcrypt/ecc.h>
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
#include <wolfssl/wolfcrypt/ed25519.h>
|
||||
#endif
|
||||
#ifdef HAVE_ED448
|
||||
#include <wolfssl/wolfcrypt/ed448.h>
|
||||
#endif
|
||||
#include <wolfssl/wolfcrypt/asn_public.h>
|
||||
#include <wolfssl/wolfcrypt/coding.h>
|
||||
#include <wolfssl/wolfcrypt/error-crypt.h>
|
||||
|
||||
#ifdef WOLFSSH_KEYGEN
|
||||
|
||||
|
|
@ -362,6 +370,326 @@ int wolfSSH_MakeMlDsaKey(byte* out, word32 outSz, word32 level)
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* builds an OpenSSH-key-v1 envelope (the only on-disk form for composite
|
||||
* keys, which have no ASN.1 representation); see
|
||||
* GetOpenSshKeyMlDsaComposite() in src/internal.c for the parser.
|
||||
* magic "openssh-key-v1\0", string ciphername, string kdfname,
|
||||
* string kdfoptions, uint32 keycount, string pubkeyblob,
|
||||
* string { checkint1, checkint2, { string type, string pub, string priv,
|
||||
* string comment }, padding }
|
||||
* pub is (mldsaPub || tradPub), priv is (mldsaSeed || tradPriv) */
|
||||
int wolfSSH_MakeMlDsaCompositeKey(byte* out, word32 outSz, word32 level,
|
||||
word32 tradType)
|
||||
{
|
||||
#if !defined(WOLFSSH_NO_MLDSA)
|
||||
static const char magic[] = "openssh-key-v1";
|
||||
static const char none[] = "none";
|
||||
const word32 noneSz = (word32)WSTRLEN(none);
|
||||
const char* keyTypeName;
|
||||
word32 keyTypeNameSz;
|
||||
byte keyId;
|
||||
CompositeParams params;
|
||||
int ret;
|
||||
WC_RNG rng;
|
||||
int rngInit = 0;
|
||||
MlDsaKey mldsaKey;
|
||||
int mldsaInit = 0;
|
||||
int mldsaGenOk;
|
||||
byte mldsaSeed[MLDSA_SEED_SZ];
|
||||
byte mldsaPub[WC_MLDSA_87_PUB_KEY_SIZE];
|
||||
byte tradPub[COMPOSITE_MAX_TRAD_PUB_SZ];
|
||||
byte tradPriv[COMPOSITE_MAX_TRAD_PRIV_SZ];
|
||||
word32 sz;
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
ecc_key eccKey;
|
||||
int eccInit = 0;
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
ed25519_key ed25519Key;
|
||||
int ed25519Init = 0;
|
||||
#endif
|
||||
#ifdef HAVE_ED448
|
||||
ed448_key ed448Key;
|
||||
int ed448Init = 0;
|
||||
#endif
|
||||
word32 fileSz, pubBlobSz, compositePubSz, compositePrivSz;
|
||||
word32 privKeysStrSz, padSz, off, i, checkint;
|
||||
|
||||
byte* tmpBuf = NULL;
|
||||
word32 b64Sz = 0;
|
||||
static const char header[] = "-----BEGIN OPENSSH PRIVATE KEY-----\n";
|
||||
static const char footer[] = "-----END OPENSSH PRIVATE KEY-----\n";
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_MakeMlDsaCompositeKey()");
|
||||
|
||||
if (out == NULL) {
|
||||
return WS_BAD_ARGUMENT;
|
||||
}
|
||||
|
||||
if (level == WOLFSSH_MLDSAKEY_44 &&
|
||||
tradType == WOLFSSH_COMPOSITE_TRAD_ED25519)
|
||||
keyId = ID_MLDSA44_ED25519;
|
||||
else {
|
||||
WLOG(WS_LOG_DEBUG, "Invalid ML-DSA composite level/trad combination");
|
||||
return WS_BAD_ARGUMENT;
|
||||
}
|
||||
|
||||
if (WS_GetCompositeParams(keyId, ¶ms) != WS_SUCCESS) {
|
||||
WLOG(WS_LOG_DEBUG, "Composite algorithm not compiled in");
|
||||
return WS_NOT_COMPILED;
|
||||
}
|
||||
|
||||
keyTypeName = IdToName(keyId);
|
||||
keyTypeNameSz = (word32)WSTRLEN(keyTypeName);
|
||||
|
||||
/* final base64 size vs. outSz is checked below, after PEM body build */
|
||||
compositePubSz = params.mldsaPubSz + params.tradPubSz;
|
||||
compositePrivSz = MLDSA_SEED_SZ + params.tradPrivSz;
|
||||
|
||||
pubBlobSz = UINT32_SZ + keyTypeNameSz + UINT32_SZ + compositePubSz;
|
||||
privKeysStrSz = UINT32_SZ * 2 /* checkints */
|
||||
+ UINT32_SZ + keyTypeNameSz
|
||||
+ UINT32_SZ + compositePubSz
|
||||
+ UINT32_SZ + compositePrivSz
|
||||
+ UINT32_SZ /* comment (empty) */;
|
||||
padSz = (MIN_BLOCK_SZ - (privKeysStrSz % MIN_BLOCK_SZ)) % MIN_BLOCK_SZ;
|
||||
privKeysStrSz += padSz;
|
||||
|
||||
fileSz = (word32)WSTRLEN(magic) + 1
|
||||
+ UINT32_SZ + noneSz /* ciphername */
|
||||
+ UINT32_SZ + noneSz /* kdfname */
|
||||
+ UINT32_SZ /* kdfoptions (empty) */
|
||||
+ UINT32_SZ /* keycount */
|
||||
+ UINT32_SZ + pubBlobSz
|
||||
+ UINT32_SZ + privKeysStrSz;
|
||||
|
||||
ret = wc_InitRng(&rng);
|
||||
if (ret != 0) {
|
||||
WLOG(WS_LOG_DEBUG, "Couldn't create RNG");
|
||||
return WS_CRYPTO_FAILED;
|
||||
}
|
||||
rngInit = 1;
|
||||
|
||||
ret = wc_RNG_GenerateBlock(&rng, mldsaSeed, sizeof(mldsaSeed));
|
||||
if (ret != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
if (wc_MlDsaKey_Init(&mldsaKey, NULL, INVALID_DEVID) != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
mldsaInit = 1;
|
||||
if (wc_MlDsaKey_SetParams(&mldsaKey, params.mldsaLevel) != 0 ||
|
||||
wc_MlDsaKey_MakeKeyFromSeed(&mldsaKey, mldsaSeed) != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.mldsaPubSz;
|
||||
if (wc_MlDsaKey_ExportPubRaw(&mldsaKey, mldsaPub, &sz) != 0 ||
|
||||
sz != params.mldsaPubSz) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
}
|
||||
if (ret != 0) {
|
||||
WLOG(WS_LOG_DEBUG, "Couldn't generate ML-DSA half of composite key");
|
||||
}
|
||||
mldsaGenOk = (ret == 0);
|
||||
|
||||
if (ret == 0 && params.tradType == TRAD_TYPE_ED25519) {
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
if (wc_ed25519_init(&ed25519Key) != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
ed25519Init = 1;
|
||||
if (wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &ed25519Key) != 0)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.tradPrivSz;
|
||||
if (wc_ed25519_export_private_only(&ed25519Key, tradPriv, &sz)
|
||||
!= 0 || sz != params.tradPrivSz)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.tradPubSz;
|
||||
if (wc_ed25519_export_public(&ed25519Key, tradPub, &sz) != 0 ||
|
||||
sz != params.tradPubSz)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
#else
|
||||
ret = WS_NOT_COMPILED;
|
||||
#endif
|
||||
}
|
||||
else if (ret == 0 && params.tradType == TRAD_TYPE_ED448) {
|
||||
#ifdef HAVE_ED448
|
||||
if (wc_ed448_init(&ed448Key) != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
ed448Init = 1;
|
||||
if (wc_ed448_make_key(&rng, ED448_KEY_SIZE, &ed448Key) != 0)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.tradPrivSz;
|
||||
if (wc_ed448_export_private_only(&ed448Key, tradPriv, &sz) != 0 ||
|
||||
sz != params.tradPrivSz)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.tradPubSz;
|
||||
if (wc_ed448_export_public(&ed448Key, tradPub, &sz) != 0 ||
|
||||
sz != params.tradPubSz)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
#else
|
||||
ret = WS_NOT_COMPILED;
|
||||
#endif
|
||||
}
|
||||
else if (ret == 0 && params.tradType == TRAD_TYPE_ECC) {
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
int eccKeySz = (int)params.tradPrivSz;
|
||||
|
||||
if (wc_ecc_init(&eccKey) != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
eccInit = 1;
|
||||
if (wc_ecc_make_key(&rng, eccKeySz, &eccKey) != 0)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.tradPrivSz;
|
||||
if (wc_ecc_export_private_only(&eccKey, tradPriv, &sz) != 0 ||
|
||||
sz != params.tradPrivSz)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ret == 0) {
|
||||
sz = params.tradPubSz;
|
||||
if (wc_ecc_export_x963(&eccKey, tradPub, &sz) != 0 ||
|
||||
sz != params.tradPubSz)
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
#else
|
||||
ret = WS_NOT_COMPILED;
|
||||
#endif
|
||||
}
|
||||
if (ret != 0 && mldsaGenOk) {
|
||||
WLOG(WS_LOG_DEBUG,
|
||||
"Couldn't generate traditional half of composite key");
|
||||
}
|
||||
|
||||
if (ret == 0 && wc_RNG_GenerateBlock(&rng, (byte*)&checkint,
|
||||
sizeof(checkint)) != 0) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
tmpBuf = (byte*)WMALLOC(fileSz, NULL, DYNTYPE_BUFFER);
|
||||
if (tmpBuf == NULL) {
|
||||
ret = WS_MEMORY_E;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
off = 0;
|
||||
WMEMCPY(tmpBuf + off, magic, WSTRLEN(magic) + 1);
|
||||
off += (word32)WSTRLEN(magic) + 1;
|
||||
c32toa(noneSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, none, noneSz); off += noneSz;
|
||||
c32toa(noneSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, none, noneSz); off += noneSz;
|
||||
c32toa(0, tmpBuf + off); off += UINT32_SZ; /* kdfoptions */
|
||||
c32toa(1, tmpBuf + off); off += UINT32_SZ; /* keycount */
|
||||
|
||||
c32toa(pubBlobSz, tmpBuf + off); off += UINT32_SZ;
|
||||
c32toa(keyTypeNameSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, keyTypeName, keyTypeNameSz); off += keyTypeNameSz;
|
||||
c32toa(compositePubSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, mldsaPub, params.mldsaPubSz);
|
||||
off += params.mldsaPubSz;
|
||||
WMEMCPY(tmpBuf + off, tradPub, params.tradPubSz); off += params.tradPubSz;
|
||||
|
||||
c32toa(privKeysStrSz, tmpBuf + off); off += UINT32_SZ;
|
||||
c32toa(checkint, tmpBuf + off); off += UINT32_SZ;
|
||||
c32toa(checkint, tmpBuf + off); off += UINT32_SZ;
|
||||
c32toa(keyTypeNameSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, keyTypeName, keyTypeNameSz); off += keyTypeNameSz;
|
||||
c32toa(compositePubSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, mldsaPub, params.mldsaPubSz);
|
||||
off += params.mldsaPubSz;
|
||||
WMEMCPY(tmpBuf + off, tradPub, params.tradPubSz); off += params.tradPubSz;
|
||||
c32toa(compositePrivSz, tmpBuf + off); off += UINT32_SZ;
|
||||
WMEMCPY(tmpBuf + off, mldsaSeed, MLDSA_SEED_SZ); off += MLDSA_SEED_SZ;
|
||||
WMEMCPY(tmpBuf + off, tradPriv, params.tradPrivSz);
|
||||
off += params.tradPrivSz;
|
||||
c32toa(0, tmpBuf + off); off += UINT32_SZ; /* comment (empty) */
|
||||
for (i = 1; i <= padSz; i++) {
|
||||
tmpBuf[off++] = (byte)i;
|
||||
}
|
||||
|
||||
if (off != fileSz) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else {
|
||||
int b64Ret = Base64_Encode(tmpBuf, fileSz, NULL, &b64Sz);
|
||||
if (b64Ret != 0 && b64Ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
else if (outSz < b64Sz + WSTRLEN(header) + WSTRLEN(footer)) {
|
||||
WLOG(WS_LOG_DEBUG, "Output buffer too small for composite key");
|
||||
ret = WS_BUFFER_E;
|
||||
}
|
||||
else {
|
||||
off = 0;
|
||||
WMEMCPY(out + off, header, WSTRLEN(header)); off += (word32)WSTRLEN(header);
|
||||
if (Base64_Encode(tmpBuf, fileSz, out + off, &b64Sz) == 0) {
|
||||
off += b64Sz;
|
||||
WMEMCPY(out + off, footer, WSTRLEN(footer)); off += (word32)WSTRLEN(footer);
|
||||
ret = (int)off;
|
||||
}
|
||||
else ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mldsaInit) wc_MlDsaKey_Free(&mldsaKey);
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
if (eccInit) wc_ecc_free(&eccKey);
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
if (ed25519Init) wc_ed25519_free(&ed25519Key);
|
||||
#endif
|
||||
#ifdef HAVE_ED448
|
||||
if (ed448Init) wc_ed448_free(&ed448Key);
|
||||
#endif
|
||||
if (rngInit) wc_FreeRng(&rng);
|
||||
|
||||
WS_FORCEZERO(mldsaSeed, sizeof(mldsaSeed));
|
||||
WS_FORCEZERO(tradPriv, sizeof(tradPriv));
|
||||
|
||||
if (tmpBuf != NULL) {
|
||||
WS_FORCEZERO(tmpBuf, fileSz);
|
||||
WFREE(tmpBuf, NULL, DYNTYPE_BUFFER);
|
||||
}
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_MakeMlDsaCompositeKey(), ret = %d",
|
||||
ret);
|
||||
return ret;
|
||||
#else
|
||||
WOLFSSH_UNUSED(out);
|
||||
WOLFSSH_UNUSED(outSz);
|
||||
WOLFSSH_UNUSED(level);
|
||||
WOLFSSH_UNUSED(tradType);
|
||||
return WS_NOT_COMPILED;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else /* WOLFSSL_KEY_GEN */
|
||||
#error "wolfSSH keygen requires that keygen is enabled in wolfSSL, use --enable-keygen or #define WOLFSSL_KEY_GEN."
|
||||
#endif /* WOLFSSL_KEY_GEN */
|
||||
|
|
|
|||
160
src/ossh.c
160
src/ossh.c
|
|
@ -297,6 +297,78 @@ static int GetOpenSshKeyMlDsa(MlDsaKey* key,
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse OpenSSH ML-DSA composite private key blob; see
|
||||
* GetOpenSshKeyPublicMlDsaComposite() for the public-key-only counterpart.
|
||||
* Returns WS_SUCCESS or negative WS_* error. */
|
||||
static int GetOpenSshKeyMlDsaComposite(byte keyId, MlDsaKey* mldsa,
|
||||
void* tradKey, void* heap, const byte* buf, word32 len, word32* idx)
|
||||
{
|
||||
const byte *pub = NULL;
|
||||
const byte *priv = NULL;
|
||||
word32 pubSz = 0;
|
||||
word32 privSz = 0;
|
||||
int ret;
|
||||
CompositeParams params;
|
||||
const CompositeTradOps* ops;
|
||||
|
||||
ret = WS_GetCompositeParams(keyId, ¶ms);
|
||||
if (ret != WS_SUCCESS) return ret;
|
||||
|
||||
ops = WS_GetTradOps(params.tradType);
|
||||
|
||||
/* caller doesn't clean up on error; each path below frees what it
|
||||
* already initialized */
|
||||
ret = wc_MlDsaKey_Init(mldsa, heap, INVALID_DEVID);
|
||||
if (ret != 0) {
|
||||
return WS_CRYPTO_FAILED;
|
||||
}
|
||||
ret = wc_MlDsaKey_SetParams(mldsa, params.mldsaLevel);
|
||||
if (ret != 0) {
|
||||
wc_MlDsaKey_Free(mldsa);
|
||||
return WS_CRYPTO_FAILED;
|
||||
}
|
||||
if (ops == NULL) {
|
||||
wc_MlDsaKey_Free(mldsa);
|
||||
return WS_UNIMPLEMENTED_E;
|
||||
}
|
||||
ret = ops->init(tradKey, heap);
|
||||
if (ret != 0) {
|
||||
wc_MlDsaKey_Free(mldsa);
|
||||
return WS_CRYPTO_FAILED;
|
||||
}
|
||||
|
||||
ret = GetStringRef(&pubSz, &pub, buf, len, idx);
|
||||
if (ret == WS_SUCCESS)
|
||||
ret = GetStringRef(&privSz, &priv, buf, len, idx);
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
word32 expectedPrivSz = MLDSA_SEED_SZ + params.tradPrivSz;
|
||||
|
||||
if (pubSz != (params.mldsaPubSz + params.tradPubSz) ||
|
||||
privSz != expectedPrivSz) {
|
||||
ret = WS_KEY_FORMAT_E;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = wc_MlDsaKey_ImportPubRaw(mldsa, pub, params.mldsaPubSz);
|
||||
}
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = wc_MlDsaKey_MakeKeyFromSeed(mldsa, priv);
|
||||
}
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = ops->importPriv(tradKey, priv + MLDSA_SEED_SZ, params.tradPrivSz,
|
||||
pub + params.mldsaPubSz, params.tradPubSz);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
wc_MlDsaKey_Free(mldsa);
|
||||
ops->free(tradKey);
|
||||
ret = WS_KEY_FORMAT_E;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSH_TPM
|
||||
|
|
@ -357,6 +429,64 @@ static int GetOpenSshKeyPublicMlDsa(MlDsaKey* key, const byte* buf,
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* public-key-only counterpart to GetOpenSshKeyMlDsaComposite(); no trailing
|
||||
* private-key string to parse. Returns WS_SUCCESS or negative WS_* error */
|
||||
static int GetOpenSshKeyPublicMlDsaComposite(byte keyId, MlDsaKey* mldsa,
|
||||
void* tradKey, void* heap, const byte* buf, word32 len, word32* idx)
|
||||
{
|
||||
int ret;
|
||||
int mldsaInit = 0;
|
||||
int tradInit = 0;
|
||||
const byte* pub = NULL;
|
||||
word32 pubSz = 0;
|
||||
CompositeParams params;
|
||||
const CompositeTradOps* ops;
|
||||
|
||||
ret = WS_GetCompositeParams(keyId, ¶ms);
|
||||
if (ret != WS_SUCCESS) return ret;
|
||||
|
||||
ops = WS_GetTradOps(params.tradType);
|
||||
|
||||
ret = wc_MlDsaKey_Init(mldsa, heap, INVALID_DEVID);
|
||||
if (ret == 0) {
|
||||
mldsaInit = 1;
|
||||
ret = wc_MlDsaKey_SetParams(mldsa, params.mldsaLevel);
|
||||
}
|
||||
if (ret == 0) {
|
||||
if (ops == NULL) {
|
||||
ret = WS_UNIMPLEMENTED_E;
|
||||
}
|
||||
else {
|
||||
ret = ops->init(tradKey, heap);
|
||||
if (ret == 0) tradInit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = GetStringRef(&pubSz, &pub, buf, len, idx);
|
||||
}
|
||||
if (ret == 0) {
|
||||
if (pubSz != (params.mldsaPubSz + params.tradPubSz)) {
|
||||
ret = WS_KEY_FORMAT_E;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_MlDsaKey_ImportPubRaw(mldsa, pub, params.mldsaPubSz);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = ops->importPub(tradKey, pub + params.mldsaPubSz, params.tradPubSz);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
if (mldsaInit) wc_MlDsaKey_Free(mldsa);
|
||||
if (tradInit) {
|
||||
ops->free(tradKey);
|
||||
}
|
||||
ret = WS_CRYPTO_FAILED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_RSA
|
||||
static int GetOpenSshPublicKeyRsa(RsaKey* key, const byte* buf, word32 len,
|
||||
|
|
@ -423,6 +553,12 @@ int GetOpenSshPublicKey(WS_KeySignature *key,
|
|||
ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len,
|
||||
idx, WC_ML_DSA_87);
|
||||
break;
|
||||
case ID_MLDSA44_ED25519:
|
||||
ret = GetOpenSshKeyPublicMlDsaComposite(keyId,
|
||||
&key->ks.mldsa_composite.mldsa,
|
||||
&key->ks.mldsa_composite.trad,
|
||||
key->heap, buf, len, idx);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ret = WS_UNIMPLEMENTED_E;
|
||||
|
|
@ -532,16 +668,36 @@ int GetOpenSshKey(WS_KeySignature *key,
|
|||
ret = GetOpenSshKeyMlDsa(
|
||||
&key->ks.mldsa.key,
|
||||
str, strSz, &subIdx, WC_ML_DSA_44);
|
||||
/* clear keyId: key already freed, avoid
|
||||
* double free */
|
||||
if (ret != WS_SUCCESS)
|
||||
key->keyId = ID_NONE;
|
||||
break;
|
||||
case ID_MLDSA65:
|
||||
ret = GetOpenSshKeyMlDsa(
|
||||
&key->ks.mldsa.key,
|
||||
str, strSz, &subIdx, WC_ML_DSA_65);
|
||||
if (ret != WS_SUCCESS)
|
||||
key->keyId = ID_NONE;
|
||||
break;
|
||||
case ID_MLDSA87:
|
||||
ret = GetOpenSshKeyMlDsa(
|
||||
&key->ks.mldsa.key,
|
||||
str, strSz, &subIdx, WC_ML_DSA_87);
|
||||
if (ret != WS_SUCCESS)
|
||||
key->keyId = ID_NONE;
|
||||
break;
|
||||
case ID_MLDSA44_ED25519:
|
||||
ret = GetOpenSshKeyMlDsaComposite(
|
||||
key->keyId,
|
||||
&key->ks.mldsa_composite.mldsa,
|
||||
&key->ks.mldsa_composite.trad,
|
||||
key->heap,
|
||||
str, strSz, &subIdx);
|
||||
/* clear keyId: key already freed, avoid
|
||||
* double free */
|
||||
if (ret != WS_SUCCESS)
|
||||
key->keyId = ID_NONE;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
|
@ -571,7 +727,9 @@ int GetOpenSshKey(WS_KeySignature *key,
|
|||
check1 <= check2;
|
||||
check1++, subIdx++) {
|
||||
if (check1 != str[subIdx]) {
|
||||
/* Bad pad value. */
|
||||
/* bad pad: free key decoded above */
|
||||
wolfSSH_KEY_clean(key);
|
||||
key->keyId = ID_NONE;
|
||||
ret = WS_KEY_FORMAT_E;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -518,6 +518,13 @@ int wolfSSH_KexTest(int argc, char** argv)
|
|||
#ifndef WOLFSSH_NO_MLDSA87
|
||||
AssertIntEQ(wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa-87"), EXIT_SUCCESS);
|
||||
#endif
|
||||
#if !defined(WOLFSSH_NO_MLDSA44) && !defined(WOLFSSH_NO_ED25519)
|
||||
/* Uses the "@openssh.com" wire name that OpenSSH negotiates for this
|
||||
* algorithm, matching what wolfSSH now emits (see NameIdMap). */
|
||||
AssertIntEQ(
|
||||
wolfSSH_KexTest_MlDsaHostKey("ssh-mldsa44-ed25519@openssh.com"),
|
||||
EXIT_SUCCESS);
|
||||
#endif
|
||||
|
||||
AssertIntEQ(wolfSSH_Cleanup(), WS_SUCCESS);
|
||||
|
||||
|
|
|
|||
1004
tests/unit.c
1004
tests/unit.c
File diff suppressed because it is too large
Load Diff
|
|
@ -40,6 +40,9 @@
|
|||
#include <wolfssl/wolfcrypt/rsa.h>
|
||||
#include <wolfssl/wolfcrypt/curve25519.h>
|
||||
#include <wolfssl/wolfcrypt/ed25519.h>
|
||||
#ifdef HAVE_ED448
|
||||
#include <wolfssl/wolfcrypt/ed448.h>
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSL_WOLFSSH
|
||||
#error "wolfssh requires wolfSSL built with WOLFSSL_WOLFSSH"
|
||||
|
|
@ -468,6 +471,8 @@ enum {
|
|||
ID_MLDSA44,
|
||||
ID_MLDSA65,
|
||||
ID_MLDSA87,
|
||||
/* always declared; NameIdMap/WS_GetCompositeParams() gate reachability */
|
||||
ID_MLDSA44_ED25519,
|
||||
#endif
|
||||
ID_X509V3_SSH_RSA,
|
||||
ID_X509V3_ECDSA_SHA2_NISTP256,
|
||||
|
|
@ -1315,9 +1320,103 @@ typedef struct WS_KeySignature {
|
|||
MlDsaKey key;
|
||||
} mldsa;
|
||||
#endif /* WOLFSSH_NO_MLDSA */
|
||||
#ifndef WOLFSSH_NO_MLDSA
|
||||
struct {
|
||||
MlDsaKey mldsa;
|
||||
union {
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
ecc_key ecc;
|
||||
#endif
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
ed25519_key ed25519;
|
||||
#endif
|
||||
#ifdef HAVE_ED448
|
||||
ed448_key ed448;
|
||||
#endif
|
||||
#if defined(WOLFSSH_NO_ECDSA) && defined(WOLFSSH_NO_ED25519) && \
|
||||
!defined(HAVE_ED448)
|
||||
/* keep union non-empty (empty union rejected by some
|
||||
* compilers) though unusable without a trad component */
|
||||
byte placeholder;
|
||||
#endif
|
||||
} trad;
|
||||
} mldsa_composite;
|
||||
#endif
|
||||
} ks;
|
||||
} WS_KeySignature;
|
||||
|
||||
#ifndef WOLFSSH_NO_ECDSA
|
||||
#define ECDSA_ASN_SIG_SZ 256
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSH_NO_MLDSA
|
||||
#define TRAD_TYPE_ECC 1
|
||||
#define TRAD_TYPE_ED25519 2
|
||||
#define TRAD_TYPE_ED448 3
|
||||
#define COMPOSITE_DOMAIN_PREFIX "CompositeAlgorithmSignatures2025"
|
||||
#define COMPOSITE_DOMAIN_PREFIX_SZ 32
|
||||
/* worst-case label size across all currently defined composite combos */
|
||||
#define COMPOSITE_MAX_LABEL_SZ 33
|
||||
#define ECC_P256_COORD_SZ 32
|
||||
#define ECC_P384_COORD_SZ 48
|
||||
/* worst-case trad public key size: P-384 uncompressed point */
|
||||
#define COMPOSITE_MAX_TRAD_PUB_SZ (1 + (2 * ECC_P384_COORD_SZ))
|
||||
/* worst-case trad private key size: Ed448 seed, else P-384 scalar */
|
||||
#ifdef HAVE_ED448
|
||||
#define COMPOSITE_MAX_TRAD_PRIV_SZ ED448_KEY_SIZE
|
||||
#else
|
||||
#define COMPOSITE_MAX_TRAD_PRIV_SZ ECC_P384_COORD_SZ
|
||||
#endif
|
||||
/* worst-case trad signature size: Ed448, else P-384 raw r/s */
|
||||
#ifdef HAVE_ED448
|
||||
#define COMPOSITE_MAX_TRAD_SIG_SZ ED448_SIG_SIZE
|
||||
#else
|
||||
#define COMPOSITE_MAX_TRAD_SIG_SZ (2 * ECC_P384_COORD_SZ)
|
||||
#endif
|
||||
/* defensive slack on top of BuildUserAuthRequestMlDsaComposite()'s
|
||||
* worst-case signature size; not load-bearing */
|
||||
#define COMPOSITE_SIG_ALLOC_SLACK_SZ 32
|
||||
|
||||
typedef struct CompositeParams {
|
||||
const char* label;
|
||||
word32 mldsaSigSz;
|
||||
word32 mldsaPubSz;
|
||||
word32 tradHashSz;
|
||||
word32 labelSz;
|
||||
word32 tradPubSz;
|
||||
word32 tradSigSz;
|
||||
word32 tradPrivSz;
|
||||
enum wc_HashType tradHashId;
|
||||
byte keyId;
|
||||
byte mldsaLevel;
|
||||
byte tradType;
|
||||
} CompositeParams;
|
||||
|
||||
/* dispatch table for a composite key's trad (ECC/Ed25519/Ed448) half; see
|
||||
* WS_GetTradOps() in src/internal.c */
|
||||
typedef struct CompositeTradOps {
|
||||
int (*init)(void* key, void* heap);
|
||||
void (*free)(void* key);
|
||||
int (*importPub)(void* key, const byte* pub, word32 pubSz);
|
||||
int (*importPriv)(void* key, const byte* priv, word32 privSz,
|
||||
const byte* pub, word32 pubSz);
|
||||
int (*exportPub)(void* key, byte* out, word32* outSz);
|
||||
int (*sign)(void* key, WC_RNG* rng, void* heap,
|
||||
enum wc_HashType tradHashId, word32 tradHashSz,
|
||||
const byte* mPrime, word32 mPrimeLen,
|
||||
byte* wireSig, word32* wireSigSz);
|
||||
int (*verify)(void* key, void* heap,
|
||||
enum wc_HashType tradHashId, word32 tradHashSz,
|
||||
const byte* wireSig, word32 wireSigSz,
|
||||
const byte* mPrime, word32 mPrimeLen);
|
||||
byte tradType;
|
||||
} CompositeTradOps;
|
||||
|
||||
WOLFSSH_LOCAL int WS_GetCompositeParams(byte keyId, CompositeParams* params);
|
||||
WOLFSSH_LOCAL const CompositeTradOps* WS_GetTradOps(byte tradType);
|
||||
WOLFSSH_LOCAL int WS_Hash_Helper(enum wc_HashType hashId, const byte* msg, word32 msgSz, byte* hash, word32 hashSz);
|
||||
#endif
|
||||
|
||||
WOLFSSH_LOCAL int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap,
|
||||
WS_KeySignature **pkey);
|
||||
WOLFSSH_LOCAL void wolfSSH_KEY_clean(WS_KeySignature* key);
|
||||
|
|
@ -1772,6 +1871,17 @@ enum WS_MessageIdLimits {
|
|||
WOLFSSH_API int wolfSSH_TestBuildUserAuthRequestMlDsa(WOLFSSH* ssh,
|
||||
byte* output, word32* idx, const WS_UserAuthData* authData,
|
||||
const byte* sigStart, word32 sigStartIdx, WS_KeySignature* keySig);
|
||||
WOLFSSH_API int wolfSSH_TestDoUserAuthRequestMlDsaComposite(WOLFSSH* ssh,
|
||||
WS_UserAuthData* authData, byte keyId, word32 pubKeyBlobSz);
|
||||
WOLFSSH_API int wolfSSH_TestPrepareUserAuthRequestMlDsaComposite(WOLFSSH* ssh,
|
||||
word32* payloadSz, const WS_UserAuthData* authData,
|
||||
WS_KeySignature* keySig);
|
||||
WOLFSSH_API int wolfSSH_TestSignHMlDsaComposite(WOLFSSH* ssh, byte* sig,
|
||||
word32* sigSz, byte keyId);
|
||||
WOLFSSH_API int wolfSSH_TestBuildUserAuthRequestMlDsaComposite(
|
||||
WOLFSSH* ssh, byte* output, word32* idx,
|
||||
const WS_UserAuthData* authData, const byte* sigStart,
|
||||
word32 sigStartIdx, WS_KeySignature* keySig);
|
||||
#endif /* !WOLFSSH_NO_MLDSA */
|
||||
#if defined(WOLFSSH_SCP) && !defined(WOLFSSH_SCP_USER_CALLBACKS)
|
||||
WOLFSSH_API int wolfSSH_TestScpExtractFileName(const char* filePath,
|
||||
|
|
|
|||
|
|
@ -46,12 +46,23 @@ extern "C" {
|
|||
#define WOLFSSH_MLDSAKEY_65 65
|
||||
#define WOLFSSH_MLDSAKEY_87 87
|
||||
|
||||
/* Traditional algorithm paired with the ML-DSA level in a composite key.
|
||||
* Not every (level, trad) pair is a defined composite: ED25519 pairs with
|
||||
* 44/65, ED448 pairs with 87 only, ECDSA pairs with all three levels (at
|
||||
* P-256 for 44/65, P-384 for 87). See WS_GetCompositeParams() in
|
||||
* src/internal.c for the authoritative list. */
|
||||
#define WOLFSSH_COMPOSITE_TRAD_ECDSA 1
|
||||
#define WOLFSSH_COMPOSITE_TRAD_ED25519 2
|
||||
#define WOLFSSH_COMPOSITE_TRAD_ED448 3
|
||||
|
||||
|
||||
WOLFSSH_API int wolfSSH_MakeRsaKey(byte* out, word32 outSz,
|
||||
word32 size, word32 e);
|
||||
WOLFSSH_API int wolfSSH_MakeEcdsaKey(byte* out, word32 outSz, word32 size);
|
||||
WOLFSSH_API int wolfSSH_MakeEd25519Key(byte* out, word32 outSz, word32 size);
|
||||
WOLFSSH_API int wolfSSH_MakeMlDsaKey(byte* out, word32 outSz, word32 level);
|
||||
WOLFSSH_API int wolfSSH_MakeMlDsaCompositeKey(byte* out, word32 outSz,
|
||||
word32 level, word32 tradType);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
Loading…
Reference in New Issue