blob: db5646a13dba3807a8dedd6861edcf84fac4f1a7 [file] [log] [blame]
/*
*
* 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