blob: 96d1aa92167f2e4df2a9eb22b8abeb8d1b49c5c8 [file] [log] [blame]
/*
*
* Copyright (c) 2020 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 <asn1/ASN1.h>
namespace chip {
namespace ASN1 {
void ASN1Reader::Init(const uint8_t * buf, uint32_t len)
{
ResetElementState();
mBuf = buf;
mBufEnd = buf + len;
mElemStart = buf;
mContainerEnd = mBufEnd;
mNumSavedContexts = 0;
}
ASN1_ERROR ASN1Reader::Next()
{
if (EndOfContents)
return ASN1_END;
if (IndefiniteLen)
{
return ASN1_ERROR_UNSUPPORTED_ENCODING;
}
else
mElemStart += (mHeadLen + ValueLen);
ResetElementState();
if (mElemStart == mContainerEnd)
return ASN1_END;
return DecodeHead();
}
ASN1_ERROR ASN1Reader::EnterConstructedType()
{
if (!Constructed)
return ASN1_ERROR_INVALID_STATE;
return EnterContainer(0);
}
ASN1_ERROR ASN1Reader::ExitConstructedType()
{
return ExitContainer();
}
ASN1_ERROR ASN1Reader::GetConstructedType(const uint8_t *& val, uint32_t & valLen)
{
if (!Constructed)
return ASN1_ERROR_INVALID_STATE;
val = mElemStart;
valLen = mHeadLen + ValueLen;
return ASN1_NO_ERROR;
}
ASN1_ERROR ASN1Reader::EnterEncapsulatedType()
{
if (Class != kASN1TagClass_Universal || (Tag != kASN1UniversalTag_OctetString && Tag != kASN1UniversalTag_BitString))
return ASN1_ERROR_INVALID_STATE;
if (Constructed)
return ASN1_ERROR_UNSUPPORTED_ENCODING;
return EnterContainer((Tag == kASN1UniversalTag_BitString) ? 1 : 0);
}
ASN1_ERROR ASN1Reader::ExitEncapsulatedType()
{
return ExitContainer();
}
ASN1_ERROR ASN1Reader::EnterContainer(uint32_t offset)
{
if (mNumSavedContexts == kMaxContextDepth)
return 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 ASN1_NO_ERROR;
}
ASN1_ERROR ASN1Reader::ExitContainer()
{
if (mNumSavedContexts == 0)
return ASN1_ERROR_INVALID_STATE;
ASN1ParseContext & prevContext = mSavedContexts[--mNumSavedContexts];
if (prevContext.IndefiniteLen)
{
return ASN1_ERROR_UNSUPPORTED_ENCODING;
}
else
mElemStart = prevContext.ElemStart + prevContext.HeadLen + prevContext.ValueLen;
mContainerEnd = prevContext.ContainerEnd;
ResetElementState();
return ASN1_NO_ERROR;
}
bool ASN1Reader::IsContained() const
{
return mNumSavedContexts > 0;
}
ASN1_ERROR ASN1Reader::GetInteger(int64_t & val)
{
if (Value == nullptr)
return ASN1_ERROR_INVALID_STATE;
if (ValueLen < 1)
return ASN1_ERROR_INVALID_ENCODING;
if (ValueLen > sizeof(int64_t))
return ASN1_ERROR_VALUE_OVERFLOW;
if (mElemStart + mHeadLen + ValueLen > mContainerEnd)
return 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 ASN1_NO_ERROR;
}
ASN1_ERROR ASN1Reader::GetBoolean(bool & val)
{
if (Value == nullptr)
return ASN1_ERROR_INVALID_STATE;
if (ValueLen != 1)
return ASN1_ERROR_INVALID_ENCODING;
if (mElemStart + mHeadLen + ValueLen > mContainerEnd)
return ASN1_ERROR_UNDERRUN;
if (*Value == 0)
val = false;
else if (*Value == 0xFF)
val = true;
else
return ASN1_ERROR_INVALID_ENCODING;
return ASN1_NO_ERROR;
}
ASN1_ERROR ASN1Reader::GetUTCTime(ASN1UniversalTime & outTime)
{
// Supported Encoding: YYMMDDHHMMSSZ
if (Value == nullptr)
return ASN1_ERROR_INVALID_STATE;
if (ValueLen < 1)
return ASN1_ERROR_INVALID_ENCODING;
if (mElemStart + mHeadLen + ValueLen > mContainerEnd)
return ASN1_ERROR_UNDERRUN;
if (ValueLen != 13 || Value[12] != 'Z')
return ASN1_ERROR_UNSUPPORTED_ENCODING;
for (int i = 0; i < 12; i++)
if (!isdigit(Value[i]))
return ASN1_ERROR_INVALID_ENCODING;
outTime.Year = (Value[0] - '0') * 10 + (Value[1] - '0');
outTime.Month = (Value[2] - '0') * 10 + (Value[3] - '0');
outTime.Day = (Value[4] - '0') * 10 + (Value[5] - '0');
outTime.Hour = (Value[6] - '0') * 10 + (Value[7] - '0');
outTime.Minute = (Value[8] - '0') * 10 + (Value[9] - '0');
outTime.Second = (Value[10] - '0') * 10 + (Value[11] - '0');
if (outTime.Year >= 50)
outTime.Year += 1900;
else
outTime.Year += 2000;
return ASN1_NO_ERROR;
}
ASN1_ERROR ASN1Reader::GetGeneralizedTime(ASN1UniversalTime & outTime)
{
// Supported Encoding: YYYYMMDDHHMMSSZ
if (Value == nullptr)
return ASN1_ERROR_INVALID_STATE;
if (ValueLen < 1)
return ASN1_ERROR_INVALID_ENCODING;
if (mElemStart + mHeadLen + ValueLen > mContainerEnd)
return ASN1_ERROR_UNDERRUN;
if (ValueLen != 15 || Value[14] != 'Z')
return ASN1_ERROR_UNSUPPORTED_ENCODING;
for (int i = 0; i < 14; i++)
if (!isdigit(Value[i]))
return ASN1_ERROR_INVALID_ENCODING;
outTime.Year = (Value[0] - '0') * 1000 + (Value[1] - '0') * 100 + (Value[2] - '0') * 10 + (Value[3] - '0');
outTime.Month = (Value[4] - '0') * 10 + (Value[5] - '0');
outTime.Day = (Value[6] - '0') * 10 + (Value[7] - '0');
outTime.Hour = (Value[8] - '0') * 10 + (Value[9] - '0');
outTime.Minute = (Value[10] - '0') * 10 + (Value[11] - '0');
outTime.Second = (Value[12] - '0') * 10 + (Value[13] - '0');
return ASN1_NO_ERROR;
}
static uint8_t ReverseBits(uint8_t v)
{
// swap adjacent bits
v = ((v >> 1) & 0x55) | ((v & 0x55) << 1);
// swap adjacent bit pairs
v = ((v >> 2) & 0x33) | ((v & 0x33) << 2);
// swap nibbles
v = (v >> 4) | (v << 4);
return v;
}
ASN1_ERROR ASN1Reader::GetBitString(uint32_t & outVal)
{
// NOTE: only supports DER encoding.
if (Value == nullptr)
return ASN1_ERROR_INVALID_STATE;
if (ValueLen < 1)
return ASN1_ERROR_INVALID_ENCODING;
if (ValueLen > 5)
return ASN1_ERROR_UNSUPPORTED_ENCODING;
if (mElemStart + mHeadLen + ValueLen > mContainerEnd)
return 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 |= (ReverseBits(Value[i]) << shift);
}
return ASN1_NO_ERROR;
}
ASN1_ERROR ASN1Reader::DecodeHead()
{
const uint8_t * p = mElemStart;
if (p >= mBufEnd)
return ASN1_ERROR_UNDERRUN;
Class = *p & 0xC0;
Constructed = (*p & 0x20) != 0;
Tag = *p & 0x1F;
p++;
if (Tag == 0x1F)
{
Tag = 0;
do
{
if (p >= mBufEnd)
return ASN1_ERROR_UNDERRUN;
if ((Tag & 0xFE000000) != 0)
return ASN1_ERROR_TAG_OVERFLOW;
Tag = (Tag << 7) | (*p & 0x7F);
p++;
} while ((*p & 0x80) != 0);
}
if (p >= mBufEnd)
return 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++)
{
if (p >= mBufEnd)
return ASN1_ERROR_UNDERRUN;
if ((ValueLen & 0xFF000000) != 0)
return ASN1_ERROR_LENGTH_OVERFLOW;
ValueLen = (ValueLen << 8) | *p;
}
IndefiniteLen = false;
}
mHeadLen = p - mElemStart;
EndOfContents = (Class == kASN1TagClass_Universal && Tag == 0 && Constructed == false && ValueLen == 0);
Value = p;
return ASN1_NO_ERROR;
}
void ASN1Reader::ResetElementState()
{
Class = 0;
Tag = 0;
Value = nullptr;
ValueLen = 0;
Constructed = false;
IndefiniteLen = false;
EndOfContents = false;
mHeadLen = 0;
}
ASN1_ERROR DumpASN1(ASN1Reader & asn1Parser, const char * prefix, const char * indent)
{
ASN1_ERROR err = ASN1_NO_ERROR;
if (indent == nullptr)
indent = " ";
int nestLevel = 0;
while (true)
{
err = asn1Parser.Next();
if (err != ASN1_NO_ERROR)
{
if (err == ASN1_END)
{
if (asn1Parser.IsContained())
{
err = asn1Parser.ExitConstructedType();
if (err != ASN1_NO_ERROR)
{
printf("ASN1Reader::ExitConstructedType() failed: %ld\n", (long) err);
return err;
}
nestLevel--;
continue;
}
else
break;
}
printf("ASN1Reader::Next() failed: %ld\n", (long) err);
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 != ASN1_NO_ERROR)
{
printf("ASN1Reader::EnterConstructedType() failed: %ld\n", (long) err);
return err;
}
nestLevel++;
}
}
return err;
}
} // namespace ASN1
} // namespace chip