Skip to content

Instantly share code, notes, and snippets.

@oidebrett
Last active January 3, 2024 19:07
Show Gist options
  • Select an option

  • Save oidebrett/a7d6f7cf0b34d0a5617f35535cad1bee to your computer and use it in GitHub Desktop.

Select an option

Save oidebrett/a7d6f7cf0b34d0a5617f35535cad1bee to your computer and use it in GitHub Desktop.
Creating new Certificates for ESP32 matter lighting app example
cd connectedhomeip
source scripts/activate.sh
#Download any new PAA certs from the DCL
python3 -m pip install click_option_group # somehow missing from
cd credentials/development
python3 ../fetch-paa-certs-from-dcl.py --use-test-net-http
cd ../production
python3 ../fetch-paa-certs-from-dcl.py --use-main-net-http
#Pair a device with the chip-tool but specify the paa-root-certs directory
./out/chip-tool pairing ble-wifi 1 <SSID> <PASSWORD> <SETUPPIN> <DISCR> --paa-trust-store-path ./credentials/production/paa-root-certs
#Install the hexdump utility xxd
sudo apt-get install xxd
#Set up the ESP-IDF toolchain before building the ESP32 lighting app
cd path/to/esp-idf
git fetch --depth 1 origin v5.1.1 #check the latest stable version
git reset --hard FETCH_HEAD
git submodule update --depth 1 --recursive --init
# -ff is for cleaning untracked files as well as submodules
git clean -ffdx
./install.sh
source export.sh
#Building the ESP32 lighting app
cd connectedhomeip
cd examples/lighting-app/esp32
idf.py set-target (target chip)
idf.py build
idf.py -p /dev/ttyUSB0 erase-flash
idf.py -p /dev/ttyUSB0 flash monitor
#Pair the ESP32 lighting device with chip0tool
cd connectedhomeip
./out/chip-tool pairing ble-wifi 1 <SSID> <PASSWORD> 20202021 3840
#check that pairing worked by reading basic information
./out/chip-tool basicinformation read vendor-name 1 0
# We will now change the certificates for the ESP32 lighting app
Export your custom VID/PID as environment variables to decrease chances of clerical error when editing your command arguments:
export VID=hexVendorId
export PID=hexProductId
#echo ${VID} FFAA
#echo ${PID} FFA1
Generate the CD using chip-cert. Currently the Commissioner only validates that the VID and PID match the data exposed elsewhere by the device: the Basic Information Cluster, DAC and DAC origin (when it has it). You may leave the other fields unchanged:
$ ./out/chip-cert gen-cd \
--key credentials/test/certification-declaration/Chip-Test-CD-Signing-Key.pem \
--cert credentials/test/certification-declaration/Chip-Test-CD-Signing-Cert.pem \
--out credentials/test/certification-declaration/Chip-Test-CD-${VID}-${PID}.der \
--format-version "1" \
--vendor-id "${VID}" \
--product-id "${PID}" \
--device-type-id "0x1234" \
--certificate-id "ZIG20141ZB330001-24" \
--security-level "0" \
--security-info "0" \
--version-number "9876" \
--certification-type "0"
Verify the CD. Make sure it contains your VID/PID (in decimal format):
$ ./out/chip-cert print-cd credentials/test/certification-declaration/Chip-Test-CD-${VID}-${PID}.der
Example output:
SignerKeyId value: hex:62FA823359ACFAA9963E1CFA140ADDF504F37160
0x01, tag[Anonymous]: 0xffffffff, type: Structure (0x15), container:
0x04, tag[Context Specific]: 0x0, type: Unsigned Fixed Point (0x04), value: 1
0x08, tag[Context Specific]: 0x1, type: Unsigned Fixed Point (0x04), value: XXXXX // <- VID
0x0A, tag[Context Specific]: 0x2, type: Array (0x16), container:
0x0D, tag[Anonymous]: 0xffffffff, type: Unsigned Fixed Point (0x04), value: XXXXX // <- PID
0x12, tag[Context Specific]: 0x3, type: Unsigned Fixed Point (0x04), value: 4660
0x15, tag[Context Specific]: 0x4, type: UTF-8 String (0x0c), length: 19, value: "ZIG20141ZB330001-24"
0x2B, tag[Context Specific]: 0x5, type: Unsigned Fixed Point (0x04), value: 0
0x2E, tag[Context Specific]: 0x6,type: Unsigned Fixed Point (0x04), value: 0
0x32, tag[Context Specific]: 0x7, type: Unsigned Fixed Point (0x04), value: 39030
0x35, tag[Context Specific]: 0x8, type: Unsigned Fixed Point (0x04), value: 0
Generate a PAI and DAC
In this example we'll use Matter's own test Product Attestation Authority (PAA) certificate and signing key Chip-Test-PAA-NoVID as our root certificate. We'll use it as the root CA to generate our own PAI and DAC.
Generate the PAI using the PAA. You may optionally include the PID information in the PAI, but omitting it gives you more flexibility for testing. If you need DACs for additional PIDs, you can execute just the DAC generation step:
$ ./out/chip-cert gen-att-cert --type i \
--subject-cn "Matter Test PAI" \
--subject-vid "${VID}" \
--valid-from "2024-01-01 14:23:43" \
--lifetime "4294967295" \
--ca-key credentials/test/attestation/Chip-Test-PAA-NoVID-Key.pem \
--ca-cert credentials/test/attestation/Chip-Test-PAA-NoVID-Cert.pem \
--out-key credentials/test/attestation/"test-PAI-${VID}-key".pem \
--out credentials/test/attestation/"test-PAI-${VID}-cert".pem
Generate the DAC using the PAI:
$ ./out/chip-cert gen-att-cert --type d \
--subject-cn "Matter Test DAC 0" \
--subject-vid "${VID}" \
--subject-pid "${PID}" \
--valid-from "2024-01-01 14:23:43" \
--lifetime "4294967295" \
--ca-key credentials/test/attestation/"test-PAI-${VID}-key".pem \
--ca-cert credentials/test/attestation/"test-PAI-${VID}-cert".pem \
--out-key credentials/test/attestation/"test-DAC-${VID}-${PID}-key".pem \
--out credentials/test/attestation/"test-DAC-${VID}-${PID}-cert".pem
Verify the DAC, PAI and PAA chain. If no errors appear in the output, it means that the certificate attestation chain is successfully verified:
$ ./out/chip-cert validate-att-cert \
--dac credentials/test/attestation/"test-DAC-${VID}-${PID}-cert".pem \
--pai credentials/test/attestation/"test-PAI-${VID}-cert".pem \
--paa credentials/test/attestation/Chip-Test-PAA-NoVID-Cert.pem
You can inspect your keys using openssl:
$ openssl ec -noout -text -in \
credentials/test/attestation/test-DAC-${VID}-${PID}-key.pem
Example output:
read EC key
Private-Key: (256 bit)
priv:
c9:f2:b3:04:b2:db:0d:6f:cd:c6:be:f3:7b:76:8d:
8c:01:4e:0b:9e:ce:3e:72:49:3c:0e:35:63:7c:6c:
6c:d6
pub:
04:4f:93:ba:3b:bf:63:90:73:98:76:1e:af:87:79:
11:e6:77:e8:e2:df:a7:49:f1:7c:ac:a8:a6:91:76:
08:5b:39:ce:6c:72:db:6d:9a:92:b3:ba:05:b0:e8:
31:a0:bf:36:50:2b:5c:72:55:7f:11:c8:01:ff:3a:
46:b9:19:60:28
ASN1 OID: prime256v1
NIST CURVE: P-256
You may also use openssl to inspect your generated certificates:
$ openssl x509 -noout -text -in \
credentials/test/attestation/test-DAC-${VID}-${PID}-cert.pem
Example output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2875998130766646679 (0x27e9990fef088d97)
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN = Matter Test PAI, 1.3.6.1.4.1.37244.2.1 = hexVendorId
Validity
Not Before: Jun 28 14:23:43 2021 GMT
Not After : Dec 31 23:59:59 9999 GMT
Subject: CN = Matter Test DAC 0, 1.3.6.1.4.1.37244.2.1 = hexVendorId
, 1.3.6.1.4.1.37244.2.2 = hexProductId
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:4f:93:ba:3b:bf:63:90:73:98:76:1e:af:87:79:
11:e6:77:e8:e2:df:a7:49:f1:7c:ac:a8:a6:91:76:
08:5b:39:ce:6c:72:db:6d:9a:92:b3:ba:05:b0:e8:
31:a0:bf:36:50:2b:5c:72:55:7f:11:c8:01:ff:3a:
46:b9:19:60:28
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature
X509v3 Subject Key Identifier:
21:0A:CA:B1:B6:5F:17:65:D8:61:19:73:84:1A:9D:52:81:19:C5:39
X509v3 Authority Key Identifier:
37:7F:24:9A:73:41:4B:16:6E:6A:42:6E:F5:E8:89:FB:75:F8:77:BB
Signature Algorithm: ecdsa-with-SHA256
Signature Value:
30:45:02:20:38:8f:c5:0d:3e:90:95:dd:7d:7c:e9:5a:05:19:
1f:2d:14:08:a3:d7:0e:b5:15:6d:d3:b0:0b:f7:b8:28:4d:bf:
02:21:00:d4:05:30:43:a6:05:00:0e:b9:99:0d:34:3d:75:fe:
d3:c1:4e:73:ff:e7:05:64:7a:62:8d:2d:38:8f:fd:4d:ad
PAA
A similar process could be used for generating a self-signed PAA, but doing so is not necessary.
Instead, what we've done here is to use an existing self-signed development PAA that doesn't include VID information.
For more examples of generating a CD, look at credentials/test/gen-test-cds.sh And for more examples of generating a PAA, PAI, and DAC, see credentials/test/gen-test-attestation-certs.sh
#Script for generating Certs - save in ../certs/generate-embeddable-certs.sh
```
#!/bin/bash
#
# generate-embeddable-certs.sh script
# —----------------------------------
#
# This script generates self-minted DAC and PAI.
# The output may easily be included in your C++ source code.
#
# Edit this information with your paths and certificates
folder="credentials/test/attestation"
chip_cert_tool="out/chip-cert"
cert_file_der="${folder}/test-PAI-${VID}-cert.der"
cert_file_pem="${folder}/test-PAI-${VID}-cert.pem"
key_file_pem="${folder}/test-PAI-${VID}-key.pem"
type="Pai"
printf "namespace chip {\n"
printf "namespace DevelopmentCerts {\n\n"
printf "#if CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID == 0x${VID} && CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID == 0x${PID}\n\n"
printcert() {
# convert cert to DER
if [ -f "${cert_file_der}" ]; then
rm "${cert_file_der}"
fi
"${chip_cert_tool}" convert-cert "${cert_file_pem}" "${cert_file_der}" --x509-der
printf "// ------------------------------------------------------------ \n"
printf "// ${type} CERTIFICATE ${cert_file_der} \n\n"
printf "constexpr uint8_t ${type}_Cert_Array[] = {\n"
less -f "${cert_file_der}" | od -t x1 -An | sed 's/\</0x/g' | sed 's/\>/,/g' | sed 's/^/ /g'
printf "};\n\n"
printf "ByteSpan k${type}Cert = ByteSpan(${type}_Cert_Array);\n\n"
printf "// ${type} PUBLIC KEY FROM ${key_file_pem} \n\n"
printf "constexpr uint8_t ${type}_PublicKey_Array[] = {\n"
openssl ec -text -noout -in "${key_file_pem}" 2>/dev/null | sed '/ASN1 OID/d' | sed '/NIST CURVE/d' | sed -n '/pub:/,$p' | sed '/pub:/d' | sed 's/\([0-9a-fA-F][0-9a-fA-F]\)/0x\1/g' | sed 's/:/, /g'
printf "};\n\n"
printf "ByteSpan k${type}PublicKey = ByteSpan(${type}_PublicKey_Array);\n\n"
printf "// ${type} PRIVATE KEY FROM ${key_file_pem} \n\n"
printf "constexpr uint8_t ${type}_PrivateKey_Array[] = {\n"
openssl ec -text -noout -in "${key_file_pem}" 2>/dev/null | sed '/read EC key/d' | sed '/Private-Key/d' | sed '/priv:/d' | sed '/pub:/,$d' | sed 's/\([0-9a-fA-F][0-9a-fA-F]\)/0x\1/g' | sed 's/:/, /g'
printf "};\n\n"
printf "ByteSpan k${type}PrivateKey = ByteSpan(${type}_PrivateKey_Array);\n\n"
}
# generates PAI
printcert
type="Dac"
cert_file_der="${folder}/test-DAC-${VID}-${PID}-cert.der"
cert_file_pem="${folder}/test-DAC-${VID}-${PID}-cert.pem"
key_file_pem="${folder}/test-DAC-${VID}-${PID}-key.pem"
# generates DAC
printcert
printf "#endif // CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID\n"
printf "} // namespace DevelopmentCerts\n"
printf "} // namespace chip\n"
```
#run the script from the connectedhomeip folder
. ../certs/generate-embeddable-certs.sh
### Code in the ProductAttestationIntermediate PAI Cert
Copy the contents of the PAI and DAC output to your implementation of DeviceAttestationCredentialsProvider::GetProductAttestationIntermediateCert.
On production devices, the PAI and DAC are in Factory Data, while the CD is embedded in the firmware itself.
If you are not yet using Factory Data, you might want to place your PAI in src/credentials/examples/ExampleDACs.cpp. In this case, append the resulting generated code to your ExampleDACs.cpp file:
```
ByteSpan kDacCert = ByteSpan(kDevelopmentDAC_Cert_FFF1_801F);
ByteSpan kDacPrivateKey = ByteSpan(kDevelopmentDAC_PrivateKey_FFF1_801F);
ByteSpan kDacPublicKey = ByteSpan(kDevelopmentDAC_PublicKey_FFF1_801F);
#endif
} // namespace DevelopmentCerts
} // namespace chip
/* ------------------------------------------ */
/* current end-of-file */
/* ------------------------------------------ */
/* ------------------------------------------ */
/* output of creds-codelab.sh script */
/* ------------------------------------------ */
namespace chip {
namespace DevelopmentCerts {
#if CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID == hexProductId
...
ByteSpan kDacPrivateKey = ByteSpan(Dac_PrivateKey_Array);
#endif // CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID
} // namespace DevelopmentCerts
} // namespace chip
```
# Replace the CD (certification-declaration)
Extract a text representation of the contents of your CD file using xxd:
$ xxd -i credentials/test/certification-declaration/Chip-Test-CD-${VID}-${PID}.der
Example output:
```
unsigned char credentials_test_certification_declaration_Chip_Test_CD_FFAA_FFA1_der[] = {
0x30, 0x81, 0xe9, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x07, 0x02, 0xa0, 0x81, 0xdb, 0x30, 0x81, 0xd8, 0x02, 0x01, 0x03, 0x31,
0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x02, 0x01, 0x30, 0x45, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x07, 0x01, 0xa0, 0x38, 0x04, 0x36, 0x15, 0x24, 0x00, 0x01, 0x25,
0x01, 0xaa, 0xff, 0x36, 0x02, 0x05, 0xa1, 0xff, 0x18, 0x25, 0x03, 0x34,
0x12, 0x2c, 0x04, 0x13, 0x5a, 0x49, 0x47, 0x32, 0x30, 0x31, 0x34, 0x31,
0x5a, 0x42, 0x33, 0x33, 0x30, 0x30, 0x30, 0x31, 0x2d, 0x32, 0x34, 0x24,
0x05, 0x00, 0x24, 0x06, 0x00, 0x25, 0x07, 0x76, 0x98, 0x24, 0x08, 0x00,
0x18, 0x31, 0x7d, 0x30, 0x7b, 0x02, 0x01, 0x03, 0x80, 0x14, 0x62, 0xfa,
0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a,
0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x04, 0x47, 0x30, 0x45, 0x02,
0x21, 0x00, 0xb8, 0x8e, 0x9e, 0xf8, 0x64, 0x80, 0x58, 0xee, 0x3a, 0xfd,
0xfe, 0xca, 0xdb, 0x2d, 0xf9, 0xcc, 0x66, 0x38, 0xf8, 0x9d, 0xd6, 0x36,
0x52, 0xcb, 0x13, 0xac, 0xfd, 0x12, 0x52, 0x07, 0x21, 0xaa, 0x02, 0x20,
0x65, 0x35, 0x64, 0x89, 0x04, 0x75, 0x71, 0x85, 0xc3, 0x3d, 0xa3, 0xce,
0x85, 0x44, 0xaf, 0x41, 0x01, 0xe6, 0x86, 0xa7, 0x74, 0x9a, 0x7e, 0xfb,
0x12, 0xea, 0x06, 0xb2, 0x0b, 0xa6, 0x4f, 0x02
};
unsigned int credentials_test_certification_declaration_Chip_Test_CD_FFAA_FFA1_der_len = 236;
```
Copy the text you extracted in the previous step to the file used to define the CD into your build. As in the case of PAI and DAC, how you do this depends on which platform you're developing on.
If you are using the credentials examples, you probably want to replace the contents of kCdForAllExamples in ExampleDACProvider::GetCertificationDeclaration, in src/credentials/examples/DeviceAttestationCredsExample.cpp:
```
#elif CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID == 0xFFAA
const uint8_t kCdForAllExamples[] = {
0x30, 0x81, 0xe9, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x07, 0x02, 0xa0, 0x81, 0xdb, 0x30, 0x81, 0xd8, 0x02, 0x01, 0x03, 0x31,
0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x02, 0x01, 0x30, 0x45, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x07, 0x01, 0xa0, 0x38, 0x04, 0x36, 0x15, 0x24, 0x00, 0x01, 0x25,
0x01, 0xfe, 0xca, 0x36, 0x02, 0x05, 0xce, 0xfa, 0x18, 0x25, 0x03, 0x34,
0x12, 0x2c, 0x04, 0x13, 0x5a, 0x49, 0x47, 0x32, 0x30, 0x31, 0x34, 0x31,
0x5a, 0x42, 0x33, 0x33, 0x30, 0x30, 0x30, 0x31, 0x2d, 0x32, 0x34, 0x24,
0x05, 0x00, 0x24, 0x06, 0x00, 0x25, 0x07, 0x76, 0x98, 0x24, 0x08, 0x00,
0x18, 0x31, 0x7d, 0x30, 0x7b, 0x02, 0x01, 0x03, 0x80, 0x14, 0x62, 0xfa,
0x82, 0x33, 0x59, 0xac, 0xfa, 0xa9, 0x96, 0x3e, 0x1c, 0xfa, 0x14, 0x0a,
0xdd, 0xf5, 0x04, 0xf3, 0x71, 0x60, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x04, 0x47, 0x30, 0x45, 0x02,
0x20, 0x53, 0x25, 0x03, 0x2c, 0x96, 0x50, 0xb6, 0x64, 0xf4, 0x18, 0xbf,
0x99, 0x47, 0xf8, 0x9d, 0xe6, 0xeb, 0x43, 0x94, 0xf1, 0xce, 0xb2, 0x61,
0x00, 0xe0, 0xf9, 0x89, 0xa8, 0x71, 0x82, 0x02, 0x0a, 0x02, 0x21, 0x00,
0xea, 0x0a, 0x40, 0xab, 0x87, 0xad, 0x7e, 0x25, 0xe1, 0xa1, 0x6c, 0xb1,
0x12, 0xfa, 0x86, 0xfe, 0xea, 0x8a, 0xaf, 0x4b, 0xc1, 0xf3, 0x6f, 0x09,
0x85, 0x46, 0x50, 0xb6, 0xd0, 0x55, 0x40, 0xe2
};
};
#Put this code just before the last else code
#else /* Fall back to the VID=0xFFF1 CD */
```
#Now build the example - note best to do a full clean and remove the build directory
cd example/esp32
idf.py clean
idf.py fullclean
rm -rf build
idf.py set-target esp32
idf.py build
idf.py -p /dev/ttyUSB0 erase-flash
idf.py -p /dev/ttyUSB0 flash monitor
#Now try to pair the device using the chip-tool
cd connectedhomeip
rm -rf /tmp/chip*
./out/chip-tool pairing ble-wifi 1 <SSID> <PASSWORD> 20202021 3840
#check that pairing worked by reading basic information
./out/chip-tool basicinformation read vendor-name 1 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment