Commit de681e02 authored by Brendan Moran's avatar Brendan Moran
Browse files

Extract basic, minimal cbor parser from suit_parser

Also add tests, makefile for parser
parent 1de9f703
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright 2021 ARM Limited or its affiliates
#
# SPDX-License-Identifier: Apache-2.0
#
# 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
#
# http://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.
# ----------------------------------------------------------------------------
CC?=cc
AR?=ar
OUT?=./out
ABS_OUT=$(abspath ${OUT})/
Q?=@
CBOR_LIB?=${ABS_OUT}source/bm_cbor.a
$(info ${CBOR_LIB})
.PHONY: all
all: lib
${ABS_OUT}:
mkdir -p ${ABS_OUT}
.PHONY: lib
lib: ${ABS_OUT}
make -C source OUT=${ABS_OUT}source/ Q=${Q}
.PHONY: tests
tests: lib ${OUT}
make -C test OUT=${ABS_OUT}test/ Q=${Q} CBOR_LIB=${CBOR_LIB}
.PHONY: test
test: lib ${ABS_OUT}
make -C test OUT=${ABS_OUT}test/ Q=${Q} CBOR_LIB=${CBOR_LIB} test
.PHONY: clean ${ABS_OUT}
clean:
rm -rf ${ABS_OUT}
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright 2021 ARM Limited or its affiliates
#
# SPDX-License-Identifier: Apache-2.0
#
# 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
#
# http://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.
# ----------------------------------------------------------------------------
CC?=cc
AR?=ar
OUT?=../out/
Q?=@
TARGET=${OUT}bm_cbor.a
SRCS=bm_cbor.c
OBJS=$(patsubst %.c,${OUT}%.o,${SRCS})
all: ${TARGET}
${OUT}:
mkdir -p ${OUT}
${OUT}%.o: %.c ${OUT}
${Q}echo " [CC] $@"
${Q}${CC} -c -o $@ $<
${TARGET}: ${OBJS}
${Q}echo " [AR] $@"
${Q}${AR} rcs $@ $^
// ----------------------------------------------------------------------------
// Copyright 2020-2021 ARM Ltd.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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
//
// http://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 "bm_cbor.h"
#include <stdint.h>
int bm_cbor_get_as_uint(const uint8_t** p, const uint8_t* end, bm_cbor_uint_t* n)
{
if (*p >= end) {
RETURN_ERROR(CBOR_ERR_OVERRUN);
}
uint8_t iv = **p & ~CBOR_TYPE_MASK;
if (iv >= 28){
RETURN_ERROR(CBOR_ERR_INTEGER_ENCODING);
}
(*p)++;
//TODO: do not increment p until size check is good
//TODO: check for integer decode overflow
if (iv < 24) {
*n = iv;
} else {
const uint8_t* uend = *p + (1 << (iv-24));
if (uend > end) {
--(*p);
RETURN_ERROR(CBOR_ERR_OVERRUN);
}
for (*n = 0; *p < uend; (*p)++) {
*n = *n << 8 | **p;
}
}
return CBOR_ERR_NONE;
}
int bm_cbor_get_uint(const uint8_t** p, const uint8_t* end, bm_cbor_uint_t* n){
uint8_t type = **p & CBOR_TYPE_MASK;
if (type != CBOR_TYPE_UINT) {
RETURN_ERROR(CBOR_ERR_TYPE_MISMATCH);
}
return bm_cbor_get_as_uint(p, end, n);
}
int bm_cbor_get_int(const uint8_t** p, const uint8_t* end, bm_cbor_int_t* n) {
uint8_t type = **p & CBOR_TYPE_MASK;
if (type != CBOR_TYPE_NINT && type != CBOR_TYPE_UINT) {
RETURN_ERROR(CBOR_ERR_TYPE_MISMATCH);
}
bm_cbor_uint_t uv;
int rc = bm_cbor_get_as_uint(p, end, &uv);
if (rc != CBOR_ERR_NONE) {
return rc;
}
if (uv & (1UL << (BM_CBOR_INT_SIZE -1))) {
// Valid CBOR, but requires larger integers to decode
RETURN_ERROR(CBOR_ERR_INTEGER_DECODE_OVERFLOW);
}
if (type == CBOR_TYPE_NINT) {
*n = -1 - (bm_cbor_int_t)uv;
} else {
*n = uv;
}
return rc;
}
int bm_cbor_extract_uint(
const uint8_t **p,
const uint8_t *end,
bm_cbor_value_t *val)
{
return bm_cbor_get_uint(p, end, &(val->u));
}
int bm_cbor_extract_int(
const uint8_t **p,
const uint8_t *end,
bm_cbor_value_t *val)
{
return bm_cbor_get_int(p, end, &(val->i));
}
int bm_cbor_extract_ref(
const uint8_t **p,
const uint8_t *end,
bm_cbor_value_t *val
)
{
int rc = bm_cbor_get_as_uint(p, end, &(val->ref.uival));
if (rc == CBOR_ERR_NONE) {
val->ref.ptr = *p;
}
return rc;
}
int bm_cbor_extract_primitive(
const uint8_t **p,
const uint8_t *end,
bm_cbor_value_t *val)
{
val->primitive = (**p & (~CBOR_TYPE_MASK));
(*p)++;
RETURN_ERROR(CBOR_ERR_NONE);
}
int cbor_check_type_extract_ref(
const uint8_t **p,
const uint8_t *end,
cbor_value_t *o_val,
const uint8_t cbor_type
) {
if ((**p & CBOR_TYPE_MASK) != cbor_type) {
PD_PRINTF("Expected: %u Actual %u\n", (unsigned) cbor_type>>5, (unsigned)(**p & CBOR_TYPE_MASK)>>5);
RETURN_ERROR(CBOR_ERR_TYPE_MISMATCH);
}
o_val->cbor_start = *p;
return cbor_extract_ref(p, end, o_val);
}
int (*cbor_extractors[])(
const uint8_t **p,
const uint8_t *end,
cbor_value_t *val) =
{
cbor_extract_uint,
cbor_extract_int,
cbor_extract_ref,
cbor_extract_ref,
cbor_extract_ref,
cbor_extract_ref,
cbor_extract_ref,
cbor_extract_primitive
};
int cbor_skip(const uint8_t **p, const uint8_t *end)
{
uint8_t ct = **p & CBOR_TYPE_MASK;
size_t handler_index = ct >> 5;
cbor_value_t val;
int rc = cbor_extractors[handler_index](p, end, &val);
if ((*p) > end) {
SET_ERROR(rc, CBOR_ERR_OVERRUN);
}
if (rc != CBOR_ERR_NONE) {
return rc;
}
switch (ct) {
case CBOR_TYPE_UINT:
case CBOR_TYPE_NINT:
break;
case CBOR_TYPE_TSTR:
case CBOR_TYPE_BSTR:
if ((*p) + val.ref.uival <= end) {
(*p) += val.ref.uival;
} else {
SET_ERROR(rc, CBOR_ERR_OVERRUN);
}
break;
case CBOR_TYPE_MAP:
val.ref.uival *= 2;
// no break;
case CBOR_TYPE_LIST:
for (size_t count = val.ref.uival; count && rc == CBOR_ERR_NONE; count--) {
rc = cbor_skip(p, end);
}
break;
case CBOR_TYPE_SIMPLE:
if (val.primitive == (CBOR_NULL & ~CBOR_TYPE_MASK)) {
break;
} else {
// PD_PRINTF("primitive : %02x\n", val.primitive);
}
default:
// PD_PRINTF("Skip Unimplemented for type %u\n", (unsigned) ct>>5);
SET_ERROR(rc, CBOR_ERR_UNIMPLEMENTED);
}
return rc;
}
\ No newline at end of file
// ----------------------------------------------------------------------------
// Copyright 2020-2021 ARM Ltd.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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
//
// http://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.
// ----------------------------------------------------------------------------
/**
* @file Basic Minimal CBOR (bm_cbor)
* @brief A minimalist CBOR parser.
*
*/
#include <stdint.h>
#define CBOR_TYPE_UINT (0 << 5)
#define CBOR_TYPE_NINT (1 << 5)
#define CBOR_TYPE_BSTR (2 << 5)
#define CBOR_TYPE_TSTR (3 << 5)
#define CBOR_TYPE_LIST (4 << 5)
#define CBOR_TYPE_MAP (5 << 5)
#define CBOR_TYPE_TAG (6 << 5)
#define CBOR_TYPE_SIMPLE (7 << 5)
#define CBOR_TYPE_MASK (7 << 5)
#define CBOR_FALSE (CBOR_TYPE_SIMPLE | 20)
#define CBOR_TRUE (CBOR_TYPE_SIMPLE | 21)
#define CBOR_NULL (CBOR_TYPE_SIMPLE | 22)
enum {
CBOR_ERR_NONE = 0,
CBOR_ERR_TYPE_MISMATCH,
CBOR_ERR_KEY_MISMATCH,
CBOR_ERR_OVERRUN,
CBOR_ERR_INTEGER_DECODE_OVERFLOW,
CBOR_ERR_INTEGER_ENCODING,
CBOR_ERR_UNIMPLEMENTED,
};
#ifndef PRINT_ON_ERROR
#define PRINT_ON_ERROR 0
#endif
#if PRINT_ON_ERROR == 0
#define BM_CBOR_ERR_PRINT(...)
#else
#include <stdio.h>
#define BM_CBOR_ERR_PRINT printf
#endif
#define SET_ERROR(RC, VAL)\
do{\
(RC)=(VAL);\
if((VAL) && PRINT_ON_ERROR){BM_CBOR_ERR_PRINT("Error " #VAL " (%i) set on %s:%u\r\n",(VAL),__FILE__,__LINE__);\
}}while(0)
#define RETURN_ERROR(VAL)\
do{\
if((VAL) && PRINT_ON_ERROR){BM_CBOR_ERR_PRINT("Error " #VAL " (%i) set on %s:%u\r\n",(VAL),__FILE__,__LINE__);}\
return (VAL);}while(0)
#ifndef BM_CBOR_INT_SIZE
#define BM_CBOR_INT_SIZE 64
#endif
#if BM_CBOR_INT_SIZE == 64
typedef uint64_t bm_cbor_uint_t;
typedef int64_t bm_cbor_int_t;
#elif BM_CBOR_INT_SIZE == 32
typedef uint32_t bm_cbor_uint_t;
typedef int32_t bm_cbor_int_t;
#else
#error integer size is not supported.
#endif
typedef struct bm_cbor_value_s {
union {
bm_cbor_uint_t u;
bm_cbor_int_t i;
struct {
bm_cbor_uint_t uival;
const uint8_t *ptr;
} ref;
uint8_t primitive;
};
} bm_cbor_value_t;
typedef struct bm_cbor_start_value_s {
bm_cbor_value_s v;
const uint8_t *s;
} bm_cbor_start_value_t;
int bm_cbor_get_as_uint(const uint8_t** p, const uint8_t* end, bm_cbor_uint_t* n);
int bm_cbor_get_uint(const uint8_t** p, const uint8_t* end, bm_cbor_uint_t* n);
int bm_cbor_get_int(const uint8_t** p, const uint8_t* end, bm_cbor_int_t* n);
int bm_cbor_extract_uint(const uint8_t **p, const uint8_t *end, bm_cbor_value_t *val);
int bm_cbor_extract_int(const uint8_t **p, const uint8_t *end, bm_cbor_value_t *val);
int bm_cbor_extract_ref(const uint8_t **p, const uint8_t *end, bm_cbor_value_t *val);
int bm_cbor_extract_primitive(const uint8_t **p, const uint8_t *end, bm_cbor_value_t *val);
\ No newline at end of file
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright 2021 ARM Limited or its affiliates
#
# SPDX-License-Identifier: Apache-2.0
#
# 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
#
# http://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.
# ----------------------------------------------------------------------------
ALL_TESTS=unit
TEST_ALL=$(patsubst %,%-run,${ALL_TESTS})
BUILD_ALL=$(patsubst %,%-build,${ALL_TESTS})
# $(info build all: ${BUILD_ALL})
.PHONY:all
all: tests
${Q}echo "Building All"
.PHONY: ${ALL_TESTS}
# .PHONY: ${TEST_ALL}
# .PHONY: ${BUILD_ALL}
.PHONY: tests
tests: ${BUILD_ALL}
${Q}echo "Building Tests"
${Q}echo "Test dependencies: $^"
.PHONY: test
test: ${TEST_ALL}
%-build: %
${Q}echo "Building $@"
${Q}make -C $< OUT=${OUT}$</ Q=${Q} CBOR_LIB=${CBOR_LIB}
%-run: %
${Q}make -C $< OUT=${OUT}$</ Q=${Q} CBOR_LIB=${CBOR_LIB} test
# -*- coding: utf-8 -*-
# ----------------------------------------------------------------------------
# Copyright 2021 ARM Limited or its affiliates
#
# SPDX-License-Identifier: Apache-2.0
#
# 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
#
# http://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.
# ----------------------------------------------------------------------------
CC?=cc
AR?=ar
OUT?=../../out/test/unit/
SRC_ROOT?=$(abspath ../../source)
Q?=@
TARGET=${OUT}unit
SRCS=unit.c
OBJS=$(patsubst %.c,${OUT}%.o,${SRCS})
all: tests
test: tests
${TARGET}
tests: ${TARGET}
${OUT}:
mkdir -p ${OUT}
${OUT}%.o: %.c ${OUT}
${Q}echo " [CC] $@"
${Q}${CC} -I ${SRC_ROOT} -c -o $@ $<
${TARGET}:${OBJS} ${CBOR_LIB}
${Q}echo " [LD] $@"
${CC} -o $@ $^
\ No newline at end of file
File added
// ----------------------------------------------------------------------------
// Copyright 2020 ARM Ltd.
//
// SPDX-License-Identifier: Apache-2.0
//
// 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
//
// http://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 "bm_cbor.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define DEBUG 1
#ifndef DEBUG
#define DEBUG 0
#endif
#define TEST_EQ_INT(X, Y, R)\
do { if((X) != (Y)) {\
if (DEBUG)\
printf("\nAt: %s:%d> Expected: %ld. Actual %ld\n", __FILE__, __LINE__, (long int)(X), (long int)(Y));\
return (R);\
}} while (0)
struct test_s {
int (*fn)();
const char* desc;
};
void print_test(size_t width, const char* desc) {
size_t maxdesc = width - 6 - 1;
char buf[80+2];
for (; width < 80; width++) {
printf(" ");
}
strncpy(buf, desc, maxdesc);
int dotlen = maxdesc - strlen(desc);
printf("%s.", buf);
for (;dotlen>0;dotlen--) {
printf(".");
}
}
int hello_test() {
return 0;
}
struct uint_test_case_s {
const uint8_t* ip;
size_t len;
int rc;
bm_cbor_uint_t expect;
int offset;
const char* name;
};
#define MK_UINT_TEST(S, RC, E, O, NAME)\
{(const uint8_t *)(S), sizeof(S)-1, (RC), (E), ((O) < 0 ? sizeof(S)-1 : (O)), (NAME)}
struct uint_test_case_s as_uint_test_cases[] = {
MK_UINT_TEST("", CBOR_ERR_OVERRUN, 0L, 0, "Empty String"),
MK_UINT_TEST("\xff", CBOR_ERR_INTEGER_ENCODING, 0L, 0, "Bad Encoding"),
MK_UINT_TEST("\x00", CBOR_ERR_NONE, 0L, -1, "Positive 0"),
MK_UINT_TEST("\x20", CBOR_ERR_NONE, 0L, -1, "Negative 0"),
MK_UINT_TEST("\x40", CBOR_ERR_NONE, 0L, -1, "Bstr 0"),
MK_UINT_TEST("\x60", CBOR_ERR_NONE, 0L, -1, "Tstr 0"),
MK_UINT_TEST("\x80", CBOR_ERR_NONE, 0L, -1, "List 0"),
MK_UINT_TEST("\xA0", CBOR_ERR_NONE, 0L, -1, "Map 0"),
MK_UINT_TEST("\xC0", CBOR_ERR_NONE, 0L, -1, "Tag 0"),
MK_UINT_TEST("\xE0", CBOR_ERR_NONE, 0L, -1, "Simple 0"),
MK_UINT_TEST("\x18\x00", CBOR_ERR_NONE, 0L, -1, "00"),
MK_UINT_TEST("\x19\x00\x00", CBOR_ERR_NONE, 0L, -1, "0000"),
MK_UINT_TEST("\x1A\x00\x00\x00\x00", CBOR_ERR_NONE, 0L, -1, "00000000"),
#if BM_CBOR_INT_SIZE == 64
MK_UINT_TEST("\x1B\x00\x00\x00\x00\x00\x00\x00\x00", CBOR_ERR_NONE, 0L, -1, "0000000000000000"),
#elif BM_CBOR_INT_SIZE == 32
MK_UINT_TEST("\x1B\x00\x00\x00\x00\x00\x00\x00\x00", CBOR_ERR_INTEGER_DECODE_OVERFLOW, 0L, 0, "0000000000000000"),
#endif
MK_UINT_TEST("\x18", CBOR_ERR_OVERRUN, 0L, 0, "00-Overrun"),
MK_UINT_TEST("\x19\x00", CBOR_ERR_OVERRUN, 0L, 0, "0000-Overrun"),
MK_UINT_TEST("\x1A\x00\x00\x00", CBOR_ERR_OVERRUN, 0L, 0, "00000000-Overrun"),
#if BM_CBOR_INT_SIZE == 64
MK_UINT_TEST("\x1B\x00\x00\x00\x00\x00\x00\x00", CBOR_ERR_OVERRUN, 0L, 0, "0000000000000000-Overrun"),
#endif
MK_UINT_TEST("\x17", CBOR_ERR_NONE, 0x17L, -1, "0x17"),
MK_UINT_TEST("\x18\x18", CBOR_ERR_NONE, 0x18L, -1, "0x18"),
MK_UINT_TEST("\x18\xFF", CBOR_ERR_NONE, 0xFFL, -1, "0xFF"),
MK_UINT_TEST("\x19\x00\xFF", CBOR_ERR_NONE, 0xFFL, -1, "0x00FF"),
MK_UINT_TEST("\x19\x01\x00", CBOR_ERR_NONE, 0x100L, -1, "0x0100"),
MK_UINT_TEST("\x19\x12\x34", CBOR_ERR_NONE, 0x1234L, -1, "0x1234"),
MK_UINT_TEST("\x19\xFF\xFF", CBOR_ERR_NONE, 0xFFFFL, -1, "0xFFFF"),
MK_UINT_TEST("\x1A\x00\x00\xFF\xFF", CBOR_ERR_NONE, 0xFFFFL, -1, "0x0000FFFF"),
MK_UINT_TEST("\x1A\x00\x01\x00\x00", CBOR_ERR_NONE, 0x10000L, -1, "0x00010000"),
MK_UINT_TEST("\x1A\x12\x34\x56\x78", CBOR_ERR_NONE, 0x12345678L, -1, "0x12345678"),
MK_UINT_TEST("\x1A\xFF\xFF\xFF\xFF", CBOR_ERR_NONE, 0xFFFFFFFFL, -1, "0xFFFFFFFF"),
#if BM_CBOR_INT_SIZE == 64
MK_UINT_TEST("\x1B\x00\x00\x00\x00\xFF\xFF\xFF\xFF", CBOR_ERR_NONE, 0xFFFFFFFFL, -1, "0x00000000FFFFFFFF"),
MK_UINT_TEST("\x1B\x00\x00\x00\x01\x00\x00\x00\x00", CBOR_ERR_NONE, 0x100000000L, -1, "0x0000000100000000"),
MK_UINT_TEST("\x1B\x12\x34\x56\x78\x9A\xBC\xDE\xF0", CBOR_ERR_NONE, 0x123456789ABCDEF0L, -1, "0x123456789ABCDEF0"),
MK_UINT_TEST("\x1B\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", CBOR_ERR_NONE, 0xFFFFFFFFFFFFFFFFL, -1, "0xFFFFFFFFFFFFFFFF"),
#endif
};
const size_t as_uint_test_cases_n = sizeof(as_uint_test_cases)/sizeof(as_uint_test_cases[0]);
int get_as_uint_test()
{
const uint8_t *p;
const uint8_t *end;
bm_cbor_uint_t n;
int rc;