Skip to content

Commit

Permalink
Updated OpCert to Use CHIP Epoch Time instead of Packed Time. (#4888)
Browse files Browse the repository at this point in the history
Ticket: #4886
  • Loading branch information
emargolis authored Feb 17, 2021
1 parent a949773 commit fbc9596
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 203 deletions.
133 changes: 34 additions & 99 deletions src/credentials/CHIPCert.cpp
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -465,9 +465,8 @@ CHIP_ERROR ChipCertificateSet::VerifySignature(const ChipCertificateData * cert,
CHIP_ERROR ChipCertificateSet::ValidateCert(const ChipCertificateData * cert, ValidationContext & context,
BitFlags<uint8_t, CertValidateFlags> validateFlags, uint8_t depth)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ChipCertificateData * caCert = nullptr;
static constexpr int kLastSecondOfDay = kSecondsPerDay - 1;
CHIP_ERROR err = CHIP_NO_ERROR;
ChipCertificateData * caCert = nullptr;

// If the depth is greater than 0 then the certificate is required to be a CA certificate...
if (depth > 0)
Expand Down Expand Up @@ -524,14 +523,13 @@ CHIP_ERROR ChipCertificateSet::ValidateCert(const ChipCertificateData * cert, Va
}

// Verify the validity time of the certificate, if requested.
if (cert->mNotBeforeDate != 0 && !validateFlags.Has(CertValidateFlags::kIgnoreNotBefore))
if (cert->mNotBeforeTime != 0 && !validateFlags.Has(CertValidateFlags::kIgnoreNotBefore))
{
VerifyOrExit(context.mEffectiveTime >= PackedCertDateToTime(cert->mNotBeforeDate), err = CHIP_ERROR_CERT_NOT_VALID_YET);
VerifyOrExit(context.mEffectiveTime >= cert->mNotBeforeTime, err = CHIP_ERROR_CERT_NOT_VALID_YET);
}
if (cert->mNotAfterDate != 0 && !validateFlags.Has(CertValidateFlags::kIgnoreNotAfter))
if (cert->mNotAfterTime != 0 && !validateFlags.Has(CertValidateFlags::kIgnoreNotAfter))
{
VerifyOrExit(context.mEffectiveTime <= PackedCertDateToTime(cert->mNotAfterDate) + kLastSecondOfDay,
err = CHIP_ERROR_CERT_EXPIRED);
VerifyOrExit(context.mEffectiveTime <= cert->mNotAfterTime, err = CHIP_ERROR_CERT_EXPIRED);
}

// If the certificate itself is trusted, then it is implicitly valid. Record this certificate as the trust
Expand Down Expand Up @@ -641,8 +639,8 @@ void ChipCertificateData::Clear()
mIssuerDN.Clear();
mSubjectKeyId.Clear();
mAuthKeyId.Clear();
mNotBeforeDate = 0;
mNotAfterDate = 0;
mNotBeforeTime = 0;
mNotAfterTime = 0;
mPublicKey = nullptr;
mPublicKeyLen = 0;
mPubKeyCurveOID = 0;
Expand Down Expand Up @@ -733,115 +731,52 @@ bool CertificateKeyId::IsEqual(const CertificateKeyId & other) const
return mId != nullptr && other.mId != nullptr && mLen == other.mLen && memcmp(mId, other.mId, mLen) == 0;
}

DLL_EXPORT CHIP_ERROR PackCertTime(const ASN1UniversalTime & time, uint32_t & packedTime)
DLL_EXPORT CHIP_ERROR ASN1ToChipEpochTime(const chip::ASN1::ASN1UniversalTime & asn1Time, uint32_t & epochTime)
{
enum
{
kCertTimeBaseYear = 2020,
kCertTimeMaxYear = kCertTimeBaseYear +
UINT32_MAX / (kMonthsPerYear * kMaxDaysPerMonth * kHoursPerDay * kMinutesPerHour * kSecondsPerMinute),
kX509NoWellDefinedExpirationDateYear = 9999
};

// The packed time in a CHIP certificate cannot represent dates prior to 2020/01/01.
if (time.Year < kCertTimeBaseYear)
{
return ASN1_ERROR_UNSUPPORTED_ENCODING;
}
CHIP_ERROR err = CHIP_NO_ERROR;

// X.509/RFC5280 defines the special time 99991231235959Z to mean 'no well-defined expiration date'.
// We represent that as a packed time value of 0, which for simplicity's sake is assigned to any
// date in the associated year.
if (time.Year == kX509NoWellDefinedExpirationDateYear)
// In CHIP certificate it is represented as a CHIP Epoch UTC time value of 0 sec (2020-01-01 00:00:00 UTC).
if ((asn1Time.Year == kX509NoWellDefinedExpirationDateYear) && (asn1Time.Month == kMonthsPerYear) &&
(asn1Time.Day == kMaxDaysPerMonth) && (asn1Time.Hour == kHoursPerDay - 1) && (asn1Time.Minute == kMinutesPerHour - 1) &&
(asn1Time.Second == kSecondsPerMinute - 1))
{
packedTime = kNullCertTime;
return CHIP_NO_ERROR;
epochTime = kNullCertTime;
}

// Technically packed certificate time values could grow beyond 32bits. However we restrict it here
// to dates that fit within 32bits to reduce code size and eliminate the need for 64bit math.
if (time.Year > kCertTimeMaxYear)
else
{
return ASN1_ERROR_UNSUPPORTED_ENCODING;
if (!CalendarToChipEpochTime(asn1Time.Year, asn1Time.Month, asn1Time.Day, asn1Time.Hour, asn1Time.Minute, asn1Time.Second,
epochTime))
{
ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
}

packedTime = time.Year - kCertTimeBaseYear;
packedTime = packedTime * kMonthsPerYear + time.Month - 1;
packedTime = packedTime * kMaxDaysPerMonth + time.Day - 1;
packedTime = packedTime * kHoursPerDay + time.Hour;
packedTime = packedTime * kMinutesPerHour + time.Minute;
packedTime = packedTime * kSecondsPerMinute + time.Second;

return CHIP_NO_ERROR;
exit:
return err;
}

DLL_EXPORT CHIP_ERROR UnpackCertTime(uint32_t packedTime, ASN1UniversalTime & time)
DLL_EXPORT CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1UniversalTime & asn1Time)
{
enum
{
kCertTimeBaseYear = 2020,
kX509NoWellDefinedExpirationDateYear = 9999,
};

// X.509/RFC5280 defines the special time 99991231235959Z to mean 'no well-defined expiration date'.
// We represent that as a packed time value of 0.
if (packedTime == kNullCertTime)
// In CHIP certificate it is represented as a CHIP Epoch time value of 0 secs (2020-01-01 00:00:00 UTC).
if (epochTime == kNullCertTime)
{
time.Year = kX509NoWellDefinedExpirationDateYear;
time.Month = kMonthsPerYear;
time.Day = kMaxDaysPerMonth;
time.Hour = kHoursPerDay - 1;
time.Minute = kMinutesPerHour - 1;
time.Second = kSecondsPerMinute - 1;
asn1Time.Year = kX509NoWellDefinedExpirationDateYear;
asn1Time.Month = kMonthsPerYear;
asn1Time.Day = kMaxDaysPerMonth;
asn1Time.Hour = kHoursPerDay - 1;
asn1Time.Minute = kMinutesPerHour - 1;
asn1Time.Second = kSecondsPerMinute - 1;
}

else
{
time.Second = static_cast<uint8_t>(packedTime % kSecondsPerMinute);
packedTime /= kSecondsPerMinute;

time.Minute = static_cast<uint8_t>(packedTime % kMinutesPerHour);
packedTime /= kMinutesPerHour;

time.Hour = static_cast<uint8_t>(packedTime % kHoursPerDay);
packedTime /= kHoursPerDay;

time.Day = static_cast<uint8_t>((packedTime % kMaxDaysPerMonth) + 1);
packedTime /= kMaxDaysPerMonth;

time.Month = static_cast<uint8_t>((packedTime % kMonthsPerYear) + 1);
packedTime /= kMonthsPerYear;

time.Year = static_cast<uint16_t>(packedTime + kCertTimeBaseYear);
ChipEpochToCalendarTime(epochTime, asn1Time.Year, asn1Time.Month, asn1Time.Day, asn1Time.Hour, asn1Time.Minute,
asn1Time.Second);
}

return CHIP_NO_ERROR;
}

DLL_EXPORT uint16_t PackedCertTimeToDate(uint32_t packedTime)
{
return static_cast<uint16_t>(packedTime / kSecondsPerDay);
}

DLL_EXPORT uint32_t PackedCertDateToTime(uint16_t packedDate)
{
return static_cast<uint32_t>(packedDate * kSecondsPerDay);
}

DLL_EXPORT uint32_t SecondsSinceEpochToPackedCertTime(uint32_t secondsSinceEpoch)
{
chip::ASN1::ASN1UniversalTime asn1Time;
uint32_t packedTime;

// Convert seconds-since-epoch to calendar date and time and store in an ASN1UniversalTime structure.
SecondsSinceEpochToCalendarTime(secondsSinceEpoch, asn1Time.Year, asn1Time.Month, asn1Time.Day, asn1Time.Hour, asn1Time.Minute,
asn1Time.Second);

// Convert the calendar date/time to a packed certificate date/time.
PackCertTime(asn1Time, packedTime);

return packedTime;
}

} // namespace Credentials
} // namespace chip
83 changes: 16 additions & 67 deletions src/credentials/CHIPCert.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
namespace chip {
namespace Credentials {

const uint32_t kKeyIdentifierLength = 20;
const uint32_t kChipIdUTF8Length = 16;
static constexpr uint32_t kKeyIdentifierLength = 20;
static constexpr uint32_t kChipIdUTF8Length = 16;
static constexpr uint16_t kX509NoWellDefinedExpirationDateYear = 9999;

/** Data Element Tags for the CHIP Certificate
*/
Expand Down Expand Up @@ -244,8 +245,8 @@ struct ChipCertificateData
ChipDN mIssuerDN; /**< Certificate Issuer DN. */
CertificateKeyId mSubjectKeyId; /**< Certificate Subject public key identifier. */
CertificateKeyId mAuthKeyId; /**< Certificate Authority public key identifier. */
uint16_t mNotBeforeDate; /**< Certificate validity: Not Before field. */
uint16_t mNotAfterDate; /**< Certificate validity: Not After field. */
uint32_t mNotBeforeTime; /**< Certificate validity: Not Before field. */
uint32_t mNotAfterTime; /**< Certificate validity: Not After field. */
const uint8_t * mPublicKey; /**< Pointer to the certificate public key. */
uint8_t mPublicKeyLen; /**< Certificate public key length. */
uint16_t mPubKeyCurveOID; /**< Public key Elliptic Curve CHIP OID. */
Expand Down Expand Up @@ -274,7 +275,7 @@ struct ChipCertificateData
*/
struct ValidationContext
{
uint32_t mEffectiveTime; /**< Current time in the CHIP Packed Certificate Time format. */
uint32_t mEffectiveTime; /**< Current CHIP Epoch UTC time. */
const ChipCertificateData * mTrustAnchor; /**< Pointer to the Trust Anchor Certificate data structure. */
const ChipCertificateData * mSigningCert; /**< Pointer to the Signing Certificate data structure. */
BitFlags<uint16_t, KeyUsageFlags> mRequiredKeyUsages; /**< Key usage extensions that should be present in the
Expand Down Expand Up @@ -604,83 +605,31 @@ CHIP_ERROR DetermineCertType(ChipCertificateData & cert);

/**
* @brief
* Convert a certificate date/time (in the form of an ASN.1 universal time structure) into a packed
* certificate date/time.
*
* @details
* Packed certificate date/times provide a compact representation for the time values within a certificate
* (notBefore and notAfter) that does not require full calendar math to interpret.
*
* A packed certificate date/time contains the fields of a calendar date/time--i.e. year, month, day, hour,
* minute, second--packed into an unsigned integer. The bit representation is organized such that
* ordinal comparisons of packed date/time values correspond to the natural ordering of the corresponding
* times. To reduce their size, packed certificate date/times are limited to representing times that are on
* or after 2020/01/01 00:00:00. When housed within a 32-bit unsigned integer, packed certificate
* date/times can represent times up to the year 2153.
* Convert a certificate date/time (in the form of an ASN.1 universal time structure) into a CHIP Epoch UTC time.
*
* @note
* This function makes no attempt to verify the correct range of the input time other than year.
* Therefore callers must make sure the supplied values are valid prior to invocation.
*
* @param time The calendar date/time to be converted.
* @param packedTime A reference to an integer that will receive packed date/time.
* @param asn1Time The calendar date/time to be converted.
* @param epochTime A reference to an integer that will receive CHIP Epoch UTC time.
*
* @retval #CHIP_NO_ERROR If the input time was successfully converted.
* @retval #ASN1_ERROR_UNSUPPORTED_ENCODING If the input time contained a year value that could not
* be represented in a packed certificate time value.
* be represented in a CHIP epoch UTC time value.
**/
CHIP_ERROR PackCertTime(const chip::ASN1::ASN1UniversalTime & time, uint32_t & packedTime);
CHIP_ERROR ASN1ToChipEpochTime(const chip::ASN1::ASN1UniversalTime & asn1Time, uint32_t & epochTime);

/**
* @brief
* Unpack a packed certificate date/time into an ASN.1 universal time structure.
* Convert a CHIP epoch UTC time into an ASN.1 universal time structure.
*
* @param packedTime A packed certificate time to be unpacked.
* @param time A reference to an ASN1UniversalTime structure to receive the unpacked date/time.
* @param epochTime A CHIP epoch UTC time to be converted.
* @param asn1Time A reference to an ASN1UniversalTime structure to receive the date/time.
*
* @retval #CHIP_NO_ERROR If the input time was successfully unpacked.
* @retval #CHIP_NO_ERROR If the input time was successfully converted.
*/
CHIP_ERROR UnpackCertTime(uint32_t packedTime, chip::ASN1::ASN1UniversalTime & time);

/**
* @brief
* Convert a packed certificate date/time to a packed certificate date.
*
* @details
* A packed certificate date contains the fields of a calendar date--year, month, day--packed into an
* unsigned integer. The bits are organized such that ordinal comparisons of packed date values
* correspond to the natural ordering of the corresponding dates. To reduce their size, packed
* certificate dates are limited to representing dates on or after 2020/01/01. When housed within
* a 16-bit unsigned integer, packed certificate dates can represent dates up to the year 2196.
*
* @param packedTime The packed certificate date/time to be converted.
*
* @return A corresponding packed certificate date.
**/
uint16_t PackedCertTimeToDate(uint32_t packedTime);

/**
* @brief
* Convert a packed certificate date to a corresponding packed certificate date/time, where
* the time portion of the value is set to 00:00:00.
*
* @param packedDate The packed certificate date to be converted.
*
* @return A corresponding packed certificate date/time.
**/
uint32_t PackedCertDateToTime(uint16_t packedDate);

/**
* @brief
* Convert the number of seconds since 1970-01-01 00:00:00 UTC to a packed certificate date/time.
*
* @param secondsSinceEpoch Number of seconds since 1970-01-01 00:00:00 UTC.
* Note: this value is compatible with *positive* values
* of the POSIX time_t value, up to the year 2105.
*
* @return A corresponding packed certificate date/time.
**/
uint32_t SecondsSinceEpochToPackedCertTime(uint32_t secondsSinceEpoch);
CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1UniversalTime & asn1Time);

/**
* @return True if the OID represents a CHIP-defined X.509 distinguished named attribute.
Expand Down
20 changes: 12 additions & 8 deletions src/credentials/CHIPCertFromX509.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,25 @@ static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writ
static CHIP_ERROR ConvertValidity(ASN1Reader & reader, TLVWriter & writer)
{
CHIP_ERROR err;
ASN1UniversalTime notBeforeTime, notAfterTime;
uint32_t packedNotBeforeTime, packedNotAfterTime;
ASN1UniversalTime asn1Time;
uint32_t chipEpochTime;

ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_TIME(notBeforeTime);
err = PackCertTime(notBeforeTime, packedNotBeforeTime);
ASN1_PARSE_TIME(asn1Time);

err = ASN1ToChipEpochTime(asn1Time, chipEpochTime);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_NotBefore), packedNotBeforeTime);

err = writer.Put(ContextTag(kTag_NotBefore), chipEpochTime);
SuccessOrExit(err);

ASN1_PARSE_TIME(notAfterTime);
err = PackCertTime(notAfterTime, packedNotAfterTime);
ASN1_PARSE_TIME(asn1Time);

err = ASN1ToChipEpochTime(asn1Time, chipEpochTime);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_NotAfter), packedNotAfterTime);

err = writer.Put(ContextTag(kTag_NotAfter), chipEpochTime);
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;
Expand Down
24 changes: 15 additions & 9 deletions src/credentials/CHIPCertToX509.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,28 +209,34 @@ static CHIP_ERROR DecodeConvertValidity(TLVReader & reader, ASN1Writer & writer,
{
CHIP_ERROR err;
ASN1UniversalTime asn1Time;
uint64_t packedTime;
uint64_t chipEpochTime;

ASN1_START_SEQUENCE
{
err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_NotBefore));
SuccessOrExit(err);
err = reader.Get(packedTime);

err = reader.Get(chipEpochTime);
SuccessOrExit(err);
VerifyOrExit(packedTime <= UINT32_MAX, err = CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
certData.mNotBeforeDate = PackedCertTimeToDate(static_cast<uint32_t>(packedTime));
err = UnpackCertTime(static_cast<uint32_t>(packedTime), asn1Time);

VerifyOrExit(chipEpochTime <= UINT32_MAX, err = CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
certData.mNotBeforeTime = static_cast<uint32_t>(chipEpochTime);

err = ChipEpochToASN1Time(static_cast<uint32_t>(chipEpochTime), asn1Time);
SuccessOrExit(err);

ASN1_ENCODE_TIME(asn1Time);

err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_NotAfter));
SuccessOrExit(err);
err = reader.Get(packedTime);

err = reader.Get(chipEpochTime);
SuccessOrExit(err);
VerifyOrExit(packedTime <= UINT32_MAX, err = CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
certData.mNotAfterDate = PackedCertTimeToDate(static_cast<uint32_t>(packedTime));
err = UnpackCertTime(static_cast<uint32_t>(packedTime), asn1Time);

VerifyOrExit(chipEpochTime <= UINT32_MAX, err = CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
certData.mNotAfterTime = static_cast<uint32_t>(chipEpochTime);

err = ChipEpochToASN1Time(static_cast<uint32_t>(chipEpochTime), asn1Time);
SuccessOrExit(err);

ASN1_ENCODE_TIME(asn1Time);
Expand Down
Loading

0 comments on commit fbc9596

Please sign in to comment.