Blob Blame History Raw
diff --git a/security/pkix/lib/pkixder.cpp b/security/pkix/lib/pkixder.cpp
--- a/security/pkix/lib/pkixder.cpp
+++ b/security/pkix/lib/pkixder.cpp
@@ -466,28 +466,63 @@ TimeChoice(Reader& tagged, uint8_t expec
   if (rv != Success) {
     return rv;
   }
 
   uint8_t b;
   if (input.Read(b) != Success) {
     return Result::ERROR_INVALID_DER_TIME;
   }
-  if (b != 'Z') {
+
+  unsigned int hourOffset = 0;
+  unsigned int minuteOffset = 0;
+  bool allowOffset = false;
+  bool haveOffset = false;
+  bool offsetIsPositive = false;
+
+  if (getenv("PKIX_ALLOW_CERT_UTCTIME_OFFSET") != 0) {
+    allowOffset = true;
+  }
+
+  if (allowOffset && (b == '+' || b == '-')) {
+    haveOffset = true;
+    rv = ReadTwoDigits(input, 0u, 23u, hourOffset);
+    if (rv != Success) {
+      return rv;
+    }
+    rv = ReadTwoDigits(input, 0u, 59u, minuteOffset);
+    if (rv != Success) {
+      return rv;
+    }
+    if (b == '+') {
+      offsetIsPositive = true;
+    }
+  } else if (b != 'Z') {
     return Result::ERROR_INVALID_DER_TIME;
   }
   if (End(input) != Success) {
     return Result::ERROR_INVALID_DER_TIME;
   }
 
   uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) +
                           (static_cast<uint64_t>(hours)      * 60u * 60u) +
                           (static_cast<uint64_t>(minutes)          * 60u) +
                           seconds;
 
+  if (haveOffset) {
+    uint64_t offsetInSeconds =
+                          (static_cast<uint64_t>(hourOffset) * 60u * 60u) +
+                          (static_cast<uint64_t>(minuteOffset)     * 60u);
+    if (offsetIsPositive) {
+      totalSeconds -= offsetInSeconds;
+    } else {
+      totalSeconds += offsetInSeconds;
+    }
+  }
+
   time = TimeFromElapsedSecondsAD(totalSeconds);
   return Success;
 }
 
 Result
 IntegralBytes(Reader& input, uint8_t tag,
               IntegralValueRestriction valueRestriction,
               /*out*/ Input& value,