[tls] Support built-in root cert specified from gn

Add support for provisioning built-in certificates speficied from gn
build argument. Certificate files are loaded, converted to DER format
and then written to a source file as C "const unsigned char[]" array
with a API to retrieve them to load into the tls client. These
preprocessing are done using python. The script requires pyOpenssl
module. Thus the script is made into a python package so that the
dependency can be resolved during build.

Change-Id: I51f3805f310e07737fd39c333f3360ef4b9973f7
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/39762
Commit-Queue: Yecheng Zhao <zyecheng@google.com>
Reviewed-by: Ali Zhang <alizhang@google.com>
diff --git a/applications/tls_example/BUILD.gn b/applications/tls_example/BUILD.gn
index 757b7c7..8636218 100644
--- a/applications/tls_example/BUILD.gn
+++ b/applications/tls_example/BUILD.gn
@@ -18,10 +18,16 @@
 
 teensy_bloaty_config = rebase_path("./teensy41_bloaty_config.bloaty")
 
+root_ca_certificates = [
+  "//applications/tls_example/trust_store/global_sign_r2.pem",
+  "//applications/tls_example/trust_store/gts_ca_101.pem",
+]
+
 tls_client_example("boringssl_teensy_ethernet") {
   tls_backend = "backends/tls/boringssl"
   transport_backend = "backends/transport/teensy_ethernet"
   bloaty_config = teensy_bloaty_config
+  root_certificates = root_ca_certificates
 
   # Build time will be used as the time source for certificate expiration check
   # by default. To use a different specified time. Add the following argument:
@@ -33,10 +39,12 @@
   tls_backend = "backends/tls/picotls"
   transport_backend = "backends/transport/teensy_ethernet"
   bloaty_config = teensy_bloaty_config
+  root_certificates = root_ca_certificates
 }
 
 tls_client_example("mbedtls_teensy_ethernet") {
   tls_backend = "backends/tls/mbedtls"
   transport_backend = "backends/transport/teensy_ethernet"
   bloaty_config = teensy_bloaty_config
+  root_certificates = root_ca_certificates
 }
diff --git a/applications/tls_example/tls_client_example.cc b/applications/tls_example/tls_client_example.cc
index 7380b96..a2e30c4 100644
--- a/applications/tls_example/tls_client_example.cc
+++ b/applications/tls_example/tls_client_example.cc
@@ -35,9 +35,6 @@
     ;
 }
 
-// PEM format certificate.
-const char kCACertChain[] = {GLOBAL_SIGN_CA_CRT GTS_CA_101_CRT};
-
 #ifdef CRL_CHECK
 // The following are CRLs previously downloaded from the above CAs and they
 // are likely to be expired when application is compiled. To perform CRL
@@ -76,10 +73,14 @@
   }
 
   // Loads trusted CA certificates.
-  if (int status = tls->LoadCACert(kCACertChain, sizeof(kCACertChain));
-      status < 0) {
-    PW_LOG_INFO("Failed to load trusted CA certificates, %d", status);
-    MyAbort();
+  auto builtin_certs = GetBuiltInRootCert();
+  PW_LOG_INFO("Found %zu built-in CA certificates", builtin_certs.size());
+  for (auto cert : builtin_certs) {
+    PW_LOG_INFO("loading cert");
+    if (int status = tls->LoadCACert(cert.data(), cert.size()); status < 0) {
+      PW_LOG_INFO("Failed to load trusted CA certificates, %d", status);
+      MyAbort();
+    }
   }
 
 #ifdef CRL_CHECK
diff --git a/applications/tls_example/tls_client_example.gni b/applications/tls_example/tls_client_example.gni
index 87719b8..6bbd9c4 100644
--- a/applications/tls_example/tls_client_example.gni
+++ b/applications/tls_example/tls_client_example.gni
@@ -50,6 +50,7 @@
     ]
   }
 
+  # Target that implements time(time_t*) to provide an injected time.
   time_injection_target_name = target_name + "_time_injection"
   time_source_code_output = "$target_gen_dir/$target_name/injected_time.c"
   pw_python_action(time_injection_target_name) {
@@ -64,6 +65,29 @@
     }
   }
 
