blob: b5517d9599784df116b75582c28a05bbb8846fe3 [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_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);
}