Coverage Report

Created: 2026-09-24 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/credentials/attestation_verifier/DefaultDeviceAttestationVerifier.cpp
Line
Count
Source
1
/*
2
 *
3
 *    Copyright (c) 2021-2022 Project CHIP Authors
4
 *
5
 *    Licensed under the Apache License, Version 2.0 (the "License");
6
 *    you may not use this file except in compliance with the License.
7
 *    You may obtain a copy of the License at
8
 *
9
 *        http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *    Unless required by applicable law or agreed to in writing, software
12
 *    distributed under the License is distributed on an "AS IS" BASIS,
13
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *    See the License for the specific language governing permissions and
15
 *    limitations under the License.
16
 */
17
#include "DefaultDeviceAttestationVerifier.h"
18
19
#include <controller/OperationalCredentialsDelegate.h>
20
#include <credentials/CHIPCert.h>
21
#include <credentials/CertificationDeclaration.h>
22
#include <credentials/DeviceAttestationConstructor.h>
23
#include <credentials/DeviceAttestationVendorReserved.h>
24
#include <credentials/attestation_verifier/TestPAAStore.h>
25
#include <crypto/CHIPCryptoPAL.h>
26
27
#include <lib/asn1/ASN1.h>
28
#include <lib/core/CHIPError.h>
29
#include <lib/core/Global.h>
30
#include <lib/support/CodeUtils.h>
31
#include <lib/support/ScopedMemoryBuffer.h>
32
#include <lib/support/Span.h>
33
#include <lib/support/StringBuilder.h>
34
#include <lib/support/logging/CHIPLogging.h>
35
36
using namespace chip::Crypto;
37
using chip::TestCerts::GetTestPaaRootStore;
38
39
namespace chip {
40
namespace Credentials {
41
42
namespace {
43
44
// As per specifications section 11.22.5.1. Constant RESP_MAX
45
constexpr size_t kMaxResponseLength = 900;
46
47
// Test CD Signing Key from `credentials/test/certification-declaration/Chip-Test-CD-Signing-Cert.pem`
48
// used to verify any in-SDK development CDs. The associated keypair to do actual signing is in
49
// `credentials/test/certification-declaration/Chip-Test-CD-Signing-Key.pem`.
50
//
51
// Note that this certificate is a self signed certificate and doesn't chain up to the CSA trusted root.
52
// This CD Signing certificate can only be used to sign CDs for testing/development purposes
53
// and should never be used in production devices.
54
//
55
// -----BEGIN CERTIFICATE-----
56
// MIIBszCCAVqgAwIBAgIIRdrzneR6oI8wCgYIKoZIzj0EAwIwKzEpMCcGA1UEAwwg
57
// TWF0dGVyIFRlc3QgQ0QgU2lnbmluZyBBdXRob3JpdHkwIBcNMjEwNjI4MTQyMzQz
58
// WhgPOTk5OTEyMzEyMzU5NTlaMCsxKTAnBgNVBAMMIE1hdHRlciBUZXN0IENEIFNp
59
// Z25pbmcgQXV0aG9yaXR5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPDmJIkUr
60
// VcrzicJb0bykZWlSzLkOiGkkmthHRlMBTL+V1oeWXgNrUhxRA35rjO3vyh60QEZp
61
// T6CIgu7WUZ3suqNmMGQwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMC
62
// AQYwHQYDVR0OBBYEFGL6gjNZrPqplj4c+hQK3fUE83FgMB8GA1UdIwQYMBaAFGL6
63
// gjNZrPqplj4c+hQK3fUE83FgMAoGCCqGSM49BAMCA0cAMEQCICxUXOTkV9im8NnZ
64
// u+vW7OHd/n+MbZps83UyH8b6xxOEAiBUB3jodDlyUn7t669YaGIgtUB48s1OYqdq
65
// 58u5L/VMiw==
66
// -----END CERTIFICATE-----
67
//
68
constexpr uint8_t gTestCdPubkeyBytes[] = {
69
    0x04, 0x3c, 0x39, 0x89, 0x22, 0x45, 0x2b, 0x55, 0xca, 0xf3, 0x89, 0xc2, 0x5b, 0xd1, 0xbc, 0xa4, 0x65,
70
    0x69, 0x52, 0xcc, 0xb9, 0x0e, 0x88, 0x69, 0x24, 0x9a, 0xd8, 0x47, 0x46, 0x53, 0x01, 0x4c, 0xbf, 0x95,
71
    0xd6, 0x87, 0x96, 0x5e, 0x03, 0x6b, 0x52, 0x1c, 0x51, 0x03, 0x7e, 0x6b, 0x8c, 0xed, 0xef, 0xca, 0x1e,
72
    0xb4, 0x40, 0x46, 0x69, 0x4f, 0xa0, 0x88, 0x82, 0xee, 0xd6, 0x51, 0x9d, 0xec, 0xba,
73
};
74
75
constexpr uint8_t gTestCdPubkeyKid[] = {
76
    0x62, 0xfa, 0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a, 0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60,
77
};
78
79
// Official CSA "Matter Certification and Testing CA"
80
//
81
// -----BEGIN CERTIFICATE-----
82
// MIICATCCAaegAwIBAgIHY3Nhcm9vdDAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND
83
// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB
84
// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjA3MDcxOTI4MDRaGA8yMTIyMDYx
85
// MzE5MjgwNFowUjEMMAoGA1UECgwDQ1NBMSwwKgYDVQQDDCNNYXR0ZXIgQ2VydGlm
86
// aWNhdGlvbiBhbmQgVGVzdGluZyBDQTEUMBIGCisGAQQBgqJ8AgEMBEM1QTAwWTAT
87
// BgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ4SjrDql2+y3IP5iEdPK1IYm/3EaCkkp+t
88
// 2GD44nf/wN4fPrYzejSEe1o6BW6ocQ6Td+7t7iUXA/3ZNQEly45Io2YwZDASBgNV
89
// HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUl+Rp0MUE
90
// FMJvxwH3fpR3OQmN9qUwHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3OQmN9qUw
91
// CgYIKoZIzj0EAwIDSAAwRQIgearlB0fCJ49UoJ6xwKPdlPEopCOL9jVCviODEleI
92
// +mQCIQDvvDCKi7kvj4R4BoFS4BVZGCk4zJ84W4tfTTfu89lRbQ==
93
// -----END CERTIFICATE-----
94
//
95
constexpr uint8_t gCdRootCert[] = {
96
    0x30, 0x82, 0x02, 0x01, 0x30, 0x82, 0x01, 0xa7, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x07, 0x63, 0x73, 0x61, 0x72, 0x6f, 0x6f,
97
    0x74, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x52, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03,
98
    0x55, 0x04, 0x0a, 0x0c, 0x03, 0x43, 0x53, 0x41, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x23, 0x4d, 0x61,
99
    0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e,
100
    0x64, 0x20, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x41, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01,
101
    0x04, 0x01, 0x82, 0xa2, 0x7c, 0x02, 0x01, 0x0c, 0x04, 0x43, 0x35, 0x41, 0x30, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x37,
102
    0x30, 0x37, 0x31, 0x39, 0x32, 0x38, 0x30, 0x34, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, 0x32, 0x30, 0x36, 0x31, 0x33, 0x31, 0x39,
103
    0x32, 0x38, 0x30, 0x34, 0x5a, 0x30, 0x52, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x03, 0x43, 0x53, 0x41,
104
    0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x23, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72,
105
    0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e,
106
    0x67, 0x20, 0x43, 0x41, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x02, 0x01, 0x0c,
107
    0x04, 0x43, 0x35, 0x41, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
108
    0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x38, 0x4a, 0x3a, 0xc3, 0xaa, 0x5d, 0xbe, 0xcb, 0x72, 0x0f,
109
    0xe6, 0x21, 0x1d, 0x3c, 0xad, 0x48, 0x62, 0x6f, 0xf7, 0x11, 0xa0, 0xa4, 0x92, 0x9f, 0xad, 0xd8, 0x60, 0xf8, 0xe2, 0x77, 0xff,
110
    0xc0, 0xde, 0x1f, 0x3e, 0xb6, 0x33, 0x7a, 0x34, 0x84, 0x7b, 0x5a, 0x3a, 0x05, 0x6e, 0xa8, 0x71, 0x0e, 0x93, 0x77, 0xee, 0xed,
111
    0xee, 0x25, 0x17, 0x03, 0xfd, 0xd9, 0x35, 0x01, 0x25, 0xcb, 0x8e, 0x48, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55,
112
    0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
113
    0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
114
    0x97, 0xe4, 0x69, 0xd0, 0xc5, 0x04, 0x14, 0xc2, 0x6f, 0xc7, 0x01, 0xf7, 0x7e, 0x94, 0x77, 0x39, 0x09, 0x8d, 0xf6, 0xa5, 0x30,
115
    0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x97, 0xe4, 0x69, 0xd0, 0xc5, 0x04, 0x14, 0xc2, 0x6f,
116
    0xc7, 0x01, 0xf7, 0x7e, 0x94, 0x77, 0x39, 0x09, 0x8d, 0xf6, 0xa5, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
117
    0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x79, 0xaa, 0xe5, 0x07, 0x47, 0xc2, 0x27, 0x8f, 0x54, 0xa0, 0x9e, 0xb1,
118
    0xc0, 0xa3, 0xdd, 0x94, 0xf1, 0x28, 0xa4, 0x23, 0x8b, 0xf6, 0x35, 0x42, 0xbe, 0x23, 0x83, 0x12, 0x57, 0x88, 0xfa, 0x64, 0x02,
119
    0x21, 0x00, 0xef, 0xbc, 0x30, 0x8a, 0x8b, 0xb9, 0x2f, 0x8f, 0x84, 0x78, 0x06, 0x81, 0x52, 0xe0, 0x15, 0x59, 0x18, 0x29, 0x38,
120
    0xcc, 0x9f, 0x38, 0x5b, 0x8b, 0x5f, 0x4d, 0x37, 0xee, 0xf3, 0xd9, 0x51, 0x6d
121
};
122
123
// Official CD "Signing Key 001"
124
//
125
// -----BEGIN CERTIFICATE-----
126
// MIICBzCCAa2gAwIBAgIHY3NhY2RrMTAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND
127
// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB
128
// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjEwMDMxOTI4NTVaGA8yMDcyMDky
129
// MDE5Mjg1NVowWDEMMAoGA1UECgwDQ1NBMTIwMAYDVQQDDClDZXJ0aWZpY2F0aW9u
130
// IERlY2xhcmF0aW9uIFNpZ25pbmcgS2V5IDAwMTEUMBIGCisGAQQBgqJ8AgEMBEM1
131
// QTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATN7uk+RPi3K+PRqcB+IZaLmv/z
132
// tAPwXhZp17Hlyu5vx3FLQufiNpXpLNdjVHOigK5ojze7lInhFim5uU/3sJkpo2Yw
133
// ZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU
134
// /jQ/lZlHdjth7kU5ExM4SU/mfY4wHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3
135
// OQmN9qUwCgYIKoZIzj0EAwIDSAAwRQIgEDWOcdKsVGtUh3evHbBd1lq4aS7yQtOp
136
// 6GrOQ3/zXBsCIQDxorh2RXSaI8m2RCcoWaiWa0nLzQepNm3C2jrQVJmC2Q==
137
// -----END CERTIFICATE-----
138
//
139
constexpr uint8_t gCdSigningKey001PubkeyBytes[] = {
140
    0x04, 0xcd, 0xee, 0xe9, 0x3e, 0x44, 0xf8, 0xb7, 0x2b, 0xe3, 0xd1, 0xa9, 0xc0, 0x7e, 0x21, 0x96, 0x8b,
141
    0x9a, 0xff, 0xf3, 0xb4, 0x03, 0xf0, 0x5e, 0x16, 0x69, 0xd7, 0xb1, 0xe5, 0xca, 0xee, 0x6f, 0xc7, 0x71,
142
    0x4b, 0x42, 0xe7, 0xe2, 0x36, 0x95, 0xe9, 0x2c, 0xd7, 0x63, 0x54, 0x73, 0xa2, 0x80, 0xae, 0x68, 0x8f,
143
    0x37, 0xbb, 0x94, 0x89, 0xe1, 0x16, 0x29, 0xb9, 0xb9, 0x4f, 0xf7, 0xb0, 0x99, 0x29,
144
};
145
146
constexpr uint8_t gCdSigningKey001Kid[] = {
147
    0xFE, 0x34, 0x3F, 0x95, 0x99, 0x47, 0x76, 0x3B, 0x61, 0xEE, 0x45, 0x39, 0x13, 0x13, 0x38, 0x49, 0x4F, 0xE6, 0x7D, 0x8E,
148
};
149
150
// Official CD "Signing Key 002"
151
//
152
// -----BEGIN CERTIFICATE-----
153
// MIICCDCCAa2gAwIBAgIHY3NhY2RrMjAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND
154
// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB
155
// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjEwMDMxOTM2NDZaGA8yMDcyMDky
156
// MDE5MzY0NlowWDEMMAoGA1UECgwDQ1NBMTIwMAYDVQQDDClDZXJ0aWZpY2F0aW9u
157
// IERlY2xhcmF0aW9uIFNpZ25pbmcgS2V5IDAwMjEUMBIGCisGAQQBgqJ8AgEMBEM1
158
// QTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQDGTfo+UJRBF3ydFe7RiU+43VO
159
// jBKuKFV9gCe51MNW2RtAjP8yJ1AXsl+Mi6IFFtXIOvK3JBKAE9/Mj5XSAKkLo2Yw
160
// ZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU
161
// 3QTbWFshTBxYFYfmVo30h7bdxwEwHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3
162
// OQmN9qUwCgYIKoZIzj0EAwIDSQAwRgIhAJruzxZ806cP/LoQ07PN9xAbjLdwUalV
163
// h0Qfx304Tb92AiEAk+jnf2qtyfKyTEHpT3Xf3bfekqUOA+8ikB1yjL5oTsI=
164
// -----END CERTIFICATE-----
165
//
166
constexpr uint8_t gCdSigningKey002PubkeyBytes[] = {
167
    0x04, 0x03, 0x19, 0x37, 0xe8, 0xf9, 0x42, 0x51, 0x04, 0x5d, 0xf2, 0x74, 0x57, 0xbb, 0x46, 0x25, 0x3e,
168
    0xe3, 0x75, 0x4e, 0x8c, 0x12, 0xae, 0x28, 0x55, 0x7d, 0x80, 0x27, 0xb9, 0xd4, 0xc3, 0x56, 0xd9, 0x1b,
169
    0x40, 0x8c, 0xff, 0x32, 0x27, 0x50, 0x17, 0xb2, 0x5f, 0x8c, 0x8b, 0xa2, 0x05, 0x16, 0xd5, 0xc8, 0x3a,
170
    0xf2, 0xb7, 0x24, 0x12, 0x80, 0x13, 0xdf, 0xcc, 0x8f, 0x95, 0xd2, 0x00, 0xa9, 0x0b,
171
};
172
173
constexpr uint8_t gCdSigningKey002Kid[] = {
174
    0xDD, 0x04, 0xDB, 0x58, 0x5B, 0x21, 0x4C, 0x1C, 0x58, 0x15, 0x87, 0xE6, 0x56, 0x8D, 0xF4, 0x87, 0xB6, 0xDD, 0xC7, 0x01,
175
};
176
177
// Official CD "Signing Key 003"
178
//
179
// -----BEGIN CERTIFICATE-----
180
// MIICBjCCAa2gAwIBAgIHY3NhY2RrMzAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND
181
// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB
182
// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjEwMDMxOTQxMDFaGA8yMDcyMDky
183
// MDE5NDEwMVowWDEMMAoGA1UECgwDQ1NBMTIwMAYDVQQDDClDZXJ0aWZpY2F0aW9u
184
// IERlY2xhcmF0aW9uIFNpZ25pbmcgS2V5IDAwMzEUMBIGCisGAQQBgqJ8AgEMBEM1
185
// QTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASfV1zV/bdSHxCk3zHwc5ErYUco
186
// 8tN/W2uWvCy/fAsRlpBXfVVdIaCWYKiwgqM56lMPeoEthpO1b9dkGF+rzTL1o2Yw
187
// ZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU
188
// RxA158BOqqi+fE1ME+PkwgmVqEswHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3
189
// OQmN9qUwCgYIKoZIzj0EAwIDRwAwRAIgIFecbY+1mVVNqxH9+8IMB8+safdyIJU2
190
// AqqtZ/w7AkQCIHiVlYTaCnJsnW5/cvj9GfIv7Eb0cjdmcAkrYGbnPQzX
191
// -----END CERTIFICATE-----
192
//
193
constexpr uint8_t gCdSigningKey003PubkeyBytes[] = {
194
    0x04, 0x9f, 0x57, 0x5c, 0xd5, 0xfd, 0xb7, 0x52, 0x1f, 0x10, 0xa4, 0xdf, 0x31, 0xf0, 0x73, 0x91, 0x2b,
195
    0x61, 0x47, 0x28, 0xf2, 0xd3, 0x7f, 0x5b, 0x6b, 0x96, 0xbc, 0x2c, 0xbf, 0x7c, 0x0b, 0x11, 0x96, 0x90,
196
    0x57, 0x7d, 0x55, 0x5d, 0x21, 0xa0, 0x96, 0x60, 0xa8, 0xb0, 0x82, 0xa3, 0x39, 0xea, 0x53, 0x0f, 0x7a,
197
    0x81, 0x2d, 0x86, 0x93, 0xb5, 0x6f, 0xd7, 0x64, 0x18, 0x5f, 0xab, 0xcd, 0x32, 0xf5,
198
};
199
200
constexpr uint8_t gCdSigningKey003Kid[] = {
201
    0x47, 0x10, 0x35, 0xE7, 0xC0, 0x4E, 0xAA, 0xA8, 0xBE, 0x7C, 0x4D, 0x4C, 0x13, 0xE3, 0xE4, 0xC2, 0x09, 0x95, 0xA8, 0x4B,
202
};
203
204
// Official CD "Signing Key 004"
205
//
206
// -----BEGIN CERTIFICATE-----
207
// MIICBjCCAa2gAwIBAgIHY3NhY2RrNDAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND
208
// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB
209
// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjEwMDMxOTQzMjFaGA8yMDcyMDky
210
// MDE5NDMyMVowWDEMMAoGA1UECgwDQ1NBMTIwMAYDVQQDDClDZXJ0aWZpY2F0aW9u
211
// IERlY2xhcmF0aW9uIFNpZ25pbmcgS2V5IDAwNDEUMBIGCisGAQQBgqJ8AgEMBEM1
212
// QTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR8/I2IEKic9PoZF3jyr+x4+FF6
213
// l6Plf8ITutiI42EedP+2hL3rqKaLJSNKXDWPNzurm20wThMG3XYgpSjRFhwLo2Yw
214
// ZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU
215
// 9oYDo2kumBByQZ6h4as4VL13ldMwHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3
216
// OQmN9qUwCgYIKoZIzj0EAwIDRwAwRAIgLqAfkbtLYYdmQsnbn0CWv3G1/lbE36nz
217
// HbLbW5t6PY4CIE8oyIHsVhNSTPcb3mwRp+Vxhs8tKhbAdwv5BGgDaAHj
218
// -----END CERTIFICATE-----
219
//
220
constexpr uint8_t gCdSigningKey004PubkeyBytes[] = {
221
    0x04, 0x7c, 0xfc, 0x8d, 0x88, 0x10, 0xa8, 0x9c, 0xf4, 0xfa, 0x19, 0x17, 0x78, 0xf2, 0xaf, 0xec, 0x78,
222
    0xf8, 0x51, 0x7a, 0x97, 0xa3, 0xe5, 0x7f, 0xc2, 0x13, 0xba, 0xd8, 0x88, 0xe3, 0x61, 0x1e, 0x74, 0xff,
223
    0xb6, 0x84, 0xbd, 0xeb, 0xa8, 0xa6, 0x8b, 0x25, 0x23, 0x4a, 0x5c, 0x35, 0x8f, 0x37, 0x3b, 0xab, 0x9b,
224
    0x6d, 0x30, 0x4e, 0x13, 0x06, 0xdd, 0x76, 0x20, 0xa5, 0x28, 0xd1, 0x16, 0x1c, 0x0b,
225
};
226
227
constexpr uint8_t gCdSigningKey004Kid[] = {
228
    0xF6, 0x86, 0x03, 0xA3, 0x69, 0x2E, 0x98, 0x10, 0x72, 0x41, 0x9E, 0xA1, 0xE1, 0xAB, 0x38, 0x54, 0xBD, 0x77, 0x95, 0xD3,
229
};
230
231
// Official CD "Signing Key 005"
232
//
233
// -----BEGIN CERTIFICATE-----
234
// MIICBzCCAa2gAwIBAgIHY3NhY2RrNTAKBggqhkjOPQQDAjBSMQwwCgYDVQQKDAND
235
// U0ExLDAqBgNVBAMMI01hdHRlciBDZXJ0aWZpY2F0aW9uIGFuZCBUZXN0aW5nIENB
236
// MRQwEgYKKwYBBAGConwCAQwEQzVBMDAgFw0yMjEwMDMxOTQ3MTVaGA8yMDcyMDky
237
// MDE5NDcxNVowWDEMMAoGA1UECgwDQ1NBMTIwMAYDVQQDDClDZXJ0aWZpY2F0aW9u
238
// IERlY2xhcmF0aW9uIFNpZ25pbmcgS2V5IDAwNTEUMBIGCisGAQQBgqJ8AgEMBEM1
239
// QTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARDilLGYqKm1yZH+V63UxNu5K4P
240
// 2zqpwWkxQms9CGf5EDrn16G4h+n4E6byb3a7zak1k3h8EneMqPKXXcRaIEL5o2Yw
241
// ZDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQU
242
// Y38mNK1i6v5q9mLvuW9v0vy//C8wHwYDVR0jBBgwFoAUl+Rp0MUEFMJvxwH3fpR3
243
// OQmN9qUwCgYIKoZIzj0EAwIDSAAwRQIhAM1HQpvkHKxLJByWaSYAPRZgh3Bis18W
244
// AViq7c/mtzEAAiBZO0lVe6Qo9iQPIBWZaVx/S/YSNO9uKNa/pvFu3V+nIg==
245
// -----END CERTIFICATE-----
246
//
247
constexpr uint8_t gCdSigningKey005PubkeyBytes[] = {
248
    0x04, 0x43, 0x8a, 0x52, 0xc6, 0x62, 0xa2, 0xa6, 0xd7, 0x26, 0x47, 0xf9, 0x5e, 0xb7, 0x53, 0x13, 0x6e,
249
    0xe4, 0xae, 0x0f, 0xdb, 0x3a, 0xa9, 0xc1, 0x69, 0x31, 0x42, 0x6b, 0x3d, 0x08, 0x67, 0xf9, 0x10, 0x3a,
250
    0xe7, 0xd7, 0xa1, 0xb8, 0x87, 0xe9, 0xf8, 0x13, 0xa6, 0xf2, 0x6f, 0x76, 0xbb, 0xcd, 0xa9, 0x35, 0x93,
251
    0x78, 0x7c, 0x12, 0x77, 0x8c, 0xa8, 0xf2, 0x97, 0x5d, 0xc4, 0x5a, 0x20, 0x42, 0xf9,
252
};
253
254
constexpr uint8_t gCdSigningKey005Kid[] = {
255
    0x63, 0x7F, 0x26, 0x34, 0xAD, 0x62, 0xEA, 0xFE, 0x6A, 0xF6, 0x62, 0xEF, 0xB9, 0x6F, 0x6F, 0xD2, 0xFC, 0xBF, 0xFC, 0x2F,
256
};
257
258
struct MatterCDSigningKey
259
{
260
    const CertificateKeyId mKid;
261
    const P256PublicKeySpan mPubkey;
262
};
263
264
constexpr std::array<MatterCDSigningKey, 6> gCdSigningKeys = { {
265
    { FixedByteSpan<20>{ gTestCdPubkeyKid }, FixedByteSpan<65>{ gTestCdPubkeyBytes } },
266
    { FixedByteSpan<20>{ gCdSigningKey001Kid }, FixedByteSpan<65>{ gCdSigningKey001PubkeyBytes } },
267
    { FixedByteSpan<20>{ gCdSigningKey002Kid }, FixedByteSpan<65>{ gCdSigningKey002PubkeyBytes } },
268
    { FixedByteSpan<20>{ gCdSigningKey003Kid }, FixedByteSpan<65>{ gCdSigningKey003PubkeyBytes } },
269
    { FixedByteSpan<20>{ gCdSigningKey004Kid }, FixedByteSpan<65>{ gCdSigningKey004PubkeyBytes } },
270
    { FixedByteSpan<20>{ gCdSigningKey005Kid }, FixedByteSpan<65>{ gCdSigningKey005PubkeyBytes } },
271
} };
272
273
struct TestAttestationTrustStore final : public ArrayAttestationTrustStore
274
{
275
0
    TestAttestationTrustStore() : ArrayAttestationTrustStore(GetTestPaaRootStore().data(), GetTestPaaRootStore().size()) {}
276
};
277
Global<TestAttestationTrustStore> gTestAttestationTrustStore;
278
279
AttestationVerificationResult MapError(CertificateChainValidationResult certificateChainValidationResult)
280
0
{
281
0
    switch (certificateChainValidationResult)
282
0
    {
283
0
    case CertificateChainValidationResult::kRootFormatInvalid:
284
0
        return AttestationVerificationResult::kPaaFormatInvalid;
285
286
0
    case CertificateChainValidationResult::kRootArgumentInvalid:
287
0
        return AttestationVerificationResult::kPaaArgumentInvalid;
288
289
0
    case CertificateChainValidationResult::kICAFormatInvalid:
290
0
        return AttestationVerificationResult::kPaiFormatInvalid;
291
292
0
    case CertificateChainValidationResult::kICAArgumentInvalid:
293
0
        return AttestationVerificationResult::kPaiArgumentInvalid;
294
295
0
    case CertificateChainValidationResult::kLeafFormatInvalid:
296
0
        return AttestationVerificationResult::kDacFormatInvalid;
297
298
0
    case CertificateChainValidationResult::kLeafArgumentInvalid:
299
0
        return AttestationVerificationResult::kDacArgumentInvalid;
300
301
0
    case CertificateChainValidationResult::kChainInvalid:
302
0
        return AttestationVerificationResult::kDacSignatureInvalid;
303
304
0
    case CertificateChainValidationResult::kNoMemory:
305
0
        return AttestationVerificationResult::kNoMemory;
306
307
0
    case CertificateChainValidationResult::kInternalFrameworkError:
308
0
        return AttestationVerificationResult::kInternalError;
309
310
0
    default:
311
0
        return AttestationVerificationResult::kInternalError;
312
0
    }
313
0
}
314
315
bool SupportsAttestationVerificationProfile(DeviceAttestationCertProfile profile)
316
0
{
317
0
    switch (profile)
318
0
    {
319
0
    case DeviceAttestationCertProfile::kEcdsaMatterLegacy:
320
0
        return true;
321
0
    case DeviceAttestationCertProfile::kMlDsa44:
322
0
        return Crypto::IsMlDsa44Supported();
323
0
    case DeviceAttestationCertProfile::kMlDsa65:
324
0
        return Crypto::IsMlDsa65Supported();
325
0
    case DeviceAttestationCertProfile::kUnknownEnumValue:
326
0
        return false;
327
0
    }
328
329
0
    return false;
330
0
}
331
332
// CertificateType class doesn't work since it doesn't encode PAA.
333
enum class AttestationChainElement : uint8_t
334
{
335
    kPAA = 0,
336
    kPAI = 1,
337
    kDAC = 2
338
};
339
340
enum class KeyIdType : uint8_t
341
{
342
    kAuthorityKeyId = 0,
343
    kSubjectKeyId   = 1,
344
};
345
346
#if CHIP_PROGRESS_LOGGING
347
const char * CertTypeAsString(AttestationChainElement certType)
348
0
{
349
0
    switch (certType)
350
0
    {
351
0
    case AttestationChainElement::kPAA:
352
0
        return "PAA";
353
0
    case AttestationChainElement::kPAI:
354
0
        return "PAI";
355
0
    case AttestationChainElement::kDAC:
356
0
        return "DAC";
357
0
    default:
358
0
        break;
359
0
    }
360
0
    return "<UNKNOWN>";
361
0
}
362
#endif // CHIP_PROGRESS_LOGGING
363
364
void LogOneKeyId(KeyIdType keyIdType, AttestationChainElement certType, ByteSpan derBuffer)
365
0
{
366
0
#if CHIP_PROGRESS_LOGGING
367
0
    const char * certTypeName = CertTypeAsString(certType);
368
369
0
    uint8_t keyIdBuf[Crypto::kAuthorityKeyIdentifierLength]; // Big enough for SKID/AKID.
370
0
    MutableByteSpan keyIdSpan{ keyIdBuf };
371
0
    CHIP_ERROR err = CHIP_NO_ERROR;
372
373
0
    const char * keyIdTypeString = "<UNKNOWN>";
374
0
    switch (keyIdType)
375
0
    {
376
0
    case KeyIdType::kAuthorityKeyId:
377
0
        err             = ExtractAKIDFromX509Cert(derBuffer, keyIdSpan);
378
0
        keyIdTypeString = "AKID";
379
0
        break;
380
0
    case KeyIdType::kSubjectKeyId:
381
0
        err             = ExtractSKIDFromX509Cert(derBuffer, keyIdSpan);
382
0
        keyIdTypeString = "SKID";
383
0
        break;
384
0
    default:
385
0
        err = CHIP_ERROR_INTERNAL;
386
0
        break;
387
0
    }
388
389
0
    if (err != CHIP_NO_ERROR)
390
0
    {
391
0
        ChipLogError(NotSpecified, "Failed to extract %s from %s: %" CHIP_ERROR_FORMAT, keyIdTypeString, certTypeName,
392
0
                     err.Format());
393
0
        return;
394
0
    }
395
396
0
    KeyIdStringifier KeyIdStringifier;
397
0
    const char * keyIdString = KeyIdStringifier.KeyIdToHex(keyIdSpan);
398
399
0
    ChipLogProgress(NotSpecified, "--> %s certificate %s: %s", certTypeName, keyIdTypeString, keyIdString);
400
#else
401
    IgnoreUnusedVariable(keyIdType);
402
    IgnoreUnusedVariable(certType);
403
    IgnoreUnusedVariable(derBuffer);
404
#endif // CHIP_PROGRESS_LOGGING
405
0
}
406
407
void LogCertificateAsPem(AttestationChainElement element, ByteSpan derBuffer)
408
0
{
409
0
#if CHIP_PROGRESS_LOGGING
410
0
    ChipLogProgress(NotSpecified, "==== %s certificate considered (%u bytes) ====", CertTypeAsString(element),
411
0
                    static_cast<unsigned>(derBuffer.size()));
412
0
    PemEncoder encoder("CERTIFICATE", derBuffer);
413
0
    for (const char * pemLine = encoder.NextLine(); pemLine != nullptr; pemLine = encoder.NextLine())
414
0
    {
415
0
        ChipLogProgress(NotSpecified, "%s", pemLine);
416
0
    }
417
#else
418
    IgnoreUnusedVariable(element);
419
    IgnoreUnusedVariable(derBuffer);
420
#endif // CHIP_PROGRESS_LOGGING
421
0
}
422
423
void LogCertDebugData(AttestationChainElement element, ByteSpan derBuffer)
424
0
{
425
0
    LogCertificateAsPem(element, derBuffer);
426
0
    LogOneKeyId(KeyIdType::kSubjectKeyId, element, derBuffer);
427
0
    LogOneKeyId(KeyIdType::kAuthorityKeyId, element, derBuffer);
428
0
}
429
430
#if CHIP_PROGRESS_LOGGING
431
const char * CertificationTypeAsString(CertificationType certificationType)
432
0
{
433
0
    switch (certificationType)
434
0
    {
435
0
    case CertificationType::kDevelopmentAndTest:
436
0
        return "Development and testing";
437
0
    case CertificationType::kProvisional:
438
0
        return "Provisional certification";
439
0
    case CertificationType::kOfficial:
440
0
        return "Certified device";
441
0
    default:
442
0
        break;
443
0
    }
444
0
    return "<UNKNOWN>";
445
0
}
446
#endif
447
448
} // namespace
449
450
CHIP_ERROR DefaultDACVerifier::GetPaaCertificateAllocationSize(ByteSpan paiDer, size_t & allocationSize)
451
0
{
452
    // The PAI request profile describes its subject key, whereas its signature identifies
453
    // the PAA algorithm. A P-256 PAI can therefore require an ML-DSA PAA buffer.
454
0
    using namespace ASN1;
455
0
    ASN1Reader reader;
456
0
    reader.Init(paiDer);
457
0
    ReturnErrorOnFailure(reader.Next());
458
0
    VerifyOrReturnError(reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Sequence,
459
0
                        CHIP_ERROR_INVALID_ARGUMENT);
460
0
    ReturnErrorOnFailure(reader.EnterConstructedType());
461
0
    ReturnErrorOnFailure(reader.Next()); // TBSCertificate
462
0
    VerifyOrReturnError(reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Sequence,
463
0
                        CHIP_ERROR_INVALID_ARGUMENT);
464
0
    ReturnErrorOnFailure(reader.Next()); // signatureAlgorithm
465
0
    VerifyOrReturnError(reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Sequence,
466
0
                        CHIP_ERROR_INVALID_ARGUMENT);
467
0
    ReturnErrorOnFailure(reader.EnterConstructedType());
468
0
    ReturnErrorOnFailure(reader.Next());
469
0
    VerifyOrReturnError(reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_ObjectId,
470
0
                        CHIP_ERROR_INVALID_ARGUMENT);
471
472
    // DER OID values for ecdsa-with-SHA256, id-ml-dsa-44, and id-ml-dsa-65.
473
0
    constexpr uint8_t kEcdsaSha256[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 };
474
0
    constexpr uint8_t kMlDsa44[]     = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x11 };
475
0
    constexpr uint8_t kMlDsa65[]     = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x12 };
476
0
    const ByteSpan signatureOid(reader.GetValue(), reader.GetValueLen());
477
0
    if (signatureOid.data_equal(ByteSpan(kEcdsaSha256)))
478
0
    {
479
0
        allocationSize = kMaxDERCertLength;
480
0
        return CHIP_NO_ERROR;
481
0
    }
482
0
    if (signatureOid.data_equal(ByteSpan(kMlDsa44)))
483
0
    {
484
0
        allocationSize = kMaxDERCertLengthMlDsa44;
485
0
        return Crypto::IsMlDsa44Supported() ? CHIP_NO_ERROR : CHIP_ERROR_NOT_IMPLEMENTED;
486
0
    }
487
0
    if (signatureOid.data_equal(ByteSpan(kMlDsa65)))
488
0
    {
489
0
        allocationSize = kMaxDERCertLengthMlDsa65;
490
0
        return Crypto::IsMlDsa65Supported() ? CHIP_NO_ERROR : CHIP_ERROR_NOT_IMPLEMENTED;
491
0
    }
492
0
    return CHIP_ERROR_UNSUPPORTED_CERT_FORMAT;
493
0
}
494
495
void DefaultDACVerifier::VerifyAttestationInformation(const DeviceAttestationVerifier::AttestationInfo & info,
496
                                                      Callback::Callback<OnAttestationInformationVerification> * onCompletion)
