Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/test-psa-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,10 @@ jobs:
make -C test psa_devid_cryptocb_test ASAN=1
rm -rf .store test/.store
./test/psa_devid_cryptocb_test

# LeakSanitizer checks that failed key file loads free their buffer
- name: Run the pk test under AddressSanitizer
run: |
rm -f test/psa_tf_psa_pk_test test/psa_server/psa_tf_psa_pk_test.o
make -C test psa_tf_psa_pk_test ASAN=1
./test/psa_tf_psa_pk_test
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,9 @@ build-test/out/
test/psa_zero_capacity_buffer_test
test/psa_tf_psa_extra_test
test/psa_want_surface_test
test/psa_tf_psa_pk_test
test/psa_tf_psa_threading_test
test/build-threads
test/build-threads-asan
.cache/
compile_commands.json
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ WOLFCRYPT_SRC := \
WOLFCRYPT_OBJ := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR)/wolfcrypt_%.o,$(WOLFCRYPT_SRC))
WOLFCRYPT_OBJ_PIC := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR_PIC)/wolfcrypt_%.o,$(WOLFCRYPT_SRC))

# MBEDTLS_THREADING_C=1 builds a thread safe library, as in Mbed TLS. See
# user_settings.h. Applications define MBEDTLS_THREADING_C too.
ifeq ($(MBEDTLS_THREADING_C),1)
CPPFLAGS += -DMBEDTLS_THREADING_C
LDFLAGS += -pthread
endif

# Changing MBEDTLS_THREADING_C rebuilds every object
OPTIONS_STAMP := $(BUILD_DIR)/options_$(if \
$(filter 1,$(MBEDTLS_THREADING_C)),threads,nothreads)

WOLFSSL_CPPFLAGS ?= -DWOLFSSL_USER_SETTINGS

CPPFLAGS += -I$(WOLFSSL_PATH) -I$(USER_SETTINGS_PATH) \
Expand Down Expand Up @@ -157,6 +168,13 @@ want-surface-test: $(LIBNAME) $(SHLIBNAME)
-lpthread -lm
$(BUILD_DIR)/psa_want_surface_test

$(OBJ) $(WOLFCRYPT_OBJ) $(OBJ_PIC) $(WOLFCRYPT_OBJ_PIC): $(OPTIONS_STAMP)

$(OPTIONS_STAMP):
@mkdir -p $(@D)
rm -f $(BUILD_DIR)/options_*
touch $@

$(OBJDIR)/%.o: src/%.c
@mkdir -p $(OBJDIR)
$(CC) $(CPPFLAGS) $(DEPFLAGS) $(CFLAGS) -c $< -o $@
Expand Down Expand Up @@ -233,7 +251,9 @@ UNIT_TESTS := psa_api_test \
psa_copy_key_cross_lifetime_test \
psa_zero_capacity_buffer_test \
psa_want_surface_test \
psa_tf_psa_extra_test
psa_tf_psa_extra_test \
psa_tf_psa_pk_test \
psa_tf_psa_threading_test

# Run the unit test loop from the repo root (psa_rsa_pss_interop_test reads
# its certificate relative to the root). Assumes the tests are already built.
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ wolfPSA is a PSA Crypto **engine** that implements the PSA Crypto API on
- wolfCrypt as the backend crypto provider.
- The default make target creates a static library (`libwolfpsa.a`) built directly
from wolfPSA sources plus the required wolfCrypt objects.
- TF-PSA-Crypto's `mbedtls/pk.h` for private keys: `mbedtls_pk_init`, `free`,
`parse_key`, `parse_keyfile` and `can_do_psa`. The context has the key's
DER and nothing else. RSA and EC keys, PEM or DER, traditional or PKCS #8.
Encrypted keys and EC keys with a compressed public key are not supported.

## Repository layout

Expand Down Expand Up @@ -60,6 +64,18 @@ AddressSanitizer build:
make ASAN=1
```

Thread-safe build, as Mbed TLS with `MBEDTLS_THREADING_C`: wolfCrypt without
`SINGLE_THREADED`, a mutex around the key store (`WOLFPSA_THREAD_SAFE`, see
`src/psa_lock.h`) and the TF-PSA-Crypto threading API in
`mbedtls/threading.h`, on pthreads:

```
make MBEDTLS_THREADING_C=1
```

Applications define `MBEDTLS_THREADING_C` too, as in their Mbed TLS
configuration, and link with `-pthread`.

You can override the wolfSSL source location:

```
Expand Down
269 changes: 269 additions & 0 deletions src/psa_tf_psa.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@

#if defined(WOLFSSL_PSA_ENGINE)

#include <limits.h>

