Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | # |
| 4 | # Copyright (c) 2022 Project CHIP Authors |
| 5 | # |
| 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | # you may not use this file except in compliance with the License. |
| 8 | # You may obtain a copy of the License at |
| 9 | # |
| 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | # |
| 12 | # Unless required by applicable law or agreed to in writing, software |
| 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | # See the License for the specific language governing permissions and |
| 16 | # limitations under the License. |
| 17 | # |
| 18 | |
| 19 | # Script that was used to fetch CHIP Development Product Attestation Authority (PAA) |
| 20 | # certificates from DCL. |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 21 | # For usage please run:. |
| 22 | # python ./credentials/fetch-paa-certs-from-dcl.py --help |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 23 | |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 24 | import copy |
Arkadiusz Bokowy | d5bfb4b | 2023-01-09 16:15:00 +0100 | [diff] [blame] | 25 | import os |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 26 | import re |
Arkadiusz Bokowy | d5bfb4b | 2023-01-09 16:15:00 +0100 | [diff] [blame] | 27 | import subprocess |
| 28 | import sys |
Arkadiusz Bokowy | d5bfb4b | 2023-01-09 16:15:00 +0100 | [diff] [blame] | 29 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 30 | import click |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 31 | import requests |
Arkadiusz Bokowy | d5bfb4b | 2023-01-09 16:15:00 +0100 | [diff] [blame] | 32 | from click_option_group import RequiredMutuallyExclusiveOptionGroup, optgroup |
| 33 | from cryptography import x509 |
| 34 | from cryptography.hazmat.primitives import serialization |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 35 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 36 | PRODUCTION_NODE_URL = "https://on.dcl.csa-iot.org:26657" |
| 37 | PRODUCTION_NODE_URL_REST = "https://on.dcl.csa-iot.org" |
| 38 | TEST_NODE_URL_REST = "https://on.test-net.dcl.csa-iot.org" |
Vijay Selvaraj | a63f826 | 2022-10-26 13:24:04 -0400 | [diff] [blame] | 39 | |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 40 | MATTER_CERT_CA_SUBJECT = "MFIxDDAKBgNVBAoMA0NTQTEsMCoGA1UEAwwjTWF0dGVyIENlcnRpZmljYXRpb24gYW5kIFRlc3RpbmcgQ0ExFDASBgorBgEEAYKifAIBDARDNUEw" |
| 41 | MATTER_CERT_CA_SUBJECT_KEY_ID = "97:E4:69:D0:C5:04:14:C2:6F:C7:01:F7:7E:94:77:39:09:8D:F6:A5" |
| 42 | |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 43 | |
| 44 | def parse_paa_root_certs(cmdpipe, paa_list): |
| 45 | """ |
| 46 | example output of a query to all x509 root certs in DCL: |
| 47 | |
| 48 | certs: |
Vijay Selvaraj | eaa0c94 | 2022-07-11 08:53:01 -0400 | [diff] [blame] | 49 | - subject: MCExHzAdBgNVBAMMFk1hdHRlciBEZXZlbG9wbWVudCBQQUE= |
| 50 | subjectKeyId: FA:92:CF:09:5E:FA:42:E1:14:30:65:16:32:FE:FE:1B:2C:77:A7:C8 |
| 51 | - subject: MDAxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBQTEUMBIGCisGAQQBgqJ8AgEMBDEyNUQ= |
| 52 | subjectKeyId: E2:90:8D:36:9C:3C:A3:C1:13:BB:09:E2:4D:C1:CC:C5:A6:66:91:D4 |
| 53 | - subject: MEsxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZHb29nbGUxFTATBgNVBAMMDE1hdHRlciBQQUEgMTEUMBIGCisGAQQBgqJ8AgEMBDYwMDY= |
| 54 | subjectKeyId: B0:00:56:81:B8:88:62:89:62:80:E1:21:18:A1:A8:BE:09:DE:93:21 |
| 55 | - subject: MFUxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEtMCsGA1UEAxMkRGlnaUNlcnQgVEVTVCBSb290IENBIGZvciBNQVRURVIgUEtJ |
| 56 | subjectKeyId: C0:E0:64:15:00:EC:67:E2:7C:AF:7C:6E:2D:49:94:C7:73:DE:B7:BA |
| 57 | - subject: MDAxLjAsBgNVBAMMJU5vbiBQcm9kdWN0aW9uIE9OTFkgLSBYRk4gUEFBIENsYXNzIDM= |
| 58 | subjectKeyId: F8:99:A9:D5:AD:71:71:E4:C3:81:7F:14:10:7F:78:F0:D9:F7:62:E9 |
| 59 | - subject: MEIxGDAWBgNVBAMMD01hdHRlciBUZXN0IFBBQTEQMA4GA1UECgwHU2Ftc3VuZzEUMBIGCisGAQQBgqJ8AgEMBDEwRTE= |
| 60 | subjectKeyId: CF:9E:0A:16:78:8B:40:30:EC:DD:AB:34:B9:C2:EC:7B:E5:34:55:C0 |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 61 | |
| 62 | Brief: |
| 63 | This method will search for the first line that contains ': ' char sequence. |
| 64 | From there, it assumes every 2 lines contain subject and subject key id info of |
| 65 | a valid PAA root certificate. |
| 66 | The paa_list parameter will contain a list of all valid PAA Root certificates |
| 67 | from DCL. |
| 68 | """ |
| 69 | |
| 70 | result = {} |
| 71 | |
| 72 | while True: |
| 73 | line = cmdpipe.stdout.readline() |
| 74 | if not line: |
| 75 | break |
| 76 | else: |
| 77 | if b': ' in line: |
| 78 | key, value = line.split(b': ') |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 79 | result[key.strip(b' -').decode("utf-8") |
| 80 | ] = value.strip().decode("utf-8") |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 81 | parse_paa_root_certs.counter += 1 |
| 82 | if parse_paa_root_certs.counter % 2 == 0: |
| 83 | paa_list.append(copy.deepcopy(result)) |
| 84 | |
| 85 | |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 86 | def write_cert(certificate, subject): |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 87 | filename = 'dcld_mirror_' + \ |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 88 | re.sub('[^a-zA-Z0-9_-]', '', re.sub('[=, ]', '_', subject)) |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 89 | with open(filename + '.pem', 'w+') as outfile: |
| 90 | outfile.write(certificate) |
| 91 | # convert pem file to der |
Tennessee Carmel-Veilleux | be99e03 | 2023-06-01 15:23:59 -0400 | [diff] [blame] | 92 | try: |
| 93 | with open(filename + '.pem', 'rb') as infile: |
| 94 | pem_certificate = x509.load_pem_x509_certificate(infile.read()) |
| 95 | with open(filename + '.der', 'wb+') as outfile: |
| 96 | der_certificate = pem_certificate.public_bytes( |
| 97 | serialization.Encoding.DER) |
| 98 | outfile.write(der_certificate) |
| 99 | except (IOError, ValueError) as e: |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 100 | print( |
| 101 | f"ERROR: Failed to convert {filename + '.pem'}: {str(e)}. Skipping...") |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 102 | |
| 103 | |
| 104 | def parse_paa_root_cert_from_dcld(cmdpipe): |
| 105 | subject = None |
| 106 | certificate = "" |
| 107 | |
| 108 | while True: |
| 109 | line = cmdpipe.stdout.readline() |
| 110 | if not line: |
| 111 | break |
| 112 | else: |
| 113 | if b'pemCert: |' in line: |
| 114 | while True: |
| 115 | line = cmdpipe.stdout.readline() |
| 116 | certificate += line.strip(b' \t').decode("utf-8") |
| 117 | if b'-----END CERTIFICATE-----' in line: |
| 118 | break |
| 119 | if b'subjectAsText:' in line: |
| 120 | subject = line.split(b': ')[1].strip().decode("utf-8") |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 121 | break |
Vijay Selvaraj | eaa0c94 | 2022-07-11 08:53:01 -0400 | [diff] [blame] | 122 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 123 | return (certificate, subject) |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 124 | |
| 125 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 126 | def use_dcld(dcld, production, cmdlist): |
| 127 | return [dcld] + cmdlist + (['--node', PRODUCTION_NODE_URL] if production else []) |
| 128 | |
| 129 | |
| 130 | @click.command() |
| 131 | @click.help_option('-h', '--help') |
| 132 | @optgroup.group('Input data sources', cls=RequiredMutuallyExclusiveOptionGroup) |
| 133 | @optgroup.option('--use-main-net-dcld', type=str, default='', metavar='PATH', help="Location of `dcld` binary, to use `dcld` for mirroring MainNet.") |
| 134 | @optgroup.option('--use-test-net-dcld', type=str, default='', metavar='PATH', help="Location of `dcld` binary, to use `dcld` for mirroring TestNet.") |
| 135 | @optgroup.option('--use-main-net-http', is_flag=True, type=str, help="Use RESTful API with HTTPS against public MainNet observer.") |
| 136 | @optgroup.option('--use-test-net-http', is_flag=True, type=str, help="Use RESTful API with HTTPS against public TestNet observer.") |
| 137 | @optgroup.group('Optional arguments') |
| 138 | @optgroup.option('--paa-trust-store-path', default='paa-root-certs', type=str, metavar='PATH', help="PAA trust store path (default: paa-root-certs)") |
| 139 | def main(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_http, paa_trust_store_path): |
| 140 | """DCL PAA mirroring tools""" |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 141 | fetch_paa_certs(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_http, paa_trust_store_path) |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 142 | |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 143 | |
| 144 | def get_cert_from_rest(rest_node_url, subject, subject_key_id): |
| 145 | response = requests.get( |
| 146 | f"{rest_node_url}/dcl/pki/certificates/{subject}/{subject_key_id}").json()["approvedCertificates"]["certs"][0] |
| 147 | certificate = response["pemCert"].rstrip("\n") |
| 148 | subject = response["subjectAsText"] |
| 149 | return certificate, subject |
| 150 | |
| 151 | |
| 152 | def fetch_cd_signing_certs(store_path): |
| 153 | ''' Only supports using main net http currently.''' |
| 154 | rest_node_url = PRODUCTION_NODE_URL_REST |
| 155 | os.makedirs(store_path, exist_ok=True) |
| 156 | original_dir = os.getcwd() |
| 157 | os.chdir(store_path) |
| 158 | |
| 159 | cd_signer_ids = requests.get( |
| 160 | f"{rest_node_url}/dcl/pki/child-certificates/{MATTER_CERT_CA_SUBJECT}/{MATTER_CERT_CA_SUBJECT_KEY_ID}").json()['childCertificates']['certIds'] |
| 161 | for signer in cd_signer_ids: |
| 162 | subject = signer['subject'] |
| 163 | subject_key_id = signer['subjectKeyId'] |
| 164 | certificate, subject = get_cert_from_rest(rest_node_url, subject, subject_key_id) |
| 165 | |
| 166 | print(f"Downloaded CD signing cert with subject: {subject}") |
| 167 | write_cert(certificate, subject) |
| 168 | |
| 169 | os.chdir(original_dir) |
| 170 | |
| 171 | |
| 172 | def fetch_paa_certs(use_main_net_dcld, use_test_net_dcld, use_main_net_http, use_test_net_http, paa_trust_store_path): |
Vijay Selvaraj | a63f826 | 2022-10-26 13:24:04 -0400 | [diff] [blame] | 173 | production = False |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 174 | dcld = use_test_net_dcld |
Vijay Selvaraj | a63f826 | 2022-10-26 13:24:04 -0400 | [diff] [blame] | 175 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 176 | if len(use_main_net_dcld) > 0: |
| 177 | dcld = use_main_net_dcld |
| 178 | production = True |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 179 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 180 | use_rest = use_main_net_http or use_test_net_http |
| 181 | if use_main_net_http: |
| 182 | production = True |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 183 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 184 | rest_node_url = PRODUCTION_NODE_URL_REST if production else TEST_NODE_URL_REST |
Vijay Selvaraj | a63f826 | 2022-10-26 13:24:04 -0400 | [diff] [blame] | 185 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 186 | os.makedirs(paa_trust_store_path, exist_ok=True) |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 187 | original_dir = os.getcwd() |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 188 | os.chdir(paa_trust_store_path) |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 189 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 190 | if use_rest: |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 191 | paa_list = requests.get( |
| 192 | f"{rest_node_url}/dcl/pki/root-certificates").json()["approvedRootCertificates"]["certs"] |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 193 | else: |
| 194 | cmdlist = ['query', 'pki', 'all-x509-root-certs'] |
| 195 | |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 196 | cmdpipe = subprocess.Popen(use_dcld( |
| 197 | dcld, production, cmdlist), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 198 | |
| 199 | paa_list = [] |
| 200 | parse_paa_root_certs.counter = 0 |
| 201 | parse_paa_root_certs(cmdpipe, paa_list) |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 202 | |
| 203 | for paa in paa_list: |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 204 | if paa['subject'] == MATTER_CERT_CA_SUBJECT and paa['subjectKeyId'] == MATTER_CERT_CA_SUBJECT_KEY_ID: |
| 205 | # Don't include the CD signing cert as a PAA root. |
| 206 | continue |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 207 | if use_rest: |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 208 | certificate, subject = get_cert_from_rest(rest_node_url, paa['subject'], paa['subjectKeyId']) |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 209 | else: |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 210 | cmdlist = ['query', 'pki', 'x509-cert', '-u', |
| 211 | paa['subject'], '-k', paa['subjectKeyId']] |
Vijay Selvaraj | a63f826 | 2022-10-26 13:24:04 -0400 | [diff] [blame] | 212 | |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 213 | cmdpipe = subprocess.Popen(use_dcld( |
| 214 | dcld, production, cmdlist), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 215 | |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 216 | (certificate, subject) = parse_paa_root_cert_from_dcld(cmdpipe) |
| 217 | |
| 218 | certificate = certificate.rstrip('\n') |
| 219 | |
C Freeman | 8143615 | 2024-06-18 13:05:51 -0400 | [diff] [blame] | 220 | print(f"Downloaded PAA certificate with subject: {subject}") |
| 221 | write_cert(certificate, subject) |
| 222 | |
| 223 | os.chdir(original_dir) |
Vijay Selvaraj | 395bb1f | 2022-03-23 15:36:37 -0400 | [diff] [blame] | 224 | |
| 225 | |
| 226 | if __name__ == "__main__": |
Vijay Selvaraj | b0d92ca | 2022-11-07 17:01:16 -0500 | [diff] [blame] | 227 | if len(sys.argv) == 1: |
| 228 | main.main(['--help']) |
| 229 | else: |
| 230 | main() |