Skip to content

Commit be9389c

Browse files
authored
Added an interface for revocation checks to DeviceAttestationVerifier (#31222)
* Added an interface for revocation checks to DeviceAttestationVerifier * Addressed review comments * Removed the changes to add revocation set support to chip-tool and it will be submitted as a followup PR * Implemented PR #31222 review comments * Fixed issues in revocation check and code cleanup
1 parent b3087d6 commit be9389c

16 files changed

+144
-10
lines changed

docs/ERROR_CODES.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ This file was **AUTOMATICALLY** generated by
4747
| 29 | 0x1D | `CHIP_ERROR_INVALID_IPK` |
4848
| 30 | 0x1E | `CHIP_ERROR_INVALID_STRING_LENGTH` |
4949
| 31 | 0x1F | `CHIP_ERROR_INVALID_LIST_LENGTH` |
50+
| 32 | 0x20 | `CHIP_ERROR_FAILED_DEVICE_ATTESTATION` |
5051
| 33 | 0x21 | `CHIP_ERROR_END_OF_TLV` |
5152
| 34 | 0x22 | `CHIP_ERROR_TLV_UNDERRUN` |
5253
| 35 | 0x23 | `CHIP_ERROR_INVALID_TLV_ELEMENT` |

src/controller/AutoCommissioner.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
389389
case CommissioningStage::kSendAttestationRequest:
390390
return CommissioningStage::kAttestationVerification;
391391
case CommissioningStage::kAttestationVerification:
392+
return CommissioningStage::kAttestationRevocationCheck;
393+
case CommissioningStage::kAttestationRevocationCheck:
392394
return CommissioningStage::kSendOpCertSigningRequest;
393395
case CommissioningStage::kSendOpCertSigningRequest:
394396
return CommissioningStage::kValidateCSR;
@@ -693,6 +695,13 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
693695
"Failed device attestation. Device vendor and/or product ID do not match the IDs expected. "
694696
"Verify DAC certificate chain and certification declaration to ensure spec rules followed.");
695697
}
698+
699+
if (report.stageCompleted == CommissioningStage::kAttestationVerification)
700+
{
701+
ChipLogError(Controller, "Failed verifying attestation information. Now checking DAC chain revoked status.");
702+
// don't error out until we check for DAC chain revocation status
703+
err = CHIP_NO_ERROR;
704+
}
696705
}
697706
else if (report.Is<CommissioningErrorInfo>())
698707
{

src/controller/CHIPDeviceController.cpp

+69-4
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ DeviceCommissioner::ContinueCommissioningAfterDeviceAttestation(DeviceProxy * de
947947
return CHIP_ERROR_INCORRECT_STATE;
948948
}
949949

950-
if (mCommissioningStage != CommissioningStage::kAttestationVerification)
950+
if (mCommissioningStage != CommissioningStage::kAttestationRevocationCheck)
951951
{
952952
ChipLogError(Controller, "Commissioning is not attestation verification phase");
953953
return CHIP_ERROR_INCORRECT_STATE;
@@ -1175,6 +1175,17 @@ void DeviceCommissioner::OnDeviceAttestationInformationVerification(
11751175
MATTER_TRACE_SCOPE("OnDeviceAttestationInformationVerification", "DeviceCommissioner");
11761176
DeviceCommissioner * commissioner = reinterpret_cast<DeviceCommissioner *>(context);
11771177

1178+
if (commissioner->mCommissioningStage == CommissioningStage::kAttestationVerification)
1179+
{
1180+
// Check for revoked DAC Chain before calling delegate. Enter next stage.
1181+
1182+
CommissioningDelegate::CommissioningReport report;
1183+
report.Set<AttestationErrorInfo>(result);
1184+
1185+
return commissioner->CommissioningStageComplete(
1186+
result == AttestationVerificationResult::kSuccess ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL, report);
1187+
}
1188+
11781189
if (!commissioner->mDeviceBeingCommissioned)
11791190
{
11801191
ChipLogError(Controller, "Device attestation verification result received when we're not commissioning a device");
@@ -1184,6 +1195,15 @@ void DeviceCommissioner::OnDeviceAttestationInformationVerification(
11841195
auto & params = commissioner->mDefaultCommissioner->GetCommissioningParameters();
11851196
Credentials::DeviceAttestationDelegate * deviceAttestationDelegate = params.GetDeviceAttestationDelegate();
11861197

1198+
if (params.GetCompletionStatus().attestationResult.HasValue())
1199+
{
1200+
auto previousResult = params.GetCompletionStatus().attestationResult.Value();
1201+
if (previousResult != AttestationVerificationResult::kSuccess)
1202+
{
1203+
result = previousResult;
1204+
}
1205+
}
1206+
11871207
if (result != AttestationVerificationResult::kSuccess)
11881208
{
11891209
CommissioningDelegate::CommissioningReport report;
@@ -1398,6 +1418,18 @@ CHIP_ERROR DeviceCommissioner::ValidateAttestationInfo(const Credentials::Device
13981418
return CHIP_NO_ERROR;
13991419
}
14001420

1421+
CHIP_ERROR
1422+
DeviceCommissioner::CheckForRevokedDACChain(const Credentials::DeviceAttestationVerifier::AttestationInfo & info)
1423+
{
1424+
MATTER_TRACE_SCOPE("CheckForRevokedDACChain", "DeviceCommissioner");
1425+
VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
1426+
VerifyOrReturnError(mDeviceAttestationVerifier != nullptr, CHIP_ERROR_INCORRECT_STATE);
1427+
1428+
mDeviceAttestationVerifier->CheckForRevokedDACChain(info, &mDeviceAttestationInformationVerificationCallback);
1429+
1430+
return CHIP_NO_ERROR;
1431+
}
1432+
14011433
CHIP_ERROR DeviceCommissioner::ValidateCSR(DeviceProxy * proxy, const ByteSpan & NOCSRElements,
14021434
const ByteSpan & AttestationSignature, const ByteSpan & dac, const ByteSpan & csrNonce)
14031435
{
@@ -3037,9 +3069,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
30373069
}
30383070
case CommissioningStage::kAttestationVerification: {
30393071
ChipLogProgress(Controller, "Verifying attestation");
3040-
if (!params.GetAttestationElements().HasValue() || !params.GetAttestationSignature().HasValue() ||
3041-
!params.GetAttestationNonce().HasValue() || !params.GetDAC().HasValue() || !params.GetPAI().HasValue() ||
3042-
!params.GetRemoteVendorId().HasValue() || !params.GetRemoteProductId().HasValue())
3072+
if (IsAttestationInformationMissing(params))
30433073
{
30443074
ChipLogError(Controller, "Missing attestation information");
30453075
CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT);
@@ -3055,9 +3085,32 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
30553085
if (ValidateAttestationInfo(info) != CHIP_NO_ERROR)
30563086
{
30573087
ChipLogError(Controller, "Error validating attestation information");
3088+
CommissioningStageComplete(CHIP_ERROR_FAILED_DEVICE_ATTESTATION);
3089+
return;
3090+
}
3091+
}
3092+
break;
3093+
case CommissioningStage::kAttestationRevocationCheck: {
3094+
ChipLogProgress(Controller, "Verifying device's DAC chain revocation status");
3095+
if (IsAttestationInformationMissing(params))
3096+
{
3097+
ChipLogError(Controller, "Missing attestation information");
30583098
CommissioningStageComplete(CHIP_ERROR_INVALID_ARGUMENT);
30593099
return;
30603100
}
3101+
3102+
DeviceAttestationVerifier::AttestationInfo info(
3103+
params.GetAttestationElements().Value(),
3104+
proxy->GetSecureSession().Value()->AsSecureSession()->GetCryptoContext().GetAttestationChallenge(),
3105+
params.GetAttestationSignature().Value(), params.GetPAI().Value(), params.GetDAC().Value(),
3106+
params.GetAttestationNonce().Value(), params.GetRemoteVendorId().Value(), params.GetRemoteProductId().Value());
3107+
3108+
if (CheckForRevokedDACChain(info) != CHIP_NO_ERROR)
3109+
{
3110+
ChipLogError(Controller, "Error validating device's DAC chain revocation status");
3111+
CommissioningStageComplete(CHIP_ERROR_FAILED_DEVICE_ATTESTATION);
3112+
return;
3113+
}
30613114
}
30623115
break;
30633116
case CommissioningStage::kSendOpCertSigningRequest: {
@@ -3424,6 +3477,18 @@ void DeviceCommissioner::ExtendFailsafeBeforeNetworkEnable(DeviceProxy * device,
34243477
}
34253478
}
34263479

3480+
bool DeviceCommissioner::IsAttestationInformationMissing(const CommissioningParameters & params)
3481+
{
3482+
if (!params.GetAttestationElements().HasValue() || !params.GetAttestationSignature().HasValue() ||
3483+
!params.GetAttestationNonce().HasValue() || !params.GetDAC().HasValue() || !params.GetPAI().HasValue() ||
3484+
!params.GetRemoteVendorId().HasValue() || !params.GetRemoteProductId().HasValue())
3485+
{
3486+
return true;
3487+
}
3488+
3489+
return false;
3490+
}
3491+
34273492
CHIP_ERROR DeviceController::GetCompressedFabricIdBytes(MutableByteSpan & outBytes) const
34283493
{
34293494
const auto * fabricInfo = GetFabricInfo();

src/controller/CHIPDeviceController.h

+10
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,14 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
10001000
*/
10011001
CHIP_ERROR ProcessCertificateChain(const ByteSpan & certificate);
10021002

1003+
/**
1004+
* @brief
1005+
* This function validates the revocation status of the DAC Chain sent by the device.
1006+
*
1007+
* @param[in] info Structure contatining all the required information for validating the device attestation.
1008+
*/
1009+
CHIP_ERROR CheckForRevokedDACChain(const Credentials::DeviceAttestationVerifier::AttestationInfo & info);
1010+
10031011
void HandleAttestationResult(CHIP_ERROR err);
10041012

10051013
CommissioneeDeviceProxy * FindCommissioneeDevice(NodeId id);
@@ -1053,6 +1061,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
10531061
// extend it).
10541062
void ExtendFailsafeBeforeNetworkEnable(DeviceProxy * device, CommissioningParameters & params, CommissioningStage step);
10551063

1064+
bool IsAttestationInformationMissing(const CommissioningParameters & params);
1065+
10561066
chip::Callback::Callback<OnDeviceConnected> mOnDeviceConnectedCallback;
10571067
chip::Callback::Callback<OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
10581068
#if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES

src/controller/CommissioningDelegate.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ const char * StageToString(CommissioningStage stage)
7070
case kAttestationVerification:
7171
return "AttestationVerification";
7272

73+
case kAttestationRevocationCheck:
74+
return "AttestationRevocationCheck";
75+
7376
case kSendOpCertSigningRequest:
7477
return "SendOpCertSigningRequest";
7578

src/controller/CommissioningDelegate.h

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum CommissioningStage : uint8_t
4747
kSendDACCertificateRequest, ///< Send DAC CertificateChainRequest (0x3E:2) command to the device
4848
kSendAttestationRequest, ///< Send AttestationRequest (0x3E:0) command to the device
4949
kAttestationVerification, ///< Verify AttestationResponse (0x3E:1) validity
50+
kAttestationRevocationCheck, ///< Verify Revocation Status of device's DAC chain
5051
kSendOpCertSigningRequest, ///< Send CSRRequest (0x3E:4) command to the device
5152
kValidateCSR, ///< Verify CSRResponse (0x3E:5) validity
5253
kGenerateNOCChain, ///< TLV encode Node Operational Credentials (NOC) chain certs
@@ -782,6 +783,7 @@ class CommissioningDelegate
782783
* kSendDACCertificateRequest: RequestedCertificate
783784
* kSendAttestationRequest: AttestationResponse
784785
* kAttestationVerification: AttestationErrorInfo if there is an error
786+
* kAttestationRevocationCheck: AttestationErrorInfo if there is an error
785787
* kSendOpCertSigningRequest: CSRResponse
786788
* kGenerateNOCChain: NocChain
787789
* kSendTrustedRootCert: None

src/controller/python/OpCredsBinding.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ class TestCommissioner : public chip::Controller::AutoCommissioner
331331
return mNeedsDST && mParams.GetDSTOffsets().HasValue();
332332
case chip::Controller::CommissioningStage::kError:
333333
case chip::Controller::CommissioningStage::kSecurePairing:
334+
// "not valid" because attestation verification always fails after entering revocation check step
335+
case chip::Controller::CommissioningStage::kAttestationVerification:
334336
return false;
335337
default:
336338
return true;

src/controller/python/test/test_scripts/commissioning_failure_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def main():
9696

9797
# TODO: Start at stage 2 once handling for arming failsafe on pase is done.
9898
if options.report:
99-
for testFailureStage in range(3, 20):
99+
for testFailureStage in range(3, 21):
100100
FailIfNot(test.TestPaseOnly(ip=options.deviceAddress1,
101101
setuppin=20202021,
102102
nodeid=1),
@@ -105,7 +105,7 @@ def main():
105105
"Commissioning failure tests failed for simulated report failure on stage {}".format(testFailureStage))
106106

107107
else:
108-
for testFailureStage in range(3, 20):
108+
for testFailureStage in range(3, 21):
109109
FailIfNot(test.TestPaseOnly(ip=options.deviceAddress1,
110110
setuppin=20202021,
111111
nodeid=1),

src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,16 @@ CHIP_ERROR DefaultDACVerifier::VerifyNodeOperationalCSRInformation(const ByteSpa
607607
return CHIP_NO_ERROR;
608608
}
609609

610+
void DefaultDACVerifier::CheckForRevokedDACChain(const AttestationInfo & info,
611+
Callback::Callback<OnAttestationInformationVerification> * onCompletion)
612+
{
613+
AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;
614+
615+
// TODO(#33124): Implement default version of CheckForRevokedDACChain
616+
617+
onCompletion->mCall(onCompletion->mContext, info, attestationError);
618+
}
619+
610620
bool CsaCdKeysTrustStore::IsCdTestKey(const ByteSpan & kid) const
611621
{
612622
return kid.data_equal(ByteSpan{ gTestCdPubkeyKid });

src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h

+3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class DefaultDACVerifier : public DeviceAttestationVerifier
7474
const ByteSpan & attestationSignatureBuffer,
7575
const Crypto::P256PublicKey & dacPublicKey, const ByteSpan & csrNonce) override;
7676

77+
void CheckForRevokedDACChain(const AttestationInfo & info,
78+
Callback::Callback<OnAttestationInformationVerification> * onCompletion) override;
79+
7780
CsaCdKeysTrustStore * GetCertificationDeclarationTrustStore() override { return &mCdKeysTrustStore; }
7881

7982
protected:

src/credentials/attestation_verifier/DeviceAttestationVerifier.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ class UnimplementedDACVerifier : public DeviceAttestationVerifier
6969
(void) csrNonce;
7070
return CHIP_ERROR_NOT_IMPLEMENTED;
7171
}
72+
73+
void CheckForRevokedDACChain(const AttestationInfo & info,
74+
Callback::Callback<OnAttestationInformationVerification> * onCompletion) override
75+
{
76+
(void) info;
77+
(void) onCompletion;
78+
VerifyOrDie(false);
79+
}
7280
};
7381

7482
// Default to avoid nullptr on getter and cleanly handle new products/clients before

src/credentials/attestation_verifier/DeviceAttestationVerifier.h

+10
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,16 @@ class DeviceAttestationVerifier
386386
const Crypto::P256PublicKey & dacPublicKey,
387387
const ByteSpan & csrNonce) = 0;
388388

389+
/**
390+
* @brief Verify whether or not the given DAC chain is revoked.
391+
*
392+
* @param[in] info All of the information required to check for revoked DAC chain.
393+
* @param[in] onCompletion Callback handler to provide Attestation Information Verification result to the caller of
394+
* CheckForRevokedDACChain()
395+
*/
396+
virtual void CheckForRevokedDACChain(const AttestationInfo & info,
397+
Callback::Callback<OnAttestationInformationVerification> * onCompletion) = 0;
398+
389399
/**
390400
* @brief Get the trust store used for the attestation verifier.
391401
*

src/lib/core/CHIPError.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ bool FormatCHIPError(char * buf, uint16_t bufSize, CHIP_ERROR err)
146146
case CHIP_ERROR_INVALID_LIST_LENGTH.AsInteger():
147147
desc = "Invalid list length";
148148
break;
149+
case CHIP_ERROR_FAILED_DEVICE_ATTESTATION.AsInteger():
150+
desc = "Failed Device Attestation";
151+
break;
149152
case CHIP_END_OF_TLV.AsInteger():
150153
desc = "End of TLV";
151154
break;

src/lib/core/CHIPError.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,14 @@ using CHIP_ERROR = ::chip::ChipError;
743743
*/
744744
#define CHIP_ERROR_INVALID_LIST_LENGTH CHIP_CORE_ERROR(0x1f)
745745

746-
// AVAILABLE: 0x20
746+
/**
747+
* @def CHIP_ERROR_FAILED_DEVICE_ATTESTATION
748+
*
749+
* @brief
750+
* Device Attestation failed.
751+
*
752+
*/
753+
#define CHIP_ERROR_FAILED_DEVICE_ATTESTATION CHIP_CORE_ERROR(0x20)
747754

748755
/**
749756
* @def CHIP_END_OF_TLV

src/lib/core/tests/TestCHIPErrorStr.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ static const CHIP_ERROR kTestElements[] =
6969
CHIP_ERROR_UNINITIALIZED,
7070
CHIP_ERROR_INVALID_STRING_LENGTH,
7171
CHIP_ERROR_INVALID_LIST_LENGTH,
72+
CHIP_ERROR_FAILED_DEVICE_ATTESTATION,
7273
CHIP_END_OF_TLV,
7374
CHIP_ERROR_TLV_UNDERRUN,
7475
CHIP_ERROR_INVALID_TLV_ELEMENT,

src/python_testing/TC_CGEN_2_4.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
kSendPAICertificateRequest = 10
3535
kSendDACCertificateRequest = 11
3636
kSendAttestationRequest = 12
37-
kSendOpCertSigningRequest = 14
38-
kSendTrustedRootCert = 17
39-
kSendNOC = 18
37+
kSendOpCertSigningRequest = 15
38+
kSendTrustedRootCert = 18
39+
kSendNOC = 19
4040

4141

4242
class TC_CGEN_2_4(MatterBaseTest):

0 commit comments

Comments
 (0)