blob: 035a0bcf99cc9c21bdb4ac947ee87b08ec8e219b [file] [log] [blame]
// 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 CborPeekIntialValueAndArgument(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 |= (uint16_t)in->buffer[in->cursor + 1] << 8;
value |= (uint16_t)in->buffer[in->cursor + 2];
} else if (bytes == 5) {
value |= (uint32_t)in->buffer[in->cursor + 1] << 24;
value |= (uint32_t)in->buffer[in->cursor + 2] << 16;
value |= (uint32_t)in->buffer[in->cursor + 3] << 8;
value |= (uint32_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 =
CborPeekIntialValueAndArgument(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 = 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 =
CborPeekIntialValueAndArgument(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 =
CborPeekIntialValueAndArgument(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 =
CborPeekIntialValueAndArgument(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 =
CborPeekIntialValueAndArgument(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 = CborPeekIntialValueAndArgument(&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 (CborReadWouldOverflow(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;
}
size_stack[stack_size++] = val;
}
in->cursor = peeker.cursor;
return CBOR_READ_RESULT_OK;
}