/* * * Copyright (c) 2020-2022 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * 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 an object for reading Abstract Syntax * Notation One (ASN.1) encoded data. * */ #include #include #include #include #include #include #include namespace chip { namespace ASN1 { using namespace chip::Encoding; void ASN1Reader::Init(const uint8_t * buf, size_t len) { ResetElementState(); mBuf = buf; mBufEnd = buf + len; mElemStart = buf; mContainerEnd = mBufEnd; mNumSavedContexts = 0; } CHIP_ERROR ASN1Reader::Next() { VerifyOrReturnError(!EndOfContents, ASN1_END); VerifyOrReturnError(!IndefiniteLen, ASN1_ERROR_UNSUPPORTED_ENCODING); // Note: avoid using addition assignment operator (+=), which may result in integer overflow // in the right hand side of an assignment (mHeadLen + ValueLen). mElemStart = mElemStart + mHeadLen + ValueLen; ResetElementState(); VerifyOrReturnError(mElemStart != mContainerEnd, ASN1_END); return DecodeHead(); } CHIP_ERROR ASN1Reader::EnterConstructedType() { VerifyOrReturnError(Constructed, ASN1_ERROR_INVALID_STATE); return EnterContainer(0); } CHIP_ERROR ASN1Reader::ExitConstructedType() { return ExitContainer(); } CHIP_ERROR ASN1Reader::GetConstructedType(const uint8_t *& val, uint32_t & valLen) { VerifyOrReturnError(Constructed, ASN1_ERROR_INVALID_STATE); val = mElemStart; valLen = mHeadLen + ValueLen; return CHIP_NO_ERROR; } CHIP_ERROR ASN1Reader::EnterEncapsulatedType() { VerifyOrReturnError(Class == kASN1TagClass_Universal && (Tag == kASN1UniversalTag_OctetString || Tag == kASN1UniversalTag_BitString), ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(!Constructed, ASN1_ERROR_UNSUPPORTED_ENCODING); return EnterContainer((Tag == kASN1UniversalTag_BitString) ? 1 : 0); } CHIP_ERROR ASN1Reader::ExitEncapsulatedType() { return ExitContainer(); } CHIP_ERROR ASN1Reader::EnterContainer(uint32_t offset) { VerifyOrReturnError(mNumSavedContexts != kMaxContextDepth, ASN1_ERROR_MAX_DEPTH_EXCEEDED); mSavedContexts[mNumSavedContexts].ElemStart = mElemStart; mSavedContexts[mNumSavedContexts].HeadLen = mHeadLen; mSavedContexts[mNumSavedContexts].ValueLen = ValueLen; mSavedContexts[mNumSavedContexts].IndefiniteLen = IndefiniteLen; mSavedContexts[mNumSavedContexts].ContainerEnd = mContainerEnd; mNumSavedContexts++; mElemStart = Value + offset; if (!IndefiniteLen) { VerifyOrReturnError(CanCastTo(mBufEnd - Value), ASN1_ERROR_VALUE_OVERFLOW); VerifyOrReturnError(static_cast(mBufEnd - Value) >= ValueLen, ASN1_ERROR_VALUE_OVERFLOW); mContainerEnd = Value + ValueLen; } ResetElementState(); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Reader::ExitContainer() { VerifyOrReturnError(mNumSavedContexts != 0, ASN1_ERROR_INVALID_STATE); ASN1ParseContext & prevContext = mSavedContexts[--mNumSavedContexts]; VerifyOrReturnError(!prevContext.IndefiniteLen, ASN1_ERROR_UNSUPPORTED_ENCODING); mElemStart = prevContext.ElemStart + prevContext.HeadLen + prevContext.ValueLen; mContainerEnd = prevContext.ContainerEnd; ResetElementState(); return CHIP_NO_ERROR; } bool ASN1Reader::IsContained() const { return mNumSavedContexts > 0; } CHIP_ERROR ASN1Reader::GetInteger(int64_t & val) { uint8_t encodedVal[sizeof(int64_t)] = { 0 }; size_t valPaddingLen = sizeof(int64_t) - ValueLen; VerifyOrReturnError(Value != nullptr, ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(ValueLen >= 1, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(ValueLen <= sizeof(int64_t), ASN1_ERROR_VALUE_OVERFLOW); VerifyOrReturnError(mElemStart + mHeadLen + ValueLen <= mContainerEnd, ASN1_ERROR_UNDERRUN); if ((*Value & 0x80) == 0x80) { for (size_t i = 0; i < valPaddingLen; i++) { encodedVal[i] = 0xFF; } } memcpy(&encodedVal[valPaddingLen], Value, ValueLen); val = static_cast(BigEndian::Get64(encodedVal)); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Reader::GetBoolean(bool & val) { VerifyOrReturnError(Value != nullptr, ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(ValueLen == 1, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(mElemStart + mHeadLen + ValueLen <= mContainerEnd, ASN1_ERROR_UNDERRUN); VerifyOrReturnError(Value[0] == 0 || Value[0] == 0xFF, ASN1_ERROR_INVALID_ENCODING); val = (Value[0] != 0); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Reader::GetUTCTime(ASN1UniversalTime & outTime) { // Supported Encoding: YYMMDDHHMMSSZ VerifyOrReturnError(Value != nullptr, ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(ValueLen >= 1, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(mElemStart + mHeadLen + ValueLen <= mContainerEnd, ASN1_ERROR_UNDERRUN); VerifyOrReturnError(ValueLen == 13 && Value[12] == 'Z', ASN1_ERROR_UNSUPPORTED_ENCODING); return outTime.ImportFrom_ASN1_TIME_string(CharSpan(reinterpret_cast(Value), ValueLen)); } CHIP_ERROR ASN1Reader::GetGeneralizedTime(ASN1UniversalTime & outTime) { // Supported Encoding: YYYYMMDDHHMMSSZ VerifyOrReturnError(Value != nullptr, ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(ValueLen >= 1, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(mElemStart + mHeadLen + ValueLen <= mContainerEnd, ASN1_ERROR_UNDERRUN); VerifyOrReturnError(ValueLen == 15 && Value[14] == 'Z', ASN1_ERROR_UNSUPPORTED_ENCODING); return outTime.ImportFrom_ASN1_TIME_string(CharSpan(reinterpret_cast(Value), ValueLen)); } static uint8_t ReverseBits(uint8_t v) { // swap adjacent bits v = static_cast(static_cast((v >> 1) & 0x55) | static_cast((v & 0x55) << 1)); // swap adjacent bit pairs v = static_cast(static_cast((v >> 2) & 0x33) | static_cast((v & 0x33) << 2)); // swap nibbles v = static_cast(static_cast(v >> 4) | static_cast(v << 4)); return v; } CHIP_ERROR ASN1Reader::GetBitString(uint32_t & outVal) { // NOTE: only supports DER encoding. VerifyOrReturnError(Value != nullptr, ASN1_ERROR_INVALID_STATE); VerifyOrReturnError(ValueLen >= 1, ASN1_ERROR_INVALID_ENCODING); VerifyOrReturnError(ValueLen <= 5, ASN1_ERROR_UNSUPPORTED_ENCODING); VerifyOrReturnError(mElemStart + mHeadLen + ValueLen <= mContainerEnd, ASN1_ERROR_UNDERRUN); if (ValueLen == 1) { outVal = 0; } else { outVal = ReverseBits(Value[1]); int shift = 8; for (uint32_t i = 2; i < ValueLen; i++, shift += 8) { outVal |= static_cast(ReverseBits(Value[i]) << shift); } } return CHIP_NO_ERROR; } CHIP_ERROR ASN1Reader::DecodeHead() { const uint8_t * p = mElemStart; VerifyOrReturnError(p < mBufEnd, ASN1_ERROR_UNDERRUN); Class = *p & 0xC0; Constructed = (*p & 0x20) != 0; Tag = *p & 0x1F; // Only tags < 31 supported. The implication of this is that encoded tags are exactly 1 byte long. VerifyOrReturnError(Tag < 0x1F, ASN1_ERROR_UNSUPPORTED_ENCODING); p++; VerifyOrReturnError(p < mBufEnd, ASN1_ERROR_UNDERRUN); if ((*p & 0x80) == 0) { ValueLen = *p & 0x7F; IndefiniteLen = false; p++; } else if (*p == 0x80) { ValueLen = 0; IndefiniteLen = true; p++; } else { ValueLen = 0; uint8_t lenLen = *p & 0x7F; p++; for (; lenLen > 0; lenLen--, p++) { VerifyOrReturnError(p < mBufEnd, ASN1_ERROR_UNDERRUN); VerifyOrReturnError((ValueLen & 0xFF000000) == 0, ASN1_ERROR_LENGTH_OVERFLOW); ValueLen = (ValueLen << 8) | *p; } IndefiniteLen = false; } VerifyOrReturnError(CanCastTo(mBufEnd - p), ASN1_ERROR_VALUE_OVERFLOW); VerifyOrReturnError(static_cast(mBufEnd - p) >= ValueLen, ASN1_ERROR_VALUE_OVERFLOW); VerifyOrReturnError(CanCastTo(p - mElemStart), ASN1_ERROR_VALUE_OVERFLOW); mHeadLen = static_cast(p - mElemStart); EndOfContents = (Class == kASN1TagClass_Universal && Tag == 0 && !Constructed && ValueLen == 0); Value = p; return CHIP_NO_ERROR; } void ASN1Reader::ResetElementState() { Class = 0; Tag = 0; Value = nullptr; ValueLen = 0; Constructed = false; IndefiniteLen = false; EndOfContents = false; mHeadLen = 0; } CHIP_ERROR DumpASN1(ASN1Reader & asn1Parser, const char * prefix, const char * indent) { CHIP_ERROR err = CHIP_NO_ERROR; if (indent == nullptr) indent = " "; int nestLevel = 0; while (true) { err = asn1Parser.Next(); if (err != CHIP_NO_ERROR) { if (err == ASN1_END) { if (asn1Parser.IsContained()) { err = asn1Parser.ExitConstructedType(); if (err != CHIP_NO_ERROR) { printf("ASN1Reader::ExitConstructedType() failed: %" CHIP_ERROR_FORMAT "\n", err.Format()); return err; } nestLevel--; continue; } break; } printf("ASN1Reader::Next() failed: %" CHIP_ERROR_FORMAT "\n", err.Format()); return err; } if (prefix != nullptr) printf("%s", prefix); for (int i = nestLevel; i; i--) printf("%s", indent); if (asn1Parser.IsEndOfContents()) printf("END-OF-CONTENTS "); else if (asn1Parser.GetClass() == kASN1TagClass_Universal) switch (asn1Parser.GetTag()) { case kASN1UniversalTag_Boolean: printf("BOOLEAN "); break; case kASN1UniversalTag_Integer: printf("INTEGER "); break; case kASN1UniversalTag_BitString: printf("BIT STRING "); break; case kASN1UniversalTag_OctetString: printf("OCTET STRING "); break; case kASN1UniversalTag_Null: printf("NULL "); break; case kASN1UniversalTag_ObjectId: printf("OBJECT IDENTIFIER "); break; case kASN1UniversalTag_ObjectDesc: printf("OBJECT DESCRIPTOR "); break; case kASN1UniversalTag_External: printf("EXTERNAL "); break; case kASN1UniversalTag_Real: printf("REAL "); break; case kASN1UniversalTag_Enumerated: printf("ENUMERATED "); break; case kASN1UniversalTag_Sequence: printf("SEQUENCE "); break; case kASN1UniversalTag_Set: printf("SET "); break; case kASN1UniversalTag_UTF8String: case kASN1UniversalTag_NumericString: case kASN1UniversalTag_PrintableString: case kASN1UniversalTag_T61String: case kASN1UniversalTag_VideotexString: case kASN1UniversalTag_IA5String: case kASN1UniversalTag_GraphicString: case kASN1UniversalTag_VisibleString: case kASN1UniversalTag_GeneralString: case kASN1UniversalTag_UniversalString: printf("STRING "); break; case kASN1UniversalTag_UTCTime: case kASN1UniversalTag_GeneralizedTime: printf("TIME "); break; default: printf("[UNIVERSAL %lu] ", static_cast(asn1Parser.GetTag())); break; } else if (asn1Parser.GetClass() == kASN1TagClass_Application) printf("[APPLICATION %lu] ", static_cast(asn1Parser.GetTag())); else if (asn1Parser.GetClass() == kASN1TagClass_ContextSpecific) printf("[%lu] ", static_cast(asn1Parser.GetTag())); else if (asn1Parser.GetClass() == kASN1TagClass_Private) printf("[PRIVATE %lu] ", static_cast(asn1Parser.GetTag())); if (asn1Parser.IsConstructed()) printf("(constructed) "); if (asn1Parser.IsIndefiniteLen()) printf("Length = indefinite\n"); else printf("Length = %ld\n", static_cast(asn1Parser.GetValueLen())); if (asn1Parser.IsConstructed()) { err = asn1Parser.EnterConstructedType(); if (err != CHIP_NO_ERROR) { printf("ASN1Reader::EnterConstructedType() failed: %" CHIP_ERROR_FORMAT "\n", err.Format()); return err; } nestLevel++; } } return err; } } // namespace ASN1 } // namespace chip