497
0
{
498
    // The exit handler below dereferences onCompletion unconditionally; reject null here.
499
0
    VerifyOrReturn(onCompletion != nullptr);
500
501
0
    AttestationVerificationResult attestationError = AttestationVerificationResult::kSuccess;
502
503
0
    Platform::ScopedMemoryBuffer<uint8_t> paaCert;
504
0
    MutableByteSpan paaDerBuffer;
505
0
    AttestationCertVidPid dacVidPid;
506
0
    AttestationCertVidPid paiVidPid;
507
0
    AttestationCertVidPid paaVidPid;
508
0
    size_t paaCertAllocatedLen = 0;
509
510
0
    VerifyOrExit(!info.attestationElementsBuffer.empty() && !info.attestationChallengeBuffer.empty() &&
511
0
                     !info.attestationSignatureBuffer.empty() && !info.dacDerBuffer.empty() && !info.attestationNonceBuffer.empty(),
512
0
                 attestationError = AttestationVerificationResult::kInvalidArgument);
513
514
0
    VerifyOrExit(info.attestationElementsBuffer.size() <= kMaxResponseLength,
515
0
                 attestationError = AttestationVerificationResult::kInvalidArgument);
516
517
0
    if (!SupportsAttestationVerificationProfile(info.attestationProfile))
518
0
    {
519
0
        ChipLogError(NotSpecified, "PQC device attestation verification is not implemented for requested profile %u",
520
0
                     to_underlying(info.attestationProfile));
521
0
        attestationError = AttestationVerificationResult::kNotImplemented;
522
0
        ExitNow();
523
0
    }
524
525
    // Ensure PAI is present
526
0
    VerifyOrExit(!info.paiDerBuffer.empty(), attestationError = AttestationVerificationResult::kPaiMissing);
527
528
    // This is an allocation/capability hint only; certificate validation below still
529
    // checks the PAI signature against the trusted PAA.
530
0
    {
531
0
        const CHIP_ERROR err = GetPaaCertificateAllocationSize(info.paiDerBuffer, paaCertAllocatedLen);
532
0
        VerifyOrExit(err == CHIP_NO_ERROR,
533
0
                     attestationError = err == CHIP_ERROR_NOT_IMPLEMENTED ? AttestationVerificationResult::kNotImplemented
534
0
                                                                          : AttestationVerificationResult::kPaiFormatInvalid);
535
0
    }
536
537
    // Validate Proper Certificate Format
538
0
    {
539
0
        VerifyOrExit(VerifyAttestationCertificateFormat(info.paiDerBuffer, AttestationCertType::kPAI) == CHIP_NO_ERROR,
540
0
                     attestationError = AttestationVerificationResult::kPaiFormatInvalid);
541
0
        VerifyOrExit(VerifyAttestationCertificateFormat(info.dacDerBuffer, AttestationCertType::kDAC) == CHIP_NO_ERROR,
542
0
                     attestationError = AttestationVerificationResult::kDacFormatInvalid);
543
0
    }
544
545
    // match DAC and PAI VIDs
546
0
    {
547
0
        VerifyOrExit(ExtractVIDPIDFromX509Cert(info.dacDerBuffer, dacVidPid) == CHIP_NO_ERROR,
548
0
                     attestationError = AttestationVerificationResult::kDacFormatInvalid);
549
0
        VerifyOrExit(ExtractVIDPIDFromX509Cert(info.paiDerBuffer, paiVidPid) == CHIP_NO_ERROR,
550
0
                     attestationError = AttestationVerificationResult::kPaiFormatInvalid);
551
0
        VerifyOrExit(paiVidPid.mVendorId.HasValue() && paiVidPid.mVendorId == dacVidPid.mVendorId,
552
0
                     attestationError = AttestationVerificationResult::kDacVendorIdMismatch);
553
0
        VerifyOrExit(dacVidPid.mProductId.HasValue(), attestationError = AttestationVerificationResult::kDacProductIdMismatch);
554
0
        if (paiVidPid.mProductId.HasValue())
555
0
        {
556
0
            VerifyOrExit(paiVidPid.mProductId == dacVidPid.mProductId,
557
0
                         attestationError = AttestationVerificationResult::kDacProductIdMismatch);
558
0
        }
559
0
    }
560
561
    // Log info about the DAC chain so far (DAC, PAI).
562
0
    if (AreVerboseLogsEnabled())
563
0
    {
564
0
        ChipLogProgress(NotSpecified, "Device candidate DAC chain details:");
565
0
        ChipLogProgress(NotSpecified, "--> DAC's VID: 0x%04X, PID: 0x%04X", dacVidPid.mVendorId.Value(),
566
0
                        dacVidPid.mProductId.Value());
567
568
0
        LogCertDebugData(AttestationChainElement::kDAC, info.dacDerBuffer);
569
0
        LogCertDebugData(AttestationChainElement::kPAI, info.paiDerBuffer);
570
0
    }
571
572
    // Validate overall attestation signature on attestation information.
573
0
    {
574
0
        P256PublicKey remoteManufacturerPubkey;
575
0
        P256ECDSASignature deviceSignature;
576
577
0
        VerifyOrExit(ExtractPubkeyFromX509Cert(info.dacDerBuffer, remoteManufacturerPubkey) == CHIP_NO_ERROR,
578
0
                     attestationError = AttestationVerificationResult::kDacFormatInvalid);
579
580
        // SetLength will fail if signature doesn't fit
581
0
        VerifyOrExit(deviceSignature.SetLength(info.attestationSignatureBuffer.size()) == CHIP_NO_ERROR,
582
0
                     attestationError = AttestationVerificationResult::kAttestationSignatureInvalidFormat);
583
0
        memcpy(deviceSignature.Bytes(), info.attestationSignatureBuffer.data(), info.attestationSignatureBuffer.size());
584
0
        VerifyOrExit(ValidateAttestationSignature(remoteManufacturerPubkey, info.attestationElementsBuffer,
585
0
                                                  info.attestationChallengeBuffer, deviceSignature) == CHIP_NO_ERROR,
586
0
                     attestationError = AttestationVerificationResult::kAttestationSignatureInvalid);
587
0
    }
588
589
    // Find PAA and validate it.
590
0
    {
591
0
        uint8_t paiAkidBuf[Crypto::kAuthorityKeyIdentifierLength];
592
0
        MutableByteSpan paiAkid(paiAkidBuf);
593
0
        CHIP_ERROR err = CHIP_NO_ERROR;
594
595
0
        VerifyOrExit(ExtractAKIDFromX509Cert(info.paiDerBuffer, paiAkid) == CHIP_NO_ERROR,
596
0
                     attestationError = AttestationVerificationResult::kPaiFormatInvalid);
597
598
0
        VerifyOrExit(paaCert.Alloc(paaCertAllocatedLen), attestationError = AttestationVerificationResult::kNoMemory);
599
600
0
        paaDerBuffer = MutableByteSpan(paaCert.Get(), paaCertAllocatedLen);
601
0
        err          = mAttestationTrustStore->GetProductAttestationAuthorityCert(paiAkid, paaDerBuffer);
602
0
        if (err == CHIP_ERROR_NOT_IMPLEMENTED)
603
0
        {
604
0
            err = gTestAttestationTrustStore->GetProductAttestationAuthorityCert(paiAkid, paaDerBuffer);
605
0
        }
606
607
0
        if (err != CHIP_NO_ERROR)
608
0
        {
609
0
            attestationError = AttestationVerificationResult::kPaaNotFound;
610
0
#if CHIP_ERROR_LOGGING
611
0
            KeyIdStringifier paiAkidWriter;
612
0
            [[maybe_unused]] const char * paiAkidHexString = paiAkidWriter.KeyIdToHex(paiAkid);
613
614
0
            ChipLogError(NotSpecified, "Unable to find PAA, err: %" CHIP_ERROR_FORMAT ", PAI's AKID: %s", err.Format(),
615
0
                         paiAkidHexString);
616
0
#endif // CHIP_ERROR_LOGGING
617
0
            ExitNow();
618
0
        }
619
620
0
        if (AreVerboseLogsEnabled())
621
0
        {
622
            // PAA details following the DAC/PAI above.
623
0
            LogCertDebugData(AttestationChainElement::kPAA, paaDerBuffer);
624
0
        }
625
626
0
        VerifyOrExit(ExtractVIDPIDFromX509Cert(paaDerBuffer, paaVidPid) == CHIP_NO_ERROR,
627
0
                     attestationError = AttestationVerificationResult::kPaaFormatInvalid);
628
629
0
        if (paaVidPid.mVendorId.HasValue())
630
0
        {
631
0
            VerifyOrExit(paaVidPid.mVendorId == paiVidPid.mVendorId,
632
0
                         attestationError = AttestationVerificationResult::kPaiVendorIdMismatch);
633
0
        }
634
635
0
        VerifyOrExit(!paaVidPid.mProductId.HasValue(), attestationError = AttestationVerificationResult::kPaaFormatInvalid);
636
0
    }
637
638
0
#if !defined(CURRENT_TIME_NOT_IMPLEMENTED)
639
0
    VerifyOrExit(IsCertificateValidAtCurrentTime(info.dacDerBuffer) == CHIP_NO_ERROR,
640
0
                 attestationError = AttestationVerificationResult::kDacExpired);
641
0
#endif
642
643
0
    CertificateChainValidationResult chainValidationResult;
644
0
    VerifyOrExit(ValidateCertificateChain(paaDerBuffer.data(), paaDerBuffer.size(), info.paiDerBuffer.data(),
645
0
                                          info.paiDerBuffer.size(), info.dacDerBuffer.data(), info.dacDerBuffer.size(),
646
0
                                          chainValidationResult) == CHIP_NO_ERROR,
647
0
                 attestationError = MapError(chainValidationResult));
648
649
0
    {
650
0
        ByteSpan certificationDeclarationSpan;
651
0
        ByteSpan attestationNonceSpan;
652
0
        uint32_t timestampDeconstructed;
653
0
        ByteSpan firmwareInfoSpan;
654
0
        DeviceAttestationVendorReservedDeconstructor vendorReserved;
655
0
        ByteSpan certificationDeclarationPayload;
656
657
0
        DeviceInfoForAttestation deviceInfo{
658
0
            .vendorId     = info.vendorId,
659
0
            .productId    = info.productId,
660
0
            .dacVendorId  = dacVidPid.mVendorId.Value(),
661
0
            .dacProductId = dacVidPid.mProductId.Value(),
662
0
            .paiVendorId  = paiVidPid.mVendorId.Value(),
663
0
            .paiProductId = paiVidPid.mProductId.ValueOr(0),
664
0
            .paaVendorId  = paaVidPid.mVendorId.ValueOr(VendorId::NotSpecified),
665
0
        };
666
667
0
        MutableByteSpan paaSKID(deviceInfo.paaSKID);
668
0
        VerifyOrExit(ExtractSKIDFromX509Cert(paaDerBuffer, paaSKID) == CHIP_NO_ERROR,
669
0
                     attestationError = AttestationVerificationResult::kPaaFormatInvalid);
670
0
        VerifyOrExit(paaSKID.size() == sizeof(deviceInfo.paaSKID),
671
0
                     attestationError = AttestationVerificationResult::kPaaFormatInvalid);
672
673
0
        VerifyOrExit(DeconstructAttestationElements(info.attestationElementsBuffer, certificationDeclarationSpan,
674
0
                                                    attestationNonceSpan, timestampDeconstructed, firmwareInfoSpan,
675
0
                                                    vendorReserved) == CHIP_NO_ERROR,
676
0
                     attestationError = AttestationVerificationResult::kAttestationElementsMalformed);
677
678
        // Verify that Nonce matches with what we sent
679
0
        VerifyOrExit(attestationNonceSpan.data_equal(info.attestationNonceBuffer),
680
0
                     attestationError = AttestationVerificationResult::kAttestationNonceMismatch);
681
682
0
        attestationError = ValidateCertificationDeclarationSignature(certificationDeclarationSpan, certificationDeclarationPayload);
683
0
        VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);
684
685
0
        attestationError = ValidateCertificateDeclarationPayload(certificationDeclarationPayload, firmwareInfoSpan, deviceInfo);
686
0
        VerifyOrExit(attestationError == AttestationVerificationResult::kSuccess, attestationError = attestationError);
687
0
    }