#include <psa/crypto.h>
#include <mbedtls/pk.h>
#include <mbedtls/platform_util.h>
#include <mbedtls/threading.h>
#include <wolfssl/wolfcrypt/memory.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/rsa.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfpsa/psa_engine.h>

/* The generated crypto config must match the build's NO_FILESYSTEM */
Expand All @@ -51,4 +58,266 @@ void mbedtls_platform_zeroize(void *buf, size_t len)
}
}

void mbedtls_pk_init(mbedtls_pk_context* ctx)
{
if (ctx != NULL) {
XMEMSET(ctx, 0, sizeof(*ctx));
}
}

void mbedtls_pk_free(mbedtls_pk_context* ctx)
{
if (ctx != NULL) {
mbedtls_platform_zeroize(ctx->der, ctx->der_len);
XFREE(ctx->der, NULL, DYNAMIC_TYPE_PRIVATE_KEY);
XMEMSET(ctx, 0, sizeof(*ctx));
}
}

enum {
PK_TYPE_NONE = 0,
PK_TYPE_RSA = 1,
PK_TYPE_ECC = 2
};

/* MEMORY_E becomes PSA_ERROR_INSUFFICIENT_MEMORY, other errors become err */
static int PkRet(int wcRet, int err)
{
if (wcRet == 0) {
return 0;
}
return (wcRet == WC_NO_ERR_TRACE(MEMORY_E)) ?
PSA_ERROR_INSUFFICIENT_MEMORY : err;
}

