// 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_reader.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 CborReadWouldOverflow(size_t size, struct CborIn* in) { return size > SIZE_MAX - in->cursor || in->cursor + size > in->buffer_size; } static enum CborReadResult CborPeekInitialValueAndArgument(struct CborIn* in, uint8_t* size, enum CborType* type, uint64_t* val) { uint8_t initial_byte; uint8_t additional_information; uint64_t value; uint8_t bytes = 1; if (CborInAtEnd(in)) { return CBOR_READ_RESULT_END; } initial_byte = in->buffer[in->cursor]; *type = initial_byte >> 5; additional_information = initial_byte & 0x1f; if (additional_information <= 23) { value = additional_information; } else if (additional_information <= 27) { bytes += 1 << (additional_information - 24); if (CborReadWouldOverflow(bytes, in)) { return CBOR_READ_RESULT_END; } value = 0; if (bytes == 2) { value |= in->buffer[in->cursor + 1]; } else if (bytes == 3) { value |= (uint64_t)in->buffer[in->cursor + 1] << 8; value |= (uint64_t)in->buffer[in->cursor + 2]; } else if (bytes == 5) { value |= (uint64_t)in->buffer[in->cursor + 1] << 24; value |= (uint64_t)in->buffer[in->cursor + 2] << 16; value |= (uint64_t)in->buffer[in->cursor + 3] << 8; value |= (uint64_t)in->buffer[in->cursor + 4]; } else if (bytes == 9) { value |= (uint64_t)in->buffer[in->cursor + 1] << 56; value |= (uint64_t)in->buffer[in->cursor + 2] << 48; value |= (uint64_t)in->buffer[in->cursor + 3] << 40; value |= (uint64_t)in->buffer[in->cursor + 4] << 32; value |= (uint64_t)in->buffer[in->cursor + 5] << 24; value |= (uint64_t)in->buffer[in->cursor + 6] << 16; value |= (uint64_t)in->buffer[in->cursor + 7] << 8; value |= (uint64_t)in->buffer[in->cursor + 8]; } } else { // Indefinite lengths and reserved values are not supported. return CBOR_READ_RESULT_MALFORMED; } *val = value; *size = bytes; return CBOR_READ_RESULT_OK; } static enum CborReadResult CborReadSize(struct CborIn* in, enum CborType type, size_t* size) { uint8_t bytes; enum CborType in_type; uint64_t raw; enum CborReadResult res = CborPeekInitialValueAndArgument(in, &bytes, &in_type, &raw); if (res != CBOR_READ_RESULT_OK) { return res; } if (in_type != type) { return CBOR_READ_RESULT_NOT_FOUND; } if (raw > SIZE_MAX) { return CBOR_READ_RESULT_MALFORMED; } *size = (size_t)raw; in->cursor += bytes; return CBOR_READ_RESULT_OK; } static enum CborReadResult CborReadStr(struct CborIn* in, enum CborType type, size_t* data_size, const uint8_t** data) { size_t size; struct CborIn peeker = *in; enum CborReadResult res = CborReadSize(&peeker, type, &size); if (res != CBOR_READ_RESULT_OK) { return res; } if (CborReadWouldOverflow(size, &peeker)) { return CBOR_READ_RESULT_END; } *data_size = size; *data = &in->buffer[peeker.cursor]; in->cursor = peeker.cursor + size; return CBOR_READ_RESULT_OK; } static enum CborReadResult CborReadSimple(struct CborIn* in, uint8_t val) { uint8_t bytes; enum CborType type; uint64_t raw; enum CborReadResult res = CborPeekInitialValueAndArgument(in, &bytes, &type, &raw); if (res != CBOR_READ_RESULT_OK) { return res; } if (type != CBOR_TYPE_SIMPLE || raw != val) { return CBOR_READ_RESULT_NOT_FOUND; } in->cursor += bytes; return CBOR_READ_RESULT_OK; } enum CborReadResult CborReadInt(struct CborIn* in, int64_t* val) { uint8_t bytes; enum CborType type; uint64_t raw; enum CborReadResult res = CborPeekInitialValueAndArgument(in, &bytes, &type, &raw); if (res != CBOR_READ_RESULT_OK) { return res; } if (type != CBOR_TYPE_UINT && type != CBOR_TYPE_NINT) { return CBOR_READ_RESULT_NOT_FOUND; } if (raw > INT64_MAX) { return CBOR_READ_RESULT_MALFORMED; } *val = (type == CBOR_TYPE_NINT) ? (-1 - (int64_t)raw) : (int64_t)raw; in->cursor += bytes; return CBOR_READ_RESULT_OK; } enum CborReadResult CborReadUint(struct CborIn* in, uint64_t* val) { uint8_t bytes; enum CborType type; enum CborReadResult res = CborPeekInitialValueAndArgument(in, &bytes, &type, val); if (res != CBOR_READ_RESULT_OK) { return res; } if (type != CBOR_TYPE_UINT) { return CBOR_READ_RESULT_NOT_FOUND; } in->cursor += bytes; return CBOR_READ_RESULT_OK; } enum CborReadResult CborReadBstr(struct CborIn* in, size_t* data_size, const uint8_t** data) { return CborReadStr(in, CBOR_TYPE_BSTR, data_size, data); } enum CborReadResult CborReadTstr(struct CborIn* in, size_t* size, const char** str) { return CborReadStr(in, CBOR_TYPE_TSTR, size, (const uint8_t**)str); } enum CborReadResult CborReadArray(struct CborIn* in, size_t* num_elements) { return CborReadSize(in, CBOR_TYPE_ARRAY, num_elements); } enum CborReadResult CborReadMap(struct CborIn* in, size_t* num_pairs) { return CborReadSize(in, CBOR_TYPE_MAP, num_pairs); } enum CborReadResult CborReadTag(struct CborIn* in, uint64_t* tag) { uint8_t bytes; enum CborType type; enum CborReadResult res = CborPeekInitialValueAndArgument(in, &bytes, &type, tag); if (res != CBOR_READ_RESULT_OK) { return res; } if (type != CBOR_TYPE_TAG) { return CBOR_READ_RESULT_NOT_FOUND; } in->cursor += bytes; return CBOR_READ_RESULT_OK; } enum CborReadResult CborReadFalse(struct CborIn* in) { return CborReadSimple(in, /*val=*/20); } enum CborReadResult CborReadTrue(struct CborIn* in) { return CborReadSimple(in, /*val=*/21); } enum CborReadResult CborReadNull(struct CborIn* in) { return CborReadSimple(in, /*val=*/22); } enum CborReadResult CborReadSkip(struct CborIn* in) { struct CborIn peeker = *in; size_t size_stack[CBOR_READ_SKIP_STACK_SIZE]; size_t stack_size = 0; size_stack[stack_size++] = 1; while (stack_size > 0) { // Get the type uint8_t bytes; enum CborType type; uint64_t val; enum CborReadResult res; res = CborPeekInitialValueAndArgument(&peeker, &bytes, &type, &val); if (res != CBOR_READ_RESULT_OK) { return res; } if (CborReadWouldOverflow(bytes, &peeker)) { return CBOR_READ_RESULT_END; } peeker.cursor += bytes; if (--size_stack[stack_size - 1] == 0) { --stack_size; } switch (type) { case CBOR_TYPE_UINT: case CBOR_TYPE_NINT: case CBOR_TYPE_SIMPLE: continue; case CBOR_TYPE_BSTR: case CBOR_TYPE_TSTR: if (val > SIZE_MAX || CborReadWouldOverflow((size_t)val, &peeker)) { return CBOR_READ_RESULT_END; } peeker.cursor += val; continue; case CBOR_TYPE_MAP: if (val > UINT64_MAX / 2) { return CBOR_READ_RESULT_END; } val *= 2; break; case CBOR_TYPE_TAG: val = 1; break; case CBOR_TYPE_ARRAY: break; default: return CBOR_READ_RESULT_MALFORMED; } // Push a new level of nesting to the stack. if (val == 0) { continue; } if (stack_size == CBOR_READ_SKIP_STACK_SIZE) { return CBOR_READ_RESULT_MALFORMED; } if (val > SIZE_MAX) { return CBOR_READ_RESULT_END; } size_stack[stack_size++] = (size_t)val; } in->cursor = peeker.cursor; return CBOR_READ_RESULT_OK; }