Merge pull request #16263 from leandrolanzieri/pr/sys/credman_add_pub_key_load

sys/credman: add key load functions
This commit is contained in:
Martine Lenders 2021-09-20 10:03:12 +02:00 committed by GitHub
commit dc22c7294c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 491 additions and 0 deletions

View File

@ -21,6 +21,7 @@ PSEUDOMODULES += cortexm_fpu
PSEUDOMODULES += cortexm_svc
PSEUDOMODULES += cpp
PSEUDOMODULES += cpu_check_address
PSEUDOMODULES += credman_load
PSEUDOMODULES += dbgpin
PSEUDOMODULES += devfs_%
PSEUDOMODULES += dhcpv6_%

View File

@ -699,6 +699,10 @@ ifneq (,$(filter sock_dtls, $(USEMODULE)))
USEMODULE += sock_udp
endif
ifneq (,$(filter credman_load, $(USEMODULE)))
USEPKG += tiny-asn1
endif
ifneq (,$(filter suit,$(USEMODULE)))
USEPKG += nanocbor
USEPKG += libcose

View File

@ -29,6 +29,7 @@
#include <unistd.h>
#include <stdint.h>
#include "kernel_defines.h"
#ifdef __cplusplus
extern "C" {
@ -45,6 +46,13 @@ extern "C" {
#ifndef CONFIG_CREDMAN_MAX_CREDENTIALS
#define CONFIG_CREDMAN_MAX_CREDENTIALS (2)
#endif
/**
* @brief Maximum number of ASN.1 objects when decoding keys.
*/
#ifndef CONFIG_CREDMAN_MAX_ASN1_OBJ
#define CONFIG_CREDMAN_MAX_ASN1_OBJ (8)
#endif
/** @} */
/**
@ -185,6 +193,76 @@ void credman_delete(credman_tag_t tag, credman_type_t type);
*/
int credman_get_used_count(void);
#if IS_USED(MODULE_CREDMAN_LOAD) || DOXYGEN
/**
* @brief Load a public key from a buffer, as a `SubjectPublicKeyInfo` sequence, according to
* RFC5280. The key should be encoded in DER format.
*
* @pre `buf != NULL && out != NULL`.
*
* @note To use this functionality include the module `credman_load`. Credman only supports ECDSA
* for now, so [RFC5480](https://tools.ietf.org/html/rfc5480) applies.
*
* @experimental This API is considered experimental and will probably change without notice!
*
* @see https://tools.ietf.org/html/rfc5280#section-4.1
*
* @param[in] buf Buffer holding the encoded public key
* @param[in] buf_len Length of @p buf
* @param[out] out ECDSA public key to populate
*
* @retval CREDMAN_OK on success
* @retval CREDMAN_INVALID if the key is not valid
*/
int credman_load_public_key(const void *buf, size_t buf_len, ecdsa_public_key_t *out);
/**
* @brief Load a private key from a buffer, as a `OneAsymmetricKey` sequence, according to RFC5958.
* This is compatible with the previous version PKCS#8 (defined in RFC5208). If the optional
* respective public key is present, it will be loaded as well. The key should be encoded in
* DER format.
*
* @pre `buf != NULL && cred != NULL`
*
* @note To use this functionality include the module `credman_load`. Credman only supports ECDSA
* for now.
*
* @experimental This API is considered experimental and will probably change without notice!
*
* @see https://tools.ietf.org/html/rfc5958#section-2
*
* @param[in] buf Buffer holding the encoded private key
* @param[in] buf_len Length of @p buf
* @param[out] cred Credential to populate
*
* @retval CREDMAN_OK on success
* @retval CREDMAN_INVALID if the key is not valid
*/
int credman_load_private_key(const void *buf, size_t buf_len, credman_credential_t *cred);
/**
* @brief Load an ECC private key from a buffer, as an `ECPrivateKey` sequence, according to RFC5915.
* If the optional respective public key is present, it will be loaded as well. The key
* should be encoded in DER format.
*
* @pre `buf != NULL && cred != NULL`
*
* @note To use this functionality include the module `credman_load`.
*
* @experimental This API is considered experimental and will probably change without notice!
*
* @see https://tools.ietf.org/html/rfc5915#section-3
*
* @param[in] buf Buffer holding the encoded private key
* @param[in] buf_len Length of @p buf
* @param[out] cred Credential to populate
*
* @retval CREDMAN_OK on success
* @retval CREDMAN_INVALID if the key is not valid
*/
int credman_load_private_ecc_key(const void *buf, size_t buf_len, credman_credential_t *cred);
#endif /* MODULE_CREDMAN_LOAD || DOXYGEN */
#ifdef TEST_SUITES
/**
* @brief Empties the credential pool

View File

@ -20,4 +20,9 @@ config CREDMAN_MAX_CREDENTIALS
Configure 'CONFIG_CREDMAN_MAX_CREDENTIALS', the maximum number of
allowed credentials in the credential pool.
config CREDMAN_MAX_ASN1_OBJ
int "Maximum number of ASN.1 objects when decoding keys"
default 8
depends on USEMODULE_CREDMAN_LOAD
endif # KCONFIG_USEMODULE_CREDMAN

View File

@ -18,15 +18,31 @@
#include "net/credman.h"
#include "mutex.h"
#include "kernel_defines.h"
#include <assert.h>
#include <string.h>
#include <inttypes.h>
#define ENABLE_DEBUG 0
#include "debug.h"
static mutex_t _mutex = MUTEX_INIT;
#if IS_USED(MODULE_CREDMAN_LOAD)
#include "tiny-asn1.h"
/* Context-specific tag in DER encoding
* (see section 8.1.2.2 of ITU-T X.690 https://www.itu.int/rec/T-REC-X.690-200811-S) */
#define ASN1_CONTEXT_TAG(v) (0xA0 | (v & 0x1F))
/* ASN.1 representation of ecPublicKey - OID 1.2.840.10045.2.1
* (see https://oidref.com/1.2.840.10045.2.1) */
static const uint8_t ecPublicKey[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 };
static int _parse_ecc_point(const asn1_tree *key, const void **x, const void **y);
#endif /* MODULE_CREDMAN_LOAD */
static credman_credential_t credentials[CONFIG_CREDMAN_MAX_CREDENTIALS];
static unsigned used = 0;
@ -90,6 +106,269 @@ end:
return ret;
}
#if IS_USED(MODULE_CREDMAN_LOAD)
int credman_load_public_key(const void *buf, size_t buf_len, ecdsa_public_key_t *out)
{
asn1_tree objects[CONFIG_CREDMAN_MAX_ASN1_OBJ];
asn1_tree pub_key;
assert(buf);
assert(out);
int obj_count = der_object_count((uint8_t *)buf, buf_len);
if (obj_count <= 0) {
DEBUG("credman: could not calculate the number of elements within the key\n");
return CREDMAN_INVALID;
}
if (obj_count > CONFIG_CREDMAN_MAX_ASN1_OBJ) {
DEBUG("credman: not enough ASN.1 objects to decode key.\n");
DEBUG("credman: current max is %d, and we need %d\n",
CONFIG_CREDMAN_MAX_ASN1_OBJ, obj_count);
return CREDMAN_INVALID;
}
int32_t res = der_decode((uint8_t *)buf, buf_len, &pub_key, objects, obj_count);
if (res < 0) {
DEBUG("credman: could not parse the key (%" PRId32 ")\n", res);
return CREDMAN_INVALID;
}
/*
* From https://tools.ietf.org/html/rfc5280#section-4.1
*
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING }
*
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
*/
/* the outer container is a 'SubjectPublicKeyInfo', which should be a SEQUENCE */
if (pub_key.type != ASN1_TYPE_SEQUENCE) {
DEBUG("credman: the public key information should be contained in an ASN.1 SEQUENCE\n");
return CREDMAN_INVALID;
}
asn1_tree *algorithm_id = pub_key.child;
asn1_tree *algorithm = algorithm_id->child;
/* for now only ECDSA is supported by credman */
/* the algorithm should be Elliptic Curve Public Key (OID 1.2.840.10045.2.1) */
if (sizeof(ecPublicKey) != algorithm->length) {
DEBUG("credman: wrong OID length for algorithm\n");
return CREDMAN_INVALID;
}
if (memcmp(ecPublicKey, algorithm->data, sizeof(ecPublicKey)) != 0) {
DEBUG("credman: wrong OID for algorithm. Expected 1.2.840.10045.2.1\n");
return CREDMAN_INVALID;
}
return _parse_ecc_point(algorithm_id->next, &out->x, &out->y);
}
int credman_load_private_key(const void *buf, size_t buf_len, credman_credential_t *cred)
{
asn1_tree objects[CONFIG_CREDMAN_MAX_ASN1_OBJ];
asn1_tree priv_key;
assert(buf);
assert(cred);
int obj_count = der_object_count((uint8_t *)buf, buf_len);
if (obj_count <= 0) {
DEBUG("credman: could not calculate the number of elements within the key\n");
return CREDMAN_INVALID;
}
if (obj_count > CONFIG_CREDMAN_MAX_ASN1_OBJ) {
DEBUG("credman: not enough ASN.1 objects to decode key.\n");
DEBUG("credman: current max is %d, and we need %d\n",
CONFIG_CREDMAN_MAX_ASN1_OBJ, obj_count);
return CREDMAN_INVALID;
}
if (der_decode((uint8_t *)buf, buf_len, &priv_key, objects, obj_count) < 0) {
DEBUG("credman: could not parse the key\n");
return CREDMAN_INVALID;
}
/*
* From https://tools.ietf.org/html/rfc5958#section-2
*
* OneAsymmetricKey ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
* privateKey PrivateKey,
* attributes [0] Attributes OPTIONAL,
* ...,
* [[2: publicKey [1] PublicKey OPTIONAL ]],
* ...
* }
*
* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
* { PUBLIC-KEY,
* { PrivateKeyAlgorithms } }
*
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL }
*
* PrivateKey ::= OCTET STRING
* -- Content varies based on type of key. The
* -- algorithm identifier dictates the format of
* -- the key.
*/
/* the outer container is a 'OneAsymmetricKey', which should be a SEQUENCE */
if (priv_key.type != ASN1_TYPE_SEQUENCE) {
DEBUG("credman: the private key information should be contained in an ASN.1 SEQUENCE\n");
return CREDMAN_INVALID;
}
/* point to version */
asn1_tree *node = priv_key.child;
if (!node || node->type != ASN1_TYPE_INTEGER) {
DEBUG("credman: invalid private key version\n");
return CREDMAN_INVALID;
}
/* point to privateKeyAlgorithm */
node = node->next;
if (!node || node->type != ASN1_TYPE_SEQUENCE || !node->length) {
DEBUG("credman: invalid private key algorithm identifier\n");
return CREDMAN_INVALID;
}
/* for now only ECDSA is supported by credman */
/* the algorithm should be Elliptic Curve Public Key (OID 1.2.840.10045.2.1) */
asn1_tree *algorithm = node->child;
if (sizeof(ecPublicKey) != algorithm->length) {
DEBUG("credman: wrong private key algorithm, only ecPublicKey is supported\n");
return CREDMAN_INVALID;
}
if (memcmp(ecPublicKey, algorithm->data, sizeof(ecPublicKey)) != 0) {
DEBUG("credman: wrong OID for algorithm. Expected 1.2.840.10045.2.1\n");
return CREDMAN_INVALID;
}
/* point to privateKey */
node = node->next;
if (!node || node->type != ASN1_TYPE_OCTET_STRING || !node->data || !node->length) {
DEBUG("credman: no private key found\n");
return CREDMAN_INVALID;
}
return credman_load_private_ecc_key(node->data, node->length, cred);
}
int credman_load_private_ecc_key(const void *buf, size_t buf_len, credman_credential_t *cred)
{
asn1_tree objects[CONFIG_CREDMAN_MAX_ASN1_OBJ];
asn1_tree priv_key;
assert(buf);
assert(cred);
int obj_count = der_object_count((uint8_t *)buf, buf_len);
if (obj_count <= 0) {
DEBUG("credman: could not calculate the number of elements within the key\n");
return CREDMAN_INVALID;
}
if (obj_count > CONFIG_CREDMAN_MAX_ASN1_OBJ) {
DEBUG("credman: not enough ASN.1 objects to decode key.\n");
DEBUG("credman: current max is %d, and we need %d\n",
CONFIG_CREDMAN_MAX_ASN1_OBJ, obj_count);
return CREDMAN_INVALID;
}
if (der_decode((uint8_t *)buf, buf_len, &priv_key, objects, obj_count) < 0) {
DEBUG("credman: could not parse the key\n");
return CREDMAN_INVALID;
}
/*
* From https://tools.ietf.org/html/rfc5915#section-3
*
* ECPrivateKey ::= SEQUENCE {
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
* privateKey OCTET STRING,
* parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
* publicKey [1] BIT STRING OPTIONAL
* }
*/
/* point to version, it SHALL be 1 */
asn1_tree *node = priv_key.child;
if (!node || node->type != ASN1_TYPE_INTEGER || node->data[0] != 0x01) {
DEBUG("credman: invalid private key version\n");
return CREDMAN_INVALID;
}
/* point to privateKey */
node = node->next;
if (!node || node->type != ASN1_TYPE_OCTET_STRING || !node->data || !node->length) {
DEBUG("credman: invalid private key\n");
return CREDMAN_INVALID;
}
cred->type = CREDMAN_TYPE_ECDSA;
cred->params.ecdsa.public_key.x = NULL;
cred->params.ecdsa.public_key.y = NULL;
cred->params.ecdsa.private_key = node->data;
/* try to find a publicKey by tag */
while (node && node->type != ASN1_CONTEXT_TAG(1)) {
node = node->next;
}
if (!node) {
return CREDMAN_OK;
}
return _parse_ecc_point(node->child, &cred->params.ecdsa.public_key.x,
&cred->params.ecdsa.public_key.y);
}
static int _parse_ecc_point(const asn1_tree *key, const void **x, const void **y)
{
if (!key || key->type != ASN1_TYPE_BIT_STRING) {
DEBUG("credman: the key should be an ASN.1 BIT STRING\n");
return CREDMAN_INVALID;
}
if (!key->length) {
DEBUG("credman: the key is missing\n");
return CREDMAN_INVALID;
}
/* SEC 1: Elliptic Curve Cryptography - Section 2.3.4 (https://www.secg.org/sec1-v2.pdf) */
/* check for uncompressed key format */
if (key->data[1] != 0x04) {
DEBUG("credman: only uncompressed format is supported\n");
return CREDMAN_INVALID;
}
size_t coords_len = (key->length - 2) / 2;
uint8_t *_x = &key->data[2]; /* skip format specifier and unused bits */
uint8_t *_y = &_x[coords_len];
*x = _x;
*y = _y;
return CREDMAN_OK;
}
#endif /* MODULE_CREDMAN_LOAD */
int credman_get(credman_credential_t *credential, credman_tag_t tag,
credman_type_t type)
{

View File

@ -1 +1,2 @@
USEMODULE += credman
USEMODULE += credman_load

View File

@ -15,6 +15,86 @@
#define CREDMAN_TEST_TAG (1)
/* $ openssl ecparam -name secp256r1 -genkey -outform der -out key.der */
static const uint8_t key_pair_der[] = {
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x04,
0x43, 0x81, 0x68, 0xa8, 0x63, 0x9e, 0xf7, 0xe3,
0x0d, 0x40, 0x5c, 0xf1, 0xea, 0xc9, 0x90, 0xf6,
0x5f, 0x63, 0x86, 0x29, 0xca, 0x20, 0x4f, 0x9f,
0x34, 0xb1, 0x9d, 0xa5, 0x17, 0x7b, 0x92, 0xa0,
0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00,
0x04, 0x95, 0x38, 0x6c, 0x0e, 0x45, 0xd3, 0x15,
0x22, 0xad, 0x29, 0xd5, 0x24, 0xd8, 0x6b, 0x29,
0xf7, 0x5c, 0x7b, 0x71, 0x89, 0xac, 0xa2, 0x86,
0xb5, 0x9b, 0xdc, 0x00, 0x28, 0xa4, 0xae, 0xd8,
0x9f, 0x8b, 0xe8, 0x63, 0xab, 0xd2, 0x30, 0x3c,
0x06, 0xcd, 0xec, 0x8a, 0xf7, 0xb9, 0xf8, 0xb4,
0x46, 0xca, 0x01, 0x55, 0x03, 0xe1, 0xbe, 0x99,
0xf7, 0x13, 0x13, 0x78, 0xbd, 0x40, 0xf9, 0xd4,
0x9c
};
/* $ openssl pkcs8 -topk8 -inform DER -outform DER -in key.der -out key_pkcs8.der -nocrypt */
static const uint8_t key_pair_pkcs8_der[] = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13,
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02,
0x01, 0x01, 0x04, 0x20, 0x04, 0x43, 0x81, 0x68,
0xa8, 0x63, 0x9e, 0xf7, 0xe3, 0x0d, 0x40, 0x5c,
0xf1, 0xea, 0xc9, 0x90, 0xf6, 0x5f, 0x63, 0x86,
0x29, 0xca, 0x20, 0x4f, 0x9f, 0x34, 0xb1, 0x9d,
0xa5, 0x17, 0x7b, 0x92, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x95, 0x38, 0x6c, 0x0e, 0x45, 0xd3,
0x15, 0x22, 0xad, 0x29, 0xd5, 0x24, 0xd8, 0x6b,
0x29, 0xf7, 0x5c, 0x7b, 0x71, 0x89, 0xac, 0xa2,
0x86, 0xb5, 0x9b, 0xdc, 0x00, 0x28, 0xa4, 0xae,
0xd8, 0x9f, 0x8b, 0xe8, 0x63, 0xab, 0xd2, 0x30,
0x3c, 0x06, 0xcd, 0xec, 0x8a, 0xf7, 0xb9, 0xf8,
0xb4, 0x46, 0xca, 0x01, 0x55, 0x03, 0xe1, 0xbe,
0x99, 0xf7, 0x13, 0x13, 0x78, 0xbd, 0x40, 0xf9,
0xd4, 0x9c
};
/* $ openssl ec -in key.der -inform DER -pubout -outform DER -out pub.der */
static const uint8_t public_key_der[] = {
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
0x42, 0x00, 0x04, 0x95, 0x38, 0x6c, 0x0e, 0x45,
0xd3, 0x15, 0x22, 0xad, 0x29, 0xd5, 0x24, 0xd8,
0x6b, 0x29, 0xf7, 0x5c, 0x7b, 0x71, 0x89, 0xac,
0xa2, 0x86, 0xb5, 0x9b, 0xdc, 0x00, 0x28, 0xa4,
0xae, 0xd8, 0x9f, 0x8b, 0xe8, 0x63, 0xab, 0xd2,
0x30, 0x3c, 0x06, 0xcd, 0xec, 0x8a, 0xf7, 0xb9,
0xf8, 0xb4, 0x46, 0xca, 0x01, 0x55, 0x03, 0xe1,
0xbe, 0x99, 0xf7, 0x13, 0x13, 0x78, 0xbd, 0x40,
0xf9, 0xd4, 0x9c
};
/* $ openssl ec -in key.der -inform DER -text */
static const uint8_t private_key[] = {
0x04, 0x43, 0x81, 0x68, 0xa8, 0x63, 0x9e, 0xf7,
0xe3, 0x0d, 0x40, 0x5c, 0xf1, 0xea, 0xc9, 0x90,
0xf6, 0x5f, 0x63, 0x86, 0x29, 0xca, 0x20, 0x4f,
0x9f, 0x34, 0xb1, 0x9d, 0xa5, 0x17, 0x7b, 0x92
};
static const uint8_t public_key_x[] = {
0x95, 0x38, 0x6c, 0x0e, 0x45, 0xd3, 0x15, 0x22,
0xad, 0x29, 0xd5, 0x24, 0xd8, 0x6b, 0x29, 0xf7,
0x5c, 0x7b, 0x71, 0x89, 0xac, 0xa2, 0x86, 0xb5,
0x9b, 0xdc, 0x00, 0x28, 0xa4, 0xae, 0xd8, 0x9f
};
static const uint8_t public_key_y[] = {
0x8b, 0xe8, 0x63, 0xab, 0xd2, 0x30, 0x3c, 0x06,
0xcd, 0xec, 0x8a, 0xf7, 0xb9, 0xf8, 0xb4, 0x46,
0xca, 0x01, 0x55, 0x03, 0xe1, 0xbe, 0x99, 0xf7,
0x13, 0x13, 0x78, 0xbd, 0x40, 0xf9, 0xd4, 0x9c
};
static int _compare_credentials(const credman_credential_t *a,
const credman_credential_t *b)
{
@ -228,6 +308,46 @@ static void test_credman_add_delete_all(void)
TEST_ASSERT_EQUAL_INT(2, credman_get_used_count());
}
static void test_credman_load_public_key_from_buffer(void)
{
ecdsa_public_key_t pub;
int res = credman_load_public_key(public_key_der, sizeof(public_key_der), &pub);
TEST_ASSERT_EQUAL_INT(CREDMAN_OK, res);
TEST_ASSERT_EQUAL_INT(memcmp(public_key_x, pub.x, sizeof(public_key_x)), 0);
TEST_ASSERT_EQUAL_INT(memcmp(public_key_y, pub.y, sizeof(public_key_y)), 0);
}
static void test_credman_load_private_key_from_buffer(void)
{
credman_credential_t cred;
int res = credman_load_private_key(key_pair_pkcs8_der, sizeof(key_pair_pkcs8_der), &cred);
TEST_ASSERT_EQUAL_INT(CREDMAN_OK, res);
TEST_ASSERT_EQUAL_INT(memcmp(private_key, cred.params.ecdsa.private_key, sizeof(private_key)),
0);
TEST_ASSERT_EQUAL_INT(memcmp(public_key_x, cred.params.ecdsa.public_key.x,
sizeof(public_key_x)), 0);
TEST_ASSERT_EQUAL_INT(memcmp(public_key_y, cred.params.ecdsa.public_key.y,
sizeof(public_key_y)), 0);
TEST_ASSERT_EQUAL_INT(cred.type, CREDMAN_TYPE_ECDSA);
}
static void test_credman_load_private_ecc_key_from_buffer(void)
{
credman_credential_t cred;
int res = credman_load_private_ecc_key(key_pair_der, sizeof(key_pair_der), &cred);
TEST_ASSERT_EQUAL_INT(CREDMAN_OK, res);
TEST_ASSERT_EQUAL_INT(memcmp(private_key, cred.params.ecdsa.private_key, sizeof(private_key)),
0);
TEST_ASSERT_EQUAL_INT(memcmp(public_key_x, cred.params.ecdsa.public_key.x,
sizeof(public_key_x)), 0);
TEST_ASSERT_EQUAL_INT(memcmp(public_key_y, cred.params.ecdsa.public_key.y,
sizeof(public_key_y)), 0);
TEST_ASSERT_EQUAL_INT(cred.type, CREDMAN_TYPE_ECDSA);
}
Test *tests_credman_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
@ -236,6 +356,9 @@ Test *tests_credman_tests(void)
new_TestFixture(test_credman_delete),
new_TestFixture(test_credman_delete_random_order),
new_TestFixture(test_credman_add_delete_all),
new_TestFixture(test_credman_load_public_key_from_buffer),
new_TestFixture(test_credman_load_private_key_from_buffer),
new_TestFixture(test_credman_load_private_ecc_key_from_buffer),
};
EMB_UNIT_TESTCALLER(credman_tests,