688
689
0
exit:
690
0
    onCompletion->mCall(onCompletion->mContext, info, attestationError);
691
0
}
692
693
AttestationVerificationResult DefaultDACVerifier::ValidateCertificationDeclarationSignature(const ByteSpan & cmsEnvelopeBuffer,
694
                                                                                            ByteSpan & certDeclBuffer)
695
0
{
696
0
    ByteSpan kid;
697
0
    VerifyOrReturnError(CMS_ExtractKeyId(cmsEnvelopeBuffer, kid) == CHIP_NO_ERROR,
698
0
                        AttestationVerificationResult::kCertificationDeclarationNoKeyId);
699
700
0
    Crypto::P256PublicKey verifyingKey;
701
0
    CHIP_ERROR err = mCdKeysTrustStore.LookupVerifyingKey(kid, verifyingKey);
702
0
    VerifyOrReturnError(err == CHIP_NO_ERROR, AttestationVerificationResult::kCertificationDeclarationNoCertificateFound);
703
704
0
#if CHIP_PROGRESS_LOGGING
705
0
    if (AreVerboseLogsEnabled())
706
0
    {
707
0
        KeyIdStringifier cdAkidWriter;
708
0
        const char * cdAkidHexString = cdAkidWriter.KeyIdToHex(kid);
709
0
        ChipLogProgress(NotSpecified, "CD signing key identifier: %s", cdAkidHexString);
710
0
    }
711
0
#endif
712
713
0
    if (mCdKeysTrustStore.IsCdTestKey(kid))
714
0
    {
715
        // Disallow test key if support not enabled
716
0
        if (!IsCdTestKeySupported())
717
0
        {
718
0
            ChipLogError(NotSpecified, "Disallowing CD signed by test key");
719
0
            return AttestationVerificationResult::kCertificationDeclarationNoCertificateFound;
720
0
        }
721
0
        ChipLogProgress(NotSpecified, "Allowing CD signed by test key");
722
0
    }
723
724
0
    VerifyOrReturnError(CMS_Verify(cmsEnvelopeBuffer, verifyingKey, certDeclBuffer) == CHIP_NO_ERROR,
725
0
                        AttestationVerificationResult::kCertificationDeclarationInvalidSignature);
726
727
    // certDeclBuffer is populated by CMS_Verify so we need to do this check after the signature check
728
0
    CertificationElementsWithoutPIDs cdContent;
729
0
    VerifyOrReturnError(DecodeCertificationElements(certDeclBuffer, cdContent) == CHIP_NO_ERROR,
730
0
                        AttestationVerificationResult::kCertificationDeclarationInvalidFormat);
731
    // Ensure we didn't use a test key for official or provisional certificates (if disallowed)
732
0
    bool testKeyAllowedForCertificationType =
733
0
        (cdContent.certificationType == to_underlying(CertificationType::kDevelopmentAndTest)) ||
734
0
        (cdContent.certificationType == to_underlying(CertificationType::kProvisional) && kEnableCdTestKeysForProvisionalCds);
735
0
    ChipLogProgress(NotSpecified, "Certification type %u", cdContent.certificationType);
736
0
    if (mCdKeysTrustStore.IsCdTestKey(kid) && !testKeyAllowedForCertificationType)
737
0
    {
738
0
        ChipLogError(NotSpecified, "Test key is disallowed for this certification type: %u", cdContent.certificationType);
739
0
        return AttestationVerificationResult::kCertificationDeclarationNoCertificateFound;
740
0
    }
741
742
0
    return AttestationVerificationResult::kSuccess;
743
0
}
744
745
AttestationVerificationResult DefaultDACVerifier::ValidateCertificateDeclarationPayload(const ByteSpan & certDeclBuffer,
746
                                                                                        const ByteSpan & firmwareInfo,
747
                                                                                        const DeviceInfoForAttestation & deviceInfo)
