| /* |
| * |
| * 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. |
| * |
| */ |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Support/ASN1.h> |
| #include <Weave/Core/WeaveEncoding.h> |
| #include <Weave/Support/CodeUtils.h> |
| |
| namespace nl { |
| namespace Weave { |
| namespace ASN1 { |
| |
| using namespace nl::Weave::Encoding; |
| |
| enum |
| { |
| kLengthFieldReserveSize = 5, |
| kMaxElementLength = INT32_MAX, |
| kUnkownLength = -1, |
| kUnknownLengthMarker = 0xFF |
| }; |
| |
| |
| void ASN1Writer::Init(uint8_t *buf, uint32_t maxLen) |
| { |
| mBuf = buf; |
| mWritePoint = buf; |
| mBufEnd = buf + maxLen; |
| mBufEnd = (uint8_t *)((uintptr_t )mBufEnd & ~3); // align on 32bit boundary |
| mDeferredLengthList = (uint8_t **)mBufEnd; |
| } |
| |
| ASN1_ERROR ASN1Writer::Finalize() |
| { |
| uint8_t *compactPoint = mBuf; |
| uint8_t *spanStart = mBuf; |
| |
| for (uint8_t **listEntry = (uint8_t **)mBufEnd; listEntry > mDeferredLengthList; ) |
| { |
| uint8_t *lenField = *--listEntry; |
| uint8_t lenFieldFirstByte = *lenField; |
| |
| if (lenFieldFirstByte == kUnknownLengthMarker) |
| return ASN1_ERROR_INVALID_STATE; |
| |
| uint8_t lenOfLen = (lenFieldFirstByte < 128) ? 1 : (lenFieldFirstByte & 0x7f) + 1; |
| |
| uint8_t *spanEnd = lenField + lenOfLen; |
| |
| if (spanStart == compactPoint) |
| compactPoint = spanEnd; |
| else |
| { |
| uint32_t spanLen = spanEnd - spanStart; |
| memmove(compactPoint, spanStart, spanLen); |
| compactPoint += spanLen; |
| } |
| |
| spanStart = lenField + kLengthFieldReserveSize; |
| } |
| |
| if (spanStart > compactPoint) |
| { |
| uint32_t spanLen = mWritePoint - spanStart; |
| memmove(compactPoint, spanStart, spanLen); |
| compactPoint += spanLen; |
| } |
| |
| mWritePoint = compactPoint; |
| |
| return ASN1_NO_ERROR; |
| } |
| |
| uint16_t ASN1Writer::GetLengthWritten() const |
| { |
| return mWritePoint - mBuf; |
| } |
| |
| ASN1_ERROR ASN1Writer::PutInteger(int64_t val) |
| { |
| uint8_t encodedVal[8]; |
| uint8_t valStart, valLen; |
| |
| BigEndian::Put64(encodedVal, (uint64_t)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 = 8 - valStart; |
| |
| return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false, encodedVal + valStart, valLen); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutBoolean(bool val) |
| { |
| ASN1_ERROR err; |
| |
| err = EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_Boolean, false, 1); |
| SuccessOrExit(err); |
| |
| *mWritePoint++ = (val) ? 0xFF : 0; |
| |
| exit: |
| return err; |
| } |
| |
| ASN1_ERROR ASN1Writer::PutObjectId(const uint8_t *val, uint16_t valLen) |
| { |
| return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId, false, val, valLen); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutString(uint32_t tag, const char *val, uint16_t valLen) |
| { |
| return PutValue(kASN1TagClass_Universal, tag, false, (const uint8_t *)val, valLen); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutOctetString(const uint8_t *val, uint16_t valLen) |
| { |
| return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_OctetString, false, val, valLen); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutOctetString(uint8_t cls, uint32_t tag, const uint8_t *val, uint16_t valLen) |
| { |
| return PutValue(cls, tag, false, val, valLen); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutOctetString(uint8_t cls, uint32_t tag, nl::Weave::TLV::TLVReader& val) |
| { |
| return PutValue(cls, tag, false, val); |
| } |
| |
| 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; |
| } |
| |
| 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 highestBit; |
| } |
| |
| ASN1_ERROR ASN1Writer::PutBitString(uint32_t val) |
| { |
| ASN1_ERROR err; |
| 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; |
| |
| err = EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_BitString, false, len); |
| SuccessOrExit(err); |
| |
| if (val == 0) |
| mWritePoint[0] = 0; |
| else |
| { |
| mWritePoint[1] = ReverseBits((uint8_t)val); |
| if (len >= 3) |
| { |
| val >>= 8; |
| mWritePoint[2] = ReverseBits((uint8_t)val); |
| if (len >= 4) |
| { |
| val >>= 8; |
| mWritePoint[3] = ReverseBits((uint8_t)val); |
| if (len == 5) |
| { |
| val >>= 8; |
| mWritePoint[4] = ReverseBits((uint8_t)val); |
| } |
| } |
| } |
| mWritePoint[0] = 7 - HighestBit(val); |
| } |
| |
| mWritePoint += len; |
| |
| exit: |
| return err; |
| } |
| |
| ASN1_ERROR ASN1Writer::PutBitString(uint8_t unusedBitCount, const uint8_t *encodedBits, uint16_t encodedBitsLen) |
| { |
| ASN1_ERROR err; |
| |
| err = EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_BitString, false, encodedBitsLen + 1); |
| SuccessOrExit(err); |
| |
| *mWritePoint++ = unusedBitCount; |
| |
| memcpy(mWritePoint, encodedBits, encodedBitsLen); |
| mWritePoint += encodedBitsLen; |
| |
| exit: |
| return err; |
| } |
| |
| ASN1_ERROR ASN1Writer::PutBitString(uint8_t unusedBitCount, nl::Weave::TLV::TLVReader& encodedBits) |
| { |
| ASN1_ERROR err; |
| uint32_t encodedBitsLen = encodedBits.GetLength(); |
| |
| err = EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_BitString, false, encodedBitsLen + 1); |
| SuccessOrExit(err); |
| |
| *mWritePoint++ = unusedBitCount; |
| |
| encodedBits.GetBytes(mWritePoint, encodedBitsLen); |
| mWritePoint += encodedBitsLen; |
| |
| exit: |
| return err; |
| } |
| |
| static void itoa2(uint32_t val, uint8_t *buf) |
| { |
| buf[1] = '0' + (val % 10); |
| val /= 10; |
| buf[0] = '0' + (val % 10); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutTime(const ASN1UniversalTime& val) |
| { |
| uint8_t buf[15]; |
| |
| itoa2(val.Year / 100, buf); |
| itoa2(val.Year, buf + 2); |
| itoa2(val.Month, buf + 4); |
| itoa2(val.Day, buf + 6); |
| itoa2(val.Hour, buf + 8); |
| itoa2(val.Minute, buf + 10); |
| itoa2(val.Second, buf + 12); |
| buf[14] = 'Z'; |
| |
| // X.509/RFC5280 mandates that times before 2050 UTC must be encoded as ASN.1 UTCTime values, while |
| // times equal or greater than 2050 must be encoded as GeneralizedTime values. The only difference |
| // (in the context of X.509 DER) is that GeneralizedTimes are encoded with a 4 digit year, while |
| // UTCTimes are encoded with a two-digit year. |
| // |
| if (val.Year >= 2050) |
| return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_GeneralizedTime, false, buf, 15); |
| else |
| return PutValue(kASN1TagClass_Universal, kASN1UniversalTag_UTCTime, false, buf + 2, 13); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutNull() |
| { |
| return EncodeHead(kASN1TagClass_Universal, kASN1UniversalTag_Null, false, 0); |
| } |
| |
| ASN1_ERROR ASN1Writer::StartConstructedType(uint8_t cls, uint32_t tag) |
| { |
| return EncodeHead(cls, tag, true, kUnkownLength); |
| } |
| |
| ASN1_ERROR ASN1Writer::EndConstructedType() |
| { |
| return WriteDeferredLength(); |
| } |
| |
| ASN1_ERROR ASN1Writer::StartEncapsulatedType(uint8_t cls, uint32_t tag, bool bitStringEncoding) |
| { |
| ASN1_ERROR err; |
| |
| err = EncodeHead(cls, tag, false, kUnkownLength); |
| SuccessOrExit(err); |
| |
| // 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) |
| { |
| if (mWritePoint == (uint8_t *)mDeferredLengthList) |
| return ASN1_ERROR_OVERFLOW; |
| *mWritePoint++ = 0; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| ASN1_ERROR ASN1Writer::EndEncapsulatedType() |
| { |
| return WriteDeferredLength(); |
| } |
| |
| ASN1_ERROR ASN1Writer::PutValue(uint8_t cls, uint32_t tag, bool isConstructed, const uint8_t *val, uint16_t valLen) |
| { |
| ASN1_ERROR err; |
| |
| err = EncodeHead(cls, tag, isConstructed, valLen); |
| SuccessOrExit(err); |
| |
| memcpy(mWritePoint, val, valLen); |
| mWritePoint += valLen; |
| |
| exit: |
| return err; |
| } |
| |
| ASN1_ERROR ASN1Writer::PutValue(uint8_t cls, uint32_t tag, bool isConstructed, nl::Weave::TLV::TLVReader& val) |
| { |
| ASN1_ERROR err; |
| uint32_t valLen = val.GetLength(); |
| |
| err = EncodeHead(cls, tag, isConstructed, valLen); |
| SuccessOrExit(err); |
| |
| val.GetBytes(mWritePoint, valLen); |
| mWritePoint += valLen; |
| |
| exit: |
| return err; |
| } |
| |
| ASN1_ERROR ASN1Writer::EncodeHead(uint8_t cls, uint32_t tag, bool isConstructed, int32_t len) |
| { |
| // Only tags <= 31 supported. The implication of this is that encoded tags are exactly 1 byte long. |
| if (tag >= 0x1F) |
| return ASN1_ERROR_UNSUPPORTED_ENCODING; |
| |
| // Compute the number of bytes required to encode the length. |
| uint8_t lenOfLen = GetLengthOfLength(len); |
| |
| // If the element length is unknown, allocate a new entry in the deferred-length list. |
| // |
| // The deferred-length list is a list of "pointers" (represented as offsets into mBuf) |
| // 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 is |
| // complete (e.g. when EndConstructed() is called). |
| // |
| if (len == kUnkownLength) |
| mDeferredLengthList--; |
| |
| // Make sure there's enough space to encode the entire value without bumping into the deferred length |
| // list at the end of the buffer. |
| uint16_t totalLen = 1 + lenOfLen + (len != kUnkownLength ? len : 0); |
| if ((mWritePoint + totalLen) > (uint8_t *)mDeferredLengthList) |
| return ASN1_ERROR_OVERFLOW; |
| |
| // Write the tag byte. |
| *mWritePoint++ = cls | (isConstructed ? 0x20 : 0) | tag; |
| |
| // Encode the length if it is known. |
| if (len != kUnkownLength) |
| EncodeLength(mWritePoint, lenOfLen, 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 list. |
| else |
| { |
| *mWritePoint = kUnknownLengthMarker; |
| *mDeferredLengthList = mWritePoint; |
| } |
| |
| mWritePoint += lenOfLen; |
| |
| return ASN1_NO_ERROR; |
| } |
| |
| ASN1_ERROR ASN1Writer::WriteDeferredLength() |
| { |
| uint8_t **listEntry; |
| uint32_t lenAdj = kLengthFieldReserveSize; |
| |
| // Scan the deferred-length list in reverse order looking for the most recent entry where |
| // the length is still unknown. This entry represents the "container" element whose encoding |
| // is now complete. |
| for (listEntry = mDeferredLengthList; listEntry < (uint8_t **)mBufEnd; listEntry++) |
| { |
| // Get a pointer to the deferred-length field. |
| uint8_t *lenField = *listEntry; |
| |
| // Get the first byte of the length field. |
| uint8_t lenFieldFirstByte = *lenField; |
| |
| // If the length is marked as unknown... |
| if (lenFieldFirstByte == kUnknownLengthMarker) |
| { |
| // Compute the final length of the element's value (3 = bytes reserved for length). |
| uint32_t elemLen = (mWritePoint - lenField) - lenAdj; |
| |
| // Return an error if the length exceeds the maximum value that can be encoded in the |
| // space reserved for the length. |
| if (elemLen > kMaxElementLength) |
| return ASN1_ERROR_LENGTH_OVERFLOW; |
| |
| // Encode the final length of the element, overwriting the unknown length marker |
| // in the process. Note that the number of bytes consumed by the final length field |
| // may be smaller than the space that was reserved for the field. This will be fixed |
| // up when the Finalize() method is called. |
| uint8_t lenOfLen = GetLengthOfLength((int32_t)elemLen); |
| EncodeLength(lenField, lenOfLen, elemLen); |
| |
| return ASN1_NO_ERROR; |
| } |
| else |
| { |
| uint8_t lenOfLen = (lenFieldFirstByte < 128) ? 1 : (lenFieldFirstByte & 0x7f) + 1; |
| lenAdj += (kLengthFieldReserveSize - lenOfLen); |
| } |
| } |
| |
| return ASN1_ERROR_INVALID_STATE; |
| } |
| |
| uint8_t ASN1Writer::GetLengthOfLength(int32_t len) |
| { |
| if (len == kUnkownLength) |
| 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 lenOfLen, int32_t lenToEncode) |
| { |
| if (lenOfLen == 1) |
| buf[0] = (uint8_t)lenToEncode; |
| else |
| { |
| --lenOfLen; |
| buf[0] = 0x80 | lenOfLen; |
| do |
| { |
| buf[lenOfLen] = (uint8_t)lenToEncode; |
| lenToEncode >>= 8; |
| } while (--lenOfLen); |
| } |
| } |
| |
| |
| |
| |
| } // namespace ASN1 |
| } // namespace Weave |
| } // namespace nl |