/* * * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * * 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 * * http://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. */ /** * @file * This file implements Abstract Syntax Notation One (ASN.1) Time functions. * */ #include #include #include #include #include #include #include #include #include #include #include namespace chip { namespace ASN1 { namespace { /** * @brief Parses the two first characters C-string interpreting its content as a two base-10 digits number. * The C-string pointer is advansed by two characters. */ uint8_t atoi2(const char *& buf) { uint8_t val = static_cast((buf[0] - '0') * 10 + (buf[1] - '0')); buf += 2; return val; } /** * @brief Converts two low significan base-10 digits of an integer value (val % 100) to a two character C-string. * The C-string pointer is advansed by two characters. */ void itoa2(uint32_t val, char *& buf) { buf[1] = static_cast('0' + (val % 10)); val /= 10; buf[0] = static_cast('0' + (val % 10)); buf += 2; } } // anonymous namespace CHIP_ERROR ASN1UniversalTime::ImportFrom_ASN1_TIME_string(const CharSpan & asn1_time) { const char * p = asn1_time.data(); const size_t size = asn1_time.size(); VerifyOrReturnError(p != nullptr, ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(size == kASN1UTCTimeStringLength || size == kASN1GeneralizedTimeStringLength, ASN1_ERROR_UNSUPPORTED_ENCODING); VerifyOrReturnError(p[size - 1] == 'Z', ASN1_ERROR_UNSUPPORTED_ENCODING); for (size_t i = 0; i < size - 1; i++) { VerifyOrReturnError(isdigit(p[i]), ASN1_ERROR_INVALID_ENCODING); } if (size == kASN1GeneralizedTimeStringLength) { Year = static_cast(atoi2(p) * 100 + atoi2(p)); } else { Year = atoi2(p); Year = static_cast(Year + ((Year >= 50) ? 1900 : 2000)); } Month = atoi2(p); Day = atoi2(p); Hour = atoi2(p); Minute = atoi2(p); Second = atoi2(p); VerifyOrReturnError(Month > 0 && Month <= kMonthsPerYear, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(Day > 0 && Day <= kMaxDaysPerMonth, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(Hour < kHoursPerDay, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(Minute < kMinutesPerHour, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(Second < kSecondsPerMinute, ASN1_ERROR_INVALID_ENCODING); return CHIP_NO_ERROR; } CHIP_ERROR ASN1UniversalTime::ExportTo_ASN1_TIME_string(MutableCharSpan & asn1_time) const { char * p = asn1_time.data(); VerifyOrReturnError(p != nullptr, ASN1_ERROR_INVALID_STATE); // X.509/RFC5280 mandates that times before 2050 UTC must be encoded as ASN.1 UTCTime values, while // times equal or greater than 2050 must be encoded as GeneralizedTime values. The only difference // (in the context of X.509 DER) is that GeneralizedTimes are encoded with a 4 digit year, while // UTCTimes are encoded with a two-digit year. if (Year < 1950 || Year >= 2050) { VerifyOrReturnError(asn1_time.size() >= kASN1GeneralizedTimeStringLength, ASN1_ERROR_UNDERRUN); itoa2(Year / 100, p); } else { VerifyOrReturnError(asn1_time.size() >= kASN1UTCTimeStringLength, ASN1_ERROR_UNDERRUN); } itoa2(Year, p); itoa2(Month, p); itoa2(Day, p); itoa2(Hour, p); itoa2(Minute, p); itoa2(Second, p); *p = 'Z'; asn1_time.reduce_size(static_cast(p - asn1_time.data() + 1)); return CHIP_NO_ERROR; } bool ASN1UniversalTime::ExportTo_UnixTime(uint32_t & unixEpoch) { return CalendarTimeToSecondsSinceUnixEpoch(Year, Month, Day, Hour, Minute, Second, unixEpoch); } } // namespace ASN1 } // namespace chip