Skip to content

Commit

Permalink
EFR32: PSA crypto validations fixed. (#22259)
Browse files Browse the repository at this point in the history
  • Loading branch information
rcasallas-silabs authored Aug 31, 2022
1 parent 297397c commit f77eed5
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 19 deletions.
90 changes: 71 additions & 19 deletions src/platform/EFR32/CHIPCryptoPALPsaEfr32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,34 @@ static bool _isValidTagLength(size_t tag_length)
return false;
}

static bool _isValidKeyLength(size_t length)
{
// 16 bytes key for AES-CCM-128, 32 for AES-CCM-256
if (length == 16 || length == 32)
{
return true;
}
return false;
/**
* @brief Compare two times
*
* @param t1 First time to compare
* @param t2 Second time to compare
* @return int 0 If both times are idential to the second, -1 if t1 < t2, 1 if t1 > t2.
*/
static int timeCompare(mbedtls_x509_time * t1, mbedtls_x509_time * t2)
{
VerifyOrReturnValue(t1->year >= t2->year, -1);
VerifyOrReturnValue(t1->year <= t2->year, 1);
// Same year
VerifyOrReturnValue(t1->mon >= t2->mon, -1);
VerifyOrReturnValue(t1->mon <= t2->mon, 1);
// Same month
VerifyOrReturnValue(t1->day >= t2->day, -1);
VerifyOrReturnValue(t1->day <= t2->day, 1);
// Same day
VerifyOrReturnValue(t1->hour >= t2->hour, -1);
VerifyOrReturnValue(t1->hour <= t2->hour, 1);
// Same hour
VerifyOrReturnValue(t1->min >= t2->min, -1);
VerifyOrReturnValue(t1->min <= t2->min, 1);
// Same minute
VerifyOrReturnValue(t1->sec >= t2->sec, -1);
VerifyOrReturnValue(t1->sec <= t2->sec, 1);
// Same second
return 0;
}

CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length,
Expand All @@ -144,6 +164,15 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
uint8_t * buffer = nullptr;
bool allocated_buffer = false;

VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(CanCastTo<int>(nonce_length), error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);

// If the ciphertext and tag outputs aren't a contiguous buffer, the PSA API requires buffer copying
if (Uint8::to_uchar(ciphertext) + plaintext_length != Uint8::to_uchar(tag))
{
Expand All @@ -152,10 +181,6 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
VerifyOrExit(buffer != nullptr, error = CHIP_ERROR_NO_MEMORY);
}

// Superimpose tag and key length requirements. The other checks are done by the PSA implementation.
VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE);

psa_crypto_init();

psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
Expand Down Expand Up @@ -198,6 +223,13 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co
uint8_t * buffer = nullptr;
bool allocated_buffer = false;

VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(key_length == kAES_CCM128_Key_Length, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT);

// If the ciphertext and tag outputs aren't a contiguous buffer, the PSA API requires buffer copying
if (Uint8::to_const_uchar(ciphertext) + ciphertext_len != Uint8::to_const_uchar(tag))
{
Expand All @@ -206,10 +238,6 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co
VerifyOrExit(buffer != nullptr, error = CHIP_ERROR_NO_MEMORY);
}

// Superimpose tag and key length requirements. The other checks are done by the PSA implementation.
VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE);

psa_crypto_init();

psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
Expand Down Expand Up @@ -302,6 +330,7 @@ CHIP_ERROR Hash_SHA256_stream::Begin(void)
psa_crypto_init();

psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext);
*context = PSA_HASH_OPERATION_INIT;
const psa_status_t result = psa_hash_setup(context, PSA_ALG_SHA_256);

VerifyOrReturnError(result == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
Expand All @@ -326,6 +355,8 @@ CHIP_ERROR Hash_SHA256_stream::GetDigest(MutableByteSpan & out_buffer)
size_t output_length = 0;
psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext);

VerifyOrReturnError(out_buffer.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL);

// Clone the context first since calculating the digest finishes the operation
psa_hash_operation_t digest_context = PSA_HASH_OPERATION_INIT;
status = psa_hash_clone(context, &digest_context);
Expand All @@ -347,6 +378,7 @@ CHIP_ERROR Hash_SHA256_stream::Finish(MutableByteSpan & out_buffer)
psa_hash_operation_t * context = to_inner_hash_sha256_context(&mContext);
size_t output_length = 0;

