Skip to content

Commit 3270557

Browse files
vijspull[bot]
authored andcommitted
Added time validity checking to Device Attestation Verifier (#12212)
* Added time validity checking to Device Attestation Verifier * Added new methods to validate a certificate and implemented review comments * Renamed IsCertificateValid to IsCertificateValidAtCurrentTime * Removed "invalid issuing timestamp" test * Disabled cert validation test in certain platforms
1 parent 3e31017 commit 3270557

9 files changed

+329
-24
lines changed

src/credentials/BUILD.gn

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import("//build_overrides/chip.gni")
1616
import("//build_overrides/nlassert.gni")
1717
import("${chip_root}/src/crypto/crypto.gni")
18+
import("${chip_root}/src/platform/device.gni")
1819

1920
static_library("credentials") {
2021
output_name = "libCredentials"
@@ -48,6 +49,10 @@ static_library("credentials") {
4849
sources += [ "${chip_root}/examples/platform/nxp/se05x/DeviceAttestationSe05xCredsExample.cpp" ]
4950
}
5051

52+
if (chip_device_platform == "esp32" || chip_device_platform == "nrfconnect") {
53+
defines = [ "CURRENT_TIME_NOT_IMPLEMENTED=1" ]
54+
}
55+
5156
cflags = [ "-Wconversion" ]
5257

5358
public_deps = [

src/credentials/DeviceAttestationVerifier.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ class UnimplementedDACVerifier : public DeviceAttestationVerifier
3333
AttestationVerificationResult VerifyAttestationInformation(const ByteSpan & attestationInfoBuffer,
3434
const ByteSpan & attestationChallengeBuffer,
3535
const ByteSpan & attestationSignatureBuffer,
36-
const ByteSpan & paiCertDerBuffer, const ByteSpan & dacCertDerBuffer,
36+
const ByteSpan & paiDerBuffer, const ByteSpan & dacDerBuffer,
3737
const ByteSpan & attestationNonce) override
3838
{
3939
(void) attestationInfoBuffer;
4040
(void) attestationChallengeBuffer;
4141
(void) attestationSignatureBuffer;
42-
(void) paiCertDerBuffer;
43-
(void) dacCertDerBuffer;
42+
(void) paiDerBuffer;
43+
(void) dacDerBuffer;
4444
(void) attestationNonce;
4545
return AttestationVerificationResult::kNotImplemented;
4646
}

src/credentials/DeviceAttestationVerifier.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -196,18 +196,19 @@ class DeviceAttestationVerifier
196196
* @param[in] attestationInfoBuffer Buffer containing attestation information portion of Attestation Response (raw TLV)
197197
* @param[in] attestationChallengeBuffer Buffer containing the attestation challenge from the secure session
198198
* @param[in] attestationSignatureBuffer Buffer the signature portion of Attestation Response
199-
* @param[in] paiCertDerBuffer Buffer containing the PAI certificate from device in DER format.
199+
* @param[in] paiDerBuffer Buffer containing the PAI certificate from device in DER format.
200200
* If length zero, there was no PAI certificate.
201-
* @param[in] dacCertDerBuffer Buffer containing the DAC certificate from device in DER format.
201+
* @param[in] dacDerBuffer Buffer containing the DAC certificate from device in DER format.
202202
* @param[in] attestationNonce Buffer containing attestation nonce.
203203
*
204204
* @returns AttestationVerificationResult::kSuccess on success or another specific
205205
* value from AttestationVerificationResult enum on failure.
206206
*/
207-
virtual AttestationVerificationResult
208-
VerifyAttestationInformation(const ByteSpan & attestationInfoBuffer, const ByteSpan & attestationChallengeBuffer,
209-
const ByteSpan & attestationSignatureBuffer, const ByteSpan & paiCertDerBuffer,
210-
const ByteSpan & dacCertDerBuffer, const ByteSpan & attestationNonce) = 0;
207+
virtual AttestationVerificationResult VerifyAttestationInformation(const ByteSpan & attestationInfoBuffer,
208+
const ByteSpan & attestationChallengeBuffer,
209+
const ByteSpan & attestationSignatureBuffer,
210+
const ByteSpan & paiDerBuffer, const ByteSpan & dacDerBuffer,
211+
const ByteSpan & attestationNonce) = 0;
211212

212213
/**
213214
* @brief Verify a CMS Signed Data signature against the CSA certificate of Subject Key Identifier that matches

src/credentials/examples/DefaultDeviceAttestationVerifier.cpp

+25-15
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class DefaultDACVerifier : public DeviceAttestationVerifier
180180
AttestationVerificationResult VerifyAttestationInformation(const ByteSpan & attestationInfoBuffer,
181181
const ByteSpan & attestationChallengeBuffer,
182182
const ByteSpan & attestationSignatureBuffer,
183-
const ByteSpan & paiCertDerBuffer, const ByteSpan & dacCertDerBuffer,
183+
const ByteSpan & paiDerBuffer, const ByteSpan & dacDerBuffer,
184184
const ByteSpan & attestationNonce) override;
185185

186186
AttestationVerificationResult ValidateCertificationDeclarationSignature(const ByteSpan & cmsEnvelopeBuffer,
@@ -199,12 +199,12 @@ class DefaultDACVerifier : public DeviceAttestationVerifier
199199
AttestationVerificationResult DefaultDACVerifier::VerifyAttestationInformation(const ByteSpan & attestationInfoBuffer,
200200
const ByteSpan & attestationChallengeBuffer,
201201
const ByteSpan & attestationSignatureBuffer,
202-
const ByteSpan & paiCertDerBuffer,
203-
const ByteSpan & dacCertDerBuffer,
202+
const ByteSpan & paiDerBuffer,
203+
const ByteSpan & dacDerBuffer,
204204
const ByteSpan & attestationNonce)
205205
{
206206
VerifyOrReturnError(!attestationInfoBuffer.empty() && !attestationChallengeBuffer.empty() &&
207-
!attestationSignatureBuffer.empty() && !paiCertDerBuffer.empty() && !dacCertDerBuffer.empty() &&
207+
!attestationSignatureBuffer.empty() && !paiDerBuffer.empty() && !dacDerBuffer.empty() &&
208208
!attestationNonce.empty(),
209209
AttestationVerificationResult::kInvalidArgument);
210210

@@ -214,17 +214,17 @@ AttestationVerificationResult DefaultDACVerifier::VerifyAttestationInformation(c
214214
uint16_t paiVid = VendorId::NotSpecified;
215215
uint16_t dacVid = VendorId::NotSpecified;
216216

217-
VerifyOrReturnError(ExtractDNAttributeFromX509Cert(MatterOid::kVendorId, paiCertDerBuffer, paiVid) == CHIP_NO_ERROR,
217+
VerifyOrReturnError(ExtractDNAttributeFromX509Cert(MatterOid::kVendorId, paiDerBuffer, paiVid) == CHIP_NO_ERROR,
218218
AttestationVerificationResult::kPaiFormatInvalid);
219-
VerifyOrReturnError(ExtractDNAttributeFromX509Cert(MatterOid::kVendorId, dacCertDerBuffer, dacVid) == CHIP_NO_ERROR,
219+
VerifyOrReturnError(ExtractDNAttributeFromX509Cert(MatterOid::kVendorId, dacDerBuffer, dacVid) == CHIP_NO_ERROR,
220220
AttestationVerificationResult::kDacFormatInvalid);
221221

222222
VerifyOrReturnError(paiVid == dacVid, AttestationVerificationResult::kDacVendorIdMismatch);
223223
dacVendorId = static_cast<VendorId>(dacVid);
224224
}
225225

226226
P256PublicKey remoteManufacturerPubkey;
227-
VerifyOrReturnError(ExtractPubkeyFromX509Cert(dacCertDerBuffer, remoteManufacturerPubkey) == CHIP_NO_ERROR,
227+
VerifyOrReturnError(ExtractPubkeyFromX509Cert(dacDerBuffer, remoteManufacturerPubkey) == CHIP_NO_ERROR,
228228
AttestationVerificationResult::kDacFormatInvalid);
229229

230230
// Validate overall attestation signature on attestation information
@@ -239,23 +239,33 @@ AttestationVerificationResult DefaultDACVerifier::VerifyAttestationInformation(c
239239

240240
uint8_t akidBuf[Crypto::kAuthorityKeyIdentifierLength];
241241
MutableByteSpan akid(akidBuf);
242-
ExtractAKIDFromX509Cert(paiCertDerBuffer, akid);
242+
ExtractAKIDFromX509Cert(paiDerBuffer, akid);
243243

244244
constexpr size_t paaCertAllocatedLen = kMaxDERCertLength;
245245
chip::Platform::ScopedMemoryBuffer<uint8_t> paaCert;
246246
VerifyOrReturnError(paaCert.Alloc(paaCertAllocatedLen), AttestationVerificationResult::kNoMemory);
247-
MutableByteSpan paa(paaCert.Get(), paaCertAllocatedLen);
248-
VerifyOrReturnError(mAttestationTrustStore->GetProductAttestationAuthorityCert(akid, paa) == CHIP_NO_ERROR,
247+
MutableByteSpan paaDerBuffer(paaCert.Get(), paaCertAllocatedLen);
248+
VerifyOrReturnError(mAttestationTrustStore->GetProductAttestationAuthorityCert(akid, paaDerBuffer) == CHIP_NO_ERROR,
249249
AttestationVerificationResult::kPaaNotFound);
250250

251-
VerifyOrReturnError(ValidateCertificateChain(paa.data(), paa.size(), paiCertDerBuffer.data(), paiCertDerBuffer.size(),
252-
dacCertDerBuffer.data(), dacCertDerBuffer.size()) == CHIP_NO_ERROR,
251+
#if !defined(CURRENT_TIME_NOT_IMPLEMENTED)
252+
VerifyOrReturnError(IsCertificateValidAtCurrentTime(dacDerBuffer) == CHIP_NO_ERROR, AttestationVerificationResult::kDacExpired);
253+
#endif
254+
255+
VerifyOrReturnError(IsCertificateValidAtIssuance(dacDerBuffer, paiDerBuffer) == CHIP_NO_ERROR,
256+
AttestationVerificationResult::kPaiExpired);
257+
258+
VerifyOrReturnError(IsCertificateValidAtIssuance(dacDerBuffer, paaDerBuffer) == CHIP_NO_ERROR,
259+
AttestationVerificationResult::kPaaExpired);
260+
261+
VerifyOrReturnError(ValidateCertificateChain(paaDerBuffer.data(), paaDerBuffer.size(), paiDerBuffer.data(), paiDerBuffer.size(),
262+
dacDerBuffer.data(), dacDerBuffer.size()) == CHIP_NO_ERROR,
253263
AttestationVerificationResult::kDacSignatureInvalid);
254264

255265
// if PAA contains VID, see if matches with DAC's VID.
256266
{
257267
uint16_t paaVid = VendorId::NotSpecified;
258-
CHIP_ERROR error = ExtractDNAttributeFromX509Cert(MatterOid::kVendorId, paa, paaVid);
268+
CHIP_ERROR error = ExtractDNAttributeFromX509Cert(MatterOid::kVendorId, paaDerBuffer, paaVid);
259269
VerifyOrReturnError(error == CHIP_NO_ERROR || error == CHIP_ERROR_KEY_NOT_FOUND,
260270
AttestationVerificationResult::kPaaFormatInvalid);
261271
if (error != CHIP_ERROR_KEY_NOT_FOUND)
@@ -289,12 +299,12 @@ AttestationVerificationResult DefaultDACVerifier::VerifyAttestationInformation(c
289299
.dacVendorId = dacVendorId,
290300
.paiVendorId = dacVendorId,
291301
};
292-
VerifyOrReturnError(ExtractDNAttributeFromX509Cert(MatterOid::kProductId, dacCertDerBuffer, deviceInfo.dacProductId) ==
302+
VerifyOrReturnError(ExtractDNAttributeFromX509Cert(MatterOid::kProductId, dacDerBuffer, deviceInfo.dacProductId) ==
293303
CHIP_NO_ERROR,
294304
AttestationVerificationResult::kDacFormatInvalid);
295305
// If PID is missing from PAI, the next method call will return CHIP_ERROR_KEY_NOT_FOUND.
296306
// Valid return values are then CHIP_NO_ERROR or CHIP_ERROR_KEY_NOT_FOUND.
297-
CHIP_ERROR error = ExtractDNAttributeFromX509Cert(MatterOid::kProductId, paiCertDerBuffer, deviceInfo.paiProductId);
307+
CHIP_ERROR error = ExtractDNAttributeFromX509Cert(MatterOid::kProductId, paiDerBuffer, deviceInfo.paiProductId);
298308
VerifyOrReturnError(error == CHIP_NO_ERROR || error == CHIP_ERROR_KEY_NOT_FOUND,
299309
AttestationVerificationResult::kPaiFormatInvalid);
300310
return ValidateCertificateDeclarationPayload(certificationDeclarationPayload, firmwareInfoSpan, deviceInfo);

src/crypto/CHIPCryptoPAL.h

+30
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,36 @@ CHIP_ERROR GetNumberOfCertsFromPKCS7(const char * pkcs7, uint32_t * n_certs);
12111211
CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate,
12121212
size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen);
12131213

1214+
/**
1215+
* @brief Validate timestamp of a certificate (toBeEvaluatedCertificate) in comparison with other certificate's
1216+
* (referenceCertificate) issuing timestamp.
1217+
*
1218+
* Errors are:
1219+
* - CHIP_ERROR_CERT_EXPIRED if the certificate timestamp does not satisfy the reference certificate's issuing timestamp.
1220+
* - CHIP_ERROR_INVALID_ARGUMENT when passing an invalid argument.
1221+
* - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors.
1222+
*
1223+
* @param referenceCertificate A DER Certificate ByteSpan used as the issuing timestamp reference.
1224+
* @param toBeEvaluatedCertificate A DER Certificate ByteSpan used to evaluate issuance against the referenceCertificate.
1225+
*
1226+
* @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise.
1227+
**/
1228+
CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & referenceCertificate, const ByteSpan & toBeEvaluatedCertificate);
1229+
1230+
/**
1231+
* @brief Validate a certificate's validity date against current time.
1232+
*
1233+
* Errors are:
1234+
* - CHIP_ERROR_CERT_EXPIRED if the certificate has expired.
1235+
* - CHIP_ERROR_INVALID_ARGUMENT when passing an invalid argument.
1236+
* - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors.
1237+
*
1238+
* @param certificate A DER Certificate ByteSpan used as the validity reference to be checked against current time.
1239+
*
1240+
* @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise.
1241+
**/
1242+
CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate);
1243+
12141244
CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey);
12151245

