Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated OpCert to Use CHIP Epoch Time instead of Packed Time. #4888

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
emargolis marked this conversation as resolved.
Show resolved Hide resolved
}

// 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