Internet-Draft U-CBOR March 2025
Rundgren Expires 19 September 2025 [Page]
Workgroup:
Internet Engineering Task Force
Internet-Draft:
draft-rundgren-universal-cbor-06
Published:
Intended Status:
Informational
Expires:
Author:
A. Rundgren, Ed.
Independent

Universal CBOR (U-CBOR)

Abstract

This document defines Universal CBOR (U-CBOR), a strict subset of CBOR (RFC 8949) intended to serve as a viable replacement for JSON in computationally advanced systems like Internet browsers, mobile phones, and Web servers. To foster interoperability, deterministic encoding is mandated. Furthermore, the document outlines how deterministic encoding combined with enhanced CBOR tools, enable cryptographic methods like signing and hashing, to optionally use "raw" (non-wrapped) CBOR data as input. This document mainly targets CBOR tool developers.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 19 September 2025.

Table of Contents

1. Introduction

The Universal CBOR (U-CBOR) specification is based on CBOR [RFC8949]. While there are different ways you can encode certain CBOR objects, this is non-trivial to support in general purpose platform-based tools, not to mention the limited utility of such measures. To cope with this, U-CBOR defines a specific (non-variant) encoding scheme, aka "Deterministic Encoding". The selected encoding scheme is believed to be compatible with most existing systems using CBOR. See also Appendix C.

U-CBOR is intended to be agnostic with respect to programming languages.

By combining the compact binary representation and the rich set of data types offered by CBOR, with a deterministic encoding scheme, U-CBOR could for new designs, serve as viable alternative to JSON [RFC8259]. Although the mandated encoding scheme is deployable in [CONSTRAINED] environments, the primary target is rather general-purpose computing platforms like mobile phones and Web servers.

However, for unleashing the full power of deterministic encoding, the ability to perform cryptographic operations on "raw" (non-wrapped) CBOR data, compliant U-CBOR tools need additional functionality. See also Appendix B.

Section 2 contains the actual specification.

1.1. Requirements Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

1.2. Common Definitions

2. Detailed Description

This section describes the three pillars that U-CBOR relies on.

2.1. Supported CBOR Objects

The following table shows the CBOR subset supported by U-CBOR:

Table 1: Supported CBOR Objects
CDDL Description
int Integer
bigint Big integer
float 16-, 32-, and 64-bit [IEEE754] numbers
tstr Text string encoded as UTF-8 [RFC3629]
bstr Byte string
bool Boolean true and false
null Represents a null object
[] Array
{} Map
#6.nnn(type) Tagged data

Conforming implementations (of course) only have to implement the U-CBOR types required by the targeted application(s).

Although extensions are imaginable (like supporting all "simple" types), extensions will most likely cause interoperability issues and are thus NOT RECOMMENDED. In addition, the mandated CBOR subset is compatible with most computer languages and platforms. Compared to the current state-of-the-art, JSON [RFC8259], the availability of bigint, bstr, and "tagged data" represent major improvements.

However, nothing prevents developers from at the application (API) level, through mapping concepts, support additional, "virtual" data types, analogous to how you map an application's data model to the set of data types available, be it a data interchange format, a database, or a programming language.

OpenAPI [OPENAPI] is an example of an API which defines data types through mapping.

2.2. Deterministic Encoding Scheme

In U-CBOR deterministic encoding is mandatory. The encoding scheme adheres to Section 4.2 of [RFC8949], but adds a few constraints (denoted by RFC+), where the RFC offers choices. The following list contains a summary of the U-CBOR deterministic encoding rules:

  • RFC+: Floating-point and integer objects MUST be treated as distinct types regardless of their numeric value. This is compliant with Rule 2 in Section 4.2.2 of [RFC8949].
  • RFC: Integers, represented by the int and bigint types, MUST use the int type if the value is between -264 and 264-1, otherwise the bigint type MUST be used. Appendix A.1 features a list of compliant integer sample values.

  • RFC: Floating-point numbers MUST always use the shortest [IEEE754] variant that preserves the precision of the original value. Appendix A.2 features a list of compliant floating-point sample values.
  • RFC+: NaN "signaling" items (like f97e01), MUST be rejected.
  • RFC: Map keys MUST be sorted in the bytewise lexicographic order of their deterministic encoding. Duplicate keys MUST be rejected. Somewhat surprisingly the following represents a properly sorted map:

    {
      "a": ... ,
      "b": ... ,
      "aa": ...
    }
  • RFC+: Since CBOR encodings according to this specification maintain uniqueness, there are no specific restrictions or tests needed in order to determine map key equivalence. As an (extreme) example, the floating-point numbers 0.0 and -0.0, and the integer number 0 could represent the distinct keys f90000, f98000, and 00 respectively.
  • RFC: Indefinite length objects MUST be rejected.

