blob: 309af285b55e1e277f0256ebcf043389057768f8 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 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 <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <lib/asn1/ASN1.h>
#include <lib/support/SafeInt.h>
namespace chip {
namespace ASN1 {
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()
{
ReturnErrorCodeIf(EndOfContents, ASN1_END);
ReturnErrorCodeIf(IndefiniteLen, ASN1_ERROR_UNSUPPORTED_ENCODING);
mElemStart += (mHeadLen + ValueLen);
ResetElementState();
ReturnErrorCodeIf(mElemStart == mContainerEnd, ASN1_END);
return DecodeHead();
}
CHIP_ERROR ASN1Reader::EnterConstructedType()
{
ReturnErrorCodeIf(!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)
{
ReturnErrorCodeIf(!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);
ReturnErrorCodeIf(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)
{
ReturnErrorCodeIf(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)
{
mContainerEnd = Value + ValueLen;
}
ResetElementState();
return CHIP_NO_ERROR;
}
CHIP_ERROR ASN1Reader::ExitContainer()
{
ReturnErrorCodeIf(mNumSavedContexts == 0, ASN1_ERROR_INVALID_STATE);
ASN1ParseContext & prevContext = mSavedContexts[--mNumSavedContexts];
ReturnErrorCodeIf(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)
{
ReturnErrorCodeIf(Value == nullptr, ASN1_ERROR_INVALID_STATE);
ReturnErrorCodeIf(ValueLen < 1, ASN1_ERROR_INVALID_ENCODING);
ReturnErrorCodeIf(ValueLen > sizeof(int64_t), ASN1_ERROR_VALUE_OVERFLOW);
ReturnErrorCodeIf(mElemStart + mHeadLen + ValueLen > mContainerEnd, ASN1_ERROR_UNDERRUN);
const uint8_t * p = Value;
val = ((*p & 0x80) == 0) ? 0 : -1;
for (uint32_t i = ValueLen; i > 0; i--, p++)
{
val = (val << 8) | *p;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ASN1Reader::GetBoolean(bool & val)
{
ReturnErrorCodeIf(Value == nullptr, ASN1_ERROR_INVALID_STATE);
ReturnErrorCodeIf(ValueLen != 1, ASN1_ERROR_INVALID_ENCODING);
ReturnErrorCodeIf(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
ReturnErrorCodeIf(Value == nullptr, ASN1_ERROR_INVALID_STATE);
ReturnErrorCodeIf(ValueLen < 1, ASN1_ERROR_INVALID_ENCODING);
ReturnErrorCodeIf(mElemStart + mHeadLen + ValueLen > mContainerEnd, ASN1_ERROR_UNDERRUN);
VerifyOrReturnError(ValueLen == 13 && Value[12] == 'Z', ASN1_ERROR_UNSUPPORTED_ENCODING);
for (int i = 0; i < 12; i++)
{
VerifyOrReturnError(isdigit(Value[i]), ASN1_ERROR_INVALID_ENCODING);
}
outTime.Year = static_cast<uint16_t>((Value[0] - '0') * 10 + (Value[1] - '0'));
outTime.Month = static_cast<uint8_t>((Value[2] - '0') * 10 + (Value[3] - '0'));
outTime.Day = static_cast<uint8_t>((Value[4] - '0') * 10 + (Value[5] - '0'));
outTime.Hour = static_cast<uint8_t>((Value[6] - '0') * 10 + (Value[7] - '0'));
outTime.Minute = static_cast<uint8_t>((Value[8] - '0') * 10 + (Value[9] - '0'));
outTime.Second = static_cast<uint8_t>((Value[10] - '0') * 10 + (Value[11] - '0'));
outTime.Year = static_cast<uint16_t>(outTime.Year + ((outTime.Year >= 50) ? 1900 : 2000));
return CHIP_NO_ERROR;
}
CHIP_ERROR ASN1Reader::GetGeneralizedTime(ASN1UniversalTime & outTime)
{
// Supported Encoding: YYYYMMDDHHMMSSZ
ReturnErrorCodeIf(Value == nullptr, ASN1_ERROR_INVALID_STATE);
ReturnErrorCodeIf(ValueLen < 1, ASN1_ERROR_INVALID_ENCODING);
ReturnErrorCodeIf(mElemStart + mHeadLen + ValueLen > mContainerEnd, ASN1_ERROR_UNDERRUN);
VerifyOrReturnError(ValueLen == 15 && Value[14] == 'Z', ASN1_ERROR_UNSUPPORTED_ENCODING);
for (int i = 0; i < 14; i++)
{
VerifyOrReturnError(isdigit(Value[i]), ASN1_ERROR_INVALID_ENCODING);
}
outTime.Year =
static_cast<uint16_t>((Value[0] - '0') * 1000 + (Value[1] - '0') * 100 + (Value[2] - '0') * 10 + (Value[3] - '0'));
outTime.Month = static_cast<uint8_t>((Value[4] - '0') * 10 + (Value[5] - '0'));
outTime.Day = static_cast<uint8_t>((Value[6] - '0') * 10 + (Value[7] - '0'));
outTime.Hour = static_cast<uint8_t>((Value[8] - '0') * 10 + (Value[9] - '0'));
outTime.Minute = static_cast<uint8_t>((Value[10] - '0') * 10 + (Value[11] - '0'));
outTime.Second = static_cast<uint8_t>((Value[12] - '0') * 10 + (Value[13] - '0'));
return CHIP_NO_ERROR;
}
static uint8_t ReverseBits(uint8_t v)
{
// swap adjacent bits
v = static_cast<uint8_t>((v >> 1) & 0x55) | static_cast<uint8_t>((v & 0x55) << 1);
// swap adjacent bit pairs
v = static_cast<uint8_t>((v >> 2) & 0x33) | static_cast<uint8_t>((v & 0x33) << 2);
// swap nibbles
v = static_cast<uint8_t>(v >> 4) | static_cast<uint8_t>(v << 4);
return v;
}
CHIP_ERROR ASN1Reader::GetBitString(uint32_t & outVal)
{
// NOTE: only supports DER encoding.
ReturnErrorCodeIf(Value == nullptr, ASN1_ERROR_INVALID_STATE);
ReturnErrorCodeIf(ValueLen < 1, ASN1_ERROR_INVALID_ENCODING);
ReturnErrorCodeIf(ValueLen > 5, ASN1_ERROR_UNSUPPORTED_ENCODING);
ReturnErrorCodeIf(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<uint32_t>(ReverseBits(Value[i]) << shift);
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ASN1Reader::DecodeHead()
{
const uint8_t * p = mElemStart;
ReturnErrorCodeIf(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++;
ReturnErrorCodeIf(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++)
{
ReturnErrorCodeIf(p >= mBufEnd, ASN1_ERROR_UNDERRUN);
ReturnErrorCodeIf((ValueLen & 0xFF000000) != 0, ASN1_ERROR_LENGTH_OVERFLOW);
ValueLen = (ValueLen << 8) | *p;
}
IndefiniteLen = false;
}
VerifyOrReturnError(CanCastTo<uint32_t>(p - mElemStart), ASN1_ERROR_VALUE_OVERFLOW);
mHeadLen = static_cast<uint32_t>(p - mElemStart);
EndOfContents = (Class == kASN1TagClass_Universal && Tag == 0 && Constructed == false && 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;
}
else
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<unsigned long>(asn1Parser.GetTag()));
break;
}
else if (asn1Parser.GetClass() == kASN1TagClass_Application)
printf("[APPLICATION %lu] ", static_cast<unsigned long>(asn1Parser.GetTag()));
else if (asn1Parser.GetClass() == kASN1TagClass_ContextSpecific)
printf("[%lu] ", static_cast<unsigned long>(asn1Parser.GetTag()));
else if (asn1Parser.GetClass() == kASN1TagClass_Private)
printf("[PRIVATE %lu] ", static_cast<unsigned long>(asn1Parser.GetTag()));
if (asn1Parser.IsConstructed())
printf("(constructed) ");
if (asn1Parser.IsIndefiniteLen())
printf("Length = indefinite\n");
else
printf("Length = %ld\n", static_cast<long>(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