|  | // Copyright 2021 Google LLC | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //     https://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. | 
|  |  | 
|  | #include "dice/cbor_writer.h" | 
|  |  | 
|  | #include <stdbool.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  | #include <string.h> | 
|  |  | 
|  | enum CborType { | 
|  | CBOR_TYPE_UINT = 0, | 
|  | CBOR_TYPE_NINT = 1, | 
|  | CBOR_TYPE_BSTR = 2, | 
|  | CBOR_TYPE_TSTR = 3, | 
|  | CBOR_TYPE_ARRAY = 4, | 
|  | CBOR_TYPE_MAP = 5, | 
|  | CBOR_TYPE_TAG = 6, | 
|  | CBOR_TYPE_SIMPLE = 7, | 
|  | }; | 
|  |  | 
|  | static bool CborWriteWouldOverflowCursor(size_t size, struct CborOut* out) { | 
|  | return size > SIZE_MAX - out->cursor; | 
|  | } | 
|  |  | 
|  | static bool CborWriteFitsInBuffer(size_t size, struct CborOut* out) { | 
|  | return out->cursor <= out->buffer_size && | 
|  | size <= out->buffer_size - out->cursor; | 
|  | } | 
|  |  | 
|  | static void CborWriteType(enum CborType type, uint64_t val, | 
|  | struct CborOut* out) { | 
|  | size_t size; | 
|  | if (val <= 23) { | 
|  | size = 1; | 
|  | } else if (val <= 0xff) { | 
|  | size = 2; | 
|  | } else if (val <= 0xffff) { | 
|  | size = 3; | 
|  | } else if (val <= 0xffffffff) { | 
|  | size = 5; | 
|  | } else { | 
|  | size = 9; | 
|  | } | 
|  | if (CborWriteWouldOverflowCursor(size, out)) { | 
|  | out->cursor = SIZE_MAX; | 
|  | return; | 
|  | } | 
|  | if (CborWriteFitsInBuffer(size, out)) { | 
|  | if (size == 1) { | 
|  | out->buffer[out->cursor] = (type << 5) | val; | 
|  | } else if (size == 2) { | 
|  | out->buffer[out->cursor] = (type << 5) | 24; | 
|  | out->buffer[out->cursor + 1] = val & 0xff; | 
|  | } else if (size == 3) { | 
|  | out->buffer[out->cursor] = (type << 5) | 25; | 
|  | out->buffer[out->cursor + 1] = (val >> 8) & 0xff; | 
|  | out->buffer[out->cursor + 2] = val & 0xff; | 
|  | } else if (size == 5) { | 
|  | out->buffer[out->cursor] = (type << 5) | 26; | 
|  | out->buffer[out->cursor + 1] = (val >> 24) & 0xff; | 
|  | out->buffer[out->cursor + 2] = (val >> 16) & 0xff; | 
|  | out->buffer[out->cursor + 3] = (val >> 8) & 0xff; | 
|  | out->buffer[out->cursor + 4] = val & 0xff; | 
|  | } else if (size == 9) { | 
|  | out->buffer[out->cursor] = (type << 5) | 27; | 
|  | out->buffer[out->cursor + 1] = (val >> 56) & 0xff; | 
|  | out->buffer[out->cursor + 2] = (val >> 48) & 0xff; | 
|  | out->buffer[out->cursor + 3] = (val >> 40) & 0xff; | 
|  | out->buffer[out->cursor + 4] = (val >> 32) & 0xff; | 
|  | out->buffer[out->cursor + 5] = (val >> 24) & 0xff; | 
|  | out->buffer[out->cursor + 6] = (val >> 16) & 0xff; | 
|  | out->buffer[out->cursor + 7] = (val >> 8) & 0xff; | 
|  | out->buffer[out->cursor + 8] = val & 0xff; | 
|  | } | 
|  | } | 
|  | out->cursor += size; | 
|  | } | 
|  |  | 
|  | static void* CborAllocStr(enum CborType type, size_t data_size, | 
|  | struct CborOut* out) { | 
|  | CborWriteType(type, data_size, out); | 
|  | bool overflow = CborWriteWouldOverflowCursor(data_size, out); | 
|  | bool fit = CborWriteFitsInBuffer(data_size, out); | 
|  | void* ptr = (overflow || !fit) ? NULL : &out->buffer[out->cursor]; | 
|  | out->cursor = overflow ? SIZE_MAX : out->cursor + data_size; | 
|  | return ptr; | 
|  | } | 
|  |  | 
|  | static void CborWriteStr(enum CborType type, size_t data_size, const void* data, | 
|  | struct CborOut* out) { | 
|  | uint8_t* ptr = CborAllocStr(type, data_size, out); | 
|  | if (ptr && data_size) { | 
|  | memcpy(ptr, data, data_size); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CborWriteInt(int64_t val, struct CborOut* out) { | 
|  | if (val < 0) { | 
|  | CborWriteType(CBOR_TYPE_NINT, (-1 - val), out); | 
|  | } else { | 
|  | CborWriteType(CBOR_TYPE_UINT, val, out); | 
|  | } | 
|  | } | 
|  |  | 
|  | void CborWriteUint(uint64_t val, struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_UINT, val, out); | 
|  | } | 
|  |  | 
|  | void CborWriteBstr(size_t data_size, const uint8_t* data, struct CborOut* out) { | 
|  | CborWriteStr(CBOR_TYPE_BSTR, data_size, data, out); | 
|  | } | 
|  |  | 
|  | uint8_t* CborAllocBstr(size_t data_size, struct CborOut* out) { | 
|  | return CborAllocStr(CBOR_TYPE_BSTR, data_size, out); | 
|  | } | 
|  |  | 
|  | void CborWriteTstr(const char* str, struct CborOut* out) { | 
|  | CborWriteStr(CBOR_TYPE_TSTR, strlen(str), str, out); | 
|  | } | 
|  |  | 
|  | char* CborAllocTstr(size_t size, struct CborOut* out) { | 
|  | return CborAllocStr(CBOR_TYPE_TSTR, size, out); | 
|  | } | 
|  |  | 
|  | void CborWriteArray(size_t num_elements, struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_ARRAY, num_elements, out); | 
|  | } | 
|  |  | 
|  | void CborWriteMap(size_t num_pairs, struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_MAP, num_pairs, out); | 
|  | } | 
|  |  | 
|  | void CborWriteTag(uint64_t tag, struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_TAG, tag, out); | 
|  | } | 
|  |  | 
|  | void CborWriteFalse(struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out); | 
|  | } | 
|  |  | 
|  | void CborWriteTrue(struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/21, out); | 
|  | } | 
|  |  | 
|  | void CborWriteNull(struct CborOut* out) { | 
|  | CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/22, out); | 
|  | } |