2.3. CBOR Tool Requirements

An important feature that deterministic encoding brings to the table is that wrapping CBOR data to be signed in bstr objects, like specified by COSE in Section 2 of [RFC9052], no longer is a prerequisite. That is, cryptographic operations can optionally be performed on "raw" CBOR data. Turn to Appendix B for an example of an application depending on such features.

However, to make this a reality, the following functionality MUST be provided by CBOR tools compliant with this specification:

  • Decoded CBOR primitives MUST remain immutable, regardless if they are stand-alone or being a part of a tagged object like bigfloat (see Section 3.4.4 of [RFC8949]).
  • It MUST be possible to find out the type of a CBOR object, before it is accessed.
  • It MUST be possible to add, delete, and update the contents of CBOR map and array objects, of decoded CBOR data.
  • It MUST be possible to reserialize decoded CBOR data, be it updated or not.
  • Irrespective of if CBOR data was decoded, updated, or created programmatically, deterministic encoding MUST be maintained.
  • Invalid or unsupported CBOR constructs, as well as CBOR data not adhering to the deterministic encoding scheme MUST be rejected. See also Appendix C and Appendix A.3.

As a consequence of these rules, CBOR data and application / platform-level data, MUST be separated for cases where reserialization could present a problem, like in this Chrome browser console example:

> let date = new Date('2025-03-02T13:08:55.0001+03:00');
> date.toISOString()
'2025-03-02T10:08:55.000Z'

How this separation actually is accomplished is out of scope for this specification. However, encapsulation of CBOR data in high-level, and self-rendering objects, represents an established method, featured in similar tools for ASN.1. The code in Appendix B.2 shows an example that updates and reserializes decoded CBOR data.

2.3.1. Protocol Primitives Support

To facilitate cross-platform protocol interoperability, implementers of U-CBOR compatible tools SHOULD include decoder API support for the following primitives:

Table 2: Protocol Primitives Support
CDDL Primitive Description Note
int Int8 8-bit signed integer 1
uint Uint8 8-bit unsigned integer 1
int Int16 16-bit signed integer 1
uint Uint16 16-bit unsigned integer 1
int Int32 32-bit signed integer 1
uint Uint32 32-bit unsigned integer 1
int Int64 64-bit signed integer 1
uint Uint64 64-bit unsigned integer 1
int / bigint BigInt Integer of arbitrary size 2
float16 Float16 16-bit floating-point number 3
float16 / float32 Float32 32-bit floating-point number 3
float Float64 64-bit floating-point number
bool Boolean Boolean
null Null Null 4
See note EpochTime Time-object expressed as a number 5
See note DateTime Time-object expressed as a text string 5
  1. Range testing MUST be performed using the traditional ranges for unsigned respectively two-complement numbers. That is, a hypothetical getUint8() MUST reject numbers outside of 0 to 255, whereas a hypothetical getInt8(), MUST reject numbers outside of -128 to 127.

  2. Note that a hypothetical getBigInt() MUST also accept CBOR int objects since int is used for integers that fit in CBOR major type 0 and 1 objects. See also Appendix A.1 and Appendix C.

  3. Some platforms do not natively support float32 and/or float16. In this case a hypothetical getFloat16() would need to use a bigger floating-point type for the return value.

    Note that a hypothetical getFloat16() MUST reject encountered Float32 and Float64 objects. See also Appendix C.

  4. Since a CBOR null typically represents the absence of a value, a decoder MUST provide a test-function, like isNull().

  5. Since CBOR does not feature a native-level time-object, Section 3.4 of [RFC8949] introduces two variants of time-objects using the CBOR tags 0 and 1. The time-objects SHOULD also be supported without the tag construct.

If a call does not match the underlying CDDL type, the call MUST be rejected,

