[tls] Support build time injection for tls example
www.google.com seems to refresh certificate every 3 months. It just
broke the tls example application as it has been using fixed time so
far. The CL adds support for injecting custom/build time as the time
source. This is done by auto-generating source code for implementing the
time_t time(time_t *) C API. The time_t value to return is passed from a
gn argument, or default to build time if not specified.
CRL checking is disabled by default as the in-tree test CRLs have
already expired. This will cause load failure when build time is used.
Will consider build-time CRL download and injection for demo purpose.
Change-Id: I6d6339a6203d2f3ba2b06f8afac5af04e3a5e683
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/39465
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 11631c0..757b7c7 100644
--- a/applications/tls_example/BUILD.gn
+++ b/applications/tls_example/BUILD.gn
@@ -22,6 +22,11 @@
tls_backend = "backends/tls/boringssl"
transport_backend = "backends/transport/teensy_ethernet"
bloaty_config = teensy_bloaty_config
+
+ # 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:
+ #
+ # time = "03/29/2021 12:00:00:00"
}
tls_client_example("picotls_teensy_ethernet") {
diff --git a/applications/tls_example/sysdeps.cc b/applications/tls_example/sysdeps.cc
index e4531c5..2ed71a8 100644
--- a/applications/tls_example/sysdeps.cc
+++ b/applications/tls_example/sysdeps.cc
@@ -21,28 +21,6 @@
#include "pw_log/log.h"
extern "C" {
-// libraries such as boringssl, picotls, wolfssl use time() to get current
-// date/time for certificate time check. For demo purpose, the following fakes
-// this function and provides a pre-set date.
-//
-// TLS_EXAMPLE_TIME specifies the current time in seconds since epoch. One easy
-// way to figure out this value for a certain date is to use a one-line python
-// code:
-//
-// import datetime
-// datetime.datetime(2021,5,21,0,0).timestamp()
-//
-// The above gives the seconds since epoch for 05/21/2021 00:00
-#ifndef TLS_EXAMPLE_TIME
-// The CRL used in the example is only valid before 2021 March.
-#define TLS_EXAMPLE_TIME 1614240000 // 2021-02-25 00:00:00
-#endif
-
-time_t time(time_t* timer) {
- time_t ret = TLS_EXAMPLE_TIME;
- return timer ? *timer = ret : ret;
-}
-
// boringssl reads from file "dev/urandom" for generating randome bytes.
// For demo purpose, we fake these file io functions.
diff --git a/applications/tls_example/time_injection/generate_time_code.py b/applications/tls_example/time_injection/generate_time_code.py
new file mode 100644
index 0000000..3c1f232
--- /dev/null
+++ b/applications/tls_example/time_injection/generate_time_code.py
@@ -0,0 +1,96 @@
+# 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.
+"""Generate time for injection
+
+The script generates a .c file that implement time_t time(time_t *) c API
+that returns a pre-set time. This is used to inject a time for the tls
+example application. The template of the code is
+
+#include <sys/time.h>
+
+static time_t injected_time = <time>;
+time_t time(time_t* timer) {
+ return timer ? *timer = injected_time : injected_time;
+}
+
+Usage:
+ inject specified time: genearte_time_code <output> -t "03/29/2021 12:00:00"
+ inject current time: genearte_time_code <output>
+"""
+import datetime
+from datetime import datetime
+import time
+import argparse
+import subprocess
+
+TIME_FORMAT = "%m/%d/%Y %H:%M:%S"
+
+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.
+
+#include <sys/time.h>
+
+// libraries such as boringssl, picotls, wolfssl use time() to get current
+// date/time for certificate time check. For demo purpose, the following fakes
+// this function and provides a pre-set date.
+
+"""
+
+FUNCTION = """
+time_t time(time_t* t) {
+ return t ? *t = injected_time : injected_time;
+}
+"""
+
+
+def parse_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("out", help="path for output header file")
+ parser.add_argument("--time", "-t", help="time to inject")
+ return parser.parse_args()
+
+
+if __name__ == "__main__":
+ args = parse_args()
+ if args.time:
+ time_stamp = datetime.strptime(args.time, TIME_FORMAT).timestamp()
+ else:
+ time_stamp = datetime.now().timestamp()
+ time_stamp = int(time_stamp)
+
+ # Generate source file
+ with open(args.out, "w") as header:
+ header.write(HEADER)
+ string_date = datetime.fromtimestamp(time_stamp).strftime(TIME_FORMAT)
+ header.write(f'// {string_date}\n')
+ header.write(f'static time_t injected_time = {int(time_stamp)};\n')
+ header.write(FUNCTION)
+ header.write("\n")
+
+ subprocess.run([
+ "clang-format",
+ "-i",
+ args.out,
+ ])
diff --git a/applications/tls_example/tls_client_example.cc b/applications/tls_example/tls_client_example.cc
index 45c91eb..8632ed1 100644
--- a/applications/tls_example/tls_client_example.cc
+++ b/applications/tls_example/tls_client_example.cc
@@ -38,10 +38,19 @@
// PEM format certificate.
const char kCACertChain[] = {GLOBAL_SIGN_CA_CRT GTS_CA_101_CRT};
-// The following CRLs are downloaded previously from the above CAs and for
-// demo purpose only. In practice, CRLs should be downloaded from CA at
-// run-time.
+#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
+// check, make sure that you download the latest CRL from CA into folder
+// applications/tls_example/trust_store and re-run
+// applications/tls_example/trust_store/generate_cert_crl_header_file.py
+// to update the header. See applications/tls_example/trust_store/README.md
+// for more detail.
+//
+// TODO(zyecheng): Alternatively, consider build time CRL download and
+// injection for demo purpose.
const char kCrls[] = {GLOBAL_SIGN_CA_CRL GTS_CA_101_CRL};
+#endif
void TlsClientExample() {
TlsInterface* tls = CreateTls();
@@ -73,11 +82,13 @@
MyAbort();
}
+#ifdef CRL_CHECK
// Loads CRLs.
if (int status = tls->LoadCrl(kCrls, sizeof(kCrls)); status < 0) {
PW_LOG_INFO("Failed to load crls, %d", status);
MyAbort();
}
+#endif
// Performs TLS handshake.
PW_LOG_INFO("Performing handshake...");
diff --git a/applications/tls_example/tls_client_example.gni b/applications/tls_example/tls_client_example.gni
index 03736cb..87719b8 100644
--- a/applications/tls_example/tls_client_example.gni
+++ b/applications/tls_example/tls_client_example.gni
@@ -14,10 +14,26 @@
import("//build_overrides/pigweed.gni")
import("$dir_pw_bloat/bloat.gni")
+import("$dir_pw_build/python_action.gni")
import("$dir_pw_build/target_types.gni")
# A convenient template for generating example applications with different
# tls/transport backends.
+#
+# The template accepts the following arguments:
+#
+# tls_backend Required. A target specifying the tls library.
+#
+# transport_backend: Required. A target specifying the transport implementation.
+#
+# no_bloaty: Whether to generate bloaty size report. Default
+# to true
+#
+# bloaty_config: An optional config file for bloaty report
+#
+# time: An optional date string to inject as time. If not spedified,
+# build time will be used. Must be format
+# month/day/year hour:minute:second
template("tls_client_example") {
assert(defined(invoker.tls_backend) && invoker.tls_backend != "",
"must provide a tls backend")
@@ -34,6 +50,20 @@
]
}
+ 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) {
+ script = "//applications/tls_example/time_injection/generate_time_code.py"
+ outputs = [ time_source_code_output ]
+ args = [ rebase_path(time_source_code_output) ]
+ if (defined(invoker.time)) {
+ args += [
+ "-t",
+ invoker.time,
+ ]
+ }
+ }
+
executable_exclude_vars = [
"tls_backend",
"transport_backend",
@@ -46,15 +76,18 @@
if (!defined(sources)) {
sources = []
}
+ time_source_file = get_target_outputs(":$time_injection_target_name")
sources += [
"sysdeps.cc",
"tls_client_example.cc",
+ time_source_file[0],
]
if (!defined(deps)) {
deps = []
}
deps += [
+ ":$time_injection_target_name",
"$dir_pw_log",
"$dir_pw_spin_delay",
"$dir_pw_sys_io",