/* * * 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 writing Abstract Syntax * Notation One (ASN.1) encoded data. * */ #include #include #include #include #include #include #include #include #include #include #include namespace chip { namespace ASN1 { using namespace chip::Encoding; enum { kLengthFieldReserveSize = 1, kUnknownLength = -1, kUnknownLengthMarker = 0xFF }; void ASN1Writer::Init(uint8_t * buf, size_t maxLen) { mBuf = buf; mWritePoint = buf; mBufEnd = buf + maxLen; mDeferredLengthCount = 0; } void ASN1Writer::InitNullWriter() { mBuf = nullptr; mWritePoint = nullptr; mBufEnd = nullptr; mDeferredLengthCount = 0; } size_t ASN1Writer::GetLengthWritten() const { return (mBuf != nullptr) ? static_cast(mWritePoint - mBuf) : 0; } CHIP_ERROR ASN1Writer::PutInteger(int64_t val) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); uint8_t encodedVal[sizeof(int64_t)]; uint8_t valStart, valLen; BigEndian::Put64(encodedVal, static_cast(val)); for (valStart = 0; valStart < 7; valStart++) { if (encodedVal[valStart] == 0x00 && (encodedVal[valStart + 1] & 0x80) == 0) continue; if (encodedVal[valStart] == 0xFF && (encodedVal[valStart + 1] & 0x80) == 0x80) continue; break; } valLen = static_cast(8 - valStart); return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, encodedVal + valStart, valLen); } CHIP_ERROR ASN1Writer::PutBoolean(bool val) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); ReturnErrorOnFailure(EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_Boolean, false, 1)); *mWritePoint++ = (val) ? 0xFF : 0; return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::PutObjectId(const uint8_t * val, uint16_t valLen) { return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId, false, val, valLen); } CHIP_ERROR ASN1Writer::PutString(uint8_t tag, const char * val, uint16_t valLen) { return PutValue(kASN1TagClass_Universal, tag, false, (const uint8_t *) val, valLen); } CHIP_ERROR ASN1Writer::PutOctetString(const uint8_t * val, uint16_t valLen) { return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_OctetString, false, val, valLen); } CHIP_ERROR ASN1Writer::PutOctetString(uint8_t cls, uint8_t tag, const uint8_t * val, uint16_t valLen) { return PutValue(cls, tag, false, val, valLen); } CHIP_ERROR ASN1Writer::PutOctetString(uint8_t cls, uint8_t tag, chip::TLV::TLVReader & tlvReader) { return PutValue(cls, tag, false, tlvReader); } 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; } static uint8_t HighestBit(uint32_t v) { uint32_t highestBit = 0; if (v > 0xFFFF) { highestBit = 16; v >>= 16; } if (v > 0xFF) { highestBit |= 8; v >>= 8; } if (v > 0xF) { highestBit |= 4; v >>= 4; } if (v > 0x3) { highestBit |= 2; v >>= 2; } highestBit |= (v >> 1); return static_cast(highestBit); } CHIP_ERROR ASN1Writer::PutBitString(uint32_t val) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); uint8_t len; if (val == 0) len = 1; else if (val < 256) len = 2; else if (val < 65536) len = 3; else if (val < (1 << 24)) len = 4; else len = 5; ReturnErrorOnFailure(EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_BitString, false, len)); if (val == 0) { mWritePoint[0] = 0; } else { mWritePoint[1] = ReverseBits(static_cast(val)); if (len >= 3) { val >>= 8; mWritePoint[2] = ReverseBits(static_cast(val)); if (len >= 4) { val >>= 8; mWritePoint[3] = ReverseBits(static_cast(val)); if (len == 5) { val >>= 8; mWritePoint[4] = ReverseBits(static_cast(val)); } } } mWritePoint[0] = static_cast(7 - HighestBit(val)); } mWritePoint += len; return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::PutBitString(uint8_t unusedBitCount, const uint8_t * encodedBits, uint16_t encodedBitsLen) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); ReturnErrorOnFailure(EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_BitString, false, encodedBitsLen + 1)); *mWritePoint++ = unusedBitCount; WriteData(encodedBits, encodedBitsLen); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::PutBitString(uint8_t unusedBitCount, chip::TLV::TLVReader & tlvReader) { ByteSpan encodedBits; ReturnErrorOnFailure(tlvReader.Get(encodedBits)); VerifyOrReturnError(CanCastTo(encodedBits.size() + 1), ASN1_ERROR_LENGTH_OVERFLOW); VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); ReturnErrorOnFailure( EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_BitString, false, static_cast(encodedBits.size() + 1))); *mWritePoint++ = unusedBitCount; WriteData(encodedBits.data(), encodedBits.size()); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::PutTime(const ASN1UniversalTime & val) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); char buf[ASN1UniversalTime::kASN1TimeStringMaxLength]; MutableCharSpan bufSpan(buf); uint8_t tag; ReturnErrorOnFailure(val.ExportTo_ASN1_TIME_string(bufSpan)); if (val.Year >= 2050) { tag = kASN1UniversalTag_GeneralizedTime; } else { tag = kASN1UniversalTag_UTCTime; } return PutValue(kASN1TagClass_Universal, tag, false, reinterpret_cast(buf), static_cast(bufSpan.size())); } CHIP_ERROR ASN1Writer::PutNull() { return EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_Null, false, 0); } CHIP_ERROR ASN1Writer::PutConstructedType(const uint8_t * val, uint16_t valLen) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); // Make sure we have enough space to write VerifyOrReturnError((mWritePoint + valLen) <= mBufEnd, ASN1_ERROR_OVERFLOW); WriteData(val, valLen); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::StartConstructedType(uint8_t cls, uint8_t tag) { return EncodeHead(cls, tag, true, kUnknownLength); } CHIP_ERROR ASN1Writer::EndConstructedType() { return WriteDeferredLength(); } CHIP_ERROR ASN1Writer::StartEncapsulatedType(uint8_t cls, uint8_t tag, bool bitStringEncoding) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); ReturnErrorOnFailure(EncodeHead(cls, tag, false, kUnknownLength)); // If the encapsulating type is BIT STRING, encode the unused bit count field. Since the BIT // STRING contains an ASN.1 DER encoding, and ASN.1 DER encodings are always multiples of 8 bits, // the unused bit count is always 0. if (bitStringEncoding) { VerifyOrReturnError(mWritePoint < mBufEnd, ASN1_ERROR_OVERFLOW); *mWritePoint++ = 0; } return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::EndEncapsulatedType() { return WriteDeferredLength(); } CHIP_ERROR ASN1Writer::PutValue(uint8_t cls, uint8_t tag, bool isConstructed, const uint8_t * val, uint16_t valLen) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); ReturnErrorOnFailure(EncodeHead(cls, tag, isConstructed, valLen)); WriteData(val, valLen); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::PutValue(uint8_t cls, uint8_t tag, bool isConstructed, chip::TLV::TLVReader & tlvReader) { ByteSpan val; ReturnErrorOnFailure(tlvReader.Get(val)); VerifyOrReturnError(CanCastTo(val.size()), ASN1_ERROR_LENGTH_OVERFLOW); VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); ReturnErrorOnFailure(EncodeHead(cls, tag, isConstructed, static_cast(val.size()))); WriteData(val.data(), val.size()); return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::EncodeHead(uint8_t cls, uint8_t tag, bool isConstructed, int32_t len) { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); uint8_t bytesForLen; uint32_t totalLen; // Only tags < 31 supported. The implication of this is that encoded tags are exactly 1 byte long. VerifyOrReturnError(tag < 0x1F, ASN1_ERROR_UNSUPPORTED_ENCODING); // Only positive and kUnknownLength values are supported for len input. VerifyOrReturnError(len >= 0 || len == kUnknownLength, ASN1_ERROR_UNSUPPORTED_ENCODING); // Compute the number of bytes required to encode the length. bytesForLen = BytesForLength(len); // Make sure there's enough space to encode the entire value. // Note that the calculated total length doesn't overflow because `len` is a signed value (int32_t). // Note that if `len` is not kUnknownLength then it is non-negative (`len` >= 0). totalLen = 1 + bytesForLen + static_cast(len != kUnknownLength ? len : 0); VerifyOrReturnError((mWritePoint + totalLen) <= mBufEnd, ASN1_ERROR_OVERFLOW); // Write the tag byte. *mWritePoint++ = cls | static_cast(isConstructed ? 0x20 : 0) | tag; // Encode the length if it is known. if (len != kUnknownLength) { EncodeLength(mWritePoint, bytesForLen, len); } // ... otherwise place a marker in the first byte of the length to indicate that the length is unknown // and save a pointer to the length field in the deferred-length array. // // The deferred-length is an array of "pointers" to length fields for which the length of the // element was unknown at the time the element head was written. Examples include constructed // types such as SEQUENCE and SET, as well non-constructed types that encapsulate other ASN.1 types // (e.g. OCTET STRINGS that contain BER/DER encodings). The final lengths are filled in later, // at the time the encoding of the element is complete (e.g. when EndConstructed() is called). else { VerifyOrReturnError(mDeferredLengthCount < kMaxDeferredLengthDepth, ASN1_ERROR_INVALID_STATE); *mWritePoint = kUnknownLengthMarker; mDeferredLengthLocations[mDeferredLengthCount++] = mWritePoint; } mWritePoint += bytesForLen; return CHIP_NO_ERROR; } CHIP_ERROR ASN1Writer::WriteDeferredLength() { VerifyOrReturnError(!IsNullWriter(), CHIP_NO_ERROR); VerifyOrReturnError(mDeferredLengthCount > 0, ASN1_ERROR_INVALID_STATE); uint8_t * lenField = mDeferredLengthLocations[mDeferredLengthCount - 1]; VerifyOrReturnError(*lenField == kUnknownLengthMarker, ASN1_ERROR_INVALID_STATE); // Compute the length of the element's value. size_t elemLen = static_cast((mWritePoint - lenField) - kLengthFieldReserveSize); VerifyOrReturnError(CanCastTo(elemLen), ASN1_ERROR_LENGTH_OVERFLOW); uint8_t bytesForLen = BytesForLength(static_cast(elemLen)); // Move the element data if the number of bytes consumed by the final length field // is different than the space that was reserved for the field. if (bytesForLen != kLengthFieldReserveSize) { mWritePoint += (bytesForLen - kLengthFieldReserveSize); VerifyOrReturnError(mWritePoint <= mBufEnd, ASN1_ERROR_OVERFLOW); memmove(lenField + bytesForLen, lenField + kLengthFieldReserveSize, elemLen); } // Encode the final length of the element, overwriting the unknown length marker // in the process. EncodeLength(lenField, bytesForLen, static_cast(elemLen)); mDeferredLengthCount--; return CHIP_NO_ERROR; } /** * Returns the number of bytes required to encode the length value. * * @param[in] len Parameter, which encoding length to be calculated. * * @return number of bytes required to encode the length value. */ uint8_t ASN1Writer::BytesForLength(int32_t len) { if (len == kUnknownLength) return kLengthFieldReserveSize; if (len < 128) return 1; if (len < 256) return 2; if (len < 65536) return 3; if (len < (1 << 24)) return 4; return 5; } void ASN1Writer::EncodeLength(uint8_t * buf, uint8_t bytesForLen, int32_t lenToEncode) { if (bytesForLen == 1) { buf[0] = static_cast(lenToEncode); } else { --bytesForLen; buf[0] = 0x80 | bytesForLen; do { buf[bytesForLen] = static_cast(lenToEncode); lenToEncode >>= 8; } while (--bytesForLen); } } void ASN1Writer::WriteData(const uint8_t * p, size_t len) { memcpy(mWritePoint, p, len); mWritePoint += len; } } // namespace ASN1 } // namespace chip