Due to considerable variations between platforms, corresponding encoder API support does not appear to be meaningful to specify in detail: Java doesn't have built-in support for unsigned integers, whereas JavaScript requires the use of the JavaScript BigInt type for dealing with 64-bit integers.

2.3.2. Media Type

Protocols building on U-CBOR, are RECOMMENDED using the media type: application/cbor.

2.3.3. Diagnostic Notation Support

Compliant U-CBOR implementations SHOULD include support for bi-directional diagnostic notation, to facilitate:

  • Human-centric debugging and logging
  • Easy creation of test and configuration data

The supported notation is compliant with a subset of Section 8 of [RFC8949] (b32' and encoding indicators were left out), but adds a few items to make diagnostic notation slightly more adapted for parsing, like single-line comments:

Table 3: Diagnostic Notation Support
CDDL             Syntax             Description Notes
/ comment text / Multi-line comment. Multi-line comments are treated as whitespace and may thus also be used between CBOR objects. 6
# comment text Single-line comment. Single-line comments are terminated by a newline character ('\n') or EOF. Single-line comments may also terminate lines holding regular CBOR items. 6
int / bigint {sign}{0b|0o|0x}n Arbitrary sized integers without fractional components or exponents. See also CBOR integer encoding. For input data in diagnostic notation, binary, octal, and hexadecimal notation is also supported by prepending numbers with 0b, 0o, and 0x respectively. The latter also permit arbitrary insertions of '_' characters between digits to enable grouping of data like 0b100_000000001. 1, 2
float16 / float32 / float64 {sign}n.n{n} Floating point values MUST include a decimal point and at least one fractional digit, whereas exponents are optional. 1, 2
float16 NaN Not a number.
float16 {sign}Infinity Infinity. 2
bstr h'hex data' Byte data provided in hexadecimal notation. Each byte MUST be represented by two hexadecimal digits. 3
bstr b64'base64 data' Byte data provided in base64 or base64URL notation. Padding with '=' characters is optional. 3, 6
bstr 'text' Byte data provided as UTF-8 encoded text. 4, 5, 6
bstr << {object, }... >> Construct holding zero or more comma-separated CBOR objects which are subsequently wrapped in a byte-string. 6
tstr "text" UTF-8 encoded text-string. 4, 5
bool true | false Boolean value.
null null Null value.
[] [ {object, }... ] Array with zero or more comma-separated CBOR objects.
{} { {key:value, }... } Map with zero or more comma-separated key/value pairs. Keys and values are expressed as CBOR objects.
#6.n n( object ) Tag holding a CBOR object. 1
  1. The letter n in the Syntax column denotes one or more digits.
  2. The optional {sign} must be a single hyphen ('-') character.
  3. Input only: between the quotes, the whitespace characters (' ', '\t', '\r', '\n') are ignored.
  4. Input only: the control characters ('\t' and '\n') inside of string quotes become a part of the text. For normalizing line terminators, a single '\r' or the combination '\r\n' are rewritten as '\n'. To avoid getting newline characters ('\n') included in multi-line text strings, a line continuation marker consisting of a backslash ('\') immediately preceding the newline may be used.
  5. Text strings may also include JavaScript compatible escape sequences ('\'', '\"', '\\', '\b', '\f', '\n', '\r', '\t', '\uhhhh').
  6. Input only.

3. IANA Considerations

This memo includes no request to IANA.

4. Security Considerations

All is good 😸

U-CBOR does not introduce security issues beyond what is already applicable to [RFC8949]. Applications may certainly add security issues, but these are out of scope for this specification.

5. References

5.1. Normative References

[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC8949]
Bormann, C. and P. Hoffman, "Concise Binary Object Representation (CBOR)", STD 94, RFC 8949, DOI 10.17487/RFC8949, , <https://www.rfc-editor.org/info/rfc8949>.
[RFC8610]
Birkholz, H., Vigano, C., and C. Bormann, "Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, , <https://www.rfc-editor.org/info/rfc8610>.
[RFC3629]
Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, , <https://www.rfc-editor.org/info/rfc3629>.
[IEEE754]
IEEE, "IEEE Standard for Floating-Point Arithmetic", IEEE Std 754-2019, DOI 10.1109/IEEESTD.2019.8766229, <https://ieeexplore.ieee.org/document/8766229>.

5.2. Informative References

[RFC9052]
Schaad, J., "CBOR Object Signing and Encryption (COSE): Structures and Process", STD 96, RFC 9052, DOI 10.17487/RFC9052, , <https://www.rfc-editor.org/info/rfc9052>.
[RFC9053]
Schaad, J., "CBOR Object Signing and Encryption (COSE): Initial Algorithms", RFC 9053, DOI 10.17487/RFC9053, , <https://www.rfc-editor.org/info/rfc9053>.
[RFC8785]
Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, , <https://www.rfc-editor.org/info/rfc8785>.
[RFC8259]
Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, , <https://www.rfc-editor.org/info/rfc8259>.
[CSF]
Rundgren, A., "CBOR Signature Format (CSF)", <https://cyberphone.github.io/javaapi/org/webpki/cbor/doc-files/signatures.html>.
[CEF]
Rundgren, A., "CBOR Encryption Format (CEF)", <https://cyberphone.github.io/javaapi/org/webpki/cbor/doc-files/encryption.html>.
[COTX]
Rundgren, A., "CBOR Object Type Extension (COTX)", <https://www.ietf.org/archive/id/draft-rundgren-cotx-04.html>.
[CONSTRAINED]
"D-CBOR for Constrained Devices", <https://github.com/cyberphone/D-CBOR/blob/main/d-cbor-4-constrained-devices.md>.
[NODE.JS]
"Node.js - JavaScript server", <https://nodejs.org/>.
[CBOR.JS]
"CBOR.js - CBOR for JavaScript", <https://github.com/cyberphone/CBOR.js>.
[CSF-LAB]
"Online CBOR and CSF test tool", <https://test.webpki.org/csf-lab>.
[PLAYGROUND]
"Online CBOR testing tool", <https://cyberphone.github.io/CBOR.js/doc/playground.html>.
[OPENKEYSTORE]
"Java library supporting JSON, CBOR, and Crypto", <https://github.com/cyberphone/openkeystore>.
[ANDROID-CBOR]
"Android/Java library supporting CBOR and Crypto", <https://github.com/cyberphone/android-cbor>.
[OPENAPI]
"The OpenAPI Initiative", <https://openapis.org/>.
[CREDENTIALS]
Sporny (et al), M., "Verifiable Credential Data Integrity 1.0", , <https://www.w3.org/TR/vc-data-integrity/>.
[CBOR.ME]
Bormann, C., "Online CBOR testing tool", <https://cbor.me/>.
[ECMASCRIPT]
Ecma International, "ECMAScript® 2024 Language Specification", Standard ECMA-262, 15th Edition, , <https://www.ecma-international.org/publications/standards/Ecma-262.htm>.
[WALLET]
Rundgren, A., "Defensive publication: Partial Encryption, Full Signature", <https://cyberphone.github.io/doc/defensive-publications/partial-encryption-full-signature.pdf>.

Appendix A. Deterministic Encoding Samples

A.1. Integers

This normative section holds a selection of CBOR integer values, with an emphasize on edge cases.

Table 4: Integers
Value CBOR Encoding Description
0 00 Smallest positive implicit int
-1 20 Smallest negative implicit int
23 17 Largest positive implicit int
-24 37 Largest negative implicit int
24 1818 Smallest positive one-byte int
-25 3818 Smallest negative one-byte int
255 18ff Largest positive one-byte int
-256 38ff Largest negative one-byte int
256 190100 Smallest positive two-byte int
-257 390100 Smallest negative two-byte int
65535 19ffff Largest positive two-byte int
-65536 39ffff Largest negative two-byte int
65536 1a00010000 Smallest positive four-byte int
-65537 3a00010000 Smallest negative four-byte int
4294967295 1affffffff Largest positive four-byte int
-4294967296 3affffffff Largest negative four-byte int
4294967296 1b0000000100000000 Smallest positive eight-byte int
-4294967297 3b0000000100000000 Smallest negative eight-byte int
18446744073709551615 1bffffffffffffffff Largest positive eight-byte int
-18446744073709551616 3bffffffffffffffff Largest negative eight-byte int
18446744073709551616 c249010000000000000000 Smallest positive bigint
-18446744073709551617 c349010000000000000000 Smallest negative bigint

A.2. Floating Point Numbers

This normative section holds a selection of [IEEE754] 16, 32, and 64-bit values, with an emphasize on edge cases.

The textual representation of the values is based on the serialization method for the Number data type, defined by [ECMASCRIPT] with one change: to comply with diagnostic notation (section 8 of [RFC8949]), all values are expressed as floating-point numbers. The rationale for using [ECMASCRIPT] serialization is because it supposed to generate the shortest and most correct representation of [IEEE754] numbers.

Table 5: Floating Point Numbers
Value CBOR Encoding Description
0.0 f90000 Zero
-0.0 f98000 Negative zero
Infinity f97c00 Infinity
-Infinity f9fc00 -Infinity
NaN f97e00 NaN
5.960464477539063e-8 f90001 Smallest positive subnormal 16-bit float
0.00006097555160522461 f903ff Largest positive subnormal 16-bit float
0.00006103515625 f90400 Smallest positive 16-bit float
65504.0 f97bff Largest positive 16-bit float
1.401298464324817e-45 fa00000001 Smallest positive subnormal 32-bit float
1.1754942106924411e-38 fa007fffff Largest positive subnormal 32-bit float
1.1754943508222875e-38 fa00800000 Smallest positive 32-bit float
3.4028234663852886e+38 fa7f7fffff Largest positive 32-bit float
5.0e-324 fb0000000000000001 Smallest positive subnormal 64-bit float
2.225073858507201e-308 fb000fffffffffffff Largest positive subnormal 64-bit float
2.2250738585072014e-308 fb0010000000000000 Smallest positive 64-bit float
1.7976931348623157e+308 fb7fefffffffffffff Largest positive 64-bit float
-0.0000033333333333333333 fbbecbf647612f3696 Randomly selected number
10.559998512268066 fa4128f5c1          -"-
10.559998512268068 fb40251eb820000001 Next in succession
295147905179352830000.0 fa61800000 268 (diagnostic notation truncates precision)
2.0 f94000 Number without a fractional part
-5.960464477539063e-8 f98001 Smallest negative subnormal 16-bit float
-5.960464477539062e-8 fbbe6fffffffffffff Adjacent smallest negative subnormal 16-bit float
-5.960464477539064e-8 fbbe70000000000001          -"-
-5.960465188081798e-8 fab3800001          -"-
0.0000609755516052246 fb3f0ff7ffffffffff Adjacent largest subnormal 16-bit float
0.000060975551605224616 fb3f0ff80000000001          -"-
0.000060975555243203416 fa387fc001          -"-
0.00006103515624999999 fb3f0fffffffffffff Adjacent smallest 16-bit float
0.00006103515625000001 fb3f10000000000001          -"-
0.00006103516352595761 fa38800001          -"-
65503.99999999999 fb40effbffffffffff Adjacent largest 16-bit float
65504.00000000001 fb40effc0000000001          -"-
65504.00390625 fa477fe001          -"-
1.4012984643248169e-45 fb369fffffffffffff Adjacent smallest subnormal 32-bit float
1.4012984643248174e-45 fb36a0000000000001          -"-
1.175494210692441e-38 fb380fffffbfffffff Adjacent largest subnormal 32-bit float
1.1754942106924412e-38 fb380fffffc0000001          -"-
1.1754943508222874e-38 fb380fffffffffffff Adjacent smallest 32-bit float
1.1754943508222878e-38 fb3810000000000001          -"-
3.4028234663852882e+38 fb47efffffdfffffff Adjacent largest 32-bit float
3.402823466385289e+38 fb47efffffe0000001          -"-

A.3. Invalid Encodings

The following table holds a selection of valid CBOR objects, not permitted by U-CBOR.

Table 6: Invalid Encodings
CBOR Encoding Diagnostic Notation Description Note
a2616200616101 {"b":0,"a":1} Improper map key ordering 1
1900ff 255 Number with leading zero bytes 1
c34a00010000000000000000 -18446744073709551617 Number with leading zero bytes 1
Fa41280000 10.5 Not in shortest encoding 1
fa7fc00000 NaN Not in shortest encoding 1
c243010000 65536 Incorrect value for bigint 1
f97e01 NaN NaN with payload 1
f7 undefined Unsupported simple type
f0 simple(16) Unsupported simple type
5f4101420203ff (_ h'01', h'0203') Unsupported indefinite length object
  1. See also Appendix C.

Appendix B. Enveloped Signatures

This is a non-normative appendix showing how U-CBOR can be used for supporting enveloped signatures.

The primary advantages with enveloped signatures compared to enveloping signatures (like used by COSE [RFC9052]), include:

Enveloped signatures are for example featured in Verified Credentials [CREDENTIALS]. A drawback with designs based on JSON [RFC8259] is that they rely on canonicalization schemes like JCS [RFC8785], that require specialized encoders and decoders, whereas U-CBOR works "straight out of the box".

B.1. Sample Signature

Although this specification is not "married" to any particular signature schema, the following example uses the CBOR Signature Format [CSF]. For the sake of simplicity, the example uses an HMAC (see Appendix B.1.4) as signature algorithm.

For a more sophisticated use of U-CBOR, combining signatures and encryption, see [WALLET].

B.1.1. Unsigned Data

Imagine you have a CBOR map object like the following that you want to sign:

{
  1: "data",
  2: "more data"
}

B.1.2. Signature Process

This section describes the steps required for adding an enveloped signature to the CBOR map object in Appendix B.1.1. To avoid confusing CBOR map keys with cryptographic keys, the former are referred to as "labels".

  1. Add an empty CSF container (a CBOR map) to the unsigned CBOR map using an application-defined label (-1).
  2. Add the designated signature algorithm to the CSF container using the CSF algorithm label (1).
  3. Optional. Add other signature meta data to the CSF container. Not used in the example.
  4. Generate a signature by invoking a (hypothetical) signature method with the following arguments:

    • the designated signature key.
    • the designated signature algorithm.
    • the deterministic encoding of the current CBOR object in its entirety. In the example that would be a301646461746102696d6f7265206461746120a10105, if expressed in hex code.
  5. Add the returned signature value to the CSF container using the CSF signature label (6).

The result after the final step (using the parameters from Appendix B.1.4), should match the following CBOR object:

{
  1: "data",
  2: "more data",
  -1: {
    1: 5,
    6: h'4853d7730cc1340682b1748dc346cf627a5e91ce62c67fff15c40257ed2a37a1'
  }
}

Note that the signature covers the entire CBOR object except for the CSF signature value and label (6).

B.1.3. Validation Process

In order to validate the enveloped signature created in the Appendix B.1.2, the following steps are performed:

  1. Fetch a reference to the CSF container using the application-defined label (-1). Next perform the following operations using the reference:

    1. Retrieve the signature algorithm using the CSF algorithm label (1).
    2. Retrieve the signature value using the CSF algorithm label (6).
    3. Remove the CSF algorithm label (6) and its associated value.

    Now we should have exactly the same CBOR object as we had before step #4 in Appendix B.1.2. That is:

    {
      1: "data",
      2: "more data",
      -1: {
        1: 5
      }
    }
  2. Validate the signature data by invoking a (hypothetical) signature validation method with the following arguments:

    • the designated signature key (in the example taken from Appendix B.1.4).
    • the signature algorithm retrieved in step #1.
    • the signature value retrieved in step #1.
    • the deterministic encoding of the current CBOR object in its entirety.

Note: this is a "bare-bones" validation process, lacking the ruggedness of a real-world implementation.

B.1.4. Example Parameters

The signature and validation processes depend on the COSE [RFC9053] algorithm "HMAC 256/256" and an associated 256-bit key, here provided in hex code:

7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a

B.2. Code Example

Using a JavaScript implementation [CBOR.JS] of U-CBOR, together with Node.js [NODE.JS], basic signature creation and validation supporting the example in Appendix B.1, could be performed by the following code:

// hmac.mjs
import CBOR from 'cbor-object';
const crypto = await import('node:crypto');

// CSF constants
const CSF_ALG_LBL = CBOR.Int(1);
const CSF_SIG_LBL = CBOR.Int(6);

// COSE => Node.js algorithm translation
const HASH_ALGORITHMS = new Map()
    .set(5, "sha256").set(6, "sha384").set(7, "sha512");

function hmac(coseAlg, key, data) {
    let alg = HASH_ALGORITHMS.get(coseAlg);
    if (alg === undefined) throw "Unknown alg: " + coseAlg;
    return crypto.createHmac(alg, key).update(data).digest();
}

const SHARED_KEY = crypto.createSecretKey(
    '7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a', 'hex');

const APP_P1_LBL  = CBOR.Int(1);                   // Application label
const APP_P2_LBL  = CBOR.Int(2);                   //        ""
const APP_CSF_LBL = CBOR.Int(-1);                  // Where to put the
                                                   // CSF container
////////////////////////////////////
// Create an unsigned CBOR object //
////////////////////////////////////
let object = CBOR.Map()
    .set(APP_P1_LBL, CBOR.String("data"))          // Application data
    .set(APP_P2_LBL, CBOR.String("more data"));    //        ""

////////////////////////////////////////
// Add a signature to the CBOR object //
////////////////////////////////////////
const COSE_ALG = 5;                                // Selected HMAC algorithm

let csf = CBOR.Map()                               // Create CSF container and
    .set(CSF_ALG_LBL, CBOR.Int(COSE_ALG));         // add COSE algorithm to it
object.set(APP_CSF_LBL, csf);                      // Add CSF container to object
let sig = hmac(COSE_ALG,                           // Generate signature over
               SHARED_KEY,                         // the current object
               object.encode());                   // encode(): all we got so far
csf.set(CSF_SIG_LBL, CBOR.Bytes(sig));             // Add signature to CSF container
let cborBinary = object.encode();                  // Return CBOR as an Uint8Array

console.log(object.toString());                    // Show in Diagnostic Notation

/////////////////////////////////////
// Validate the signed CBOR object //
/////////////////////////////////////
object = CBOR.decode(cborBinary);                  // Decode CBOR object
csf = object.get(APP_CSF_LBL);                     // Get CSF container
let alg = csf.get(CSF_ALG_LBL).getInt();           // Get COSE algorithm
let readSig = csf.remove(CSF_SIG_LBL).getBytes();  // Get and REMOVE signature value
let actualSig = hmac(alg,                          // Calculate signature over
                     SHARED_KEY,                   // the current object
                     object.encode());             // encode(): all but the signature
if (CBOR.compareArrays(readSig, actualSig)) {      // HMAC validation
    throw "Signature did not validate";
}
// Validated object, access the "payload":
let p1 = object.get(APP_P1_LBL).getString();       // p1 should now contain "data"

Note that this code depends heavily on the CBOR tool features outlined in Section 2.3.

Appendix C. Supporting Existing Systems

It is assumed that most systems using CBOR are able to process an (application specific), selection of CBOR data items that are encoded in compliance with [RFC8949]. Since the deterministic encoding scheme mandated by U-CBOR, also is compliant with [RFC8949], there should be no major interoperability issues. That is, if the previous assumption actually is correct 😏

However, in the other direction (U-CBOR tools processing data from Systems using "legacy" CBOR encoding schemes), the situation is likely to be considerably more challenging since deterministic encoding "by design" is strict. Due to this potential obstacle, implementers of U-CBOR tools, are RECOMMENDED to offer decoder options that permit "relaxing" the rigidness of deterministic encoding with respect to:

Numbers:
Numbers MUST still be compliant with [RFC8949].
Sorted maps:
Duplicate keys MUST still be rejected.

Note that regardless of the format of decoded CBOR data, a compliant U-CBOR implementation MUST maintain deterministic encoding. See also Appendix A.3.

Appendix D. Compatible Online Tools

For testing and learning about U-CBOR, there are currently a number of compatible online tools (subject to availability...).

[PLAYGROUND]:
Browser-based CBOR "playground"
[CSF-LAB]:
Server-based CBOR and [CSF] test system

Appendix E. Compatible Implementations

For using U-CBOR in applications, there are currently a number of compatible libraries.

[CBOR.JS]:
JavaScript-based implementation supporting browsers as well as [NODE.JS]
[OPENKEYSTORE]:
Java-based implementation that also supports [CSF] and [CEF]
[ANDROID-CBOR]:
Android Java-based implementation that also supports [CSF] and [CEF]

Document History

Acknowledgements

For verifying the correctness of the encoding scheme, the [CBOR.ME] on-line CBOR tool, by the [RFC8949] author, Carsten Bormann, proved to be invaluable.

Author's Address

Anders Rundgren (editor)
Independent
Montpellier
France