748
0
{
749
0
    CertificationElementsWithoutPIDs cdContent;
750
0
    CertificationElementsDecoder cdElementsDecoder;
751
0
    VerifyOrReturnError(DecodeCertificationElements(certDeclBuffer, cdContent) == CHIP_NO_ERROR,
752
0
                        AttestationVerificationResult::kCertificationDeclarationInvalidFormat);
753
754
0
    if (!firmwareInfo.empty())
755
0
    {
756
        // TODO: validate contents based on DCL
757
0
    }
758
759
    // Verify the cd elements are as required by the spec
760
    // security_level, security_information are meant to be ignored. version_number is not meant to be interpreted by the
761
    // commissioners.
762
0
    if (cdContent.formatVersion != 1 || cdContent.certificationType >= chip::to_underlying(CertificationType::kReserved))
763
0
    {
764
0
        return AttestationVerificationResult::kAttestationElementsMalformed;
765
0
    }
766
767
    // The vendor_id field in the Certification Declaration SHALL match the VendorID attribute found in the Basic Information
768
    // cluster
769
0
    VerifyOrReturnError(cdContent.vendorId == deviceInfo.vendorId,
770
0
                        AttestationVerificationResult::kCertificationDeclarationInvalidVendorId);
771
772
    //  The product_id_array field in the Certification Declaration SHALL contain the value of the ProductID attribute found in
773
    //  the Basic Information cluster.
774
0
    VerifyOrReturnError(cdElementsDecoder.IsProductIdIn(certDeclBuffer, deviceInfo.productId),
775
0
                        AttestationVerificationResult::kCertificationDeclarationInvalidProductId);
776
777
0
    if (cdContent.dacOriginVIDandPIDPresent)
778
0
    {
779
        // The Vendor ID (VID) subject DN in the DAC SHALL match the dac_origin_vendor_id field in the Certification
780
        // Declaration.
781
0
        VerifyOrReturnError(deviceInfo.dacVendorId == cdContent.dacOriginVendorId,
782
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidVendorId);
783
        // The Vendor ID (VID) subject DN in the PAI SHALL match the dac_origin_vendor_id field in the Certification
784
        // Declaration.
785
0
        VerifyOrReturnError(deviceInfo.paiVendorId == cdContent.dacOriginVendorId,
786
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidVendorId);
787
        // The Product ID (PID) subject DN in the DAC SHALL match the dac_origin_product_id field in the Certification
788
        // Declaration.
789
0
        VerifyOrReturnError(deviceInfo.dacProductId == cdContent.dacOriginProductId,
790
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidProductId);
791
        // The Product ID (PID) subject DN in the PAI, if such a Product ID is present, SHALL match the dac_origin_product_id
792
        // field in the Certification Declaration.
793
0
        if (deviceInfo.paiProductId != 0) // if PAI PID is present
794
0
        {
795
0
            VerifyOrReturnError(deviceInfo.paiProductId == cdContent.dacOriginProductId,
796
0
                                AttestationVerificationResult::kCertificationDeclarationInvalidProductId);
797
0
        }
798
0
    }
799
0
    else
800
0
    {
801
        //  The Vendor ID (VID) subject DN in the DAC SHALL match the vendor_id field in the Certification Declaration
802
0
        VerifyOrReturnError(deviceInfo.dacVendorId == cdContent.vendorId,
803
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidVendorId);
804
        // The Vendor ID (VID) subject DN in the PAI SHALL match the vendor_id field in the Certification Declaration.
805
0
        VerifyOrReturnError(deviceInfo.paiVendorId == cdContent.vendorId,
806
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidVendorId);
807
        // The Product ID (PID) subject DN in the DAC SHALL be present in the product_id_array field in the Certification
808
        // Declaration.
809
0
        VerifyOrReturnError(cdElementsDecoder.IsProductIdIn(certDeclBuffer, deviceInfo.dacProductId),
810
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidProductId);
811
        // The Product ID (PID) subject DN in the PAI, if such a Product ID is present, SHALL match one of the values present in
812
        // the product_id_array field in the Certification Declaration.
813
0
        if (deviceInfo.paiProductId != 0) // if PAI PID is present
814
0
        {
815
0
            VerifyOrReturnError(cdElementsDecoder.IsProductIdIn(certDeclBuffer, deviceInfo.paiProductId),
816
0
                                AttestationVerificationResult::kCertificationDeclarationInvalidProductId);
817
0
        }
818
0
    }
819
820
0
    if (cdContent.authorizedPAAListPresent)
821
0
    {
822
        // The Subject Key Id of the PAA SHALL match one of the values present in the authorized_paa_list
823
        // in the Certification Declaration.
824
0
        VerifyOrReturnError(cdElementsDecoder.HasAuthorizedPAA(certDeclBuffer, ByteSpan(deviceInfo.paaSKID)),
825
0
                            AttestationVerificationResult::kCertificationDeclarationInvalidPAA);
826
0
    }
827
828
0
    if (AreVerboseLogsEnabled())
829
0
    {
830
0
        ChipLogProgress(NotSpecified, "Device certification declaration details:");
831
0
        ChipLogProgress(NotSpecified, "--> VID: 0x%04X", cdContent.vendorId);
832
        // TODO (https://github.com/project-chip/connectedhomeip/issues/39714): Figure out how to
833
        // log the product_id_array, which is not in cdContent.
834
0
        ChipLogProgress(NotSpecified, "--> Device type ID: " ChipLogFormatMEI, ChipLogValueMEI(cdContent.deviceTypeId));
835
0
        ChipLogProgress(NotSpecified, "--> Certification type: %d (%s)", cdContent.certificationType,
836
0
                        CertificationTypeAsString(static_cast<CertificationType>(cdContent.certificationType)));
837
0
        if (cdContent.dacOriginVIDandPIDPresent)
838
0
        {
839
0
            ChipLogProgress(NotSpecified, "--> DAC origin VID: 0x%04X, PID: 0x%04X", cdContent.dacOriginVendorId,
840
0
                            cdContent.dacOriginProductId);
841
0
        }
842
0
    }
843
844
0
    return AttestationVerificationResult::kSuccess;
845
0
}
846
847
CHIP_ERROR DefaultDACVerifier::VerifyNodeOperationalCSRInformation(const ByteSpan & nocsrElementsBuffer,
848
                                                                   const ByteSpan & attestationChallengeBuffer,
849
                                                                   const ByteSpan & attestationSignatureBuffer,
850
                                                                   const P256PublicKey & dacPublicKey, const ByteSpan & csrNonce)
