| // 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, |
| // Type 6, tags, are not supported. |
| CBOR_TYPE_SIMPLE = 7, |
| }; |
| |
| static size_t CborWriteType(enum CborType type, uint64_t val, |
| struct CborOut* out) { |
| // Check how much space is needed. |
| 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; |
| } |
| // Don't allow offset to overflow. |
| if (size > SIZE_MAX - out->offset) { |
| return 0; |
| } |
| // Only write if a buffer is provided. |
| if (out->buffer) { |
| if (out->size < out->offset + size) { |
| return 0; |
| } |
| if (size == 1) { |
| out->buffer[out->offset] = (type << 5) | val; |
| } else if (size == 2) { |
| out->buffer[out->offset] = (type << 5) | 24; |
| out->buffer[out->offset + 1] = val & 0xff; |
| } else if (size == 3) { |
| out->buffer[out->offset] = (type << 5) | 25; |
| out->buffer[out->offset + 1] = (val >> 8) & 0xff; |
| out->buffer[out->offset + 2] = val & 0xff; |
| } else if (size == 5) { |
| out->buffer[out->offset] = (type << 5) | 26; |
| out->buffer[out->offset + 1] = (val >> 24) & 0xff; |
| out->buffer[out->offset + 2] = (val >> 16) & 0xff; |
| out->buffer[out->offset + 3] = (val >> 8) & 0xff; |
| out->buffer[out->offset + 4] = val & 0xff; |
| } else if (size == 9) { |
| out->buffer[out->offset] = (type << 5) | 27; |
| out->buffer[out->offset + 1] = (val >> 56) & 0xff; |
| out->buffer[out->offset + 2] = (val >> 48) & 0xff; |
| out->buffer[out->offset + 3] = (val >> 40) & 0xff; |
| out->buffer[out->offset + 4] = (val >> 32) & 0xff; |
| out->buffer[out->offset + 5] = (val >> 24) & 0xff; |
| out->buffer[out->offset + 6] = (val >> 16) & 0xff; |
| out->buffer[out->offset + 7] = (val >> 8) & 0xff; |
| out->buffer[out->offset + 8] = val & 0xff; |
| } else { |
| return 0; |
| } |
| } |
| // Update the offset with the size it needs. |
| out->offset += size; |
| return size; |
| } |
| |
| static size_t CborWriteStr(enum CborType type, size_t data_size, |
| const uint8_t* data, struct CborOut* out) { |
| // Write the type. |
| size_t type_size = CborWriteType(type, data_size, out); |
| if (type_size == 0) { |
| return 0; |
| } |
| // Don't allow offset to overflow. |
| if (data_size > SIZE_MAX - out->offset) { |
| return 0; |
| } |
| // Write the data if a buffer is provided. |
| if (data_size > 0 && out->buffer) { |
| if (out->size < out->offset + data_size) { |
| return 0; |
| } |
| memcpy(&out->buffer[out->offset], data, data_size); |
| } |
| // Update the offset with the size it needs. |
| out->offset += data_size; |
| return type_size + data_size; |
| } |
| |
| size_t CborWriteInt(int64_t val, struct CborOut* out) { |
| if (val < 0) { |
| return CborWriteType(CBOR_TYPE_NINT, (-1 - val), out); |
| } |
| return CborWriteType(CBOR_TYPE_UINT, val, out); |
| } |
| |
| size_t CborWriteBstr(size_t data_size, const uint8_t* data, |
| struct CborOut* out) { |
| return CborWriteStr(CBOR_TYPE_BSTR, data_size, data, out); |
| } |
| |
| size_t CborWriteTstr(const char* str, struct CborOut* out) { |
| return CborWriteStr(CBOR_TYPE_TSTR, strlen(str), (const uint8_t*)str, out); |
| } |
| |
| size_t CborWriteArray(size_t num_elements, struct CborOut* out) { |
| return CborWriteType(CBOR_TYPE_ARRAY, num_elements, out); |
| } |
| |
| size_t CborWriteMap(size_t num_pairs, struct CborOut* out) { |
| return CborWriteType(CBOR_TYPE_MAP, num_pairs, out); |
| } |
| |
| size_t CborWriteFalse(struct CborOut* out) { |
| return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/20, out); |
| } |
| |
| size_t CborWriteTrue(struct CborOut* out) { |
| return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/21, out); |
| } |
| |
| size_t CborWriteNull(struct CborOut* out) { |
| return CborWriteType(CBOR_TYPE_SIMPLE, /*val=*/22, out); |
| } |