12161246
/**

src/crypto/CHIPCryptoPALOpenSSL.cpp

+74
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,80 @@ CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t root
17031703
return err;
17041704
}
17051705

1706+
CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & referenceCertificate, const ByteSpan & toBeEvaluatedCertificate)
1707+
{
1708+
CHIP_ERROR error = CHIP_NO_ERROR;
1709+
X509 * x509ReferenceCertificate = nullptr;
1710+
X509 * x509toBeEvaluatedCertificate = nullptr;
1711+
const unsigned char * pReferenceCertificate = referenceCertificate.data();
1712+
const unsigned char * pToBeEvaluatedCertificate = toBeEvaluatedCertificate.data();
1713+
ASN1_TIME * refNotBeforeTime = nullptr;
1714+
ASN1_TIME * tbeNotBeforeTime = nullptr;
1715+
ASN1_TIME * tbeNotAfterTime = nullptr;
1716+
// int result = 0;
1717+
1718+
VerifyOrReturnError(!referenceCertificate.empty() && !toBeEvaluatedCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT);
1719+
1720+
x509ReferenceCertificate = d2i_X509(NULL, &pReferenceCertificate, static_cast<long>(referenceCertificate.size()));
1721+
VerifyOrExit(x509ReferenceCertificate != nullptr, error = CHIP_ERROR_NO_MEMORY);
1722+
1723+
x509toBeEvaluatedCertificate = d2i_X509(NULL, &pToBeEvaluatedCertificate, static_cast<long>(toBeEvaluatedCertificate.size()));
1724+
VerifyOrExit(x509toBeEvaluatedCertificate != nullptr, error = CHIP_ERROR_NO_MEMORY);
1725+
1726+
refNotBeforeTime = X509_get_notBefore(x509ReferenceCertificate);
1727+
tbeNotBeforeTime = X509_get_notBefore(x509toBeEvaluatedCertificate);
1728+
tbeNotAfterTime = X509_get_notAfter(x509toBeEvaluatedCertificate);
1729+
VerifyOrExit(refNotBeforeTime && tbeNotBeforeTime && tbeNotAfterTime, error = CHIP_ERROR_INTERNAL);
1730+
1731+
// TODO: Handle PAA/PAI re-issue and enable below time validations
1732+
// result = ASN1_TIME_compare(refNotBeforeTime, tbeNotBeforeTime);
1733+
// check if referenceCertificate is issued at or after tbeCertificate's notBefore timestamp
1734+
// VerifyOrExit(result >= 0, error = CHIP_ERROR_CERT_EXPIRED);
1735+
1736+
// result = ASN1_TIME_compare(refNotBeforeTime, tbeNotAfterTime);
1737+
// check if referenceCertificate is issued at or before tbeCertificate's notAfter timestamp
1738+
// VerifyOrExit(result <= 0, error = CHIP_ERROR_CERT_EXPIRED);
1739+
1740+
exit:
1741+
X509_free(x509ReferenceCertificate);
1742+
X509_free(x509toBeEvaluatedCertificate);
1743+
1744+
return error;
1745+
}
1746+
1747+
CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate)
1748+
{
1749+
CHIP_ERROR error = CHIP_NO_ERROR;
1750+
X509 * x509Certificate = nullptr;
1751+
const unsigned char * pCertificate = certificate.data();
1752+
ASN1_TIME * time = nullptr;
1753+
int result = 0;
1754+
1755+
VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT);
1756+
1757+
x509Certificate = d2i_X509(NULL, &pCertificate, static_cast<long>(certificate.size()));
1758+
VerifyOrExit(x509Certificate != nullptr, error = CHIP_ERROR_NO_MEMORY);
1759+
1760+
time = X509_get_notBefore(x509Certificate);
1761+
VerifyOrExit(time, error = CHIP_ERROR_INTERNAL);
1762+
1763+
result = X509_cmp_current_time(time);
1764+
// check if certificate's notBefore timestamp is earlier than or equal to current time.
1765+
VerifyOrExit(result == -1, error = CHIP_ERROR_CERT_EXPIRED);
1766+
1767+
time = X509_get_notAfter(x509Certificate);
1768+
VerifyOrExit(time, error = CHIP_ERROR_INTERNAL);
1769+
1770+
result = X509_cmp_current_time(time);
1771+
// check if certificate's notAfter timestamp is later than current time.
1772+
VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED);
1773+
1774+
exit:
1775+
X509_free(x509Certificate);
1776+
1777+
return error;
1778+
}
1779+
17061780
CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey)
17071781
{
17081782
CHIP_ERROR err = CHIP_NO_ERROR;

0 commit comments

Comments
 (0)