+  # Target that provides a set of hardcoded root certificates.
+  root_certificates = []
+  if (defined(invoker.root_certificates)) {
+    root_certificates += invoker.root_certificates
+  }
+
+  trust_store_target = target_name + "_trust_store"
+  trust_store_code_output = "$target_gen_dir/$target_name/trust_store.cc"
+  trust_store_code_path = rebase_path(trust_store_code_output)
+  pw_python_action(trust_store_target) {
+    script = "//applications/tls_example/trust_store/py" +
+             "/trust_store_generation/generate_trust_store.py"
+    outputs = [ trust_store_code_output ]
+    args = [ trust_store_code_path ]
+    foreach(cert, root_certificates) {
+      args += [
+        "-r",
+        rebase_path(cert),
+      ]
+    }
+    python_deps = [ "//applications/tls_example/trust_store/py" ]
+  }
+
   executable_exclude_vars = [
     "tls_backend",
     "transport_backend",
@@ -81,6 +105,7 @@
       "sysdeps.cc",
       "tls_client_example.cc",
       time_source_file[0],
+      trust_store_code_path,
     ]
 
     if (!defined(deps)) {
@@ -88,6 +113,7 @@
     }
     deps += [
       ":$time_injection_target_name",
+      ":$trust_store_target",
       "$dir_pw_log",
       "$dir_pw_spin_delay",
       "$dir_pw_sys_io",
diff --git a/applications/tls_example/trust_store/README.md b/applications/tls_example/trust_store/README.md
index 55c3955..0e16252 100644
--- a/applications/tls_example/trust_store/README.md
+++ b/applications/tls_example/trust_store/README.md
@@ -8,8 +8,3 @@
 
 The certificates and the corresponding crls can be downloaded from
 https://pki.goog/repository/
-
-generate_cert_crl_header_file.py is a reference python script for converting
-downloaded PEM format certificate/CRL files into C macros in a C header file.
-The macro can be used in embedded code for loading root CAs and CRLs to tls
-libraries.
diff --git a/applications/tls_example/trust_store/ca_certificates_crls.h b/applications/tls_example/trust_store/ca_certificates_crls.h
index a309c07..5129601 100644
--- a/applications/tls_example/trust_store/ca_certificates_crls.h
+++ b/applications/tls_example/trust_store/ca_certificates_crls.h
@@ -1,123 +1,74 @@
-// Copyright 2021 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
+// Copyright 2021 The Pigweed Authors

+//

+// Licensed under the Apache License, Version 2.0 (the "License"); you may not

+// use this file except in compliance with the License. You may obtain a copy of

+// the License at

+//

+//     https://www.apache.org/licenses/LICENSE-2.0

+//

+// Unless required by applicable law or agreed to in writing, software

+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+// License for the specific language governing permissions and limitations under

+// the License.

+

 #ifndef CA_CERTIFICATE_CRLS_H

 #define CA_CERTIFICATE_CRLS_H

-
-#define GLOBAL_SIGN_CA_CRT \

- "-----BEGIN CERTIFICATE-----\r\n" \

-"MIIDvDCCAqSgAwIBAgINAgPk9GHsmdnVeWbKejANBgkqhkiG9w0BAQUFADBMMSAw\r\n" \

-"HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs\r\n" \

-"U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0wNjEyMTUwODAwMDBaFw0yMTEy\r\n" \

-"MTUwODAwMDBaMEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBDQSAtIFIyMRMw\r\n" \

-"EQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMIIBIjANBgkq\r\n" \

-"hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAps8kDr4ubyiZRULEqz4hVJsL03+EcPoS\r\n" \

-"s8u/h1/Gf4bTsjBc1v2t8Xvc5fhglgmSEPXQU977e35ziKxSiHtKpspJpl6op4xa\r\n" \

-"Ebx6guu+jOmzrJYlB5dKmSoHL7Qed7+KD7UCfBuWuMW5Oiy81hK561l94tAGhl9e\r\n" \

-"SWq1OV6INOy8eAwImIRsqM1LtKB9DHlN8LgtyyHK1WxbfeGgKYSh+dOUScskYpEg\r\n" \

-"vN0L1dnM+eonCitzkcadG6zIy+jgoPQvkItN+7A2G/YZeoXgbfJhE4hcn+CTClGX\r\n" \

-"ilrOr6vV96oJqmC93Nlf33KpYBNeAAHJSvo/pOoHAyECjoLKA8KbjwIDAQABo4Gc\r\n" \

-"MIGZMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSb\r\n" \

-"4gdXZxwewGoG3lm0mi3f3BmGLjAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f\r\n" \

-"3BmGLjA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vY3JsLmdsb2JhbHNpZ24ubmV0\r\n" \

-"L3Jvb3QtcjIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQANeX81Z1YqDIs4EaLjG0qP\r\n" \

-"OxIzaJI/y4kiRj3a+y3KOx74clIkLuMgi/9/5iv/n+1LyhGU9g7174slbzJOPbSp\r\n" \

-"p1eT19ST2mYbdgTLx/hm3tTLoHIY/w4ZbnQYwfnPwAG4RefnEFYPQJmpD+Wh8BJw\r\n" \

-"Bgtm2drTale/T6NBwmwnEFunfaMfMX3g6IBrx7VKnxIkJh/3p190WveLKgl9n7i5\r\n" \

-"SWce/4woPimEn9WfEQWRvp6wKhaCKFjuCMuulEZusoOUJ4LfJnXxcuQTgIrSnwI7\r\n" \

-"KfSSjsd42w3lX1fbgJp7vPmLM6OBRvAXuYRKTFqMAWbb7OaGIEE+cbxY6PDepnva\r\n" \

-"-----END CERTIFICATE-----\r\n" \

-
-#define GLOBAL_SIGN_CA_CRL \

- "-----BEGIN X509 CRL-----\r\n" \

-"MIIDsjCCApoCAQEwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xvYmFsU2ln\r\n" \

-"biBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds\r\n" \

-"b2JhbFNpZ24XDTIwMTAwMTAwMDAwMFoXDTIxMDQxNTAwMDAwMFowggHnMCoCCwQA\r\n" \

-"AAAAASINPA91Fw0xNDExMjUwMDAwMDBaMAwwCgYDVR0VBAMKAQUwKgILBAAAAAAB\r\n" \

-"Ig08FMUXDTE0MTEyNTAwMDAwMFowDDAKBgNVHRUEAwoBBTAqAgsEAAAAAAEQC4yh\r\n" \

-"GxcNMTQxMTI1MDAwMDAwWjAMMAoGA1UdFQQDCgEFMCoCCwQAAAAAASf792cAFw0x\r\n" \

-"NDExMjUwMDAwMDBaMAwwCgYDVR0VBAMKAQUwKgILBAAAAAABRE7wRk4XDTE2MTAw\r\n" \

-"NzAwMDAwMFowDDAKBgNVHRUEAwoBBTAqAgsEAAAAAAESVq1fshcNMTYxMDA3MDAw\r\n" \

-"MDAwWjAMMAoGA1UdFQQDCgEFMCoCCwQAAAAAAS9O4VtjFw0xNzA0MDcwMDAwMDBa\r\n" \

-"MAwwCgYDVR0VBAMKAQUwKgILBAAAAAABL07hXdQXDTE3MDQwNzAwMDAwMFowDDAK\r\n" \

-"BgNVHRUEAwoBBTAqAgsEAAAAAAFETvBKVRcNMTkwOTMwMDAwMDAwWjAMMAoGA1Ud\r\n" \

-"FQQDCgEFMCsCDDASm/2A0ZPWs+I05BcNMTkwOTMwMDAwMDAwWjAMMAoGA1UdFQQD\r\n" \

-"CgEFMCwCDQHjqTAc/HIGOD+aUx0XDTE5MDkzMDAwMDAwMFowDDAKBgNVHRUEAwoB\r\n" \

-"BaAvMC0wCgYDVR0UBAMCASIwHwYDVR0jBBgwFoAUm+IHV2ccHsBqBt5ZtJot39wZ\r\n" \

-"hi4wDQYJKoZIhvcNAQELBQADggEBACNjTpjVd8K0aImkgYdqr0wqfNxOvdv1E/Zg\r\n" \

-"cxjhGyMb9ol8lzz8W5qK+SuqX9lv0kJoeM4tuYyNlYvuMzVSQoen40KNXgqbsv4f\r\n" \

-"FAZdsFfe7yIBIqpgOxSwGuegShfm5e2tliZeDcV2cS6XRoF+dX9CeHsUuv1IeJRd\r\n" \

-"OO+fPiqPSdyTmRsFcifSbWQq2Qh0xvKrDlH416AyGzOXWSieui09eM1UvMLjzAqP\r\n" \

-"iQksl/HPzJ4BHCxw7b1yOfJWL2s305qyA6pACS2CpkzLSh3ZDSVtSkbKkXgMsmf5\r\n" \

-"7oCW+hJX2X+THdOPhg1dmsudewYTSviUTOSNdEyWo0n5JXJDAWM=\r\n" \

-"-----END X509 CRL-----\r\n" \

-
-#define GTS_CA_101_CRT \

- "-----BEGIN CERTIFICATE-----\r\n" \

-"MIIESjCCAzKgAwIBAgINAeO0mqGNiqmBJWlQuDANBgkqhkiG9w0BAQsFADBMMSAw\r\n" \

-"HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs\r\n" \

-"U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy\r\n" \

-"MTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg\r\n" \

-"U2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxTzEwggEiMA0GCSqGSIb3DQEBAQUA\r\n" \

-"A4IBDwAwggEKAoIBAQDQGM9F1IvN05zkQO9+tN1pIRvJzzyOTHW5DzEZhD2ePCnv\r\n" \

-"UA0Qk28FgICfKqC9EksC4T2fWBYk/jCfC3R3VZMdS/dN4ZKCEPZRrAzDsiKUDzRr\r\n" \

-"mBBJ5wudgzndIMYcLe/RGGFl5yODIKgjEv/SJH/UL+dEaltN11BmsK+eQmMF++Ac\r\n" \

-"xGNhr59qM/9il71I2dN8FGfcddwuaej4bXhp0LcQBbjxMcI7JP0aM3T4I+DsaxmK\r\n" \

-"FsbjzaTNC9uzpFlgOIg7rR25xoynUxv8vNmkq7zdPGHXkxWY7oG9j+JkRyBABk7X\r\n" \

-"rJfoucBZEqFJJSPk7XA0LKW0Y3z5oz2D0c1tJKwHAgMBAAGjggEzMIIBLzAOBgNV\r\n" \

-"HQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud\r\n" \

-"EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJjR+G4Q68+b7GCfGJAboOt9Cf0rMB8G\r\n" \

-"A1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl\r\n" \

-"BggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp\r\n" \

-"MCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g\r\n" \

-"BDgwNjA0BgZngQwBAgIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y\r\n" \

-"ZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAGoA+Nnn78y6pRjd9XlQWNa7H\r\n" \

-"TgiZ/r3RNGkmUmYHPQq6Scti9PEajvwRT2iWTHQr02fesqOqBY2ETUwgZQ+lltoN\r\n" \

-"FvhsO9tvBCOIazpswWC9aJ9xju4tWDQH8NVU6YZZ/XteDSGU9YzJqPjY8q3MDxrz\r\n" \

-"mqepBCf5o8mw/wJ4a2G6xzUr6Fb6T8McDO22PLRL6u3M4Tzs3A2M1j6bykJYi8wW\r\n" \

-"IRdAvKLWZu/axBVbzYmqmwkm5zLSDW5nIAJbELCQCZwMH56t2Dvqofxs6BBcCFIZ\r\n" \

-"USpxu6x6td0V7SvJCCosirSmIatj/9dSSVDQibet8q/7UK4v4ZUN80atnZz1yg==\r\n" \

-"-----END CERTIFICATE-----\r\n" \

-
-#define GTS_CA_101_CRL \

- "-----BEGIN X509 CRL-----\r\n" \

-"MIIEwzCCA6sCAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV\r\n" \

-"BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN\r\n" \

-"MjEwMjIyMTg0MjI5WhcNMjEwMzA0MTc0MjI4WjCCAsgwIgIRAPOeVuL3pOWMCAAA\r\n" \

-"AABxCdoXDTIxMDIxNTExMzM0NlowIgIRALPSpDcvsGBZCAAAAABxCd8XDTIxMDIx\r\n" \

-"NTExMzM0N1owIgIRALFzu+oSx0W2CAAAAABxDAMXDTIxMDIxNTE0MTczMFowIQIQ\r\n" \

-"KdqEr0zEct8IAAAAAHEMCBcNMjEwMjE1MTQxNzMxWjAiAhEA+6BtbqQ+8ewIAAAA\r\n" \

-"AHEMyhcNMjEwMjE2MTEzMzQ0WjAiAhEAkfsP0PIrH08IAAAAAHEM0hcNMjEwMjE2\r\n" \

-"MTEzMzQ2WjAhAhAcxc/PVxodqwgAAAAAcQ2sFw0yMTAyMTcxMTMzNDVaMCECEHsF\r\n" \

-"sk/6a6UQCAAAAABxDbQXDTIxMDIxNzExMzM0N1owIgIRAI6CTuDMsL/oCAAAAABx\r\n" \

-"Dn4XDTIxMDIxODExMzM0NVowIgIRAOLZnq2ph1MpCAAAAABxDoQXDTIxMDIxODEx\r\n" \

-"MzM0OFowIgIRANoBw6kMQ3agCAAAAABxDqUXDTIxMDIxODE0NTMyNVowIQIQU0+z\r\n" \

-"IUNAjfcIAAAAAHEOqRcNMjEwMjE4MTQ1MzI3WjAiAhEAoKn9zoskKLwIAAAAAHEP\r\n" \

-"dhcNMjEwMjE5MTQ1MzI4WjAhAhB0V6Mx1YFI7QgAAAAAcQ99Fw0yMTAyMTkxNDUz\r\n" \

-"MjdaMCICEQC+tg2atFn2KQgAAAAAcRA+Fw0yMTAyMjAxNDUzMjVaMCECEFH4Oss7\r\n" \

-"Ux84CAAAAABxEEYXDTIxMDIyMDE0NTMyNlowIQIQZcbYEr1xuBsIAAAAAHERCBcN\r\n" \

-"MjEwMjIxMTQ1MzM3WjAiAhEA31c6SN9C0jsIAAAAAHERCRcNMjEwMjIxMTQ1MzM3\r\n" \

-"WjAhAhABFs/t9ooeaAgAAAAAcRHWFw0yMTAyMjIxNDUzMjZaMCICEQDQqZXLbVbw\r\n" \

-"RQgAAAAAcRHbFw0yMTAyMjIxNDUzMjZaoGkwZzAfBgNVHSMEGDAWgBSY0fhuEOvP\r\n" \

-"m+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBbwwNwYDVR0cAQH/BC0wK6AmoCSGImh0\r\n" \

-"dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29yZS5jcmyBAf8wDQYJKoZIhvcNAQEL\r\n" \

-"BQADggEBABcDrP2YlEoVf6hgtyztTftJLS+a2FAI5LpbRfqNeyM1wLP8XqmufhV4\r\n" \

-"3/2H2dj9YO0cokpan31ZwJX2NV9YIM8OU4wha5FWxHxBQ1MQ/MhIFjmboQ36D0rl\r\n" \

-"jYZOuc4F8uRwU+5/+e62t0+ZO7e8oz+1uVYDcOXCgQgJkr4aSKJuGrhYTiV9iRpI\r\n" \

-"Wjf3hZECG4bOfjrAchses+xxAd2WDb09cUa8qeqFqFqLRHSiqJBxqCzb9kDEayBI\r\n" \

-"XYvkJ/XPJFoSVLvzZpL+UjDDtY+YCkWU3m6QJ9Mamhy8bqtU9bNyoV7JTOa+V4cd\r\n" \

-"XA7cpwQ3pwG2LJTQjLG3+Lvk0HECG14=\r\n" \

-"-----END X509 CRL-----\r\n" \

-
-#endif // CA_CERTIFICATE_CRLS_H
+

+#define GLOBAL_SIGN_CA_CRL                                               \

+  "-----BEGIN X509 CRL-----\r\n"                                         \

+  "MIIDsjCCApoCAQEwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xvYmFsU2ln\r\n" \

+  "biBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds\r\n" \

+  "b2JhbFNpZ24XDTIwMTAwMTAwMDAwMFoXDTIxMDQxNTAwMDAwMFowggHnMCoCCwQA\r\n" \

+  "AAAAASINPA91Fw0xNDExMjUwMDAwMDBaMAwwCgYDVR0VBAMKAQUwKgILBAAAAAAB\r\n" \

+  "Ig08FMUXDTE0MTEyNTAwMDAwMFowDDAKBgNVHRUEAwoBBTAqAgsEAAAAAAEQC4yh\r\n" \

+  "GxcNMTQxMTI1MDAwMDAwWjAMMAoGA1UdFQQDCgEFMCoCCwQAAAAAASf792cAFw0x\r\n" \

+  "NDExMjUwMDAwMDBaMAwwCgYDVR0VBAMKAQUwKgILBAAAAAABRE7wRk4XDTE2MTAw\r\n" \

+  "NzAwMDAwMFowDDAKBgNVHRUEAwoBBTAqAgsEAAAAAAESVq1fshcNMTYxMDA3MDAw\r\n" \

+  "MDAwWjAMMAoGA1UdFQQDCgEFMCoCCwQAAAAAAS9O4VtjFw0xNzA0MDcwMDAwMDBa\r\n" \

+  "MAwwCgYDVR0VBAMKAQUwKgILBAAAAAABL07hXdQXDTE3MDQwNzAwMDAwMFowDDAK\r\n" \

+  "BgNVHRUEAwoBBTAqAgsEAAAAAAFETvBKVRcNMTkwOTMwMDAwMDAwWjAMMAoGA1Ud\r\n" \

+  "FQQDCgEFMCsCDDASm/2A0ZPWs+I05BcNMTkwOTMwMDAwMDAwWjAMMAoGA1UdFQQD\r\n" \

+  "CgEFMCwCDQHjqTAc/HIGOD+aUx0XDTE5MDkzMDAwMDAwMFowDDAKBgNVHRUEAwoB\r\n" \

+  "BaAvMC0wCgYDVR0UBAMCASIwHwYDVR0jBBgwFoAUm+IHV2ccHsBqBt5ZtJot39wZ\r\n" \

+  "hi4wDQYJKoZIhvcNAQELBQADggEBACNjTpjVd8K0aImkgYdqr0wqfNxOvdv1E/Zg\r\n" \

+  "cxjhGyMb9ol8lzz8W5qK+SuqX9lv0kJoeM4tuYyNlYvuMzVSQoen40KNXgqbsv4f\r\n" \

+  "FAZdsFfe7yIBIqpgOxSwGuegShfm5e2tliZeDcV2cS6XRoF+dX9CeHsUuv1IeJRd\r\n" \

+  "OO+fPiqPSdyTmRsFcifSbWQq2Qh0xvKrDlH416AyGzOXWSieui09eM1UvMLjzAqP\r\n" \

+  "iQksl/HPzJ4BHCxw7b1yOfJWL2s305qyA6pACS2CpkzLSh3ZDSVtSkbKkXgMsmf5\r\n" \

+  "7oCW+hJX2X+THdOPhg1dmsudewYTSviUTOSNdEyWo0n5JXJDAWM=\r\n"             \

+  "-----END X509 CRL-----\r\n"

+

+#define GTS_CA_101_CRL                                                   \

+  "-----BEGIN X509 CRL-----\r\n"                                         \

+  "MIIEwzCCA6sCAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV\r\n" \

+  "BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN\r\n" \

+  "MjEwMjIyMTg0MjI5WhcNMjEwMzA0MTc0MjI4WjCCAsgwIgIRAPOeVuL3pOWMCAAA\r\n" \

+  "AABxCdoXDTIxMDIxNTExMzM0NlowIgIRALPSpDcvsGBZCAAAAABxCd8XDTIxMDIx\r\n" \

+  "NTExMzM0N1owIgIRALFzu+oSx0W2CAAAAABxDAMXDTIxMDIxNTE0MTczMFowIQIQ\r\n" \

+  "KdqEr0zEct8IAAAAAHEMCBcNMjEwMjE1MTQxNzMxWjAiAhEA+6BtbqQ+8ewIAAAA\r\n" \

+  "AHEMyhcNMjEwMjE2MTEzMzQ0WjAiAhEAkfsP0PIrH08IAAAAAHEM0hcNMjEwMjE2\r\n" \

+  "MTEzMzQ2WjAhAhAcxc/PVxodqwgAAAAAcQ2sFw0yMTAyMTcxMTMzNDVaMCECEHsF\r\n" \

+  "sk/6a6UQCAAAAABxDbQXDTIxMDIxNzExMzM0N1owIgIRAI6CTuDMsL/oCAAAAABx\r\n" \

+  "Dn4XDTIxMDIxODExMzM0NVowIgIRAOLZnq2ph1MpCAAAAABxDoQXDTIxMDIxODEx\r\n" \

+  "MzM0OFowIgIRANoBw6kMQ3agCAAAAABxDqUXDTIxMDIxODE0NTMyNVowIQIQU0+z\r\n" \

+  "IUNAjfcIAAAAAHEOqRcNMjEwMjE4MTQ1MzI3WjAiAhEAoKn9zoskKLwIAAAAAHEP\r\n" \

+  "dhcNMjEwMjE5MTQ1MzI4WjAhAhB0V6Mx1YFI7QgAAAAAcQ99Fw0yMTAyMTkxNDUz\r\n" \

+  "MjdaMCICEQC+tg2atFn2KQgAAAAAcRA+Fw0yMTAyMjAxNDUzMjVaMCECEFH4Oss7\r\n" \

+  "Ux84CAAAAABxEEYXDTIxMDIyMDE0NTMyNlowIQIQZcbYEr1xuBsIAAAAAHERCBcN\r\n" \

+  "MjEwMjIxMTQ1MzM3WjAiAhEA31c6SN9C0jsIAAAAAHERCRcNMjEwMjIxMTQ1MzM3\r\n" \

+  "WjAhAhABFs/t9ooeaAgAAAAAcRHWFw0yMTAyMjIxNDUzMjZaMCICEQDQqZXLbVbw\r\n" \

+  "RQgAAAAAcRHbFw0yMTAyMjIxNDUzMjZaoGkwZzAfBgNVHSMEGDAWgBSY0fhuEOvP\r\n" \

+  "m+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBbwwNwYDVR0cAQH/BC0wK6AmoCSGImh0\r\n" \

+  "dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29yZS5jcmyBAf8wDQYJKoZIhvcNAQEL\r\n" \

+  "BQADggEBABcDrP2YlEoVf6hgtyztTftJLS+a2FAI5LpbRfqNeyM1wLP8XqmufhV4\r\n" \

+  "3/2H2dj9YO0cokpan31ZwJX2NV9YIM8OU4wha5FWxHxBQ1MQ/MhIFjmboQ36D0rl\r\n" \

+  "jYZOuc4F8uRwU+5/+e62t0+ZO7e8oz+1uVYDcOXCgQgJkr4aSKJuGrhYTiV9iRpI\r\n" \

+  "Wjf3hZECG4bOfjrAchses+xxAd2WDb09cUa8qeqFqFqLRHSiqJBxqCzb9kDEayBI\r\n" \

+  "XYvkJ/XPJFoSVLvzZpL+UjDDtY+YCkWU3m6QJ9Mamhy8bqtU9bNyoV7JTOa+V4cd\r\n" \

+  "XA7cpwQ3pwG2LJTQjLG3+Lvk0HECG14=\r\n"                                 \

+  "-----END X509 CRL-----\r\n"

+

+std::span<const std::span<const unsigned char>> GetBuiltInRootCert();

+

+#endif  // CA_CERTIFICATE_CRLS_H

diff --git a/applications/tls_example/trust_store/generate_cert_crl_header_file.py b/applications/tls_example/trust_store/generate_cert_crl_header_file.py
deleted file mode 100644
index 3d56bc9..0000000
--- a/applications/tls_example/trust_store/generate_cert_crl_header_file.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright 2021 The Pigweed Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-#     https:#www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-"""The script generates a header file containing C macros of GlobalSign and
-GTA CA 101 root certificates and their CRLs"""
-
-import os
-
-
-def generate_pem_macro(file, macro_name):
-    """Generates a C macro declaration for a PEM file"""
-    ret = []
-    with open(file, "r") as f:
-        start = []
-        end = []
-        begin = False
-        exit = False
-        for l in f:
-            l = l.strip('\r\n')
-            if l.startswith('-----BEGIN'):
-                begin = True
-                start = l
-            elif l.startswith('-----END'):
-                begin = False
-                end = l
-            elif begin:
-                ret.append(l)
-        ret.insert(0, start)
-        ret.append(end)
-
-    s = ""
-    for ele in ret:
-        s += f'\"{ele}\\r\\n\" \\\r\n'
-    return f'#define {macro_name} \\\r\n {s}'
-
-
-if __name__ == "__main__":
-    with open("ca_certificates_crls.h", "w") as f:
-        f.write("""// Copyright 2021 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-""")
-        f.write("#ifndef CA_CERTIFICATE_CRLS_H\r\n")
-        f.write("#define CA_CERTIFICATE_CRLS_H\r\n")
-        f.write("\n")
-
-        f.write(generate_pem_macro("global_sign_r2.pem", "GLOBAL_SIGN_CA_CRT"))
-        f.write("\n")
-
-        f.write(
-            generate_pem_macro("global_sign_r2.crl.pem", "GLOBAL_SIGN_CA_CRL"))
-        f.write("\n")
-
-        f.write(generate_pem_macro("gts_ca_101.pem", "GTS_CA_101_CRT"))
-        f.write("\n")
-
-        f.write(generate_pem_macro("gts_ca_101.crl.pem", "GTS_CA_101_CRL"))
-        f.write("\n")
-
-        f.write("#endif // CA_CERTIFICATE_CRLS_H")
-        f.write("\n")
diff --git a/applications/tls_example/trust_store/py/BUILD.gn b/applications/tls_example/trust_store/py/BUILD.gn
new file mode 100644
index 0000000..ef69905
--- /dev/null
+++ b/applications/tls_example/trust_store/py/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+import("$dir_pw_build/python.gni")
+
+pw_python_package("py") {
+  setup = [ "setup.py" ]
+  sources = [
+    "trust_store_generation/__init__.py",
+    "trust_store_generation/generate_trust_store.py",
+  ]
+  pylintrc = "$dir_pigweed/.pylintrc"
+}
diff --git a/applications/tls_example/trust_store/py/setup.py b/applications/tls_example/trust_store/py/setup.py
new file mode 100644
index 0000000..40bb548
--- /dev/null
+++ b/applications/tls_example/trust_store/py/setup.py
@@ -0,0 +1,28 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Trust store preprocessing scripts"""
+
+import setuptools  # type: ignore
+
+setuptools.setup(
+    name='trust_store_generation',
+    version='0.0.1',
+    author='Pigweed Authors',
+    author_email='pigweed-developers@googlegroups.com',
+    description='Trust store synthesis',
+    packages=setuptools.find_packages(),
+    package_data={'trust_store_generation': ['py.typed']},
+    zip_safe=False,
+    install_requires=['pyOpenSSL'],
+)
diff --git a/applications/tls_example/trust_store/py/trust_store_generation/__init__.py b/applications/tls_example/trust_store/py/trust_store_generation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/applications/tls_example/trust_store/py/trust_store_generation/__init__.py
diff --git a/applications/tls_example/trust_store/py/trust_store_generation/generate_trust_store.py b/applications/tls_example/trust_store/py/trust_store_generation/generate_trust_store.py
new file mode 100644
index 0000000..b94a5ac
--- /dev/null
+++ b/applications/tls_example/trust_store/py/trust_store_generation/generate_trust_store.py
@@ -0,0 +1,143 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Trusts tore synthesis
+
+The script generates source code for implementing built-in certificates. The code
+template is:
+
+namespace {
+const unsigned char kRootCACert_0[] = {...};
+
+const unsigned char kRootCACert_1[] = {...};
+
+...
+
+const std::span<const unsigned char> kRootCerts[] = {
+    std::span{kRootCACert_0},
+    std::span{kRootCACert_1},
+};
+}  // namespace
+
+std::span<const std::span<const unsigned char>> GetBuiltInRootCert() {
+  return std::span{kRootCerts};
+}
+
+GetBuiltInRootCert() is the API for retrieving the certificates.
+
+Usage:
+
+synthesis_trust_store <output> -r <cert0> -r <cert1> .....
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+from OpenSSL import crypto
+
+LICENSE_HEADER = """// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+"""
+
+ROOT_CERT_API_FUNC = """
+std::span<const std::span<const unsigned char>> GetBuiltInRootCert() {
+    return std::span{kRootCerts};
+}
+"""
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("out", help="output header")
+    parser.add_argument("--root_cert",
+                        "-r",
+                        help="root CA certificate",
+                        action="append")
+    return parser.parse_args()
+
+
+def generate_c_array_declaration(data, var_name):
+    """Generate a C array declaration for a byte data"""
+    return "".join([
+        f'const unsigned char {var_name}[] = {{',
+        " ".join([f'0x{b:x},' for b in data]), "};"
+    ])
+
+
+def load_certificate_file(file):
+    """Load certificate file from a PEM/DER format file"""
+    with open(file, 'rb') as cert:
+        data = cert.read()
+        try:
+            x509 = crypto.load_certificate(crypto.FILETYPE_PEM, data)
+        except crypto.Error:
+            x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, data)
+        return crypto.dump_certificate(crypto.FILETYPE_ASN1, x509)
+
+
+def generate_der_c_array_for_cert(file, var_name):
+    data = load_certificate_file(file)
+    return generate_c_array_declaration(data, var_name)
+
+
+def main():
+    args = parse_args()
+    print(args)
+    with open(args.out, "w") as header:
+        header.write(LICENSE_HEADER)
+        header.write("#include <span>\n\n")
+        certs = args.root_cert if args.root_cert else []
+        cert_collection_array = "".join([
+            "const ",
+            "std::span<const unsigned char>",
+            "kRootCerts[] = {",
+        ])
+        header.write("namespace {\n\n")
+        for i, cert in enumerate(certs):
+            var_name = f'kRootCACert_{i}'
+            # A comment to indicate the file the content comes from.
+            header.write("// Generated from\n")
+            header.write(f'// {cert}\n')
+            header.write(generate_der_c_array_for_cert(cert, var_name))
+            header.write("\n\n")
+            cert_collection_array += f'std::span{{{var_name}}},'
+        cert_collection_array += "};"
+        header.write(cert_collection_array)
+        header.write("\n\n")
+        # namespace {}
+        header.write("}\n\n")
+        header.write(ROOT_CERT_API_FUNC)
+        header.write("\n\n")
+
+    subprocess.run([
+        "clang-format",
+        "-i",
+        args.out,
+    ])
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/applications/tls_example/trust_store/py/trust_store_generation/py.typed b/applications/tls_example/trust_store/py/trust_store_generation/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/applications/tls_example/trust_store/py/trust_store_generation/py.typed
diff --git a/third_party/pigweed b/third_party/pigweed
index f47febb..1c00790 160000
--- a/third_party/pigweed
+++ b/third_party/pigweed
@@ -1 +1 @@
-Subproject commit f47febb0da2dd27118d3c8134b957cb79fc5f26f
+Subproject commit 1c00790fafae06c3ed41bb08158e2fe51eebf551