VerifyOrReturnError(out_buffer.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL);
const psa_status_t status = psa_hash_finish(context, Uint8::to_uchar(out_buffer.data()), out_buffer.size(), &output_length);

VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
Expand Down Expand Up @@ -506,8 +538,8 @@ CHIP_ERROR PBKDF2_sha256::pbkdf2_sha256(const uint8_t * password, size_t plen, c
// U2 ends up in md1
//

status = psa_driver_wrapper_mac_compute(&attr, password, plen, PSA_ALG_HMAC(PSA_ALG_SHA_256), md1, sizeof(md1), md1,
sizeof(md1), &output_length);
status = psa_driver_wrapper_mac_compute(&attr, password, plen, PSA_ALG_HMAC(PSA_ALG_SHA_256), md1, sizeof(md1_buffer),
md1, sizeof(md1_buffer), &output_length);

VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(output_length == PSA_HASH_LENGTH(PSA_ALG_SHA_256), error = CHIP_ERROR_INTERNAL);
Expand Down Expand Up @@ -705,7 +737,7 @@ CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, co
status = psa_driver_wrapper_verify_hash(&attr, Uint8::to_const_uchar(*this), Length(), PSA_ALG_ECDSA(PSA_ALG_SHA_256), hash,
hash_length, signature.ConstBytes(), signature.Length());

VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INVALID_SIGNATURE);
exit:
_log_PSA_error(status);
psa_reset_key_attributes(&attr);
Expand Down Expand Up @@ -1275,8 +1307,12 @@ CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t root
CHIP_ERROR error = CHIP_NO_ERROR;
mbedtls_x509_crt certChain;
mbedtls_x509_crt rootCert;
mbedtls_x509_time leaf_valid_from;
mbedtls_x509_crt * cert = NULL;
int mbedResult;
uint32_t flags;
int compare_from = 0;
int compare_until = 0;

result = CertificateChainValidationResult::kInternalFrameworkError;

Expand All @@ -1293,6 +1329,7 @@ CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t root
/* Start of chain */
mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(leafCertificate), leafCertificateLen);
VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kLeafFormatInvalid, error = CHIP_ERROR_INTERNAL));
leaf_valid_from = certChain.valid_from;

/* Add the intermediate to the chain */
mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(caCertificate), caCertificateLen);
Expand All @@ -1302,6 +1339,21 @@ CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t root
mbedResult = mbedtls_x509_crt_parse(&rootCert, Uint8::to_const_uchar(rootCertificate), rootCertificateLen);
VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kRootFormatInvalid, error = CHIP_ERROR_INTERNAL));

/* Validates that intermediate and root certificates are valid at the time of the leaf certificate's start time. */
compare_from = timeCompare(&leaf_valid_from, &rootCert.valid_from);
compare_until = timeCompare(&leaf_valid_from, &rootCert.valid_to);
VerifyOrExit((compare_from >= 0) && (compare_until <= 0),
(result = CertificateChainValidationResult::kChainInvalid, error = CHIP_ERROR_CERT_NOT_TRUSTED));
cert = certChain.next;
while (cert)
{
compare_from = timeCompare(&leaf_valid_from, &cert->valid_from);
compare_until = timeCompare(&leaf_valid_from, &cert->valid_to);
VerifyOrExit((compare_from >= 0) && (compare_until <= 0),
(result = CertificateChainValidationResult::kChainInvalid, error = CHIP_ERROR_CERT_NOT_TRUSTED));
cert = cert->next;
}

/* Verify the chain against the root */
mbedResult = mbedtls_x509_crt_verify(&certChain, &rootCert, NULL, NULL, &flags, NULL, NULL);

Expand Down
2 changes: 2 additions & 0 deletions src/platform/EFR32/Efr32PsaOpaqueKeypair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ CHIP_ERROR EFR32OpaqueP256Keypair::ECDSA_sign_msg(const uint8_t * msg, size_t ms
CHIP_ERROR error = CHIP_NO_ERROR;
size_t output_length = 0;

VerifyOrExit((msg != nullptr) && (msg_length > 0), error = CHIP_ERROR_INVALID_ARGUMENT);

error = Sign(msg, msg_length, out_signature.Bytes(), out_signature.Capacity(), &output_length);

SuccessOrExit(error);
Expand Down

0 comments on commit f77eed5

Please sign in to comment.