851
0
{
852
0
    VerifyOrReturnError(!nocsrElementsBuffer.empty() && !attestationChallengeBuffer.empty() &&
853
0
                            !attestationSignatureBuffer.empty() && !csrNonce.empty(),
854
0
                        CHIP_ERROR_INVALID_ARGUMENT);
855
856
0
    VerifyOrReturnError(nocsrElementsBuffer.size() <= kMaxResponseLength, CHIP_ERROR_INVALID_ARGUMENT);
857
0
    VerifyOrReturnError(csrNonce.size() == Controller::kCSRNonceLength, CHIP_ERROR_INVALID_ARGUMENT);
858
859
0
    ByteSpan csrSpan;
860
0
    ByteSpan csrNonceSpan;
861
0
    ByteSpan vendorReserved1Span;
862
0
    ByteSpan vendorReserved2Span;
863
0
    ByteSpan vendorReserved3Span;
864
0
    ReturnErrorOnFailure(DeconstructNOCSRElements(nocsrElementsBuffer, csrSpan, csrNonceSpan, vendorReserved1Span,
865
0
                                                  vendorReserved2Span, vendorReserved3Span));
866
867
0
    VerifyOrReturnError(csrNonceSpan.size() == Controller::kCSRNonceLength, CHIP_ERROR_INVALID_ARGUMENT);
868
869
    // Verify that Nonce matches with what we sent
870
0
    VerifyOrReturnError(csrNonceSpan.data_equal(csrNonce), CHIP_ERROR_INVALID_ARGUMENT);
871
872
    // Validate overall attestation signature on attestation information
873
0
    P256ECDSASignature signature;
874
    // SetLength will fail if signature doesn't fit
875
0
    ReturnErrorOnFailure(signature.SetLength(attestationSignatureBuffer.size()));
876
0
    memcpy(signature.Bytes(), attestationSignatureBuffer.data(), attestationSignatureBuffer.size());
877
878
0
    ReturnErrorOnFailure(ValidateAttestationSignature(dacPublicKey, nocsrElementsBuffer, attestationChallengeBuffer, signature));
879
880
0
    return CHIP_NO_ERROR;
881
0
}
882
883
void DefaultDACVerifier::CheckForRevokedDACChain(const AttestationInfo & info,
884
                                                 Callback::Callback<OnAttestationInformationVerification> * onCompletion)
