/* * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #if !defined(CONFIG_BUILD_WITH_TFM) && defined(CONFIG_OPENTHREAD_CRYPTO_PSA) #include #endif #if defined(CONFIG_OPENTHREAD_ECDSA) #include #include #endif static otError psaToOtError(psa_status_t aStatus) { switch (aStatus) { case PSA_SUCCESS: return OT_ERROR_NONE; case PSA_ERROR_INVALID_ARGUMENT: return OT_ERROR_INVALID_ARGS; case PSA_ERROR_BUFFER_TOO_SMALL: return OT_ERROR_NO_BUFS; default: return OT_ERROR_FAILED; } } static psa_key_type_t toPsaKeyType(otCryptoKeyType aType) { switch (aType) { case OT_CRYPTO_KEY_TYPE_RAW: return PSA_KEY_TYPE_RAW_DATA; case OT_CRYPTO_KEY_TYPE_AES: return PSA_KEY_TYPE_AES; case OT_CRYPTO_KEY_TYPE_HMAC: return PSA_KEY_TYPE_HMAC; case OT_CRYPTO_KEY_TYPE_ECDSA: return PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1); default: return PSA_KEY_TYPE_NONE; } } static psa_algorithm_t toPsaAlgorithm(otCryptoKeyAlgorithm aAlgorithm) { switch (aAlgorithm) { case OT_CRYPTO_KEY_ALG_AES_ECB: return PSA_ALG_ECB_NO_PADDING; case OT_CRYPTO_KEY_ALG_HMAC_SHA_256: return PSA_ALG_HMAC(PSA_ALG_SHA_256); case OT_CRYPTO_KEY_ALG_ECDSA: return PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256); default: /* * There is currently no constant like PSA_ALG_NONE, but 0 is used * to indicate an unknown algorithm. */ return (psa_algorithm_t)0; } } static psa_key_usage_t toPsaKeyUsage(int aUsage) { psa_key_usage_t usage = 0; if (aUsage & OT_CRYPTO_KEY_USAGE_EXPORT) { usage |= PSA_KEY_USAGE_EXPORT; } if (aUsage & OT_CRYPTO_KEY_USAGE_ENCRYPT) { usage |= PSA_KEY_USAGE_ENCRYPT; } if (aUsage & OT_CRYPTO_KEY_USAGE_DECRYPT) { usage |= PSA_KEY_USAGE_DECRYPT; } if (aUsage & OT_CRYPTO_KEY_USAGE_SIGN_HASH) { usage |= PSA_KEY_USAGE_SIGN_HASH; } if (aUsage & OT_CRYPTO_KEY_USAGE_VERIFY_HASH) { usage |= PSA_KEY_USAGE_VERIFY_HASH; } return usage; } static bool checkKeyUsage(int aUsage) { /* Check if only supported flags have been passed */ int supported_flags = OT_CRYPTO_KEY_USAGE_EXPORT | OT_CRYPTO_KEY_USAGE_ENCRYPT | OT_CRYPTO_KEY_USAGE_DECRYPT | OT_CRYPTO_KEY_USAGE_SIGN_HASH | OT_CRYPTO_KEY_USAGE_VERIFY_HASH; return (aUsage & ~supported_flags) == 0; } static bool checkContext(otCryptoContext *aContext, size_t aMinSize) { /* Verify that the passed context is initialized and points to a big enough buffer */ return aContext != NULL && aContext->mContext != NULL && aContext->mContextSize >= aMinSize; } void otPlatCryptoInit(void) { psa_crypto_init(); #if !defined(CONFIG_BUILD_WITH_TFM) && defined(CONFIG_OPENTHREAD_CRYPTO_PSA) /* * In OpenThread, Settings are initialized after KeyManager by default. If device uses * PSA with emulated TFM, Settings have to be initialized at the end of otPlatCryptoInit(), * to be available before storing Network Key. */ __ASSERT_EVAL((void)settings_subsys_init(), int err = settings_subsys_init(), !err, "Failed to initialize settings"); #endif } otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType, otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage, otCryptoKeyStorage aKeyPersistence, const uint8_t *aKey, size_t aKeyLen) { #if defined(CONFIG_OPENTHREAD_ECDSA) int version; size_t len; unsigned char *p = (unsigned char *)aKey; unsigned char *end; #endif psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_status_t status = 0; if (aKeyRef == NULL || aKey == NULL || !checkKeyUsage(aKeyUsage)) { return OT_ERROR_INVALID_ARGS; } #if defined(CONFIG_OPENTHREAD_ECDSA) /* Check if key is ECDSA pair and extract private key from it since PSA expects it. */ if (aKeyType == OT_CRYPTO_KEY_TYPE_ECDSA) { end = p + aKeyLen; status = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (status != 0) { return OT_ERROR_FAILED; } end = p + len; status = mbedtls_asn1_get_int(&p, end, &version); if (status != 0) { return OT_ERROR_FAILED; } status = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); if (status != 0 || len != 32) { return OT_ERROR_FAILED; } aKey = p; aKeyLen = len; } #endif psa_set_key_type(&attributes, toPsaKeyType(aKeyType)); psa_set_key_algorithm(&attributes, toPsaAlgorithm(aKeyAlgorithm)); psa_set_key_usage_flags(&attributes, toPsaKeyUsage(aKeyUsage)); switch (aKeyPersistence) { case OT_CRYPTO_KEY_STORAGE_PERSISTENT: psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); psa_set_key_id(&attributes, *aKeyRef); break; case OT_CRYPTO_KEY_STORAGE_VOLATILE: psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); break; } status = psa_import_key(&attributes, aKey, aKeyLen, aKeyRef); psa_reset_key_attributes(&attributes); return psaToOtError(status); } otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef, uint8_t *aBuffer, size_t aBufferLen, size_t *aKeyLen) { if (aBuffer == NULL) { return OT_ERROR_INVALID_ARGS; } return psaToOtError(psa_export_key(aKeyRef, aBuffer, aBufferLen, aKeyLen)); } otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef) { return psaToOtError(psa_destroy_key(aKeyRef)); } bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef) { psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_status_t status; status = psa_get_key_attributes(aKeyRef, &attributes); psa_reset_key_attributes(&attributes); return status == PSA_SUCCESS; } otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext) { psa_mac_operation_t *operation; if (!checkContext(aContext, sizeof(psa_mac_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; *operation = psa_mac_operation_init(); return OT_ERROR_NONE; } otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext) { psa_mac_operation_t *operation; if (!checkContext(aContext, sizeof(psa_mac_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_mac_abort(operation)); } otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, const otCryptoKey *aKey) { psa_mac_operation_t *operation; psa_status_t status; if (aKey == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; status = psa_mac_sign_setup(operation, aKey->mKeyRef, PSA_ALG_HMAC(PSA_ALG_SHA_256)); return psaToOtError(status); } otError otPlatCryptoHmacSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength) { psa_mac_operation_t *operation; if (aBuf == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_mac_update(operation, (const uint8_t *)aBuf, aBufLength)); } otError otPlatCryptoHmacSha256Finish(otCryptoContext *aContext, uint8_t *aBuf, size_t aBufLength) { psa_mac_operation_t *operation; size_t mac_length; if (aBuf == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_mac_sign_finish(operation, aBuf, aBufLength, &mac_length)); } otError otPlatCryptoAesInit(otCryptoContext *aContext) { psa_key_id_t *key_ref; if (!checkContext(aContext, sizeof(psa_key_id_t))) { return OT_ERROR_INVALID_ARGS; } key_ref = aContext->mContext; *key_ref = (psa_key_id_t)0; /* In TF-M 1.5.0 this can be replaced with PSA_KEY_ID_NULL */ return OT_ERROR_NONE; } otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCryptoKey *aKey) { psa_key_id_t *key_ref; if (aKey == NULL || !checkContext(aContext, sizeof(psa_key_id_t))) { return OT_ERROR_INVALID_ARGS; } key_ref = aContext->mContext; *key_ref = aKey->mKeyRef; return OT_ERROR_NONE; } otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uint8_t *aInput, uint8_t *aOutput) { const size_t block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES); psa_status_t status = PSA_SUCCESS; psa_key_id_t *key_ref; size_t cipher_length; if (aInput == NULL || aOutput == NULL || !checkContext(aContext, sizeof(psa_key_id_t))) { return OT_ERROR_INVALID_ARGS; } key_ref = aContext->mContext; status = psa_cipher_encrypt(*key_ref, PSA_ALG_ECB_NO_PADDING, aInput, block_size, aOutput, block_size, &cipher_length); return psaToOtError(status); } otError otPlatCryptoAesFree(otCryptoContext *aContext) { return OT_ERROR_NONE; } otError otPlatCryptoSha256Init(otCryptoContext *aContext) { psa_hash_operation_t *operation; if (!checkContext(aContext, sizeof(psa_hash_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; *operation = psa_hash_operation_init(); return OT_ERROR_NONE; } otError otPlatCryptoSha256Deinit(otCryptoContext *aContext) { psa_hash_operation_t *operation; if (!checkContext(aContext, sizeof(psa_hash_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_hash_abort(operation)); } otError otPlatCryptoSha256Start(otCryptoContext *aContext) { psa_hash_operation_t *operation; if (!checkContext(aContext, sizeof(psa_hash_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_hash_setup(operation, PSA_ALG_SHA_256)); } otError otPlatCryptoSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength) { psa_hash_operation_t *operation; if (aBuf == NULL || !checkContext(aContext, sizeof(psa_hash_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_hash_update(operation, (const uint8_t *)aBuf, aBufLength)); } otError otPlatCryptoSha256Finish(otCryptoContext *aContext, uint8_t *aHash, uint16_t aHashSize) { psa_hash_operation_t *operation; size_t hash_size; if (aHash == NULL || !checkContext(aContext, sizeof(psa_hash_operation_t))) { return OT_ERROR_INVALID_ARGS; } operation = aContext->mContext; return psaToOtError(psa_hash_finish(operation, aHash, aHashSize, &hash_size)); } void otPlatCryptoRandomInit(void) { psa_crypto_init(); } void otPlatCryptoRandomDeinit(void) { } otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize) { return psaToOtError(psa_generate_random(aBuffer, aSize)); } #if defined(CONFIG_OPENTHREAD_ECDSA) otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair) { psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_id_t key_id = 0; psa_status_t status; size_t exported_length; psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT); psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attributes, 256); status = psa_generate_key(&attributes, &key_id); if (status != PSA_SUCCESS) { goto out; } status = psa_export_key(key_id, aKeyPair->mDerBytes, OT_CRYPTO_ECDSA_MAX_DER_SIZE, &exported_length); if (status != PSA_SUCCESS) { goto out; } aKeyPair->mDerLength = exported_length; out: psa_reset_key_attributes(&attributes); psa_destroy_key(key_id); return psaToOtError(status); } otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair, const otPlatCryptoSha256Hash *aHash, otPlatCryptoEcdsaSignature *aSignature) { psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_id_t key_id; psa_status_t status; size_t signature_length; psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH); psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attributes, 256); status = psa_import_key(&attributes, aKeyPair->mDerBytes, aKeyPair->mDerLength, &key_id); if (status != PSA_SUCCESS) { goto out; } status = psa_sign_hash(key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, OT_CRYPTO_ECDSA_SIGNATURE_SIZE, &signature_length); if (status != PSA_SUCCESS) { goto out; } out: psa_reset_key_attributes(&attributes); psa_destroy_key(key_id); return psaToOtError(status); } otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey, const otPlatCryptoSha256Hash *aHash, const otPlatCryptoEcdsaSignature *aSignature) { psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_id_t key_id; psa_status_t status; uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE]; psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH); psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attributes, 256); /* * `psa_import_key` expects a key format as specified by SEC1 §2.3.3 for the * uncompressed representation of the ECPoint. */ buffer[0] = 0x04; memcpy(buffer + 1, aPublicKey->m8, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE); status = psa_import_key(&attributes, buffer, sizeof(buffer), &key_id); if (status != PSA_SUCCESS) { goto out; } status = psa_verify_hash(key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, OT_CRYPTO_ECDSA_SIGNATURE_SIZE); if (status != PSA_SUCCESS) { goto out; } out: psa_reset_key_attributes(&attributes); psa_destroy_key(key_id); return psaToOtError(status); } otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef, const otPlatCryptoSha256Hash *aHash, otPlatCryptoEcdsaSignature *aSignature) { psa_status_t status; size_t signature_length; status = psa_sign_hash(aKeyRef, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, OT_CRYPTO_ECDSA_SIGNATURE_SIZE, &signature_length); if (status != PSA_SUCCESS) { goto out; } __ASSERT_NO_MSG(signature_length == OT_CRYPTO_ECDSA_SIGNATURE_SIZE); out: return psaToOtError(status); } otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKeyRef, const otPlatCryptoSha256Hash *aHash, const otPlatCryptoEcdsaSignature *aSignature) { psa_status_t status; status = psa_verify_hash(aKeyRef, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, OT_CRYPTO_ECDSA_SIGNATURE_SIZE); if (status != PSA_SUCCESS) { goto out; } out: return psaToOtError(status); } otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, otPlatCryptoEcdsaPublicKey *aPublicKey) { psa_status_t status; size_t exported_length; uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE]; status = psa_export_public_key(aKeyRef, buffer, sizeof(buffer), &exported_length); if (status != PSA_SUCCESS) { goto out; } __ASSERT_NO_MSG(exported_length == sizeof(buffer)); memcpy(aPublicKey->m8, buffer + 1, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE); out: return psaToOtError(status); } otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef) { psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_status_t status; psa_key_id_t key_id = (psa_key_id_t)aKeyRef; psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_SIGN_HASH); psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); psa_set_key_id(&attributes, key_id); psa_set_key_bits(&attributes, 256); status = psa_generate_key(&attributes, &key_id); if (status != PSA_SUCCESS) { goto out; } out: psa_reset_key_attributes(&attributes); return psaToOtError(status); } otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, uint16_t aPasswordLen, const uint8_t *aSalt, uint16_t aSaltLen, uint32_t aIterationCounter, uint16_t aKeyLen, uint8_t *aKey) { psa_status_t status = PSA_SUCCESS; psa_key_id_t key_id = PSA_KEY_ID_NULL; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_algorithm_t algorithm = PSA_ALG_PBKDF2_AES_CMAC_PRF_128; psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); psa_set_key_algorithm(&attributes, algorithm); psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(aPasswordLen)); status = psa_import_key(&attributes, aPassword, aPasswordLen, &key_id); if (status != PSA_SUCCESS) { goto out; } status = psa_key_derivation_setup(&operation, algorithm); if (status != PSA_SUCCESS) { goto out; } status = psa_key_derivation_input_integer(&operation, PSA_KEY_DERIVATION_INPUT_COST, aIterationCounter); if (status != PSA_SUCCESS) { goto out; } status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SALT, aSalt, aSaltLen); if (status != PSA_SUCCESS) { goto out; } status = psa_key_derivation_input_key(&operation, PSA_KEY_DERIVATION_INPUT_PASSWORD, key_id); if (status != PSA_SUCCESS) { goto out; } status = psa_key_derivation_output_bytes(&operation, aKey, aKeyLen); if (status != PSA_SUCCESS) { goto out; } out: psa_reset_key_attributes(&attributes); psa_key_derivation_abort(&operation); psa_destroy_key(key_id); __ASSERT_NO_MSG(status == PSA_SUCCESS); return psaToOtError(status); } #endif /* #if CONFIG_OPENTHREAD_ECDSA */