Keystore 2.0: Fixes in the legacy wrapper.
* Filter out invalid tags before passing them to the Keymaster device.
* Support negative timestamps for NOT_BEFORE/AFTER tags. Because the
Keystore CTS test wants to create historic certificates predating
Jan 1970.
Test: Keystore CTS tests.
Change-Id: I25a4440bf1318e9925f7a0c2ac481c29a56a412e
diff --git a/keystore2/src/crypto/certificate_utils.cpp b/keystore2/src/crypto/certificate_utils.cpp
index 4aed224..31c7fb4 100644
--- a/keystore2/src/crypto/certificate_utils.cpp
+++ b/keystore2/src/crypto/certificate_utils.cpp
@@ -167,6 +167,47 @@
return key_usage;
}
+template <typename Out, typename In> static Out saturate(In in) {
+ if constexpr (std::is_signed_v<Out> == std::is_signed_v<In>) {
+ if constexpr (sizeof(Out) >= sizeof(In)) {
+ // Same sign, and In fits into Out. Cast is lossless.
+ return static_cast<Out>(in);
+ } else {
+ // Out is smaller than In we may need to truncate.
+ // We pick the smaller of `out::max()` and the greater of `out::min()` and `in`.
+ return static_cast<Out>(
+ std::min(static_cast<In>(std::numeric_limits<Out>::max()),
+ std::max(static_cast<In>(std::numeric_limits<Out>::min()), in)));
+ }
+ } else {
+ // So we have different signs. This puts the lower bound at 0 because either input or output
+ // is unsigned. The upper bound is max of the smaller type or, if they are equal the max of
+ // the signed type.
+ if constexpr (std::is_signed_v<Out>) {
+ if constexpr (sizeof(Out) > sizeof(In)) {
+ return static_cast<Out>(in);
+ } else {
+ // Because `out` is the signed one, the lower bound of `in` is 0 and fits into
+ // `out`. We just have to compare the maximum and we do it in type In because it has
+ // a greater range than Out, so Out::max() is guaranteed to fit.
+ return static_cast<Out>(
+ std::min(static_cast<In>(std::numeric_limits<Out>::max()), in));
+ }
+ } else {
+ // Out is unsigned. So we can return 0 if in is negative.
+ if (in < 0) return 0;
+ if constexpr (sizeof(Out) >= sizeof(In)) {
+ // If Out is wider or equal we can assign lossless.
+ return static_cast<Out>(in);
+ } else {
+ // Otherwise we have to take the minimum of Out::max() and `in`.
+ return static_cast<Out>(
+ std::min(static_cast<In>(std::numeric_limits<Out>::max()), in));
+ }
+ }
+ }
+}
+
// Creates a rump certificate structure with serial, subject and issuer names, as well as
// activation and expiry date.
// Callers should pass an empty X509_Ptr and check the return value for CertUtilsError::Ok (0)
@@ -174,8 +215,8 @@
std::variant<CertUtilsError, X509_Ptr>
makeCertRump(std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial,
std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject,
- const uint64_t activeDateTimeMilliSeconds,
- const uint64_t usageExpireDateTimeMilliSeconds) {
+ const int64_t activeDateTimeMilliSeconds,
+ const int64_t usageExpireDateTimeMilliSeconds) {
// Create certificate structure.
X509_Ptr certificate(X509_new());
@@ -218,16 +259,16 @@
return std::get<CertUtilsError>(subjectName);
}
+ time_t notBeforeTime = saturate<time_t>(activeDateTimeMilliSeconds / 1000);
// Set activation date.
ASN1_TIME_Ptr notBefore(ASN1_TIME_new());
- if (!notBefore || !ASN1_TIME_set(notBefore.get(), activeDateTimeMilliSeconds / 1000) ||
+ if (!notBefore || !ASN1_TIME_set(notBefore.get(), notBeforeTime) ||
!X509_set_notBefore(certificate.get(), notBefore.get() /* Don't release; copied */))
return CertUtilsError::BoringSsl;
// Set expiration date.
time_t notAfterTime;
- notAfterTime = (time_t)std::min((uint64_t)std::numeric_limits<time_t>::max(),
- usageExpireDateTimeMilliSeconds / 1000);
+ notAfterTime = saturate<time_t>(usageExpireDateTimeMilliSeconds / 1000);
ASN1_TIME_Ptr notAfter(ASN1_TIME_new());
if (!notAfter || !ASN1_TIME_set(notAfter.get(), notAfterTime) ||
@@ -242,7 +283,7 @@
makeCert(const EVP_PKEY* evp_pkey,
std::optional<std::reference_wrapper<const std::vector<uint8_t>>> serial,
std::optional<std::reference_wrapper<const std::vector<uint8_t>>> subject,
- const uint64_t activeDateTimeMilliSeconds, const uint64_t usageExpireDateTimeMilliSeconds,
+ const int64_t activeDateTimeMilliSeconds, const int64_t usageExpireDateTimeMilliSeconds,
bool addSubjectKeyIdEx, std::optional<KeyUsageExtension> keyUsageEx,
std::optional<BasicConstraintsExtension> basicConstraints) {