/* An RSA or EC private key, traditional or PKCS #8. *type becomes PK_TYPE_*
* when it is not NULL. */
static int PkCheckKey(const byte* der, word32 len, int* type)
{
int ret = MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
int found = PK_TYPE_NONE;
word32 idx;
#ifndef NO_RSA
RsaKey* rsa;
#endif /* !NO_RSA */
#ifdef HAVE_ECC
ecc_key* ecc;
#endif /* HAVE_ECC */

#ifndef NO_RSA
rsa = wc_NewRsaKey(NULL, INVALID_DEVID, NULL);
if (rsa == NULL) {
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
idx = 0;
ret = PkRet(wc_RsaPrivateKeyDecode(der, &idx, rsa, len),
MBEDTLS_ERR_PK_KEY_INVALID_FORMAT);
if (ret == 0) {
found = PK_TYPE_RSA;
}
wc_DeleteRsaKey(rsa, &rsa);
#endif /* !NO_RSA */
#ifdef HAVE_ECC
if (ret == MBEDTLS_ERR_PK_KEY_INVALID_FORMAT) {
ecc = wc_ecc_key_new(NULL);
if (ecc == NULL) {
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
idx = 0;
ret = PkRet(wc_EccPrivateKeyDecode(der, &idx, ecc, len),
MBEDTLS_ERR_PK_KEY_INVALID_FORMAT);
if (ret == 0) {
found = PK_TYPE_ECC;
}
wc_ecc_key_free(ecc);
}
#endif /* HAVE_ECC */
if (type != NULL) {
*type = found;
}
(void)der;
(void)len;
(void)idx;
return ret;
}

/* PEM or DER. Encrypted keys are not supported, so pwd is not used. */
int mbedtls_pk_parse_key(mbedtls_pk_context* ctx, const unsigned char* key,
size_t keylen, const unsigned char* pwd, size_t pwdlen)
{
int ret;

(void)pwd;
(void)pwdlen;

if (ctx == NULL || ctx->der != NULL || key == NULL ||
keylen > (size_t)INT_MAX) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (keylen == 0) {
return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
}

/* Big enough for the DER of a PEM key */
ctx->der = (unsigned char*)XMALLOC(keylen, NULL, DYNAMIC_TYPE_PRIVATE_KEY);
if (ctx->der == NULL) {
return PSA_ERROR_INSUFFICIENT_MEMORY;
}
#ifdef WOLFSSL_PEM_TO_DER
ret = wc_KeyPemToDer(key, (int)keylen, ctx->der, (int)keylen, NULL);
#else
ret = WC_NO_ERR_TRACE(ASN_NO_PEM_HEADER);
#endif /* WOLFSSL_PEM_TO_DER */
if (ret > 0) {
ctx->der_len = (size_t)ret;
ret = 0;
}
else if (ret == WC_NO_ERR_TRACE(ASN_NO_PEM_HEADER)) {
XMEMCPY(ctx->der, key, keylen);
ctx->der_len = keylen;
ret = 0;
}
else {
ret = PkRet(ret, MBEDTLS_ERR_PK_KEY_INVALID_FORMAT);
}

if (ret == 0) {
ret = PkCheckKey(ctx->der, (word32)ctx->der_len, NULL);
}
if (ret != 0) {
mbedtls_pk_free(ctx);
}
return ret;
}

/* The key pair signs and verifies; an RSA key also encrypts and decrypts */
int mbedtls_pk_can_do_psa(const mbedtls_pk_context* pk, psa_algorithm_t alg,
psa_key_usage_t usage)
{
const int sign = (usage == PSA_KEY_USAGE_SIGN_HASH ||
usage == PSA_KEY_USAGE_VERIFY_HASH);
const int crypt = (usage == PSA_KEY_USAGE_ENCRYPT ||
usage == PSA_KEY_USAGE_DECRYPT);
int type = PK_TYPE_NONE;

if (pk == NULL || pk->der == NULL ||
PkCheckKey(pk->der, (word32)pk->der_len, &type) != 0) {
return 0;
}
if (type == PK_TYPE_RSA) {
return (sign && (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
PSA_ALG_IS_RSA_PSS(alg))) ||
(crypt && (alg == PSA_ALG_RSA_PKCS1V15_CRYPT ||
PSA_ALG_IS_RSA_OAEP(alg)));
}
return type == PK_TYPE_ECC && sign && PSA_ALG_IS_ECDSA(alg);
}

#if defined(MBEDTLS_FS_IO)
int mbedtls_pk_parse_keyfile(mbedtls_pk_context* ctx, const char* path,
const char* password)
{
byte* buf = NULL;
size_t len = 0;
int ret;

ret = PkRet(wc_FileLoad(path, &buf, &len, NULL),
MBEDTLS_ERR_PK_FILE_IO_ERROR);
if (ret == 0) {
ret = mbedtls_pk_parse_key(ctx, buf, len,
(const unsigned char*)password,
(password == NULL) ? 0 : XSTRLEN(password));
}
/* wc_FileLoad() can fail with buf allocated */
mbedtls_platform_zeroize(buf, len);
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
#endif /* MBEDTLS_FS_IO */

#if defined(MBEDTLS_THREADING_PTHREAD)
static int ThreadingRet(int posixRet)
{
return (posixRet == 0) ? 0 : MBEDTLS_ERR_THREADING_USAGE_ERROR;
}

void mbedtls_mutex_init(mbedtls_threading_mutex_t* mutex)
{
if (mutex != NULL) {
mutex->initialized =
(char)(pthread_mutex_init(&mutex->mutex, NULL) == 0);
}
}

/* Does nothing for a zeroed or freed mutex, as in TF-PSA-Crypto */
void mbedtls_mutex_free(mbedtls_threading_mutex_t* mutex)
{
if (mutex != NULL && mutex->initialized) {
(void)pthread_mutex_destroy(&mutex->mutex);
mutex->initialized = 0;
}
}

int mbedtls_mutex_lock(mbedtls_threading_mutex_t* mutex)
{
if (mutex == NULL || !mutex->initialized) {
return MBEDTLS_ERR_THREADING_USAGE_ERROR;
}
return ThreadingRet(pthread_mutex_lock(&mutex->mutex));
}

int mbedtls_mutex_unlock(mbedtls_threading_mutex_t* mutex)
{
if (mutex == NULL || !mutex->initialized) {
return MBEDTLS_ERR_THREADING_USAGE_ERROR;
}
return ThreadingRet(pthread_mutex_unlock(&mutex->mutex));
}

int mbedtls_condition_variable_init(
mbedtls_threading_condition_variable_t* cond)
{
if (cond == NULL) {
return MBEDTLS_ERR_THREADING_USAGE_ERROR;
}
return ThreadingRet(pthread_cond_init(&cond->cond, NULL));
}

void mbedtls_condition_variable_free(
mbedtls_threading_condition_variable_t* cond)
{
if (cond != NULL) {
(void)pthread_cond_destroy(&cond->cond);
}
}

int mbedtls_condition_variable_signal(
mbedtls_threading_condition_variable_t* cond)
{
if (cond == NULL) {
return MBEDTLS_ERR_THREADING_USAGE_ERROR;
}
return ThreadingRet(pthread_cond_signal(&cond->cond));
}

int mbedtls_condition_variable_broadcast(
mbedtls_threading_condition_variable_t* cond)
{
if (cond == NULL) {
return MBEDTLS_ERR_THREADING_USAGE_ERROR;
}
return ThreadingRet(pthread_cond_broadcast(&cond->cond));
}

int mbedtls_condition_variable_wait(
mbedtls_threading_condition_variable_t* cond,
mbedtls_threading_mutex_t* mutex)
{
if (cond == NULL || mutex == NULL || !mutex->initialized) {
return MBEDTLS_ERR_THREADING_USAGE_ERROR;
}
return ThreadingRet(pthread_cond_wait(&cond->cond, &mutex->mutex));
}
#endif /* MBEDTLS_THREADING_PTHREAD */

#endif /* WOLFSSL_PSA_ENGINE */
Loading
Loading