diff --git a/.gitignore b/.gitignore index d34fbf37..1bc8d81d 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ signature/signature #cergen certgen/newCert* certgen/certgen_example +certgen/csr_cryptocb certgen/csr_example certgen/csr_sign certgen/csr_w_ed25519_example diff --git a/certgen/Makefile b/certgen/Makefile index 000d612a..870b6f27 100644 --- a/certgen/Makefile +++ b/certgen/Makefile @@ -13,7 +13,7 @@ CFLAGS=-I$(WOLF_INSTALL_DIR)/include -Wall LIBS=-L$(WOLF_INSTALL_DIR)/lib -lwolfssl -all:certgen_example csr_example csr_w_ed25519_example csr_sign +all:certgen_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb certgen_example:certgen_example.o $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) @@ -27,8 +27,11 @@ csr_w_ed25519_example:csr_w_ed25519_example.o csr_sign:csr_sign.o $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) +csr_cryptocb:csr_cryptocb.o + $(CC) -o $@ $^ $(CFLAGS) $(CPPFLAGS) $(LIBS) + .PHONY: clean all clean: - rm -f *.o certgen_example csr_example csr_w_ed25519_example csr_sign + rm -f *.o certgen_example csr_example csr_w_ed25519_example csr_sign csr_cryptocb rm -f newCert.* diff --git a/certgen/csr_cryptocb.c b/certgen/csr_cryptocb.c new file mode 100644 index 00000000..2a5fa3f0 --- /dev/null +++ b/certgen/csr_cryptocb.c @@ -0,0 +1,505 @@ +/* csr_cryptocb.c + * + * Copyright (C) 2006-2022 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Example of generating a PEM-encoded certificate signing request (CSR). */ + +#ifndef WOLFSSL_USER_SETTINGS +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#define LARGE_TEMP_SZ 4096 + +#define DEBUG_CRYPTOCB + +#if defined(WOLF_CRYPTO_CB) && defined(WOLFSSL_CERT_REQ) && \ + defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN) + +static void usage(void) +{ + printf("Invalid input supplied try one of the below examples\n"); + printf("Examples:\n\n"); + printf("./csr_cryptocb rsa\n"); + printf("./csr_cryptocb ecc\n"); + printf("./csr_cryptocb ed25519\n"); +} + +/* Example custom context for crypto callback */ +typedef struct { + const char* keyFilePub; + const char* keyFilePriv; +} myCryptoCbCtx; + +#ifdef DEBUG_CRYPTOCB +static const char* GetAlgoTypeStr(int algo) +{ + switch (algo) { /* enum wc_AlgoType */ + case WC_ALGO_TYPE_HASH: return "Hash"; + case WC_ALGO_TYPE_CIPHER: return "Cipher"; + case WC_ALGO_TYPE_PK: return "PK"; + case WC_ALGO_TYPE_RNG: return "RNG"; + case WC_ALGO_TYPE_SEED: return "Seed"; + case WC_ALGO_TYPE_HMAC: return "HMAC"; + } + return NULL; +} +static const char* GetPkTypeStr(int pk) +{ + switch (pk) { + case WC_PK_TYPE_RSA: return "RSA"; + case WC_PK_TYPE_DH: return "DH"; + case WC_PK_TYPE_ECDH: return "ECDH"; + case WC_PK_TYPE_ECDSA_SIGN: return "ECDSA-Sign"; + case WC_PK_TYPE_ECDSA_VERIFY: return "ECDSA-Verify"; + case WC_PK_TYPE_ED25519_SIGN: return "ED25519-Sign"; + case WC_PK_TYPE_ED25519_VERIFY: return "ED25519-Verify"; + case WC_PK_TYPE_CURVE25519: return "CURVE25519"; + case WC_PK_TYPE_RSA_KEYGEN: return "RSA KeyGen"; + case WC_PK_TYPE_EC_KEYGEN: return "ECC KeyGen"; + } + return NULL; +} +#endif /* DEBUG_CRYPTOCB */ + +/* reads file size, allocates buffer, reads into buffer, returns buffer */ +static int load_file(const char* fname, byte** buf, size_t* bufLen) +{ + int ret; + long int fileSz; + XFILE lFile; + + if (fname == NULL || buf == NULL || bufLen == NULL) + return BAD_FUNC_ARG; + + /* set defaults */ + *buf = NULL; + *bufLen = 0; + + /* open file (read-only binary) */ + lFile = XFOPEN(fname, "rb"); + if (!lFile) { + printf("Error loading %s\n", fname); + return BAD_PATH_ERROR; + } + + fseek(lFile, 0, SEEK_END); + fileSz = (int)ftell(lFile); + rewind(lFile); + if (fileSz > 0) { + *bufLen = (size_t)fileSz; + *buf = (byte*)malloc(*bufLen); + if (*buf == NULL) { + ret = MEMORY_E; + printf("Error allocating %lu bytes\n", (unsigned long)*bufLen); + } + else { + size_t readLen = fread(*buf, *bufLen, 1, lFile); + + /* check response code */ + ret = (readLen > 0) ? 0 : -1; + } + } + else { + ret = BUFFER_E; + } + fclose(lFile); + + return ret; +} + +static int load_key_file(const char* fname, byte* derBuf, word32* derLen, + int isPubKey) +{ + int ret; + byte* buf = NULL; + size_t bufLen; + + ret = load_file(fname, &buf, &bufLen); + if (ret != 0) + return ret; + + if (isPubKey) { + ret = wc_PubKeyPemToDer(buf, (word32)bufLen, derBuf, (word32)bufLen); + } else { + ret = wc_KeyPemToDer(buf, (word32)bufLen, derBuf, (word32)bufLen, NULL); + } + if (ret < 0) { + free(buf); + return ret; + } + *derLen = ret; + free(buf); + + return 0; +} + +#ifdef WOLFSSL_DER_TO_PEM +static int save_der_as_pem(const byte* der, word32 derSz, const char* arg1, + int type) +{ + int ret; + byte pem[LARGE_TEMP_SZ]; + int pemSz; + FILE* file = NULL; + char outFile[255]; + + memset(pem, 0, sizeof(pem)); + ret = wc_DerToPem(der, derSz, pem, sizeof(pem), type); + if (ret <= 0) { + printf("CSR DER to PEM failed: %d\n", ret); + return ret; + } + pemSz = ret; + printf("%s (%d)\n", pem, pemSz); + + snprintf(outFile, sizeof(outFile), "%s-csr.pem", arg1); + printf("Saved CSR PEM to \"%s\"\n", outFile); + file = fopen(outFile, "wb"); + if (file) { + ret = (int)fwrite(pem, 1, pemSz, file); + if (ret == pemSz) { + ret = 0; + } + fclose(file); + } + else { + ret = -1; + } + return ret; +} +#endif + +/* Example crypto dev callback function that calls software version */ +/* This is where you would plug-in calls to your own hardware crypto */ +static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) +{ + int ret = CRYPTOCB_UNAVAILABLE; /* return this to bypass HW and use SW */ + myCryptoCbCtx* myCtx = (myCryptoCbCtx*)ctx; + byte der[LARGE_TEMP_SZ]; + word32 derSz; + word32 idx = 0; + + if (info == NULL) + return BAD_FUNC_ARG; + + ret = load_key_file(myCtx->keyFilePriv, der, &derSz, 0); + if (ret != 0) { + printf("Error %d loading %s\n", ret, myCtx->keyFilePriv); + return ret; + } + + if (info->algo_type == WC_ALGO_TYPE_PK) { + #ifdef DEBUG_CRYPTOCB + printf("CryptoCb: %s %s (%d)\n", GetAlgoTypeStr(info->algo_type), + GetPkTypeStr(info->pk.type), info->pk.type); + #endif + + #ifndef NO_RSA + if (info->pk.type == WC_PK_TYPE_RSA) { + RsaKey rsaPriv; + ret = wc_InitRsaKey_ex(&rsaPriv, NULL, INVALID_DEVID); + if (ret != 0) { + return ret; + } + ret = wc_RsaPrivateKeyDecode(der, &idx, &rsaPriv, derSz); + if (ret != 0) { + wc_FreeRsaKey(&rsaPriv); + return ret; + } + + switch (info->pk.rsa.type) { + case RSA_PUBLIC_ENCRYPT: + case RSA_PUBLIC_DECRYPT: + /* perform software based RSA public op */ + ret = wc_RsaFunction( + info->pk.rsa.in, info->pk.rsa.inLen, + info->pk.rsa.out, info->pk.rsa.outLen, + info->pk.rsa.type, &rsaPriv, info->pk.rsa.rng); + break; + case RSA_PRIVATE_ENCRYPT: + case RSA_PRIVATE_DECRYPT: + /* perform software based RSA private op */ + ret = wc_RsaFunction( + info->pk.rsa.in, info->pk.rsa.inLen, + info->pk.rsa.out, info->pk.rsa.outLen, + info->pk.rsa.type, &rsaPriv, info->pk.rsa.rng); + break; + } + wc_FreeRsaKey(&rsaPriv); + } + #endif /* !NO_RSA */ + #ifdef HAVE_ECC + if (info->pk.type == WC_PK_TYPE_ECDSA_SIGN) { + ecc_key eccPriv; + ret = wc_ecc_init_ex(&eccPriv, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_EccPrivateKeyDecode(der, &idx, &eccPriv, derSz); + if (ret == 0) { + ret = wc_ecc_sign_hash( + info->pk.eccsign.in, info->pk.eccsign.inlen, + info->pk.eccsign.out, info->pk.eccsign.outlen, + info->pk.eccsign.rng, &eccPriv); + } + wc_ecc_free(&eccPriv); + } + } + #endif /* HAVE_ECC */ + #ifdef HAVE_ED25519 + if (info->pk.type == WC_PK_TYPE_ED25519_SIGN) { + ed25519_key edPriv; + ret = wc_ed25519_init_ex(&edPriv, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Ed25519PrivateKeyDecode(der, &idx, &edPriv, derSz); + if (ret == 0) { + /* calculate public key */ + ret = wc_ed25519_make_public(&edPriv, edPriv.p, + ED25519_PUB_KEY_SIZE); + } + if (ret == 0) { + edPriv.pubKeySet = 1; + ret = wc_ed25519_sign_msg_ex( + info->pk.ed25519sign.in, info->pk.ed25519sign.inLen, + info->pk.ed25519sign.out, info->pk.ed25519sign.outLen, + &edPriv, info->pk.ed25519sign.type, + info->pk.ed25519sign.context, + info->pk.ed25519sign.contextLen); + } + wc_ed25519_free(&edPriv); + } + } + #endif /* HAVE_ED25519 */ + } + + (void)devIdArg; + (void)myCtx; + + return ret; +} + +static int gen_csr(const char* arg1) +{ + int ret; + int type; +#ifdef HAVE_ECC + ecc_key ecKeyPub; +#endif +#ifndef NO_RSA + RsaKey rsaKeyPub; +#endif +#ifdef HAVE_ED25519 + ed25519_key edKeyPub; +#endif + void* keyPtr = NULL; + WC_RNG rng; + Cert req; + byte der[LARGE_TEMP_SZ]; + word32 derSz; + word32 idx = 0; + myCryptoCbCtx myCtx; + int devId = 1; /* any value besides INVALID_DEVID */ + + if (XSTRNCMP(arg1, "rsa", 3) == 0) + type = RSA_TYPE; + else if (XSTRNCMP(arg1, "ecc", 3) == 0) + type = ECC_TYPE; + else if (XSTRNCMP(arg1, "ed25519", 7) == 0) + type = ED25519_TYPE; + else + return NOT_COMPILED_IN; + + wolfCrypt_Init(); + + /* register a devID for crypto callbacks */ + ret = wc_CryptoCb_RegisterDevice(devId, myCryptoDevCb, &myCtx); + if (ret != 0) { + printf("Crypto callback register failed: %d\n", ret); + goto exit; + } + + ret = wc_InitRng(&rng); + if (ret != 0) { + printf("RNG initialization failed: %d\n", ret); + goto exit; + } + + /* setup test key */ +#ifdef HAVE_ECC + if (type == ECC_TYPE) { + myCtx.keyFilePub = "../certs/ecc-keyPub.pem"; + myCtx.keyFilePriv = "../certs/ecc-key.pem"; + } +#endif +#ifndef NO_RSA + if (type == RSA_TYPE) { + myCtx.keyFilePub = "../certs/client-keyPub.pem"; + myCtx.keyFilePriv = "../certs/client-key.pem"; + } +#endif +#ifdef HAVE_ED25519 + if (type == ED25519_TYPE) { + myCtx.keyFilePub = "../certs/ed25519-keyPub.pem"; + myCtx.keyFilePriv = "../certs/ed25519-keyPriv.pem"; + } +#endif + + /* convert PEM to DER */ + derSz = sizeof(der); + ret = load_key_file(myCtx.keyFilePub, der, &derSz, 1); + if (ret != 0) { + printf("Error %d loading the public key %s\n", ret, myCtx.keyFilePub); + goto exit; + } + + /* setup public key */ +#ifdef HAVE_ECC + if (type == ECC_TYPE) { + keyPtr = &ecKeyPub; + ret = wc_ecc_init_ex(&ecKeyPub, NULL, devId); + } +#endif +#ifndef NO_RSA + if (type == RSA_TYPE) { + keyPtr = &rsaKeyPub; + ret = wc_InitRsaKey_ex(&rsaKeyPub, NULL, devId); + } +#endif +#ifdef HAVE_ED25519 + if (type == ED25519_TYPE) { + keyPtr = &edKeyPub; + ret = wc_ed25519_init_ex(&edKeyPub, NULL, devId); + } +#endif + if (ret != 0) { + printf("Key initialization failed: %d\n", ret); + goto exit; + } + + /* decode public key */ +#ifdef HAVE_ECC + if (type == ECC_TYPE) { + ret = wc_EccPublicKeyDecode(der, &idx, &ecKeyPub, derSz); + } +#endif +#ifndef NO_RSA + if (type == RSA_TYPE) { + ret = wc_RsaPublicKeyDecode(der, &idx, &rsaKeyPub, derSz); + } +#endif +#ifdef HAVE_ED25519 + if (type == ED25519_TYPE) { + ret = wc_Ed25519PublicKeyDecode(der, &idx, &edKeyPub, derSz); + } +#endif + if (ret != 0) { + printf("Key decode failed: %d\n", ret); + goto exit; + } + + /* setup the CSR data */ + ret = wc_InitCert(&req); + if (ret != 0) { + printf("Init Cert failed: %d\n", ret); + goto exit; + } + strncpy(req.subject.country, "US", CTC_NAME_SIZE); + strncpy(req.subject.state, "OR", CTC_NAME_SIZE); + strncpy(req.subject.locality, "Portland", CTC_NAME_SIZE); + strncpy(req.subject.org, "wolfSSL", CTC_NAME_SIZE); + strncpy(req.subject.unit, "Development", CTC_NAME_SIZE); + strncpy(req.subject.commonName, "www.wolfssl.com", CTC_NAME_SIZE); + strncpy(req.subject.email, "info@wolfssl.com", CTC_NAME_SIZE); + ret = wc_MakeCertReq_ex(&req, der, sizeof(der), type, keyPtr); + if (ret <= 0) { + printf("Make Cert Req failed: %d\n", ret); + goto exit; + } + derSz = ret; + +#ifdef HAVE_ECC + if (type == ECC_TYPE) + req.sigType = CTC_SHA256wECDSA; +#endif +#ifndef NO_RSA + if (type == RSA_TYPE) + req.sigType = CTC_SHA256wRSA; +#endif +#ifdef HAVE_ED25519 + if (type == ED25519_TYPE) + req.sigType = CTC_ED25519; +#endif + /* Because the key has devId set, it will call myCryptoDevCb for signing */ + ret = wc_SignCert_ex(req.bodySz, req.sigType, der, sizeof(der), type, + keyPtr, &rng); + if (ret <= 0) { + printf("Sign Cert failed: %d\n", ret); + goto exit; + } + derSz = ret; + +#ifdef WOLFSSL_DER_TO_PEM + ret = save_der_as_pem(der, derSz, arg1, CERTREQ_TYPE); +#endif + + ret = 0; /* success */ + +exit: +#ifdef HAVE_ECC + if (type == ECC_TYPE) + wc_ecc_free(&ecKeyPub); +#endif +#ifndef NO_RSA + if (type == RSA_TYPE) + wc_FreeRsaKey(&rsaKeyPub); +#endif +#ifdef HAVE_ED25519 + if (type == ED25519_TYPE) + wc_ed25519_free(&edKeyPub); +#endif + wc_FreeRng(&rng); + + wolfCrypt_Cleanup(); + + return ret; +} + +#endif + +int main(int argc, char** argv) +{ +#if !defined(WOLF_CRYPTO_CB) || !defined(WOLFSSL_CERT_REQ) || \ + !defined(WOLFSSL_CERT_EXT) || !defined(WOLFSSL_CERT_GEN) + printf("Please compile wolfSSL with --enable-certreq --enable-certgen --enable-certext --enable-cryptocb\n"); + return 0; +#else + if (argc != 2) { + usage(); + return 1; + } + + return gen_csr(argv[1]); +#endif +} diff --git a/certgen/csr_example.c b/certgen/csr_example.c index 30638562..4be19560 100644 --- a/certgen/csr_example.c +++ b/certgen/csr_example.c @@ -255,7 +255,7 @@ exit: int main(int argc, char** argv) { #if !defined(WOLFSSL_CERT_REQ) || !defined(WOLFSSL_CERT_GEN) || !defined(WOLFSSL_KEY_GEN) - printf("Please compile wolfSSL with --enable-certreq --enable-certgen --enable-keygen\n"); + printf("Please compile wolfSSL with --enable-certreq --enable-certgen --enable-certext --enable-keygen\n"); return 0; #else if (argc != 2) { diff --git a/certs/client-keyPub.pem b/certs/client-keyPub.pem new file mode 100644 index 00000000..5f7c1cdd --- /dev/null +++ b/certs/client-keyPub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwwPRK/45pDJFO1PIhCsq +fHSavaoqUgdH1qY2sgcyjtC6aXvGw0Se1IFI/S1oootnu6F1yDYsStIb94u6zw35 +7+zxgR57mwNHmr9lzH9lJGmm6BSJW+Q098WwFJP1Z3s6enjhAVZWkaYTQo3SPECc +TO/Rht83URsMoTv18aNKNeThzpbfG36/TpfQEOioCDCBryALQxTFdGe0MoJvjYbC +iECZNoO6HkByIhfXUmUkc7DO7xnNrv94bHvAEgPUTnINUG07ozujmV6dyNkMhbPZ +itlUJttt+qy7/yVMxNF59HHThkAYE7BjtXJOMMSXhIYtVi/XFfd/wK71/Fvl+6G6 +0wIDAQAB +-----END PUBLIC KEY----- diff --git a/certs/ecc-keyPub.pem b/certs/ecc-keyPub.pem new file mode 100644 index 00000000..30363efa --- /dev/null +++ b/certs/ecc-keyPub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuzOsTCdQSsZKpQTDPN6fNttyLc6U +6iv6yyAJOSwW6GEC6a9N0wKTmjFbl5Ihf/DPGNqREQI0huggWDMLgDSJ2A== +-----END PUBLIC KEY----- diff --git a/certs/ed25519-keyPriv.pem b/certs/ed25519-keyPriv.pem new file mode 100644 index 00000000..d7c8a771 --- /dev/null +++ b/certs/ed25519-keyPriv.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIJK1TOyvgca7AdbV3r03l1rSxvbDhbU75uTsMunHylLr +-----END PRIVATE KEY----- diff --git a/certs/ed25519-keyPub.pem b/certs/ed25519-keyPub.pem new file mode 100644 index 00000000..134666ea --- /dev/null +++ b/certs/ed25519-keyPub.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEA5ldbExvHURRr7Tv10fqrnmy26wIJo5n1br+dPP5UOeY= +-----END PUBLIC KEY-----