885
0
{
886
0
    if (mRevocationDelegate != nullptr)
887
0
    {
888
0
        mRevocationDelegate->CheckForRevokedDACChain(info, onCompletion);
889
0
    }
890
0
    else
891
0
    {
892
0
        ChipLogProgress(NotSpecified, "WARNING: No revocation delegate available. Revocation checks will be skipped!");
893
0
        onCompletion->mCall(onCompletion->mContext, info, AttestationVerificationResult::kSuccess);
894
0
    }
895
0
}
896
897
bool CsaCdKeysTrustStore::IsCdTestKey(const ByteSpan & kid) const
898
0
{
899
0
    return kid.data_equal(ByteSpan{ gTestCdPubkeyKid });
900
0
}
901
902
CHIP_ERROR CsaCdKeysTrustStore::AddTrustedKey(const ByteSpan & kid, const Crypto::P256PublicKey & pubKey)
903
0
{
904
0
    VerifyOrReturnError(kid.size() <= SingleKeyEntry::kMaxKidSize, CHIP_ERROR_INVALID_ARGUMENT);
905
0
    VerifyOrReturnError(!kid.empty(), CHIP_ERROR_INVALID_ARGUMENT);
906
0
    VerifyOrReturnError(mNumTrustedKeys != kMaxNumTrustedKeys, CHIP_ERROR_NO_MEMORY);
907
908
0
    auto & entry = mTrustedKeys[mNumTrustedKeys];
909
910
0
    entry.kidSize = kid.size();
911
0
    memcpy(&entry.kidBuffer[0], kid.data(), kid.size());
912
0
    entry.publicKey = pubKey;
913
914
0
    ++mNumTrustedKeys;
915
0
    return CHIP_NO_ERROR;
916
0
}
917
918
CHIP_ERROR CsaCdKeysTrustStore::AddTrustedKey(const ByteSpan & derCertBytes)
919
0
{
920
0
    uint8_t kidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 };
