Initial snapshot: Aliro applet, harness, Nucleo NFC10A1 reader port

Three components, all bench-validated to varying depths:

- applet/: CSA Aliro v1.0 Java Card applet for J3R180. AUTH0 + AUTH1
  expedited-standard flow end-to-end green via PC/SC bench reader
  (aliro-bench-test). Userland AES-256-GCM and HMAC-SHA-256 layered
  on top of J3R180's primitives because the card lacks both natively.
  P-256 curve params seeded explicitly per J3R180's quirk.

- harness/: Python orchestrator (aliro-trustgen, aliro-personalize,
  aliro-bench-test) for trust-bundle generation, card personalization
  via PersonalizationApplet, and PC/SC AUTH0+AUTH1 transactions. 126
  pytest cases passing.

- reader/STM32CubeExpansion_ALIRO_V1_0_0/: ST X-CUBE-ALIRO V1.0.0
  with our NFC10A1 port (NUCLEO-U545RE-Q + X-NUCLEO-NFC10A1, ST25R200
  shared with NFC09A1). nfc10-only/ project, NFC10A1 BSP shim,
  ALIRO_TRUST_OVERRIDE include into vendor's provisioning.c, and an
  ALIRO_APDU_TRACE wrapper around demoTransceiveBlocking. Boots,
  detects the J3R180, completes SELECT + AUTH0; AUTH1 currently fails
  with RFAL ERR_PROTO (0xB) — under investigation, see
  docs/plans/2026-04-20-nucleo-nfc10a1-port.md and bench-notes/.

Excluded: x-cube-aliro.zip vendor archive, harness/.venv, build dirs,
generated aliro_trust.h (contains private reader scalar), all PEMs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 10:17:46 -07:00
commit 782074f6ae
8786 changed files with 2902373 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
name: CI
on: [push, pull_request]
# Fan out for the two compilers
# Fan out disabling on feature at a time -- too slow to do the full combo fan out
jobs:
main:
strategy:
fail-fast: false
matrix:
c-compiler: [gcc, clang]
config:
- os-image: ubuntu-latest
container: ubuntu:22.04
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_NON_INTEGER_LABELS'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_TAGS'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DUSEFULBUF_DISABLE_ALL_FLOAT'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_FLOAT_HW_USE'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_PREFERRED_FLOAT'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_ENCODE_USAGE_GUARDS'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS'
- os-image: ubuntu-latest
container: ubuntu:22.04
dis-xxx: '-DQCBOR_DISABLE_DECODE_CONFORMANCE'
name: ${{ matrix.config.dis-xxx }} • ${{ matrix.c-compiler }} • ${{ matrix.config.container }}
runs-on: ${{ matrix.config.os-image }}
container: ${{ matrix.config.container }}
steps:
- uses: actions/checkout@v3
- name: Install build tools
run: |
set -ex
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y build-essential cmake ${{ matrix.c-compiler }}
echo "CC=${{ matrix.c-compiler }}" >> $GITHUB_ENV
- name: Build QCBOR
run: |
set -ex
make warn CMD_LINE=${{ matrix.config.dis-xxx }}
- name: Run tests
run: ./qcbortest

View File

@@ -0,0 +1,57 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# Compiled binaries
qcbortest
qcbormin

View File

@@ -0,0 +1,107 @@
#-------------------------------------------------------------------------------
# Copyright (c) 2022-2023, Arm Limited. All rights reserved.
# Copyright (c) 2024, Laurence Lundblade. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#-------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.15)
project(qcbor
DESCRIPTION "QCBOR"
LANGUAGES C
VERSION 2.0.0
)
set(BUILD_QCBOR_TEST "OFF" CACHE STRING "Build QCBOR test suite [OFF, LIB, APP]")
set(BUILD_QCBOR_WARN OFF CACHE BOOL "Compile with the warning flags used in the QCBOR release process")
# BUILD_SHARED_LIBS is a built-in global CMake flag
# The shared library is not made by default because of platform
# variability For example MacOS and Linux behave differently and some
# IoT OS's don't support them at all.
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries instead of static ones")
# Configuration:
# Floating-point support (see README.md for more information)
set(QCBOR_OPT_DISABLE_FLOAT_HW_USE OFF CACHE BOOL "Eliminate dependency on FP hardware and FP instructions")
set(QCBOR_OPT_DISABLE_FLOAT_PREFERRED OFF CACHE BOOL "Eliminate support for half-precision and CBOR preferred serialization")
set(QCBOR_OPT_DISABLE_FLOAT_ALL OFF CACHE BOOL "Eliminate floating-point support completely")
if (BUILD_QCBOR_WARN)
# Compile options applying to all targets in current directory and below
add_compile_options(-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wcast-qual)
endif()
add_library(qcbor)
target_sources(qcbor
PRIVATE
src/qcbor_main_encode.c
src/qcbor_number_encode.c
src/ieee754.c
src/qcbor_main_decode.c
src/qcbor_spiffy_decode.c
src/qcbor_tag_decode.c
src/qcbor_number_decode.c
src/qcbor_err_to_str.c
src/UsefulBuf.c
)
target_include_directories(qcbor
PUBLIC
inc
PRIVATE
src
)
target_compile_definitions(qcbor
PRIVATE
$<$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_HW_USE}>:QCBOR_DISABLE_FLOAT_HW_USE>
$<$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_PREFERRED}>:QCBOR_DISABLE_PREFERRED_FLOAT>
$<$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_ALL}>:USEFULBUF_DISABLE_ALL_FLOAT>
)
if (BUILD_SHARED_LIBS)
target_compile_options(qcbor PRIVATE -Os -fPIC)
endif()
# The math library is needed for floating-point support.
# To avoid need for it #define QCBOR_DISABLE_FLOAT_HW_USE
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
# Using GCC
target_link_libraries(qcbor
PRIVATE
$<$<NOT:$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_HW_USE}>>:m>
)
endif()
set(HEADERS
inc/qcbor/qcbor.h
inc/qcbor/qcbor_common.h
inc/qcbor/qcbor_private.h
inc/qcbor/qcbor_encode.h
inc/qcbor/qcbor_decode.h
inc/qcbor/qcbor_number_decode.h
inc/qcbor/qcbor_main_decode.h
inc/qcbor/qcbor_tag_decode.h
inc/qcbor/qcbor_spiffy_decode.h
inc/qcbor/UsefulBuf.h
)
set_target_properties(
qcbor PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
PUBLIC_HEADER "${HEADERS}"
)
include(GNUInstallDirs)
install(
TARGETS qcbor
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/qcbor"
)
if (NOT BUILD_QCBOR_TEST STREQUAL "OFF")
enable_testing()
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,37 @@
QCBOR is available under what is essentially the 3-Clause BSD License.
Files created inside Qualcomm and open-sourced through CAF (The Code
Aurora Forum) have a slightly modified 3-Clause BSD License. The
modification additionally disclaims NON-INFRINGEMENT.
Files created after release to CAF use the standard 3-Clause BSD
License with no modification. These files have the SPDX license
identifier, "SPDX-License-Identifier: BSD-3-Clause" in them.
BSD 3-Clause License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,196 @@
# Makefile -- UNIX-style make for qcbor as a lib and command line test
#
# Copyright (c) 2018-2025, Laurence Lundblade. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#
# The math library is needed for floating-point support. To
# avoid need for it #define QCBOR_DISABLE_FLOAT_HW_USE
LIBS=-lm
# The QCBOR makefile uses a minimum of compiler flags so that it will
# work out-of-the-box with a wide variety of compilers. For example,
# some compilers error out on some of the warnings flags gcc supports.
# The $(CMD_LINE) variable allows passing in extra flags. This is
# used on the stringent build script that is in
# https://github.com/laurencelundblade/qdv. This script is used
# before pushes to master (though not yet through an automated build
# process). See "warn:" below.
CFLAGS=$(CMD_LINE) -I inc -I test -Os -fPIC
QCBOR_OBJ=src/UsefulBuf.o \
src/qcbor_main_encode.o \
src/qcbor_number_encode.o \
src/qcbor_main_decode.o \
src/qcbor_spiffy_decode.o \
src/qcbor_number_decode.o \
src/qcbor_tag_decode.o \
src/ieee754.o \
src/qcbor_err_to_str.o
TEST_OBJ=test/UsefulBuf_Tests.o \
test/qcbor_encode_tests.o \
test/qcbor_decode_tests.o \
test/run_tests.o \
test/float_tests.o \
test/half_to_double_from_rfc7049.o \
example.o \
tag-examples.o \
ub-example.o
COV_FILES=test/*.gcno src/*.gcno test/*.gcda src/*.gcda *.gcno *.gcda *.gcov
.PHONY: all so install uninstall clean warn
all: qcbortest libqcbor.a
so: libqcbor.so
qcbortest: libqcbor.a $(TEST_OBJ) cmd_line_main.o
$(CC) -o $@ $^ libqcbor.a $(LIBS)
qcborcov: $(QCBOR_OBJ) $(TEST_OBJ) cmd_line_main.o
$(CC) -fprofile-arcs -ftest-coverage -o $@ $^ $(LIBS)
libqcbor.a: $(QCBOR_OBJ)
ar -r $@ $^
# run "make warn" as a handy way to compile with the warning flags
# used in the QCBOR release process. See CFLAGS above.
warn:
make CMD_LINE="$(CMD_LINE) -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wcast-qual"
# run "make coverage" to build for gcov test coverage tool
coverage:
make CMD_LINE="$(CMD_LINE) -fprofile-arcs -ftest-coverage" qcborcov
# The shared library is not made by default because of platform
# variability For example MacOS and Linux behave differently and some
# IoT OS's don't support them at all.
libqcbor.so: $(QCBOR_OBJ)
$(CC) -shared $^ $(CFLAGS) -o $@
PUBLIC_INTERFACE=inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_encode.h \
inc/qcbor/qcbor_number_encode.h \
inc/qcbor/qcbor_tag_encode.h \
inc/qcbor/qcbor_decode.h \
inc/qcbor/qcbor_main_decode.h \
inc/qcbor/qcbor_spiffy_decode.h \
inc/qcbor/qcbor_tag_decode.h \
inc/qcbor/qcbor_number_decode.h
src/UsefulBuf.o: inc/qcbor/UsefulBuf.h
src/qcbor_main_encode.o: inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_encode.h
src/qcbor_tag_encode.o: inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_encode.h \
inc/qcbor/qcbor_number_encode.h \
inc/qcbor/qcbor_tag_encode.h \
src/ieee754.h
src/qcbor_main_decode.o: inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_decode.h \
inc/qcbor/qcbor_spiffy_decode.h \
inc/qcbor/qcbor_tag_decode.h \
src/decode_nesting.h \
src/ieee754.h
src/qcbor_tag_decode.o: inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_decode.h \
inc/qcbor/qcbor_spiffy_decode.h \
src/decode_nesting.h \
inc/qcbor/qcbor_tag_decode.h
src/qcbor_spiffy_decode.o: inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_decode.h \
inc/qcbor/qcbor_spiffy_decode.h \
src/decode_nesting.h \
inc/qcbor/qcbor_tag_decode.h
src/qcbor_number_decode.o: inc/qcbor/UsefulBuf.h \
inc/qcbor/qcbor_private.h \
inc/qcbor/qcbor_common.h \
inc/qcbor/qcbor_main_decode.h \
inc/qcbor/qcbor_tag_decode.h \
inc/qcbor/qcbor_spiffy_decode.h \
inc/qcbor/qcbor_number_decode.h \
src/ieee754.h
src/iee754.o: src/ieee754.h \
inc/qcbor/qcbor_common.h
src/qcbor_err_to_str.o: inc/qcbor/qcbor_common.h
example.o: $(PUBLIC_INTERFACE)
ub-example.o: $(PUBLIC_INTERFACE)
tag-examples.o: $(PUBLIC_INTERFACE)
test/run_tests.o: test/UsefulBuf_Tests.h test/float_tests.h test/run_tests.h test/qcbor_encode_tests.h test/qcbor_decode_tests.h inc/qcbor/qcbor_private.h
test/UsefulBuf_Tests.o: test/UsefulBuf_Tests.h inc/qcbor/UsefulBuf.h
test/qcbor_encode_tests.o: test/qcbor_encode_tests.h $(PUBLIC_INTERFACE)
test/qcbor_decode_tests.o: test/qcbor_decode_tests.h $(PUBLIC_INTERFACE)
test/float_tests.o: test/float_tests.h test/half_to_double_from_rfc7049.h $(PUBLIC_INTERFACE)
test/half_to_double_from_rfc7049.o: test/half_to_double_from_rfc7049.h
cmd_line_main.o: test/run_tests.h $(PUBLIC_INTERFACE)
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
install: libqcbor.a $(PUBLIC_INTERFACE)
install -d $(DESTDIR)$(PREFIX)/lib/
install -m 644 libqcbor.a $(DESTDIR)$(PREFIX)/lib/
install -d $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_private.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_common.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_main_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_spiffy_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_number_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_tag_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_spiffy_decode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_main_encode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_number_encode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_tag_encode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/qcbor_encode.h $(DESTDIR)$(PREFIX)/include/qcbor
install -m 644 inc/qcbor/UsefulBuf.h $(DESTDIR)$(PREFIX)/include/qcbor
install_so: libqcbor.so
install -m 755 libqcbor.so $(DESTDIR)$(PREFIX)/lib/libqcbor.so.1.0.0
ln -sf libqcbor.so.1 $(DESTDIR)$(PREFIX)/lib/libqcbor.so
ln -sf libqcbor.so.1.0.0 $(DESTDIR)$(PREFIX)/lib/libqcbor.so.1
uninstall: libqcbor.a $(PUBLIC_INTERFACE)
$(RM) -d $(DESTDIR)$(PREFIX)/include/qcbor/*
$(RM) -d $(DESTDIR)$(PREFIX)/include/qcbor/
$(RM) $(addprefix $(DESTDIR)$(PREFIX)/lib/, \
libqcbor.a libqcbor.so libqcbor.so.1 libqcbor.so.1.0.0)
clean:
rm -f $(QCBOR_OBJ) $(TEST_OBJ) $(COV_FILES) libqcbor.a cmd_line_main.o libqcbor.a libqcbor.so qcbormin qcbortest

View File

@@ -0,0 +1,239 @@
![QCBOR Logo](https://github.com/laurencelundblade/qdv/blob/master/logo.png?raw=true)
## QCBOR 2.0 Alpha
This is a QCBOR 2.0 alpha release. It is suitable for test and prototyping,
but not commerical use. The planned 2.0 changes are in, but they are
not fully tested or documented. The interface is also subject to change.
QCBOR 2.0 is compatible with 1.0 with one exception that can be overriden
with a call to QCBORDecode_Compatibilityv1().
Please file issues found with this release in GitHub.
### Major Changes
* Unexpected tag numbers are not silently ignored unless in v1 compatibility
mode. This is more correct decoding behavior.
* New API to explicitly decode tag numbers to make tag numbers easier to
work with.
* Plug-in API scheme for custom tag content processors.
* Sort maps and check for duplicate labels when encoding.
* Encode modes for dCBOR, CDE and Preferred Serialization.
* Decode modes that check for conformance with dCBOR, CDE and Preferred Serialization.
* Full support for preferred serialization of big numbers, decimal fractions and big numbers.
* Full support for 65-bit negative integers.
* The decoding interface is split into four files (sources files are too).
### Compatibility with QCBOR v1
QCBOR v1 does not error out when a tag number occurs where it is not
expected. QCBOR v2 does. Tag numbers really do change the type of an
item, so it is more correct to error out.
For many protocols, the v2 behavior produces a more correct implementation,
so it is often better to not return to QCBOR v1 behavior.
Another difference is that the tag content decoders for big numbers,
big floats, URIs and such are not enabled by default. These are
what notices the things like the big number and big float tags,
decodes them and turns them into QCBOR types for big numbers and floats.
QCBORDecode_Compatibilityv1() disables the error out on unprocessed
tag numbers and installs the same tag content decoders that v1 had.
The tag number decoders can be installed with the retention of
erroring out on unprocessed tag numbers by calling
QCBORDecode_InstallTagDecoders(pMe, QCBORDecode_TagDecoderTablev1, NULL);
QCBOR v2 requires tag numbers to be consumed in one of three ways.
It may be consumed explicitly with QCBORDecode_VGetNextTagNumber().
It may be consumed by a tag content process or like QCBORDecode_DateEpochTagCB()
installed with QCBORDecode_InstallTagDecoders(). It may be
consumed with a spiffy decode function like QCBORDecode_GetTBigNumber().
**QCBOR** is a powerful, commercial-quality CBOR encoder-decoder that
implements these RFCs:
* [RFC8949](https://tools.ietf.org/html/rfc8949) The CBOR Standard. (Nearly everything
except full (complex) duplicate detection))
* [RFC7049](https://tools.ietf.org/html/rfc7049) The previous CBOR standard.
Replaced by RFC 8949.
* [RFC8742](https://tools.ietf.org/html/rfc8742) CBOR Sequences
* [RFC8943](https://tools.ietf.org/html/rfc8943) CBOR Dates
* [RFC8943](https://tools.ietf.org/html/rfc8943) CBOR Dates
* [dCBOR](https://www.ietf.org/archive/id/draft-mcnally-deterministic-cbor-11.html) "dCBOR, deterministic encoding"
## QCBOR Characteristics
**Implemented in C with minimal dependency** Dependent only
on C99, <stdint.h>, <stddef.h>, <stdbool.h> and <string.h> making
it highly portable. <math.h> and <fenv.h> are used too, but their
use can disabled. No #ifdefs or compiler options need to be set for
QCBOR to run correctly.
**Focused on C / native data representation** Careful conversion of
CBOR data types in to C data types, handling over and
underflow, strict typing and such so the caller doesn't have to
worry so much about this and so code using QCBOR passes static
analyzers easier. Simpler code because there is no support for
encoding/decoding to/from JSON, pretty printing, diagnostic
notation... Only encoding from native C representations and decoding
to native C representations is supported.
**Small simple memory model** Malloc is not needed. The encode
context is 176 bytes, decode context is 312 bytes and the
description of decoded data item is 56 bytes. Stack use is light and
there is no recursion. The caller supplies the memory to hold the
encoded CBOR and encode/decode contexts so caller has full control
of memory usage making it good for embedded implementations that
have to run in small fixed memory.
**Easy decoding of maps** The "spiffy decode" functions allow
fetching map items directly by label. Detection of duplicate map
items is automatically performed. This makes decoding of complex
protocols much simpler, say when compared to TinyCBOR.
**Supports most of RFC 8949** With some size limits, all data types
and formats in the specification are supported. Map sorting is main
CBOR feature that is not supported. The same decoding API supports
both definite and indefinite-length map and array decoding. Decoding
indefinite length strings is supported but requires a string
allocator be set up. Encoding of indefinite length strings is
planned, but not yet supported.
**Extensible and general** Provides a way to handle data types that
are not directly supported.
**Secure coding style** Uses a construct called UsefulBuf as a
discipline for very safe coding and handling of binary data.
**Small code size** In the smallest configuration the object
code is less than 4KB on 64-bit x86 CPUs. The design is such that
object code for QCBOR APIs not used is not referenced.
**Clear documented public interface** The public interface is
separated from the implementation. It can be put to use without
reading the source.
**Comprehensive test suite** Easy to verify on a new platform or OS
with the test suite. The test suite dependencies are minimal and the
same as the library's.
## Documentation
Full API documentation is at https://www.securitytheory.com/qcbor-docs/
## Comparison to TinyCBOR
TinyCBOR is a popular widely used implementation. Like QCBOR,
it is a solid, well-maintained commercial quality implementation. This
section is for folks trying to understand the difference in
the approach between QCBOR and TinyCBOR.
TinyCBOR's API is more minimalist and closer to the CBOR
encoding mechanics than QCBOR's. QCBOR's API is at a somewhat higher
level of abstraction.
QCBOR really does implement just about everything described in
RFC 8949. The main part missing is sorting of maps when encoding.
TinyCBOR implements a smaller part of the standard.
No detailed code size comparison has been made, but in a spot check
that encodes and decodes a single integer shows QCBOR about 25%
larger. QCBOR encoding is actually smaller, but QCBOR decoding is
larger. This includes the code to call the library, which is about the
same for both libraries, and the code linked from the libraries. QCBOR
is a bit more powerful, so you get value for the extra code brought
in, especially when decoding more complex protocols.
QCBOR tracks encoding and decoding errors internally so the caller
doesn't have to check the return code of every call to an encode or
decode function. In many cases the error check is only needed as the
last step or an encode or decode. TinyCBOR requires an error check on
each call.
QCBOR provides a substantial feature that allows searching for data
items in a map by label. It works for integer and text string labels
(and at some point byte-string labels). This includes detection of
items with duplicate labels. This makes the code for decoding CBOR
simpler, similar to the encoding code and easier to read. TinyCBOR
supports search by string, but no integer, nor duplicate detection.
QCBOR provides explicit support many of the registered CBOR tags. For
example, QCBOR supports big numbers and decimal fractions including
their conversion to floats, uint64_t and such.
Generally, QCBOR supports safe conversion of most CBOR number formats
into number formats supported in C. For example, a data item can be
fetched and converted to a C uint64_t whether the input CBOR is an
unsigned 64-bit integer, signed 64-bit integer, floating-point number,
big number, decimal fraction or a big float. The conversion is
performed with full proper error detection of overflow and underflow.
QCBOR has a special feature for decoding byte-string wrapped CBOR. It
treats this similar to entering an array with one item. This is
particularly use for CBOR protocols like COSE that make use of
byte-string wrapping. The implementation of these protocols is
simpler and uses less memory.
QCBOR's test suite is written in the same portable C that QCBOR is
where TinyCBOR requires Qt for its test. QCBOR's test suite is
designed to be able to run on small embedded devices the same as
QCBOR.
## Code Status
This is the 2.0 alpha release. It has large changes and feature
additions. It's not ready for commericial use yet. The main short
coming is the need for more testing of the newer features. It will
go through alpha, then to beta and then to an official 2.0
commerical release sometimes in 2025.
The official QCBOR commercial quality release (as of this writing) is QCBOR 1.5. It
is very stable. Only small fixes and features additions have been
made to it over the last years.
QCBOR was originally developed by Qualcomm. It was [open sourced
through CAF](https://source.codeaurora.org/quic/QCBOR/QCBOR/) with a
permissive Linux license, September 2018 (thanks Qualcomm!).
## Other Software Using QCBOR
* [t_cose](https://github.com/laurencelundblade/t_cose) implements enough of
[COSE, RFC 8152](https://tools.ietf.org/html/rfc8152) to support
[CBOR Web Token (CWT)](https://tools.ietf.org/html/rfc8392) and
[Entity Attestation Token (EAT)](https://tools.ietf.org/html/draft-ietf-rats-eat-06).
Specifically it supports signing and verification of the COSE_Sign1 message.
* [ctoken](https://github.com/laurencelundblade/ctoken) is an implementation of
EAT and CWT.
## Credits
* Ganesh Kanike for porting to QSEE
* Mark Bapst for sponsorship and release as open source by Qualcomm
* Sachin Sharma for release through CAF
* Tamas Ban for porting to TF-M and 32-bit ARM
* Michael Eckel for Makefile improvements
* Jan Jongboom for indefinite length encoding
* Peter Uiterwijk for error strings and other
* Michael Richarson for CI set up and fixing some compiler warnings
* Máté Tóth-Pál for float-point disabling and other
* Dave Thaler for portability to Windows
### Copyright for this README
Copyright (c) 2018-2025, Laurence Lundblade. All rights reserved.
Copyright (c) 2021-2023, Arm Limited. All rights reserved.

View File

@@ -0,0 +1,27 @@
# Security Policy
## Reporting a Vulnerability
Please report security vulnerabilities by sending email to lgl@island-resort.com.
Please include "QCBOR SECURITY" in the subject line.
In most cases the vulnerability should not be reported by filing an issue in GitHub as this
will publically disclose the issue before a fix is available.
Laurence Lundblade maintains this code and will respond in a day or two with an initial
evaluation.
Security fixes will be prioritized over other work.
Vulnerabilities will be fixed promptly, but some may be more complex than others
and take longer. If the fix is quick, it will usually be turned around in a
few days.
## Availability of Fixes
When the fix has been created, it will be privately verified with the party that reported it.
Only after the fix has been verified and the reporter has had a chance to integrate the fix,
will it be made available as a public commit in GitHub.
If the reporter doesn't respond or can't integrate the fix, it will be made public after 30 days.

View File

@@ -0,0 +1,48 @@
/*==============================================================================
cmd_line_mainc.c -- Runs tests for QCBOR encoder-decoder
Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 9/13/18
=============================================================================*/
#include <stdio.h>
#include "run_tests.h"
#include "example.h"
#include "tag-examples.h"
#include "ub-example.h"
/*
This is an implementation of OutputStringCB built using stdio. If
you don't have stdio, replaces this.
*/
static void fputs_wrapper(const char *szString, void *pOutCtx, int bNewLine)
{
fputs(szString, (FILE *)pOutCtx);
if(bNewLine) {
fputs("\n", pOutCtx);
}
}
int main(int argc, const char * argv[])
{
(void)argc; // Avoid unused parameter error
RunQCborExample();
RunTagExamples();
RunUsefulBufExample();
// This call prints out sizes of data structures to remind us
// to keep them small.
PrintSizesQCBOR(&fputs_wrapper, stdout);
// This runs all the tests
return RunTestsQCBOR(argv+1, &fputs_wrapper, stdout, NULL);
}

View File

@@ -0,0 +1,332 @@
@anchor Building
## Building
The library is built solely from the src and inc directores. The inc directory
contains the public interface. The test app and such are in other
directories.
There is a basic makefile that will build the library and command
line test app. CMake is also available, please read
the "Building with CMake" section for more information.
QCBOR will compile and fully function without any build configuration
or set up. It is 100% portable.
There are a number of C preprocessor #defines that can be set.
Their primary purpose is to reduce library object code sizes
by disabling features. A couple slightly improve performance.
See the comment sections on "Configuration" in inc/UsefulBuf.h and
the pre processor defines that start with QCBOR_DISABLE_XXX.
The test directory includes the tests that are nearly as portable as
the main implementation. If your development environment doesn't
support UNIX style command line and make, you should be able to make a
simple project and add the test files to it. Then just call
RunTests() to invoke them all.
### Building with CMake
CMake can also be used to build QCBOR and the test application. Having the root
`CMakeLists.txt` file, QCBOR can be easily integrated with your project's
existing CMake environment. The result of the build process is a static library,
to build a shared library instead you must add the
`-DBUILD_SHARED_LIBS=ON` option at the CMake configuration step.
The tests can be built into a simple command line application to run them as it
was mentioned before; or it can be built as a library to be integrated with your
development environment.
The `BUILD_QCBOR_TEST` CMake option can be used for building the tests, it can
have three values: `APP`, `LIB` or `OFF` (default, test are not included in the
build).
Building the QCBOR library:
```bash
cd <QCBOR_base_folder>
# Configuring the project and generating a native build system
cmake -S . -B <build_folder>
# Building the project
cmake --build <build_folder>
```
Building and running the QCBOR test app:
```bash
cd <QCBOR_base_folder>
# Configuring the project and generating a native build system
cmake -S . -B <build_folder> -DBUILD_QCBOR_TEST=APP
# Building the project
cmake --build <build_folder>
# Running the test app
.<build_folder>/test/qcbortest
```
To enable all the compiler warnings that are used in the QCBOR release process
you can use the `BUILD_QCBOR_WARN` option at the CMake configuration step:
```bash
cmake -S . -B <build_folder> -DBUILD_QCBOR_WARN=ON
```
### Floating Point Support & Configuration
By default, all QCBOR floating-point features are enabled:
* Encoding and decoding of basic float types, single and double-precision
* Encoding and decoding of half-precision with conversion to/from single
and double-precision
* Preferred serialization of floating-point
* Floating point dates
* Methods that can convert big numbers, decimal fractions and other numbers
to/from floating-point
If full floating-point is not needed, the following #defines can be
used to reduce object code size and dependency.
See discussion in qcbor_encode.h for other details.
#### #define QCBOR_DISABLE_FLOAT_HW_USE
This removes dependency on:
* Floating-point hardware and floating-point instructions
* `<math.h>` and `<fenv.h>`
* The math library (libm, -lm)
For most limited environments, this removes enough floating-point
dependencies to be able to compile and run QCBOR.
Note that this does not remove use of the types double and float from
QCBOR, but it limits QCBOR's use of them to converting the encoded
byte stream to them and copying them. Converting and copying them
usually don't require any hardware, libraries or includes. The C
compiler takes care of it on its own.
QCBOR uses its own implementation of half-precision float-pointing
that doesn't depend on math libraries. It uses masks and shifts
instead. Thus, even with this define, half-precision encoding and
decoding works.
When this is defined, the QCBOR functionality lost is minimal and only
for decoding:
* Decoding floating-point format dates are not handled
* There is no conversion between floats and integers when decoding. For
example, QCBORDecode_GetUInt64ConvertAll() will be unable to convert
to and from float-point.
* Floats will be unconverted to double when decoding.
No interfaces are disabled or removed with this define. If input that
requires floating-point conversion or functions are called that
request floating-point conversion, an error code like
`QCBOR_ERR_HW_FLOAT_DISABLED` will be returned.
This saves only a small amount of object code. The primary purpose for
defining this is to remove dependency on floating point hardware and
libraries.
#### #define QCBOR_DISABLE_PREFERRED_FLOAT
This eliminates support of:
- encode/decode of half-precision
- shortest-form encoding of floats
- QCBORDecode_GetNumberConvertPrecisely()
This saves about 1KB of object code, though much of this can be saved
by not calling any functions to encode doubles or floats or
QCBORDecode_GetNumberConvertPrecisely()
With this defined, single and double-precision floating-point numbers
can still be encoded and decoded. Some conversion of floating-point to
and from integers, big numbers and such is also supported. Floating-point
dates are still supported.
#### #define USEFULBUF_DISABLE_ALL_FLOAT
This eliminates floating point support completely (along with related function
headers). This is useful if the compiler options deny the usage of floating
point operations completely, and the usage of a soft floating point ABI is not
possible.
#### Compiler options
Compilers support a number of options that control
which float-point related code is generated. For example,
it is usually possible to give options to the compiler to avoid all
floating-point hardware and instructions, to use software
and replacement libraries instead. These are usually
bigger and slower, but these options may still be useful
in getting QCBOR to run in some environments in
combination with `QCBOR_DISABLE_FLOAT_HW_USE`.
In particular, `-mfloat-abi=soft`, disables use of
hardware instructions for the float and double
types in C for some architectures.
#### CMake options
If you are using CMake, it can also be used to configure the floating-point
support. These options can be enabled by adding them to the CMake configuration
step and setting their value to 'ON' (True). The following table shows the
available options and the associated #defines.
| CMake option | #define |
|-----------------------------------|-------------------------------|
| QCBOR_OPT_DISABLE_FLOAT_HW_USE | QCBOR_DISABLE_FLOAT_HW_USE |
| QCBOR_OPT_DISABLE_FLOAT_PREFERRED | QCBOR_DISABLE_PREFERRED_FLOAT |
| QCBOR_OPT_DISABLE_FLOAT_ALL | USEFULBUF_DISABLE_ALL_FLOAT |
@anchor CodeSize
## Code Size
These are approximate sizes on a 64-bit x86 CPU with the -Os optimization.
All QCBOR_DISABLE_XXX are set and compiler stack frame checking is disabled
for smallest but not for largest. Smallest is the library functions for a
protocol with strings, integers, arrays, maps and Booleans, but not floats
and standard tag types.
| | smallest | largest |
|---------------|----------|---------|
| encode only | | |
| decode only | | |
| combined | | |
From the table above, one can see that the amount of code pulled in
from the QCBOR library varies a lot, ranging from 1KB to 15KB. The
main factor is the number of QCBOR functions called and
which ones they are. QCBOR minimizes internal
interdependency so only code necessary for the called functions is
brought in.
Encoding is simpler and smaller. An encode-only implementation may
bring in only 1KB of code.
Encoding of floating-point brings in a little more code as does
encoding of tagged types and encoding of bstr wrapping.
Basic decoding using QCBORDecode_GetNext() brings in 3KB.
Use of the supplied MemPool by calling QCBORDecode_SetMemPool() to
setup to decode indefinite-length strings adds 0.5KB.
Basic use of spiffy decode to brings in about 3KB. Using more spiffy
decode functions, such as those for tagged types bstr wrapping brings
in more code.
Finally, use of all of the integer conversion functions will bring in
about 5KB, though you can use the simpler ones like
QCBORDecode_GetInt64() without bringing in very much code.
In addition to using fewer QCBOR functions, the following are some
ways to make the code smaller.
The gcc compiler output is usually smaller than llvm because stack
guards are off by default (be sure you actually have gcc and not llvm
installed to be invoked by the gcc command). You can also turn off
stack gaurds with llvm. It is safe to turn off stack gaurds with this
code because Usefulbuf provides similar defenses and this code was
carefully written to be defensive.
If QCBOR is installed as a shared library, then of course only one
copy of the code is in memory no matter how many applications use it.
### Disabling Features
The main control over the amount of QCBOR code that gets linked
is through which QCBOR functions are used. Linking against a
library or dead stripping will eliminate all code not explicitly
called.
In addition to using fewer QCBOR functions, the following #defines
can be set to further reduce code size. For example,
QCBOR_DISABLE_ENCODE_USAGE_GUARDS will reduce the size
of many common encoding functions by performing less error
checking (but not compromising any code safety).
The amounts saved listed below are approximate. They depends on
the CPU, the compiler, configuration, which functions are
use and so on.
| #define | Saves |
| ----------------------------------------| ------|
| QCBOR_DISABLE_ENCODE_USAGE_GUARDS | |
| QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS | |
| QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS | |
| QCBOR_DISABLE_EXP_AND_MANTISSA | |
| QCBOR_DISABLE_PREFERRED_FLOAT | |
| QCBOR_DISABLE_FLOAT_HW_USE | |
| QCBOR_DISABLE_TAGS | |
| QCBOR_DISABLE_NON_INTEGER_LABELS | |
| USEFULBUF_DISABLE_ALL_FLOAT | |
QCBOR_DISABLE_ENCODE_USAGE_GUARDS affects encoding only. It doesn't
disable any encoding features, just some error checking. Disable it
when you are confident that an encoding implementation is complete and
correct.
Indefinite lengths are a feature of CBOR that makes encoding simpler
and the decoding more complex. They allow the encoder to not have to
know the length of a string, map or array when they start encoding
it. Their main use is when encoding has to be done on a very
constrained device. Conversely when decoding on a very constrained
device, it is good to prohibit use of indefinite lengths so the
decoder can be smaller.
The QCBOR decode API processes both definite and indefinite lengths
with the same API, except to decode indefinite-length strings a
storage allocator must be configured.
To reduce the size of the decoder define
QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS particularly if you are not
configuring a storage allocator.
Further reduction can be by defining
QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS which will result in an error
when an indefinite-length map or array arrives for decoding.
QCBOR_DISABLE_UNCOMMON_TAGS is removed from QCBOR v2. It didn't save
very much and you can get the same effect by not installing
the tag content handlers.
QCBOR_DISABLE_EXP_AND_MANTISSA disables the decoding of decimal
fractions and big floats.
@anchor QCBOR_DISABLE_TAGS
QCBOR_DISABLE_TAGS disables all CBOR tag decoding. If the input has
a single tag, the unrecoverable error, @ref QCBOR_ERR_TAGS_DISABLED, occurs.
The decoder is suitable only for protocols that
have no tags. This reduces the size of the core of the decoder,
particularly QCBORDecode_VGetNext(), by a about 500 bytes.
"Borrowed" tag content formats (e.g. an epoch-based date
without the tag number), can still be processed. See @ref Disabilng-Tag-Decoding.
QCBOR_DISABLE_NON_INTEGER_LABELS causes any label that doesn't
fit in an int64_t to result in a QCBOR_ERR_MAP_LABEL_TYPE error.
This also disables QCBOR_DECODE_MODE_MAP_AS_ARRAY and
QCBOR_DECODE_MODE_MAP_STRINGS_ONLY. It is fairly common for CBOR-based
protocols to use only small integers as labels.
See the discussion above on floating-point.
### Size of spiffy decode
When creating a decode implementation, there is a choice of whether
or not to use spiffy decode features or to just use
QCBORDecode_GetNext().
The implementation using spiffy decode will be simpler resulting in
the calling code being smaller, but the amount of code brought in
from the QCBOR library will be larger. Basic use of spiffy decode
brings in about 2KB of object code. If object code size is not a
concern, then it is probably better to use spiffy decode because it
is less work, there is less complexity and less testing to worry
about.
If code size is a concern, then use of QCBORDecode_GetNext() will
probably result in smaller overall code size for simpler CBOR
protocols. However, if the CBOR protocol is complex then use of
spiffy decode may reduce overall code size. An example of a complex
protocol is one that involves decoding a lot of maps or maps that
have many data items in them. The overall code may be smaller
because the general purpose spiffy decode map processor is the one
used for all the maps.

View File

@@ -0,0 +1,4 @@
Most documentation is in the header files in ../inc/qcbor/.
The files here are additional articles.
There is cross-referencing between these articles and the header files.

View File

@@ -0,0 +1,188 @@
@anchor BigNumbers
# Big Numbers
## Basics
Big numbers are integers that can be as long and as large as desired,
limited only by the amount of memory allocated for them.
They are represented as a byte string plus an indication of sign. The
most significant byte is first (network byte order) both in the
encoded form and as input and output by QCBOR APIs.
CBOR has only one encoded representation of zero for integers, big
numbers included. That means the whole negative number space is
offset by one. Since big numbers are encoded as a sign and some bytes,
the steps to encode a negative value are to take the absolute value
and then adjust by one. On encoding, the adjustment is to subtract one
from the absolute value, and on decoding to add one to the absolute
value.
Take -256 as an example. It is represented in memory with two bytes,
0x01 0x00, plus indication that it is negative, perhaps a boolean.
When it is CBOR-encoded it becomes 0xff plus an indication of the
sign.
Notice that the length changed. This is one place in the design of
CBOR where the bytes off the wire cannot be simply used as they
are. This has an effect on the big number encoding and decoding
APIs.
The tag numbers 2 and 3 indicate a big number. The tag content is a
byte string with the absolute value of the big number, offset by one
when negative. Tag number 2 indicates positive and 3 negative.
For example, 256 encoded as a positive big number is 0xc2 0x42 0x01
0x00 and -256 encodes as 0xc3 0x41 0xff (non-preferred; see preferred
example in next section).
## Preferred Serialization
The integer number space encodable by big numbers overlaps with the
number space encodable by regular integers, by CBOR type 0 and type 1,
the range from -(2^64) to (2^64)-1.
RFC 8949 big number preferred serialization requires that numbers in
the type 0 and 1 integer range must not be encoded as big
numbers. They must be encoded as type 0 and type 1 integers.
For example, 256 must be encoded as 0x19 0x01 0x00 and -256 as
0x38 0xff.
One purpose of this is to save some bytes, but typically only one byte
is saved. The more important reason for this is determinism. There is
only one way to represent a particular value.
Technically speaking, this reduction of the big number space to the
integer space is not about serialization. It doesn't change the way
big numbers and integers are serialized by themselves.
Note, also that CBOR can encode what can be called "65-big negative
numbers", 64 bits of precision and one bit to indicate a negative
one. These can't be represented by 64-bit two's compliment signed
integers or 64-bit unsigned integers. While many programming
environments, like Ruby and Python that have big number support built
in will support these, some environments like C and the CPU
instructions do not. QCBOR big number decoding supports them with
explicit code to carry the negative number in as a uint64_t and a sign
indicator. Big number specific preferred serialization requires
encoding of 65-bit negative integers. Other than big numbers QCBOR
avoids 65-big negative integers.
## QCBOR APIs
The negative number offset of one and the reduction required by
big number preferred serialization have a large effect on the QCBOR
big number API.
QCBOREncode_AddTBigNumber() and QCBORDecode_GetTBigNumber() are the
most comprehensive and easiest to use APIs. They automatically take
care of the offset of one for negative numbers and big number
preferred serialization. Unlike most other decode APIs,
QCBORDecode_GetTBigNumber() requires an output buffer of sufficient
size.
If the protocol being implemented does not use preferred
serialization, then QCBOREncode_AddTBigNumberNoPreferred() and
QCBORDecode_GetTBigNumberNoPreferred() can be used. Only tags 2 and 3
will be output when encoding. Type 0 and 1 integers will never be
output. Decoding will error if type 0 and type 1 integers are
encountered.
It is likely that any implementation for a protocol that uses big
numbers will link in a big number library. When that's so, it can be
used to offset the negative numbers by one rather than the internal
implementation that QCBOR has. If both the offset by one and the
preferred serialization are side-stepped, the big numbers APIs become
simple pass throughs for encoding and decoding byte strings. This
reduces the amount of object linked from the QCBOR library by about
1KB.
This is what QCBOREncode_AddTBigNumberRaw() and
QCBORDecode_GetTBigNumberRaw() do. Note that decoding doesn't require
an output buffer be supplied because the bytes off the wire can simply
be returned. When these are used, the big number library should be
used to add one to negative numbers before encoding and subtract one
from negative numbers after decoding.
Last, QCBORDecode_StringsTagCB(), @ref QCBOR_TYPE_POSBIGNUM,
@ref QCBOR_TYPE_NEGBIGNUM, and QCBORDecode_ProcessBigNumber() need to
be described. QCBOR's tag processing callbacks can be used to handle
big numbers by installing QCBORDecode_StringsTagCB() for tag numbers 2
and 3. This will result in positive and negative big numbers being
returned in a @ref QCBORItem as QCBOR types @ref QCBOR_TYPE_POSBIGNUM and
@ref QCBOR_TYPE_NEGBIGNUM. QCBORDecode_StringsTagCB() is very simple,
and not much object code. Neither the offset of one for negative
values nor preferred serialization number reduction is preformed. It is
equivalent to QCBORDecode_GetTBigNumberRaw().
QCBORDecode_ProcessBigNumber() may be used to fully process a
@ref QCBORItem that is expected to be a big number. It will perform the
negative offset and handle the preferred serialization number
reduction. QCBORDecode_ProcessBigNumber() is what is used internally
to implement QCBORDecode_GetTBigNumber() and links in a lot of
object code. Note that QCBORDecode_ProcessBigNumber() requires an
output buffer be supplied.
QCBORDecode_ProcessBigNumbernoPreferred() is the same as
QCBORDecode_ProcessBigNumber() but will error out if the @ref QCBORItem
contains anything but @ref QCBOR_TYPE_POSBIGNUM, @ref QCBOR_TYPE_NEGBIGNUM.
Note that if QCBORDecode_StringsTagCB() is installed,
QCBORDecode_GetTBigNumber(), QCBORDecode_GetTBigNumberNoPreferred()
and QCBORDecode_GetTBigNumberRaw() all still work as documented.
## "Borrowed" Big Number Tag Content
Like all other tag decoding functions in QCBOR, the big number tag
decoders can also work with borrowed tag content for big numbers. For
example, the value of a map item might be specified that it be decoded
as a big number even though there is no tag number indicating so.
The main thing to mention here is that without a tag number, there's
nothing in the encoded CBOR to indicate the sign. That must be
indicated some other way. For example, it might be that the map item
is defined to always be a positive big number.
On the encode side, that means the sign boolean is ignored when
QCBOREncode_AddTBigNumber() is called with @c uTagRequirement other
than @ref QCBOR_ENCODE_AS_TAG.
On the decode side, the sign boolean becomes an input parameter rather
than an output parameter so that QCBORDecode_GetTBigNumber() can know
whether to apply the offset of one in case the value is negative.
## Big Number Conversions
QCBOR provides a number of decode functions that can convert big
numbers to other representations like floating point. For example, see
QCBORDecode_GetDoubleConvertAll().
Note also that QCBORDecode_ProcessBigNumber() can convert integers to
big numbers. It will work even if the protocol item is never supposed
to be a big number. It will just happily convert any type 0 or type 1
CBOR integer to a big number.
## Backwards Compatibility with QCBOR v1
QCBOR v1 supports only the minimal pass through processing of big
numbers, not the offset of one for negative values or the big number
specific preferred serialization. The main functions in v1 are
QCBOREncode_AddTPositiveBignum(), QCBOREncode_AddTNegativeBignum() and
QCBORDecode_GetBignum(). These are equivalent to
QCBOREncode_AddTBigNumberRaw() and QCBORDecode_GetTBigNumberRaw().
Also, in v1 @ref CBOR_TAG_POS_BIGNUM and @ref CBOR_TAG_NEG_BIGNUM are
always processed into a @ref QCBORItem of type @ref
QCBOR_TYPE_POSBIGNUM and @ref QCBOR_TYPE_NEGBIGNUM by an equivalent of
QCBORDecode_StringsTagCB.
All the v1 methods for big numbers such as
QCBOREncode_AddTPositiveBignum() and QCBORDecode_GetBignum() are fully
supported in v2. When QCBORDecode_StringsTagCB() is installed for tags
2 and 3 such as by calling QCBORDecode_CompatibilityV1(), QCBOR v2 big
number behavior is 100% backwards compatible.

View File

@@ -0,0 +1,144 @@
@anchor Overview
# QCBOR Overview
This implements CBOR -- Concise Binary Object Representation as
defined in [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html).
More information is at http://cbor.io. This is a near-complete
implementation of the specification.
[RFC 8742](https://www.rfc-editor.org/rfc/rfc8742.html) CBOR Sequences is
also supported. Limitations are listed further down.
See @ref Encoding for general discussion on encoding,
@ref BasicDecode for general discussion on the basic decode features
and @ref SpiffyDecode for general discussion on the easier-to-use
decoder functions.
CBOR is intentionally designed to be translatable to JSON, but not
all CBOR can convert to JSON. See RFC 8949 for more info on how to
construct CBOR that is the most JSON friendly.
The memory model for encoding and decoding is that encoded CBOR must
be in a contiguous buffer in memory. During encoding the caller must
supply an output buffer and if the encoding would go off the end of
the buffer an error is returned. During decoding the caller supplies
the encoded CBOR in a contiguous buffer and the decoder returns
pointers and lengths into that buffer for strings.
This implementation does not require malloc. All data structures
passed in/out of the APIs can fit on the stack.
Decoding of indefinite-length strings is a special case that requires
a "string allocator" to allocate memory into which the segments of
the string are coalesced. Without this, decoding will error out if an
indefinite-length string is encountered (indefinite-length maps and
arrays do not require the string allocator). A simple string
allocator called MemPool is built-in and will work if supplied with a
block of memory to allocate. The string allocator can optionally use
malloc() or some other custom scheme.
Here are some terms and definitions:
- "Item", "Data Item": An integer or string or such. The basic "thing" that
CBOR is about. An array is an item itself that contains some items.
- "Array": An ordered sequence of items, the same as JSON.
- "Map": A collection of label/value pairs. Each pair is a data
item. A JSON "object" is the same as a CBOR "map".
- "Label": The data item in a pair in a map that names or identifies
the pair, not the value. This implementation refers to it as a
"label". JSON refers to it as the "name". The CBOR RFC refers to it
this as a "key". This implementation chooses label instead because
key is too easily confused with a cryptographic key. The COSE
standard, which uses CBOR, has also chosen to use the term "label"
rather than "key" for this same reason.
- "Key": See "Label" above.
- "Tag": A data item that is an explicitly labeled new data
type made up of the tagging integer and the tag content.
See @ref CBORTags.
- "Initial Byte": The first byte of an encoded item. Encoding and
decoding of this byte is taken care of by the implementation.
- "Additional Info": In addition to the major type, all data items
have some other info. This is usually the length of the data but can
be several other things. Encoding and decoding of this is taken care
of by the implementation.
CBOR has two mechanisms for tagging and labeling the data values like
integers and strings. For example, an integer that represents
someone's birthday in epoch seconds since Jan 1, 1970 could be
encoded like this:
- First it is CBOR_MAJOR_TYPE_POSITIVE_INT (@ref QCBOR_TYPE_INT64),
the primitive positive integer.
- Next it has a "tag" @ref CBOR_TAG_DATE_EPOCH indicating the integer
represents a date in the form of the number of seconds since Jan 1,
1970.
- Last it has a string "label" like "BirthDate" indicating the
meaning of the data.
The encoded binary looks like this:
a1 # Map of 1 item
69 # Indicates text string of 9 bytes
426972746844617465 # The text "BirthDate"
c1 # Tags next integer as epoch date
1a # Indicates a 4-byte integer
580d4172 # unsigned integer date 1477263730
Implementors using this API will primarily work with
labels. Generally, tags are only needed for making up new data
types. This implementation covers most of the data types defined in
the RFC using tags. It also, allows for the use of custom tags if
necessary.
This implementation explicitly supports labels that are text strings
and integers. Text strings translate nicely into JSON objects and are
very readable. Integer labels are much less readable but can be very
compact. If they are in the range of 0 to 23, they take up only one
byte.
CBOR allows a label to be any type of data including an array or a
map. It is possible to use this API to construct and parse such
labels, but it is not explicitly supported.
@anchor Limitations
## Limitations
Summary limitations:
- The entire encoded CBOR must fit into contiguous memory.
- Max size of encoded CBOR data is a few bytes less than
@c UINT32_MAX (4GB).
- Max array / map nesting level when encoding or decoding is
@ref QCBOR_MAX_ARRAY_NESTING (this is typically 15).
- Max items in an array or map when encoding or decoding is
@ref QCBOR_MAX_ITEMS_IN_ARRAY (typically 65,536).
- Does not directly support labels in maps other than text strings & integers.
- Traversal, duplicate and sort order checking errors out for labels that are arrays or maps.
- Does not directly support integer labels beyond whats fits in @c int64_t
or @c uint64_t.
- Epoch dates limited to @c INT64_MAX (+/- 292 billion years).
- Exponents for bigfloats and decimal integers are limited to whats fits in
@c int64_t.
- Tags on labels are ignored during decoding.
- The maximum tag nesting is @c QCBOR_MAX_TAGS_PER_ITEM (typically 4).
- Works only on 32- and 64-bit CPUs.
- QCBORDecode_EnterBstrWrapped() doesn't work on indefinite-length strings.
The public interface uses @c size_t for all lengths. Internally the
implementation uses 32-bit lengths by design to use less memory and
fit structures on the stack. This limits the encoded CBOR it can
work with to size @c UINT32_MAX (4GB).
This implementation requires two's compliment integers. While
C doesn't require two's compliment, <stdint.h> does. Other
parts of this implementation may also require two's compliment.

View File

@@ -0,0 +1,7 @@
@anchor Serialization
# Serialization and Determinism
To be filled in...

View File

@@ -0,0 +1,288 @@
@anchor CBORTags
# QCBOR-oriented Introduction to Tags
## New Types
CBOR allows for the definition of new data types beyond basic
primitives like integers, strings, array and such. These new types can either be
simple extensions of a primitive type with additional semantics or
more complex structures involving large aggregations.
The mechanism for identifying these new types is called tagging.
Tagging uses a simple unsigned integer to indicate that the following
CBOR item is a different type.
For example, when an encoded integer is preceeded by the encoded
tag number 1, the integer represents an epoch date.
It's important to note that CBOR uses the word "tag" in an unusual
way. In CBOR, a "tag" refers to the combination of the tag number and
the tag content. By the normal dictionary definition, a "tag" would
be just a tag number, not an aggregation of tag number and tag content
By analogy, if you attach a small label to an elephant's ear, the
"tag" in CBOR terms would be the combination of the label (tag number)
and the elephant (tag content).
QCBOR always uses the term "tag number" to refer to the integer that
identifies the type, "tag content" to refer to the target of the
indicating integer and "tag" as the full combination of both.
The tag content is always a single data item. However, this item can
itself be a complex structure, such as a map or an array, which may
contain nested maps and arrays, allowing for arbitrarily complex tag
content.
Tag numbers can range up to UINT64_MAX, providing a large number of
possible tags. Some are defined by standards and registered in the
IANA CBOR Tag Registry, while a substantial range is available for
proprietary use.
@anchor AreTagsOptional
## Are Tags "Optional"?
The description of tags in
[RFC 7049](https://tools.ietf.org/html/rfc7049) and in some other
places can lead one to think they are optional prefixes that can be
ignored at times. This is not true.
As stated above, a tag is exactly a tag number and a single data item
that is the tag content. Its purpose in the encoded CBOR is to
indicate something is of a data type. Ignoring it would be like
ignoring a typedef or struct in C.
However, it is common in CBOR-based protocols to use the format,
semantics and definition of the tag content without it actually being a
*tag*. One can think of this as *borrowing* the tag content or implied
type information.
For example, [RFC 8392](https://tools.ietf.org/html/rfc8392) which
defines a CBOR Web Token, a CWT, says that the NumericDate field is
represented as a CBOR numeric date described as tag 1 in the CBOR
standard, but with the tag number 1 omitted from the encoding. A
NumericDate is thus not a tag. It just borrows the content format and
semantics from tag 1.
This borrowing of the content makes a lot of sense for data items that
are labeled members of a map where the type of the data can be easily
inferred by the label and the full use of a tag with a tag number
would be redundant.
There is another way that tags are "optional". RFC 8392 serves again
as an example. A CWT is officially defined as a COSE-secured map
containing a bunch of claims where each claim is a labeled data
item. This COSE-secured map-of-claims is the definition of a *CWT* and
stands on its own as the definition of a protocol message. One can say
that some protocol message is a *CWT* without ever mention the word
tag or the *CWT Tag*.
Then RFC 8392 goes on to define a *CWT Tag* as a tag with tag number
of 61 and tag content being a *CWT*. The content format definition
comes first and stands on it's own.
To recap, the tags defined in RFC 7049 such as the date formats define
the content type of the tag only in the context of the tag itself. To
use the content formats outside of the tag, the content format must be
borrowed. By contrast some definitions first define the content
format in an independent way, then they define a tag to enclose that
particular content format. A CWT is of the later sort.
Finally, every CBOR protocol should explicitly spell out how it is
using each tag, borrowing tag content and such. If the protocol you
are trying to implement doesn't, ask the designer. Generally,
protocols designs should not allow for some data item to be
either a tag or to be the borrowed tag content. While allowing this
tag optionality is a form of Postel's law, "be liberal in what you
accept", current wisdom is somewhat the opposite.
## QCBOR Tag APIs
The encode APIs are in @ref inc/qcbor/qcbor_tag_encode.h "qcbor_tag_encode.h"
and decoding APIs in @ref inc/qcbor/qcbor_tag_decode.h "qcbor_tag_decode.h"
The base primitives for encoding and decoding tag numbers are
QCBOREncode_AddTagNumber() and QCBORDecode_VGetNextTagNumber(). These
are used in constructing and decoding tags. Note that for decoding,
all tag numbers have to be consumed before decoding the tag
content. This is different from QCBOR v1 where tag numbers did not
have to be explicitly consumed.
QCBOR also provides APIs for directly encoding and decoding all the
tags standardized in [RFC 8949](https://tools.ietf.org/html/rfc8949)
(dates, big numbers and such). For encoding their names start with
"QCBOREncode_AddT" and for decoding they start with "QCBOREncode_GetT"
These APIs and structures support both the full tag form and the
borrowed content form that is not a tag. An argument of type @ref QCBOREncodeTagReq
and @ref QCBORDecodeTagReq is provided respectively to the tag encode
and decode functions to distinguish between full tags and borrowed
content.
Early versions of QCBOR do not support encoding borrowed content. The
old APIs for dates, big numbers and such are listed as deprecated, but
will continue to be supported. The encode side has functions like
QCBOREncode_AddDateEpoch() rather than
QCBOREncode_AddTDateEpoch(). The tag decode APIs always supported
borrowed content.
Last, QCBORDecode_InstallTagDecoders() allows callbacks to be
installed that will fire on a particular tag number. These callbacks
decode the tag content and put it into a QCBORItem with a new QCBOR
data type. The decoded tags show up as a @ref QCBORItem fetched by
QCBORDecode_VGetNext().
A set of callbacks called @ref QCBORDecode_TagDecoderTablev1 is
provided for all the standard tags from RFC 8949. These are not
automatically installed in QCBOR v2. These were built into QCBOR v1.
Three ways of decoding a tag are described here.
First, tag numbers can be fetched with QCBORDecode_VGetNextTagNumber().
This is the base primitive for tag decoding. It's like GetNext(), but
for tag numbers. It only consumed tag numbers. GetNext() only consumes
things without tag numbers. That means all tag numbers must be consumed
before GetNext is called. This is new in QCBOR v2 and not the same
as QCBOR v1.
Second, a tag content decoder call back may be created and installed via
QCBORDecode_InstallTagDecoders(). It will fire when a tag number
is encountered. With this there is no need to consume the tag
numbers with QCBORDecode_VGetNextTagNumber(). The decoded tag
will show up as a QCBORItem of a different type when GetNext()
is called.
Third, QCBOR provides APIs for the standard types. For example
GetEpochDate(). (These APIs additionally provide a way to
decode the following CBOR as the tag content even though there
is no tag number present).
Additionally, QCBOR provides a set of standard callbacks
for standard types that can be installed with QCBORDecode_InstallTagDecoders().
These were installed by default in QCBOR v1, but aren't in v2.
## Nested Tags
CBOR tags are an enclosing or encapsulating format. When one tag
encloses another, the enclosed tag is the content for the enclosing
tag.
Encoding nested tags is easy with QCBOREncode_AddTagNumber(). Just
call it several times before calling the functions to encode the tag
content.
When QCBOR decodes tags it does so by first completely processing the
built-in tags that it knows how to process. It returns that processed
item.
If tags occur that QCBOR doesn't know how to process, it will return
the tag content as a @ref QCBORItem and list the tags that
encapsulate. The caller then has the information it needs to process
tag that QCBOR did not.
Nesting of tags is certainly used in CBOR protocols, but deep nesting
is less common so QCBOR has an implementation limit of 4 levels of tag
encapsulation on some tag content. (This can be increased by changing
@ref QCBOR_MAX_TAGS_PER_ITEM, but it will increase stack memory use by
increasing the size of a QCBORItem).
QCBOR also saves memory by mapping the tag values larger than
UINT16_MAX, so the tags have to fetched through an accessor function.
When decoding with QCBORDecode_GetNext(), the encapsulating tags are
listed in the QCBORItem returned. When decoding with spiffy decoding
functions the tags encapsulating the last-decoded item are saved in
the decode context and have to be fetched with
QCBORDecode_GetNthTagOfLast().
## Tags for Encapsulated CBOR
Tag 24 and 63 deserve special mention. The content of these tags is a
byte string containing encoded CBOR. The content of tag 24 is a single
CBOR data item and the content of tag 63 is a CBOR sequence, more than
one data item. Said another way, with tag 24 you can have deeply
nested complex structures, but the if you do the one data item must be
either a map or an array or a tag that defined to be complex and
nested. With tag 63, the content can be a sequence of integers not
held together in a map or array. Tag 63 is defined in
[RFC 8742](https://tools.ietf.org/html/rfc8742).
The point of encapsulating CBOR this way is often so it can be
cryptographically signed. It works well with off-the-shelf encoders
and decoders to sign and verify CBOR this way because the decoder can
just get the byte string that it needs to hash in a normal way, then
feed the content back into another instance of the CBOR decoder.
It is also a way to break up complex CBOR structures so they can be
decoded in layers. Usually, with CBOR one error will render the whole
structure un-decodable because there is little redundancy in the
encoding. By nesting like this, an error in the wrapped CBOR will not
cause decoding error in the wrapping CBOR.
QCBOR can be asked to treat these two tags as nesting like maps and
arrays are nesting with the spiffy decode
QCBORDecode_EnterBstrWrapped() decoding function. It is kind of like
entering an array with one item, but with the difference that the end
is defined by the end of the byte string not the end of the array.
These tags work like others in that they can be the proper tag or they
can be the borrowed content. The QCBOR API supports this as any other
tag.
Finally, the payload and protected headers of COSE are worth
mentioning here. Neither are officially tag 24 or 63 though they look
like it and QCBORs decode APIs can be used on them.
The protected headers are a CBOR byte string that always contains
encoded CBOR. It could have been described as tag 24 borrowed content.
The payload is always a byte string, but only sometimes contains
encoded CBOR. It never could have been defined as tag 24. When the
payload is known to contain CBOR, like the case of a CWT, then QCBOR's
QCBORDecode_EnterBstrWrapped() can be used to decode it.
## Tags that Can be Ignored
There are a few specially defined tags that can actually be
ignored. These are the following:
21 Hint that content should be base64url encoded
22 Hint that content should be base64 encoded
23 Hint that content should be base16 encoded
57799 Tag that serves as a CBOR magic number
The content format for all these tags is that it can be any valid
CBOR. Decoding of these tags doesn't have to check the content format.
Tag 55799 is not really for consumption by the CBOR decoder. Rather it
is for file format checkers and such. The other tags are just hints
in how to process the content. They don't really create new data types
with new semantics.
Other than these four, just about every other tag defined thus far
requires the content to be of a specific type and results in a new
data type that a protocol decoder must understand.
## Standard Tags and the Tags Registry
Tags used in CBOR protocols should at least be registered in the
[IANA CBOR Tags Registry](https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml).
A small number of tags (0-23), must be full IETF standards. Tags
24-255 require published documentation. Beyond tag 255, the tags are
first come first served. Any tag can be an IETF standard if the
authors chooses to take it through the process.
There is no range for private use, so any tag used in a CBOR protocol
should be registered. The range of tag values is very large to
accommodate this.
As described above, it is common to use data types from the registry
in a CBOR protocol without the explicit tag, to borrow the content, so
in a way the IANA registry is a registry of data types.

View File

@@ -0,0 +1,56 @@
# Tag 1 Implementators FAQ
+-------------------------------+---------------------+------------------------+
| | 1 second resolution | microsecond resolution |
+-------------------------------+---------------------+------------------------+
| Recent Human Time Scale | 32-bit unsigned | double |
| 1970 - 2106 | | |
+-------------------------------+---------------------+------------------------+
| Recent Human Past | 32-bit negative | double |
| 1834 - 1970 | | |
+-------------------------------+---------------------+------------------------+
| Universal Scale, Now & Future | 64-bit unsigned | double |
| 1970 - 500 billion years | | |
+-------------------------------+---------------------+------------------------+
| Universal Scale, Past | 64-bit negative | double |
| 500 billion years ago - 1969 | | |
+-------------------------------+---------------------+------------------------+
Q: I just want to implement the minimum, what should I do?
A: You should support 64-bit unsigned encoding. This will cover just about every use case because it works from 1970 until over 500 billion years in the future and 1 second resolution is enough for most human activity. The earth is only 4.5 billion years old. Note that time values up to 2106 will be encoded as 32-bit integers even though 64-bit integers are supported because only 32-bits are needed to count seconds up to the year 2106.
Q: Im implementing on an 8-bit CPU and 64-bit integers are really a problem.
A: You can support just 32-bit integers, but they will stop working in 2106.
Q: Why 2106 and not 2038?
A: Because CBOR encodes positive and negative integers with different major types and thus is able to use the full 32-bits for positive integers.
Q: What if I need time values before 1970?
A: Implement 64 or 32-bit negative time values, but note that there is no clear standard for this as POSIX and UTC time are not defined for this time period. If your implementation assumes every days is 86,400 seconds and follow the rules for leap years, you should have accuracy to within hours, if not minutes.
Q: Is Tag 1 better than Tag 0 for time values?
A: In a lot of ways it is, because it is just a simple integer. It takes up a lot less space. It is a lot simpler to parse. Most OSs have the ability to turn POSIX time into a structure of month, day, year, hour, minute and second. Note however that POSIX time has a discontinuity of about once every year for leap second adjustment.
Q: What is a leap second?
A: It actually takes the earth about 1 year and 1 second to revolve around the sun, so 1 extra second has to be added almost every year. UTC Time handles this by counting the seconds from 0-60 (not 0-59) in the last minute of the year. This standard uses POSIX time which can be said to handle this by the clock stopping for 1 second in the last minute of the year.
Q: Do I have to implement floating point time?
A: No. There are not many use cases that need it. However, for maximal interoperability, it is good to support it.
Q: When should I use floating point?
A: Only if you need time resolution greater than one second. There is no benefit otherwise. 64-bit time can represent time +/-500 billion years in the same number of bits, so floating point time is unnecessary for very large times scales.
Q: What resolution do I get with floating point?
A: It varies over the years. For 1970 to 1971 you get almost nanosecond accuracy. For the current century, 2000-2099, you get microsecond accuracy. 285 million years from now, it will be less than a second and the 64-bit unsigned representation will have more resolution. This is because a double only has 52 bits of resolution.
Q: Should I implement single or double floating point?
A: If you are going to use floating point you should always implement double. Single has no advantage over 32-bit integers. It provides less range and less precision. It has only 23 bits of resolution. It is of course good to support decoding of single in case someone sends you one, but there no point to ever sending a tag 1 encoded time value as a single.
Q: Can I disallow floating point time in the definition of my protocol?
A: Yes. This is a good idea if you do not need resolution less than one second. It will make implementations simpler and more compact. Note that while most CPUs do support IEEE 754 floating point, particularly small ones do not.
Q: What if Im transmitting thousands of time stamps and space is a problem?
A: If you want to maintain 1 second resolution, there is really no more compact way to transmit time than tag 1 with 32-bit or 64-bit integer. If you wish reduce resolution, use a different time, perhaps one that counts days rather than seconds.

View File

@@ -0,0 +1,35 @@
/*! @mainpage QCBOR Documentation
QCBOR can be downloaded from its [GitHub repository](https://github.com/laurencelundblade/QCBOR).
This documentation corresponds to QCBOR v2, currently in its alpha release
phase. While much of the content also applies to v1 -- which is stable,
commercially suitable, and compatible with v2 -- it's important to note that
v2 introduces new features and APIs not available in v1.
@par Table of Contents
API Reference:
- Common
- Error codes and common constants: @ref inc/qcbor/qcbor_common.h "qcbor_common.h"
- Functions for managing buffers of binary data: @ref inc/qcbor/UsefulBuf.h "UsefulBuf.h"
- Encoding
- Main/Basic encode functions: @ref inc/qcbor/qcbor_main_encode.h "qcbor_main_encode.h"
- Number encode functions: @ref inc/qcbor/qcbor_number_encode.h "qcbor_number_encode.h"
- Tag encode functions: @ref inc/qcbor/qcbor_tag_encode.h "qcbor_tag_encode.h"
- Decoding
- Main/Basic decode functions: @ref inc/qcbor/qcbor_main_decode.h "qcbor_main_decode.h"
- Spiffy decode functions: @ref inc/qcbor/qcbor_spiffy_decode.h "qcbor_spiffy_decode.h"
- Tag decode functions: @ref inc/qcbor/qcbor_tag_decode.h "qcbor_tag_decode.h"
- Number decode functions: @ref inc/qcbor/qcbor_number_decode.h "qcbor_number_decode.h"
Subject Matter:
- @ref Overview "QCBOR overview and implementation limits"
- @ref Building "Building QCBOR with make and cmake"
- @ref CodeSize "Minimizing the amount of code linked and disabling features"
- @ref SpiffyDecode "The 'spiffy' decode functions for easier map decoding"
- CBOR Numbers
- @ref BigNumbers "Characteristics, encoding and decoding big numbers"
- Tags
- @ref CBORTags
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,386 @@
/* =========================================================================
example.c -- Example code for QCBOR
Copyright (c) 2020-2021, Laurence Lundblade. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 6/30/2020
========================================================================== */
#include <stdio.h>
#include "example.h"
#include "qcbor/qcbor_main_encode.h"
#include "qcbor/qcbor_number_encode.h"
#include "qcbor/qcbor_main_decode.h"
#include "qcbor/qcbor_spiffy_decode.h"
#include "qcbor/qcbor_number_decode.h"
/**
* This is a simple example of encoding and decoding some CBOR from
* and to a C structure.
*
* This also includes a comparison between the original structure
* and the one decoded from the CBOR to confirm correctness.
*/
#define MAX_CYLINDERS 16
/**
* The data structure representing a car engine that is encoded and
* decoded in this example.
*/
typedef struct
{
UsefulBufC Manufacturer;
int64_t uDisplacement;
int64_t uHorsePower;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
double dDesignedCompresion;
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
int64_t uNumCylinders;
bool bTurboCharged;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
struct {
double dMeasuredCompression;
} cylinders[MAX_CYLINDERS];
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
} CarEngine;
/**
* @brief Initialize the Engine data structure with values to encode.
*
* @param[out] pE The Engine structure to fill in
*/
void EngineInit(CarEngine *pE)
{
pE->Manufacturer = UsefulBuf_FROM_SZ_LITERAL("Porsche");
pE->uDisplacement = 3296;
pE->uHorsePower = 210;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
pE->dDesignedCompresion = 9.1;
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
pE->uNumCylinders = 6;
pE->bTurboCharged = false;
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
pE->cylinders[0].dMeasuredCompression = 9.0;
pE->cylinders[1].dMeasuredCompression = 9.2;
pE->cylinders[2].dMeasuredCompression = 8.9;
pE->cylinders[3].dMeasuredCompression = 8.9;
pE->cylinders[4].dMeasuredCompression = 9.1;
pE->cylinders[5].dMeasuredCompression = 9.0;
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
}
/**
* @brief Compare two Engine structure for equality.
*
* @param[in] pE1 First Engine to compare.
* @param[in] pE2 Second Engine to compare.
*
* @retval Return @c true if the two Engine data structures are exactly the
* same.
*/
static bool EngineCompare(const CarEngine *pE1, const CarEngine *pE2)
{
if(pE1->uNumCylinders != pE2->uNumCylinders) {
return false;
}
if(pE1->bTurboCharged != pE2->bTurboCharged) {
return false;
}
if(pE1->uDisplacement != pE2->uDisplacement) {
return false;
}
if(pE1->uHorsePower != pE2->uHorsePower) {
return false;
}
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
if(pE1->dDesignedCompresion != pE2->dDesignedCompresion) {
return false;
}
for(int64_t i = 0; i < pE2->uNumCylinders; i++) {
if(pE1->cylinders[i].dMeasuredCompression !=
pE2->cylinders[i].dMeasuredCompression) {
return false;
}
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
if(UsefulBuf_Compare(pE1->Manufacturer, pE2->Manufacturer)) {
return false;
}
return true;
}
/**
* @brief Encode an initialized CarEngine data structure in CBOR.
*
* @param[in] pEngine The data structure to encode.
* @param[in] Buffer Pointer and length of buffer to output to.
*
* @return The pointer and length of the encoded CBOR or
* @ref NULLUsefulBufC on error.
*
* This encodes the input structure @c pEngine as a CBOR map of
* label-value pairs. An array of float is one of the items in the
* map.
*
* This uses the UsefulBuf convention of passing in a non-const empty
* buffer to be filled in and returning a filled in const buffer. The
* buffer to write into is given as a pointer and length in a
* UsefulBuf. The buffer returned with the encoded CBOR is a
* UsefulBufC also a pointer and length. In this implementation the
* pointer to the returned data is exactly the same as that of the
* empty buffer. The returned length will be smaller than or equal to
* that of the empty buffer. This gives correct const-ness for the
* buffer passed in and the data returned.
*
* @c Buffer must be big enough to hold the output. If it is not @ref
* NULLUsefulBufC will be returned. @ref NULLUsefulBufC will be
* returned for any other encoding errors.
*
* This can be called with @c Buffer set to @ref SizeCalculateUsefulBuf
* in which case the size of the encoded engine will be calculated,
* but no actual encoded CBOR will be output. The calculated size is
* in @c .len of the returned @ref UsefulBufC.
*/
UsefulBufC EncodeEngine(const CarEngine *pEngine, UsefulBuf Buffer)
{
/* Set up the encoding context with the output buffer */
QCBOREncodeContext EncodeCtx;
QCBOREncode_Init(&EncodeCtx, Buffer);
/* Proceed to output all the items, letting the internal error
* tracking do its work */
QCBOREncode_OpenMap(&EncodeCtx);
QCBOREncode_AddTextToMapSZ(&EncodeCtx, "Manufacturer", pEngine->Manufacturer);
QCBOREncode_AddInt64ToMapSZ(&EncodeCtx, "NumCylinders", pEngine->uNumCylinders);
QCBOREncode_AddInt64ToMapSZ(&EncodeCtx, "Displacement", pEngine->uDisplacement);
QCBOREncode_AddInt64ToMapSZ(&EncodeCtx, "Horsepower", pEngine->uHorsePower);
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
QCBOREncode_AddDoubleToMapSZ(&EncodeCtx, "DesignedCompression", pEngine->dDesignedCompresion);
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBOREncode_OpenArrayInMapSZ(&EncodeCtx, "Cylinders");
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
for(int64_t i = 0 ; i < pEngine->uNumCylinders; i++) {
QCBOREncode_AddDouble(&EncodeCtx,
pEngine->cylinders[i].dMeasuredCompression);
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBOREncode_CloseArray(&EncodeCtx);
QCBOREncode_AddBoolToMapSZ(&EncodeCtx, "Turbo", pEngine->bTurboCharged);
QCBOREncode_CloseMap(&EncodeCtx);
/* Get the pointer and length of the encoded output. If there was
* any encoding error, it will be returned here */
UsefulBufC EncodedCBOR;
QCBORError uErr;
uErr = QCBOREncode_Finish(&EncodeCtx, &EncodedCBOR);
if(uErr != QCBOR_SUCCESS) {
return NULLUsefulBufC;
} else {
return EncodedCBOR;
}
}
/**
* Error results when decoding an Engine data structure.
*/
typedef enum {
EngineSuccess,
CBORNotWellFormed,
TooManyCylinders,
EngineProtocolerror,
WrongNumberOfCylinders
} EngineDecodeErrors;
/**
* Convert @ref QCBORError to @ref EngineDecodeErrors.
*/
static EngineDecodeErrors ConvertError(QCBORError uErr)
{
EngineDecodeErrors uReturn;
switch(uErr)
{
case QCBOR_SUCCESS:
uReturn = EngineSuccess;
break;
case QCBOR_ERR_HIT_END:
uReturn = CBORNotWellFormed;
break;
default:
uReturn = EngineProtocolerror;
break;
}
return uReturn;
}
/**
* @brief Simplest engine decode using spiffy decode features.
*
* @param[in] EncodedEngine Pointer and length of CBOR-encoded engine.
* @param[out] pE The structure filled in from the decoding.
*
* @return The decode error or success.
*
* This decodes the CBOR into the engine structure.
*
* As QCBOR automatically supports both definite and indefinite maps
* and arrays, this will decode either.
*
* This uses QCBOR's spiffy decode functions, so the implementation is
* simple and closely parallels the encode implementation in
* EncodeEngineDefiniteLength().
*
* Another way to decode without using spiffy decode functions is to
* use QCBORDecode_GetNext() to traverse the whole tree. This
* requires a more complex implementation, but is faster and will pull
* in less code from the CBOR library. The speed advantage is likely
* of consequence when decoding much much larger CBOR on slow small
* CPUs.
*
* A middle way is to use the spiffy decode
* QCBORDecode_GetItemsInMap(). The implementation has middle
* complexity and uses less CPU.
*/
EngineDecodeErrors DecodeEngineSpiffy(UsefulBufC EncodedEngine, CarEngine *pE)
{
QCBORError uErr;
QCBORDecodeContext DecodeCtx;
/* Let QCBORDecode internal error tracking do its work. */
QCBORDecode_Init(&DecodeCtx, EncodedEngine, QCBOR_DECODE_MODE_NORMAL);
QCBORDecode_EnterMap(&DecodeCtx, NULL);
QCBORDecode_GetTextStringInMapSZ(&DecodeCtx, "Manufacturer", &(pE->Manufacturer));
QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Displacement", &(pE->uDisplacement));
QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "Horsepower", &(pE->uHorsePower));
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
QCBORDecode_GetDoubleInMapSZ(&DecodeCtx, "DesignedCompression", &(pE->dDesignedCompresion));
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBORDecode_GetBoolInMapSZ(&DecodeCtx, "Turbo", &(pE->bTurboCharged));
QCBORDecode_GetInt64InMapSZ(&DecodeCtx, "NumCylinders", &(pE->uNumCylinders));
/* Check the internal tracked error now before going on to
* reference any of the decoded data, particularly
* pE->uNumCylinders */
uErr = QCBORDecode_GetError(&DecodeCtx);
if(uErr != QCBOR_SUCCESS) {
goto Done;
}
if(pE->uNumCylinders > MAX_CYLINDERS) {
return TooManyCylinders;
}
QCBORDecode_EnterArrayFromMapSZ(&DecodeCtx, "Cylinders");
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
for(int64_t i = 0; i < pE->uNumCylinders; i++) {
QCBORDecode_GetDouble(&DecodeCtx,
&(pE->cylinders[i].dMeasuredCompression));
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
QCBORDecode_ExitArray(&DecodeCtx);
QCBORDecode_ExitMap(&DecodeCtx);
/* Catch further decoding error here */
uErr = QCBORDecode_Finish(&DecodeCtx);
Done:
return ConvertError(uErr);
}
int32_t RunQCborExample(void)
{
CarEngine InitialEngine;
CarEngine DecodedEngine;
/* For every buffer used by QCBOR a pointer and a length are always
* carried in a UsefulBuf. This is a secure coding and hygene
* practice to help make sure code never runs off the end of a
* buffer.
*
* UsefulBuf structures are passed as a stack parameter to make the
* code prettier. The object code generated isn't much different
* from passing a pointer parameter and a length parameter.
*
* This macro is equivalent to:
* uint8_t __pBufEngineBuffer[300];
* UsefulBuf EngineBuffer = {__pBufEngineBuffer, 300};
*/
UsefulBuf_MAKE_STACK_UB( EngineBuffer, 300);
/* The pointer in UsefulBuf is not const and used for representing
* a buffer to be written to. For UsefulbufC, the pointer is const
* and is used to represent a buffer that has been written to.
*/
UsefulBufC EncodedEngine;
EngineDecodeErrors uErr;
/* Initialize the structure with some values. */
EngineInit(&InitialEngine);
/* Encode the engine structure. */
EncodedEngine = EncodeEngine(&InitialEngine, EngineBuffer);
if(UsefulBuf_IsNULLC(EncodedEngine)) {
printf("Engine encode failed\n");
goto Done;
}
printf("Example: Definite Length Engine Encoded in %zu bytes\n",
EncodedEngine.len);
/* Decode the CBOR */
uErr = DecodeEngineSpiffy(EncodedEngine, &DecodedEngine);
printf("Example: Spiffy Engine Decode Result: %d\n", uErr);
if(uErr) {
goto Done;
}
/* Check the results */
if(!EngineCompare(&InitialEngine, &DecodedEngine)) {
printf("Example: Spiffy Engine Decode comparison fail\n");
}
/* Further example of how to calculate the encoded size, then allocate */
UsefulBufC EncodedEngineSize;
EncodedEngineSize = EncodeEngine(&InitialEngine, SizeCalculateUsefulBuf);
if(UsefulBuf_IsNULLC(EncodedEngine)) {
printf("Engine encode size calculation failed\n");
goto Done;
}
(void)EncodedEngineSize; /* Supress unsed variable warning */
/* Here malloc could be called to allocate a buffer. Then
* EncodeEngine() can be called a second time to actually
* encode. (The actual code is not live here to avoid a
* dependency on malloc()).
* UsefulBuf MallocedBuffer;
* MallocedBuffer.len = EncodedEngineSize.len;
* MallocedBuffer.ptr = malloc(EncodedEngineSize.len);
* EncodedEngine = EncodeEngine(&InitialEngine, MallocedBuffer);
*/
Done:
printf("\n");
return 0;
}

View File

@@ -0,0 +1,20 @@
/*==============================================================================
example.h -- QCBOR encode and decode example
Copyright (c) 2020, Laurence Lundblade. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 6/30/20
=============================================================================*/
#ifndef qcborExample_h
#define qcborExample_h
#include <stdint.h>
int32_t RunQCborExample(void);
#endif /* qcborExample_h */

View File

@@ -0,0 +1 @@
#include "qcbor/UsefulBuf.h"

View File

@@ -0,0 +1 @@
#include "qcbor/qcbor.h"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
Copyright (c) 2018-2020, Laurence Lundblade.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors, nor the name "Laurence Lundblade" may be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=============================================================================*/
/**
* @file qcbor.h
*
* Backwards compatibility for includers of qcbor.h (which has been split
* into four include files).
*/
#include "qcbor_encode.h"
#include "qcbor_decode.h"

View File

@@ -0,0 +1,651 @@
/* ==========================================================================
* qcbor_common -- Common definitions for encding and decoding.
*
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2025, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors, nor the name "Laurence Lundblade" may be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
/* See https://www.securitytheory.com/qcbor-docs/ for the full
* searchable documnetation built from these headers.
*/
#ifndef qcbor_common_h
#define qcbor_common_h
#ifdef __cplusplus
extern "C" {
#if 0
} // Keep editor indention formatting happy
#endif
#endif
/**
* @file qcbor_common.h
*
* qcbor_common.h contains error codes and constant values that are
* common between encoding and decoding.
*/
/**
* Semantic versioning for QCBOR x.y.z from 1.3.0 on
*
* Note:
* - QCBOR 1.2 is indicated by the #define QCBOR_1_2
* - QCBOR 1.1 is indicated by the #define QCBOR_1_1
* - QCBOR 1.0 is indicated by the absence of all the above
*/
#define QCBOR_VERSION_MAJOR 2
#define QCBOR_VERSION_MINOR 0
#define QCBOR_VERSION_PATCH 0
/* This is an alpha (not ready for commercial use) release of 2.0.0 */
/**
* This define indicates a version of QCBOR that supports spiffy
* decode, the decode functions found in qcbor_spiffy_decode.h.
*
* Versions of QCBOR that support spiffy decode are backwards
* compatible with previous versions, but there are a few minor
* exceptions such as some aspects of tag handling that are
* different. This define can be used to handle these variances.
*/
#define QCBOR_SPIFFY_DECODE
/* Standard CBOR Major type for positive integers of various lengths. */
#define CBOR_MAJOR_TYPE_POSITIVE_INT 0
/* Standard CBOR Major type for negative integer of various lengths. */
#define CBOR_MAJOR_TYPE_NEGATIVE_INT 1
/* Standard CBOR Major type for an array of arbitrary 8-bit bytes. */
#define CBOR_MAJOR_TYPE_BYTE_STRING 2
/* Standard CBOR Major type for a UTF-8 string. Note this is true 8-bit UTF8
* with no encoding and no NULL termination. */
#define CBOR_MAJOR_TYPE_TEXT_STRING 3
/* Standard CBOR Major type for an ordered array of other CBOR data items. */
#define CBOR_MAJOR_TYPE_ARRAY 4
/* Standard CBOR Major type for CBOR MAP. Maps an array of pairs. The
* first item in the pair is the "label" (key, name or identfier) and the second
* item is the value. */
#define CBOR_MAJOR_TYPE_MAP 5
/* Standard CBOR major type for a tag number. This creates a CBOR "tag" that
* is the tag number and a data item that follows as the tag content.
*
* Note that this was called an optional tag in RFC 7049, but there's
* not really anything optional about it. It was misleading. It is
* renamed in RFC 8949.
*/
#define CBOR_MAJOR_TYPE_TAG 6
#define CBOR_MAJOR_TYPE_OPTIONAL 6
/* Standard CBOR simple types like float, the values true, false, null... */
#define CBOR_MAJOR_TYPE_SIMPLE 7
/*
* Tags that are used with CBOR_MAJOR_TYPE_OPTIONAL. These
* are types defined in RFC 8949 and some additional ones
* in the IANA CBOR tags registry.
*/
/** See QCBOREncode_AddTDateString(). */
#define CBOR_TAG_DATE_STRING 0
/** See QCBOREncode_AddTDateEpoch(). */
#define CBOR_TAG_DATE_EPOCH 1
/** A postive big number. See QCBOREncode_AddTBigNumber(),
* QCBORDecode_GetTBigNumber() and QCBORDecode_StringsTagCB(). */
#define CBOR_TAG_POS_BIGNUM 2
/** A negative big number. See QCBOREncode_AddTBigNumber(),
* QCBORDecode_GetTBigNumber() and QCBORDecode_StringsTagCB(). */
#define CBOR_TAG_NEG_BIGNUM 3
/** CBOR tag for a two-element array representing a fraction with a
* mantissa and base-10 scaling factor. See
* QCBOREncode_AddTDecimalFraction() and @ref expAndMantissa. */
#define CBOR_TAG_DECIMAL_FRACTION 4
/** CBOR tag for a two-element array representing a fraction with a
* mantissa and base-2 scaling factor. See QCBOREncode_AddTBigFloat()
* and @ref expAndMantissa. */
#define CBOR_TAG_BIGFLOAT 5
/** Not Decoded by QCBOR. Tag for COSE format encryption with no
* recipient identification. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). No API is provided
* for this tag. */
#define CBOR_TAG_COSE_ENCRYPT0 16
#define CBOR_TAG_COSE_ENCRYPTO 16
/** Not Decoded by QCBOR. Tag for COSE format MAC'd data with no
* recipient identification. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). No API is provided for this
* tag. */
#define CBOR_TAG_COSE_MAC0 17
/** Tag for COSE format single signature signing. No API is provided
* for this tag. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). */
#define CBOR_TAG_COSE_SIGN1 18
/** A hint that the following byte string should be encoded in
* Base64URL when converting to JSON or similar text-based
* representations. Call @c
* QCBOREncode_AddTagNumber(pCtx,CBOR_TAG_ENC_AS_B64URL) before the call to
* QCBOREncode_AddBytes(). */
#define CBOR_TAG_ENC_AS_B64URL 21
/** A hint that the following byte string should be encoded in Base64
* when converting to JSON or similar text-based
* representations. Call @c
* QCBOREncode_AddTagNumber(pCtx,CBOR_TAG_ENC_AS_B64) before the call to
* QCBOREncode_AddBytes(). */
#define CBOR_TAG_ENC_AS_B64 22
/** A hint that the following byte string should be encoded in base-16
* format per [RFC 4648]
* (https://www.rfc-editor.org/rfc/rfc4648.html) when converting to
* JSON or similar text-based representations. Essentially, Base-16
* encoding is the standard case- insensitive hex encoding and may be
* referred to as "hex". Call @c QCBOREncode_AddTagNumber(pCtx,CBOR_TAG_ENC_AS_B16)
* before the call to QCBOREncode_AddBytes(). */
#define CBOR_TAG_ENC_AS_B16 23
/** See QCBORDecode_EnterBstrWrapped()). */
#define CBOR_TAG_CBOR 24
/** See QCBOREncode_AddTURI(). */
#define CBOR_TAG_URI 32
/** See QCBOREncode_AddTB64URLText(). */
#define CBOR_TAG_B64URL 33
/** See QCBOREncode_AddB64Text(). */
#define CBOR_TAG_B64 34
/** See QCBOREncode_AddTRegex(). */
#define CBOR_TAG_REGEX 35
/** See QCBOREncode_AddMIMEData(). */
#define CBOR_TAG_MIME 36
/** See QCBOREncode_AddTBinaryUUID(). */
#define CBOR_TAG_BIN_UUID 37
/** The data is a CBOR Web Token per [RFC 8392]
* (https://www.rfc-editor.org/rfc/rfc8392.html). No API is provided for this
* tag. */
#define CBOR_TAG_CWT 61
/** Tag for COSE format encryption. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). No API is provided for this
* tag. */
#define CBOR_TAG_CBOR_SEQUENCE 63
/** Not Decoded by QCBOR. Tag for COSE format encrypt. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). No API is provided for this
* tag. */
#define CBOR_TAG_COSE_ENCRYPT 96
#define CBOR_TAG_ENCRYPT 96
/** Not Decoded by QCBOR. Tag for COSE format MAC. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). No API is provided for this
tag. */
#define CBOR_TAG_COSE_MAC 97
#define CBOR_TAG_MAC 97
/** Not Decoded by QCBOR. Tag for COSE format signed data. See [RFC 9052, COSE]
* (https://www.rfc-editor.org/rfc/rfc9052.html). No API is provided for this
tag. */
#define CBOR_TAG_COSE_SIGN 98
#define CBOR_TAG_SIGN 98
/** Tag for date counted by days from Jan 1 1970 per [RFC 8943]
* (https://www.rfc-editor.org/rfc/rfc8943.html). See
* QCBOREncode_AddTDaysEpoch(). */
#define CBOR_TAG_DAYS_EPOCH 100
/** Not Decoded by QCBOR. World geographic coordinates. See ISO 6709, [RFC 5870]
* (https://www.rfc-editor.org/rfc/rfc5870.html) and WGS-84. No API is
* provided for this tag. */
#define CBOR_TAG_GEO_COORD 103
/** Binary MIME.*/
#define CBOR_TAG_BINARY_MIME 257
/** Tag for date string without time or time zone per [RFC 8943]
* (https://www.rfc-editor.org/rfc/rfc8943.html). See
* QCBOREncode_AddTDaysString(). */
#define CBOR_TAG_DAYS_STRING 1004
/** The magic number, self-described CBOR. No API is provided for this
* tag. */
#define CBOR_TAG_CBOR_MAGIC 55799
/** The 16-bit invalid tag from the CBOR tags registry */
#define CBOR_TAG_INVALID16 0xffff
/** The 32-bit invalid tag from the CBOR tags registry */
#define CBOR_TAG_INVALID32 0xffffffff
/** The 64-bit invalid tag from the CBOR tags registry */
#define CBOR_TAG_INVALID64 0xffffffffffffffff
/** Allows tag content handler installed by QCBORDecode_InstallTagDecoders to match any tag number */
#define CBOR_TAG_ANY (CBOR_TAG_INVALID64 - 1)
/**
* Error codes returned by QCBOR Encoder-Decoder.
*
* They are grouped to keep the code size of
* QCBORDecode_IsNotWellFormedError() and
* QCBORDecode_IsUnrecoverableError() minimal.
*
* 1..19: Encode errors
* 20..: Decode errors
* 20-39: QCBORDecode_IsNotWellFormedError()
* 30..59: QCBORDecode_IsUnrecoverableError()
* 60..: Other decode errors
*
* Error renumbering may occur in the future when new error codes are
* added for new QCBOR features.
*/
typedef enum {
/** The encode or decode completed correctly. */
QCBOR_SUCCESS = 0,
/** The buffer provided for the encoded output when doing encoding
* was too small and the encoded output will not fit. */
QCBOR_ERR_BUFFER_TOO_SMALL = 1,
/** During encoding, an attempt to create simple value between 24
* and 31. */
QCBOR_ERR_ENCODE_UNSUPPORTED = 2,
/** During encoding, the length of the encoded CBOR exceeded
* @ref QCBOR_MAX_SIZE, which is slightly less than
* @c UINT32_MAX. */
QCBOR_ERR_BUFFER_TOO_LARGE = 3,
/** During encoding, the array or map nesting was deeper than this
* implementation can handle. Note that in the interest of code
* size and memory use, QCBOR has a hard limit on array
* nesting. The limit is defined as the constant
* @ref QCBOR_MAX_ARRAY_NESTING. */
QCBOR_ERR_ARRAY_NESTING_TOO_DEEP = 4,
/** During encoding, @c QCBOREncode_CloseXxx() called for a
* different type than is currently open. */
QCBOR_ERR_CLOSE_MISMATCH = 5,
/** During encoding, the array or map had too many items in it. The
* limits are @ref QCBOR_MAX_ITEMS_IN_ARRAY and
* @ref QCBOR_MAX_ITEMS_IN_MAP. */
QCBOR_ERR_ARRAY_TOO_LONG = 6,
/** During encoding, more arrays or maps were closed than
* opened. This is a coding error on the part of the caller of the
* encoder. */
QCBOR_ERR_TOO_MANY_CLOSES = 7,
/** During encoding, the number of array or map opens was not
* matched by the number of closes. Also occurs with opened byte
* strings that are not closed. */
QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN = 8,
/** During encoding, opening a byte string while a byte string is
* open is not allowed. */
QCBOR_ERR_OPEN_BYTE_STRING = 9,
/** Trying to cancel a byte string wrapping after items have been
* added to it. */
QCBOR_ERR_CANNOT_CANCEL = 10,
#define QCBOR_START_OF_NOT_WELL_FORMED_ERRORS 20
/** During decoding, the CBOR is not well-formed because a simple
* value between 0 and 31 is encoded in a two-byte integer rather
* than one. */
QCBOR_ERR_BAD_TYPE_7 = 20,
/** During decoding, returned by QCBORDecode_Finish() if all the
* inputs bytes have not been consumed. This is considered not
* well-formed. */
QCBOR_ERR_EXTRA_BYTES = 21,
/** During decoding, some CBOR construct was encountered that this
* decoder doesn't support, primarily this is the reserved
* additional info values, 28 through 30. The CBOR is not
* well-formed.
*/
QCBOR_ERR_UNSUPPORTED = 22,
/** During decoding, the an array or map was not fully consumed.
* Returned by QCBORDecode_Finish(). The CBOR is not
* well-formed. */
QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED = 23,
/** During decoding, an integer type is encoded with a bad length
* (that of an indefinite length string). The CBOR is not-well
* formed. */
QCBOR_ERR_BAD_INT = 24,
#define QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS 30
/** During decoding, one of the chunks in an indefinite-length
* string is not of the type of the start of the string. The CBOR
* is not well-formed. This error makes no further decoding
* possible. */
QCBOR_ERR_INDEFINITE_STRING_CHUNK = 30,
/** During decoding, hit the end of the given data to decode. For
* example, a byte string of 100 bytes was expected, but the end
* of the input was hit before finding those 100 bytes. Corrupted
* CBOR input will often result in this error. See also
* @ref QCBOR_ERR_NO_MORE_ITEMS. The CBOR is not well-formed.
* This error makes no further decoding possible. */
QCBOR_ERR_HIT_END = 31,
/** During decoding, a break occurred outside an indefinite-length
* item. The CBOR is not well-formed. This error makes no further
* decoding possible. */
QCBOR_ERR_BAD_BREAK = 32,
#define QCBOR_END_OF_NOT_WELL_FORMED_ERRORS 39
/** During decoding, the input is too large. It is greater than
* QCBOR_MAX_SIZE. This is an implementation limit.
* This error makes no further decoding possible. */
QCBOR_ERR_INPUT_TOO_LARGE = 40,
/** During decoding, the array or map nesting was deeper than this
* implementation can handle. Note that in the interest of code
* size and memory use, QCBOR has a hard limit on array
* nesting. The limit is defined as the constant
* @ref QCBOR_MAX_ARRAY_NESTING. This error makes no further
* decoding possible. */
QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP = 41,
/** During decoding, the array or map had too many items in it.
* This limit is @ref QCBOR_MAX_ITEMS_IN_ARRAY (65,534) for
* arrays and @ref QCBOR_MAX_ITEMS_IN_MAP (32,767) for maps. This
* error makes no further decoding possible. */
QCBOR_ERR_ARRAY_DECODE_TOO_LONG = 42,
/** When decoding, a string's size is greater than what a size_t
* can hold less 4. In all but some very strange situations this
* is because of corrupt input CBOR and should be treated as
* such. The strange situation is a CPU with a very small size_t
* (e.g., a 16-bit CPU) and a large string (e.g., > 65KB). This
* error makes no further decoding possible. */
QCBOR_ERR_STRING_TOO_LONG = 43,
/** Something is wrong with a decimal fraction or bigfloat such as
* it not consisting of an array with two integers. This error
* makes no further decoding possible. */
QCBOR_ERR_BAD_EXP_AND_MANTISSA = 44,
/** Unable to decode an indefinite-length string because no string
* allocator was configured. See QCBORDecode_SetMemPool() or
* QCBORDecode_SetUpAllocator(). This error makes no further
* decoding possible.*/
QCBOR_ERR_NO_STRING_ALLOCATOR = 45,
/** Error allocating memory for a string, usually out of memory.
* This primarily occurs decoding indefinite-length strings. This
* error makes no further decoding possible. */
QCBOR_ERR_STRING_ALLOCATE = 46,
/** During decoding, the type of the label for a map entry is not
* one that can be handled in the current decoding mode. Typically
* this is because a label is not an integer or a string. This is
* an implementation limit. */
QCBOR_ERR_MAP_LABEL_TYPE = 47,
/** When the built-in tag decoding encounters an unexpected type,
* this error is returned. This error is unrecoverable because the
* built-in tag decoding doesn't try to consume the unexpected
* type. In previous versions of QCBOR this was considered a
* recoverable error hence QCBOR_ERR_BAD_TAG_CONTENT. Going
* back further, RFC 7049 use the name "optional tags". That name
* is no longer used because "optional" was causing confusion. See
* also @ref QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT. */
QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT = 48,
QCBOR_ERR_BAD_TAG_CONTENT = 48,
QCBOR_ERR_BAD_OPT_TAG = 48,
/** Indefinite length string handling is disabled and there is an
* indefinite length string in the input CBOR. */
QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED = 49,
/** Indefinite length arrays and maps handling are disabled and
* there is an indefinite length map or array in the input
* CBOR. */
QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED = 50,
/** All decoding of tags (major type 6) has been disabled and a tag
* occurred in the decode input. */
QCBOR_ERR_TAGS_DISABLED = 51,
// TODO: maybe reduce number of conformance codes to not use up unrecoverable errors
/** Decoded CBOR is does not conform to preferred serialization. The CBOR head's argument is not
* encoded in shortest form, or indefinite lengths are used. */
QCBOR_ERR_PREFERRED_CONFORMANCE = 52,
/** Decoded CBOR does not conform to CDE. This occurs when a map is not sorted. Other
* CDE issues are reported as QCBOR_ERR_PREFERRED_CONFORMANCE. */
QCBOR_ERR_CDE_CONFORMANCE = 53,
/** Decoded CBOR does not conform to dCBOR. Floating point numbers are not reduced to integers.
* Other issues are reported as either QCBOR_ERR_CDE_CONFORMANCE or QCBOR_ERR_PREFERRED_CONFORMANCE. */
QCBOR_ERR_DCBOR_CONFORMANCE = 54,
/** A map is unsorted and should be for CDE or dCBOR. */
QCBOR_ERR_UNSORTED = 55,
/** Conformance checking requested, preferred serialization disabled, float in the input. */
QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE = 56,
#define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 59
/** More than @ref QCBOR_MAX_TAGS_PER_ITEM tags encountered for a
* CBOR Item. @ref QCBOR_MAX_TAGS_PER_ITEM is a limit of this
* implementation. x */
QCBOR_ERR_TOO_MANY_TAGS = 60,
/** When decoding for a specific type, the type was not expected. */
QCBOR_ERR_UNEXPECTED_TYPE = 61,
/** Duplicate label detected in a map. */
QCBOR_ERR_DUPLICATE_LABEL = 62,
/** During decoding, the buffer given to QCBORDecode_SetMemPool()
* is either too small, smaller than
* @ref QCBOR_DECODE_MIN_MEM_POOL_SIZE or too large, larger than
* UINT32_MAX. */
QCBOR_ERR_MEM_POOL_SIZE = 63,
/** During decoding, an integer smaller than INT64_MIN was received
* (CBOR can represent integers smaller than INT64_MIN, but C
* cannot). */
QCBOR_ERR_INT_OVERFLOW = 64,
/** During decoding, a date greater than +- 292 billion years from
* Jan 1 1970 encountered during parsing. This is an
* implementation limit. */
QCBOR_ERR_DATE_OVERFLOW = 65,
/** During decoding, @c QCBORDecode_ExitXxx() was called for a
* different type than @c QCBORDecode_EnterXxx(). */
QCBOR_ERR_EXIT_MISMATCH = 66,
/** All well-formed data items have been consumed and there are no
* more. If parsing a CBOR stream this indicates the non-error end
* of the stream. If not parsing a CBOR stream/sequence, this
* probably indicates that some data items expected are not
* present. See also @ref QCBOR_ERR_HIT_END. */
QCBOR_ERR_NO_MORE_ITEMS = 67,
/** When finding an item by label, an item with the requested label
* was not found. */
QCBOR_ERR_LABEL_NOT_FOUND = 68,
/** Number conversion failed because of sign. For example a
* negative int64_t can't be converted to a uint64_t */
QCBOR_ERR_NUMBER_SIGN_CONVERSION = 69,
/** When converting a decoded number, the value is too large or too
* small for the conversion target. */
QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW = 70,
/** Trying to get an item by label when a map has not been
* entered. */
QCBOR_ERR_MAP_NOT_ENTERED = 71,
/** A callback indicates processing should not continue for some
* non-CBOR reason. */
QCBOR_ERR_CALLBACK_FAIL = 72,
/** This error code is deprecated. Instead,
* @ref QCBOR_ERR_PREFERRED_FLOAT_DISABLED,
* @ref QCBOR_ERR_HW_FLOAT_DISABLED or @ref QCBOR_ERR_ALL_FLOAT_DISABLED
* is returned depending on the specific floating-point functionality
* that is disabled and the type of floating-point input. */
QCBOR_ERR_FLOAT_DATE_DISABLED = 73,
/** Support for preferred serialization is disabled (QCBOR
* was compiled with QCBOR_DISABLE_PREFERRED_FLOAT). */
QCBOR_ERR_PREFERRED_FLOAT_DISABLED = 74,
QCBOR_ERR_HALF_PRECISION_DISABLED = 74, /* Deprecated */
/** Use of floating-point HW is disabled. This affects all type
* conversions to and from double and float types. */
QCBOR_ERR_HW_FLOAT_DISABLED = 75,
/** Unable to complete operation because a floating-point value
* that is a NaN (not a number), that is too large, too small,
* infinity or -infinity was encountered in encoded CBOR. Usually
* this because conversion of the float-point value was being
* attempted. */
QCBOR_ERR_FLOAT_EXCEPTION = 76,
/** Floating point support is completely turned off,
* encoding/decoding floating point numbers is not possible. */
QCBOR_ERR_ALL_FLOAT_DISABLED = 77,
/** Like @ref QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT, but recoverable.
* If an implementation decodes a tag and can and does consume the
* whole tag contents when it is not the correct tag content, this
* error can be returned. None of the built-in tag decoders do this
* (to save object code). */
QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT = 78,
/** Attempt to output non-preferred, non-CDE or non-dCBOR when not
* allowed by mode. See QCBOREncode_SerializationPreferred(),
* QCBOREncode_SerializationCDE(),
* QCBOREncode_SerializationdCBOR() and @ref QCBOR_ENCODE_CONFIG_DISALLOW_NON_PREFERRED_NUMBERS.
*/
QCBOR_ERR_NOT_PREFERRED = 79,
/** Trying to do something that is not allowed. */
QCBOR_ERR_NOT_ALLOWED = 80,
/** QCBORDecode_EnterBstrWrapped() cannot be used on
* indefinite-length strings because they exist in the memory pool for
* a @ref QCBORStringAllocate. */
QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING = 81,
/** Can't output a negative zero big num */
QCBOR_ERR_NO_NEGATIVE_ZERO = 87,
/** A tag number was not expected such as one is encountered with
* @ref QCBOR_TAG_REQUIREMENT_NOT_A_TAG. */
QCBOR_ERR_UNEXPECTED_TAG_NUMBER = 89,
/** In QCBOR v2, tag numbers must be processed by QCBORDecode_GetNextTagNumber().
* See @ref QCBOR_DECODE_ALLOW_UNPROCESSED_TAG_NUMBERS. */
QCBOR_ERR_UNPROCESSED_TAG_NUMBER = 90,
/** A tag number is expected, but missing. */
QCBOR_ERR_MISSING_TAG_NUMBER = 91,
/** A range of error codes that can be made use of by the
* caller. QCBOR internally does nothing with these except notice
* that they are not QCBOR_SUCCESS. See QCBORDecode_SetError(). */
QCBOR_ERR_FIRST_USER_DEFINED = 128,
/** See @ref QCBOR_ERR_FIRST_USER_DEFINED */
QCBOR_ERR_LAST_USER_DEFINED = 255
/* This is stored in uint8_t; never add values > 255 */
} QCBORError;
/**
* @brief Get string describing an error code.
*
* @param[in] uErr The error code.
*
* @return NULL-terminated string describing error or "Unidentified
* error" if the error is not known.
*
* This is not thread-safe because it uses a static buffer
* for formatting, but this is only a diagnostic and the only
* consequence is the wrong description.
*/
const char *
qcbor_err_to_str(QCBORError uErr);
/* The maximum size in bytes for input to decode or encoder output. */
/* It is slightly less than UINT32_MAX to accommodate
* QCBOR_NON_BOUNDED_OFFSET and so the limit can be tested on 32-bit
* machines. This will cause trouble where size_t is less than 32
* bits.
*/
#define QCBOR_MAX_SIZE (UINT32_MAX - 100)
/**
* The maximum nesting of arrays and maps when encoding or
* decoding. The error @ref QCBOR_ERR_ARRAY_NESTING_TOO_DEEP will be
* returned on encoding or @ref QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP on
* decoding if it is exceeded. Do not increase this over 255.
*/
#define QCBOR_MAX_ARRAY_NESTING 15
/**
* The maximum number of items in a single array when encoding or
* decoding. See also @ref QCBOR_MAX_ITEMS_IN_MAP.
*/
#define QCBOR_MAX_ITEMS_IN_ARRAY (UINT16_MAX-1) /* -1 is because the
* value UINT16_MAX is
* used to indicate
* indefinite-length.
*/
/**
* The maximum number of items in a single map when encoding or
* decoding. See also @ref QCBOR_MAX_ITEMS_IN_ARRAY.
*/
#define QCBOR_MAX_ITEMS_IN_MAP (QCBOR_MAX_ITEMS_IN_ARRAY/2)
#ifdef __cplusplus
}
#endif
#endif /* qcbor_common_h */

View File

@@ -0,0 +1,20 @@
/* ==========================================================================
* qcbor_decode.h -- Backwards compatibility to v1 qcbor_decode.h
*
* Copyright (c) 2024, Laurence Lundblade. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in file named "LICENSE"
*
* Created on 12/1/24.
* ========================================================================== */
#ifndef qcbor_decode_h
#define qcbor_decode_h
#include "qcbor/qcbor_main_decode.h"
#include "qcbor/qcbor_tag_decode.h"
#include "qcbor/qcbor_number_decode.h"
#endif /* qcbor_decode_h */

View File

@@ -0,0 +1,20 @@
/* ==========================================================================
* qcbor_encode.h -- Backwards compatibility to v1 qcbor_encode.h
*
* Copyright (c) 2024, Laurence Lundblade. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in file named "LICENSE"
*
* Created on 12/18/24.
* ========================================================================== */
#ifndef qcbor_encode_h
#define qcbor_encode_h
#include "qcbor/qcbor_main_encode.h"
#include "qcbor/qcbor_number_encode.h"
#include "qcbor/qcbor_tag_encode.h"
#endif /* qcbor_encode_h */

View File

@@ -0,0 +1,432 @@
/* ==========================================================================
* qcbor_private -- Non-public data structures for encding and decoding.
*
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2025, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors, nor the name "Laurence Lundblade" may be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
#ifndef qcbor_private_h
#define qcbor_private_h
#include <stdint.h>
#include "UsefulBuf.h"
#include "qcbor/qcbor_common.h"
#ifdef __cplusplus
extern "C" {
#if 0
} // Keep editor indention formatting happy
#endif
#endif
/* This was originally defined as QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA,
* but this is inconsistent with all the other QCBOR_DISABLE_
* #defines, so the name was changed and this was added for backwards
* compatibility
*/
#ifdef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
#define QCBOR_DISABLE_EXP_AND_MANTISSA
#endif
/* If USEFULBUF_DISABLE_ALL_FLOAT is defined then define
* QCBOR_DISABLE_FLOAT_HW_USE and QCBOR_DISABLE_PREFERRED_FLOAT
*/
#ifdef USEFULBUF_DISABLE_ALL_FLOAT
#ifndef QCBOR_DISABLE_FLOAT_HW_USE
#define QCBOR_DISABLE_FLOAT_HW_USE
#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
#define QCBOR_DISABLE_PREFERRED_FLOAT
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
/*
* Convenience macro for selecting the proper return value in case floating
* point feature(s) are disabled.
*
* The macros:
*
* FLOAT_ERR_CODE_NO_FLOAT(x) Can be used when disabled floating point should
* result error, and all other cases should return
* 'x'.
*
* The below macros always return QCBOR_ERR_ALL_FLOAT_DISABLED when all
* floating point is disabled.
*
* FLOAT_ERR_CODE_NO_PREF_FLOAT(x) Can be used when disabled preferred float
* results in error, and all other cases should
* return 'x'.
* FLOAT_ERR_CODE_NO_FLOAT_HW(x) Can be used when disabled hardware floating
* point results in error, and all other cases
* should return 'x'.
* FLOAT_ERR_CODE_NO_PREF_FLOAT_NO_FLOAT_HW(x) Can be used when either disabled
* preferred float or disabling
* hardware floating point results
* in error, and all other cases
* should return 'x'.
*/
#ifdef USEFULBUF_DISABLE_ALL_FLOAT
#define FLOAT_ERR_CODE_NO_FLOAT(x) QCBOR_ERR_ALL_FLOAT_DISABLED
#define FLOAT_ERR_CODE_NO_PREF_FLOAT(x) QCBOR_ERR_ALL_FLOAT_DISABLED
#define FLOAT_ERR_CODE_NO_FLOAT_HW(x) QCBOR_ERR_ALL_FLOAT_DISABLED
#define FLOAT_ERR_CODE_NO_PREF_FLOAT_NO_FLOAT_HW(x) QCBOR_ERR_ALL_FLOAT_DISABLED
#else /* USEFULBUF_DISABLE_ALL_FLOAT*/
#define FLOAT_ERR_CODE_NO_FLOAT(x) x
#ifdef QCBOR_DISABLE_PREFERRED_FLOAT
#define FLOAT_ERR_CODE_NO_PREF_FLOAT(x) QCBOR_ERR_PREFERRED_FLOAT_DISABLED
#define FLOAT_ERR_CODE_NO_PREF_FLOAT_NO_FLOAT_HW(x) QCBOR_ERR_PREFERRED_FLOAT_DISABLED
#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
#define FLOAT_ERR_CODE_NO_PREF_FLOAT(x) x
#ifdef QCBOR_DISABLE_FLOAT_HW_USE
#define FLOAT_ERR_CODE_NO_PREF_FLOAT_NO_FLOAT_HW(x) QCBOR_ERR_HW_FLOAT_DISABLED
#else
#define FLOAT_ERR_CODE_NO_PREF_FLOAT_NO_FLOAT_HW(x) x
#endif
#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
#ifdef QCBOR_DISABLE_FLOAT_HW_USE
#define FLOAT_ERR_CODE_NO_FLOAT_HW(x) QCBOR_ERR_HW_FLOAT_DISABLED
#else /* ! QCBOR_DISABLE_FLOAT_HW_USE */
#define FLOAT_ERR_CODE_NO_FLOAT_HW(x) x
#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT*/
/*
* These are special values for the AdditionalInfo bits that are part of
* the first byte. Mostly they encode the length of the data item.
*/
#define LEN_IS_ONE_BYTE 24
#define LEN_IS_TWO_BYTES 25
#define LEN_IS_FOUR_BYTES 26
#define LEN_IS_EIGHT_BYTES 27
#define ADDINFO_RESERVED1 28
#define ADDINFO_RESERVED2 29
#define ADDINFO_RESERVED3 30
#define LEN_IS_INDEFINITE 31
/*
* 24 is a special number for CBOR. Integers and lengths
* less than it are encoded in the same byte as the major type.
*/
#define CBOR_TWENTY_FOUR 24
/*
* Values for the 5 bits for items of major type 7
*/
#define CBOR_SIMPLEV_FALSE 20
#define CBOR_SIMPLEV_TRUE 21
#define CBOR_SIMPLEV_NULL 22
#define CBOR_SIMPLEV_UNDEF 23
#define CBOR_SIMPLEV_ONEBYTE 24
#define HALF_PREC_FLOAT 25
#define SINGLE_PREC_FLOAT 26
#define DOUBLE_PREC_FLOAT 27
#define CBOR_SIMPLE_BREAK 31
#define CBOR_SIMPLEV_RESERVED_START CBOR_SIMPLEV_ONEBYTE
#define CBOR_SIMPLEV_RESERVED_END CBOR_SIMPLE_BREAK
/* The number of tags that are 16-bit or larger that can be handled
* in a decode.
*/
#define QCBOR_NUM_MAPPED_TAGS 4
/* The number of tags (of any size) recorded for an individual item. */
#define QCBOR_MAX_TAGS_PER_ITEM1 4
/*
* PRIVATE DATA STRUCTURE
*
* Holds the data for tracking array and map nesting during
* encoding. Pairs up with the Nesting_xxx functions to make an
* "object" to handle nesting encoding.
*
* uStart is a uint32_t instead of a size_t to keep the size of this
* struct down so it can be on the stack without any concern. It
* would be about double if size_t was used instead.
*
* Size approximation (varies with CPU/compiler):
* 64-bit machine: (15 + 1) * (4 + 2 + 1 + 1 pad) + 8 = 136 bytes
* 32-bit machine: (15 + 1) * (4 + 2 + 1 + 1 pad) + 4 = 132 bytes
*/
typedef struct __QCBORTrackNesting {
/* PRIVATE DATA STRUCTURE */
struct {
/* See QCBOREncode_OpenMapOrArray() for details on how this works */
uint32_t uStart; /* uStart is the position where the array starts */
uint16_t uCount; /* Number of items in the arrary or map; counts items
* in a map, not pairs of items */
uint8_t uMajorType; /* Indicates if item is a map or an array */
} pArrays[QCBOR_MAX_ARRAY_NESTING+1], /* stored state for nesting levels */
*pCurrentNesting; /* the current nesting level */
} QCBORTrackNesting;
/*
* PRIVATE DATA STRUCTURE
*
* Context / data object for encoding some CBOR. Used by all encode
* functions to form a public "object" that does the job of encdoing.
*
* Size approximation (varies with CPU/compiler):
* 64-bit machine: 27 + 1 (+ 4 padding) + 136 = 32 + 136 = 168 bytes
* 32-bit machine: 15 + 1 + 132 = 148 bytes
*/
typedef struct _QCBOREncodeContext QCBORPrivateEncodeContext;
struct _QCBOREncodeContext {
/* PRIVATE DATA STRUCTURE */
UsefulOutBuf OutBuf; /* Pointer to output buffer, its length and
* position in it. */
uint8_t uError; /* Error state, always from QCBORError enum */
int uConfigFlags; /* enum QCBOREncodeConfig */
void (*pfnCloseMap)(QCBORPrivateEncodeContext *); /* Use of function
* pointer explained in QCBOREncode_Config() */
QCBORTrackNesting nesting; /* Keep track of array and map nesting */
};
/*
* PRIVATE DATA STRUCTURE
*
* Holds the data for array and map nesting for decoding work. This
* structure and the DecodeNesting_Xxx() functions in qcbor_decode.c
* form an "object" that does the work for arrays and maps. All access
* to this structure is through DecodeNesting_Xxx() functions.
*
* 64-bit machine size
* 128 = 16 * 8 for the two unions
* 64 = 16 * 4 for the uLevelType, 1 byte padded to 4 bytes for alignment
* 16 = 16 bytes for two pointers
* 208 TOTAL
*
* 32-bit machine size is 200 bytes
*/
typedef struct __QCBORDecodeNesting {
/* PRIVATE DATA STRUCTURE */
struct nesting_decode_level {
/*
* This keeps tracking info for each nesting level. There are two
* main types of levels:
* 1) Byte count tracking. This is for the top level input CBOR
* which might be a single item or a CBOR sequence and byte
* string wrapped encoded CBOR.
* 2) Item count tracking. This is for maps and arrays.
*
* uLevelType has value QCBOR_TYPE_BYTE_STRING for 1) and
* QCBOR_TYPE_MAP or QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP_AS_ARRAY
* for 2).
*
* Item count tracking is either for definite or indefinite-length
* maps/arrays. For definite lengths, the total count and items
* unconsumed are tracked. For indefinite-length, uTotalCount is
* QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH (UINT16_MAX) and
* there is no per-item count of members. For indefinite-length
* maps and arrays, uCountCursor is UINT16_MAX if not consumed
* and zero if it is consumed in the pre-order
* traversal. Additionally, if entered in bounded mode,
* uCountCursor is QCBOR_COUNT_INDICATES_ZERO_LENGTH to indicate
* it is empty.
*
* This also records whether a level is bounded or not. All
* byte-count tracked levels (the top-level sequence and
* bstr-wrapped CBOR) are bounded implicitly. Maps and arrays
* may or may not be bounded. They are bounded if they were
* Entered() and not if they were traversed with GetNext(). They
* are marked as bounded by uStartOffset not being @c UINT32_MAX.
*/
/*
* If uLevelType can put in a separately indexed array, the
* union/struct will be 8 bytes rather than 9 and a lot of
* wasted padding for alignment will be saved.
*/
uint8_t uLevelType;
union {
struct {
#define QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH UINT16_MAX
#define QCBOR_COUNT_INDICATES_ZERO_LENGTH UINT16_MAX-1
uint16_t uCountTotal;
uint16_t uCountCursor;
#define QCBOR_NON_BOUNDED_OFFSET UINT32_MAX
/* The start of the array or map in bounded mode so
* the input can be rewound for GetInMapXx() by label. */
uint32_t uStartOffset;
} ma; /* for maps and arrays */
struct {
/* The end of the input before the bstr was entered so that
* it can be restored when the bstr is exited. */
uint32_t uSavedEndOffset;
/* The beginning of the bstr so that it can be rewound. */
uint32_t uBstrStartOffset;
} bs; /* for top-level sequence and bstr-wrapped CBOR */
} u;
} pLevels[QCBOR_MAX_ARRAY_NESTING+1],
*pCurrent,
*pCurrentBounded;
/*
* pCurrent is for item-by-item pre-order traversal.
*
* pCurrentBounded points to the current bounding level or is NULL
* if there isn't one.
*
* pCurrent must always be below pCurrentBounded as the pre-order
* traversal is always bounded by the bounding level.
*
* When a bounded level is entered, the pre-order traversal is set
* to the first item in the bounded level. When a bounded level is
* exited, the pre-order traversl is set to the next item after the
* map, array or bstr. This may be more than one level up, or even
* the end of the input CBOR.
*/
} QCBORDecodeNesting;
typedef struct {
/* PRIVATE DATA STRUCTURE */
void *pAllocateCxt;
UsefulBuf (* pfAllocator)(void *pAllocateCxt, void *pOldMem, size_t uNewSize);
} QCBORInternalAllocator;
/* Private data structure for mapped tag numbers. The 0th entry
* is the one first in the traversal and furthest from the tag content.*/
typedef uint16_t QCBORMappedTagNumbers[QCBOR_MAX_TAGS_PER_ITEM1];
/*
* PRIVATE DATA STRUCTURE
*
* The decode context. This data structure plus the public
* QCBORDecode_xxx functions form an "object" that does CBOR decoding.
*
* Size approximation (varies with CPU/compiler):
* 64-bit machine: 32 + 1 + 1 + 6 bytes padding + 72 + 16 + 8 + 8 = 144 bytes
* 32-bit machine: 16 + 1 + 1 + 2 bytes padding + 68 + 8 + 8 + 4 = 108 bytes
*/
struct _QCBORDecodeContext {
/* PRIVATE DATA STRUCTURE */
UsefulInputBuf InBuf;
QCBORDecodeNesting nesting;
/* If a string allocator is configured for indefinite-length
* strings, it is configured here.
*/
QCBORInternalAllocator StringAllocator;
/* These are special for the internal MemPool allocator. They are
* not used otherwise. We tried packing these in the MemPool
* itself, but there are issues with memory alignment.
*/
uint32_t uMemPoolSize;
uint32_t uMemPoolFreeOffset;
/* A cached offset to the end of the current map 0 if no value is
* cached.
*/
#define QCBOR_MAP_OFFSET_CACHE_INVALID UINT32_MAX
uint32_t uMapEndOffsetCache;
uint32_t uDecodeMode;
uint8_t bStringAllocateAll;
uint8_t uLastError; /* QCBORError stuffed into a uint8_t */
uint8_t bAllowAllLabels; /* Used internally only, not external yet */
/* See QCBORDecode_Private_MapTagNumber() for how tags are mapped. */
// TODO: can this be removed if tags are disabled?
uint64_t auMappedTagNumbers[QCBOR_NUM_MAPPED_TAGS];
QCBORMappedTagNumbers auLastTagNumbers;
const struct QCBORTagDecoderEntry *pTagDecoderTable;
void *pTagDecodersContext;
/* Tag number cursor. See QCBORDecode_Private_TagNumberCursor(). */
size_t uTagNumberCheckOffset;
uint8_t uTagNumberIndex;
#define QCBOR_ALL_TAGS_PROCESSED UINT8_MAX
};
/* Used internally in the impementation here Must not conflict with
* any of the official CBOR types
*/
#define CBOR_MAJOR_NONE_TAG_LABEL_REORDER 10
#define CBOR_MAJOR_NONE_TYPE_OPEN_BSTR 12
/* Add this to types to indicate they are to be encoded as indefinite lengths */
#define QCBOR_INDEFINITE_LEN_TYPE_MODIFIER 0x80
#define CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN \
CBOR_MAJOR_TYPE_ARRAY + QCBOR_INDEFINITE_LEN_TYPE_MODIFIER
#define CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN \
CBOR_MAJOR_TYPE_MAP + QCBOR_INDEFINITE_LEN_TYPE_MODIFIER
#define CBOR_MAJOR_NONE_TYPE_SIMPLE_BREAK \
CBOR_MAJOR_TYPE_SIMPLE + QCBOR_INDEFINITE_LEN_TYPE_MODIFIER
/* Value of QCBORItem.val.string.len when the string length is
* indefinite. Used temporarily in the implementation and never
* returned in the public interface.
*/
#define QCBOR_STRING_LENGTH_INDEFINITE SIZE_MAX
/* The number of elements in a C array of a particular type */
#define C_ARRAY_COUNT(array, type) (sizeof(array)/sizeof(type))
#define ABSOLUTE_VALUE(x) ((x) < 0 ? -(x) : (x))
#ifdef __cplusplus
}
#endif
#endif /* qcbor_private_h */

View File

@@ -0,0 +1,61 @@
# Guidelines from https://docs.fedoraproject.org/en-US/packaging-guidelines/CMake/
Name: qcbor
Version: 2.0.0.a4
Release: 0%{?dist}
Summary: A CBOR encoder/decoder library
URL: https://github.com/laurencelundblade/QCBOR
License: BSD-3-Clause
Source0: %{URL}/archive/refs/tags/v2.0.tar.gz
BuildRequires: cmake
BuildRequires: gcc
%description
Comprehensive, powerful, commercial-quality CBOR encoder and decoder
that is still suited for small devices.
%package devel
Summary: Development files for the QCBOR library
Requires: %{name}%{?_isa} = %{version}
%description devel
Development files needed to build and link to the QCBOR library.
%prep
%setup -q -n QCBOR-2.0
%cmake -DBUILD_QCBOR_TEST=APP
%build
%cmake_build
%install
%cmake_install
%check
# TODO use %ctest when supported by QCBOR config
./%{_vpath_builddir}/test/qcbortest
%files
%license LICENSE
%doc README.md
%{_libdir}/*.so.*
%files devel
%license LICENSE
%doc README.md
%{_includedir}/qcbor
%{_libdir}/*.so
%changelog
* Sep 21 2025 Laurence Lundblade <lgl@island-resort.com> - 2.0.0.a4
- QCBOR 2.0 alpha release 4. Big tag decoding rework. Not ready for commercial use.
* Tue Deb 11 2025 Laurence Lundblade <lgl@island-resort.com> - 2.0.0.a3
- QCBOR 2.0 alpha release 3. GetString error handling fix. Documentation fixes. Not ready for commercial use.
* Fri Dec 20 2024 Laurence Lundblade <lgl@island-resort.com> - 2.0.0.a1
- QCBOR 2.0 alpha release 1. Not ready for commercial use.

View File

@@ -0,0 +1,673 @@
/*==============================================================================
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2025, Laurence Lundblade.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors, nor the name "Laurence Lundblade" may be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
/*=============================================================================
FILE: UsefulBuf.c
DESCRIPTION: General purpose input and output buffers
EDIT HISTORY FOR FILE:
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
when who what, where, why
-------- ---- ---------------------------------------------------
02/21/2025 llundblade Improve magic number to detect lack of initialization.
02/21/2025 llundblade Bug fixes to UsefulOutBuf_Compare().
02/21/2025 llundblade Rename to UsefulOutBuf_OutSubString().
08/08/2024 llundblade Add UsefulOutBuf_SubString().
21/05/2024 llundblade Comment formatting and some code tidiness.
1/7/2024 llundblade Add UsefulInputBuf_Compare().
28/02/2022 llundblade Rearrange UsefulOutBuf_Compare().
19/11/2023 llundblade Add UsefulOutBuf_GetOutput().
19/11/2023 llundblade Add UsefulOutBuf_Swap().
19/11/2023 llundblade Add UsefulOutBuf_Compare().
19/12/2022 llundblade Don't pass NULL to memmove when adding empty data.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf
3/6/2021 mcr/llundblade Fix warnings related to --Wcast-qual
01/28/2020 llundblade Refine integer signedness to quiet static analysis.
01/08/2020 llundblade Documentation corrections & improved code formatting.
11/08/2019 llundblade Re check pointer math and update comments
3/6/2019 llundblade Add UsefulBuf_IsValue()
09/07/17 llundbla Fix critical bug in UsefulBuf_Find() -- a read off
the end of memory when the bytes to find is longer
than the bytes to search.
06/27/17 llundbla Fix UsefulBuf_Compare() bug. Only affected comparison
for < or > for unequal length buffers. Added
UsefulBuf_Set() function.
05/30/17 llundbla Functions for NULL UsefulBufs and const / unconst
11/13/16 llundbla Initial Version.
============================================================================*/
#include "UsefulBuf.h"
/* used to catch use of uninitialized or corrupted UsefulOutBuf */
#define USEFUL_OUT_BUF_MAGIC (0x0B0F)
/*
* Public function -- see UsefulBuf.h
*/
UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src)
{
/* Do this with subtraction so it doesn't give an erroneous
* result if uOffset + Src.len overflows. Right side is equivalent to
* uOffset + Src.len > Dest.len
*/
if(uOffset > Dest.len || Src.len > Dest.len - uOffset) {
return NULLUsefulBufC;
}
memcpy((uint8_t *)Dest.ptr + uOffset, Src.ptr, Src.len);
return (UsefulBufC){Dest.ptr, Src.len + uOffset};
}
/*
* Public function -- see UsefulBuf.h
*/
int UsefulBuf_Compare(const UsefulBufC UB1, const UsefulBufC UB2)
{
/* Use comparisons rather than subtracting lengths to
* return an int instead of a size_t
*/
if(UB1.len < UB2.len) {
return -1;
} else if (UB1.len > UB2.len) {
return 1;
} /* else UB1.len == UB2.len */
return memcmp(UB1.ptr, UB2.ptr, UB1.len);
}
/*
* Public function -- see UsefulBuf.h
*/
size_t UsefulBuf_IsValue(const UsefulBufC UB, uint8_t uValue)
{
if(UsefulBuf_IsNULLOrEmptyC(UB)) {
/* Not a match */
return 0;
}
const uint8_t * const pEnd = (const uint8_t *)UB.ptr + UB.len;
for(const uint8_t *p = UB.ptr; p < pEnd; p++) {
if(*p != uValue) {
/* Byte didn't match */
/* Cast from signed to unsigned. Safe because the loop increments.*/
return (size_t)(p - (const uint8_t *)UB.ptr);
}
}
/* Success. All bytes matched */
return SIZE_MAX;
}
/*
* Public function -- see UsefulBuf.h
*/
size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind)
{
if(BytesToSearch.len < BytesToFind.len) {
return SIZE_MAX;
}
for(size_t uPos = 0; uPos <= BytesToSearch.len - BytesToFind.len; uPos++) {
UsefulBufC SearchNext;
SearchNext.ptr = ((const uint8_t *)BytesToSearch.ptr) + uPos;
SearchNext.len = BytesToFind.len;
if(!UsefulBuf_Compare(SearchNext, BytesToFind)) {
return uPos;
}
}
return SIZE_MAX;
}
/*
* Public function -- see UsefulBuf.h
*/
UsefulBufC
UsefulBuf_SkipLeading(UsefulBufC String, uint8_t uByte)
{
for(;String.len; String.len--) {
if(*(const uint8_t *)String.ptr != uByte) {
break;
}
String.ptr = (const uint8_t *)String.ptr + 1;
}
return String;
}
/*
* Public function -- see UsefulBuf.h
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
void UsefulOutBuf_Init(UsefulOutBuf *pMe, UsefulBuf Storage)
{
pMe->magic = USEFUL_OUT_BUF_MAGIC;
UsefulOutBuf_Reset(pMe);
pMe->UB = Storage;
#if 0
/* This check is off by default.
*
* The following check fails on ThreadX
*
* Sanity check on the pointer and size to be sure we are not
* passed a buffer that goes off the end of the address space.
* Given this test, we know that all unsigned lengths less than
* me->size are valid and won't wrap in any pointer additions
* based off of pStorage in the rest of this code.
*/
const uintptr_t ptrM = UINTPTR_MAX - Storage.len;
if(Storage.ptr && (uintptr_t)Storage.ptr > ptrM) /* Check #0 */
me->err = 1;
#endif
}
/*
* Public function -- see UsefulBuf.h
*
* The core of UsefulOutBuf -- put some bytes in the buffer without writing off
* the end of it.
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*
* This function inserts the source buffer, NewData, into the destination
* buffer, me->UB.ptr.
*
* Destination is represented as:
* me->UB.ptr -- start of the buffer
* me->UB.len -- size of the buffer UB.ptr
* me->data_len -- length of value data in UB
*
* Source is data:
* NewData.ptr -- start of source buffer
* NewData.len -- length of source buffer
*
* Insertion point:
* uInsertionPos.
*
* Steps:
*
* 0. Corruption checks on UsefulOutBuf
*
* 1. Figure out if the new data will fit or not
*
* 2. Is insertion position in the range of valid data?
*
* 3. If insertion point is not at the end, slide data to the right of the
* insertion point to the right
*
* 4. Put the new data in at the insertion position.
*
*/
void UsefulOutBuf_InsertUsefulBuf(UsefulOutBuf *pMe, UsefulBufC NewData, size_t uInsertionPos)
{
/* 0. Sanity check the UsefulOutBuf structure
* A "counter measure". If magic number is not the right number it
* probably means pMe was not initialized or it was corrupted. Attackers
* can defeat this, but it is a hurdle and does good with very
* little code.
*/
if(pMe->magic != USEFUL_OUT_BUF_MAGIC) {
pMe->err = 1;
return; /* Magic number is wrong due to uninitalization or corrption */
}
if(pMe->err) {
/* Already in error state. */
return;
}
/* Make sure valid data is less than buffer size. This would only occur
* if there was corruption of me, but it is also part of the checks to
* be sure there is no pointer arithmatic under/overflow.
*/
if(pMe->data_len > pMe->UB.len) { /* Check #1 */
pMe->err = 1;
/* Offset of valid data is off the end of the UsefulOutBuf due to
* uninitialization or corruption
*/
return;
}
/* 1. Will it fit?
* WillItFit() is the same as: NewData.len <= (me->UB.len - me->data_len)
* Check #1 makes sure subtraction in RoomLeft will not wrap around
*/
if(! UsefulOutBuf_WillItFit(pMe, NewData.len)) { /* Check #2 */
/* The new data will not fit into the the buffer. */
pMe->err = 1;
return;
}
/* 2. Check the Insertion Position
* This, with Check #1, also confirms that uInsertionPos <= me->data_len and
* that uInsertionPos + pMe->UB.ptr will not wrap around the end of the
* address space.
*/
if(uInsertionPos > pMe->data_len) { /* Check #3 */
/* Off the end of the valid data in the buffer. */
pMe->err = 1;
return;
}
/* 3. Slide existing data to the right */
if (!UsefulOutBuf_IsBufferNULL(pMe)) {
uint8_t *pSourceOfMove = ((uint8_t *)pMe->UB.ptr) + uInsertionPos; /* PtrMath #1 */
size_t uNumBytesToMove = pMe->data_len - uInsertionPos; /* PtrMath #2 */
uint8_t *pDestinationOfMove = pSourceOfMove + NewData.len; /* PtrMath #3*/
/* To know memmove won't go off end of destination, see PtrMath #4.
* Use memove because it handles overlapping buffers
*/
memmove(pDestinationOfMove, pSourceOfMove, uNumBytesToMove);
/* 4. Put the new data in */
uint8_t *pInsertionPoint = pSourceOfMove;
/* To know memmove won't go off end of destination, see PtrMath #5 */
if(NewData.ptr != NULL) {
memmove(pInsertionPoint, NewData.ptr, NewData.len);
}
}
pMe->data_len += NewData.len;
}
/*
* Rationale that describes why the above pointer math is safe
*
* PtrMath #1 will never wrap around over because
* Check #0 in UsefulOutBuf_Init that me->UB.ptr + me->UB.len doesn't wrap
* Check #1 makes sure me->data_len is less than me->UB.len
* Check #3 makes sure uInsertionPos is less than me->data_len
*
* PtrMath #2 will never wrap around under because
* Check #3 makes sure uInsertionPos is less than me->data_len
*
* PtrMath #3 will never wrap around over because
* PtrMath #1 is checked resulting in pSourceOfMove being between me->UB.ptr and me->UB.ptr + me->data_len
* Check #2 that NewData.len will fit in the unused space left in me->UB
*
* PtrMath #4 will never wrap under because
* Calculation for extent or memmove is uRoomInDestination = me->UB.len - (uInsertionPos + NewData.len)
* Check #3 makes sure uInsertionPos is less than me->data_len
* Check #3 allows Check #2 to be refactored as NewData.Len > (me->size - uInsertionPos)
* This algebraically rearranges to me->size > uInsertionPos + NewData.len
*
* PtrMath #5 will never wrap under because
* Calculation for extent of memove is uRoomInDestination = me->UB.len - uInsertionPos;
* Check #1 makes sure me->data_len is less than me->size
* Check #3 makes sure uInsertionPos is less than me->data_len
*/
/*
* Public function for advancing data length. See qcbor/UsefulBuf.h
*/
void UsefulOutBuf_Advance(UsefulOutBuf *pMe, size_t uAmount)
{
/* This function is a trimmed down version of
* UsefulOutBuf_InsertUsefulBuf(). This could be combined with the
* code in UsefulOutBuf_InsertUsefulBuf(), but that would make
* UsefulOutBuf_InsertUsefulBuf() bigger and this will be very
* rarely used.
*/
/* 0. Sanity check the UsefulOutBuf structure
*
* A "counter measure". If magic number is not the right number it
* probably means me was not initialized or it was
* corrupted. Attackers can defeat this, but it is a hurdle and
* does good with very little code.
*/
if(pMe->magic != USEFUL_OUT_BUF_MAGIC) {
pMe->err = 1;
return; /* Magic number is wrong due to uninitalization or corrption */
}
if(pMe->err) {
/* Already in error state. */
return;
}
/* Make sure valid data is less than buffer size. This would only
* occur if there was corruption of me, but it is also part of the
* checks to be sure there is no pointer arithmatic
* under/overflow.
*/
if(pMe->data_len > pMe->UB.len) { /* Check #1 */
pMe->err = 1;
/* Offset of valid data is off the end of the UsefulOutBuf due
* to uninitialization or corruption.
*/
return;
}
/* 1. Will it fit?
*
* WillItFit() is the same as: NewData.len <= (me->UB.len -
* me->data_len) Check #1 makes sure subtraction in RoomLeft will
* not wrap around
*/
if(! UsefulOutBuf_WillItFit(pMe, uAmount)) { /* Check #2 */
/* The new data will not fit into the the buffer. */
pMe->err = 1;
return;
}
pMe->data_len += uAmount;
}
/*
* Public function -- see UsefulBuf.h
*/
UsefulBufC UsefulOutBuf_OutUBuf(UsefulOutBuf *pMe)
{
if(pMe->magic != USEFUL_OUT_BUF_MAGIC) {
pMe->err = 1;
return NULLUsefulBufC;
}
if(pMe->err) {
return NULLUsefulBufC;
}
return (UsefulBufC){pMe->UB.ptr, pMe->data_len};
}
/*
* Public function -- see UsefulBuf.h
*
* Copy out the data accumulated in to the output buffer.
*/
UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *pMe, UsefulBuf pDest)
{
const UsefulBufC Tmp = UsefulOutBuf_OutUBuf(pMe);
if(UsefulBuf_IsNULLC(Tmp)) {
return NULLUsefulBufC;
}
return UsefulBuf_Copy(pDest, Tmp);
}
/*
* Public function -- see UsefulBuf.h
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
UsefulBufC UsefulOutBuf_OutSubString(UsefulOutBuf *pMe,
const size_t uStart,
const size_t uLen)
{
const UsefulBufC Tmp = UsefulOutBuf_OutUBuf(pMe);
if(UsefulBuf_IsNULLC(Tmp)) {
return NULLUsefulBufC;
}
if(uStart > Tmp.len) {
return NULLUsefulBufC;
}
if(Tmp.len - uStart < uLen) {
return NULLUsefulBufC;
}
UsefulBufC SubString;
SubString.ptr = (const uint8_t *)Tmp.ptr + uStart;
SubString.len = uLen;
return SubString;
}
/*
* Public function -- see UsefulBuf.h
*
* The core of UsefulInputBuf -- consume bytes without going off end of buffer.
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
const void * UsefulInputBuf_GetBytes(UsefulInputBuf *pMe, size_t uAmount)
{
/* Already in error state. Do nothing. */
if(pMe->err) {
return NULL;
}
if(!UsefulInputBuf_BytesAvailable(pMe, uAmount)) {
/* Number of bytes asked for is more than available */
pMe->err = 1;
return NULL;
}
/* This is going to succeed */
const void * const result = ((const uint8_t *)pMe->UB.ptr) + pMe->cursor;
/* Won't overflow because of check using UsefulInputBuf_BytesAvailable() */
pMe->cursor += uAmount;
return result;
}
/*
* Public function -- see UsefulBuf.h
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
int
UsefulInputBuf_Compare(UsefulInputBuf *pUInBuf,
const size_t uOffset1,
const size_t uLen1,
const size_t uOffset2,
const size_t uLen2)
{
UsefulBufC UB1;
UsefulBufC UB2;
const size_t uInputSize = UsefulInputBuf_GetBufferLength(pUInBuf);
/* Careful length check that works even if uLen1 + uOffset1 > SIZE_MAX */
if(uOffset1 > uInputSize || uLen1 > uInputSize - uOffset1) {
return 1;
}
UB1.ptr = (const uint8_t *)pUInBuf->UB.ptr + uOffset1;
UB1.len = uLen1;
/* Careful length check that works even if uLen2 + uOffset2 > SIZE_MAX */
if(uOffset2 > uInputSize || uLen2 > uInputSize - uOffset2) {
return -1;
}
UB2.ptr = (const uint8_t *)pUInBuf->UB.ptr + uOffset2;
UB2.len = uLen2;
return UsefulBuf_Compare(UB1, UB2);
}
/*
* Public function -- see UsefulBuf.h
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
int UsefulOutBuf_Compare(UsefulOutBuf *pMe,
const size_t uStart1, const size_t uLen1,
const size_t uStart2, const size_t uLen2)
{
const uint8_t *pBase;
const uint8_t *pEnd;
const uint8_t *p1;
const uint8_t *p2;
const uint8_t *p1Start;
const uint8_t *p2Start;
const uint8_t *p1End;
const uint8_t *p2End;
int uComparison;
size_t uComparedLen1;
size_t uComparedLen2;
pBase = pMe->UB.ptr;
pEnd = (const uint8_t *)pBase + pMe->data_len;
p1Start = pBase + uStart1;
p2Start = pBase + uStart2;
p1End = p1Start + uLen1;
p2End = p2Start + uLen2;
uComparison = 0;
for(p1 = p1Start, p2 = p2Start;
p1 < pEnd && p2 < pEnd && p1 < p1End && p2 < p2End;
p1++, p2++) {
uComparison = *p2 - *p1;
if(uComparison != 0) {
break;
}
}
/* Loop might have terminated because strings were off
* the end of the buffer. Compute actual lengths compared.
*/
uComparedLen1 = uLen1;
if(p1 >= pEnd) {
uComparedLen1 = (size_t)(p1 - p1Start);
}
uComparedLen2 = uLen2;
if(p2 >= pEnd) {
uComparedLen2 = (size_t)(p2 - p2Start);
}
if(uComparison == 0) {
/* All bytes were equal, now check the lengths */
if(uComparedLen2 > uComparedLen1) {
/* string 1 is a substring of string 2 */
uComparison = 1;
} else if(uComparedLen1 > uComparedLen2) {
/* string 2 is a substring of string 1 */
uComparison = -1;
} else {
/* do nothing, uComparison already is 0 */
}
}
return uComparison;
}
/**
* @brief Reverse order of bytes in a buffer.
*
* This reverses bytes starting at pStart, up to, but not including
* the byte at pEnd
*/
static void
UsefulOutBuf_Private_ReverseBytes(uint8_t *pStart, uint8_t *pEnd)
{
uint8_t uTmp;
while(pStart < pEnd) {
pEnd--;
uTmp = *pStart;
*pStart = *pEnd;
*pEnd = uTmp;
pStart++;
}
}
/*
* Public function -- see UsefulBuf.h
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
void UsefulOutBuf_Swap(UsefulOutBuf *pMe, size_t uStartOffset, size_t uPivotOffset, size_t uEndOffset)
{
uint8_t *pBase;
if(uStartOffset > pMe->data_len || uPivotOffset > pMe->data_len || uEndOffset > pMe->data_len) {
return;
}
if(uStartOffset > uPivotOffset || uStartOffset > uEndOffset || uPivotOffset > uEndOffset) {
return;
}
/* This is the "reverse" algorithm to swap two memory regions */
pBase = pMe->UB.ptr;
UsefulOutBuf_Private_ReverseBytes(pBase + uStartOffset, pBase + uPivotOffset);
UsefulOutBuf_Private_ReverseBytes(pBase + uPivotOffset, pBase + uEndOffset);
UsefulOutBuf_Private_ReverseBytes(pBase + uStartOffset, pBase + uEndOffset);
}
/*
* Public function -- see UsefulBuf.h
*/
UsefulBufC
UsefulOutBuf_OutUBufOffset(UsefulOutBuf *pMe, size_t uOffset)
{
UsefulBufC ReturnValue;
ReturnValue = UsefulOutBuf_OutUBuf(pMe);
if(UsefulBuf_IsNULLC(ReturnValue)) {
return NULLUsefulBufC;
}
if(uOffset >= ReturnValue.len) {
return NULLUsefulBufC;
}
ReturnValue.ptr = (const uint8_t *)ReturnValue.ptr + uOffset;
ReturnValue.len -= uOffset;
return ReturnValue;
}

View File

@@ -0,0 +1,419 @@
/* ==========================================================================
* decode_nesting.c -- All inline implementation of QCBORDecodeNesting
*
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2025, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Forked from qcbor_decode.c on 11/28/24
* ========================================================================== */
#ifndef decode_nesting_h
#define decode_nesting_h
#include "qcbor/qcbor_private.h"
/* When this was not all explicitly inline, the compiler decided to
* inline everything on its own, so we know there's no loss by
* making it all inline.
*/
static inline void
DecodeNesting_Init(QCBORDecodeNesting *pNesting)
{
/* Assumes that *pNesting has been zero'd before this call. */
pNesting->pLevels[0].uLevelType = QCBOR_TYPE_BYTE_STRING;
pNesting->pCurrent = &(pNesting->pLevels[0]);
}
static inline bool
DecodeNesting_IsCurrentDefiniteLength(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
/* Not a map or array */
return false;
}
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
if(pNesting->pCurrent->u.ma.uCountTotal == QCBOR_COUNT_INDICATES_INDEFINITE_LENGTH) {
/* Is indefinite */
return false;
}
#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
/* All checks passed; is a definte length map or array */
return true;
}
static inline bool
DecodeNesting_IsBoundedType(const QCBORDecodeNesting *pNesting, uint8_t uType)
{
if(pNesting->pCurrentBounded == NULL) {
return false;
}
uint8_t uItemDataType = pNesting->pCurrentBounded->uLevelType;
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
if(uItemDataType == QCBOR_TYPE_MAP_AS_ARRAY) {
uItemDataType = QCBOR_TYPE_ARRAY;
}
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
if(uItemDataType != uType) {
return false;
}
return true;
}
static inline bool
DecodeNesting_IsCurrentBounded(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
return true;
}
if(pNesting->pCurrent->u.ma.uStartOffset != QCBOR_NON_BOUNDED_OFFSET) {
return true;
}
return false;
}
static inline bool
DecodeNesting_IsBoundedEmpty(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrentBounded->u.ma.uCountCursor == QCBOR_COUNT_INDICATES_ZERO_LENGTH) {
return true;
} else {
return false;
}
}
static inline bool
DecodeNesting_IsAtEndOfBoundedLevel(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrentBounded == NULL) {
/* No bounded map or array set up */
return false;
}
if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
/* Not a map or array; end of those is by byte count */
return false;
}
if(!DecodeNesting_IsCurrentBounded(pNesting)) {
/* In a traveral at a level deeper than the bounded level */
return false;
}
/* Works for both definite- and indefinitelength maps/arrays */
if(pNesting->pCurrentBounded->u.ma.uCountCursor != 0 &&
pNesting->pCurrentBounded->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) {
/* Count is not zero, still unconsumed item */
return false;
}
/* All checks passed, got to the end of an array or map*/
return true;
}
static inline bool
DecodeNesting_IsEndOfDefiniteLengthMapOrArray(const QCBORDecodeNesting *pNesting)
{
/* Must only be called on map / array */
if(pNesting->pCurrent->u.ma.uCountCursor == 0) {
return true;
} else {
return false;
}
}
static inline bool
DecodeNesting_IsCurrentTypeMap(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrent->uLevelType == CBOR_MAJOR_TYPE_MAP) {
return true;
} else {
return false;
}
}
static inline bool
DecodeNesting_IsCurrentAtTop(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrent == &(pNesting->pLevels[0])) {
return true;
} else {
return false;
}
}
static inline bool
DecodeNesting_IsCurrentBstrWrapped(const QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrent->uLevelType == QCBOR_TYPE_BYTE_STRING) {
/* is a byte string */
return true;
}
return false;
}
static inline uint8_t
DecodeNesting_GetCurrentLevel(const QCBORDecodeNesting *pNesting)
{
const ptrdiff_t nLevel = pNesting->pCurrent - &(pNesting->pLevels[0]);
/* Limit in DecodeNesting_Descend against more than
* QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe
*/
return (uint8_t)nLevel;
}
static inline void
DecodeNesting_DecrementDefiniteLengthMapOrArrayCount(QCBORDecodeNesting *pNesting)
{
/* Only call on a definite-length array / map */
pNesting->pCurrent->u.ma.uCountCursor--;
}
static inline void
DecodeNesting_ZeroMapOrArrayCount(QCBORDecodeNesting *pNesting)
{
pNesting->pCurrent->u.ma.uCountCursor = 0;
}
static inline void
DecodeNesting_ResetMapOrArrayCount(QCBORDecodeNesting *pNesting)
{
if(pNesting->pCurrent->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) {
pNesting->pCurrentBounded->u.ma.uCountCursor = pNesting->pCurrentBounded->u.ma.uCountTotal;
}
}
static inline void
DecodeNesting_ReverseDecrement(QCBORDecodeNesting *pNesting)
{
/* Only call on a definite-length array / map */
pNesting->pCurrent->u.ma.uCountCursor++;
}
static inline void
DecodeNesting_ClearBoundedMode(QCBORDecodeNesting *pNesting)
{
pNesting->pCurrent->u.ma.uStartOffset = QCBOR_NON_BOUNDED_OFFSET;
}
static inline QCBORError
DecodeNesting_Descend(QCBORDecodeNesting *pNesting, uint8_t uType)
{
/* Error out if nesting is too deep */
if(pNesting->pCurrent >= &(pNesting->pLevels[QCBOR_MAX_ARRAY_NESTING])) {
return QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP;
}
/* The actual descend */
pNesting->pCurrent++;
pNesting->pCurrent->uLevelType = uType;
return QCBOR_SUCCESS;
}
static inline QCBORError
DecodeNesting_DescendMapOrArray(QCBORDecodeNesting *pNesting,
const uint8_t uQCBORType,
const uint16_t uCount)
{
QCBORError uError = QCBOR_SUCCESS;
if(uCount == 0) {
/* Nothing to do for empty definite-length arrays. They are just are
* effectively the same as an item that is not a map or array.
*/
goto Done;
/* Empty indefinite-length maps and arrays are handled elsewhere */
}
/* Rely on check in QCBOR_Private_DecodeArrayOrMap() for definite-length
* arrays and maps that are too long */
uError = DecodeNesting_Descend(pNesting, uQCBORType);
if(uError != QCBOR_SUCCESS) {
goto Done;
}
pNesting->pCurrent->u.ma.uCountCursor = uCount;
pNesting->pCurrent->u.ma.uCountTotal = uCount;
DecodeNesting_ClearBoundedMode(pNesting);
Done:
return uError;;
}
static inline QCBORError
DecodeNesting_DescendIntoBstrWrapped(QCBORDecodeNesting *pNesting,
uint32_t uEndOffset,
uint32_t uStartOffset)
{
QCBORError uError;
uError = DecodeNesting_Descend(pNesting, QCBOR_TYPE_BYTE_STRING);
if(uError != QCBOR_SUCCESS) {
goto Done;
}
/* Fill in the new byte string level */
pNesting->pCurrent->u.bs.uSavedEndOffset = uEndOffset;
pNesting->pCurrent->u.bs.uBstrStartOffset = uStartOffset;
/* Bstr wrapped levels are always bounded */
pNesting->pCurrentBounded = pNesting->pCurrent;
Done:
return uError;;
}
static inline void
DecodeNesting_Ascend(QCBORDecodeNesting *pNesting)
{
pNesting->pCurrent--;
}
static inline void
DecodeNesting_SetCurrentToBoundedLevel(QCBORDecodeNesting *pNesting)
{
pNesting->pCurrent = pNesting->pCurrentBounded;
}
static inline void
DecodeNesting_SetMapOrArrayBoundedMode(QCBORDecodeNesting *pNesting, bool bIsEmpty, size_t uStart)
{
/* Should be only called on maps and arrays */
/*
* DecodeNesting_EnterBoundedMode() checks to be sure uStart is not
* larger than DecodeNesting_EnterBoundedMode which keeps it less than
* uin32_t so the cast is safe.
*/
pNesting->pCurrent->u.ma.uStartOffset = (uint32_t)uStart;
if(bIsEmpty) {
pNesting->pCurrent->u.ma.uCountCursor = QCBOR_COUNT_INDICATES_ZERO_LENGTH;
}
}
static inline QCBORError
DecodeNesting_EnterBoundedMapOrArray(QCBORDecodeNesting *pNesting,
bool bIsEmpty,
size_t uOffset)
{
/*
* Should only be called on map/array.
*
* Have descended into this before this is called. The job here is
* just to mark it in bounded mode.
*
* Check against QCBOR_MAX_SIZE make sure that
* uOffset doesn't collide with QCBOR_NON_BOUNDED_OFFSET.
*
* Cast of uOffset to uint32_t for cases where SIZE_MAX < UINT32_MAX.
*/
if((uint32_t)uOffset >= QCBOR_MAX_SIZE) {
return QCBOR_ERR_INPUT_TOO_LARGE;
}
pNesting->pCurrentBounded = pNesting->pCurrent;
DecodeNesting_SetMapOrArrayBoundedMode(pNesting, bIsEmpty, uOffset);
return QCBOR_SUCCESS;
}
static inline uint32_t
DecodeNesting_GetPreviousBoundedEnd(const QCBORDecodeNesting *pMe)
{
return pMe->pCurrentBounded->u.bs.uSavedEndOffset;
}
static inline uint8_t
DecodeNesting_GetBoundedModeLevel(const QCBORDecodeNesting *pNesting)
{
const ptrdiff_t nLevel = pNesting->pCurrentBounded - &(pNesting->pLevels[0]);
/* Limit in DecodeNesting_Descend against more than
* QCBOR_MAX_ARRAY_NESTING gaurantees cast is safe
*/
return (uint8_t)nLevel;
}
static inline uint32_t
DecodeNesting_GetMapOrArrayStart(const QCBORDecodeNesting *pNesting)
{
return pNesting->pCurrentBounded->u.ma.uStartOffset;
}
static inline void
DecodeNesting_LevelUpCurrent(QCBORDecodeNesting *pNesting)
{
pNesting->pCurrent = pNesting->pCurrentBounded - 1;
}
static inline void
DecodeNesting_LevelUpBounded(QCBORDecodeNesting *pNesting)
{
while(pNesting->pCurrentBounded != &(pNesting->pLevels[0])) {
pNesting->pCurrentBounded--;
if(DecodeNesting_IsCurrentBounded(pNesting)) {
break;
}
}
}
static inline void
DecodeNesting_PrepareForMapSearch(QCBORDecodeNesting *pNesting,
QCBORDecodeNesting *pSave)
{
*pSave = *pNesting;
}
static inline void
DecodeNesting_RestoreFromMapSearch(QCBORDecodeNesting *pNesting,
const QCBORDecodeNesting *pSave)
{
*pNesting = *pSave;
}
#endif /* decode_nesting_h */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,257 @@
/* ==========================================================================
* ieee754.h -- Conversion between half, double & single-precision floats
*
* Copyright (c) 2018-2025, Laurence Lundblade. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in file named "LICENSE"
*
* Created on 7/23/18
* ========================================================================== */
#ifndef ieee754_h
#define ieee754_h
#include <stdint.h>
/** @file ieee754.h
*
* This implements floating-point conversion between half, single and
* double precision floating-point numbers, in particular conversion to
* smaller representation (e.g., double to single) that does not lose
* precision for CBOR preferred serialization.
*
* This also implements conversion of floats to whole numbers as
* is required for dCBOR.
*
* This implementation works entirely with shifts and masks and does
* not require any floating-point HW or library.
*
* This conforms to IEEE 754-2008, but note that it doesn't specify
* conversions, just the encodings.
*
* This is complete, supporting +/- infinity, +/- zero, subnormals and
* NaN payloads. NaN significands, which contain the NaN payload, are
* shortened by dropping the right most bits if they are zero and
* shifting to the right. If the rightmost bits are not zero the
* shortening is not performed. When converting from smaller to
* larger, the significand is shifted left and zero-padded. This is
* what is specified by CBOR preferred serialization. There is no
* special handling of silent and quiet NaNs. They are treated as
* part of the significand.
*
* A previous version of this was usable as a general library for
* conversion. This version is reduced to what is needed for CBOR.
*/
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
/**
* @brief Convert half-precision float to double-precision float.
*
* @param[in] uHalfPrecision Half-precision number to convert.
*
* @returns double-precision value.
*
* This is a lossless conversion because every half-precision value
* can be represented as a double. There is no error condition.
*
* There is no half-precision type in C, so it is represented here as
* a @c uint16_t. The bits of @c uHalfPrecision are as described for
* half-precision by IEEE 754.
*/
double
IEEE754_HalfToDouble(uint16_t uHalfPrecision);
/**
* @brief Convert single-precision float to double-precision float.
*
* @param[in] uSingle float bits copied to a uint32_t.
*
* @returns double-precision value.
*
* This is a lossless conversion because every single-precision value
* can be represented as a double. There is no error condition.
*
* This is in lieu of a cast that usually results in CPU instructions
* that convert. These instructions don't reliably handle NaN payloads.
* This does.
*/
double
IEEE754_SingleToDouble(uint32_t uSingle);
/** Holds a floating-point value that could be half, single or
* double-precision. The value is in a @c uint64_t that may be copied
* to a float or double. Simply casting uValue will usually work but
* may generate compiler or static analyzer warnings. Using
* UsefulBufUtil_CopyUint64ToDouble() or
* UsefulBufUtil_CopyUint32ToFloat() will not (and will not generate
* any extra code).
*/
typedef struct {
enum {IEEE754_UNION_IS_HALF = 2,
IEEE754_UNION_IS_SINGLE = 4,
IEEE754_UNION_IS_DOUBLE = 8,
} uSize; /* Size of uValue */
uint64_t uValue;
} IEEE754_union;
/** Holds result of an attempt to convert a floating-point
* number to an int64_t or uint64_t.
*/
struct IEEE754_ToInt {
enum {IEEE754_ToInt_IS_INT,
IEEE754_ToInt_IS_UINT,
IEEE754_ToInt_IS_65BIT_NEG,
IEEE754_ToInt_NO_CONVERSION,
IEEE754_ToInt_NaN
} type;
union {
uint64_t un_signed;
int64_t is_signed;
} integer;
};
/**
* @brief Convert a double to either single or half-precision.
*
* @param[in] d The value to convert.
* @param[in] bAllowHalfPrecision If true, convert to either half or
* single precision.
*
* @returns Unconverted value, or value converted to single or half-precision.
*
* This always succeeds. If the value cannot be converted without the
* loss of precision, it is not converted.
*
* This handles all subnormals and NaN payloads.
*/
IEEE754_union
IEEE754_DoubleToSmaller(double d, int bAllowHalfPrecision, int bNoNaNPayload);
/**
* @brief Convert a single-precision float to half-precision.
*
* @param[in] uSingle type @c float bits copied to a uint32_t.
*
* @returns Either unconverted value or value converted to half-precision.
*
* This always succeeds. If the value cannot be converted without the
* loss of precision, it is not converted.
*
* This handles all subnormals and NaN payloads.
*/
IEEE754_union
IEEE754_SingleToHalf(uint32_t uSingle, int bNoNanPayloads);
/**
* @brief Convert a double-precision float to an integer if whole number
*
* @param[in] d The value to convert.
*
* @returns Either converted number or conversion status.
*
* If the value is a whole number that will fit either in a uint64_t
* or an int64_t, it is converted. If it is a NaN, then there is no
* conversion and the fact that it is a NaN is indicated in the
* returned structure. If it can't be converted, then that is
* indicated in the returned structure.
*
* This always returns positive numbers as a uint64_t even if they will
* fit in an int64_t.
*
* This never fails because of precision, but may fail because of range.
*/
struct IEEE754_ToInt
IEEE754_DoubleToInt(double d);
/**
* @brief Convert a single-precision float to an integer if whole number
*
* @param[in] uSingle Type @c float bits copied to a uint32_t.
*
* @returns Either converted number or conversion status.
*
* If the value is a whole number that will fit either in a uint64_t
* or an int64_t, it is converted. If it is a NaN, then there is no
* conversion and the fact that it is a NaN is indicated in the
* returned structure. If it can't be converted, then that is
* indicated in the returned structure.
*
* This always returns positive numbers as a uint64_t even if they will
* fit in an int64_t.
*
* This never fails because of precision, but may fail because of range.
*/
struct IEEE754_ToInt
IEEE754_SingleToInt(uint32_t uSingle);
/**
* @brief Convert an unsigned integer to a double with no precision loss.
*
* @param[in] uInt The value to convert.
* @param[in] uIsNegative 0 if positive, 1 if negative.
*
* @returns Either the converted number or 0.5 if no conversion.
*
* The conversion will fail if the input can not be represented in the
* 52 bits or precision that a double has. 0.5 is returned to indicate
* no conversion. It is out-of-band from non-error results, because
* all non-error results are whole integers.
*/
#define IEEE754_UINT_TO_DOUBLE_OOB 0.5
double
IEEE754_UintToDouble(uint64_t uInt, int uIsNegative);
#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
/**
* @brief Tests whether NaN is "quiet" vs having a payload.
*
* @param[in] dNum Double number to test.
*
* @returns 0 if a quiet NaN, 1 if it has a payload.
*
* A quiet NaN is usually represented as 0x7ff8000000000000. That is
* the significand bits are 0x8000000000000. If the significand bits
* are other than 0x8000000000000 it is considered to have a NaN
* payload.
*
* Note that 0x7ff8000000000000 is not specified in a standard, but it
* is commonly implemented and chosen by CBOR as the best way to
* represent a NaN.
*/
int
IEEE754_DoubleHasNaNPayload(double dNum);
/**
* @brief Tests whether NaN is "quiet" vs having a payload.
*
* @param[in] uSingle type @c float bits copied to a uint32_t.
*
* @returns 0 if a quiet NaN, 1 if it has a payload.
*
* See IEEE754_DoubleHasNaNPayload(). A single precision quiet NaN
* is 0x7fc00000.
*/
int
IEEE754_SingleHasNaNPayload(uint32_t uSingle);
#endif /* ieee754_h */

View File

@@ -0,0 +1,95 @@
/* ==========================================================================
* err_to_str.c -- strings names for errors
*
* Copyright (c) 2020, Patrick Uiterwijk. All rights reserved.
* Copyright (c) 2020,2024, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in file named "LICENSE"
*
* Created on 3/21/20
* ========================================================================== */
#include "qcbor/qcbor_common.h"
#include "UsefulBuf.h"
#define ERR_TO_STR_CASE(errpart) case errpart: return #errpart;
const char *
qcbor_err_to_str(const QCBORError uErr) {
switch (uErr) {
ERR_TO_STR_CASE(QCBOR_SUCCESS)
ERR_TO_STR_CASE(QCBOR_ERR_BUFFER_TOO_SMALL)
ERR_TO_STR_CASE(QCBOR_ERR_ENCODE_UNSUPPORTED)
ERR_TO_STR_CASE(QCBOR_ERR_BUFFER_TOO_LARGE)
ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_NESTING_TOO_DEEP)
ERR_TO_STR_CASE(QCBOR_ERR_CLOSE_MISMATCH)
ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_TOO_LONG)
ERR_TO_STR_CASE(QCBOR_ERR_TOO_MANY_CLOSES)
ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN)
ERR_TO_STR_CASE(QCBOR_ERR_OPEN_BYTE_STRING)
ERR_TO_STR_CASE(QCBOR_ERR_CANNOT_CANCEL)
ERR_TO_STR_CASE(QCBOR_ERR_BAD_TYPE_7)
ERR_TO_STR_CASE(QCBOR_ERR_EXTRA_BYTES)
ERR_TO_STR_CASE(QCBOR_ERR_UNSUPPORTED)
ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED)
ERR_TO_STR_CASE(QCBOR_ERR_BAD_INT)
ERR_TO_STR_CASE(QCBOR_ERR_INDEFINITE_STRING_CHUNK)
ERR_TO_STR_CASE(QCBOR_ERR_HIT_END)
ERR_TO_STR_CASE(QCBOR_ERR_BAD_BREAK)
ERR_TO_STR_CASE(QCBOR_ERR_INPUT_TOO_LARGE)
ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP)
ERR_TO_STR_CASE(QCBOR_ERR_ARRAY_DECODE_TOO_LONG)
ERR_TO_STR_CASE(QCBOR_ERR_STRING_TOO_LONG)
ERR_TO_STR_CASE(QCBOR_ERR_BAD_EXP_AND_MANTISSA)
ERR_TO_STR_CASE(QCBOR_ERR_NO_STRING_ALLOCATOR)
ERR_TO_STR_CASE(QCBOR_ERR_STRING_ALLOCATE)
ERR_TO_STR_CASE(QCBOR_ERR_MAP_LABEL_TYPE)
ERR_TO_STR_CASE(QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT)
ERR_TO_STR_CASE(QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_TAGS_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_TOO_MANY_TAGS)
ERR_TO_STR_CASE(QCBOR_ERR_UNEXPECTED_TYPE)
ERR_TO_STR_CASE(QCBOR_ERR_DUPLICATE_LABEL)
ERR_TO_STR_CASE(QCBOR_ERR_MEM_POOL_SIZE)
ERR_TO_STR_CASE(QCBOR_ERR_INT_OVERFLOW)
ERR_TO_STR_CASE(QCBOR_ERR_DATE_OVERFLOW)
ERR_TO_STR_CASE(QCBOR_ERR_EXIT_MISMATCH)
ERR_TO_STR_CASE(QCBOR_ERR_NO_MORE_ITEMS)
ERR_TO_STR_CASE(QCBOR_ERR_LABEL_NOT_FOUND)
ERR_TO_STR_CASE(QCBOR_ERR_NUMBER_SIGN_CONVERSION)
ERR_TO_STR_CASE(QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW)
ERR_TO_STR_CASE(QCBOR_ERR_MAP_NOT_ENTERED)
ERR_TO_STR_CASE(QCBOR_ERR_CALLBACK_FAIL)
ERR_TO_STR_CASE(QCBOR_ERR_FLOAT_DATE_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_PREFERRED_FLOAT_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_HW_FLOAT_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_FLOAT_EXCEPTION)
ERR_TO_STR_CASE(QCBOR_ERR_ALL_FLOAT_DISABLED)
ERR_TO_STR_CASE(QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT)
ERR_TO_STR_CASE(QCBOR_ERR_CANNOT_ENTER_ALLOCATED_STRING)
default:
if(uErr >= QCBOR_ERR_FIRST_USER_DEFINED && uErr <= QCBOR_ERR_LAST_USER_DEFINED) {
/* Static buffer is not thread safe, but this is only a diagnostic */
static char buf[20];
UsefulOutBuf OB;
/* Make NULL-terminated string "USER_DEFINED_NNN" using UsefulOutBuf */
UsefulOutBuf_Init(&OB, UsefulBuf_FROM_BYTE_ARRAY(buf));
UsefulOutBuf_AppendString(&OB, "USER_DEFINED_");
UsefulOutBuf_AppendByte(&OB, (uint8_t)(uErr/100 + '0'));
UsefulOutBuf_AppendByte(&OB, (uint8_t)(((uErr/10) % 10) + '0'));
UsefulOutBuf_AppendByte(&OB, (uint8_t)(((uErr/10) % 10) + '0'));
UsefulOutBuf_AppendByte(&OB, 0x00);
return buf;
} else {
return "Unidentified QCBOR error";
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,541 @@
/* ===========================================================================
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2025, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors, nor the name "Laurence Lundblade" may be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
#include "qcbor/qcbor_number_encode.h"
#include "ieee754.h"
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
#include <math.h> /* Only for NAN definition */
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
/**
* @file qcbor_number_encode.c
*
*/
/*
* Public function for adding signed integers. See qcbor/qcbor_encode.h
*/
void
QCBOREncode_AddInt64(QCBOREncodeContext *pMe, const int64_t nNum)
{
uint8_t uMajorType;
uint64_t uValue;
if(nNum < 0) {
/* In CBOR -1 encodes as 0x00 with major type negative int.
* First add one as a signed integer because that will not
* overflow. Then change the sign as needed for encoding (the
* opposite order, changing the sign and subtracting, can cause
* an overflow when encoding INT64_MIN). */
int64_t nTmp = nNum + 1;
uValue = (uint64_t)-nTmp;
uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
} else {
uValue = (uint64_t)nNum;
uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
}
QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, uValue, 0);
}
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
/**
* @brief Semi-private method to add a double using preferred encoding.
*
* @param[in] pMe The encode context.
* @param[in] dNum The double to add.
*
* This converts the double to a float or half-precision if it can be done
* without a loss of precision. See QCBOREncode_AddDouble().
*/
void
QCBOREncode_Private_AddPreferredDouble(QCBOREncodeContext *pMe, double dNum)
{
IEEE754_union FloatResult;
bool bNoNaNPayload;
struct IEEE754_ToInt IntResult;
uint64_t uNegValue;
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD)) {
pMe->uError = QCBOR_ERR_NOT_ALLOWED;
return;
}
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION) {
IntResult = IEEE754_DoubleToInt(dNum);
switch(IntResult.type) {
case IEEE754_ToInt_IS_INT:
QCBOREncode_AddInt64(pMe, IntResult.integer.is_signed);
return;
case IEEE754_ToInt_IS_UINT:
QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
return;
case IEEE754_ToInt_IS_65BIT_NEG:
{
if(IntResult.integer.un_signed == 0) {
uNegValue = UINT64_MAX;
} else {
uNegValue = IntResult.integer.un_signed-1;
}
QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
}
return;
case IEEE754_ToInt_NaN:
dNum = NAN;
bNoNaNPayload = true;
break;
case IEEE754_ToInt_NO_CONVERSION:
bNoNaNPayload = true;
}
} else {
bNoNaNPayload = false;
}
FloatResult = IEEE754_DoubleToSmaller(dNum, true, bNoNaNPayload);
QCBOREncode_Private_AddType7(pMe,
(uint8_t)FloatResult.uSize,
FloatResult.uValue);
}
/**
* @brief Semi-private method to add a float using preferred encoding.
*
* @param[in] pMe The encode context.
* @param[in] fNum The float to add.
*
* This converts the float to a half-precision if it can be done
* without a loss of precision. See QCBOREncode_AddFloat().
*/
void
QCBOREncode_Private_AddPreferredFloat(QCBOREncodeContext *pMe, float fNum)
{
IEEE754_union FloatResult;
bool bNoNaNPayload;
struct IEEE754_ToInt IntResult;
uint64_t uNegValue;
const uint32_t uSingle = UsefulBufUtil_CopyFloatToUint32(fNum);
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
if(IEEE754_SingleHasNaNPayload(uSingle) && !(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD)) {
pMe->uError = QCBOR_ERR_NOT_ALLOWED;
return;
}
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
if(pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_FLOAT_REDUCTION) {
IntResult = IEEE754_SingleToInt(uSingle);
switch(IntResult.type) {
case IEEE754_ToInt_IS_INT:
QCBOREncode_AddInt64(pMe, IntResult.integer.is_signed);
return;
case IEEE754_ToInt_IS_UINT:
QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
return;
case IEEE754_ToInt_IS_65BIT_NEG:
{
if(IntResult.integer.un_signed == 0) {
uNegValue = UINT64_MAX;
} else {
uNegValue = IntResult.integer.un_signed-1;
}
QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
}
return;
case IEEE754_ToInt_NaN:
fNum = NAN;
bNoNaNPayload = true;
break;
case IEEE754_ToInt_NO_CONVERSION:
bNoNaNPayload = true;
}
} else {
bNoNaNPayload = false;
}
FloatResult = IEEE754_SingleToHalf(uSingle, bNoNaNPayload);
QCBOREncode_Private_AddType7(pMe,
(uint8_t)FloatResult.uSize,
FloatResult.uValue);
}
#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
/**
* @brief Convert a big number to unsigned integer.
*
* @param[in] BigNumber Big number to convert.
*
* @return Converted unsigned.
*
* The big number must be less than 8 bytes long.
**/
static uint64_t
QCBOREncode_Private_BigNumberToUInt(const UsefulBufC BigNumber)
{
uint64_t uInt;
size_t uIndex;
uInt = 0;
for(uIndex = 0; uIndex < BigNumber.len; uIndex++) {
uInt = (uInt << 8) + UsefulBufC_NTH_BYTE(BigNumber, uIndex);
}
return uInt;
}
/**
* @brief Is there a carry when you subtract 1 from the BigNumber?
*
* @param[in] BigNumber Big number to check for carry.
*
* @return If there is a carry, \c true.
*
* If this returns @c true, then @c BigNumber - 1 is one byte shorter
* than @c BigNumber.
**/
static bool
QCBOREncode_Private_BigNumberCarry(const UsefulBufC BigNumber)
{
bool bCarry;
UsefulBufC SubBigNum;
// Improvement: rework without recursion?
if(BigNumber.len == 0) {
return true; /* Subtracting one from zero-length string gives a carry */
} else {
SubBigNum = UsefulBuf_Tail(BigNumber, 1);
bCarry = QCBOREncode_Private_BigNumberCarry(SubBigNum);
if(UsefulBufC_NTH_BYTE(BigNumber, 0) == 0x00 && bCarry) {
/* Subtracting one from 0 gives a carry */
return true;
} else {
return false;
}
}
}
/**
* @brief Output negative bignum bytes with subtraction of 1.
*
* @param[in] pMe The decode context.
* @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
* @ref QCBOR_ENCODE_AS_BORROWED.
* @param[in] BigNumber The negative big number.
*/
static void
QCBOREncode_Private_AddTNegativeBigNumber(QCBOREncodeContext *pMe,
const uint8_t uTagRequirement,
const UsefulBufC BigNumber)
{
size_t uLen;
bool bCarry;
bool bCopiedSomething;
uint8_t uByte;
UsefulBufC SubString;
UsefulBufC NextSubString;
QCBOREncode_Private_BigNumberTag(pMe, uTagRequirement, true);
/* This works on any length without the need of an additional buffer */
/* This subtracts one, possibly making the string shorter by one
* 0x01 -> 0x00
* 0x01 0x00 -> 0xff
* 0x00 0x01 0x00 -> 0x00 0xff
* 0x02 0x00 -> 0x01 0xff
* 0xff -> 0xfe
* 0xff 0x00 -> 0xfe 0xff
* 0x01 0x00 0x00 -> 0xff 0xff
*
* This outputs the big number a byte at a time to be able to operate on
* a big number of any length without memory allocation.
*/
/* Compute the length up front because it goes in the encoded head */
bCarry = QCBOREncode_Private_BigNumberCarry(UsefulBuf_Tail(BigNumber, 1));
uLen = BigNumber.len;
if(bCarry && BigNumber.len > 1 && UsefulBufC_NTH_BYTE(BigNumber, 0) >= 1) {
uLen--;
}
QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uLen,0);
SubString = BigNumber;
bCopiedSomething = false;
while(SubString.len) {
uByte = UsefulBufC_NTH_BYTE(SubString, 0);
NextSubString = UsefulBuf_Tail(SubString, 1);
bCarry = QCBOREncode_Private_BigNumberCarry(NextSubString);
if(bCarry) {
uByte--;
}
/* This avoids all but the last leading zero. See
* QCBOREncode_Private_SkipLeadingZeros() */
if(bCopiedSomething || NextSubString.len == 0 || uByte != 0) {
UsefulOutBuf_AppendByte(&(pMe->OutBuf), uByte);
bCopiedSomething = true;
}
SubString = NextSubString;
}
}
/**
* @brief Remove leading zeros.
*
* @param[in] BigNumber The big number.
*
* @return Big number with no leading zeros.
*
* If the big number is all zeros, this returns a big number that is
* a single zero rather than the empty string.
*
* RFC 8949 3.4.3 does not explicitly say decoders MUST handle the empty
* string, but does say decoders MUST handle leading zeros. So
* Postel's Law is applied here and 0 is not encoded as an empty
* string.
*/
static UsefulBufC
QCBOREncode_Private_SkipLeadingZeros(const UsefulBufC BigNumber)
{
UsefulBufC NLZ;
NLZ = UsefulBuf_SkipLeading(BigNumber, 0x00);
/* An all-zero string reduces to one 0, not an empty string. */
if(NLZ.len == 0 &&
BigNumber.len > 0 &&
UsefulBufC_NTH_BYTE(BigNumber, 0) == 0x00) {
NLZ.len = 1;
}
return NLZ;
}
/**
* @brief Output a big number, preferred or not, with negative offset
*
* @param[in] pMe The decode context.
* @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
* @ref QCBOR_ENCODE_AS_BORROWED.
* @param[in] bPreferred Uses preferred serialization if true
* @param[in] bNegative Indicates big number is negative or postive.
* @param[in] BigNumber The big number.
*
* Regardless of whether preferred serialization is used, if the big
* number is negative, one is subtracted before is output per CBOR
* convetion for big numbers. This requires a little big number
* arithmetic and adds some object code.
*
* If preferred serialization is used, then if the number is smaller
* than UINT64_MAX and postive it is output as type 0 and if it is
* equal to or smaller than UINT64_MAX it is output as a type 1
* integer minus one.
*
* See QCBOREncode_AddTBigNumberRaw() for simple copy through.
*/
void
QCBOREncode_Private_AddTBigNumberMain(QCBOREncodeContext *pMe,
const uint8_t uTagRequirement,
const bool bPreferred,
const bool bNegative,
const UsefulBufC BigNumber)
{
uint64_t uInt;
bool bIs2exp64;
uint8_t uMajorType;
UsefulBufC BigNumberNLZ;
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
if(!bPreferred && pMe->uConfigFlags & QCBOR_ENCODE_CONFIG_ONLY_PREFERRED_BIG_NUMBERS) {
pMe->uError = QCBOR_ERR_NOT_PREFERRED;
return;
}
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
BigNumberNLZ = QCBOREncode_Private_SkipLeadingZeros(BigNumber);
static const uint8_t twoExp64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
bIs2exp64 = ! UsefulBuf_Compare(BigNumber, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(twoExp64));
if(bPreferred && (BigNumberNLZ.len <= 8 || (bNegative && bIs2exp64))) {
if(bIs2exp64) {
/* 2^64 is a 9 byte big number. Since negative numbers are offset
* by one in CBOR, it can be encoded as a type 1 negative. The
* conversion below won't work because the uInt will overflow
* before the subtraction of 1.
*/
uInt = UINT64_MAX;
} else {
uInt = QCBOREncode_Private_BigNumberToUInt(BigNumberNLZ);
if(bNegative) {
uInt--;
}
}
uMajorType = bNegative ? CBOR_MAJOR_TYPE_NEGATIVE_INT :
CBOR_MAJOR_TYPE_POSITIVE_INT;
QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, uInt, 0);
} else {
if(bNegative) {
QCBOREncode_Private_AddTNegativeBigNumber(pMe, uTagRequirement, BigNumberNLZ);
} else {
QCBOREncode_AddTBigNumberRaw(pMe, false, uTagRequirement,BigNumberNLZ);
}
}
}
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/**
* @brief Semi-private method to add bigfloats and decimal fractions.
*
* @param[in] pMe The encoding context to add the value to.
* @param[in] uTagNumber The type 6 tag indicating what this is to be.
* @param[in] nMantissa The @c int64_t mantissa if it is not a big number.
* @param[in] nExponent The exponent.
*
* This outputs either the @ref CBOR_TAG_DECIMAL_FRACTION or
* @ref CBOR_TAG_BIGFLOAT tag. if @c uTag is @ref CBOR_TAG_INVALID64,
* then this outputs the "borrowed" content format.
*
* The tag content output by this is an array with two members, the
* exponent and then the mantissa. The mantissa can be either a big
* number or an @c int64_t.
*
* This implementation cannot output an exponent further from 0 than
* @c INT64_MAX.
*
* To output a mantissa that is between INT64_MAX and UINT64_MAX from 0,
* it must be as a big number.
*
* Typically, QCBOREncode_AddTDecimalFraction(), QCBOREncode_AddTBigFloat(),
* QCBOREncode_AddTDecimalFractionBigNum() or QCBOREncode_AddTBigFloatBigNum()
* is called instead of this.
*/
void
QCBOREncode_Private_AddTExpIntMantissa(QCBOREncodeContext *pMe,
const int uTagRequirement,
const uint64_t uTagNumber,
const int64_t nExponent,
const int64_t nMantissa)
{
/* This is for encoding either a big float or a decimal fraction,
* both of which are an array of two items, an exponent and a
* mantissa. The difference between the two is that the exponent
* is base-2 for big floats and base-10 for decimal fractions, but
* that has no effect on the code here.
*/
/* Separate from QCBOREncode_Private_AddTExpBigMantissa() because
* linking QCBOREncode_AddTBigNumber() adds a lot because it
* does preferred serialization of big numbers and the offset of 1
* for CBOR negative numbers.
*/
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
QCBOREncode_AddTagNumber(pMe, uTagNumber);
}
QCBOREncode_OpenArray(pMe);
QCBOREncode_AddInt64(pMe, nExponent);
QCBOREncode_AddInt64(pMe, nMantissa);
QCBOREncode_CloseArray(pMe);
}
void
QCBOREncode_Private_AddTExpBigMantissa(QCBOREncodeContext *pMe,
const int uTagRequirement,
const uint64_t uTagNumber,
const int64_t nExponent,
const UsefulBufC BigNumMantissa,
const bool bBigNumIsNegative)
{
/* This is for encoding either a big float or a decimal fraction,
* both of which are an array of two items, an exponent and a
* mantissa. The difference between the two is that the exponent
* is base-2 for big floats and base-10 for decimal fractions, but
* that has no effect on the code here.
*/
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
QCBOREncode_AddTagNumber(pMe, uTagNumber);
}
QCBOREncode_OpenArray(pMe);
QCBOREncode_AddInt64(pMe, nExponent);
QCBOREncode_AddTBigNumber(pMe, QCBOR_ENCODE_AS_TAG, bBigNumIsNegative, BigNumMantissa);
QCBOREncode_CloseArray(pMe);
}
void
QCBOREncode_Private_AddTExpBigMantissaRaw(QCBOREncodeContext *pMe,
const int uTagRequirement,
const uint64_t uTagNumber,
const int64_t nExponent,
const UsefulBufC BigNumMantissa,
const bool bBigNumIsNegative)
{
/* This is for encoding either a big float or a decimal fraction,
* both of which are an array of two items, an exponent and a
* mantissa. The difference between the two is that the exponent
* is base-2 for big floats and base-10 for decimal fractions, but
* that has no effect on the code here.
*/
/* Separate from QCBOREncode_Private_AddTExpBigMantissa() because
* linking QCBOREncode_AddTBigNumber() adds a lot because it
* does preferred serialization of big numbers and the offset of 1
* for CBOR negative numbers.
*/
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
QCBOREncode_AddTagNumber(pMe, uTagNumber);
}
QCBOREncode_OpenArray(pMe);
QCBOREncode_AddInt64(pMe, nExponent);
QCBOREncode_AddTBigNumberRaw(pMe, QCBOR_ENCODE_AS_TAG, bBigNumIsNegative, BigNumMantissa);
QCBOREncode_CloseArray(pMe);
}
#endif /* ! QCBOR_DISABLE_EXP_AND_MANTISSA */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
/* ==========================================================================
* tag-examples.c -- Tag decoding examples
*
* Copyright (c) 2024, Laurence Lundblade. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*
* Created on 10/7/24
* ========================================================================== */
#include "tag-examples.h"
#include "qcbor/qcbor_tag_decode.h"
#include <stdio.h>
// The CBOR to decode
static const uint8_t spAddrs[] = {
0xD8, 0x34, 0x44, 0xC0, 0x00, 0x02, 0x01
};
#define CBOR_TAG_IPV4 52
#define CBOR_TAG_IPV6 54
#ifndef QCBOR_DISABLE_TAGS
#define USER_TYPE_IPV4_ADDR 130
#define USER_TYPE_IPV6_ADDR 131
static QCBORError
IPAddrDecodeCallBack(QCBORDecodeContext *pDecodeCtx,
void *pTagDecodersContext,
uint64_t uTagNumber,
QCBORItem *pDecodedItem)
{
(void)pTagDecodersContext;
(void)pDecodeCtx;
if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING) {
return QCBOR_ERR_UNEXPECTED_TYPE;
}
switch(uTagNumber) {
case CBOR_TAG_IPV4:
if(pDecodedItem->val.string.len != 4) {
return QCBOR_ERR_BAD_TAG_CONTENT;
}
pDecodedItem->uDataType = USER_TYPE_IPV4_ADDR;
break;
case CBOR_TAG_IPV6:
if(pDecodedItem->val.string.len != 6) {
return QCBOR_ERR_BAD_TAG_CONTENT;
}
pDecodedItem->uDataType = USER_TYPE_IPV6_ADDR;
break;
default:
return QCBOR_ERR_UNEXPECTED_TAG_NUMBER;
}
return QCBOR_SUCCESS;
}
const struct QCBORTagDecoderEntry Example_TagDecoderTable[] = {
{CBOR_TAG_IPV4, IPAddrDecodeCallBack},
{CBOR_TAG_IPV6, IPAddrDecodeCallBack},
{CBOR_TAG_INVALID64, NULL}
};
void
Example_DecodeIPAddrWithCallBack(void)
{
QCBORDecodeContext DCtx;
QCBORItem Item;
QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spAddrs), 0);
QCBORDecode_InstallTagDecoders(&DCtx, Example_TagDecoderTable, NULL);
QCBORDecode_VGetNext(&DCtx, &Item);
QCBORDecode_Finish(&DCtx);
if(QCBORDecode_GetError(&DCtx)) {
printf("Fail\n");
} else {
printf("%d\n", Item.uDataType);
}
}
#endif /* ! QCBOR_DISABLE_TAGS */
/* If bMustBeTag is true, the input to decode must start with
* a tag number indicating an IP address. The type of IP address
* is returned in puIPVersion.
*
* if bMustBeTag is false, the input must not have a tag
* number. It is just the tag content that is defined for
* for IP Addresses. puIPVersion because an input parameter
* and indicates the type of IP address.
*/
void
GetIPAddr(QCBORDecodeContext *pDecodeCtx,
bool bMustBeTag,
uint8_t *puIPVersion,
UsefulBufC *pAddr)
{
QCBORItem Item;
size_t nExpectedLen;
QCBORError uErr;
#ifndef QCBOR_DISABLE_TAGS
if(bMustBeTag) {
uint64_t uTagNumber;
QCBORDecode_GetNextTagNumber(pDecodeCtx, &uTagNumber);
switch(uTagNumber) {
case CBOR_TAG_IPV4:
*puIPVersion = 4;
break;
case CBOR_TAG_IPV6:
*puIPVersion = 6;
break;
case CBOR_TAG_INVALID64:
if(bMustBeTag) {
uErr = QCBOR_ERR_BAD_TAG_CONTENT;
}
default:
uErr = QCBOR_ERR_UNEXPECTED_TYPE;
goto Done;
}
}
#else
(void)bMustBeTag;
#endif /* ! QCBOR_DISABLE_TAGS */
if(*puIPVersion == 4) {
nExpectedLen = 4;
} else if(*puIPVersion == 6) {
nExpectedLen = 16;
} else {
uErr = 150; // TODO:
goto Done;
}
QCBORDecode_VGetNext(pDecodeCtx, &Item);
if(QCBORDecode_GetError(pDecodeCtx)) {
return;
}
if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
uErr = QCBOR_ERR_BAD_TAG_CONTENT;
goto Done;
}
if(Item.val.string.len != nExpectedLen) {
uErr = QCBOR_ERR_BAD_TAG_CONTENT;
goto Done;
}
uErr = QCBOR_SUCCESS;
*pAddr = Item.val.string;
Done:
QCBORDecode_SetError(pDecodeCtx, uErr);
}
void
Example_DecodeIPAddrWithGet(void)
{
QCBORDecodeContext DCtx;
uint8_t uType;
UsefulBufC Addr;
QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spAddrs), 0);
GetIPAddr(&DCtx, true, &uType, &Addr);
QCBORDecode_Finish(&DCtx);
if(QCBORDecode_GetError(&DCtx)) {
printf("Fail\n");
} else {
printf("%d\n", uType);
}
}
int32_t
RunTagExamples(void)
{
#ifndef QCBOR_DISABLE_TAGS
Example_DecodeIPAddrWithCallBack();
#endif /* ! QCBOR_DISABLE_TAGS */
Example_DecodeIPAddrWithGet();
return 0;
}

View File

@@ -0,0 +1,19 @@
/* ==========================================================================
* tag-examples.h -- Tag decoding examples
*
* Copyright (c) 2024, Laurence Lundblade. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*
* Created on 10/7/24
* ========================================================================== */
#ifndef tag_examples_h
#define tag_examples_h
#include <stdint.h>
int32_t RunTagExamples(void);
#endif /* tag_examples_h */

View File

@@ -0,0 +1,79 @@
#-------------------------------------------------------------------------------
# Copyright (c) 2022-2023, Arm Limited. All rights reserved.
# Copyright (c) 2024, Laurence Lundblade. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#-------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.15)
# Validate value of BUILD_QCBOR_TEST config option
if ((NOT BUILD_QCBOR_TEST STREQUAL "LIB") AND (NOT BUILD_QCBOR_TEST STREQUAL "APP"))
message(FATAL_ERROR "QCBOR | Invalid Config: BUILD_QCBOR_TEST=${BUILD_QCBOR_TEST}")
endif()
add_library(qcbor_test STATIC)
target_sources(qcbor_test
PRIVATE
float_tests.c
half_to_double_from_rfc7049.c
qcbor_decode_tests.c
qcbor_encode_tests.c
run_tests.c
UsefulBuf_Tests.c
)
target_include_directories(qcbor_test
PUBLIC
.
PRIVATE
../inc
)
target_compile_definitions(qcbor_test
PUBLIC
$<$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_HW_USE}>:QCBOR_DISABLE_FLOAT_HW_USE>
$<$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_PREFERRED}>:QCBOR_DISABLE_PREFERRED_FLOAT>
$<$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_ALL}>:USEFULBUF_DISABLE_ALL_FLOAT>
)
target_link_libraries(qcbor_test
PRIVATE
qcbor
# The math library is needed for floating-point support.
# To avoid need for it #define QCBOR_DISABLE_FLOAT_HW_USE
# Using GCC
$<$<AND:$<STREQUAL:${CMAKE_C_COMPILER_ID},"GNU">,$<NOT:$<BOOL:${QCBOR_OPT_DISABLE_FLOAT_HW_USE}>>>:m>
)
if (BUILD_QCBOR_TEST STREQUAL "APP")
add_executable(qcbortest)
target_sources(qcbortest
PRIVATE
../cmd_line_main.c
../example.c
../tag-examples.c
../ub-example.c
)
target_include_directories(qcbortest
PRIVATE
../
)
target_link_libraries(qcbortest
PRIVATE
qcbor
qcbor_test
)
message(STATUS "Adding test qcbortest")
add_test(
NAME qcbortest
COMMAND qcbortest
)
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/* ==========================================================================
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018, Laurence Lundblade.
* Copyright (c) 2021, Arm Limited.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors, nor the name "Laurence Lundblade" may be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
#ifndef UsefulBuf_UsefulBuf_Tests_h
#define UsefulBuf_UsefulBuf_Tests_h
const char * UOBTest_NonAdversarial(void);
const char * TestBasicSanity(void);
const char * UOBTest_BoundaryConditionsTest(void);
const char * UBMacroConversionsTest(void);
const char * UBUtilTests(void);
const char * UIBTest_IntegerFormat(void);
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
const char * UBUTest_CopyUtil(void);
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
const char * UBAdvanceTest(void);
const char * UOBExtraTests(void);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
/* ===========================================================================
* float_tests.h -- tests for floats and conversion to/from half-precision
*
* Copyright (c) 2018-2025, Laurence Lundblade. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in file named "LICENSE"
*
* Created on 9/19/18
* ==========================================================================*/
#ifndef float_tests_h
#define float_tests_h
#include <stdint.h>
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
/* This tests a large number half-precision values
* in the conversion to/from half/double against
* the sample code in the CBOR RFC. */
int32_t HalfPrecisionAgainstRFCCodeTest(void);
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
/*
* This tests floating point encoding, decoding
* and conversion for lots of different values.
* It covers Preferred Serialization processing
* of floating point. It's focus is on the numbers
* not the encode/decode functions.
*/
int32_t FloatValuesTests(void);
/*
* This tests encoding and decoding of floating-point NaNs.
*/
int32_t NaNPayloadsTest(void);
/*
* This calls each and every method for encoding
* floating-point numbers.
*/
int32_t GeneralFloatEncodeTests(void);
/*
* Tests float decoding, including error codes in scenarios
* where various float features are disabled. This also
* tests decoding using spiffy decode methods.
*/
int32_t GeneralFloatDecodeTests(void);
#endif /* float_tests_h */

View File

@@ -0,0 +1,76 @@
/*
Copyright (c) 2013 IETF Trust and the persons identified as the
document authors. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents
(http://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with respect
to this document. Code Components extracted from this document must
include Simplified BSD License text as described in Section 4.e of
the Trust Legal Provisions and are provided without warranty as
described in the Simplified BSD License.
*/
/*
This code is from RFC 7049/8949. It is not used in the main implementation
because:
a) it adds a dependency on <math.h> and ldexp().
b) the license may be an issue
QCBOR does support half-precision, but rather than using
floating-point math like this, it does it with bit shifting
and masking.
This code is here to test that code.
*/
#include "half_to_double_from_rfc7049.h"
#include <math.h>
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
double decode_half(const unsigned char *halfp) {
int half = (halfp[0] << 8) + halfp[1];
int exp = (half >> 10) & 0x1f;
int mant = half & 0x3ff;
double val;
if (exp == 0) val = ldexp(mant, -24);
else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
else val = mant == 0 ? INFINITY : NAN;
return half & 0x8000 ? -val : val;
}
/* This is a first version from Carsten in July 2025 to be published in
* an RFC. Probably there will be updates. */
/* returns 0..0xFFFF if float16 encoding possible, -1 otherwise.
b64 is a binary64 floating point as an unsigned long. */
int try_float16_encode(unsigned long b64) {
unsigned long s16 = b64 >> 48 & 0x8000;
unsigned long mant = b64 & 0xfffffffffffffUL;
unsigned long exp = b64 >> 52 & 0x7ff;
if (exp == 0 && mant == 0) /* f64 denorms are out of range */
return (int)s16; /* so handle 0.0 and -0.0 only */
if (exp >= 999 && exp < 1009) { /* f16 denorm, exp16 = 0 */
if (mant & ((1UL << (1051 - exp)) - 1))
return -1; /* bits lost in f16 denorm */
return (int)(s16 + ((mant + 0x10000000000000UL) >> (1051 - exp)));
}
if (mant & 0x3ffffffffffUL) /* bits lost in f16 */
return -1;
if (exp >= 1009 && exp <= 1038) /* normalized f16 */
return (int)(s16 + ((exp - 1008) << 10) + (mant >> 42));
if (exp == 2047) /* Inf, NaN */
return (int)(s16 + 0x7c00 + (mant >> 42));
return -1;
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */

View File

@@ -0,0 +1,22 @@
/*==============================================================================
half_to_double_from_rfc7049.h -- interface to IETF float conversion code.
Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 9/23/18
============================================================================*/
#ifndef half_to_double_from_rfc7049_h
#define half_to_double_from_rfc7049_h
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
double decode_half(const unsigned char *halfp);
int try_float16_encode(unsigned long b64);
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
#endif /* half_to_double_from_rfc7049_h */

View File

@@ -0,0 +1,339 @@
/*==============================================================================
not_well_formed_cbor.h -- vectors to test for handling of not-well-formed CBOR
This is part of QCBOR -- https://github.com/laurencelundblade/QCBOR
Copyright (c) 2019, Laurence Lundblade. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 7/27/19
==============================================================================*/
#ifndef not_well_formed_cbor_h
#define not_well_formed_cbor_h
#include <stddef.h> // for size_t
#include <stdint.h> // for uint8_t
struct someBinaryBytes {
const uint8_t *p; // Pointer to the bytes
size_t n; // Length of the bytes
};
static const struct someBinaryBytes paNotWellFormedCBOR[] = {
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
// Indefinite length strings must be closed off
// An indefinite length byte string not closed off
{(uint8_t[]){0x5f, 0x41, 0x00}, 3},
// An indefinite length text string not closed off
{(uint8_t[]){0x7f, 0x61, 0x00}, 3},
// All the chunks in an indefinite length string must be of the
// type of indefinite length string and indefinite
// indefinite length byte string with text string chunk
{(uint8_t[]){0x5f, 0x61, 0x00, 0xff}, 4},
// indefinite length text string with a byte string chunk
{(uint8_t[]){0x7f, 0x41, 0x00, 0xff}, 4},
// indefinite length byte string with an positive integer chunk
{(uint8_t[]){0x5f, 0x00, 0xff}, 3},
// indefinite length byte string with an negative integer chunk
{(uint8_t[]){0x5f, 0x21, 0xff}, 3},
// indefinite length byte string with an array chunk
{(uint8_t[]){0x5f, 0x80, 0xff}, 3},
// indefinite length byte string with an map chunk
{(uint8_t[]){0x5f, 0xa0, 0xff}, 3},
#ifndef QCBOR_DISABLE_TAGS
// indefinite length byte string with tagged integer chunk
{(uint8_t[]){0x5f, 0xc0, 0x00, 0xff}, 4},
#endif /* QCBOR_DISABLE_TAGS */
// indefinite length byte string with an simple type chunk
{(uint8_t[]){0x5f, 0xe0, 0xff}, 3},
// indefinite length byte string with indefinite string inside
{(uint8_t[]){0x5f, 0x5f, 0x41, 0x00, 0xff, 0xff}, 6},
// indefinite length text string with indefinite string inside
{(uint8_t[]){0x7f, 0x7f, 0x61, 0x00, 0xff, 0xff}, 6},
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
// Definte length maps and arrays must be closed by having the
// right number of items
// A definte length array that is supposed to have 1 item, but has none
{(uint8_t[]){0x81}, 1},
// A definte length array that is supposed to have 2 items, but has only 1
{(uint8_t[]){0x82, 0x00}, 2},
// A definte length array that is supposed to have 511 items, but has only 1
{(uint8_t[]){0x9a, 0x01, 0xff, 0x00}, 4},
// A definte length map that is supposed to have 1 item, but has none
{(uint8_t[]){0xa1}, 1},
// A definte length map that is supposed to have s item, but has only 1
{(uint8_t[]){0xa2, 0x01, 0x02}, 3},
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
// Indefinte length maps and arrays must be ended by a break
// Indefinite length array with zero items and no break
{(uint8_t[]){0x9f}, 1},
// Indefinite length array with two items and no break
{(uint8_t[]){0x9f, 0x01, 0x02}, 3},
// Indefinite length map with zero items and no break
{(uint8_t[]){0xbf}, 1},
// Indefinite length map with two items and no break
{(uint8_t[]){0xbf, 0x01, 0x02, 0x01, 0x02}, 5},
// Some extra test vectors for unclosed arrays and maps
// Unclosed indefinite array containing a close definite array
{(uint8_t[]){0x9f, 0x80, 0x00}, 3},
// Definite length array containing an unclosed indefinite array
{(uint8_t[]){0x81, 0x9f}, 2},
// Deeply nested definite length arrays with deepest one unclosed
{(uint8_t[]){0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81}, 9},
// Deeply nested indefinite length arrays with deepest one unclosed
{(uint8_t[]){0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xff, 0xff, 0xff, 0xff}, 9},
// Mixed nesting with indefinite unclosed
{(uint8_t[]){0x9f, 0x81, 0x9f, 0x81, 0x9f, 0x9f, 0xff, 0xff, 0xff}, 9},
// Mixed nesting with definite unclosed
{(uint8_t[]){0x9f, 0x82, 0x9f, 0x81, 0x9f, 0x9f, 0xff, 0xff, 0xff, 0xff}, 10},
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
// The "argument" for the data item is missing bytes
// Positive integer missing 1 byte argument
{(uint8_t[]){0x18}, 1},
// Positive integer missing 2 byte argument
{(uint8_t[]){0x19}, 1},
// Positive integer missing 4 byte argument
{(uint8_t[]){0x1a}, 1},
// Positive integer missing 8 byte argument
{(uint8_t[]){0x1b}, 1},
// Positive integer missing 1 byte of 2 byte argument
{(uint8_t[]){0x19, 0x01}, 2},
// Positive integer missing 2 bytes of 4 byte argument
{(uint8_t[]){0x1a, 0x01, 0x02}, 3},
// Positive integer missing 1 bytes of 7 byte argument
{(uint8_t[]){0x1b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8},
// Negative integer missing 1 byte argument
{(uint8_t[]){0x38}, 1},
// Binary string missing 1 byte argument
{(uint8_t[]){0x58}, 1},
// Text string missing 1 byte argument
{(uint8_t[]){0x78}, 1},
// Array missing 1 byte argument
{(uint8_t[]){0x98}, 1},
// Map missing 1 byte argument
{(uint8_t[]){0xb8}, 1},
// Tag missing 1 byte argument
{(uint8_t[]){0xd8}, 1},
// Simple missing 1 byte argument
{(uint8_t[]){0xf8}, 1},
// Half-precision missing 1 byte
{(uint8_t[]){0xf9, 0x00}, 2},
// Float missing 2 bytes
{(uint8_t[]){0xfa, 0x00, 0x00}, 3},
// Double missing 5 bytes
{(uint8_t[]){0xfb, 0x00, 0x00, 0x00}, 4},
// Breaks must not occur in definite length arrays and maps
// Array of length 1 with sole member replaced by a break
{(uint8_t[]){0x81, 0xff}, 2},
// Array of length 2 with 2nd member replaced by a break
{(uint8_t[]){0x82, 0x00, 0xff}, 3},
// Map of length 1 with sole member label replaced by a break
{(uint8_t[]){0xa1, 0xff}, 2},
// Map of length 1 with sole member label replaced by break
// Alternate representation that some decoders handle difference
{(uint8_t[]){0xa1, 0xff, 0x00}, 3},
// Array of length 1 with 2nd member value replaced by a break
{(uint8_t[]){0xa1, 0x00, 0xff}, 3},
// Map of length 2 with 2nd entry label replaced by a break
{(uint8_t[]){0xa2, 0x00, 0x00, 0xff, 0x00}, 5},
// Map of length 2 with 2nd entry value replaced by a break
{(uint8_t[]){0xa2, 0x00, 0x00, 0x00, 0xff}, 5},
// Breaks must not occur on their own out of an indefinite length
// data item
// A bare break is not well formed
{(uint8_t[]){0xff}, 1},
// A bare break after a zero length definite length array
{(uint8_t[]){0x80, 0xff}, 2},
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
// A bare break after a zero length indefinite length map
{(uint8_t[]){0x9f, 0xff, 0xff}, 3},
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
// Forbidden two-byte encodings of simple types
// Must use 0xe0 instead
{(uint8_t[]){0xf8, 0x00}, 2},
// Should use 0xe1 instead
{(uint8_t[]){0xf8, 0x01}, 2},
// Should use 0xe2 instead
{(uint8_t[]){0xf8, 0x02}, 2},
// Should use 0xe3 instead
{(uint8_t[]){0xf8, 0x03}, 2},
// Should use 0xe4 instead
{(uint8_t[]){0xf8, 0x04}, 2},
// Should use 0xe5 instead
{(uint8_t[]){0xf8, 0x05}, 2},
// Should use 0xe6 instead
{(uint8_t[]){0xf8, 0x06}, 2},
// Should use 0xe7 instead
{(uint8_t[]){0xf8, 0x07}, 2},
// Should use 0xe8 instead
{(uint8_t[]){0xf8, 0x08}, 2},
// Should use 0xe9 instead
{(uint8_t[]){0xf8, 0x09}, 2},
// Should use 0xea instead
{(uint8_t[]){0xf8, 0x0a}, 2},
// Should use 0xeb instead
{(uint8_t[]){0xf8, 0x0b}, 2},
// Should use 0xec instead
{(uint8_t[]){0xf8, 0x0c}, 2},
// Should use 0xed instead
{(uint8_t[]){0xf8, 0x0d}, 2},
// Should use 0xee instead
{(uint8_t[]){0xf8, 0x0e}, 2},
// Should use 0xef instead
{(uint8_t[]){0xf8, 0x0f}, 2},
// Should use 0xf0 instead
{(uint8_t[]){0xf8, 0x10}, 2},
// Should use 0xf1 instead
{(uint8_t[]){0xf8, 0x11}, 2},
// Should use 0xf2 instead
{(uint8_t[]){0xf8, 0x12}, 2},
// Must use 0xf3 instead
{(uint8_t[]){0xf8, 0x13}, 2},
// Must use 0xf4 instead
{(uint8_t[]){0xf8, 0x14}, 2},
// Must use 0xf5 instead
{(uint8_t[]){0xf8, 0x15}, 2},
// Must use 0xf6 instead
{(uint8_t[]){0xf8, 0x16}, 2},
// Must use 0xf7 instead
{(uint8_t[]){0xf8, 0x17}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x18}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x19}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x1a}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x1b}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x1c}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x1d}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x1e}, 2},
// Reserved (as defined in RFC 8126), considered not-well-formed
{(uint8_t[]){0xf8, 0x1f}, 2},
// Integers with "argument" equal to an indefinite length
// Positive integer with "argument" an indefinite length
{(uint8_t[]){0x1f}, 1},
// Negative integer with "argument" an indefinite length
{(uint8_t[]){0x3f}, 1},
#ifndef QCBOR_DISABLE_TAGS
// CBOR tag with "argument" an indefinite length
{(uint8_t[]){0xdf, 0x00}, 2},
// CBOR tag with "argument" an indefinite length alternate vector
{(uint8_t[]){0xdf}, 1},
#endif /* QCBOR_DISABLE_TAGS */
// Missing content bytes from a definite length string
// A byte string is of length 1 without the 1 byte
{(uint8_t[]){0x41}, 1},
// A text string is of length 1 without the 1 byte
{(uint8_t[]){0x61}, 1},
// Byte string should have 65520 bytes, but has one
{(uint8_t[]){0x59, 0xff, 0xf0, 0x00}, 6},
// Byte string should have 65520 bytes, but has one
{(uint8_t[]){0x79, 0xff, 0xf0, 0x00}, 6},
// Use of unassigned additional information values
// Major type positive integer with reserved value 28
{(uint8_t[]){0x1c}, 1},
// Major type positive integer with reserved value 29
{(uint8_t[]){0x1d}, 1},
// Major type positive integer with reserved value 30
{(uint8_t[]){0x1e}, 1},
// Major type negative integer with reserved value 28
{(uint8_t[]){0x3c}, 1},
// Major type negative integer with reserved value 29
{(uint8_t[]){0x3d}, 1},
// Major type negative integer with reserved value 30
{(uint8_t[]){0x3e}, 1},
// Major type byte string with reserved value 28 length
{(uint8_t[]){0x5c}, 1},
// Major type byte string with reserved value 29 length
{(uint8_t[]){0x5d}, 1},
// Major type byte string with reserved value 30 length
{(uint8_t[]){0x5e}, 1},
// Major type text string with reserved value 28 length
{(uint8_t[]){0x7c}, 1},
// Major type text string with reserved value 29 length
{(uint8_t[]){0x7d}, 1},
// Major type text string with reserved value 30 length
{(uint8_t[]){0x7e}, 1},
// Major type array with reserved value 28 length
{(uint8_t[]){0x9c}, 1},
// Major type array with reserved value 29 length
{(uint8_t[]){0x9d}, 1},
// Major type array with reserved value 30 length
{(uint8_t[]){0x9e}, 1},
// Major type map with reserved value 28 length
{(uint8_t[]){0xbc}, 1},
// Major type map with reserved value 29 length
{(uint8_t[]){0xbd}, 1},
// Major type map with reserved value 30 length
{(uint8_t[]){0xbe}, 1},
// Major type tag with reserved value 28 length
{(uint8_t[]){0xdc}, 1},
// Major type tag with reserved value 29 length
{(uint8_t[]){0xdd}, 1},
// Major type tag with reserved value 30 length
{(uint8_t[]){0xde}, 1},
// Major type simple with reserved value 28 length
{(uint8_t[]){0xfc}, 1},
// Major type simple with reserved value 29 length
{(uint8_t[]){0xfd}, 1},
// Major type simple with reserved value 30 length
{(uint8_t[]){0xfe}, 1},
// Maps must have an even number of data items (key & value)
// Map with 1 item when it should have 2
{(uint8_t[]){0xa1, 0x00}, 2},
// Map with 3 item when it should have 4
{(uint8_t[]){0xa2, 0x00, 0x00, 0x00}, 2},
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
// Map with 1 item when it should have 2
{(uint8_t[]){0xbf, 0x00, 0xff}, 3},
// Map with 3 item when it should have 4
{(uint8_t[]){0xbf, 0x00, 0x00, 0x00, 0xff}, 5},
#endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
};
#endif /* not_well_formed_cbor_h */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,343 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
Copyright (c) 2018-2024, Laurence Lundblade.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors, nor the name "Laurence Lundblade" may be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
============================================================================*/
#ifndef __QCBOR__qcbort_decode_tests__
#define __QCBOR__qcbort_decode_tests__
#include <stdint.h>
/*
Notes:
- All the functions in qcbor_decode.h are called once in the aggregation
of all the tests below.
- All the types that are supported are given as input and parsed by these tests
- There is some hostile input such as invalid lengths and CBOR too complex
and types this parser doesn't handle
*/
/*
Parse a well-known set of integers including those around the boundaries and
make sure the expected values come out
*/
int32_t IntegerValuesParseTest(void);
/*
Decode a simple CBOR encoded array and make sure it returns all the correct values.
This is a decode test.
*/
int32_t SimpleArrayTest(void);
/*
Tests with empty maps and arrays
*/
int32_t EmptyMapsAndArraysTest(void);
/*
Make sure a maximally deep array can be parsed and that the
reported nesting level is correct. This uses test vector
of CBOR encoded data with a depth of 10. This a parse test.
*/
int32_t ParseDeepArrayTest(void);
/*
See that the correct error is reported when parsing
an array of depth 11, one too large.
*/
int32_t ParseTooDeepArrayTest(void);
/*
Try to parse some legit CBOR types that this parsers
doesn't support.
*/
int32_t UnsupportedCBORDecodeTest(void);
/*
This takes the encoded CBOR integers used in the above test and parses
it over and over with one more byte less each time. It should fail
every time on incorrect CBOR input. This is a hostile input decode test.
*/
int32_t ShortBufferParseTest(void);
/*
Same as ShortBufferParseTest, but with a different encoded CBOR input.
It is another hostile input test
*/
int32_t ShortBufferParseTest2(void);
/*
Parses the somewhat complicated CBOR MAP and makes sure all the correct
values parse out. About 15 values are tested. This is a decode test.
*/
int32_t ParseMapTest(void);
/*
Parses a map that contains a zero-length map as value.
*/
int32_t ParseEmptyMapInMapTest(void);
/*
Test the decoder mode where maps are treated as arrays.
*/
int32_t ParseMapAsArrayTest(void);
/*
Test parsing of some simple values like true, false, null...
*/
int32_t SimpleValueDecodeTests(void);
/*
This tests all the not-well-formed CBOR from the CBOR RFC.
(This is the CBORbis RFC which is not yet published at the
time this test was added).
*/
int32_t NotWellFormedTests(void);
/*
Tests a number of failure cases on bad CBOR to get the right error code
*/
int32_t DecodeFailureTests(void);
/*
Parses all possible inputs that are two bytes long. Main point
is that the test doesn't crash as it doesn't evaluate the
input for correctness in any way.
(Parsing all possible 3 byte strings takes too long on all but
very fast machines).
*/
int32_t ComprehensiveInputTest(void);
/*
Parses all possible inputs that are four bytes long. Main point
is that the test doesn't crash as it doesn't evaluate the
input for correctness in any way. This runs very slow, so it
is only practical as a once-in-a-while regression test on
fast machines.
*/
int32_t BigComprehensiveInputTest(void);
/*
Test the date types -- epoch and strings
*/
int32_t DateParseTest(void);
/*
Test spiffy date decoding functions
*/
int32_t SpiffyDateDecodeTest(void);
/*
Test decode of CBOR tagging like the CBOR magic number and many others.
*/
int32_t TagNumberDecodeTest(void);
/*
Parse some big numbers, positive and negative
*/
int32_t BignumDecodeTest(void);
/*
Test of mode where only string labels are allowed
*/
int32_t StringDecoderModeFailTest(void);
/*
Parse some nested maps
*/
int32_t NestedMapTest(void);
/*
Parse maps with indefinite lengths
*/
int32_t NestedMapTestIndefLen(void);
/*
Parse some maps and arrays with indefinite lengths.
Includes some error cases.
*/
int32_t IndefiniteLengthArrayMapTest(void);
/*
Parse indefinite length strings. Uses
MemPool. Includes error cases.
*/
int32_t IndefiniteLengthStringTest(void);
/*
Test deep nesting of indefinite length
maps and arrays including too deep.
*/
int32_t IndefiniteLengthNestTest(void);
/*
Test parsing strings were all strings, not
just indefinite length strings, are
allocated. Includes error test cases.
*/
int32_t AllocAllStringsTest(void);
/*
Direct test of MemPool string allocator
*/
int32_t MemPoolTest(void);
/*
Test the setting up of an external string allocator.
*/
int32_t SetUpAllocatorTest(void);
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/*
Test decoding of decimal fractions and big floats, both of which are
made up of an exponent and mantissa.
*/
int32_t ExponentAndMantissaDecodeTests(void);
/*
Hostile input tests for decimal fractions and big floats.
*/
int32_t ExponentAndMantissaDecodeFailTests(void);
#endif /* ! QCBOR_DISABLE_EXP_AND_MANTISSA */
int32_t EnterMapTest(void);
int32_t IntegerConvertTest(void);
/*
Tests decoding of CBOR Sequences defined in RFC 8742
*/
int32_t CBORSequenceDecodeTests(void);
/*
Tests for functions to safely convert integer types.
*/
int32_t IntToTests(void);
/*
Test the decoding of bstr-wrapped CBOR.
*/
int32_t EnterBstrTest(void);
/*
Test decoding of tagged types like UUID
*/
int32_t DecodeTaggedTypeTests(void);
/*
Test the detection of input that is too large. Requires
a special build that makes QCBOR_MAX_SIZE small.
*/
int32_t TooLargeInputTest(void);
/*
Test spiffy decoding of indefinite length strings.
*/
int32_t SpiffyStringTest(void);
/*
Test PeekNext().
*/
int32_t PeekAndRewindTest(void);
/*
Test decoding of booleans
*/
int32_t BoolTest(void);
/*
Test GitHub issue #134: decode an indefinite-length string with a zero-length first chunk.
*/
int32_t CBORTestIssue134(void);
/*
* Test the decode checking features for dCBOR, CDE and preferred.
*/
int32_t DecodeConformanceTests(void);
int32_t PreciseNumbersDecodeTest(void);
int32_t ErrorHandlingTests(void);
/*
* Test QCBORDecode_GetArray and QCBORDecode_GetMap
*/
int32_t GetMapAndArrayTest(void);
int32_t TellTests(void);
int32_t TagModesFanOutTest(void);
#endif /* defined(__QCBOR__qcbort_decode_tests__) */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
Copyright (c) 2018-2024, Laurence Lundblade.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors, nor the name "Laurence Lundblade" may be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=============================================================================*/
#ifndef __QCBOR__qcbor_encode_tests__
#define __QCBOR__qcbor_encode_tests__
#include <stdint.h>
/*
Notes:
- All the functions in qcbor_encode.h are called once in the aggregation of all
the tests below.
*/
/*
Most basic test.
*/
int32_t BasicEncodeTest(void);
/*
Encode lots of integer values, particularly around the boundary and
make sure they Match the expected binary output. Primarily an
encoding test.
*/
int32_t IntegerValuesTest1(void);
/*
Create nested arrays to the max depth allowed and make sure it
succeeds. This is an encoding test.
*/
int32_t ArrayNestingTest1(void);
/*
Create nested arrays to one more than the meax depth and make sure it
fails. This is an encoding test.
*/
int32_t ArrayNestingTest2(void);
/*
Encoding test. Create arrays to max depth and close one extra time
and look for correct error code
*/
int32_t ArrayNestingTest3(void);
/*
This tests the QCBOREncode_AddRaw() function by adding two chunks of
raw CBOR to an array and comparing with expected values.
*/
int32_t EncodeRawTest(void);
/*
This creates a somewhat complicated CBOR MAP and verifies it against
expected data. This is an encoding test.
*/
int32_t MapEncodeTest(void);
/*
* Big number encoding tests.
*/
int32_t BigNumEncodeTests(void);
/*
Encodes true, false and the like
*/
int32_t SimpleValuesTest1(void);
/*
Encodes basic maps and arrays with indefinite length
*/
int32_t IndefiniteLengthTest(void);
/*
Indefinite length arrays and maps use the 'magic' number 31, verify
that everything with length 31 still works properly
*/
int32_t EncodeLengthThirtyoneTest(void);
/*
* Tests Encoding most data formats that are supported.
*/
int32_t EncodeDateTest(void);
/*
Encodes particular data structure that a particular app will need...
*/
int32_t RTICResultsTest(void);
/*
Calls all public encode methods in qcbor_encode.h once.
*/
int32_t AllAddMethodsTest(void);
/*
The binary string wrapping of maps and arrays used by COSE
*/
int32_t BstrWrapTest(void);
/*
Test error cases for bstr wrapping encoding such as closing an open
array with CloseBstrWrap
*/
int32_t BstrWrapErrorTest(void);
/*
Test complicated nested bstr wrapping
*/
int32_t BstrWrapNestTest(void);
/*
Test encoding a COSE_Sign1 with bstr wrapping
*/
int32_t CoseSign1TBSTest(void);
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
/*
Test encoding of decimal fractions and big floats, both of which are
made up of an exponent and mantissa
*/
int32_t ExponentAndMantissaEncodeTests(void);
#endif /* ! QCBOR_DISABLE_EXP_AND_MANTISSA */
/*
Test the error cases when encoding CBOR such as buffer too large,
buffer too small, array nesting too deep. Aims to cover the error
codes returned when encoding CBOR
*/
int32_t EncodeErrorTests(void);
/*
Test QCBOREncode_EncodeHead(). This is a minimal test because every other
test here exercises it in some way.
*/
int32_t QCBORHeadTest(void);
/* Fully test QCBOREncode_OpenBytes(), QCBOREncode_CloseBytes()
* and friends.
*/
int32_t OpenCloseBytesTest(void);
/* Test map sorting */
int32_t SortMapTest(void);
#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
/* Test CBOR Deterministic Encoding */
int32_t CDETest(void);
/* Test "dCBOR" mode */
int32_t DCBORTest(void);
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
int32_t SubStringTest(void);
#endif /* defined(__QCBOR__qcbor_encode_tests__) */

View File

@@ -0,0 +1,383 @@
/*==============================================================================
run_tests.c -- test aggregator and results reporting
Copyright (c) 2018-2025, Laurence Lundblade. All rights reserved.
Copyright (c) 2021, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 9/30/18
=============================================================================*/
#include "run_tests.h"
#include "UsefulBuf.h"
#include <stdbool.h>
#include "float_tests.h"
#include "qcbor_decode_tests.h"
#include "qcbor_encode_tests.h"
#include "UsefulBuf_Tests.h"
// For size printing and some conditionals
#include "qcbor/qcbor_encode.h"
#include "qcbor/qcbor_decode.h"
#include "qcbor/qcbor_spiffy_decode.h"
/*
Test configuration
*/
typedef int32_t (test_fun_t)(void);
typedef const char * (test_fun2_t)(void);
#define TEST_ENTRY(test_name) {#test_name, test_name, true}
#define TEST_ENTRY_DISABLED(test_name) {#test_name, test_name, false}
typedef struct {
const char *szTestName;
test_fun_t *test_fun;
bool bEnabled;
} test_entry;
typedef struct {
const char *szTestName;
test_fun2_t *test_fun;
bool bEnabled;
} test_entry2;
static test_entry2 s_tests2[] = {
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
TEST_ENTRY(UBUTest_CopyUtil),
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
TEST_ENTRY(UOBTest_NonAdversarial),
TEST_ENTRY(TestBasicSanity),
TEST_ENTRY(UOBTest_BoundaryConditionsTest),
TEST_ENTRY(UBMacroConversionsTest),
TEST_ENTRY(UBUtilTests),
TEST_ENTRY(UIBTest_IntegerFormat),
TEST_ENTRY(UBAdvanceTest),
TEST_ENTRY(UOBExtraTests)
};
static test_entry s_tests[] = {
TEST_ENTRY(BigNumEncodeTests),
#ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
TEST_ENTRY(DecodeConformanceTests),
#endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
TEST_ENTRY(ErrorHandlingTests),
TEST_ENTRY(OpenCloseBytesTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
TEST_ENTRY(GetMapAndArrayTest),
TEST_ENTRY(TellTests),
TEST_ENTRY(ParseMapAsArrayTest),
#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
TEST_ENTRY(ArrayNestingTest3),
#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
TEST_ENTRY(SpiffyDateDecodeTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY(EnterBstrTest),
TEST_ENTRY(IntegerConvertTest),
TEST_ENTRY(EnterMapTest),
TEST_ENTRY(QCBORHeadTest),
TEST_ENTRY(EmptyMapsAndArraysTest),
TEST_ENTRY(NotWellFormedTests),
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
TEST_ENTRY(IndefiniteLengthNestTest),
TEST_ENTRY(IndefiniteLengthArrayMapTest),
TEST_ENTRY(NestedMapTestIndefLen),
#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
TEST_ENTRY(SimpleValueDecodeTests),
TEST_ENTRY(DecodeFailureTests),
TEST_ENTRY(EncodeRawTest),
TEST_ENTRY(RTICResultsTest),
TEST_ENTRY(MapEncodeTest),
TEST_ENTRY(ArrayNestingTest1),
TEST_ENTRY(ArrayNestingTest2),
TEST_ENTRY(EncodeDateTest),
TEST_ENTRY(SimpleValuesTest1),
TEST_ENTRY(IntegerValuesTest1),
TEST_ENTRY(AllAddMethodsTest),
TEST_ENTRY(ParseTooDeepArrayTest),
TEST_ENTRY(ComprehensiveInputTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
TEST_ENTRY(ParseMapTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY(BasicEncodeTest),
TEST_ENTRY(NestedMapTest),
TEST_ENTRY(BignumDecodeTest),
#ifndef QCBOR_DISABLE_TAGS
TEST_ENTRY(TagNumberDecodeTest),
TEST_ENTRY(DateParseTest),
TEST_ENTRY(DecodeTaggedTypeTests),
#endif /* ! QCBOR_DISABLE_TAGS */
TEST_ENTRY(ShortBufferParseTest2),
TEST_ENTRY(ShortBufferParseTest),
TEST_ENTRY(ParseDeepArrayTest),
TEST_ENTRY(SimpleArrayTest),
TEST_ENTRY(IntegerValuesParseTest),
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
TEST_ENTRY(AllocAllStringsTest),
TEST_ENTRY(MemPoolTest),
TEST_ENTRY(IndefiniteLengthStringTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
TEST_ENTRY(SpiffyStringTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY(SetUpAllocatorTest),
TEST_ENTRY(CBORTestIssue134),
#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
TEST_ENTRY(PreciseNumbersDecodeTest),
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
TEST_ENTRY(FloatValuesTests),
TEST_ENTRY(NaNPayloadsTest),
TEST_ENTRY(GeneralFloatEncodeTests),
TEST_ENTRY(GeneralFloatDecodeTests),
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
TEST_ENTRY(BstrWrapTest),
TEST_ENTRY(BstrWrapErrorTest),
TEST_ENTRY(BstrWrapNestTest),
TEST_ENTRY(CoseSign1TBSTest),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
TEST_ENTRY(StringDecoderModeFailTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
TEST_ENTRY_DISABLED(TooLargeInputTest),
TEST_ENTRY(EncodeErrorTests),
#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
TEST_ENTRY(IndefiniteLengthTest),
#endif /* ! QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
TEST_ENTRY(EncodeLengthThirtyoneTest),
TEST_ENTRY(CBORSequenceDecodeTests),
TEST_ENTRY(IntToTests),
#ifndef QCBOR_DISABLE_NON_INTEGER_LABELS
TEST_ENTRY(PeekAndRewindTest),
#endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
#ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
TEST_ENTRY(ExponentAndMantissaDecodeTests),
#ifndef QCBOR_DISABLE_TAGS
TEST_ENTRY(ExponentAndMantissaDecodeFailTests),
#endif /* ! QCBOR_DISABLE_TAGS */
TEST_ENTRY(ExponentAndMantissaEncodeTests),
#endif /* ! QCBOR_DISABLE_EXP_AND_MANTISSA */
TEST_ENTRY(SortMapTest),
#if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
TEST_ENTRY(CDETest),
TEST_ENTRY(DCBORTest),
#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
TEST_ENTRY(ParseEmptyMapInMapTest),
TEST_ENTRY(SubStringTest),
TEST_ENTRY(BoolTest),
TEST_ENTRY(TagModesFanOutTest)
};
/*
Convert a number up to 999999999 to a string. This is so sprintf doesn't
have to be linked in so as to minimized dependencies even in test code.
StringMem should be 12 bytes long, 9 for digits, 1 for minus and
1 for \0 termination.
*/
static const char *NumToString(int32_t nNum, UsefulBuf StringMem)
{
const int32_t nMax = 1000000000;
UsefulOutBuf OutBuf;
UsefulOutBuf_Init(&OutBuf, StringMem);
if(nNum < 0) {
UsefulOutBuf_AppendByte(&OutBuf, '-');
nNum = -nNum;
}
if(nNum > nMax-1) {
return "XXX";
}
bool bDidSomeOutput = false;
for(int32_t n = nMax; n > 0; n/=10) {
int nDigitValue = nNum/n;
if(nDigitValue || bDidSomeOutput){
bDidSomeOutput = true;
UsefulOutBuf_AppendByte(&OutBuf, (uint8_t)('0' + nDigitValue));
nNum -= nDigitValue * n;
}
}
if(!bDidSomeOutput){
UsefulOutBuf_AppendByte(&OutBuf, '0');
}
UsefulOutBuf_AppendByte(&OutBuf, '\0');
return UsefulOutBuf_GetError(&OutBuf) ? "" : StringMem.ptr;
}
/*
Public function. See run_test.h.
*/
int RunTestsQCBOR(const char *szTestNames[],
OutputStringCB pfOutput,
void *poutCtx,
int *pNumTestsRun)
{
int nTestsFailed = 0;
int nTestsRun = 0;
UsefulBuf_MAKE_STACK_UB(StringStorage, 12);
test_entry2 *t2;
const test_entry2 *s_tests2_end = s_tests2 + sizeof(s_tests2)/sizeof(test_entry2);
for(t2 = s_tests2; t2 < s_tests2_end; t2++) {
if(szTestNames[0]) {
// Some tests have been named
const char **szRequestedNames;
for(szRequestedNames = szTestNames; *szRequestedNames; szRequestedNames++) {
if(!strcmp(t2->szTestName, *szRequestedNames)) {
break; // Name matched
}
}
if(*szRequestedNames == NULL) {
// Didn't match this test
continue;
}
} else {
// no tests named, but don't run "disabled" tests
if(!t2->bEnabled) {
// Don't run disabled tests when all tests are being run
// as indicated by no specific test names being given
continue;
}
}
const char * szTestResult = (t2->test_fun)();
nTestsRun++;
if(pfOutput) {
(*pfOutput)(t2->szTestName, poutCtx, 0);
}
if(szTestResult) {
if(pfOutput) {
(*pfOutput)(" FAILED (returned ", poutCtx, 0);
(*pfOutput)(szTestResult, poutCtx, 0);
(*pfOutput)(")", poutCtx, 1);
}
nTestsFailed++;
} else {
if(pfOutput) {
(*pfOutput)( " PASSED", poutCtx, 1);
}
}
}
test_entry *t;
const test_entry *s_tests_end = s_tests + sizeof(s_tests)/sizeof(test_entry);
for(t = s_tests; t < s_tests_end; t++) {
if(szTestNames[0]) {
// Some tests have been named
const char **szRequestedNames;
for(szRequestedNames = szTestNames; *szRequestedNames; szRequestedNames++) {
if(!strcmp(t->szTestName, *szRequestedNames)) {
break; // Name matched
}
}
if(*szRequestedNames == NULL) {
// Didn't match this test
continue;
}
} else {
// no tests named, but don't run "disabled" tests
if(!t->bEnabled) {
// Don't run disabled tests when all tests are being run
// as indicated by no specific test names being given
continue;
}
}
int32_t nTestResult = (t->test_fun)();
nTestsRun++;
if(pfOutput) {
(*pfOutput)(t->szTestName, poutCtx, 0);
}
if(nTestResult) {
if(pfOutput) {
(*pfOutput)(" FAILED (returned ", poutCtx, 0);
(*pfOutput)(NumToString(nTestResult, StringStorage), poutCtx, 0);
(*pfOutput)(")", poutCtx, 1);
}
nTestsFailed++;
} else {
if(pfOutput) {
(*pfOutput)( " PASSED", poutCtx, 1);
}
}
}
if(pNumTestsRun) {
*pNumTestsRun = nTestsRun;
}
if(pfOutput) {
(*pfOutput)( "SUMMARY: ", poutCtx, 0);
(*pfOutput)( NumToString(nTestsRun, StringStorage), poutCtx, 0);
(*pfOutput)( " tests run; ", poutCtx, 0);
(*pfOutput)( NumToString(nTestsFailed, StringStorage), poutCtx, 0);
(*pfOutput)( " tests failed", poutCtx, 1);
}
return nTestsFailed;
}
/*
Public function. See run_test.h.
*/
static void PrintSize(const char *szWhat,
uint32_t uSize,
OutputStringCB pfOutput,
void *pOutCtx)
{
UsefulBuf_MAKE_STACK_UB(buffer, 20);
(*pfOutput)(szWhat, pOutCtx, 0);
(*pfOutput)(" ", pOutCtx, 0);
(*pfOutput)(NumToString((int32_t)uSize, buffer), pOutCtx, 0);
(*pfOutput)("", pOutCtx, 1);
}
/*
Public function. See run_test.h.
*/
void PrintSizesQCBOR(OutputStringCB pfOutput, void *pOutCtx)
{
// These will never be large so cast is safe
PrintSize("sizeof(QCBORTrackNesting)", (uint32_t)sizeof(QCBORTrackNesting), pfOutput, pOutCtx);
PrintSize("sizeof(QCBOREncodeContext)", (uint32_t)sizeof(QCBOREncodeContext), pfOutput, pOutCtx);
PrintSize("sizeof(QCBORDecodeNesting)", (uint32_t)sizeof(QCBORDecodeNesting), pfOutput, pOutCtx);
PrintSize("sizeof(QCBORDecodeContext)", (uint32_t)sizeof(QCBORDecodeContext), pfOutput, pOutCtx);
PrintSize("sizeof(QCBORItem)", (uint32_t)sizeof(QCBORItem), pfOutput, pOutCtx);
(*pfOutput)("", pOutCtx, 1);
}

View File

@@ -0,0 +1,69 @@
/*==============================================================================
run_tests.h -- test aggregator and results reporting
Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created 9/30/18
=============================================================================*/
/**
@file run_tests.h
*/
/**
@brief Type for function to output a text string
@param[in] szString The string to output
@param[in] pOutCtx A context pointer; NULL if not needed
@param[in] bNewline If non-zero, output a newline after the string
This is a prototype of a function to be passed to RunTests() to
output text strings.
This can be implemented with stdio (if available) using a straight
call to fputs() where the FILE * is passed as the pOutCtx as shown in
the example code below. This code is for Linux where the newline is
a \\n. Windows usually prefers \\r\\n.
@code
static void fputs_wrapper(const char *szString, void *pOutCtx, int bNewLine)
{
fputs(szString, (FILE *)pOutCtx);
if(bNewLine) {
fputs("\n", pOutCtx);
}
}
@endcode
*/
typedef void (*OutputStringCB)(const char *szString, void *pOutCtx, int bNewline);
/**
@brief Runs the QCBOR tests.
@param[in] szTestNames An argv-style list of test names to run. If
empty, all are run.
@param[in] pfOutput Function that is called to output text strings.
@param[in] pOutCtx Context pointer passed to output function.
@param[out] pNumTestsRun Returns the number of tests run. May be NULL.
@return The number of tests that failed. Zero means overall success.
*/
int RunTestsQCBOR(const char *szTestNames[],
OutputStringCB pfOutput,
void *pOutCtx,
int *pNumTestsRun);
/**
@brief Print sizes of encoder-decoder contexts.
@param[in] pfOutput Function that is called to output text strings.
@param[in] pOutCtx Context pointer passed to output function.
*/
void PrintSizesQCBOR(OutputStringCB pfOutput, void *pOutCtx);

View File

@@ -0,0 +1,260 @@
/* =========================================================================
ub-example.c -- Example code for UsefulBuf
Copyright (c) 2022, Laurence Lundblade. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 4/8/22
========================================================================== */
#include "ub-example.h"
#include "UsefulBuf.h"
/*
* A considerable number of the security issues with C code come from
* mistakes made with pointers and lengths. UsefulBuf adopts a
* convention that a pointer and length *always* go together to help
* mitigate this. With UsefulBuf there are never pointers without
* lengths, so you always know how big a buffer or some binary data
* is.
*
* C99 allows passing structures so a structure is used. Compilers are
* smart these days so the object code produced is little different
* than passing two separate parameters. Passing structures also makes
* the interfaces prettier. Assignments of structures also can make
* code prettier.
*
* ALong with the UsefulBuf structure, there are a bunch of (tested!)
* functions to manipulate them so code using it may have no pointer
* manipulation at all.
*
* Constness is also a useful and desirous thing. See
* https://stackoverflow.com/questions/117293/use-of-const-for-function-parameters
* Keeping const distinct from non-const is helpful when reading the
* code and helps avoid some coding mistakes. In this example the
* buffers filled in with data are const and the ones that are
* to-be-filled in are not const.
*
* This contrived example copies data from input to output expanding
* bytes with the value 'x' to 'xx'.
*
* Input -- This is the pointer and length of the input, the bytes to
* copy. Note that UsefulBufC.ptr is a const void * indicating that
* input data won't be changed by this function. There is a "C" in
* "UsefulBufC "to indicate the value is const. The length here is
* the length of the valid input data. Note also that the parameter
* Input is const, so this is fully const and clearly an [in]
* parameter.
*
* OutputBuffer -- This is a pointer and length of the memory to be
* used to store the output. The correct length here is critical for
* code security. Note that UsefulBuf.ptr is void *, it is not const
* indicating data can be written to it. Note that the parameter
* itself *is* const indicating that the code below will not point
* this to some other buffer or change the length and clearly marking
* it as an [in] parameter.
*
* Output -- This is the interesting and unusual one. To stay
* consistent with always pairing a length and a pointer, this is
* returned as a UsefulBuC. Also, to stay consistent with valid data
* being const, it is a UsefulBufC, not a UsefulBuf. It is however, an
* [out] parameter so the parameter is a pointer to a UsefulBufC.
*
* In this case and most cases, the pointer in Output->ptr will be the
* same as OutputBuffer.ptr. This may seem redundant, but there are a
* few reasons for it. First, is the goal of always pairing a pointer
* and a length. Second is being more strict and correct with
* constness. Third is the code hygiene and clarity of having
* variables for to-be-filled buffers be distinct from those
* containing valid data. Fourth, there are no [in,out] parameters,
* only [in] parameters and [out] parameters (the to-be-filled-in
* buffer is considered an [in] parameter).
*
* Note that the compiler will be smart and should generate pretty
* much the same code as for a traditional interface. On x86 with
* gcc-11 and no stack guards, the UB code is 81 bytes and the
* traditional code is 77 bytes.
*
* Finally, this supports computing of the length of the would-be
* output without actually doing any outputting. Pass {NULL, SIZE_MAX}
* for the OutputBuffer and the length will be returned in Output.
*/
int
ExpandxUB(const UsefulBufC Input,
const UsefulBuf OutputBuffer,
UsefulBufC *Output)
{
size_t uInputPosition;
size_t uOutputPosition;
uOutputPosition = 0;
/* Loop over all the bytes in Input */
for(uInputPosition = 0; uInputPosition < Input.len; uInputPosition++) {
const uint8_t uInputByte = ((const uint8_t*)Input.ptr)[uInputPosition];
/* Copy every byte */
if(OutputBuffer.ptr != NULL) {
((uint8_t *)OutputBuffer.ptr)[uOutputPosition] = uInputByte;
}
uOutputPosition++;
if(uOutputPosition >= OutputBuffer.len) {
return -1;
}
/* Double output 'x' because that is what this contrived example does */
if(uInputByte== 'x') {
if(OutputBuffer.ptr != NULL) {
((uint8_t *)OutputBuffer.ptr)[uOutputPosition] = 'x';
}
uOutputPosition++;
if(uOutputPosition >= OutputBuffer.len) {
return -1;
}
}
}
*Output = (UsefulBufC){OutputBuffer.ptr, uOutputPosition};
return 0; /* success */
}
/* This is the more tradional way to implement this. */
int
ExpandxTraditional(const uint8_t *pInputPointer,
const size_t uInputLength,
uint8_t *pOutputBuffer,
const size_t uOutputBufferLength,
size_t *puOutputLength)
{
size_t uInputPosition;
size_t uOutputPosition;
uOutputPosition = 0;
/* Loop over all the bytes in Input */
for(uInputPosition = 0; uInputPosition < uInputLength; uInputPosition++) {
const uint8_t uInputByte = ((const uint8_t*)pInputPointer)[uInputPosition];
/* Copy every byte */
if(pOutputBuffer != NULL) {
((uint8_t *)pOutputBuffer)[uOutputPosition] = uInputByte;
}
uOutputPosition++;
if(uOutputPosition >= uOutputBufferLength) {
return -1;
}
/* Double output 'x' because that is what this contrived example does */
if(uInputByte== 'x') {
if(pOutputBuffer != NULL) {
((uint8_t *)pOutputBuffer)[uOutputPosition] = 'x';
}
uOutputPosition++;
if(uOutputPosition >= uOutputBufferLength) {
return -1;
}
}
}
*puOutputLength = uOutputPosition;
return 0; /* success */
}
/*
* Here's an example of going from a traditional interface
* interface to a UsefulBuf interface.
*/
int
ExpandxTraditionalAdaptor(const uint8_t *pInputPointer,
size_t uInputLength,
uint8_t *pOutputBuffer,
size_t uOutputBufferLength,
size_t *puOutputLength)
{
UsefulBufC Input;
UsefulBuf OutputBuffer;
UsefulBufC Output;
int nReturn;
Input = (UsefulBufC){pInputPointer, uInputLength};
OutputBuffer = (UsefulBuf){pOutputBuffer, uOutputBufferLength};
nReturn = ExpandxUB(Input, OutputBuffer, &Output);
*puOutputLength = Output.len;
return nReturn;
}
/* Here's an example for going from a UsefulBuf interface
to a traditional interface. */
int
ExpandxUBAdaptor(const UsefulBufC Input,
const UsefulBuf OutputBuffer,
UsefulBufC *Output)
{
Output->ptr = OutputBuffer.ptr;
return ExpandxTraditional(Input.ptr, Input.len,
OutputBuffer.ptr, OutputBuffer.len,
&(Output->len));
}
#define INPUT "xyz123xyz"
int32_t RunUsefulBufExample(void)
{
/* ------------ UsefulBuf examples ------------- */
UsefulBufC Input = UsefulBuf_FROM_SZ_LITERAL(INPUT);
/* This macros makes a 20 byte buffer on the stack. It also makes
* a UsefulBuf on the stack. It sets up the UsefulBuf to point to
* the 20 byte buffer and sets it's length to 20 bytes. This
* is the empty, to-be-filled in memory for the output. It is not
* const. */
MakeUsefulBufOnStack(OutBuf, sizeof(INPUT) * 2);
/* This is were the pointer and the length of the completed output
* will be placed. Output.ptr is a pointer to const bytes. */
UsefulBufC Output;
ExpandxUB(Input, OutBuf, &Output);
ExpandxUBAdaptor(Input, OutBuf, &Output);
/* ------ Get Size example -------- */
ExpandxUB(Input, (UsefulBuf){NULL, SIZE_MAX}, &Output);
/* Size is in Output.len */
/* ---------- Traditional examples (for comparison) --------- */
uint8_t puBuffer[sizeof(INPUT) * 2];
size_t uOutputSize;
ExpandxTraditional((const uint8_t *)INPUT, sizeof(INPUT),
puBuffer, sizeof(puBuffer),
&uOutputSize);
ExpandxTraditionalAdaptor((const uint8_t *)INPUT, sizeof(INPUT),
puBuffer, sizeof(puBuffer),
&uOutputSize);
return 0;
}

View File

@@ -0,0 +1,19 @@
/* =========================================================================
ub-example.h -- Example code for UsefulBuf
Copyright (c) 2022, Laurence Lundblade. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
See BSD-3-Clause license in file named "LICENSE"
Created on 4/8/22
========================================================================== */
#ifndef ub_example_h
#define ub_example_h
#include <stdint.h>
int32_t RunUsefulBufExample(void);
#endif /* ub_example_h */

View File

@@ -0,0 +1,130 @@
name: CI
on: [push, pull_request]
jobs:
main:
strategy:
fail-fast: false
matrix:
c-compiler: [gcc, clang]
config:
# OpenSSL 1.1.1 (Ubuntu 20.04)
- os-image: ubuntu-latest
container: ubuntu:20.04
crypto-provider: OpenSSL
# OpenSSL 3.0 (Ubuntu 22.04)
- os-image: ubuntu-latest
container: ubuntu:22.04
crypto-provider: OpenSSL
- os-image: ubuntu-latest
container: ubuntu:20.04
crypto-provider: MbedTLS
crypto-provider-version: 'v2.28.0'
crypto-provider-extra: ''
crypto-provider-build-extra: ''
- os-image: ubuntu-latest
container: ubuntu:20.04
crypto-provider: MbedTLS
crypto-provider-version: 'v3.1.0'
crypto-provider-extra: ''
crypto-provider-build-extra: ''
- os-image: ubuntu-latest
container: ubuntu:20.04
crypto-provider: MbedTLS
crypto-provider-version: 'v3.4.0'
crypto-provider-extra: ''
crypto-provider-build-extra: ''
- os-image: ubuntu-latest
container: ubuntu:20.04
crypto-provider: MbedTLS
crypto-provider-version: 'v3.4.0'
crypto-provider-extra: ''
crypto-provider-build-extra: 'python3 scripts/config.py set MBEDTLS_ECP_RESTARTABLE'
- os-image: ubuntu-latest
container: ubuntu:20.04
crypto-provider: Test
name: ${{ matrix.config.crypto-provider }} ${{ matrix.config.crypto-provider-version }} • ${{ matrix.c-compiler }} • ${{ matrix.config.container }}
runs-on: ${{ matrix.config.os-image }}
container: ${{ matrix.config.container }}
steps:
- uses: actions/checkout@v3
- name: Install build tools
run: |
set -ex
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y build-essential cmake python3 ${{ matrix.c-compiler }} \
python3-jinja2 python3-jsonschema
echo "CC=${{ matrix.c-compiler }}" >> $GITHUB_ENV
- name: Install OpenSSL
if: matrix.config.crypto-provider == 'OpenSSL'
run: apt-get install -y libssl-dev
- name: Fetch MbedTLS
if: matrix.config.crypto-provider == 'MbedTLS'
uses: actions/checkout@v3
with:
repository: ARMmbed/mbedtls
ref: ${{ matrix.config.crypto-provider-version }}
path: mbedtls
- name: Install MbedTLS
if: matrix.config.crypto-provider == 'MbedTLS'
run: |
cd mbedtls
${{ matrix.config.crypto-provider-build-extra }}
make -j $(nproc)
make install
- name: Fetch QCBOR
uses: actions/checkout@v3
with:
repository: laurencelundblade/QCBOR
path: QCBOR
- name: Install QCBOR
run: |
cd QCBOR
make -j$(nproc)
make install
- name: Build t_cose
run: |
set -ex
mkdir build
cd build
cmake -DCRYPTO_PROVIDER=${{ matrix.config.crypto-provider }} \
${{ matrix.config.crypto-provider-extra }} \
${{ matrix.config.defines }} \
..
make -j $(nproc)
- name: Run examples
run: build/t_cose_examples
- name: Run tests
if: matrix.config.crypto-provider == 'MbedTLS' &&
matrix.config.crypto-provider-version == 'v3.4.0' &&
matrix.config.crypto-provider-build-extra == ''
# This Mbed TLS version has the option of restartable ECP, however it is
# not turned on. In this case the restartable testcase should fail.
run: build/t_cose_test | grep 'restart_test_2_step FAILED'
- name: Run tests
if: ${{ ! ( matrix.config.crypto-provider == 'MbedTLS' &&
matrix.config.crypto-provider-version == 'v3.4.0' &&
matrix.config.crypto-provider-build-extra == '' ) }}
run: build/t_cose_test

View File

@@ -0,0 +1,33 @@
name: GitHub Pages
on:
push:
branches:
- "master"
workflow_dispatch:
permissions: read-all
jobs:
main:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- name: Install Doxygen
run: sudo apt-get install doxygen
- name: Run Doxygen
run: doxygen doxygen/t_cose_doxyfile
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: doxygen/output/html
publish_branch: gh-pages
force_orphan: true

View File

@@ -0,0 +1,64 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# Compiled binaries
t_cose_test
t_cose_basic_example_ossl
t_cose_basic_example_psa
t_cose_encryption_example_psa
# CMake build folder
build/
# Doxygen output folder
doxygen/output

View File

@@ -0,0 +1,174 @@
cmake_minimum_required(VERSION 3.12)
project(t_cose
DESCRIPTION "t_cose"
LANGUAGES C
VERSION 1.0.1)
# Constants
set(CRYPTO_PROVIDERS "OpenSSL" "MbedTLS" "Test")
# Project options
set(CRYPTO_PROVIDER "OpenSSL" CACHE STRING "The crypto provider to use: ${CRYPTO_PROVIDERS}")
set(BUILD_TESTS ON CACHE BOOL "Build tests")
set(BUILD_EXAMPLES ON CACHE BOOL "Build examples")
if (NOT CRYPTO_PROVIDER IN_LIST CRYPTO_PROVIDERS)
message(FATAL_ERROR "CRYPTO_PROVIDER must be one of ${CRYPTO_PROVIDERS}")
endif()
# Built-in CMake options
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Create shared instead of static libraries")
# Used in find_package() calls as preferred search paths
set(QCBOR_ROOT "" CACHE PATH "Installation prefix of QCBOR")
set(MbedTLS_ROOT "" CACHE PATH "Installation prefix of MbedTLS")
set(OpenSSL_ROOT "" CACHE PATH "Installation prefix of OpenSSL")
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "No build type selected, defaulting to Release")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
if(CRYPTO_PROVIDER STREQUAL "MbedTLS")
find_package(MbedTLS REQUIRED)
set(CRYPTO_LIBRARY MbedTLS::MbedCrypto)
set(CRYPTO_COMPILE_DEFS -DT_COSE_USE_PSA_CRYPTO=1 ${CC_DEFINES})
set(CRYPTO_ADAPTER_SRC crypto_adapters/t_cose_psa_crypto.c)
elseif(CRYPTO_PROVIDER STREQUAL "OpenSSL")
find_package(OpenSSL REQUIRED)
set(CRYPTO_LIBRARY OpenSSL::Crypto)
set(CRYPTO_COMPILE_DEFS -DT_COSE_USE_OPENSSL_CRYPTO=1)
set(CRYPTO_ADAPTER_SRC crypto_adapters/t_cose_openssl_crypto.c)
elseif(CRYPTO_PROVIDER STREQUAL "Test")
add_library(b_con_hash crypto_adapters/b_con_hash/sha256.c)
target_include_directories(b_con_hash PUBLIC crypto_adapters/b_con_hash)
set(CRYPTO_LIBRARY b_con_hash)
set(CRYPTO_COMPILE_DEFS -DT_COSE_USE_B_CON_SHA256 -DT_COSE_ENABLE_HASH_FAIL_TEST)
set(CRYPTO_ADAPTER_SRC crypto_adapters/t_cose_test_crypto.c)
else()
message(FATAL_ERROR "Bug!")
endif()
# Global compile options applying to all targets
add_compile_options(-pedantic -Wall)
set(T_COSE_SRC_COMMON
src/t_cose_sign1_sign.c
src/t_cose_parameters.c
src/t_cose_sign1_verify.c
src/t_cose_util.c
src/t_cose_key.c
src/t_cose_sign_sign.c
src/t_cose_mac_compute.c
src/t_cose_signature_sign_main.c
src/t_cose_signature_sign_restart.c
src/t_cose_signature_sign_eddsa.c
src/t_cose_sign_verify.c
src/t_cose_mac_validate.c
src/t_cose_signature_verify_main.c
src/t_cose_signature_verify_eddsa.c
src/t_cose_encrypt_enc.c
src/t_cose_encrypt_dec.c
src/t_cose_recipient_dec_keywrap.c
src/t_cose_recipient_enc_keywrap.c
src/t_cose_recipient_dec_esdh.c
src/t_cose_recipient_enc_esdh.c
src/t_cose_qcbor_gap.c
)
find_package(QCBOR REQUIRED)
add_library(t_cose ${T_COSE_SRC_COMMON} ${CRYPTO_ADAPTER_SRC})
target_compile_options(t_cose PRIVATE -ffunction-sections)
target_compile_definitions(t_cose PRIVATE ${CRYPTO_COMPILE_DEFS})
target_include_directories(t_cose PUBLIC inc PRIVATE src)
target_link_libraries(t_cose PUBLIC QCBOR::QCBOR PRIVATE ${CRYPTO_LIBRARY})
include(GNUInstallDirs)
install(TARGETS t_cose
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(DIRECTORY inc/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if (BUILD_EXAMPLES)
set(EXAMPLE_SRC_COMMON
examples/examples_main.c
examples/print_buf.c
examples/signing_examples.c
examples/encryption_examples.c
examples/example_keys.c
)
if (CRYPTO_PROVIDER STREQUAL "MbedTLS")
set(EXAMPLE_SRC_EXTRA examples/init_keys_psa.c)
elseif(CRYPTO_PROVIDER STREQUAL "OpenSSL")
set(EXAMPLE_SRC_EXTRA examples/init_keys_ossl.c)
elseif(CRYPTO_PROVIDER STREQUAL "Test")
set(EXAMPLE_SRC_EXTRA examples/init_keys_test.c)
else()
message(FATAL_ERROR "Bug!")
endif()
add_executable(t_cose_examples ${EXAMPLE_SRC_COMMON} ${EXAMPLE_SRC_EXTRA})
target_include_directories(t_cose_examples PRIVATE examples)
target_link_libraries(t_cose_examples PRIVATE t_cose ${CRYPTO_LIBRARY})
# Crypto defs are needed because the tests include headers from src/
target_compile_definitions(t_cose_examples PRIVATE ${CRYPTO_COMPILE_DEFS} ${EXAMPLE_EXTRA_DEFS})
endif()
if (BUILD_TESTS)
enable_testing()
set(TEST_SRC_COMMON
main.c
test/run_tests.c
test/t_cose_compute_validate_mac_test.c
test/t_cose_crypto_test.c
test/t_cose_encrypt_decrypt_test.c
test/t_cose_make_test_messages.c
test/data/test_messages.c
test/t_cose_param_test.c
test/t_cose_test.c
examples/example_keys.c
)
if (NOT CRYPTO_PROVIDER STREQUAL "Test")
list(APPEND TEST_SRC_COMMON test/t_cose_sign_verify_test.c)
endif()
if (CRYPTO_PROVIDER STREQUAL "MbedTLS")
set(TEST_SRC_EXTRA examples/init_keys_psa.c)
elseif(CRYPTO_PROVIDER STREQUAL "OpenSSL")
set(TEST_SRC_EXTRA examples/init_keys_ossl.c)
elseif(CRYPTO_PROVIDER STREQUAL "Test")
set(TEST_SRC_EXTRA examples/init_keys_test.c)
set(TEST_EXTRA_DEFS -DT_COSE_ENABLE_HASH_FAIL_TEST -DT_COSE_DISABLE_SIGN_VERIFY_TESTS)
else()
message(FATAL_ERROR "Bug!")
endif()
add_executable(t_cose_test ${TEST_SRC_COMMON} ${TEST_SRC_EXTRA})
target_include_directories(t_cose_test PRIVATE src test examples)
target_link_libraries(t_cose_test PRIVATE t_cose ${CRYPTO_LIBRARY})
# Crypto defs are needed because the tests include headers from src/
target_compile_definitions(t_cose_test PRIVATE ${CRYPTO_COMPILE_DEFS} ${TEST_EXTRA_DEFS})
add_test(NAME t_cose_test COMMAND t_cose_test)
endif()

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, Laurence Lundblade
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,272 @@
# Makefile.common -- Make configuration common to all crypto adaptors
#
# Copyright (c) 2019-2023, Laurence Lundblade. All rights reserved.
# Copyright (c) 2020, Michael Eckel, Fraunhofer SIT.
# Copyright (c) 2022-2023 Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#
# This contains the core dependecy definitions for t_cose. These
# dependencies are invariant by crypto library.
# ---- Object files for t_cose library ----
SRC_OBJ=src/t_cose_util.o \
src/t_cose_parameters.o \
src/t_cose_key.o \
src/t_cose_sign_sign.o \
src/t_cose_sign_verify.o \
src/t_cose_sign1_sign.o \
src/t_cose_sign1_verify.o \
src/t_cose_signature_sign_main.o \
src/t_cose_signature_sign_restart.o \
src/t_cose_signature_sign_eddsa.o \
src/t_cose_signature_verify_main.o \
src/t_cose_signature_verify_eddsa.o \
src/t_cose_mac_compute.o \
src/t_cose_mac_validate.o \
src/t_cose_encrypt_enc.o \
src/t_cose_encrypt_dec.o \
src/t_cose_recipient_dec_keywrap.o \
src/t_cose_recipient_enc_keywrap.o \
src/t_cose_recipient_dec_esdh.o \
src/t_cose_recipient_enc_esdh.o \
src/t_cose_qcbor_gap.o
# ---- Object files for test suite ----
TEST_OBJ=test/t_cose_test.o \
test/run_tests.o \
test/t_cose_sign_verify_test.o \
test/t_cose_make_test_messages.o \
test/data/test_messages.o \
test/t_cose_compute_validate_mac_test.o \
test/t_cose_param_test.o \
test/t_cose_crypto_test.o \
test/t_cose_encrypt_decrypt_test.o \
examples/example_keys.o
# ---- Object files for examples ----
EXAMPLE_OBJ=examples/examples_main.o \
examples/signing_examples.o \
examples/encryption_examples.o \
examples/example_keys.o \
examples/print_buf.o
# ---- public headers -----
PUBLIC_INTERFACE=inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_key.h \
inc/t_cose/t_cose_encrypt_dec.h \
inc/t_cose/t_cose_encrypt_enc.h \
inc/t_cose/t_cose_mac_compute.h \
inc/t_cose/t_cose_mac_validate.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/t_cose_recipient_dec.h \
inc/t_cose/t_cose_recipient_dec_keywrap.h \
inc/t_cose/t_cose_recipient_enc.h \
inc/t_cose/t_cose_recipient_enc_keywrap.h \
inc/t_cose/t_cose_recipient_dec_esdh.h \
inc/t_cose/t_cose_recipient_enc.h \
inc/t_cose/t_cose_recipient_enc_keywrap.h \
inc/t_cose/t_cose_recipient_enc_esdh.h \
inc/t_cose/t_cose_sign1_sign.h \
inc/t_cose/t_cose_sign1_verify.h \
inc/t_cose/t_cose_sign_sign.h \
inc/t_cose/t_cose_sign_verify.h \
inc/t_cose/t_cose_signature_sign.h \
inc/t_cose/t_cose_signature_sign_eddsa.h \
inc/t_cose/t_cose_signature_sign_main.h \
inc/t_cose/t_cose_signature_verify.h \
inc/t_cose/t_cose_signature_verify_eddsa.h \
inc/t_cose/t_cose_signature_verify_main.h \
inc/t_cose/t_cose_standard_constants.h
# ---- library source dependecies -----
# TODO: fill in all the header dependencies
src/t_cose_parameters.o: src/t_cose_parameters.c \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_sign1_verify.h \
inc/t_cose/t_cose_standard_constants.h
src/t_cose_util.o: src/t_cose_util.c \
src/t_cose_util.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_standard_constants.h \
src/t_cose_crypto.h
src/t_cose_key.o: src/t_cose_key.c \
inc/t_cose/t_cose_key.h \
src/t_cose_crypto.h
src/t_cose_sign_sign.o: src/t_cose_sign_sign.c \
inc/t_cose/t_cose_sign1_sign.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h\
inc/t_cose/t_cose_signature_sign.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/t_cose_standard_constants.h \
src/t_cose_crypto.h \
src/t_cose_util.h
src/t_cose_sign_verify.o: src/t_cose_sign_verify.c \
inc/t_cose/t_cose_sign_verify.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_signature_verify.h \
inc/t_cose/t_cose_parameters.h \
src/t_cose_crypto.h \
src/t_cose_util.h \
inc/t_cose/t_cose_standard_constants.h
src/t_cose_sign1_sign.o: src/t_cose_sign1_sign.c \
inc/t_cose/t_cose_sign1_sign.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/t_cose_standard_constants.h \
src/t_cose_crypto.h \
src/t_cose_util.h
src/t_cose_sign1_verify.o: src/t_cose_sign1_verify.c \
inc/t_cose/t_cose_sign1_verify.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/t_cose_standard_constants.h \
src/t_cose_crypto.h \
src/t_cose_util.h
src/t_cose_signature_sign_main.o: src/t_cose_signature_sign_main.c \
inc/t_cose/t_cose_signature_sign_main.h \
inc/t_cose/t_cose_signature_sign.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_sign1_verify.h \
src/t_cose_crypto.h \
inc/t_cose/t_cose_standard_constants.h \
src/t_cose_util.h
src/t_cose_signature_verify_main.o: src/t_cose_signature_verify_main.c \
inc/t_cose/t_cose_signature_verify_main.h \
inc/t_cose/t_cose_signature_verify.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_sign1_verify.h \
src/t_cose_util.h \
src/t_cose_crypto.h \
inc/t_cose/t_cose_standard_constants.h
src/t_cose_signature_sign_eddsa.o: src/t_cose_signature_sign_eddsa.c
src/t_cose_signature_verify_eddsa.o: src/t_cose_signature_verify_eddsa.c
src/t_cose_mac_compute.o: inc/t_cose/t_cose_mac_compute.h \
src/t_cose_crypto.h \
src/t_cose_util.h \
inc/t_cose/t_cose_common.h
src/t_cose_mac_validate.o: inc/t_cose/t_cose_mac_validate.h \
src/t_cose_crypto.h \
src/t_cose_util.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/t_cose_common.h
src/t_cose_encrypt_enc.o: src/t_cose_encrypt_enc.c
src/t_cose_encrypt_dec.o: src/t_cose_encrypt_dec.c
src/t_cose_recipient_enc_keywrap.o: src/t_cose_recipient_enc_keywrap.c
src/t_cose_recipient_dec_keywrap.o: src/t_cose_recipient_dec_keywrap.c
src/t_cose_recpient_enc_esdh.o: src/t_cose_recipient_enc_esdh.c \
inc/t_cose/t_cose_standard_constants.h \
inc/t_cose/t_cose_key.h \
inc/t_cose/t_cose_encrypt_enc.h \
inc/t_cose/t_cose_recipient_enc.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/t_cose_recipient_enc_keywrap.h \
inc/t_cose/q_useful_buf.h \
src/t_cose_crypto.h \
src/t_cose_util.h \
src/t_cose_recpient_dec_esdh.o: src/t_cose_recipient_dec_esdh.c \
inc/t_cose/t_cose_recipient_dec_esdh.h \
inc/t_cose/t_cose_parameters.h \
inc/t_cose/q_useful_buf.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_standard_constants.h \
inc/t_cose/t_cose_recipient_dec.h \
inc/t_cose/t_cose_key.h \
inc/t_cose/t_cose_encrypt_enc.h \
inc/t_cose/t_cose_recipient_enc.h \
src/t_cose_crypto.h \
src/t_cose_util.h
# ---- test dependencies -----
test/t_cose_test.o: test/t_cose_test.h \
test/t_cose_make_test_messages.h \
src/t_cose_crypto.h $(PUBLIC_INTERFACE)
test/t_cose_sign_verify_test.o: test/t_cose_sign_verify_test.h \
test/t_cose_make_test_messages.h \
src/t_cose_crypto.h \
examples/init_keys.h \
$(PUBLIC_INTERFACE)
test/t_cose_make_test_messages.o: test/t_cose_make_test_messages.h \
inc/t_cose/t_cose_sign1_sign.h \
inc/t_cose/t_cose_common.h \
inc/t_cose/t_cose_standard_constants.h \
src/t_cose_crypto.h src/t_cose_util.h
test/run_test.o: test/run_test.h \
test/t_cose_test.h \
test/t_cose_hash_fail_test.h \
test/t_cose_compute_validate_mac_test.h \
test/t_cose_compute_validate_mac_test.o: inc/t_cose/t_cose_mac_compute.h \
inc/t_cose/t_cose_mac_validate.h \
test/t_cose_compute_validate_mac_test.h
test/t_cose_encrypt_decrypt_test.o: test/t_cose_encrypt_decrypt_test.c \
test/t_cose_encrypt_decrypt_test.h \
examples/init_keys.h \
$(PUBLIC_INTERFACE)
# ---- example dependencies ----
examples/encryption_examples.o: examples/encryption_examples.h \
examples/encryption_examples.c \
$(PUBLIC_INTERFACE) \
examples/init_keys.h \
examples/print_buf.h
examples/signing_examples.o: examples/signing_examples.h \
examples/signing_examples.c \
examples/init_keys.h \
$(PUBLIC_INTERFACE) \
examples/print_buf.h
examples/print_buf.o: examples/print_buf.c \
examples/print_buf.h
examples/examples_main.o: examples/examples_main.c \
examples/encryption_examples.h \
examples/signing_examples.h

View File

@@ -0,0 +1,161 @@
# Makefile.ossl -- Makefile for t_cose with the OpenSSL crypto library
#
# Copyright (c) 2019-2023, Laurence Lundblade. All rights reserved.
# Copyright (c) 2020, Michael Eckel, Fraunhofer SIT.
# Copyright (c) 2022, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#
# ---- General comment ----
# This makefile is for t_cose with the OpenSSL crypto library. See
# longer explanation in README.md. Adjust CRYPTO_INC and CRYPTO_LIB
# for the location of the OpenSSL library on your build machine.
# Makefile.common, included below, contains the lists of library, test
# and example object files that are invariant by crypto library.
# ---- QCBOR location ----
# This is for direct reference to QCBOR that is not installed in
# /usr/local or some system location. The path may need to be adjusted
# for your location of QCBOR.
#QCBOR_DIR=../../QCBOR/master
#QCBOR_INC=-I $(QCBOR_DIR)/inc
#QCBOR_LIB=$(QCBOR_DIR)/libqcbor.a
# This is for reference to QCBOR that is installed in /usr/local or in
# some system location. This will typically use dynamic linking if
# there is a libqcbor.so
QCBOR_INC=-I /usr/local/include
QCBOR_LIB=-lqcbor
# ---- crypto configuration -----
# These two are for direct reference to OpenSSL that is not installed
# in /usr/local or some system location. The path names
# may need to be adjusted for your location of OpenSSL
#CRYPTO_INC=-I ../../openssl/openssl-1.1.1b/include/openssl -I ../../openssl/openssl-1.1.1b/include
#CRYPTO_LIB=../../openssl/openssl-1.1.1b/libcrypto.a
# These two are for reference to OpenSSL that has been installed in
# /usr/local/ or in some system location.
CRYPTO_LIB=-l crypto
CRYPTO_INC=-I /usr/local/include
CRYPTO_CONFIG_OPTS=-DT_COSE_USE_OPENSSL_CRYPTO
CRYPTO_OBJ=crypto_adapters/t_cose_openssl_crypto.o
CRYPTO_TEST_OBJ=examples/init_keys_ossl.o
CRYPTO_EXAMPLE_OBJ=examples/init_keys_ossl.o
# ---- other configuration ----
# Use as needed. It has been used to disable features that aren't ready
# or that aren't passing tests
OTHER_OPTS=
# ---- compiler configuration -----
# This makefile uses a minimum of compiler flags so that it will
# work out-of-the-box with a wide variety of compilers. For example,
# some compilers error out on some of the warnings flags gcc supports.
# The $(CMD_LINE) variable allows passing in extra flags. This is
# used on the stringent build script that is in
# https://github.com/laurencelundblade/tdv. This script is used
# before pushes to master (though not yet through an automated build
# process). See "warn:" below.
C_OPTS=-Os -fPIC -Wno-deprecated-declarations
# ---- Aggregate includes and options ----
INC=-I inc -I test -I src -I examples
ALL_INC=$(INC) $(CRYPTO_INC) $(QCBOR_INC)
CFLAGS=$(CMD_LINE) $(ALL_INC) $(C_OPTS) $(CRYPTO_CONFIG_OPTS) $(OTHER_OPTS)
# ---- The build targets ----
.PHONY: all install install_headers install_so uninstall clean warn
all: libt_cose.a t_cose_test t_cose_examples
# run "make warn" as a handy way to compile with the warning flags
# used in the QCBOR release process. See C_OPTS above.
warn:
make -f Makefile.ossl CMD_LINE="-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wcast-qual"
# ---- Bring in common definitions of SRC_OBJ, TEST_OBJ, ... -----
include Makefile.common
# --- Finally, the things that actually get built ----
libt_cose.a: $(SRC_OBJ) $(CRYPTO_OBJ)
ar -r $@ $^
# The shared library which is not made by default because of platform
# variability For example MacOS and Linux behave differently and some
# IoT OS's don't support them at all.
libt_cose.so: $(SRC_OBJ) $(CRYPTO_OBJ)
cc -shared $^ -o $@ $(CRYPTO_LIB) $(QCBOR_LIB)
t_cose_test: main.o $(TEST_OBJ) $(CRYPTO_TEST_OBJ) libt_cose.a
cc -dead_strip -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
t_cose_basic_example_ossl: examples/t_cose_basic_example_ossl.o examples/encryption_examples_ossl.o libt_cose.a
cc -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
crypto_adapters/t_cose_ossl_crypto.o: crypto_adapters/t_cose_ossl_crypto.c \
src/t_cose_crypto.h \
src/t_cose_util.h \
t_cose/t_cose_standard_constants.h
examples/init_keys_ossl.o: examples/init_keys_ossl.c \
examples/init_keys.h \
$(PUBLIC_INTERFACE)
t_cose_examples: $(EXAMPLE_OBJ) $(CRYPTO_EXAMPLE_OBJ) libt_cose.a
cc -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
# ---- Installation ----
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
install: libt_cose.a install_headers
install -d $(DESTDIR)$(PREFIX)/lib/
install -m 644 libt_cose.a $(DESTDIR)$(PREFIX)/lib/
install_headers: $(PUBLIC_INTERFACE)
install -d $(DESTDIR)$(PREFIX)/include/t_cose
@for i in $(PUBLIC_INTERFACE); do \
install -m 644 $$i $(DESTDIR)$(PREFIX)/include/t_cose ; \
done
# The shared library is not installed by default because of platform variability.
install_so: libt_cose.so install_headers
install -m 755 libt_cose.so $(DESTDIR)$(PREFIX)/lib/libt_cose.so.1.0.0
ln -sf libt_cose.so.1 $(DESTDIR)$(PREFIX)/lib/libt_cose.so
ln -sf libt_cose.so.1.0.0 $(DESTDIR)$(PREFIX)/lib/libt_cose.so.1
uninstall: libt_cose.a $(PUBLIC_INTERFACE)
$(RM) -d $(DESTDIR)$(PREFIX)/include/t_cose/*
$(RM) -d $(DESTDIR)$(PREFIX)/include/t_cose/
$(RM) $(addprefix $(DESTDIR)$(PREFIX)/lib/, \
libt_cose.a libt_cose.so libt_cose.so.1 libt_cose.so.1.0.0)
clean:
rm -f $(SRC_OBJ) $(TEST_OBJ) $(CRYPTO_OBJ) $(CRYPTO_TEST_OBJ) $(EXAMPLE_OBJ) $(CRYPTO_EXAMPLE_OBJ) \
t_cose_examples libt_cose.so \
main.o t_cose_test libt_cose.a libt_cose.so

View File

@@ -0,0 +1,158 @@
# Makefile.psa -- Makefile for t_cose with the Mbed TLS crypto library
#
# Copyright (c) 2019-2023, Laurence Lundblade. All rights reserved.
# Copyright (c) 2020, Michael Eckel, Fraunhofer SIT.
# Copyright (c) 2022, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#
# ---- General comment ----
# This makefile is for t_cose with the Mbed TLS crypto library. Mbed
# TLS is an implementation of the PSA crypto API. See longer
# explanation in README.md. Adjust CRYPTO_INC and CRYPTO_LIB for the
# location of the Mbed TLS library on your build machine.
# Makefile.common, included below, contains the lists of library, test
# and example object files that are invariant by crypto library.
# ---- QCBOR location ----
# This is for direct reference to QCBOR that is not installed in
# /usr/local or some system location. The path may need to be adjusted
# for your location of QCBOR.
#QCBOR_DIR=../../QCBOR/master
#QCBOR_INC=-I $(QCBOR_DIR)/inc
#QCBOR_LIB=$(QCBOR_DIR)/libqcbor.a
# This is for reference to QCBOR that is installed in /usr/local or in
# some system location. This will typically use dynamic linking if
# there is a libqcbor.so
QCBOR_INC=-I /usr/local/include
QCBOR_LIB=-lqcbor
# ---- crypto configuration -----
# These two are for direct reference to Mbed TLS crypto that is not
# installed in /usr/local or some system location. The path names may
# need to be adjusted for your location of Mbed TLS
#CRYPTO_INC=-I ../../mbedtls/include/
#CRYPTO_LIB=../../mbedtls/library/libmbedcrypto.a
# These two are for reference to Mbed TLS that has been installed in
# /usr/local or in some system location.
CRYPTO_LIB=-l mbedcrypto
CRYPTO_INC=-I /usr/local/include
CRYPTO_CONFIG_OPTS=-DT_COSE_USE_PSA_CRYPTO
CRYPTO_OBJ=crypto_adapters/t_cose_psa_crypto.o
CRYPTO_TEST_OBJ=examples/init_keys_psa.o
CRYPTO_EXAMPLE_OBJ=examples/init_keys_psa.o
# ---- other configuration ----
# Use as needed. It has been used to disable features that aren't ready
# or that aren't passing tests
OTHER_OPTS=
# ---- compiler configuration -----
# This makefile uses a minimum of compiler flags so that it will
# work out-of-the-box with a wide variety of compilers. For example,
# some compilers error out on some of the warnings flags gcc supports.
# The $(CMD_LINE) variable allows passing in extra flags. This is
# used on the stringent build script that is in
# https://github.com/laurencelundblade/tdv. This script is used
# before pushes to master (though not yet through an automated build
# process). See "warn:" below.
C_OPTS=-Os -fPIC
# ---- Aggregate includes and options ----
INC=-I inc -I test -I src -I examples
ALL_INC=$(INC) $(CRYPTO_INC) $(QCBOR_INC)
CFLAGS=$(CMD_LINE) $(ALL_INC) $(C_OPTS) $(CRYPTO_CONFIG_OPTS) $(OTHER_OPTS)
# ---- The build targets ----
.PHONY: all install install_headers install_so uninstall clean warn
all: libt_cose.a t_cose_test t_cose_examples
# run "make warn" as a handy way to compile with the warning flags
# used in the QCBOR release process. See C_OPTS above.
warn:
make -f Makefile.psa CMD_LINE="-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wcast-qual"
# ---- Bring in common definitions of SRC_OBJ, TEST_OBJ, ... -----
include Makefile.common
# --- Finally, the things that actually get built ----
libt_cose.a: $(SRC_OBJ) $(CRYPTO_OBJ)
ar -r $@ $^
# The shared library which is not made by default because of platform
# variability For example MacOS and Linux behave differently and some
# IoT OS's don't support them at all.
libt_cose.so: $(SRC_OBJ) $(CRYPTO_OBJ)
cc -shared $^ -o $@ $(CRYPTO_LIB) $(QCBOR_LIB)
t_cose_test: main.o $(TEST_OBJ) $(CRYPTO_TEST_OBJ) libt_cose.a
cc -dead_strip -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
crypto_adapters/t_cose_psa_crypto.o: crypto_adapters/t_cose_psa_crypto.c \
src/t_cose_crypto.h \
src/t_cose_util.h \
inc/t_cose/t_cose_standard_constants.h
examples/init_keys_psa.o: examples/init_keys_psa.c \
examples/init_keys.h \
$(PUBLIC_INTERFACE)
t_cose_examples: $(EXAMPLE_OBJ) $(CRYPTO_EXAMPLE_OBJ) libt_cose.a
cc -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
# ---- Installation ----
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
install: libt_cose.a install_headers
install -d $(DESTDIR)$(PREFIX)/lib/
install -m 644 libt_cose.a $(DESTDIR)$(PREFIX)/lib/
install_headers: $(PUBLIC_INTERFACE)
install -d $(DESTDIR)$(PREFIX)/include/t_cose
@for i in $(PUBLIC_INTERFACE); do \
install -m 644 $$i $(DESTDIR)$(PREFIX)/include/t_cose ; \
done
# The shared library is not installed by default because of platform variability.
install_so: libt_cose.so install_headers
install -m 755 libt_cose.so $(DESTDIR)$(PREFIX)/lib/libt_cose.so.1.0.0
ln -sf libt_cose.so.1 $(DESTDIR)$(PREFIX)/lib/libt_cose.so
ln -sf libt_cose.so.1.0.0 $(DESTDIR)$(PREFIX)/lib/libt_cose.so.1
uninstall: libt_cose.a $(PUBLIC_INTERFACE)
$(RM) -d $(DESTDIR)$(PREFIX)/include/t_cose/*
$(RM) -d $(DESTDIR)$(PREFIX)/include/t_cose/
$(RM) $(addprefix $(DESTDIR)$(PREFIX)/lib/, \
libt_cose.a libt_cose.so libt_cose.so.1 libt_cose.so.1.0.0)
clean:
rm -f $(SRC_OBJ) $(TEST_OBJ) $(CRYPTO_OBJ) $(CRYPTO_TEST_OBJ) $(EXAMPLE_OBJ) $(CRYPTO_EXAMPLE_OBJ) \
t_cose_examples libt_cose.so \
main.o t_cose_test libt_cose.a libt_cose.so

View File

@@ -0,0 +1,125 @@
# Makefile.test -- Makefile for no-crypto test config for t_cose
#
# Copyright (c) 2019-2023, Laurence Lundblade. All rights reserved.
# Copyright (c) 2020, Michael Eckel, Fraunhofer SIT.
# Copyright (c) 2022, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# See BSD-3-Clause license in README.md
#
# ---- General comment ----
# This makefile is for t_cose with no crypto library. This
# t_cose configuration is used for bring up on a new platform
# and testing. No external crypto library is needed. The crypto
# that it does use (e.g., SHA-256) is built-in.
# Makefile.common, included below, contains the lists of library, test
# and example object files that are invariant by crypto library.
# ---- QCBOR location ----
# This is for direct reference to QCBOR that is not installed in
# /usr/local or some system location. The path may need to be adjusted
# for your location of QCBOR.
#QCBOR_DIR=../../QCBOR/master
#QCBOR_INC=-I $(QCBOR_DIR)/inc
#QCBOR_LIB=$(QCBOR_DIR)/libqcbor.a
# This is for reference to QCBOR that is installed in /usr/local or in
# some system location. This will typically use dynamic linking if
# there is a libqcbor.so
QCBOR_INC=-I /usr/local/include
QCBOR_LIB=-lqcbor
# ---- crypto configuration -----
# Uses only the internal Brad Conte hash implementation that is bundled with t_cose
CRYPTO_INC=-I crypto_adapters/b_con_hash
CRYPTO_LIB=
CRYPTO_CONFIG_OPTS=-DT_COSE_USE_B_CON_SHA256
CRYPTO_OBJ=crypto_adapters/t_cose_test_crypto.o crypto_adapters/b_con_hash/sha256.o
CRYPTO_EXAMPLE_OBJ=examples/init_keys_test.o
CRYPTO_TEST_OBJ=examples/init_keys_test.o
# ---- other configuration ----
# Use as needed. It has been used to disable features that aren't ready
# or that aren't passing tests
OTHER_OPTS=-DT_COSE_ENABLE_HASH_FAIL_TEST -DT_COSE_DISABLE_SIGN_VERIFY_TESTS
# ---- compiler configuration -----
# This makefile uses a minimum of compiler flags so that it will
# work out-of-the-box with a wide variety of compilers. For example,
# some compilers error out on some of the warnings flags gcc supports.
# The $(CMD_LINE) variable allows passing in extra flags. This is
# used on the stringent build script that is in
# https://github.com/laurencelundblade/tdv. This script is used
# before pushes to master (though not yet through an automated build
# process). See "warn:" below.
C_OPTS=-Os -fPIC
# ---- Aggregate includes and options ----
INC=-I inc -I test -I src -I examples
ALL_INC=$(INC) $(CRYPTO_INC) $(QCBOR_INC)
CFLAGS=$(CMD_LINE) $(ALL_INC) $(C_OPTS) $(CRYPTO_CONFIG_OPTS) $(OTHER_OPTS)
# ---- The build targets ----
.PHONY: all clean warn
all: libt_cose.a t_cose_test t_cose_examples
# run "make warn" as a handy way to compile with the warning flags
# used in the QCBOR release process. See C_OPTS above.
warn:
make -f Makefile.test CMD_LINE="-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wcast-qual"
# ---- Bring in common definitions of SRC_OBJ, TEST_OBJ, ... -----
include Makefile.common
# --- Finally, the things that actually get built ----
libt_cose.a: $(SRC_OBJ) $(CRYPTO_OBJ)
ar -r $@ $^
# The shared library which is not made by default because of platform
# variability For example MacOS and Linux behave differently and some
# IoT OS's don't support them at all.
libt_cose.so: $(SRC_OBJ) $(CRYPTO_OBJ)
cc -shared $^ -o $@ $(CRYPTO_LIB) $(QCBOR_LIB)
t_cose_test: main.o $(TEST_OBJ) $(CRYPTO_TEST_OBJ) libt_cose.a
cc -dead_strip -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
crypto_adapters/t_cose_test_crypto.o: crypto_adapters/t_cose_test_crypto.c \
src/t_cose_crypto.h \
src/t_cose_util.h \
inc/t_cose/t_cose_standard_constants.h \
crypto_adapters/b_con_hash/sha256.h
crypto_adapters/b_con_hash/sha256.o: crypto_adapters/b_con_hash/sha256.h
examples/init_keys_test.o: examples/init_keys_test.c \
examples/init_keys.h \
$(PUBLIC_INTERFACE)
t_cose_examples: $(EXAMPLE_OBJ) $(CRYPTO_EXAMPLE_OBJ) libt_cose.a
cc -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
clean:
rm -f $(SRC_OBJ) $(TEST_OBJ) $(CRYPTO_OBJ) $(CRYPTO_TEST_OBJ) $(EXAMPLE_OBJ) $(CRYPTO_EXAMPLE_OBJ) \
t_cose_examples libt_cose.so \
main.o t_cose_test libt_cose.a libt_cose.so

View File

@@ -0,0 +1,381 @@
This is the DEV BRANCH for t_cose 2.0. It is an ALPHA quality
release. The major features are in place. Test and documentation are
not complete. There will be changes.
COMPATIBILITY NOTE: v2.0 is backwards compatible with v1.x, but there
are new APIs in v2.0 for signing and verification. The v2.0 APIs
support both COSE_Sign and COSE_Sign1 and support multiple
signatures. The v2.0 API are named "t_cose_sign_xxx" and the
old ones, which support only COSE_Sign1, are named "t_cose_sign1_xxx".
If you are using this v2.0 t_cose library, it is
better to use the v2.0 API, t_cose_sign_xxx because it uses
less memory and links in less code. This is because in v2.0
the old APIs are supported by a compatibility layer.
However, some use cases may want to continue with v1.x. It is smaller
over all. But note that it only supports COSE_Sign1.
![t_cose](https://github.com/laurencelundblade/t_cose/blob/master/t-cose-logo.png?raw=true)
*t_cose* 2.x implements the following parts of [COSE, RFC 9052](https://tools.ietf.org/html/rfc9052)
and [COSE, RFC 9053](https://tools.ietf.org/html/rfc9053):
* COSE_Sign1 (single signer) with ECDSA, EdDSA and RSA algorithmns
* COSE_Sign (multiple signatures) with ECDSA, EdDSA and RSA
* COSE_Mac0 (single MAC) with HMAC 256, 384 and 512
* COSE_Encrypt0 (single recipient) with AES GCM 128, 192 and 256
* COSE_Encrypt (multiple recipients) with ECDH + AES key wrap or just with AES key wrap
* AES CBC and CTR modes per [AES-CTR and AES-CBC, RFC 9459](https://tools.ietf.org/html/rfc9459)
**Implemented in C with minimal dependency** There are three main
dependencies: 1) [QCBOR](https://github.com/laurencelundblade/QCBOR),
2) A cryptographic library, 3) C99, <stdint.h>,
<stddef.h>, <stdbool.h> and <string.h>. It is highly
portable to different HW, OS's and cryptographic libraries. Except for
some minor configuration for the cryptographic library, no #ifdefs or
compiler options need to be set for it to run correctly.
**Crypto Library Integration Layer** Works with different cryptographic
libraries via a simple integration layer. The integration layer is kept small and simple,
just enough for the use cases, so that integration is simpler. Integration layers for
the OpenSSL and ARM Mbed TLS (PSA Cryptography API) cryptographic libraries
are included.
**Secure coding style** Uses a construct called UsefulBuf / q_useful_buf as a
discipline for safe coding and handling of binary data.
**Small simple memory model** Malloc is not used. Stack use is generally
small. The caller supplies the larger buffers like those for the paylod giving full
control of memory usage and allocation making t_cose useful for embedded
implementations that have to run in small fixed memory.
## Code Status
As of Novermber 2023, the major t_cose 2.0 features are in and
functioning. There are many details to fix, interop testing and general
testing to do, documentation to complete and correct.
Backwards compatibility with t_cose 1.x APIs is provided, however
the new APIs for signing and verification have a smaller code size
than the backwards compatible APIs.
Integration with the [OpenSSL](https://www.openssl.org) and [Arm Mbed
TLS](https://github.com/ARMmbed/mbedtls) cryptographic libraries is
fully supported.
## Building and Dependencies
Except for the crypto library set up, t_cose is very portable and
should largely just work in any environment. It needs a few standard
libraries and [QCBOR](https://github.com/laurencelundblade/QCBOR)
(which is also very portable). Hence most of this section is about
crypto library set up.
### QCBOR
If QCBOR is installed in /usr/local, then the makefiles should find
it. If not then QCBOR may need to be downloaded. The makefiles can be
modified to reference it other than in /usr/local.
### Supported Cryptographic Libraries
Here's three crypto library configurations that are supported. Others
can be added with relative ease.
#### Test Crypto -- Makefile.test
This configuration should work instantly on any device and is useful
to do a large amount of testing with, but can't be put to full
commercial use. What it lacks is integration with an crypto libraries
implementation so it can't produce real COSE messages.
This configuration (and only this configuration) uses a bundled
SHA-256 implementation (SHA-256 is simple and easy to bundle, ECDSA is
not).
To build run:
make -f Makefile.test
#### OpenSSL Crypto -- Makefile.ossl
This OpenSSL integration supports SHA-256, SHA-384 and SHA-512 with
ECDSA, EdDSA, or RSAPSS to support the COSE algorithms ES256, ES384 and
ES512, PS256, PS384 and PS512. It also support AES, key wrap and ECDH
for encryption. It is a full and tested integration
with OpenSSL crypto.
If OpenSSL is installed in /usr/local or as a standard library, you can
probably just run make:
make -f Makefile.ossl
The specific things that Makefile.ossl does is:
* Links the crypto_adapters/t_cose_openssl_crypto.o into libt_cose.a
* Links test/test/t_cose_make_openssl_test_key.o into the test binary
* `#define T_COSE_USE_OPENSSL_CRYPTO`
t_cose is regularly tested against OpenSSL 1.1.1 and 3.0.
The crypto adaptor for OpenSSL is about twice the size of that for
Mbed TLS because the API doesn't line up well with the needs for COSE
(OpenSSL is ASN.1/DER oriented). Memory allocation is performed inside
OpenSSL and in the crypto adaptation layer. This makes the OpenSSL
crypto library less suitable for embedded use.
No deprecated or to-be-deprecated APIs are used.
There are several different sets of APIs in OpenSSL that can be used
to implement ECDSA and hashing. The ones chosen are the most official
and well-supported, however others might suit particular uses cases
better. An older t_cose used some to-be-deprecated APIs and is a more
efficient than this one. It is unfortunate that these APIs
(ECDSA_do_sign and ECDSA_do_verify) are slated for deprecation and
there is no supported alternative to those that work only with DER-encoded
signatures.
There are no known problems with the code and test coverage for the
adaptor is good. Not every single memory allocation failure has
test coverage, but the code should handle them all correctly.
#### PSA Crypto -- Makefile.psa
As of March 2022, t_cose works with the PSA 1.0 Crypto API as
implemented by Mbed TLS 2.x and 3.x.
This integration supports SHA-256, SHA-384 and SHA-512 with
ECDSA, EdDSA or RSAPSS to support the COSE algorithms ES256, ES384 and
ES512, PS256, PS384 and PS512. It also support AES, key wrap and ECDH
for encryption.
If Mbed TLS is installed in /usr/local, you can probably just run
make:
make -f Makefile.psa
If this doesn't work or you have Mbed TLS elsewhere edit the makefile.
The specific things that Makefile.psa does is:
* Links the crypto_adapters/t_cose_psa_crypto.o into libt_cose.a
* Links test/test/t_cose_make_psa_test_key.o into the test binary
* `#define T_COSE_USE_PSA_CRYPTO`
This crypto adapter is small and simple. The adapter allocates no
memory and as far as I know it internally allocates no memory. It is a
good choice for embedded use.
It makes use of the 1.0 version of the PSA cryptographic API. No
deprecated or to-be-deprecated functions are called (an older t_cose
used some to be deprecated APIs).
It is regularly tested against the latest version 2 and version 3 of
Mbed TLS, an implementation of the PSA crypto API.
Confidence in the adaptor code is high and reasonably well tested
because it is simple.
### General Crypto Library Strategy
The functions that t_cose needs from the crypto library are all
defined in src/t_cose_crypto.h. This is a porting or adaption
layer. There are no #ifdefs in the main t_cose code for different
crypto libraries. When it needs a crypto function it just calls the
interface defined in t_cose_crypto.h.
When integrating t_cose with a new cryptographic library, what is
necessary is to write some code, an "adaptor", that implements
t_cose_crypto.h using the new target cryptographic library. This can
be done without changes to any t_cose code for many cryptographic
libraries. See the interface documentation in t_cose_crypto.h for what
needs to be implemented.
That said, there is one case where t_cose source code needs to be
modified. This is for hash algorithm implementations that are linked
into and run inline with t_cose and that have a context structure. In
this case t_cose_crypto.h should be modified to use that context
structure. Use the OpenSSL configuration as an example.
To complete the set up for a new cryptographic library and test it, a
new test adaptation file is also needed. This file makes public key
pairs of the correct type for use with testing. This file is usually
named test/t_cose_make_xxxx_test_key.c and is linked in with the test
app. The keys it makes are passed through t_cose untouched, through
the t_cose_crypto.h interface into the underlying crypto.
#### Unsupported Crypto
Not all crypto libraries will support all algorithms that t_cose can
make use of. For example, t_cose will likely be able to make use of PQ
(post-quantum) algorithms, but that not all libraries will support all
of these algorithms.
Algorithm variability is handled in two layers: 1) compilation and
linking in the t_cose crypto adaptor layer and 2) run-time indication
in the public API. That is, there is no variability in the t_cose
public API because of variation in algorithms available. When an
algorithm isn't available, it manifests by one of the standard public
APIs returning T_COSE_ERR_UNSUPPORTED_XXXX.
All algorithm identification in t_cose is by COSE integer algorithm
IDs.
To go into detail of how compilation and linking are handled, the
crypto adaptation layer should rely on #defines in the crypto library
headers to know what files it can include and what functions it can
call. Hopefully, the crypto libraries provide the necessary
#defines. t_cose should always compile and link successfully with any
crypto libraries it supports no matter the version or
configuration. For example, if a version of Mbed TLS that doesn't
support HPKE is used, it should still compile and link with no user
intervention. The user will find out that HPKE is missing when they
get a T_COSE_ERR_UNSUPPORTED_XXXX error from the public API.
If a crypto library doesn't provide #defines, then algorithms in it
that are made use of should be disabled by default. To enable them,
the user will have to go to the trouble of explicitly #defining
something to enable it. This is so t_cose always compiles and links
correctly the first time without the user doing any configuration
work.
t_cose does assume some algorithms like SHA-2 will always be present
because they are widely supported and generally necessary.
If an implementation or test case wishes to query whether a particular
t_cose build supports a particular algorithm the public API
t_cose_is_algorithm_supported() is provided.
In the automated tests, cases that use specific algorithms that are
known to be not widely supported are enabled and disabled at runtime
by querying whether they are not supported through the runtime public
API. They are generally not handled by #ifdefs unless absolutely
necessary.
For continuous integration (CI), it is fine if not every test covers
every algorithm, but every algorithm should be covered by at least one
test. This may require configuring CI with some alternate versions of
crypto libraries.
### Omitting Algorithms to Reduce Code Size
Previously, t_cose had #defines like T_COSE_DISABLE_ES512 to omit
algorthms to reduce code size. A different strategy is used for t_cose
2.0 that takes advantage of the dynamic linking of the signing,
COSE_Signature and COSE_Recipient implementations.
For t_cose 2.0, the bulk of the crypto algorithms are called by the
implementations of the t_cose_signature and of t_cose_recipient.
These are not hard-linked to the rest of the t_cose. Assuming dead
stripping, paticular implementations for particular algorithms will
not be linked in unless they are explicitly called. The dead stripping
can take care of this through the t_cose_crypto layer, though some
adjustments may be necessary for it to be most effective.
For example, the ECDSA algorithm is only called from
t_cose_signature_sign_main and t_cose_signature_verify_main. If a
user of t_cose never calls these, for example, never calls
t_cose_signature_sign_main_init() it won't be linked and the
references to the of the algorithms won't be made.
For this to work well, the t_cose_crypto layer needs to have separate
functions for separate algorithms. This is something to be looked
into.
## Memory Usage
### Code
Things that make the code smaller:
* PSA / Mbed crypto takes less code to interface with than OpenSSL
* gcc is usually smaller than llvm because stack guards are off by default
* Use only 256-bit crypto with the T_COSE_DISABLE_ESXXX options
* Disable short-circut sig debug facility T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
* Disable the content type header T_COSE_DISABLE_CONTENT_TYPE
* Disable COSE_Sign structure support with T_COSE_DISABLE_COSE_SIGN
(use only COSE_Sign1 signature structures if adequate).
### Heap and stack
Malloc is not used.
Stack usage is variable depending on the key and hash size and the
stack usage by the cryptographic library that performs the hash and
public key crypto functions. The maximum requirement is roughly
2KB. This is an estimate from examining the code, not an actual
measurement.
Since the keys, hash outputs and signatures are stored on the stack,
the stronger the security, the more stack is used. By default up to
512 bit EC is enabled. Disable 512 and 384 bit EC to reduce stack
usage by about 100 bytes.
Different cryptographic libraries may have very different stack usage
characteristics. For example if one use malloc rather than the stack,
it will (hopefully) use less stack. The guess estimate range of usage
by the cryptographic library is between 64 and 1024 bytes of stack.
Aside from the cryptographic library, the base stack use by t_cose is
500 bytes for signing and 1500 bytes for verification. With a large
cryptographic library, the total is about 1500 bytes for signing and
2000 bytes for verification (for verification, the crypto library
stack re uses stack used to decode header parameters so the increment
isn't so large).
The design is such that only one copy of the output, the COSE_Sign1,
need be in memory. It makes use of special features in QCBOR that
allows contstuction of the output including the payload, using just
the single output buffer to accomplish this.
A buffer to hold the signed COSE result must be passed in. It must be
about 100 bytes larger than the combined size of the payload and key
id for ECDSA 256. It can be allocated as the caller wishes.
### Crypto library memory usage
In addition to the above memory usage, the crypto library will use
some stack and/or heap memory. This will vary quite a bit by crypto
library. Some may use malloc. Some may not.
The OpenSSL library does use malloc, even with ECDSA. Another
implementation of ECDSA might not use malloc, as the keys are small
enough.
### Mixed code style
QCBOR uses camelCase and t_cose follows
[Arm's coding guidelines](https://git.trustedfirmware.org/TF-M/trusted-firmware-m.git/tree/docs/contributing/coding_guide.rst)
resulting in code with mixed styles. For better or worse, an Arm-style version of UsefulBuf
is created and used and so there is a duplicate of UsefulBuf. The two are identical. They
just have different names.
## Limitations
* Most inputs and outputs must be in a continguous buffer. One
exception to this is that CBOR payloads being signed can be
constructed piecemeal into the output buffer and signed without
using a separate buffer.
* Doesn't handle COSE string algorithm IDs. Only COSE integer
algorithm IDs are handled. Thus far no string algorithm IDs have
been assigned by IANA.
* Does not handle CBOR indefinite length strings (indefinite length
maps and arrays are handled).
* Counter signatures are not supported.
## Credit
* Dávid Vincze for work on COSE_Mac.
* Mate Toth-Pal for restartable signing.
* Paul Liétar for RSA PSS (PS256..PS512) and EdDSA.
* Maik Riechert for cmake, CI and other.
* Ken Takayama for the bulk of the detached content implementation and AES-CTR and AES-CBC.
* Tamas Ban for lots code review comments, design ideas and porting to ARM PSA.
* Rob Coombs, Shebu Varghese Kuriakose and other ARM folks for sponsorship.
* Michael Eckel for makefile fixes.
* Hannes Tschofenig for initial implementation of encryption.
## Copyright and License
t_cose is available under the 3-Clause BSD License.

View File

@@ -0,0 +1,27 @@
# Security Policy
## Reporting a Vulnerability
Please report security vulnerabilities by sending email to lgl@island-resort.com.
Please include "t_cose SECURITY" in the subject line.
In most cases the vulnerability should not be reported by filing an issue in GitHub as this
will publically disclose the issue before a fix is available.
Laurence Lundblade maintains this code and will respond in a day or two with an initial
evaluation.
Security fixes will be prioritized over other work.
Vulnerabilities will be fixed promptly, but some may be more complex than others
and take longer. If the fix is quick, it will usually be turned around in a
few days.
## Availability of Fixes
When the fix has been created, it will be privately verified with the party that reported it.
Only after the fix has been verified and the reporter has had a chance to integrate the fix,
will it be made available as a public commit in GitHub.
If the reporter doesn't respond or can't integrate the fix, it will be made public after 30 days.

View File

@@ -0,0 +1,108 @@
#[=======================================================================[.rst:
FindMbedTLS
-------
Finds the mbedTLS library.
Imported Targets
^^^^^^^^^^^^^^^^
This module provides the following imported targets, if found:
``MbedTLS::MbedTLS``
The mbedTLS library
``MbedTLS::MbedCrypto``
The mbedTLS crypto library
``MbedTLS::MbedX509``
The mbedTLS X509 library
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables:
``MbedTLS_FOUND``
True if the system has the MbedTLS library.
``MbedTLS_INCLUDE_DIRS``
Include directories needed to use MbedTLS.
``MbedTLS_LIBRARIES``
Libraries needed to link to MbedTLS.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``MbedTLS_INCLUDE_DIR``
The directory containing ``mbedtls/``.
``MbedTLS_LIBRARY``
The path to the mbedtls library.
``MbedTLS_CRYPTO_LIBRARY``
The path to the mbedcrypto library.
``MbedTLS_X509_LIBRARY``
The path to the mbedx509 library.
#]=======================================================================]
find_path(MbedTLS_INCLUDE_DIR
NAMES mbedtls/version.h
)
find_library(MbedTLS_LIBRARY
NAMES mbedtls
)
find_library(MbedTLS_CRYPTO_LIBRARY
NAMES mbedcrypto
)
find_library(MbedTLS_X509_LIBRARY
NAMES mbedx509
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MbedTLS
FOUND_VAR MbedTLS_FOUND
REQUIRED_VARS
MbedTLS_LIBRARY
MbedTLS_CRYPTO_LIBRARY
MbedTLS_X509_LIBRARY
MbedTLS_INCLUDE_DIR
)
if(MbedTLS_FOUND)
set(MbedTLS_LIBRARIES "${MbedTLS_LIBRARY}" "${MbedTLS_X509_LIBRARY}" "${MbedTLS_CRYPTO_LIBRARY}")
set(MbedTLS_INCLUDE_DIRS "${MbedTLS_INCLUDE_DIR}")
endif()
if(MbedTLS_FOUND AND NOT TARGET MbedTLS::MbedCrypto)
add_library(MbedTLS::MbedCrypto UNKNOWN IMPORTED)
set_target_properties(MbedTLS::MbedCrypto PROPERTIES
IMPORTED_LOCATION "${MbedTLS_CRYPTO_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MbedTLS_INCLUDE_DIR}"
)
endif()
if(MbedTLS_FOUND AND NOT TARGET MbedTLS::MbedX509)
add_library(MbedTLS::MbedX509 UNKNOWN IMPORTED)
set_target_properties(MbedTLS::MbedX509 PROPERTIES
IMPORTED_LOCATION "${MbedTLS_X509_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MbedTLS_INCLUDE_DIR}"
)
target_link_libraries(MbedTLS::MbedX509 INTERFACE MbedTLS::MbedCrypto)
endif()
if(MbedTLS_FOUND AND NOT TARGET MbedTLS::MbedTLS)
add_library(MbedTLS::MbedTLS UNKNOWN IMPORTED)
set_target_properties(MbedTLS::MbedTLS PROPERTIES
IMPORTED_LOCATION "${MbedTLS_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MbedTLS_INCLUDE_DIR}"
)
target_link_libraries(MbedTLS::MbedX509 INTERFACE MbedTLS::MbedCrypto MbedTLS::MbedX509)
endif()
mark_as_advanced(
MbedTLS_INCLUDE_DIR
MbedTLS_LIBRARY
MbedTLS_CRYPTO_LIBRARY
MbedTLS_X509_LIBRARY
)

View File

@@ -0,0 +1,81 @@
#[=======================================================================[.rst:
FindQCBOR
-------
Finds the QCBOR library.
Imported Targets
^^^^^^^^^^^^^^^^
This module provides the following imported targets, if found:
``QCBOR::QCBOR``
The QCBOR library
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables:
``QCBOR_FOUND``
True if the system has the QCBOR library.
``QCBOR_INCLUDE_DIRS``
Include directories needed to use QCBOR.
``QCBOR_LIBRARIES``
Libraries needed to link to QCBOR.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``QCBOR_INCLUDE_DIR``
The directory containing ``t_cose/``.
``QCBOR_LIBRARY``
The path to the QCBOR library.
#]=======================================================================]
include(CheckLibraryExists)
find_path(QCBOR_INCLUDE_DIR
NAMES qcbor/qcbor.h
)
find_library(QCBOR_LIBRARY
NAMES qcbor
)
CHECK_LIBRARY_EXISTS(m sin "" HAVE_LIB_M)
set(EXTRA_LIBS)
if(HAVE_LIB_M)
set(EXTRA_LIBS m)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(QCBOR
FOUND_VAR QCBOR_FOUND
REQUIRED_VARS
QCBOR_LIBRARY
QCBOR_INCLUDE_DIR
)
if(QCBOR_FOUND)
set(QCBOR_LIBRARIES "${QCBOR_LIBRARY} ${EXTRA_LIBS}")
set(QCBOR_INCLUDE_DIRS "${QCBOR_INCLUDE_DIR}")
endif()
if(QCBOR_FOUND AND NOT TARGET QCBOR::QCBOR)
add_library(QCBOR::QCBOR UNKNOWN IMPORTED)
set_target_properties(QCBOR::QCBOR PROPERTIES
IMPORTED_LOCATION "${QCBOR_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${QCBOR_INCLUDE_DIR}"
)
if (EXTRA_LIBS)
target_link_libraries(QCBOR::QCBOR INTERFACE ${EXTRA_LIBS})
endif()
endif()
mark_as_advanced(
QCBOR_INCLUDE_DIR
QCBOR_LIBRARY
)

View File

@@ -0,0 +1,161 @@
/*********************************************************************
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
* Copyright Laurence Lundblade 2020 for addition of casts so it
compiles without warnings with pendantic compilers settings.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include <memory.h>
#include "sha256.h"
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
/*********************** FUNCTION DEFINITIONS ***********************/
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (WORD)((data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]));
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
t2 = EP0(a) + MAJ(a,b,c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
void sha256_init(SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
{
WORD i;
for (i = 0; i < len; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
sha256_transform(ctx, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
{
WORD i;
i = ctx->datalen;
// Pad whatever data is left in the buffer.
if (ctx->datalen < 56) {
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else {
ctx->data[i++] = 0x80;
while (i < 64)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = (BYTE)(ctx->bitlen);
ctx->data[62] = (BYTE)(ctx->bitlen >> 8);
ctx->data[61] = (BYTE)(ctx->bitlen >> 16);
ctx->data[60] = (BYTE)(ctx->bitlen >> 24);
ctx->data[59] = (BYTE)(ctx->bitlen >> 32);
ctx->data[58] = (BYTE)(ctx->bitlen >> 40);
ctx->data[57] = (BYTE)(ctx->bitlen >> 48);
ctx->data[56] = (BYTE)(ctx->bitlen >> 56);
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}

View File

@@ -0,0 +1,34 @@
/*********************************************************************
* Filename: sha256.h
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Defines the API for the corresponding SHA1 implementation.
*********************************************************************/
#ifndef SHA256_H
#define SHA256_H
/*************************** HEADER FILES ***************************/
#include <stddef.h>
/****************************** MACROS ******************************/
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
/**************************** DATA TYPES ****************************/
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
typedef struct {
BYTE data[64];
WORD datalen;
unsigned long long bitlen;
WORD state[8];
} SHA256_CTX;
/*********************** FUNCTION DECLARATIONS **********************/
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
#endif // SHA256_H

View File

@@ -0,0 +1,27 @@
/*
* t_cose_psa_crypto.h
*
* Copyright 2022, Laurence Lundblade
* Copyright (c) 2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#ifndef t_cose_psa_crypto_h
#define t_cose_psa_crypto_h
#include <psa/crypto.h>
#define PSA_CRYPTO_HAS_RESTARTABLE_SIGNING \
((MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 4) || \
MBEDTLS_VERSION_MAJOR > 3)
#if PSA_CRYPTO_HAS_RESTARTABLE_SIGNING
struct t_cose_psa_crypto_context {
psa_sign_hash_interruptible_operation_t operation;
};
#endif /* PSA_CRYPTO_HAS_RESTARTABLE_SIGNING */
#endif /* t_cose_psa_crypto_h */

View File

@@ -0,0 +1,814 @@
/*
* t_cose_test_crypto.c
*
* Copyright 2019-2023, Laurence Lundblade
* Copyright (c) 2022-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*
* Created 3/31/2019.
*/
#include "t_cose_crypto.h"
#include "t_cose/t_cose_standard_constants.h"
#include "t_cose_test_crypto.h"
#include "t_cose_util.h"
#define SIGN_ITERATION_COUNT 5
/*
* This file is stub crypto for initial bring up and test of t_cose.
* It is NOT intended for commercial use. When this file is used as
* the crypto adapter, no external crypto library is necessary. This is
* convenient because sometimes it takes a while to sort out the crypto
* porting layer for a new platform. With this most of t_cose can be tested
* and demo signatures (short-circuit signatures) can be generated to
* simulate out this would work.
*
* This file uses no signature algorithm. It uses the Brad Conte hash
* implementation that is bundled with t_cose for the purpose of this
* testing, not for commercial use.
*/
/*
* See documentation in t_cose_crypto.h
*
* This will typically not be referenced and thus not linked,
* for deployed code. This is mainly used for test.
*/
bool
t_cose_crypto_is_algorithm_supported(int32_t cose_algorithm_id)
{
static const int32_t supported_algs[] = {
T_COSE_ALGORITHM_SHA_256,
#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
T_COSE_ALGORITHM_SHORT_CIRCUIT_256,
T_COSE_ALGORITHM_SHORT_CIRCUIT_384,
T_COSE_ALGORITHM_SHORT_CIRCUIT_512,
#endif /* !T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
T_COSE_ALGORITHM_NONE /* List terminator */
};
for(const int32_t *i = supported_algs; *i != T_COSE_ALGORITHM_NONE; i++) {
if(*i == cose_algorithm_id) {
return true;
}
}
return false;
}
static bool
t_cose_algorithm_is_short_circuit(int32_t cose_algorithm_id)
{
/* The simple list of COSE alg IDs that use ECDSA */
static const int32_t ecdsa_list[] = {
T_COSE_ALGORITHM_SHORT_CIRCUIT_256,
T_COSE_ALGORITHM_SHORT_CIRCUIT_384,
T_COSE_ALGORITHM_SHORT_CIRCUIT_512,
T_COSE_ALGORITHM_NONE
};
return t_cose_check_list(cose_algorithm_id, ecdsa_list);
}
/* The Brad Conte hash implementaiton bundled with t_cose */
#include "sha256.h"
/* Use of this file requires definition of T_COSE_USE_B_CON_SHA256 when
* making t_cose_crypto.h.
*
* This only implements SHA-256 as that is all that is needed for the
* non signing and verification tests using short-circuit signatures.
*/
#ifdef T_COSE_ENABLE_HASH_FAIL_TEST
/* Global variable just for this particular test. Not thread
* safe or good for commercial use.
*/
int hash_test_mode = 0;
#endif
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_sig_size(int32_t cose_algorithm_id,
struct t_cose_key signing_key,
size_t *sig_size)
{
(void)signing_key;
/* sizes are 2x to simulate an ECDSA signature */
*sig_size =
cose_algorithm_id == T_COSE_ALGORITHM_SHORT_CIRCUIT_256 ? 2 * 256/8 :
cose_algorithm_id == T_COSE_ALGORITHM_SHORT_CIRCUIT_384 ? 2 * 384/8 :
cose_algorithm_id == T_COSE_ALGORITHM_SHORT_CIRCUIT_512 ? 2 * 512/8 :
0;
return *sig_size == 0 ? T_COSE_ERR_UNSUPPORTED_SIGNING_ALG : T_COSE_SUCCESS;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_sign(int32_t cose_algorithm_id,
struct t_cose_key signing_key,
void *crypto_context,
struct q_useful_buf_c hash_to_sign,
struct q_useful_buf signature_buffer,
struct q_useful_buf_c *signature)
{
enum t_cose_err_t return_value;
size_t array_index;
size_t amount_to_copy;
size_t sig_size;
struct t_cose_test_crypto_context *cc = (struct t_cose_test_crypto_context *)crypto_context;
/* This is used for testing the crypto context */
if(cc != NULL && cc->test_error != T_COSE_SUCCESS) {
return cc->test_error;
}
/* This makes the short-circuit signature that is a concatenation
* of copies of the hash. */
return_value = t_cose_crypto_sig_size(cose_algorithm_id, signing_key, &sig_size);
if(return_value != T_COSE_SUCCESS) {
goto Done;
}
/* Check the signature length against buffer size */
if(sig_size > signature_buffer.len) {
/* Buffer too small for this signature type */
return_value = T_COSE_ERR_SIG_BUFFER_SIZE;
goto Done;
}
/* Loop concatening copies of the hash to fill out to signature size */
for(array_index = 0; array_index < sig_size; array_index += hash_to_sign.len) {
amount_to_copy = sig_size - array_index;
if(amount_to_copy > hash_to_sign.len) {
amount_to_copy = hash_to_sign.len;
}
memcpy((uint8_t *)signature_buffer.ptr + array_index,
hash_to_sign.ptr,
amount_to_copy);
}
signature->ptr = signature_buffer.ptr;
signature->len = sig_size;
return_value = T_COSE_SUCCESS;
Done:
return return_value;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_sign_restart(bool started,
int32_t cose_algorithm_id,
struct t_cose_key signing_key,
void *crypto_context,
struct q_useful_buf_c hash_to_sign,
struct q_useful_buf signature_buffer,
struct q_useful_buf_c *signature)
{
struct t_cose_test_crypto_context *cc = (struct t_cose_test_crypto_context *)crypto_context;
/* If this is the first iteration */
if(!started) {
cc->sign_iterations_left = SIGN_ITERATION_COUNT;
}
if(cc->sign_iterations_left-- > 1) {
return T_COSE_ERR_SIG_IN_PROGRESS;
}
return t_cose_crypto_sign(cose_algorithm_id,
signing_key,
crypto_context,
hash_to_sign,
signature_buffer,
signature);
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_verify(int32_t cose_algorithm_id,
struct t_cose_key verification_key,
void *crypto_context,
struct q_useful_buf_c hash_to_verify,
struct q_useful_buf_c signature)
{
struct q_useful_buf_c hash_from_sig;
enum t_cose_err_t return_value;
struct t_cose_test_crypto_context *cc = (struct t_cose_test_crypto_context *)crypto_context;
(void)verification_key;
/* This is used for testing the crypto context */
if(cc != NULL && cc->test_error != T_COSE_SUCCESS) {
return cc->test_error;
}
if(!t_cose_algorithm_is_short_circuit(cose_algorithm_id)) {
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
hash_from_sig = q_useful_buf_head(signature, hash_to_verify.len);
if(q_useful_buf_c_is_null(hash_from_sig)) {
return_value = T_COSE_ERR_SIG_VERIFY;
goto Done;
}
if(q_useful_buf_compare(hash_from_sig, hash_to_verify)) {
return_value = T_COSE_ERR_SIG_VERIFY;
} else {
return_value = T_COSE_SUCCESS;
}
Done:
return return_value;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
int32_t cose_hash_alg_id)
{
#ifdef T_COSE_ENABLE_HASH_FAIL_TEST
if(hash_test_mode == 1) {
return T_COSE_ERR_HASH_GENERAL_FAIL;
}
#endif
if(cose_hash_alg_id != T_COSE_ALGORITHM_SHA_256) {
return T_COSE_ERR_UNSUPPORTED_HASH;
}
sha256_init(&(hash_ctx->b_con_hash_context));
return 0;
}
/*
* See documentation in t_cose_crypto.h
*/
void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
struct q_useful_buf_c data_to_hash)
{
if(data_to_hash.ptr) {
sha256_update(&(hash_ctx->b_con_hash_context),
data_to_hash.ptr,
data_to_hash.len);
}
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
struct q_useful_buf buffer_to_hold_result,
struct q_useful_buf_c *hash_result)
{
#ifdef T_COSE_ENABLE_HASH_FAIL_TEST
if(hash_test_mode == 2) {
return T_COSE_ERR_HASH_GENERAL_FAIL;
}
#endif
sha256_final(&(hash_ctx->b_con_hash_context), buffer_to_hold_result.ptr);
*hash_result = (UsefulBufC){buffer_to_hold_result.ptr, 32};
return 0;
}
enum t_cose_err_t
t_cose_crypto_hmac_setup(struct t_cose_crypto_hmac *hmac_ctx,
struct t_cose_key signing_key,
const int32_t cose_alg_id)
{
(void)hmac_ctx;
(void)signing_key;
(void)cose_alg_id;
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
void
t_cose_crypto_hmac_update(struct t_cose_crypto_hmac *hmac_ctx,
struct q_useful_buf_c payload)
{
(void)hmac_ctx;
(void)payload;
}
enum t_cose_err_t
t_cose_crypto_hmac_finish(struct t_cose_crypto_hmac *hmac_ctx,
struct q_useful_buf tag_buf,
struct q_useful_buf_c *tag)
{
(void)hmac_ctx;
(void)tag_buf;
(void)tag;
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_sign_eddsa(struct t_cose_key signing_key,
void *crypto_context,
struct q_useful_buf_c tbs,
struct q_useful_buf signature_buffer,
struct q_useful_buf_c *signature)
{
(void)signing_key;
(void)crypto_context;
(void)tbs;
(void)signature_buffer;
(void)signature;
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_verify_eddsa(struct t_cose_key verification_key,
void *crypto_context,
struct q_useful_buf_c tbs,
struct q_useful_buf_c signature)
{
(void)verification_key;
(void)crypto_context;
(void)tbs;
(void)signature;
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_generate_ec_key(const int32_t cose_ec_curve_id,
struct t_cose_key *key)
{
(void)key;
(void)cose_ec_curve_id;
return T_COSE_ERR_KEY_GENERATION_FAILED;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_get_random(struct q_useful_buf buffer,
size_t number,
struct q_useful_buf_c *random)
{
if (number > buffer.len) {
return(T_COSE_ERR_TOO_SMALL);
}
/* In test mode this just fills a buffer with 'x' */
memset(buffer.ptr, 'x', number);
random->ptr = buffer.ptr;
random->len = number;
return T_COSE_SUCCESS;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_make_symmetric_key_handle(int32_t cose_algorithm_id,
struct q_useful_buf_c symmetric_key,
struct t_cose_key *key_handle)
{
(void)cose_algorithm_id;
key_handle->key.buffer = symmetric_key;
return T_COSE_SUCCESS;
}
/*
* See documentation in t_cose_crypto.h
*/
void
t_cose_crypto_free_symmetric_key(struct t_cose_key key)
{
(void)key;
}
/* Compute size of ciphertext, given size of plaintext. Returns
* SIZE_MAX if the algorithm is unknown. Also returns the tag
* length. */
static size_t
aead_byte_count(const int32_t cose_algorithm_id,
size_t plain_text_len)
{
/* So far this just works for GCM AEAD algorithms, but can be
* augmented for others.
*
* For GCM as used by COSE and HPKE, the authentication tag is
* appended to the end of the cipher text and is always 16 bytes.
* Since GCM is a variant of counter mode, the ciphertext length
* is the same as the plaintext length. (This is not true of other
* ciphers).
* https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
*/
/* The same tag length for all COSE and HPKE AEAD algorithms supported.*/
const size_t common_gcm_tag_length = 16;
switch(cose_algorithm_id) {
case T_COSE_ALGORITHM_A128GCM:
return plain_text_len + common_gcm_tag_length;
case T_COSE_ALGORITHM_A192GCM:
return plain_text_len + common_gcm_tag_length;
case T_COSE_ALGORITHM_A256GCM:
return plain_text_len + common_gcm_tag_length;
default: return SIZE_MAX;;
}
}
#define FAKE_TAG "tagtagtagtagtagt"
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_aead_encrypt(const int32_t cose_algorithm_id,
struct t_cose_key key,
struct q_useful_buf_c nonce,
struct q_useful_buf_c aad,
struct q_useful_buf_c plaintext,
struct q_useful_buf ciphertext_buffer,
struct q_useful_buf_c *ciphertext)
{
struct q_useful_buf_c tag = Q_USEFUL_BUF_FROM_SZ_LITERAL(FAKE_TAG);
(void)nonce;
(void)aad;
(void)cose_algorithm_id;
(void)key;
if(ciphertext_buffer.ptr == NULL) {
/* Called in length calculation mode. Return length & exit. */
ciphertext->len = aead_byte_count(cose_algorithm_id,
plaintext.len);;
return T_COSE_SUCCESS;
}
/* Use useful output to copy the plaintext as pretend encryption
* and add "tagtag.." as a pretend tag.*/
UsefulOutBuf UOB;
UsefulOutBuf_Init(&UOB, ciphertext_buffer);
UsefulOutBuf_AppendUsefulBuf(&UOB, plaintext);
UsefulOutBuf_AppendUsefulBuf(&UOB, tag);
*ciphertext = UsefulOutBuf_OutUBuf(&UOB);
if(q_useful_buf_c_is_null(*ciphertext)) {
return T_COSE_ERR_TOO_SMALL;
}
return T_COSE_SUCCESS;
}
/* Compute size of ciphertext, given size of plaintext. Returns
* SIZE_MAX if the algorithm is unknown. Also returns the tag
* length. */
static size_t
non_aead_byte_count(const int32_t cose_algorithm_id,
size_t plain_text_len)
{
/* This works for CTR (counter) and CBC, non AEAD algorithms,
* but can be augmented for others.
*
* For both CTR and CBC as used by COSE and HPKE, the authentication tag is not
* appended.
*
* For CTR the ciphertext length is the same as the plaintext length.
* (This is not true of other ciphers).
* https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
*
* For CBC the plaintext will be padded from 1 bytes to the block size.
* The block size is 16 bytes for all A128CBC, A192CBC and A256CBC.
* If the plaintext size is 30 bytes for A128CBC, 2 bytes padding will be inserted,
* and each padding byte has value 0x02.
*/
switch(cose_algorithm_id) {
case T_COSE_ALGORITHM_A128CTR:
case T_COSE_ALGORITHM_A192CTR:
case T_COSE_ALGORITHM_A256CTR:
return plain_text_len;
case T_COSE_ALGORITHM_A128CBC:
case T_COSE_ALGORITHM_A192CBC:
case T_COSE_ALGORITHM_A256CBC:
return plain_text_len + (16 - plain_text_len % 16);
default:
return SIZE_MAX;
}
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_non_aead_encrypt(const int32_t cose_algorithm_id,
struct t_cose_key key,
struct q_useful_buf_c nonce,
struct q_useful_buf_c plaintext,
struct q_useful_buf ciphertext_buffer,
struct q_useful_buf_c *ciphertext)
{
struct q_useful_buf_c tag = Q_USEFUL_BUF_FROM_SZ_LITERAL(FAKE_TAG);
(void)nonce;
(void)cose_algorithm_id;
(void)key;
if(ciphertext_buffer.ptr == NULL) {
/* Called in length calculation mode. Return length & exit. */
ciphertext->len = non_aead_byte_count(cose_algorithm_id,
plaintext.len);;
return T_COSE_SUCCESS;
}
/* Use useful output to copy the plaintext as pretend encryption
* and add "tagtag.." as a pretend tag.*/
UsefulOutBuf UOB;
UsefulOutBuf_Init(&UOB, ciphertext_buffer);
UsefulOutBuf_AppendUsefulBuf(&UOB, plaintext);
UsefulOutBuf_AppendUsefulBuf(&UOB, tag);
*ciphertext = UsefulOutBuf_OutUBuf(&UOB);
if(q_useful_buf_c_is_null(*ciphertext)) {
return T_COSE_ERR_TOO_SMALL;
}
return T_COSE_SUCCESS;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_aead_decrypt(const int32_t cose_algorithm_id,
struct t_cose_key key,
struct q_useful_buf_c nonce,
struct q_useful_buf_c aad,
struct q_useful_buf_c ciphertext,
struct q_useful_buf plaintext_buffer,
struct q_useful_buf_c *plaintext)
{
struct q_useful_buf_c expected_tag = Q_USEFUL_BUF_FROM_SZ_LITERAL(FAKE_TAG);
struct q_useful_buf_c received_tag;
struct q_useful_buf_c received_plaintext;
(void)nonce;
(void)aad;
(void)cose_algorithm_id;
(void)key;
UsefulInputBuf UIB;
UsefulInputBuf_Init(&UIB, ciphertext);
if(ciphertext.len < expected_tag.len) {
return T_COSE_ERR_DECRYPT_FAIL;
}
received_plaintext = UsefulInputBuf_GetUsefulBuf(&UIB, ciphertext.len - expected_tag.len);
received_tag = UsefulInputBuf_GetUsefulBuf(&UIB, expected_tag.len);
if(q_useful_buf_compare(expected_tag, received_tag)) {
return T_COSE_ERR_DATA_AUTH_FAILED;
}
*plaintext = q_useful_buf_copy(plaintext_buffer, received_plaintext);
if(q_useful_buf_c_is_null(*plaintext)) {
return T_COSE_ERR_TOO_SMALL;
}
return T_COSE_SUCCESS;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_non_aead_decrypt(const int32_t cose_algorithm_id,
struct t_cose_key key,
struct q_useful_buf_c nonce,
struct q_useful_buf_c ciphertext,
struct q_useful_buf plaintext_buffer,
struct q_useful_buf_c *plaintext)
{
struct q_useful_buf_c expected_tag = Q_USEFUL_BUF_FROM_SZ_LITERAL(FAKE_TAG);
struct q_useful_buf_c received_tag;
struct q_useful_buf_c received_plaintext;
(void)nonce;
(void)cose_algorithm_id;
(void)key;
UsefulInputBuf UIB;
UsefulInputBuf_Init(&UIB, ciphertext);
if(ciphertext.len < expected_tag.len) {
return T_COSE_ERR_DECRYPT_FAIL;
}
received_plaintext = UsefulInputBuf_GetUsefulBuf(&UIB, ciphertext.len - expected_tag.len);
received_tag = UsefulInputBuf_GetUsefulBuf(&UIB, expected_tag.len);
if(q_useful_buf_compare(expected_tag, received_tag)) {
return T_COSE_ERR_DATA_AUTH_FAILED;
}
*plaintext = q_useful_buf_copy(plaintext_buffer, received_plaintext);
if(q_useful_buf_c_is_null(*plaintext)) {
return T_COSE_ERR_TOO_SMALL;
}
return T_COSE_SUCCESS;
}
static const uint8_t rfc_3394_key_wrap_iv[] = {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6};
enum t_cose_err_t
t_cose_crypto_kw_wrap(int32_t cose_algorithm_id,
struct t_cose_key kek,
struct q_useful_buf_c plaintext,
struct q_useful_buf ciphertext_buffer,
struct q_useful_buf_c *ciphertext_result)
{
UsefulOutBuf UOB;
(void)cose_algorithm_id;
(void)kek;
UsefulOutBuf_Init(&UOB, ciphertext_buffer);
UsefulOutBuf_AppendUsefulBuf(&UOB, plaintext);
UsefulOutBuf_AppendUsefulBuf(&UOB, Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(rfc_3394_key_wrap_iv));
*ciphertext_result = UsefulOutBuf_OutUBuf(&UOB);
if(q_useful_buf_c_is_null(*ciphertext_result)){
return T_COSE_ERR_TOO_SMALL;
}
return T_COSE_SUCCESS;
}
enum t_cose_err_t
t_cose_crypto_kw_unwrap(int32_t cose_algorithm_id,
struct t_cose_key kek,
struct q_useful_buf_c ciphertext,
struct q_useful_buf plaintext_buffer,
struct q_useful_buf_c *plaintext_result)
{
UsefulBufC tag;
struct q_useful_buf_c plain_text;
const struct q_useful_buf_c expected_tag = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(rfc_3394_key_wrap_iv);
(void)cose_algorithm_id;
(void)kek;
UsefulInputBuf UIB;
UsefulInputBuf_Init(&UIB, ciphertext);
plain_text = UsefulInputBuf_GetUsefulBuf(&UIB, ciphertext.len - expected_tag.len);
tag = UsefulInputBuf_GetUsefulBuf(&UIB, expected_tag.len);
if(UsefulBuf_Compare(tag, expected_tag)) {
return T_COSE_ERR_DATA_AUTH_FAILED;
}
*plaintext_result = UsefulBuf_Copy(plaintext_buffer, plain_text);
if(q_useful_buf_c_is_null(*plaintext_result)) {
return T_COSE_ERR_TOO_SMALL;
}
return T_COSE_SUCCESS;
}
enum t_cose_err_t
t_cose_crypto_hkdf(const int32_t cose_hash_algorithm_id,
const struct q_useful_buf_c salt,
const struct q_useful_buf_c ikm,
const struct q_useful_buf_c info,
const struct q_useful_buf okm_buffer)
{
(void)cose_hash_algorithm_id;
(void)salt;
(void)ikm;
(void)info;
/* This makes a fixed fake output of all x's */
(void)UsefulBuf_Set(okm_buffer, 'x');
return T_COSE_SUCCESS;
}
/*
* See documentation in t_cose_crypto.h
*/
enum t_cose_err_t
t_cose_crypto_import_ec2_pubkey(int32_t cose_ec_curve_id,
struct q_useful_buf_c x_coord,
struct q_useful_buf_c y_coord,
bool y_bool,
struct t_cose_key *pub_key)
{
(void)cose_ec_curve_id;
(void)x_coord;
(void)y_coord;
(void)y_bool;
(void)pub_key;
return T_COSE_ERR_FAIL;
}
enum t_cose_err_t
t_cose_crypto_export_ec2_key(struct t_cose_key pub_key,
int32_t *curve,
struct q_useful_buf x_coord_buf,
struct q_useful_buf_c *x_coord,
struct q_useful_buf y_coord_buf,
struct q_useful_buf_c *y_coord,
bool *y_bool)
{
(void)curve;
(void)x_coord;
(void)x_coord_buf;
(void)y_coord_buf;
(void)y_coord;
(void)y_bool;
(void)pub_key;
return T_COSE_ERR_FAIL;
}
enum t_cose_err_t
t_cose_crypto_ecdh(struct t_cose_key private_key,
struct t_cose_key public_key,
struct q_useful_buf shared_key_buf,
struct q_useful_buf_c *shared_key)
{
(void)private_key;
(void)public_key;
(void)shared_key_buf;
(void)shared_key;
return T_COSE_ERR_FAIL;
}
void
t_cose_crypto_free_ec_key(struct t_cose_key key_handle)
{
(void)key_handle;
}

View File

@@ -0,0 +1,29 @@
/*
* t_cose_test_crypto.h
*
* Copyright 2022, Laurence Lundblade
* Copyright (c) 2023, Arm Limited. All rights reserved.
* Created by Laurence Lundblade on 12/9/22.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#ifndef t_cose_test_crypto_h
#define t_cose_test_crypto_h
struct t_cose_test_crypto_context {
/* This is used to test the crypto_context feature. If its
* value is SUCCESS, then operation is as normal. If it's
* value is something else, then that error is returned.
*/
enum t_cose_err_t test_error;
/* This is used to test the restartable behaviour of t_cose. If its value
* is greater than 1 when operating in restartable mode, then
* T_COSE_ERR_SIG_IN_PROGRESS is returned instead of T_COSE_SUCCESS.
*/
size_t sign_iterations_left;
};
#endif /* t_cose_test_crypto_h */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,460 @@
/*
* encryption_examples.c
*
* Copyright (c) 2022, Arm Limited. All rights reserved.
* Copyright 2025, Laurence Lundblade
*
* Created by Laurence Lundblade on 2/6/23 from previous files.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include "encryption_examples.h"
#include "t_cose/t_cose_common.h"
#include "t_cose/t_cose_encrypt_enc.h"
#include "t_cose/t_cose_encrypt_dec.h"
#include "t_cose/t_cose_key.h"
#include <stdio.h>
#include "print_buf.h"
#include "init_keys.h"
#define PAYLOAD "This is the payload"
#define TEST_SENDER_IDENTITY "sender"
#define TEST_RECIPIENT_IDENTITY "recipient"
/* This file is crypto-library independent. It works for OpenSSL, Mbed
* TLS and others. The key initialization, which *is* crypto-library
* dependent, has been separated.
*
* Each example should pretty much stand on its own and be pretty
* clean and well-commented code. Its purpose is to be an example
* (not a test case). Someone should be able to easily copy the
* example as a starting point for their use case.
*/
int32_t
encrypt0_example(void)
{
struct t_cose_encrypt_enc enc_context;
enum t_cose_err_t err;
struct t_cose_key cek;
struct q_useful_buf_c encrypted_cose_message;
struct q_useful_buf_c decrypted_cose_message;
struct q_useful_buf_c encrypted_payload;
Q_USEFUL_BUF_MAKE_STACK_UB( cose_message_buf, 1024);
Q_USEFUL_BUF_MAKE_STACK_UB( encrypted_payload_buf, 1024);
Q_USEFUL_BUF_MAKE_STACK_UB( decrypted_payload_buf, 1024);
struct t_cose_encrypt_dec_ctx dec_ctx;
printf("\n---- START EXAMPLE encrypt0 ----\n");
printf("Create COSE_Encrypt0 with detached payload using A128GCM\n");
/* This is the simplest form of COSE encryption, a COSE_Encrypt0.
* It has only headers and the ciphertext.
*
* Further in this example, the ciphertext is detached, so the
* COSE_Encrypt0 only consists of the protected and unprotected
* headers and a CBOR NULL where the ciphertext usually
* occurs. The ciphertext is output separatly and conveyed
* separately.
*
*/
t_cose_encrypt_enc_init(&enc_context,
T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0,
T_COSE_ALGORITHM_A128GCM);
/* For COSE_Encrypt0, we simply make a t_cose_key for the
* content encryption key, the CEK, and give it to t_cose. It's
* the only key there is and it is simply a key to be used with
* AES, a string of bytes. (It is still t_cose_key, not a byte string
* so it can be a PSA key handle so it can be used with with
* an encryption implementation that doesn't allow the key to
* leave a protected domain, an HSM for example).
*
* There is no COSE_Recipient so t_cose_encrypt_add_recipient() is
* not called.
*
* No kid is provided in line with the examples of Encrypt0
* in RFC 9052. RFC 9052 text describing Encrypt0 also implies that
* no kid should be needed, but it doesn't seem to prohibit
* the kid header and t_cose will allow it to be present.
*/
t_cose_key_init_symmetric(T_COSE_ALGORITHM_A128GCM,
Q_USEFUL_BUF_FROM_SZ_LITERAL("aaaaaaaaaaaaaaaa"),
&cek);
t_cose_encrypt_set_cek(&enc_context, cek);
err = t_cose_encrypt_enc_detached(&enc_context,
Q_USEFUL_BUF_FROM_SZ_LITERAL("This is a real plaintext."),
NULL_Q_USEFUL_BUF_C,
encrypted_payload_buf,
cose_message_buf,
&encrypted_payload,
&encrypted_cose_message);
if(err != T_COSE_SUCCESS) {
goto Done;
}
print_useful_buf("COSE_Encrypt0: ", encrypted_cose_message);
print_useful_buf("Detached ciphertext: ", encrypted_payload);
printf("\n");
printf("Completed encryption; starting decryption\n");
t_cose_encrypt_dec_init(&dec_ctx, T_COSE_OPT_MESSAGE_TYPE_ENCRYPT0);
t_cose_encrypt_dec_set_cek(&dec_ctx, cek);
err = t_cose_encrypt_dec_detached_msg(&dec_ctx,
encrypted_cose_message,
NULL_Q_USEFUL_BUF_C,
encrypted_payload,
decrypted_payload_buf,
&decrypted_cose_message,
NULL,
NULL);
if (err != T_COSE_SUCCESS) {
printf("\nDecryption failed %d!\n", err);
goto Done;
}
print_useful_buf("Plaintext: ", decrypted_cose_message);
Done:
printf("---- %s EXAMPLE encrypt0 (%d) ----\n\n",
err ? "FAILED" : "COMPLETED", err);
return (int32_t)err;
}
#ifndef T_COSE_DISABLE_KEYWRAP
#include "t_cose/t_cose_recipient_enc_keywrap.h"
#include "t_cose/t_cose_recipient_dec_keywrap.h"
int32_t
key_wrap_example(void)
{
struct t_cose_recipient_enc_keywrap kw_recipient;
struct t_cose_encrypt_enc enc_context;
struct t_cose_recipient_dec_keywrap kw_unwrap_recipient;
struct t_cose_encrypt_dec_ctx dec_context;
enum t_cose_err_t err;
struct t_cose_key kek;
struct q_useful_buf_c encrypted_cose_message;
struct q_useful_buf_c encrypted_payload;
struct q_useful_buf_c decrypted_payload;
Q_USEFUL_BUF_MAKE_STACK_UB( cose_message_buf, 1024);
Q_USEFUL_BUF_MAKE_STACK_UB( encrypted_payload_buf, 1024);
Q_USEFUL_BUF_MAKE_STACK_UB( decrypted_payload_buf, 1024);
printf("\n---- START EXAMPLE key_wrap ----\n");
printf("Create COSE_Encrypt with detached payload using AES-KW\n");
/* ---- Make key handle for wrapping key -----
*
* The wrapping key, the KEK, is just the bytes "aaaa....". The
* API requires input keys be struct t_cose_key so there's a
* little work to do here.
*/
err = t_cose_key_init_symmetric(T_COSE_ALGORITHM_A128KW,
Q_USEFUL_BUF_FROM_SZ_LITERAL("aaaaaaaaaaaaaaaa"),
&kek);
if(err) {
goto Done;
}
/* ---- Set up keywrap recipient object ----
*
* The initializes an object of type struct
* t_cose_recipient_enc_keywrap, the object/context for making a
* COSE_Recipient for key wrap.
*
* We have to tell it the key wrap algorithm and give it the key
* and kid.
*
* This object gets handed to the main encryption API which will
* excersize it through a callback to create the COSE_Recipient.
*/
t_cose_recipient_enc_keywrap_init(&kw_recipient, T_COSE_ALGORITHM_A128KW);
t_cose_recipient_enc_keywrap_set_key(&kw_recipient,
kek,
Q_USEFUL_BUF_FROM_SZ_LITERAL("Kid A"));
/* ----- Set up to make COSE_Encrypt ----
*
* Initialize. Have to say what algorithm is used to encrypt the
* main content, the payload.
*
* Also tell the encryptor about the object to make the key wrap
* COSE_Recipient by just giving it the pointer to it. It will get
* called back in the next step.
*/
t_cose_encrypt_enc_init(&enc_context, T_COSE_OPT_MESSAGE_TYPE_ENCRYPT, T_COSE_ALGORITHM_A128GCM);
t_cose_encrypt_add_recipient(&enc_context, (struct t_cose_recipient_enc *)&kw_recipient);
/* ---- Actually Encrypt ----
*
* All the crypto gets called here including the encryption of the
* payload and the key wrap.
*
* There are two buffers given, one for just the encrypted
* payload and one for the COSE message. TODO: detached vs not and sizing.
*/
err = t_cose_encrypt_enc_detached(&enc_context,
Q_USEFUL_BUF_FROM_SZ_LITERAL("This is a real plaintext."),
NULL_Q_USEFUL_BUF_C,
encrypted_payload_buf,
cose_message_buf,
&encrypted_payload,
&encrypted_cose_message);
if (err != 0) {
goto Done;
}
print_useful_buf("COSE_Encrypt: ", encrypted_cose_message);
print_useful_buf("Detached Ciphertext: ", encrypted_payload);
printf("\n");
t_cose_encrypt_dec_init(&dec_context, T_COSE_OPT_MESSAGE_TYPE_ENCRYPT);
t_cose_recipient_dec_keywrap_init(&kw_unwrap_recipient);
t_cose_recipient_dec_keywrap_set_kek(&kw_unwrap_recipient, kek, NULL_Q_USEFUL_BUF_C);
t_cose_encrypt_dec_add_recipient(&dec_context, (struct t_cose_recipient_dec *)&kw_unwrap_recipient);
err = t_cose_encrypt_dec_detached_msg(&dec_context,
encrypted_cose_message, /* ciphertext */
NULL_Q_USEFUL_BUF_C,
encrypted_payload,
decrypted_payload_buf,
&decrypted_payload,
NULL,
NULL);
if(err) {
goto Done;
}
print_useful_buf("Decrypted Payload:", decrypted_payload);
Done:
printf("---- %s EXAMPLE key_wrap (%d) ----\n\n",
err ? "FAILED" : "COMPLETED", err);
return (int32_t)err;
}
#endif /* !T_COSE_DISABLE_KEYWRAP */
#ifndef T_COSE_DISABLE_ESDH
#include "t_cose/t_cose_recipient_enc_esdh.h"
#include "t_cose/t_cose_recipient_dec_esdh.h"
int32_t
esdh_example(void)
{
enum t_cose_err_t result;
struct t_cose_key privatekey;
struct t_cose_key publickey;
struct t_cose_encrypt_enc enc_ctx;
struct t_cose_recipient_enc_esdh recipient;
struct q_useful_buf_c cose_encrypted_message;
Q_USEFUL_BUF_MAKE_STACK_UB ( cose_encrypt_message_buffer, 400);
struct t_cose_encrypt_dec_ctx dec_ctx;
struct t_cose_recipient_dec_esdh dec_recipient;
Q_USEFUL_BUF_MAKE_STACK_UB ( decrypted_buffer, 400);
struct q_useful_buf_c decrypted_payload;
struct t_cose_parameter *params;
printf("\n---- START EXAMPLE ESDH ----\n");
printf("Create COSE_Encrypt with attached payload using ESDH\n");
/* Create a key pair. This is a fixed test key pair. The creation
* of this key pair is crypto-library dependent because t_cose_key
* is crypto-library dependent. See t_cose_key.h and the examples
* to understand key-pair creation better. */
result = init_fixed_test_ec_encryption_key(T_COSE_ELLIPTIC_CURVE_P_256,
&publickey, /* out: public key to be used for encryption */
&privatekey); /* out: corresponding private key for decryption */
if(result != T_COSE_SUCCESS) {
goto Done;
}
/* Initialize the encryption context telling it we want
* a COSE_Encrypt (not a COSE_Encrypt0) because we're doing ECDH with a
* COSE_Recipient. Also tell it the AEAD algorithm for the
* body of the message.
*/
t_cose_encrypt_enc_init(&enc_ctx,
T_COSE_OPT_MESSAGE_TYPE_ENCRYPT,
T_COSE_ALGORITHM_A128GCM);
/* Create the recipient object telling it the algorithm and the public key
* for the COSE_Recipient it's going to make.
*/
t_cose_recipient_enc_esdh_init(&recipient,
T_COSE_ALGORITHM_ECDH_ES_A128KW, /* content key distribution id */
T_COSE_ELLIPTIC_CURVE_P_256); /* curve id */
t_cose_recipient_enc_esdh_set_key(&recipient,
publickey,
Q_USEFUL_BUF_FROM_SZ_LITERAL(TEST_RECIPIENT_IDENTITY));
/* Give the recipient object to the main encryption context.
* (Only one recipient is set here, but there could be more).
*/
t_cose_encrypt_add_recipient(&enc_ctx,
(struct t_cose_recipient_enc *)&recipient);
/* Now do the actual encryption */
result = t_cose_encrypt_enc(&enc_ctx, /* in: encryption context */
Q_USEFUL_BUF_FROM_SZ_LITERAL(PAYLOAD), /* in: payload to encrypt */
NULL_Q_USEFUL_BUF_C, /* in/unused: AAD */
cose_encrypt_message_buffer, /* in: buffer for COSE_Encrypt */
&cose_encrypted_message); /* out: COSE_Encrypt */
if (result != T_COSE_SUCCESS) {
printf("error encrypting (%d)\n", result);
goto Done;
}
print_useful_buf("\nCOSE_Encrypt: ", cose_encrypted_message);
t_cose_encrypt_dec_init(&dec_ctx, 0);
t_cose_recipient_dec_esdh_init(&dec_recipient);
t_cose_recipient_dec_esdh_set_key(&dec_recipient, privatekey, NULL_Q_USEFUL_BUF_C);
t_cose_encrypt_dec_add_recipient(&dec_ctx,
(struct t_cose_recipient_dec *)&dec_recipient);
result = t_cose_encrypt_dec_msg(&dec_ctx,
cose_encrypted_message,
NULL_Q_USEFUL_BUF_C, /* in/unused: AAD */
decrypted_buffer,
&decrypted_payload,
&params,
NULL);
if(result != T_COSE_SUCCESS) {
goto Done;
}
print_useful_buf("Decrypted Payload:", decrypted_payload);
Done:
printf("---- %s EXAMPLE ESDH (%d) ----\n\n",
result ? "FAILED" : "COMPLETED", result);
return (int32_t)result;
}
int32_t
esdh_example_detached(void)
{
enum t_cose_err_t result;
struct t_cose_key skR;
struct t_cose_key pkR;
struct t_cose_encrypt_enc enc_ctx;
struct t_cose_recipient_enc_esdh recipient;
struct q_useful_buf_c cose_encrypted_message;
Q_USEFUL_BUF_MAKE_STACK_UB ( cose_encrypt_message_buffer, 400);
struct q_useful_buf_c encrypted_detached_payload;
Q_USEFUL_BUF_MAKE_STACK_UB ( encrypted_detached_ciphertext_buffer, 50);
printf("\n---- START EXAMPLE ESDH ----\n");
printf("Create COSE_Encrypt with detached payload using ESDH\n");
/* Create a key pair. This is a fixed test key pair. The creation
* of this key pair is crypto-library dependent because t_cose_key
* is crypto-library dependent. See t_cose_key.h and the examples
* to understand key-pair creation better. */
result = init_fixed_test_ec_encryption_key(T_COSE_ELLIPTIC_CURVE_P_256,
&pkR, /* out: public key to be used for encryption */
&skR); /* out: corresponding private key for decryption */
if(result != T_COSE_SUCCESS) {
goto Done;
}
/* Initialize the encryption context telling it we want
* a COSE_Encrypt (not a COSE_Encrypt0) because we're doing ECDH with a
* COSE_Recipient. Also tell it the AEAD algorithm for the
* body of the message.
*/
t_cose_encrypt_enc_init(&enc_ctx,
T_COSE_OPT_MESSAGE_TYPE_ENCRYPT,
T_COSE_ALGORITHM_A128GCM);
/* Create the recipient object telling it the algorithm and the public key
* for the COSE_Recipient it's going to make.
*/
t_cose_recipient_enc_esdh_init(&recipient,
T_COSE_ALGORITHM_ECDH_ES_A128KW, /* content key distribution id */
T_COSE_ELLIPTIC_CURVE_P_256); /* curve id */
t_cose_recipient_enc_esdh_set_key(&recipient,
pkR,
Q_USEFUL_BUF_FROM_SZ_LITERAL(TEST_RECIPIENT_IDENTITY));
/* Give the recipient object to the main encryption context.
* (Only one recipient is set here, but there could be more).
*/
t_cose_encrypt_add_recipient(&enc_ctx,
(struct t_cose_recipient_enc *)&recipient);
/* Now do the actual encryption */
result = t_cose_encrypt_enc_detached(&enc_ctx, /* in: encryption context */
Q_USEFUL_BUF_FROM_SZ_LITERAL(PAYLOAD), /* in: payload to encrypt */
NULL_Q_USEFUL_BUF_C, /* in/unused: AAD */
encrypted_detached_ciphertext_buffer, /* in: buffer for detached ciphertext */
cose_encrypt_message_buffer, /* in: buffer for COSE_Encrypt */
&encrypted_detached_payload, /* out: encrypted detached */
&cose_encrypted_message); /* out: COSE_Encrypt */
if (result != T_COSE_SUCCESS) {
printf("error encrypting (%d)\n", result);
goto Done;
}
print_useful_buf("COSE_Encrypt: ", cose_encrypted_message);
print_useful_buf("Detached Ciphertext: ", encrypted_detached_payload);
/* TBD: Decryption goes in here.
* Assume everything worked fine.
*/
result = 0;
Done:
printf("---- %s EXAMPLE ESDH (%d) ----\n\n",
result ? "FAILED" : "COMPLETED", result);
/* Free test keys */
free_fixed_test_ec_encryption_key(pkR);
free_fixed_test_ec_encryption_key(skR);
return (int32_t)result;
}
#endif /* !T_COSE_DISABLE_ESDH */

View File

@@ -0,0 +1,27 @@
/*
* encryption_examples.h
*
* Copyright (c) 2022, Arm Limited. All rights reserved.
* Copyright 2023, Laurence Lundblade
*
* Created by Laurence Lundblade on 2/6/23 from previous files.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#ifndef encryption_examples_h
#define encryption_examples_h
#include <stdint.h>
int32_t key_wrap_example(void);
int32_t encrypt0_example(void);
int32_t esdh_example(void);
int32_t esdh_example_detached(void);
#endif /* encryption_examples_h */

View File

@@ -0,0 +1,569 @@
/*
* example_keys.c
*
* Copyright 2023, Laurence Lundblade
*
* Created by Laurence Lundblade on 6/13/23.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include "example_keys.h"
/*
* See example_keys.h for documentation.
*
* All keys here are const byte arrays.
*
* In t_cose source, always 8 bytes per line.
*/
const unsigned char ec_P_256_key_pair_der[121] = {
/* Sequence */
0x30, 0x77,
/* Version number */
0x02, 0x01, 0x01,
/* Private key DER intro */
0x04, 0x20,
/* Private key bytes */
0xd9,
0xb5, 0xe7, 0x1f, 0x77, 0x28, 0xbf, 0xe5, 0x63,
0xa9, 0xdc, 0x93, 0x75, 0x62, 0x27, 0x7e, 0x32,
0x7d, 0x98, 0xd9, 0x94, 0x80, 0xf3, 0xdc, 0x92,
0x41, 0xe5, 0x74, 0x2a, 0xc4, 0x58, 0x89,
/* OID for prime256v1 */
0xa0,
0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07,
/* Public key DER intro and padding */
0xa1, 0x44, 0x03, 0x42, 0x00,
/* SEC Serialization of X and Y */
0x04,
/* X */
0x40, 0x41, 0x6c, 0x8c, 0xda, 0xa0, 0xf7,
0xa1, 0x75, 0x69, 0x55, 0x53, 0xc3, 0x27, 0x9c,
0x10, 0x9c, 0xe9, 0x27, 0x7e, 0x53, 0xc5, 0x86,
0x2a, 0xa7, 0x15, 0xed, 0xc6, 0x36, 0xf1, 0x71,
0xca,
/* Y */
0x32, 0xf1, 0x76, 0x43, 0x54, 0x96, 0x15,
0xe5, 0xc8, 0x34, 0x0d, 0x43, 0x32, 0xdd, 0x13,
0x77, 0x8a, 0xec, 0x87, 0x15, 0x76, 0xa3, 0x3c,
0x26, 0x08, 0x6c, 0x32, 0x0c, 0x9f, 0xf3, 0x3f,
0xc7
};
const unsigned char ec_P_256_priv_key_sec1[32] = {
0xd9, 0xb5, 0xe7, 0x1f, 0x77, 0x28, 0xbf, 0xe5,
0x63, 0xa9, 0xdc, 0x93, 0x75, 0x62, 0x27, 0x7e,
0x32, 0x7d, 0x98, 0xd9, 0x94, 0x80, 0xf3, 0xdc,
0x92, 0x41, 0xe5, 0x74, 0x2a, 0xc4, 0x58, 0x89
};
const unsigned char ec_P_256_pub_key_der[91] = {
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
0x42, 0x00, 0x04, 0x40, 0x41, 0x6c, 0x8c, 0xda, 0xa0, 0xf7, 0xa1, 0x75,
0x69, 0x55, 0x53, 0xc3, 0x27, 0x9c, 0x10, 0x9c, 0xe9, 0x27, 0x7e, 0x53,
0xc5, 0x86, 0x2a, 0xa7, 0x15, 0xed, 0xc6, 0x36, 0xf1, 0x71, 0xca, 0x32,
0xf1, 0x76, 0x43, 0x54, 0x96, 0x15, 0xe5, 0xc8, 0x34, 0x0d, 0x43, 0x32,
0xdd, 0x13, 0x77, 0x8a, 0xec, 0x87, 0x15, 0x76, 0xa3, 0x3c, 0x26, 0x08,
0x6c, 0x32, 0x0c, 0x9f, 0xf3, 0x3f, 0xc7
};
const unsigned char ec_P_384_key_pair_der[167] = {
0x30, 0x81, 0xa4, 0x02, 0x01, 0x01, 0x04, 0x30,
0x63, 0x88, 0x1c, 0xbf, 0x86, 0x65, 0xec, 0x39,
0x27, 0x33, 0x24, 0x2e, 0x5a, 0xae, 0x63, 0x3a,
0xf5, 0xb1, 0xb4, 0x54, 0xcf, 0x7a, 0x55, 0x7e,
0x44, 0xe5, 0x7c, 0xca, 0xfd, 0xb3, 0x59, 0xf9,
0x72, 0x66, 0xec, 0x48, 0x91, 0xdf, 0x27, 0x79,
0x99, 0xbd, 0x1a, 0xbc, 0x09, 0x36, 0x49, 0x9c,
0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
0x22, 0xa1, 0x64, 0x03, 0x62, 0x00, 0x04, 0x14,
0x2a, 0x78, 0x91, 0x06, 0x9b, 0xbe, 0x43, 0xa9,
0xe8, 0xd2, 0xa7, 0xbd, 0x03, 0xdf, 0xc9, 0x12,
0x62, 0x66, 0xb7, 0x84, 0xe3, 0x33, 0x4a, 0xf2,
0xb5, 0xf9, 0x5e, 0xe0, 0x3f, 0xe5, 0xc7, 0xdc,
0x1d, 0x56, 0xb3, 0x9f, 0x30, 0x6f, 0x97, 0xba,
0x00, 0xd8, 0xcf, 0x41, 0xea, 0x95, 0x5f, 0xeb,
0x55, 0x62, 0xab, 0x7c, 0xb7, 0x58, 0xd0, 0xe8,
0xde, 0xcf, 0x64, 0x69, 0x32, 0x50, 0xb3, 0x06,
0x70, 0xb0, 0xbc, 0x84, 0xcb, 0xa7, 0x1f, 0x2f,
0x1b, 0xf6, 0xad, 0x54, 0x56, 0x0a, 0x75, 0x83,
0xe1, 0xcf, 0xb6, 0x12, 0x2e, 0x0a, 0xde, 0xf9,
0xaa, 0x37, 0x64, 0x1a, 0x51, 0x1c, 0x27
};
const unsigned char ec_P_384_priv_key_sec1[48] = {
0x63, 0x88, 0x1c, 0xbf, 0x86, 0x65, 0xec, 0x39,
0x27, 0x33, 0x24, 0x2e, 0x5a, 0xae, 0x63, 0x3a,
0xf5, 0xb1, 0xb4, 0x54, 0xcf, 0x7a, 0x55, 0x7e,
0x44, 0xe5, 0x7c, 0xca, 0xfd, 0xb3, 0x59, 0xf9,
0x72, 0x66, 0xec, 0x48, 0x91, 0xdf, 0x27, 0x79,
0x99, 0xbd, 0x1a, 0xbc, 0x09, 0x36, 0x49, 0x9c
};
const unsigned char ec_P_521_key_pair_der[223] = {
0x30, 0x81, 0xdc, 0x02, 0x01, 0x01, 0x04, 0x42,
0x00, 0x4b, 0x35, 0x4d, 0xa4, 0xab, 0xf7, 0xa5,
0x4f, 0xac, 0xee, 0x06, 0x49, 0x4a, 0x97, 0x0e,
0xa6, 0x5f, 0x85, 0xf0, 0x6a, 0x2e, 0xfb, 0xf8,
0xdd, 0x60, 0x9a, 0xf1, 0x0b, 0x7a, 0x13, 0xf7,
0x90, 0xf8, 0x9f, 0x49, 0x02, 0xbf, 0x5d, 0x5d,
0x71, 0xa0, 0x90, 0x93, 0x11, 0xfd, 0x0c, 0xda,
0x7b, 0x6a, 0x5f, 0x7b, 0x82, 0x9d, 0x79, 0x61,
0xe1, 0x6b, 0x31, 0x0a, 0x30, 0x6f, 0x4d, 0xf3,
0x8b, 0xe3, 0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81,
0x04, 0x00, 0x23, 0xa1, 0x81, 0x89, 0x03, 0x81,
0x86, 0x00, 0x04, 0x00, 0x64, 0x27, 0x45, 0x07,
0x38, 0xbd, 0xd7, 0x1a, 0x87, 0xea, 0x20, 0xfb,
0x93, 0x6f, 0x1c, 0xde, 0xb3, 0x42, 0xcc, 0xf4,
0x58, 0x87, 0x79, 0x0f, 0x69, 0xaf, 0x5b, 0xff,
0x72, 0x96, 0x35, 0xb9, 0x6e, 0x8a, 0x55, 0x64,
0x00, 0x44, 0xfe, 0x63, 0x20, 0x4f, 0x65, 0x3a,
0x3a, 0x47, 0xcf, 0x3a, 0x7f, 0x60, 0x5d, 0xcb,
0xe6, 0xb4, 0x5a, 0x57, 0x2f, 0xc8, 0x74, 0x62,
0xcf, 0x98, 0x58, 0x33, 0x59, 0x00, 0xb9, 0xd0,
0xbc, 0x76, 0x2a, 0x37, 0x15, 0x3b, 0x9d, 0x3c,
0x62, 0xe9, 0xcc, 0x63, 0x00, 0xab, 0x7b, 0x01,
0xb1, 0x00, 0x77, 0x02, 0x14, 0xdb, 0x5e, 0xb8,
0xda, 0xac, 0x72, 0xf1, 0xd4, 0xa6, 0x17, 0xc5,
0x12, 0x97, 0x95, 0x6b, 0x98, 0x0b, 0xe0, 0x19,
0xf1, 0xf6, 0xd1, 0x0c, 0x09, 0xec, 0x1e, 0x2f,
0x51, 0x7a, 0x87, 0x71, 0x3c, 0x63, 0x25, 0x01,
0x43, 0xc0, 0xa8, 0x52, 0x1f, 0xf9, 0x53
};
const unsigned char ec_P_521_priv_key_sec1[66] = {
0x00, 0x4b, 0x35, 0x4d, 0xa4, 0xab, 0xf7, 0xa5,
0x4f, 0xac, 0xee, 0x06, 0x49, 0x4a, 0x97, 0x0e,
0xa6, 0x5f, 0x85, 0xf0, 0x6a, 0x2e, 0xfb, 0xf8,
0xdd, 0x60, 0x9a, 0xf1, 0x0b, 0x7a, 0x13, 0xf7,
0x90, 0xf8, 0x9f, 0x49, 0x02, 0xbf, 0x5d, 0x5d,
0x71, 0xa0, 0x90, 0x93, 0x11, 0xfd, 0x0c, 0xda,
0x7b, 0x6a, 0x5f, 0x7b, 0x82, 0x9d, 0x79, 0x61,
0xe1, 0x6b, 0x31, 0x0a, 0x30, 0x6f, 0x4d, 0xf3,
0x8b, 0xe3
};
const unsigned char cose_ex_P_256_priv_sec1[32] = {
0xaf, 0xf9, 0x07, 0xc9, 0x9f, 0x9a, 0xd3, 0xaa,
0xe6, 0xc4, 0xcd, 0xf2, 0x11, 0x22, 0xbc, 0xe2,
0xbd, 0x68, 0xb5, 0x28, 0x3e, 0x69, 0x07, 0x15,
0x4a, 0xd9, 0x11, 0x84, 0x0f, 0xa2, 0x08, 0xcf};
const unsigned char cose_ex_P_256_pub_sec1[65] = {
/* SEC Serialization of X and Y */
0x04,
/* X & Y */
0x65, 0xed, 0xa5, 0xa1, 0x25, 0x77, 0xc2, 0xba,
0xe8, 0x29, 0x43, 0x7f, 0xe3, 0x38, 0x70, 0x1a,
0x10, 0xaa, 0xa3, 0x75, 0xe1, 0xbb, 0x5b, 0x5d,
0xe1, 0x08, 0xde, 0x43, 0x9c, 0x08, 0x55, 0x1d,
0x1e, 0x52, 0xed, 0x75, 0x70, 0x11, 0x63, 0xf7,
0xf9, 0xe4, 0x0d, 0xdf, 0x9f, 0x34, 0x1b, 0x3d,
0xc9, 0xba, 0x86, 0x0a, 0xf7, 0xe0, 0xca, 0x7c,
0xa7, 0xe9, 0xee, 0xcd, 0x00, 0x84, 0xd1, 0x9c
};
const unsigned char cose_ex_P_256_pair_der[121] = {
/* Sequence */
0x30, 0x77,
/* Version number */
0x02, 0x01, 0x01,
/* Private key DER intro */
0x04, 0x20,
/* Private key bytes */
0xaf, 0xf9, 0x07, 0xc9, 0x9f, 0x9a, 0xd3, 0xaa,
0xe6, 0xc4, 0xcd, 0xf2, 0x11, 0x22, 0xbc, 0xe2,
0xbd, 0x68, 0xb5, 0x28, 0x3e, 0x69, 0x07, 0x15,
0x4a, 0xd9, 0x11, 0x84, 0x0f, 0xa2, 0x08, 0xcf,
/* OID for prime256v1 */
0xa0,
0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07,
/* Public key DER intro and padding */
0xa1, 0x44, 0x03, 0x42, 0x00,
/* SEC Serialization of X and Y */
0x04,
/* X & Y */
0x65, 0xed, 0xa5, 0xa1, 0x25, 0x77, 0xc2, 0xba,
0xe8, 0x29, 0x43, 0x7f, 0xe3, 0x38, 0x70, 0x1a,
0x10, 0xaa, 0xa3, 0x75, 0xe1, 0xbb, 0x5b, 0x5d,
0xe1, 0x08, 0xde, 0x43, 0x9c, 0x08, 0x55, 0x1d,
0x1e, 0x52, 0xed, 0x75, 0x70, 0x11, 0x63, 0xf7,
0xf9, 0xe4, 0x0d, 0xdf, 0x9f, 0x34, 0x1b, 0x3d,
0xc9, 0xba, 0x86, 0x0a, 0xf7, 0xe0, 0xca, 0x7c,
0xa7, 0xe9, 0xee, 0xcd, 0x00, 0x84, 0xd1, 0x9c
};
const unsigned char cose_ex_P_256_pub_der[91] = {
/* Sequence */
0x30, 0x59,
/* Sequence */
0x30, 0x13,
/* OID for pub key */
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01,
/* OID for prime256v1 */
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
0x01, 0x07,
/* Pub key DER intro */
0x03, 0x42, 0x00,
/* SECG Serialization */
/* 0x04 == uncompressed --> X and Y follow */
0x04,
0x65, 0xed, 0xa5, 0xa1, 0x25, 0x77, 0xc2, 0xba,
0xe8, 0x29, 0x43, 0x7f, 0xe3, 0x38, 0x70, 0x1a,
0x10, 0xaa, 0xa3, 0x75, 0xe1, 0xbb, 0x5b, 0x5d,
0xe1, 0x08, 0xde, 0x43, 0x9c, 0x08, 0x55, 0x1d,
0x1e, 0x52, 0xed, 0x75, 0x70, 0x11, 0x63, 0xf7,
0xf9, 0xe4, 0x0d, 0xdf, 0x9f, 0x34, 0x1b, 0x3d,
0xc9, 0xba, 0x86, 0x0a, 0xf7, 0xe0, 0xca, 0x7c,
0xa7, 0xe9, 0xee, 0xcd, 0x00, 0x84, 0xd1, 0x9c
};
const unsigned char cose_ex_P_521_priv_sec1[66] = {
0x00, 0x08, 0x51, 0x38, 0xdd, 0xab, 0xf5, 0xca,
0x97, 0x5f, 0x58, 0x60, 0xf9, 0x1a, 0x08, 0xe9,
0x1d, 0x6d, 0x5f, 0x9a, 0x76, 0xad, 0x40, 0x18,
0x76, 0x6a, 0x47, 0x66, 0x80, 0xb5, 0x5c, 0xd3,
0x39, 0xe8, 0xab, 0x6c, 0x72, 0xb5, 0xfa, 0xcd,
0xb2, 0xa2, 0xa5, 0x0a, 0xc2, 0x5b, 0xd0, 0x86,
0x64, 0x7d, 0xd3, 0xe2, 0xe6, 0xe9, 0x9e, 0x84,
0xca, 0x2c, 0x36, 0x09, 0xfd, 0xf1, 0x77, 0xfe,
0xb2, 0x6d
};
const unsigned char cose_ex_P_521_pub_sec1[133] = {
/* SEC Serialization of X and Y */
0x04,
/* X */
0x00, 0x72, 0x99, 0x2c, 0xb3, 0xac, 0x08, 0xec,
0xf3, 0xe5, 0xc6, 0x3d, 0xed, 0xec, 0x0d, 0x51,
0xa8, 0xc1, 0xf7, 0x9e, 0xf2, 0xf8, 0x2f, 0x94,
0xf3, 0xc7, 0x37, 0xbf, 0x5d, 0xe7, 0x98, 0x66,
0x71, 0xea, 0xc6, 0x25, 0xfe, 0x82, 0x57, 0xbb,
0xd0, 0x39, 0x46, 0x44, 0xca, 0xaa, 0x3a, 0xaf,
0x8f, 0x27, 0xa4, 0x58, 0x5f, 0xbb, 0xca, 0xd0,
0xf2, 0x45, 0x76, 0x20, 0x08, 0x5e, 0x5c, 0x8f,
0x42, 0xad,
/* Y */
0x01, 0xdc, 0xa6, 0x94, 0x7b, 0xce, 0x88, 0xbc,
0x57, 0x90, 0x48, 0x5a, 0xc9, 0x74, 0x27, 0x34,
0x2b, 0xc3, 0x5f, 0x88, 0x7d, 0x86, 0xd6, 0x5a,
0x08, 0x93, 0x77, 0xe2, 0x47, 0xe6, 0x0b, 0xaa,
0x55, 0xe4, 0xe8, 0x50, 0x1e, 0x2a, 0xda, 0x57,
0x24, 0xac, 0x51, 0xd6, 0x90, 0x90, 0x08, 0x03,
0x3e, 0xbc, 0x10, 0xac, 0x99, 0x9b, 0x9d, 0x7f,
0x5c, 0xc2, 0x51, 0x9f, 0x3f, 0xe1, 0xea, 0x1d,
0x94, 0x75
};
const unsigned char cose_ex_P_521_pair_der[223] = {
/* Sequence */
0x30, 0x81, 0xdc,
/* Version number */
0x02, 0x01, 0x01,
/* Private key DER intro */
0x04, 0x42,
/* Private key bytes */
0x00, 0x08, 0x51, 0x38, 0xdd, 0xab, 0xf5, 0xca,
0x97, 0x5f, 0x58, 0x60, 0xf9, 0x1a, 0x08, 0xe9,
0x1d, 0x6d, 0x5f, 0x9a, 0x76, 0xad, 0x40, 0x18,
0x76, 0x6a, 0x47, 0x66, 0x80, 0xb5, 0x5c, 0xd3,
0x39, 0xe8, 0xab, 0x6c, 0x72, 0xb5, 0xfa, 0xcd,
0xb2, 0xa2, 0xa5, 0x0a, 0xc2, 0x5b, 0xd0, 0x86,
0x64, 0x7d, 0xd3, 0xe2, 0xe6, 0xe9, 0x9e, 0x84,
0xca, 0x2c, 0x36, 0x09, 0xfd, 0xf1, 0x77, 0xfe,
0xb2, 0x6d,
/* OID for prime256v1 */
0xa0, 0x07, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00,
0x23,
/* Public key DER intro and padding */
0xa1, 0x81, 0x89, 0x03, 0x81, 0x86, 0x00,
/* SEC Serialization of X and Y */
0x04,
/* X */
0x00, 0x72, 0x99, 0x2c, 0xb3, 0xac, 0x08, 0xec,
0xf3, 0xe5, 0xc6, 0x3d, 0xed, 0xec, 0x0d, 0x51,
0xa8, 0xc1, 0xf7, 0x9e, 0xf2, 0xf8, 0x2f, 0x94,
0xf3, 0xc7, 0x37, 0xbf, 0x5d, 0xe7, 0x98, 0x66,
0x71, 0xea, 0xc6, 0x25, 0xfe, 0x82, 0x57, 0xbb,
0xd0, 0x39, 0x46, 0x44, 0xca, 0xaa, 0x3a, 0xaf,
0x8f, 0x27, 0xa4, 0x58, 0x5f, 0xbb, 0xca, 0xd0,
0xf2, 0x45, 0x76, 0x20, 0x08, 0x5e, 0x5c, 0x8f,
0x42, 0xad,
/* Y */
0x01, 0xdc, 0xa6, 0x94, 0x7b, 0xce, 0x88, 0xbc,
0x57, 0x90, 0x48, 0x5a, 0xc9, 0x74, 0x27, 0x34,
0x2b, 0xc3, 0x5f, 0x88, 0x7d, 0x86, 0xd6, 0x5a,
0x08, 0x93, 0x77, 0xe2, 0x47, 0xe6, 0x0b, 0xaa,
0x55, 0xe4, 0xe8, 0x50, 0x1e, 0x2a, 0xda, 0x57,
0x24, 0xac, 0x51, 0xd6, 0x90, 0x90, 0x08, 0x03,
0x3e, 0xbc, 0x10, 0xac, 0x99, 0x9b, 0x9d, 0x7f,
0x5c, 0xc2, 0x51, 0x9f, 0x3f, 0xe1, 0xea, 0x1d,
0x94, 0x75
};
const unsigned char cose_ex_P_521_pub_der[158] = {
/* Sequence */
0x30, 0x81, 0x9b,
/* Sequence */
0x30, 0x10,
/* OID id-ecPublicKey for pub key */
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01,
/* OID for secp521r1 */
0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23,
/* Pub key DER intro */
0x03, 0x81, 0x86, 0x00,
/* SEC Serialization of X and Y */
0x04,
/* X */
0x00, 0x72, 0x99, 0x2c, 0xb3, 0xac, 0x08, 0xec,
0xf3, 0xe5, 0xc6, 0x3d, 0xed, 0xec, 0x0d, 0x51,
0xa8, 0xc1, 0xf7, 0x9e, 0xf2, 0xf8, 0x2f, 0x94,
0xf3, 0xc7, 0x37, 0xbf, 0x5d, 0xe7, 0x98, 0x66,
0x71, 0xea, 0xc6, 0x25, 0xfe, 0x82, 0x57, 0xbb,
0xd0, 0x39, 0x46, 0x44, 0xca, 0xaa, 0x3a, 0xaf,
0x8f, 0x27, 0xa4, 0x58, 0x5f, 0xbb, 0xca, 0xd0,
0xf2, 0x45, 0x76, 0x20, 0x08, 0x5e, 0x5c, 0x8f,
0x42, 0xad,
/* Y */
0x01, 0xdc, 0xa6, 0x94, 0x7b, 0xce, 0x88, 0xbc,
0x57, 0x90, 0x48, 0x5a, 0xc9, 0x74, 0x27, 0x34,
0x2b, 0xc3, 0x5f, 0x88, 0x7d, 0x86, 0xd6, 0x5a,
0x08, 0x93, 0x77, 0xe2, 0x47, 0xe6, 0x0b, 0xaa,
0x55, 0xe4, 0xe8, 0x50, 0x1e, 0x2a, 0xda, 0x57,
0x24, 0xac, 0x51, 0xd6, 0x90, 0x90, 0x08, 0x03,
0x3e, 0xbc, 0x10, 0xac, 0x99, 0x9b, 0x9d, 0x7f,
0x5c, 0xc2, 0x51, 0x9f, 0x3f, 0xe1, 0xea, 0x1d,
0x94, 0x75
};
const unsigned char RSA_2048_key_pair_der[1191] = {
0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02,
0x82, 0x01, 0x01, 0x00, 0x9e, 0x4e, 0x3b, 0x05,
0xb4, 0x33, 0xe5, 0x49, 0x68, 0xdc, 0x64, 0xa0,
0x4e, 0x2c, 0x63, 0xd8, 0x11, 0x25, 0x7c, 0xe0,
0x63, 0xb6, 0x64, 0x89, 0x0b, 0xbd, 0xcd, 0x62,
0x30, 0xd4, 0x52, 0xa2, 0x52, 0xe0, 0x61, 0x84,
0xee, 0xf9, 0x6e, 0x14, 0x9f, 0x9e, 0x0e, 0xee,
0x67, 0x51, 0x09, 0xa9, 0x15, 0x05, 0x07, 0x17,
0x09, 0x93, 0x76, 0x87, 0x45, 0x2b, 0x89, 0xfd,
0x7c, 0xa7, 0xfa, 0xfc, 0x1b, 0x7b, 0x6e, 0xbb,
0x5f, 0xfc, 0x8a, 0xca, 0xc9, 0x14, 0xb4, 0xd0,
0xe8, 0x91, 0xb1, 0x60, 0xe8, 0x89, 0x54, 0xe0,
0x06, 0x0b, 0x59, 0xff, 0x90, 0x12, 0x34, 0x47,
0xd7, 0xbf, 0x82, 0xa8, 0x48, 0x77, 0x46, 0x56,
0x2c, 0xfb, 0x84, 0x00, 0x01, 0xdb, 0x6b, 0x14,
0xe2, 0x5a, 0xc7, 0x77, 0x3c, 0x8e, 0x48, 0x59,
0x21, 0xc6, 0x7a, 0x28, 0x17, 0x3f, 0xfa, 0xe1,
0xea, 0xc4, 0x6f, 0xa0, 0x0d, 0xf0, 0x04, 0x5a,
0x29, 0x97, 0x2e, 0x96, 0x35, 0x25, 0xba, 0x0a,
0x39, 0x51, 0x9e, 0x1d, 0x64, 0x95, 0xad, 0xc8,
0xc1, 0xa6, 0xfd, 0x61, 0xa1, 0x56, 0x40, 0x96,
0x85, 0x42, 0x83, 0x1e, 0x8f, 0xc8, 0xfa, 0x70,
0x2b, 0xea, 0xbd, 0xe6, 0x2d, 0x6f, 0x6a, 0x73,
0x00, 0x2a, 0x8f, 0x8e, 0x2c, 0x28, 0xdb, 0xc0,
0xa0, 0x23, 0x37, 0x6f, 0x67, 0xe3, 0x3d, 0x8f,
0xe6, 0x12, 0xbe, 0x8c, 0xdf, 0x67, 0xfb, 0xbf,
0xe2, 0x80, 0xd0, 0xdf, 0xe0, 0xf9, 0x68, 0xeb,
0x7f, 0x37, 0x4f, 0x17, 0xb8, 0x1e, 0x06, 0x46,
0x1a, 0x47, 0x6b, 0xd3, 0x40, 0x2c, 0x9a, 0xd1,
0xc5, 0x5c, 0xd2, 0x59, 0xad, 0x78, 0x82, 0x1b,
0x07, 0x49, 0x0e, 0x70, 0xa4, 0x69, 0x0c, 0xac,
0xf4, 0x78, 0x2e, 0x2d, 0x3e, 0x94, 0xc2, 0x3b,
0x80, 0xbc, 0x88, 0x91, 0xc9, 0xfe, 0x06, 0x1c,
0x19, 0xe3, 0x22, 0xbf, 0x02, 0x03, 0x01, 0x00,
0x01, 0x02, 0x82, 0x01, 0x00, 0x57, 0x8b, 0x07,
0x94, 0xc5, 0xec, 0x94, 0xf5, 0x9d, 0xa9, 0x93,
0x74, 0x1b, 0x06, 0xed, 0x48, 0x05, 0x63, 0x67,
0xc5, 0x67, 0x1e, 0xec, 0x45, 0xe5, 0x5a, 0x57,
0x03, 0xdf, 0xe0, 0xea, 0xb9, 0x9d, 0x7f, 0x3c,
0x2e, 0x99, 0x41, 0x12, 0xa1, 0x11, 0x0c, 0x05,
0x51, 0xcd, 0x8c, 0xc0, 0xfc, 0xe2, 0x04, 0xdf,
0xc0, 0xdb, 0xa8, 0xd2, 0xb9, 0x47, 0x85, 0x26,
0x50, 0x29, 0xe9, 0x73, 0x20, 0x8b, 0xca, 0x1c,
0x98, 0x3e, 0x22, 0x98, 0x56, 0x40, 0x10, 0xd5,
0x55, 0x59, 0xe7, 0x87, 0xe2, 0x01, 0x76, 0x40,
0x9b, 0x8a, 0x7c, 0x28, 0x8e, 0xed, 0x8b, 0x43,
0xa2, 0x1f, 0x2b, 0x67, 0x03, 0xcc, 0xdf, 0x38,
0xe4, 0x5b, 0x07, 0xd4, 0x1d, 0x74, 0xe9, 0x74,
0x34, 0x1e, 0x60, 0xf9, 0x41, 0x75, 0x19, 0x71,
0xe4, 0xe8, 0x8a, 0xab, 0xef, 0x13, 0xbc, 0x6b,
0xef, 0x17, 0x36, 0xfe, 0x4a, 0xf3, 0xe6, 0x17,
0x45, 0xd5, 0xfd, 0x7b, 0x82, 0xc6, 0x35, 0x72,
0x77, 0x91, 0x3d, 0x05, 0xd4, 0x00, 0xa3, 0x0d,
0xd5, 0x9a, 0x4e, 0x6b, 0xf4, 0x6f, 0xd5, 0xe9,
0x31, 0x58, 0x3e, 0x01, 0xfc, 0x7e, 0x7a, 0x80,
0x8f, 0x1e, 0x78, 0xbc, 0x31, 0x23, 0x03, 0x6a,
0x30, 0x31, 0x4e, 0xbb, 0x0e, 0x8f, 0xed, 0x26,
0x8d, 0x2d, 0x29, 0xc9, 0x83, 0xb8, 0x57, 0x39,
0x90, 0xd0, 0x43, 0x51, 0xb6, 0xf8, 0x5c, 0x20,
0xbe, 0x8e, 0x5d, 0xed, 0xde, 0x82, 0xe7, 0x0a,
0xf2, 0x7f, 0x76, 0x8c, 0x9d, 0x8a, 0x76, 0xa5,
0xb3, 0x63, 0x59, 0x4a, 0xcb, 0x90, 0x2b, 0x5f,
0xa4, 0xb9, 0x63, 0x10, 0x12, 0xaa, 0xa8, 0x87,
0xed, 0x60, 0x06, 0x2d, 0x1f, 0x0f, 0xad, 0x19,
0xde, 0xd0, 0xff, 0x6f, 0x2c, 0xc2, 0x4c, 0x9e,
0x1f, 0x89, 0xc8, 0x18, 0xa0, 0x42, 0xad, 0xa0,
0xa0, 0x37, 0x17, 0x68, 0x01, 0x02, 0x81, 0x81,
0x00, 0xcf, 0xcd, 0x4a, 0x0e, 0xcb, 0xe9, 0x19,
0x57, 0x2d, 0x42, 0x8a, 0xbf, 0xf9, 0x9b, 0xbc,
0xe1, 0x45, 0x87, 0x1c, 0xbe, 0xc4, 0x64, 0x9b,
0xbb, 0x40, 0x0c, 0xc5, 0x34, 0xbe, 0xbf, 0xcf,
0x6c, 0xc1, 0x4c, 0x5d, 0x72, 0x6b, 0x3f, 0xdf,
0x0c, 0x81, 0x7b, 0x2c, 0x30, 0xbf, 0x93, 0x49,
0x99, 0x28, 0xb1, 0x88, 0xf9, 0x76, 0x13, 0x6d,
0xe3, 0x1a, 0x85, 0xcf, 0x34, 0x77, 0x72, 0x76,
0x70, 0xe9, 0xe5, 0x5e, 0xc6, 0x1d, 0x7f, 0xec,
0x11, 0x6e, 0xf8, 0x50, 0x9d, 0xb3, 0x04, 0xd9,
0x0c, 0xc3, 0xf5, 0x40, 0x98, 0x8c, 0x77, 0x96,
0x89, 0x69, 0x10, 0xb3, 0xa8, 0x43, 0x99, 0x95,
0xc8, 0x6c, 0x21, 0x16, 0x36, 0x33, 0xf8, 0x6c,
0x4b, 0x99, 0x24, 0x64, 0x93, 0xbb, 0xbf, 0xa5,
0x3f, 0xed, 0xd4, 0x66, 0x9c, 0x3e, 0xd6, 0xf9,
0x62, 0x43, 0x41, 0xe5, 0xaf, 0xfe, 0x8e, 0x98,
0xbf, 0x02, 0x81, 0x81, 0x00, 0xc3, 0x05, 0xfc,
0x0e, 0xaa, 0x94, 0x58, 0xbe, 0x92, 0xdb, 0x0e,
0x89, 0x30, 0x18, 0x7e, 0xa2, 0x2c, 0x5f, 0x16,
0xad, 0x9f, 0xd2, 0x4b, 0x40, 0x8d, 0x60, 0x30,
0xfa, 0x9b, 0xaa, 0xcb, 0x20, 0xcd, 0x18, 0x63,
0x1d, 0x51, 0xda, 0xb3, 0x61, 0xb1, 0xcc, 0x82,
0x45, 0x2a, 0x84, 0x82, 0x7b, 0xb5, 0xc1, 0x0c,
0xd4, 0xe5, 0xe4, 0x0f, 0x03, 0xe7, 0x92, 0x48,
0x24, 0x85, 0x4c, 0xa6, 0x02, 0xd3, 0x7b, 0xe8,
0xb8, 0x9e, 0xf4, 0x92, 0xb9, 0x55, 0x71, 0x2e,
0x80, 0x45, 0x7c, 0x80, 0x62, 0x20, 0x1b, 0x9a,
0xbb, 0x18, 0x36, 0x36, 0x5d, 0x69, 0xf0, 0xea,
0x41, 0x5c, 0x4c, 0x75, 0x5c, 0x62, 0xc9, 0x4f,
0xae, 0xb0, 0xad, 0x98, 0xc5, 0x03, 0xf2, 0xf9,
0xde, 0x1f, 0x01, 0xe9, 0x1e, 0x3d, 0xe8, 0xf8,
0x84, 0xaf, 0x49, 0x61, 0x2f, 0x4e, 0x20, 0xb4,
0x18, 0x79, 0xb3, 0xf6, 0x01, 0x02, 0x81, 0x80,
0x72, 0xe2, 0x03, 0xf7, 0x7a, 0x34, 0x3c, 0x96,
0x3d, 0xa7, 0x74, 0x1d, 0xfe, 0x59, 0x63, 0x6b,
0x07, 0x8d, 0x53, 0x0f, 0x04, 0x74, 0xba, 0xc4,
0x22, 0xfc, 0xec, 0x69, 0xe4, 0xab, 0x16, 0x7a,
0x01, 0xc3, 0xbe, 0x45, 0xeb, 0x95, 0x3c, 0x33,
0x25, 0xc2, 0x7b, 0x03, 0xd8, 0x66, 0x0d, 0x62,
0x67, 0x64, 0xff, 0x5d, 0x2b, 0x32, 0x42, 0xa6,
0x33, 0x9b, 0x96, 0x9a, 0x63, 0x0f, 0x1c, 0xfb,
0xff, 0xd3, 0x97, 0x39, 0xe0, 0x45, 0x40, 0xb5,
0xc2, 0xab, 0xf5, 0xa5, 0xb9, 0xbb, 0x0c, 0x64,
0x4a, 0x51, 0xe4, 0x8c, 0x71, 0xdc, 0x0b, 0x95,
0x9c, 0x48, 0x67, 0x8a, 0xb7, 0x14, 0xca, 0x02,
0x2c, 0x05, 0x7e, 0xca, 0x28, 0xa1, 0x46, 0xfd,
0xe4, 0x84, 0x82, 0x36, 0x4a, 0xae, 0x01, 0x25,
0xfe, 0xce, 0x56, 0x8c, 0x3b, 0x11, 0x8e, 0x7e,
0x0c, 0xc0, 0xf9, 0xc2, 0xfa, 0xf0, 0xca, 0xf1,
0x02, 0x81, 0x80, 0x61, 0x53, 0x61, 0x40, 0xe8,
0x7b, 0xf3, 0xf5, 0xd7, 0x50, 0x1e, 0xe6, 0xf3,
0xeb, 0xa5, 0x76, 0xc5, 0x72, 0x06, 0xdd, 0x4a,
0xff, 0x25, 0xb2, 0xe7, 0x5a, 0xf3, 0xd6, 0x7d,
0x4d, 0x34, 0xe5, 0xff, 0xb4, 0x85, 0xf2, 0x21,
0xe1, 0x64, 0xd8, 0x02, 0x65, 0x2f, 0x35, 0xd9,
0x4c, 0x1b, 0xda, 0x25, 0x10, 0x5c, 0x98, 0xfa,
0xc9, 0x5f, 0x7c, 0xf1, 0x5a, 0x1d, 0x4a, 0xac,
0x83, 0x5d, 0xed, 0xd7, 0x20, 0xe5, 0x39, 0x0d,
0x8a, 0xbc, 0x96, 0x65, 0x3f, 0x80, 0x97, 0x5f,
0x16, 0x0c, 0xf3, 0xeb, 0x56, 0x1b, 0x57, 0xf7,
0x73, 0x46, 0x9a, 0x43, 0xbe, 0x89, 0x09, 0x69,
0x48, 0x76, 0xe1, 0x4e, 0x23, 0x6c, 0xf2, 0x9f,
0x15, 0x63, 0x42, 0x1f, 0x00, 0x69, 0x16, 0x22,
0x9f, 0x4f, 0x79, 0x5a, 0x28, 0x23, 0xae, 0x03,
0xd4, 0x38, 0xfd, 0xe4, 0x9d, 0x89, 0x83, 0x15,
0x69, 0x6c, 0x01, 0x02, 0x81, 0x81, 0x00, 0xc3,
0x8d, 0xfa, 0x78, 0xed, 0xb8, 0x99, 0xd3, 0xee,
0xd0, 0xbd, 0x74, 0xf3, 0x6e, 0xd1, 0xb4, 0x37,
0xc0, 0x89, 0x6c, 0xf0, 0x69, 0xbc, 0xbe, 0x5c,
0xd4, 0x6a, 0xa5, 0xba, 0x39, 0x3e, 0x68, 0x87,
0xeb, 0x35, 0x6d, 0x24, 0x3c, 0x3f, 0x11, 0xcd,
0x31, 0x60, 0x8b, 0xb6, 0x7f, 0x6c, 0x42, 0xe3,
0x8d, 0xc3, 0x90, 0x79, 0x9a, 0xba, 0x1c, 0xac,
0x72, 0x5d, 0x05, 0x8a, 0x50, 0x87, 0x34, 0x67,
0xba, 0x19, 0x2c, 0xd6, 0x9b, 0x3f, 0xd7, 0x32,
0x4f, 0x60, 0x9e, 0x19, 0x00, 0x1e, 0x29, 0xfd,
0x8f, 0xcd, 0xec, 0x75, 0xcd, 0x42, 0xcc, 0x5f,
0xad, 0x42, 0xa3, 0xf6, 0xc5, 0x5a, 0x14, 0xaa,
0x9f, 0x75, 0xe6, 0x13, 0x96, 0xdf, 0x73, 0xcd,
0xd8, 0x8b, 0x02, 0x9c, 0xeb, 0xa5, 0x2f, 0x06,
0x12, 0xc3, 0x0c, 0xf3, 0xbb, 0x9f, 0x16, 0xdb,
0xe6, 0xd2, 0x78, 0x58, 0x35, 0xb7, 0x4b
};
const unsigned char ed25519_key_pair_der[48] = {
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20,
0x5f, 0xe3, 0x9b, 0x74, 0x55, 0xa0, 0x73, 0xd1,
0x38, 0xc2, 0xe7, 0xd4, 0xe5, 0x06, 0x30, 0x52,
0x9f, 0xce, 0x7d, 0xdc, 0xe8, 0x22, 0x80, 0x2a,
0x68, 0x5d, 0xa8, 0x99, 0x16, 0x5d, 0x44, 0x58
};

View File

@@ -0,0 +1,275 @@
/*
* example_keys.h
*
* Copyright 2023, Laurence Lundblade
*
* Created by Laurence Lundblade on 6/13/23.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#ifndef example_keys_h
#define example_keys_h
/* These are hard-coded keys used for testing. A big reason for hard
* coding in byte arrays is so that tests don't need any extra
* files. Everything for test compiles into one executable.
*
* The actual import of the keys into data structures used by crypto
* libraries is dependent on the library. The most widely used formats
* are ASN.1/DER so that is mostly what is used here. See
* init_keys_xxx.[ch]. These are pretty good examples for what you
* might do in your implementation.
*
* Eventually, t_cose will have better support for COSE_Key, but even
* then most keys will still be in ASN.1/DER format.
*
* Note how ridiculously piece meal the formats for DER- encoded keys
* are. Perhaps a dozen RFCs :-(. Implementations seem to be
* hit-or-miss in what they support. Maybe some day much more of this
* will be CBOR-format COSE_Keys... :^)
*/
/*
* The format of an EC key in a file or in a protocol message is in
* 2-3 layers.
*
* First, SEC1 (reference below) byte-encoding of the mathematical values.
*
* Second, a structure that wraps the SEC1 bytes along with a curve
* identifier. There are three:
* RFC 5480 and RFC 5915 — ASN.1/DER
* JWK — JSON
* COSE_Key — CBOR
*
* Sometimes a third layer, PEM, to make ASN.1/DER into text.
*
* SEC1 defines the representation of the mathematical values use in EC
* cryptography like an X and Y coordinate in bytes. The SEC1
* specification is widely used as the basis of most protocol and file
* formats and as for import and export formats for cryptographic
* libraries.
*
* SEC1 defines private key serialization as a sequence of bytes.
*
* SEC1 defines the public key as a point, an X and Y coordinate. It
* defines compressed and uncompressed formats. The compressed format
* is half the size. It used to be covered by a patent, but the
* patent has expired. The public key is serialized in one of three
* ways:
* 0x04 || X-coordinate || y-coordinate (uncompressed)
* 0x02 || X-coordinate (compressed, Y positive)
* 0x03 || X-coordinate (compressed, Y negative)
*
*
* Generally, the SEC1 serialization is not used directly in
* protocols. Rather it is put into an ASN.1/DER, JSON or CBOR data
* structure. Often that additional structure identifies it as an EC
* key and gives the curve.
*
* The most common protocol/file format for EC keys is ASN.1/DER
* defined by RFC 5480 for public keys and RFC 5915 for private
* keys. RFC 5915 can optionally carry a public key along side the
* private key. These are the file formats that the “openssl ec”
* command reads and writes.
*
* Heres the ASN.1 from RFC 5915:
*
* ECPrivateKey ::= SEQUENCE {
* version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
* privateKey OCTET STRING,
* parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
* publicKey [1] BIT STRING OPTIONAL
* }
*
* And the ASN.1 from RFC 5480:
*
* SubjectPublicKeyInfo ::= SEQUENCE {
* algorithm AlgorithmIdentifier,
* subjectPublicKey BIT STRING
* }
*
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
*
* ECParameters ::= CHOICE {
* namedCurve OBJECT IDENTIFIER
* -- implicitCurve NULL
* -- specifiedCurve SpecifiedECDomain
* }
*
* DER is a binary format, so it is often made into a PEM text file
* for convenience of handling. PEM is more or less base64 encoding
* with a little bit of extra text labeling to know the PEM file is a
* key and whether it is public or private.
*
* -----BEGIN EC PRIVATE KEY-----
* MHcCAQEEIK/5B8mfmtOq5sTN8hEivOK9aLUoPmkHFUrZEYQPogjPoAoGCCqGSM49
* AwEHoUQDQgAEZe2loSV3wrroKUN/4zhwGhCqo3Xhu1td4QjeQ5wIVR0eUu11cBFj
* 9/nkDd+fNBs9ybqGCvfgynyn6e7NAITRnA==
* -----END EC PRIVATE KEY——
*
*
* -----BEGIN PUBLIC KEY-----
* MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZe2loSV3wrroKUN/4zhwGhCqo3Xh
* u1td4QjeQ5wIVR0eUu11cBFj9/nkDd+fNBs9ybqGCvfgynyn6e7NAITRnA==
* -----END PUBLIC KEY-----
*
* Not discussed here are X.509 certificates. It is comment to send
* public keys around in them so you know where the public key came
* from and can figure out how to trust it. X.509 is ASN.1/DER and
* often PEM wrapped. The public key inside it is in RFC 5480 format.
*
* Each EC key supplied here is in several forms to accommodate
* different cryptographic library import APIs, to give good examples
* and for other use if need be. The SEC1 form is given and the
* ASN.1/DER form is given. The PEM form is not given.
*
* Some of the keys are from the COSE examples GitHub repository and
* some are not. The intent is to use as many from the COSE examples
* as possible in the long run.
*
* [SEC1]
* Certicom Research, "SEC 1: Elliptic Curve Cryptography", Standards for
* Efficient Cryptography, May 2009, <https://www.secg.org/sec1-v2.pdf>.
*/
/*
* This describes how I converted the keys in KeySet.txt to what is
* here. The keys in KeySet.txt are CBOR diagnostic notation of a
* COSE_Key. They kinda look like JWKs, but they are not. I haven't
* found any tools to process them yet.
*
* First I made the SEC1 bytes for private key and the public key:
*
* xxd -r -p << EOD | xxd -i
*
* The hex text from KeySet.txt is fed into the above command stdin.
* The hex text C array initialization output by this is pasted in to
* example_keys.c. For the private key, just the "d" value. For the
* public key the "x" and then "y" value. Then the C code is edited to
* add a 0x04 to the start of the x and y.
*
* Next... (there has to be a better way), I generated a random key
* pairs in DER format using the openssl command line for the curves:
*
* openssl ecparam -name secp521r1 -genkey -noout -out 521.der -outform der
*
* That was imported into a C array initialization with:
*
* xxd -i -c 8 521.der
*
* Then the C array initialization was edited to splice in the COSE
* example private key and the the COSE example public key. You can
* see the comments in the code for the ASN.1/DER to figure where to
* splice.
*
* Finally the edited variables were turned into DER files and checked
* in to github to have them handy for future use. See grep command in
* // comment below that takes the C array initialization and turns it
* into a binary DER file.
*/
//grep -v '/\*.*\*/' << EOF | xxd -r -p
extern const unsigned char ec_P_256_key_pair_der[121];
extern const unsigned char ec_P_256_priv_key_sec1[32];
extern const unsigned char ec_P_256_pub_key_der[91];
extern const unsigned char ec_P_384_key_pair_der[167];
extern const unsigned char ec_P_384_priv_key_sec1[48];
extern const unsigned char ec_P_521_key_pair_der[223];
extern const unsigned char ec_P_521_priv_key_sec1[66];
/* These keys are the ones used in the COSE Work Group GitHub
* Examples Repository */
// KID: meriadoc.brandybuck@buckland.example
extern const unsigned char cose_ex_P_256_priv_sec1[32];
extern const unsigned char cose_ex_P_256_pub_sec1[65];
extern const unsigned char cose_ex_P_256_pair_der[121];
extern const unsigned char cose_ex_P_256_pub_der[91];
// KID: bilbo.baggins@hobbiton.example
extern const unsigned char cose_ex_P_521_priv_sec1[66];
extern const unsigned char cose_ex_P_521_pub_sec1[133];
extern const unsigned char cose_ex_P_521_pair_der[223];
extern const unsigned char cose_ex_P_521_pub_der[158];
/*
* The RSA keypair is provided only in PKCS #1 DER format
* as both OpenSSL and MbedTLS can import it. PKCS #1 is
* documented in RFC 8017.
*
* This is imported with psa_import_key() in Mbed TLS
* and d2i_PrivateKey().
*
* RSAPrivateKey ::= SEQUENCE {
* version Version,
* modulus INTEGER, -- n
* publicExponentINTEGER, -- e
* privateExponent INTEGER, -- d
* prime1INTEGER, -- p
* prime2INTEGER, -- q
* exponent1 INTEGER, -- d mod (p-1)
* exponent2 INTEGER, -- d mod (q-1)
* coefficient INTEGER, -- (inverse of q) mod p
* otherPrimeInfos OtherPrimeInfos OPTIONAL
* }
*
* PKCS 8 is another format for a private key, but
* that is not provided.
*
* This was generated with:
* openssl genrsa 2048 | sed -e '1d' -e '$d' | base64 --decode | xxd -i
*
*/
extern const unsigned char RSA_2048_key_pair_der[1191];
/* Pretty sure this is per RFC 8410 in DER (which is
* based on RFC 5958). This is imported by
* d2i_PrivateKey() in OpenSSL. MbedTLS doesn't
* support EdDSA.
*
* OneAsymmetricKey ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
* privateKey PrivateKey,
* attributes [0] IMPLICIT Attributes OPTIONAL,
* ...,
* [[2: publicKey [1] IMPLICIT PublicKey OPTIONAL ]],
* ...
* }
*
* PrivateKey ::= OCTET STRING
*
* PublicKey ::= BIT STRING
*
* 0:d=0 hl=2 l= 46 cons: SEQUENCE
* 2:d=1 hl=2 l= 1 prim: INTEGER :00
* 5:d=1 hl=2 l= 5 cons: SEQUENCE
* 7:d=2 hl=2 l= 3 prim: OBJECT :ED25519
* 12:d=1 hl=2 l= 34 prim: OCTET STRING [HEX DUMP]:04205FE39B7455A073D138C2E7D4E50630529FCE7DDCE822802A685DA899165D4458
*/
extern const unsigned char ed25519_key_pair_der[48];
#endif /* example_keys_h */

View File

@@ -0,0 +1,71 @@
/*
* examples_main.c
*
* Copyright 2023, Laurence Lundblade
*
* Created by Laurence Lundblade on 2/21/23.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include <stdbool.h>
#include <stdio.h>
#include "signing_examples.h"
#include "encryption_examples.h"
typedef int32_t (test_fun_t)(void);
#define TEST_ENTRY(test_name) {#test_name, test_name, true}
typedef struct {
const char *szTestName;
test_fun_t *test_fun;
bool bEnabled;
} test_entry;
static test_entry s_tests[] = {
TEST_ENTRY(one_step_sign_example),
TEST_ENTRY(two_step_sign_example),
TEST_ENTRY(one_step_multi_sign_detached_example),
TEST_ENTRY(old_one_step_sign_example),
TEST_ENTRY(old_two_step_sign_example),
TEST_ENTRY(encrypt0_example),
#ifndef T_COSE_DISABLE_KEYWRAP
TEST_ENTRY(key_wrap_example),
#endif /* !T_COSE_DISABLE_KEYWRAP */
TEST_ENTRY(esdh_example),
TEST_ENTRY(esdh_example_detached),
};
int main(int argc, const char * argv[])
{
(void)argc; /* Avoid unused parameter error */
(void)argv;
int nTestsFailed = 0;
int nTestsRun = 0;
test_entry *t;
const test_entry *s_tests_end = s_tests + sizeof(s_tests)/sizeof(test_entry);
for(t = s_tests; t < s_tests_end; t++) {
/* Could bring in command line arges from run_tests.c here */
int32_t nTestResult = (int32_t)(t->test_fun)();
nTestsRun++;
if(nTestResult) {
nTestsFailed++;
}
}
printf("\n%d of %d EXAMPLES FAILED\n", nTestsFailed, nTestsRun);
}

View File

@@ -0,0 +1,96 @@
/*
* init_keys.h
*
* Copyright 2023, Laurence Lundblade
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#ifndef init_keys_h
#define init_keys_h
#include "t_cose/t_cose_key.h"
/* Initializes a key to fixed test key for the specified algorithm
*
* This is used by examples and by test cases.
*
* Go read the source to learn how keys work for your
* particular crypto library.
*
* This always initializes to the exact same key pair for
* a given algorithm. (This saves us having to pass in some
* serialized representation of the key pair. It's (so far) not
* straight forward for all the crypto libraries to support the
* same serialization formats; for OSSL it's a DER format; for
* Mbed TLS is a point on a curve).
*
* This interface is independent of the crypto library, but the
* implementation is not.
*
* This is pulled out from the example to keep them independent
* of any particular crypto library.
*
* free_fixed_signing_key() should be called when done with the
* keys returned here to work for certain with all crypto libraries
* even though some don't require it.
*
* TODO: should this be by curve instead of signing algorithm?
*
*/
enum t_cose_err_t
init_fixed_test_signing_key(int32_t cose_algorithm_id,
struct t_cose_key *key_pair);
void
free_fixed_signing_key(struct t_cose_key key_pair);
/**
* \brief Initialize two key handles with public and private for test
*
* \param[in] cose_ec_curve_id Curve for the key pair
* \param[out] public_key Handle for public key of the pair
* \param[out] private_key Handle for the private key of the pair.
*
* This is for key pairs for EC encryption. Typically this gets fed
* into ECDH either for HPKE or the RFC 9053 COSE encryption key
* distrubution methods.
*
* The curve and number of bits are associated with the key not with
* the encryption algorithm, so this takes the COSE EC curve ID as an
* argument, not the encryption algorithm.
*
* While the crypto library representation of a private key usually
* also includes the public key, they are separate here as in the real
* world the encryptor and decryptor will not be the same. In the real
* world, the encryptor will not have the private key.
*
* Both keys returned here must be freed with
* free_fixed_test_ec_encryption_key().
*/
enum t_cose_err_t
init_fixed_test_ec_encryption_key(int32_t cose_ec_curve_id,
struct t_cose_key *public_key,
struct t_cose_key *private_key);
void
free_fixed_test_ec_encryption_key(struct t_cose_key key);
/* Returns true if key pair leaks were detected. This is
* necessary only for testing. Not all crypto libraries
* support this.
*/
int check_for_key_allocation_leaks(void);
#endif /* init_keys_h */

View File

@@ -0,0 +1,263 @@
/*
* init_keys_ossl.c
*
* Copyright 2019-2023, Laurence Lundblade
* Copyright (c) 2022, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include "init_keys.h"
#include "example_keys.h"
#include "t_cose/t_cose_standard_constants.h"
#include "t_cose/t_cose_common.h"
#include "t_cose/t_cose_key.h"
#include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/x509.h"
/*
* The input bytes are what d2i_PrivateKey() will decode. It's
* documentation is sparse. It says it must be DER format and is
* related to PKCS #8. This seems to be a set of DER-encoded ASN.1
* data types such as:
*
* ECPrivateKey defined in RFC 5915
*
* The key object returned by this is malloced and has to be freed by
* by calling free_key(). This heap use is a part of OpenSSL and not
* t_cose which does not use the heap.
*
*
*/
static enum t_cose_err_t
init_signing_key_der(int32_t cose_algorithm_id,
struct q_useful_buf_c der_encoded,
struct t_cose_key *key_pair)
{
EVP_PKEY *pkey;
int key_type;
enum t_cose_err_t return_value;
long der_length;
switch (cose_algorithm_id) {
case T_COSE_ALGORITHM_ES256:
case T_COSE_ALGORITHM_ES384:
case T_COSE_ALGORITHM_ES512:
key_type = EVP_PKEY_EC;
break;
case T_COSE_ALGORITHM_PS256:
case T_COSE_ALGORITHM_PS384:
case T_COSE_ALGORITHM_PS512:
key_type = EVP_PKEY_RSA;
break;
case T_COSE_ALGORITHM_EDDSA:
key_type = EVP_PKEY_ED25519;
break;
default:
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
/* Safely convert size_t to long */
if(der_encoded.len > LONG_MAX) {
return T_COSE_ERR_FAIL;
}
der_length = (long)der_encoded.len;
/* This imports the public key too */
pkey = d2i_PrivateKey(key_type, /* in: type */
NULL, /* unused: defined as EVP_PKEY **a */
(const unsigned char **)&der_encoded.ptr, /*in: pointer to DER byes; out: unused */
der_length /* in: length of DER bytes */
);
if(pkey == NULL) {
// TODO: better error?
return_value = T_COSE_ERR_FAIL;
goto Done;
}
key_pair->key.ptr = pkey;
return_value = T_COSE_SUCCESS;
Done:
return return_value;
}
/*
* Public function, see init_key.h
*/
enum t_cose_err_t
init_fixed_test_signing_key(int32_t cose_algorithm_id,
struct t_cose_key *key_pair)
{
struct q_useful_buf_c der_encoded_key;
/* Select the key bytes based on the algorithm */
switch (cose_algorithm_id) {
case T_COSE_ALGORITHM_ES256:
der_encoded_key = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ec_P_256_key_pair_der);
break;
case T_COSE_ALGORITHM_ES384:
der_encoded_key = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ec_P_384_key_pair_der);
break;
case T_COSE_ALGORITHM_ES512:
der_encoded_key = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ec_P_521_key_pair_der);
break;
case T_COSE_ALGORITHM_PS256:
case T_COSE_ALGORITHM_PS384:
case T_COSE_ALGORITHM_PS512:
der_encoded_key = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(RSA_2048_key_pair_der);
break;
case T_COSE_ALGORITHM_EDDSA:
der_encoded_key = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ed25519_key_pair_der);
break;
default:
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
/* Turn the DER bytes into a t_cose_key */
return init_signing_key_der(cose_algorithm_id,
der_encoded_key,
key_pair);
}
/*
* Public function, see init_keys.h
*/
void free_fixed_signing_key(struct t_cose_key key_pair)
{
EVP_PKEY_free(key_pair.key.ptr);
}
/*
* Public function, see init_key.h
*/
enum t_cose_err_t
init_fixed_test_ec_encryption_key(int32_t cose_ec_curve_id,
struct t_cose_key *public_key,
struct t_cose_key *private_key)
{
long pub_der_len;
long priv_der_len;
const unsigned char * priv_der_ptr;
const unsigned char * pub_der_ptr;
EVP_PKEY *pkey;
enum t_cose_err_t return_value;
/* The input key bytes are in ASN.1/DER format (RFC 5915 and RFC
* 5480) since that is what d2i_PrivateKey() and d2i_PUBKEY()
* accept.*/
switch(cose_ec_curve_id) {
case T_COSE_ELLIPTIC_CURVE_P_256:
pub_der_ptr = cose_ex_P_256_pub_der;
pub_der_len = sizeof(cose_ex_P_256_pub_der);
priv_der_ptr = cose_ex_P_256_pair_der;
priv_der_len = sizeof(cose_ex_P_256_pair_der);
break;
case T_COSE_ELLIPTIC_CURVE_P_521:
pub_der_ptr = cose_ex_P_521_pub_der;
pub_der_len = sizeof(cose_ex_P_521_pub_der);
priv_der_ptr = cose_ex_P_521_pair_der;
priv_der_len = sizeof(cose_ex_P_521_pair_der);
break;
default:
return T_COSE_ERR_PRIVATE_KEY_IMPORT_FAILED;
}
/* d2i_PrivateKey documentation isn't very clear. What is known
* from experimentation is that it does not support SEC1 raw keys
* and that it does support RFC 5915 ASN.1/DER keys.
*/
pkey = d2i_PrivateKey(EVP_PKEY_EC, /* in: type */
NULL, /* unused: defined as EVP_PKEY **a */
&priv_der_ptr, /* in: pointer to DER byes; out: unused */
priv_der_len /* in: length of DER bytes */
);
if(pkey == NULL) {
return_value = T_COSE_ERR_PRIVATE_KEY_IMPORT_FAILED;
goto Done;
}
private_key->key.ptr = pkey;
/* d2i_PrivateKey documentation isn't very clear. What is known
* from experimentation is that it does not support SEC1 raw keys
* and that it does support RFC 5480 ASN.1/DER keys.
*/
/* The openssl documentation says something about providing a
* PKEY initialized with an EC Key of the right curve/group, but
* that doesn't seem to be necessary. Probably because the RFC 5480
* input provided here includes the curve identifier so it is
* parsed out and set.
*/
pkey = d2i_PUBKEY(NULL, /* unused: defined as EVP_PKEY **a */
&pub_der_ptr, /* in: pointer to DER byes; out: unused */
pub_der_len /* in: length of DER bytes */
);
if(pkey == NULL) {
EVP_PKEY_free(private_key->key.ptr);
return_value = T_COSE_ERR_PRIVATE_KEY_IMPORT_FAILED;
goto Done;
}
public_key->key.ptr = pkey;
return_value = T_COSE_SUCCESS;
Done:
return return_value;
}
/*
* Public function, see init_key.h
*/
void
free_fixed_test_ec_encryption_key(struct t_cose_key key)
{
EVP_PKEY_free(key.key.ptr);
}
/*
* Public function, see init_keys.h
*/
int check_for_key_allocation_leaks(void)
{
/* So far no good way to do this for OpenSSL or malloc() in
general in a nice portable way. The PSA version does check so
there is some coverage of the code even though there is no
check here.
*/
return 0;
}
/*
char* e;
long err = ERR_peek_last_error_line(NULL, NULL);
e = ERR_error_string(err, NULL);
*/

View File

@@ -0,0 +1,300 @@
/*
* init_keys_psa.c
*
* Copyright 2019-2023, Laurence Lundblade
* Copyright (c) 2022, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include "t_cose/t_cose_common.h"
#include "t_cose/t_cose_standard_constants.h"
#include "t_cose/t_cose_key.h"
#include "psa/crypto.h"
#include "example_keys.h"
/*
* Import a signing key. Not sure what all formats this actually
* handles yet, but do know that just the private key works. Note that
* the curve and algorithm type are specified here directly.
*/
static enum t_cose_err_t
init_signing_key_from_xx(int32_t cose_algorithm_id,
struct q_useful_buf_c key_bytes,
struct t_cose_key *key_pair)
{
psa_key_type_t key_type;
psa_status_t crypto_result;
psa_key_handle_t key_handle;
psa_algorithm_t key_alg;
psa_key_attributes_t key_attributes;
/* There is not a 1:1 mapping from COSE algorithm to key type, but
* there is usually an obvious curve for an algorithm. That
* is what this does.
*/
switch(cose_algorithm_id) {
case T_COSE_ALGORITHM_ES256:
key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
key_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
break;
case T_COSE_ALGORITHM_ES384:
key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
key_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
break;
case T_COSE_ALGORITHM_ES512:
key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
key_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_512);
break;
case T_COSE_ALGORITHM_PS256:
key_type = PSA_KEY_TYPE_RSA_KEY_PAIR;
key_alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_256);
break;
case T_COSE_ALGORITHM_PS384:
key_type = PSA_KEY_TYPE_RSA_KEY_PAIR;
key_alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_384);
break;
case T_COSE_ALGORITHM_PS512:
key_type = PSA_KEY_TYPE_RSA_KEY_PAIR;
key_alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_512);
break;
default:
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
/* OK to call this multiple times */
crypto_result = psa_crypto_init();
if(crypto_result != PSA_SUCCESS) {
return T_COSE_ERR_FAIL;
}
/* When importing a key with the PSA API there are two main things
* to do.
*
* First you must tell it what type of key it is as this cannot be
* discovered from the raw data (because the import is not of a
* format like RFC 5915). The variable key_type contains that
* information including the EC curve. This is sufficient for
* psa_import_key() to succeed, but you probably want actually use
* the key.
*
* Second, you must say what algorithm(s) and operations the key
* can be used as the PSA Crypto Library has policy enforcement.
*/
key_attributes = psa_key_attributes_init();
/* The type of key including the EC curve */
psa_set_key_type(&key_attributes, key_type);
/* Say what algorithm and operations the key can be used with/for */
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attributes, key_alg);
/* Import the private key. psa_import_key() automatically
* generates the public key from the private so no need to import
* more than the private key. With ECDSA the public key is always
* deterministically derivable from the private key.
*/
crypto_result = psa_import_key(&key_attributes,
key_bytes.ptr,
key_bytes.len,
&key_handle);
if(crypto_result != PSA_SUCCESS) {
return T_COSE_ERR_FAIL;
}
/* This assignment relies on
* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER not being defined. If
* it is defined key_handle is a structure. This does not seem to
* be typically defined as it seems that is for a PSA
* implementation architecture as a service rather than an linked
* library. If it is defined, the structure will probably be less
* than 64 bits, so it can still fit in a t_cose_key. */
key_pair->key.handle = key_handle;
return T_COSE_SUCCESS;
}
/*
* Public function, see init_keys.h
*/
enum t_cose_err_t
init_fixed_test_signing_key(int32_t cose_algorithm_id,
struct t_cose_key *key_pair)
{
struct q_useful_buf_c key_bytes;
/* PSA doesn't support EdDSA so no keys for it here (OpenSSL does). */
switch(cose_algorithm_id) {
case T_COSE_ALGORITHM_ES256:
key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ec_P_256_priv_key_sec1);
break;
case T_COSE_ALGORITHM_ES384:
key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ec_P_384_priv_key_sec1);
break;
case T_COSE_ALGORITHM_ES512:
key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(ec_P_521_priv_key_sec1);
break;
case T_COSE_ALGORITHM_PS256:
key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(RSA_2048_key_pair_der);
break;
case T_COSE_ALGORITHM_PS384:
key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(RSA_2048_key_pair_der);
break;
case T_COSE_ALGORITHM_PS512:
key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(RSA_2048_key_pair_der);
break;
default:
return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
}
return init_signing_key_from_xx(cose_algorithm_id, key_bytes, key_pair);
}
/*
* Public function, see init_keys.h
*/
void free_fixed_signing_key(struct t_cose_key key_pair)
{
psa_destroy_key((psa_key_handle_t)key_pair.key.handle);
}
/*
* Public function, see init_keys.h
*/
enum t_cose_err_t
init_fixed_test_ec_encryption_key(int32_t cose_ec_curve_id,
struct t_cose_key *public_key,
struct t_cose_key *private_key)
{
psa_status_t status;
psa_key_attributes_t attributes;
psa_key_type_t type_private;
psa_key_type_t type_public;
uint32_t key_bitlen;
struct q_useful_buf_c priv_key_bytes;
struct q_useful_buf_c pub_key_bytes;
psa_crypto_init();
switch (cose_ec_curve_id) {
case T_COSE_ELLIPTIC_CURVE_P_256:
type_private = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
type_public = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
priv_key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(cose_ex_P_256_priv_sec1);
pub_key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(cose_ex_P_256_pub_sec1);
key_bitlen = 256;
break;
case T_COSE_ELLIPTIC_CURVE_P_521:
type_private = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1);
type_public = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1);
priv_key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(cose_ex_P_521_priv_sec1);
pub_key_bytes = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(cose_ex_P_521_pub_sec1);
key_bitlen = 521;
break;
default:
return T_COSE_ERR_UNSUPPORTED_ELLIPTIC_CURVE_ALG;
}
/* Import the private key from the SEC1 representation. It is
* the only format supported by psa_import_key(). ASN.1/DER/PEM
* formats are not supported.
*/
attributes = psa_key_attributes_init();
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&attributes, PSA_ALG_ECDH);
psa_set_key_type(&attributes, type_private);
psa_set_key_bits(&attributes, key_bitlen);
status = psa_import_key(&attributes,
priv_key_bytes.ptr, priv_key_bytes.len,
(mbedtls_svc_key_id_t *)(&private_key->key.handle));
/* Import the public key from the SEC1 representation. It is
* the only format supported by psa_import_key(). ASN.1/DER/PEM
* formats are not supported.
*/
attributes = psa_key_attributes_init();
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&attributes, PSA_ALG_ECDH);
psa_set_key_type(&attributes, type_public);
psa_set_key_bits(&attributes, key_bitlen);
status = psa_import_key(&attributes,
pub_key_bytes.ptr, pub_key_bytes.len,
(mbedtls_svc_key_id_t *)(&public_key->key.handle));
/*
* With PSA, it is also possible to import the private key as
* psa_import_key() will automatically derive the public key,
* the key handle will the key pair and will be usable as a public key.
attributes = psa_key_attributes_init();
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&attributes, PSA_ALG_ECDH);
psa_set_key_type(&attributes, type_private);
psa_set_key_bits(&attributes, key_bitlen);
status = psa_import_key(&attributes,
key_bytes.ptr, key_bytes.len,
(mbedtls_svc_key_id_t *)(&public_key->key.handle));
*/
if (status != PSA_SUCCESS) {
psa_destroy_key((psa_key_handle_t)private_key->key.handle);
return T_COSE_ERR_PRIVATE_KEY_IMPORT_FAILED;
}
return T_COSE_SUCCESS;
}
/*
* Public function, see init_keys.h
*/
void
free_fixed_test_ec_encryption_key(struct t_cose_key key)
{
psa_destroy_key((psa_key_handle_t)key.key.handle);
}
/*
* Public function, see init_keys.h
*/
int check_for_key_allocation_leaks(void)
{
return 0;
}

View File

@@ -0,0 +1,73 @@
/*
* init_keys_test.c
*
* Copyright 2019-2023, Laurence Lundblade
*
* SPDX-License-Identifier: BSD-3-Clause
*
* See BSD-3-Clause license in README.md
*/
#include "t_cose/t_cose_common.h"
#include "t_cose/t_cose_key.h"
/*
* Public function, see init_keys.h
*/
enum t_cose_err_t
init_fixed_test_signing_key(int32_t cose_ec_curve_id,
struct t_cose_key *key_pair)
{
(void)cose_ec_curve_id;
(void)key_pair;
return T_COSE_SUCCESS;
}
/*
* Public function, see init_keys.h
*/
void free_fixed_signing_key(struct t_cose_key key_pair)
{
(void)key_pair;
}
/*
* Public function, see init_key.h
*/
enum t_cose_err_t
init_fixed_test_ec_encryption_key(int32_t cose_algorithm_id,
struct t_cose_key *public_key,
struct t_cose_key *private_key)
{
(void)cose_algorithm_id;
(void)public_key;
(void)private_key;
return T_COSE_SUCCESS;
}
/*
* Public function, see init_key.h
*/
void
free_fixed_test_ec_encryption_key(struct t_cose_key key_pair)
{
(void)key_pair;
}
/*
* Public function, see init_keys.h
*/
int check_for_key_allocation_leaks(void)
{
return 0;
}

View File

@@ -0,0 +1,15 @@
To list curves:
openssl ecparam -list_curves
To generate an ECDSA key:
openssl ecparam -genkey -name secp384r1 -out k.pem
To print out the ECDSA key:
openssl ec -in k.pem -noout -text
https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html
https://superuser.com/questions/1103401/generate-an-ecdsa-key-and-csr-with-openssl

Some files were not shown because too many files have changed in this diff Show More