921
0
    MutableByteSpan kidSpan{ kidBuf };
922
0
    P256PublicKey pubKey;
923
924
0
    VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractSKIDFromX509Cert(derCertBytes, kidSpan), CHIP_ERROR_INVALID_ARGUMENT);
925
0
    VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractPubkeyFromX509Cert(derCertBytes, pubKey), CHIP_ERROR_INVALID_ARGUMENT);
926
927
0
    if (!IsCdTestKey(kidSpan))
928
0
    {
929
        // Verify cert against CSA CD root of trust.
930
0
        CertificateChainValidationResult chainValidationResult;
931
0
        VerifyOrReturnError(CHIP_NO_ERROR ==
932
0
                                ValidateCertificateChain(gCdRootCert, sizeof(gCdRootCert), nullptr, 0, derCertBytes.data(),
933
0
                                                         derCertBytes.size(), chainValidationResult),
934
0
                            CHIP_ERROR_INVALID_ARGUMENT);
935
0
        VerifyOrReturnError(chainValidationResult == CertificateChainValidationResult::kSuccess, CHIP_ERROR_INVALID_ARGUMENT);
936
0
    }
937
938
0
    return AddTrustedKey(kidSpan, pubKey);
939
0
}
940
941
CHIP_ERROR CsaCdKeysTrustStore::LookupVerifyingKey(const ByteSpan & kid, Crypto::P256PublicKey & outPubKey) const
942
0
{
943
    // First, search for the well known keys
944
0
    for (auto & cdSigningKey : gCdSigningKeys)
945
0
    {
946
0
        if (kid.data_equal(cdSigningKey.mKid))
947
0
        {
948
0
            outPubKey = cdSigningKey.mPubkey;
949
0
            return CHIP_NO_ERROR;
950
0
        }
951
0
    }
952
953
    // Seconds, search externally added keys
954
0
    for (size_t keyIdx = 0; keyIdx < mNumTrustedKeys; keyIdx++)
955
0
    {
956
0
        auto & entry = mTrustedKeys[keyIdx];
957
0
        if (kid.data_equal(entry.GetKid()))
958
0
        {
959
0
            outPubKey = entry.publicKey;
960
0
            return CHIP_NO_ERROR;
961
0
        }
962
0
    }
963
964
    // If we get here, the desired key was not found
965
0
    return CHIP_ERROR_KEY_NOT_FOUND;
966
0
}
967
968
const AttestationTrustStore * GetTestAttestationTrustStore()
969
0
{
970
0
    return &gTestAttestationTrustStore.get();
971
0
}
972
973
DeviceAttestationVerifier * GetDefaultDACVerifier(const AttestationTrustStore * paaRootStore,
974
                                                  DeviceAttestationRevocationDelegate * revocationDelegate)
975
0
{
976
0
    static DefaultDACVerifier defaultDACVerifier{ paaRootStore, revocationDelegate };
977
978
0
    return &defaultDACVerifier;
979
0
}
980
981
} // namespace Credentials
982
} // namespace chip