RFC 8949 CBOR 2020年12月
Bormann & Hoffman 标准轨道 [页]
流:
互联网工程任务组(IETF)
RFC:
8949
STD:
94
废止:
7049
类别:
标准轨道
发布:
ISSN:
2070-1721
作者:
C. Bormann
Universität Bremen TZI
P. Hoffman
ICANN

RFC 8949

简明二进制对象表示(CBOR)

摘要

简明二进制对象表示(CBOR)是一种数据格式,其设计 目标包括 实现极小代码大小、相当小的消息大小,以及 无需版本协商的 可扩展性。这些设计目标使其不同于早期的 二进制 序列化格式,例如 ASN.1 和 MessagePack。

本文档废止 RFC 7049,在保持与 RFC 7049 交换格式完全兼容的同时,提供编辑性改进、新的 细节和勘误修正。它不会创建该格式的 新版本。

本备忘录状态

这是一份互联网标准轨道文档。

本文档是互联网工程任务组 (IETF)的产物。它代表了 IETF 社区的共识。它已经 接受公开审查,并已由 互联网工程指导组(IESG)批准发布。关于 互联网标准的更多信息见 RFC 7841 第 2 节。

关于本文档的当前状态、任何 勘误以及如何提供反馈的信息,可以在 https://www.rfc-editor.org/info/rfc8949 获取。

目录

1. 引言

用于结构化数据的二进制表示的标准化格式 有数百种(也称为二进制序列化格式)。在 这些格式中,有些面向特定信息领域,而另一些则 泛化用于任意数据。在 IETF 中,后者类别中可能最知名的 格式是 ASN.1 的 BER 和 DER [ASN.1]

此处定义的格式遵循一些特定设计目标,而这些目标 目前的格式并未很好满足。底层数据模型是 JSON 数据模型 [RFC8259] 的扩展版本。需要 注意的是, 这并不是建议普遍扩展 RFC 8259 中的语法, 因为这样做会与已经部署的 JSON 文档产生重大的向后不兼容。相反,本文档只是 定义了自己的数据模型, 该模型从 JSON 出发。

附录 E 列出了一些现有的二进制格式 并讨论 它们在多大程度上适合或不适合简明 二进制对象表示(CBOR)的设计目标。

本文档废止 [RFC7049], 在保持与 RFC 7049 交换格式完全兼容的同时,提供编辑性改进、新的 细节和勘误修正。它不会创建该格式的 新版本。

1.1. 目标

CBOR 的目标大致按重要性递减顺序 如下:

  1. 该表示必须能够无歧义地编码大多数 互联网标准中使用的 常见数据格式。

    • 它必须使用二进制编码表示一组合理的基本 数据类型和 结构。这里的“合理”很大程度上 受 JSON 的能力影响,主要增加了 二进制字节串。所支持的结构仅限于 数组和树;不支持循环和格状图。
    • 并不要求所有数据 格式都被唯一 编码;也就是说,数字“7”可以 用多种不同方式编码,这是可以接受的。
  2. 编码器或解码器的代码必须能够足够紧凑, 以支持内存、处理器能力 和指令集都非常受限的系统。

    • 编码器和解码器需要能够用非常 少量代码实现 (例如,在 [RFC7228] 中定义的第 1 类受限节点中)。
    • 该格式应使用现代机器的 数据表示 (例如,不要求二进制到十进制的转换)。
  3. 数据必须能够在没有模式描述的情况下被解码。

    • 与 JSON 类似,已编码数据应当是 自描述的,以便 可以编写通用解码器。
  4. 序列化必须相当紧凑,但对于编码器和解码器而言, 数据紧凑性次于代码紧凑性。

    • 这里的“合理”以上限 大小为 JSON, 并受实现复杂性的限制,后者限制了 为实现这种紧凑性所能投入的工作量。 使用通用压缩方案或大量 位操作会违反复杂性目标。
  5. 该格式必须既适用于受限节点,也适用于 高容量应用。

    • 这意味着它在编码和解码时 对 CPU 使用必须相当节省。 这既与受限 节点相关,也与数据量非常大的 应用中的潜在使用相关。
  6. 该格式必须支持所有 JSON 数据类型,以便在 JSON 之间 相互转换。

    • 只要所表示的数据处于 JSON 的能力范围内, 它就必须支持合理程度的 转换。必须可以为所有 数据类型定义一种朝向 JSON 的单向映射。
  7. 该格式必须是可扩展的,并且扩展后的数据必须能够被 早期解码器解码。

    • 该格式被设计为可使用数十年。
    • 该格式必须支持一种 允许回退的可扩展性形式, 使得不理解某个扩展的解码器 仍能解码该消息。
    • 该格式必须能够在未来由后续 IETF 标准进行扩展。

1.2. 术语

本文档中的关键词“MUST”、“MUST NOT”、 “REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、 “RECOMMENDED”、“NOT RECOMMENDED”、 “MAY”和“OPTIONAL”应按照 BCP 14 [RFC2119] [RFC8174] 中所述进行解释,但仅当它们像这里所示以全大写形式出现时才如此。

术语“byte”按其现在的惯常含义使用,作为 “octet”的同义词。所有多字节值都按网络字节序编码 (即最高有效字节在前,也称为“big-endian”)。

本规范使用以下术语:

数据项:
一片单独的 CBOR 数据。数据项的结构可以 包含零个、一个或多个嵌套数据项。该术语既用于 表示格式中的数据项,也用于 可由解码器从中导出的抽象概念;前者可以 通过使用术语“已编码数据项”来专门指称。
解码器:
解码良构的已编码 CBOR 数据项并将其提供给 应用的过程。形式上说,解码器包含一个解析器,用于 按 CBOR 的语法规则拆分输入,也包含一个 语义处理器,用于将数据准备成适合 应用的形式。
编码器:
从应用信息生成 CBOR 数据 项的(良构)表示格式的过程。
数据流:
零个或多个数据项的序列,未进一步组装到 更大的包含数据项中(参见 [RFC8742] 了解一个应用)。 构成数据流的独立数据项 有时也称为“顶层数据 项”。
良构:
遵循 CBOR 语法结构的数据项。 良构数据项使用 CBOR 中定义的、由其值 所隐含的初始字节以及字节串 和/或数据项,并且不包含后续的无关数据。 按定义,CBOR 解码器只从良构数据项返回内容。
有效:
既良构又遵循适用于 CBOR 数据项的 语义限制的数据项(第 5.3 节)。
预期:
除了其普通英语含义外,术语“expected”用于 描述应用对其输入数据提出的、超出 CBOR 有效性之外的 要求。良构(总体上可处理)、有效(由 有效性检查通用解码器检查)和预期(由 应用检查)构成可接受性层级。
流解码器:
解码数据流并在序列中的每个数据 项被接收时将其提供给应用的 过程。

诸如 Infinity、NaN (非数)、负零和次正规数等浮点值的术语和概念 在 [IEEE754] 中定义。

在解释位算术或数据类型时,本文档使用 C 编程语言 [C] 中熟悉的记法,例外是 “..”表示包含所给两端点的范围,而上标 记法表示求幂。例如,2 的 64 次方 记作:264。 在本规范的纯文本版本中,上标记法 不可用,因此用替代记法呈现。 该记法并未针对本 RFC 优化;遗憾的是,它 与 C 的异或存在歧义(异或只在附录中使用, 而附录又不使用求幂),因此纯文本版本的读者 需要谨慎理解。

示例和伪代码 假定有符号整数使用二进制补码表示,并且 有符号整数的右移会执行符号扩展;这些 假设也在 2020 版 C++ 的第 6.8.1 节(basic.fundamental) 和第 7.6.7 节(expr.shift)中规定(目前可作为 最终草案获得,[Cplusplus20])。

类似于十六进制数的“0x”记法, 二进制记法中的数字以 “0b”为前缀。下划线可以仅为 可读性添加到数字中,因此 0b00100001(0x21)可以写作 0b001_00001,以强调对字节中位的期望解释; 在这种情况下,它被拆分为三位和五位。已编码 CBOR 数据 项有时以“0x”或“0b”记法给出;这些值 首先按 C 中的方式解释为数字,然后按 网络字节序解释为字节串,包括该记法中 表示的任何前导零字节。

词语可以用斜体表示强调;在本规范的纯文本 形式中,这通过在词语两侧加下划线字符来表示。 逐字文本(例如,来自 编程语言的名称)可以设置为 monospace 字体;在纯 文本中,这通过用双引号包围 文本来近似表示,但这种方式有些歧义(双引号也保留其通常含义)。

2. CBOR 数据模型

CBOR 明确说明其通用数据模型,该模型定义了 CBOR 中可表示的所有数据项的集合。其基本通用 数据模型可以通过注册“简单值”和 标签来扩展。应用随后可以创建所得扩展通用 数据模型的一个子集,以构建其特定数据模型。

在能够表示通用数据模型中数据项的环境中, 可以实现通用 CBOR 编码器和解码器 (这通常涉及为那些在环境中尚无自然表示的 数据项定义额外的实现数据类型)。 提供通用编码器和 解码器的能力是 CBOR 的一个明确设计目标;然而,许多应用 会提供自己的应用特定编码器和/或解码器。

第 3 节 中定义的基本(未扩展)通用数据模型中,数据项是 以下之一:

请注意,在此模型中,整数和浮点值是不同的, 即使它们具有相同的数值。

还请注意,序列化变体在通用 数据模型层级不可见。这种刻意的不可见性包括已编码 浮点值的字节数。它也包括“实参”(参见 第 3 节)的编码选择,例如 整数的编码、文本或字节串长度的编码、数组中元素数 或映射中成对数的编码,或标签号的编码。

2.1. 扩展通用数据 模型

本文档已经通过注册 一些简单值和标签号来扩展此基本通用数据模型,例如:

  • falsetruenullundefined (由 20..23 标识的简单值,第 3.3 节
  • 范围和精度 大于上述范围的整数和浮点值 (标签号 2 到 5,第 3.4 节
  • 应用数据类型,例如由 RFC 3339 定义的 时间点或日期/时间字符串(标签号 1 和 0,第 3.4 节

扩展通用数据模型的其他元素可以(并且已经) 通过为 CBOR 创建的 IANA 注册表来定义。即使 通用编码器或解码器不知道这样的扩展, 使用该扩展的数据项也可以通过 在应用接口处将它们表示在基本 通用数据模型内,即作为通用简单值或 通用标签,在应用之间传递。

换言之,基本通用数据模型按 本文档定义是稳定的,而扩展通用数据模型通过 注册新的简单值或标签号而扩展,但永不缩小。

虽然强烈期望通用编码器和解码器 能够以适合其编程环境的形式表示 falsetruenullundefined 被有意 省略),但由标签创建的数据模型扩展的实现确实是 可选的,并且属于实现质量问题。

2.2. 特定数据模型

基于 CBOR 的协议的特定数据模型通常会取 扩展通用数据模型的 一个子集,并为该子集及其组成部分中的 数据项分配应用语义。 在记录此类特定数据模型并指定数据项的类型时, 最好使用其 通用数据模型名称(“负整数”、“数组”)来识别类型,而不是 引用其 CBOR 表示的方面(“主类型 1”、 “主类型 4”)。

特定数据模型还可以指定值等价性(包括 不同类型的值),用于映射键和编码器自由度。例如, 在通用数据模型中,有效映射 MAY 同时具有 00.0 作为键,并且编码器 MUST NOT0.0 编码为整数 (主类型 0,第 3.1 节)。然而,如果特定数据 模型 声明整数值的浮点表示和整数表示 是等价的,则在单个 映射中同时使用映射键 00.0 会被认为 是重复的,即使它们被编码为不同主类型,因此是无效的;并且编码器可以 将整数值 浮点数编码为整数,反之亦然,也许是为了节省已编码字节。

3. CBOR 编码规范

CBOR 数据项(第 2 节)被编码为 或从 承载良构已编码数据项的字节串中解码,如本节所述。编码在 附录 B表 7 中按初始字节索引进行了总结。 编码器 MUST 只生成良构的 已编码数据项。解码器 MUST NOT 在遇到非良构的已编码 CBOR 数据项输入时 返回已解码的数据项(这并不 削弱诊断和恢复工具的有用性,这些工具 可能从损坏的已编码 CBOR 数据项中提供一些信息)。

每个已编码数据项的初始字节同时包含 关于主类型的信息(高 3 位,见 第 3.1 节)和附加信息(低 5 位)。 除少数例外外,附加信息的值 描述如何装载一个无符号整数“实参”:

小于 24:
实参的值就是附加信息的值。
24、25、26 或 27:
实参的值分别保存在随后 1、2、4 或 8 个字节中, 按网络字节序排列。对于主类型 7 和 附加信息值 25、26、27,这些字节不是作为 整数实参使用,而是作为浮点值使用(参见 第 3.3 节)。
28、29、30:
这些值保留供 CBOR 格式的未来扩展使用。 在当前版本的 CBOR 中,已编码项不是良构的。
31:
不导出实参值。 如果主类型为 0、1 或 6,则已编码项不是 良构的。对于主类型 2 到 5,该项的长度是 不定的;对于主类型 7,该字节根本不构成数据 项,而是终止一个不定长项;这些都在 第 3.2 节 中描述。

初始字节以及为构造 实参而消耗的任何附加字节统称为数据项的头部

此实参的含义取决于主类型。 例如,在主类型 0 中,实参就是数据 项本身的值(而在主类型 1 中,数据项的值 由实参计算得出);在主类型 2 和 3 中,它给出随后 字符串数据的字节长度;而在主类型 4 和 5 中,它用于 确定所包含的数据项数量。

如果已编码字节序列在数据项结束前终止, 则该项不是良构的。如果最外层已编码项被解码后, 已编码 字节序列仍有剩余字节,则该编码不是 单个良构 CBOR 项。根据应用,解码器可以 将该编码视为非良构,或者只是向应用标识 剩余字节的起始位置。

CBOR 解码器实现可以基于一个包含 初始字节所有 256 个已定义值的跳转表(表 7)。受限实现中的解码器 也可以改为使用初始字节和后续字节的结构, 以获得更紧凑的代码(参见 附录 C 以大致了解其可能的样子)。

3.1. 主类型

下面列出主类型以及与该类型关联的附加信息和 其他字节。

主类型 0:
范围为 0..264-1(含两端)的无符号整数。已 编码项的值就是实参本身。例如, 整数 10 表示为一个字节 0b000_01010(主类型 0, 附加信息 10)。整数 500 会是 0b000_11001 (主类型 0,附加信息 25),后跟两个字节 0x01f4,即十进制的 500。
主类型 1:
范围为 -264..-1(含两端)的负整数。该项的值 是 -1 减去实参。例如,整数 -500 会是 0b001_11001(主类型 1,附加信息 25) 后跟两个字节 0x01f3,即十进制的 499。
主类型 2:
字节串。字符串中的字节数等于 实参。例如,一个长度为 5 的字节 串将具有初始字节 0b010_00101 (主类型 2,附加信息 5 表示长度),后跟 5 个二进制内容字节。长度为 500 的字节串 将具有 3 个初始字节 0b010_11001(主类型 2,附加 信息 25 表示两字节长度),后跟两个 字节 0x01f4 表示长度 500,再后跟 500 个二进制 内容字节。
主类型 3:
文本字符串(第 2 节),编码为 UTF-8 [RFC3629]。字符串中的字节数等于 实参。包含无效 UTF-8 序列的字符串是 良构但无效的(第 1.2 节)。此类型用于 需要解释或显示人类可读文本的系统,并且 允许区分非结构化字节和具有指定字符集 (Unicode)及编码(UTF-8)的文本。与 JSON 等格式 不同,此类型中的 Unicode 字符从不 转义。因此,换行字符(U+000A)在 字符串中始终表示为字节 0x0a,而绝不表示为 字节 0x5c6e(字符“\”和“n”),也不表示为 0x5c7530303061(字符“\”、 “u”、“0”、“0”、“0”和“a”)。
主类型 4:
数据项数组。在其他格式中,数组也称为列表、序列或 元组(不过“CBOR sequence”是稍有不同的东西,参见 [RFC8742])。 实参是数组中数据项的数量。数组中的项 不需要全都属于同一类型。例如,一个包含 任意类型 10 个项的数组将具有初始字节 0b100_01010(主类型 4,附加信息 10 表示 长度),后跟其余 10 个项。
主类型 5:
成对数据项的映射。映射也称为表、 字典、散列或对象(在 JSON 中)。映射由 成对的数据项组成,每对由一个键和紧随其后的 一个值构成。实参是映射中数据项的数量。 例如,包含 9 对的映射将具有初始字节 0b101_01001(主类型 5,附加信息 9 表示 对数),后跟其余 18 个项。第一项 是第一个键,第二项是第一个值,第三项 是第二个键,依此类推。因为映射中的项成对出现, 它们的总数始终为偶数:包含奇数 个项的映射(最后一个键数据项之后没有值数据)不是良构的。 具有重复键的映射可能是 良构的,但它无效,因此会导致不确定 解码;另见 第 5.6 节
主类型 6:
带标签的数据项(“标签”),其标签号是范围 0..264-1(含两端)的整数,即实参, 其所包含的数据项(标签内容)是紧随 头部之后的单个已编码数据项。 参见 第 3.4 节
主类型 7:
浮点数和简单值,以及“break” 停止码。参见 第 3.3 节

这八个主类型形成一个简单表,显示数据项初始字节 256 个可能值中的哪些被使用 (表 7)。

在主类型 6 和 7 中,许多可能值保留供 未来规范使用。有关这些 值的更多信息,请参见 第 9 节

表 1 总结了 CBOR 定义的主 类型, 暂时忽略 第 3.2 节。此表中的数字 N 表示实参。

表 1CBOR 主类型的定长用法概览(N = 实参)
主类型 含义 内容
0 无符号整数 N -
1 负整数 -1-N -
2 字节串 N 字节
3 文本字符串 N 字节(UTF-8 文本)
4 数组 N 个数据项(元素)
5 映射 2N 个数据项(键/值对)
6 编号为 N 的标签 1 个数据项
7 简单/浮点 -

3.2. 某些主类型的 不定长度

四种 CBOR 项(数组、映射、字节串和文本字符串)可以 使用附加信息值 31 以不定长度进行编码。 如果某个项的编码需要在数组或映射内部项数, 或字符串总长度尚未知之前开始, 这会很有用。(在该数据项内部,能够在 全部内容已知之前开始发送数据 项的能力通常称为“流式传输”。)

不定长数组和映射的处理方式不同于 不定长字符串(字节串和文本字符串)。

3.2.1. “break”停止 码

“break”停止码用主类型 7 和附加 信息值 31(0b111_11111)编码。它本身不是数据项: 它只是用于关闭不定长项的语法特性。

如果“break”停止码出现在预期为数据项的位置, 而不是直接位于不定长字符串、数组或 映射内部——例如,直接位于定长数组或映射 内部——则包含它的项不是良构的。

3.2.2. 不定长数组和映射

不定长数组和映射使用其主 类型加附加信息值 31 来表示,随后是 任意长度的零个或多个项(对于数组)或键/值对(对于 映射),最后是“break”停止码(第 3.2.1 节)。换言之,不定长 数组和映射看起来与其他数组和映射相同,只是 以附加信息值 31 开始,并以 “break”停止码结束。

如果“break”停止码在映射中的键之后出现,以替代 该 键的值,则该映射不是良构的。

不禁止嵌套不定长 数组或映射项。“break”只终止单个项,因此 嵌套的不定长项需要的“break”停止码数量 正好与启动不定长项的类型字节数量相同。

例如,假设编码器要表示抽象数组 [1, [2, 3], [4, 5]]。定长编码将是 0x8301820203820405:

83        -- 长度为 3 的数组
   01     -- 1
   82     -- 长度为 2 的数组
      02  -- 2
      03  -- 3
   82     -- 长度为 2 的数组
      04  -- 4
      05  -- 5

可以按需要将不定长编码独立应用于 此数据项中编码的三个数组中的每一个,从而得到 如下表示:

0x9f018202039f0405ffff
9F        -- 开始不定长数组
   01     -- 1
   82     -- 长度为 2 的数组
      02  -- 2
      03  -- 3
   9F     -- 开始不定长数组
      04  -- 4
      05  -- 5
      FF  -- “break”(内部数组)
   FF     -- “break”(外部数组)
0x9f01820203820405ff
9F        -- 开始不定长数组
   01     -- 1
   82     -- 长度为 2 的数组
      02  -- 2
      03  -- 3
   82     -- 长度为 2 的数组
      04  -- 4
      05  -- 5
   FF     -- “break”
0x83018202039f0405ff
83        -- 长度为 3 的数组
   01     -- 1
   82     -- 长度为 2 的数组
      02  -- 2
      03  -- 3
   9F     -- 开始不定长数组
      04  -- 4
      05  -- 5
      FF  -- “break”
0x83019f0203ff820405
83        -- 长度为 3 的数组
   01     -- 1
   9F     -- 开始不定长数组
      02  -- 2
      03  -- 3
      FF  -- “break”
   82     -- 长度为 2 的数组
      04  -- 4
      05  -- 5

一个不定长映射的示例(恰好有两个 键/值对)可能是:

0xbf6346756ef563416d7421ff
BF           -- 开始不定长映射
   63        -- 第一个键,UTF-8 字符串长度 3
      46756e --   “Fun”
   F5        -- 第一个值,true
   63        -- 第二个键,UTF-8 字符串长度 3
      416d74 --   “Amt”
   21        -- 第二个值,-2
   FF        -- “break”

3.2.3. 不定长字节串和文本字符串

不定长字符串由一个字节表示,该字节包含 字节串或文本字符串的主类型,并带有附加 信息值 31,随后是一系列零个或多个具有 定长的指定类型字符串(“块”),并由 “break”停止码结束(第 3.2.1 节)。不定长字符串 所表示的数据项是这些块的串接。如果没有 块,则该数据项是指定类型的空 字符串。零长度块虽然不是特别有用, 但被允许。

如果不定长字符串指示符 (0b010_11111 或 0b011_11111)和“break”停止码之间的任何项 不是相同主类型的定长字符串项,则该字符串不是良构的。

该设计不允许将嵌套的 不定长字符串作为块放入不定长字符串。 如果允许这样做,就会要求解码器实现保持一个栈,或 至少保持一个嵌套层级计数。在 编码器一侧这是不必要的,因为内部不定长字符串会由 块组成,而这些块可以直接放入外部不定长 字符串。

如果不定长文本字符串内部的任何定长文本 字符串无效,则该不定长文本字符串无效。请注意, 这意味着单个 Unicode 码点 (标量值)的 UTF-8 字节不能分散在多个块之间: 文本字符串的新块只能在码点边界开始。

例如,假设一个已编码数据项由以下字节组成:

0b010_11111 0b010_00100 0xaabbccdd 0b010_00011 0xeeff99 0b111_11111
5F              -- 开始不定长字节串
   44           -- 长度为 4 的字节串
      aabbccdd  -- 字节内容
   43           -- 长度为 3 的字节串
      eeff99    -- 字节内容
   FF           -- “break”

解码后,这会得到一个具有七个 字节的单个字节串: 0xaabbccddeeff99。

3.2.4. 主类型不定长用法摘要

表 2 总结了 CBOR 定义的主类型在 不定长编码中(附加信息设为 31)如何使用。

表 2CBOR 主类型的不定长用法概览(附加信息 = 31)
主类型 含义 包含到“break”停止 码为止
0 (非良构) -
1 (非良构) -
2 字节串 定长字节串
3 文本字符串 定长文本字符串
4 数组 数据项(元素)
5 映射 数据项(键/值对)
6 (非良构) -
7 “break”停止码 -

3.3. 浮点数 和无内容的值

主类型 7 用于两类数据:浮点数和 不需要任何内容的“简单值”。初始字节中 5 位 附加信息的每个值都有其各自的 含义,如 表 3 所定义。与整数的主类型一样, 此主类型的项不携带内容数据;所有 信息都在初始字节(头部)中。

表 3主类型 7 中附加 信息的值
5 位值 语义
0..23 简单值(值 0..23)
24 简单值(值 32..255,位于 后续字节中)
25 IEEE 754 半精度浮点数(后跟 16 位)
26 IEEE 754 单精度浮点数(后跟 32 位)
27 IEEE 754 双精度浮点数(后跟 64 位)
28-30 保留,在当前 文档中非良构
31 不定长项的“break”停止码 (第 3.2.1 节

与所有其他主类型一样,5 位值 24 表示 单字节扩展:它后面跟随一个附加字节,用来 表示简单值。(为尽量减少混淆,只使用值 32 到 255。)这保持了初始字节的结构: 与其他主类型一样,其长度始终取决于 第一个字节中的附加信息。表 4 列出 已分配和可用于简单值的数值。

表 4简单值
语义
0..19 (未分配)
20 false
21 true
22 null
23 undefined
24..31 (保留)
32..255 (未分配)

编码器 MUST NOT 发出以 0xf8(主类型 7,附加信息 24)开头,并继续 以小于 0x20(十进制 32)的字节结尾的双字节序列。这样的序列不是 良构的。(这意味着编码器不能将 falsetruenullundefined 编码为双字节序列,并且这些值只有单字节 变体是良构的;更一般地说,每个 简单值只有一个表示变体。)

5 位值 25、26 和 27 分别用于 16 位、32 位和 64 位 IEEE 754 二进制浮点值 [IEEE754]。这些浮点值 被编码在适当大小的附加字节中。(有关 16 位浮点数的一些信息,参见 附录 D。)

3.4. 项的标记

在 CBOR 中,数据项可以被标签包围,以赋予其一些 附加语义,这些语义由标签号唯一标识。 标签是主类型 6,其实参(第 3 节)指示 标签 号,并且它包含一个单独的被包围数据项,即 标签内容。 (如果标签要求其内容具有进一步结构,则该结构由 被包围的数据项提供。) 我们使用术语标签来指由标签号和标签内容二者 组成的整个数据项:标签内容就是 被标记的数据项。

例如,假设长度为 12 的字节串被标记为 标签号 2,以指示它是无符号大数第 3.4.3 节)。 已编码数据项将以字节 0b110_00010(主类型 6,附加信息 2 表示标签号)开始,后跟 已编码的标签内容:0b010_01100(主类型 2,附加信息 12 表示长度),再后跟该大数的 12 个字节。

在扩展通用数据模型中,标签号的 定义描述了通过该标签号 传达的附加语义。 这些语义可以包括某些带标签数据 项与其他数据项的等价性,包括某些可在 基本通用数据模型中表示的数据项。例如,0xc24101, 一个其标签内容为单字节 0x01 的字节串的大数,等价于整数 1,而后者也可以编码 为 0x01、0x1801 或 0x190001。 标签定义可以指定一种推荐给 通用 编码器的首选序列化(第 4.1 节);这可能更偏好基本通用数据模型表示, 而不是使用标签的表示。

标签定义通常定义哪些嵌套数据项 对此类标签是有效的。标签定义可以将其内容限制为 非常具体的语法结构,如本文档中定义的标签那样; 或者也可以更语义化地定义其内容。后者的一个 示例是标签 40 和 1040 如何接受多种 表示数组的方式 [RFC8746]

作为惯例,许多标签不接受 nullundefined 值作为标签内容;相反,期望是可以用 nullundefined 值代替整个标签;第 3.4.2 节 提供了关于一个特定标签在应用协议中以及映射 到平台类型时处理这一惯例的进一步考虑。

解码器不需要理解每个标签号的标签,并且在一些应用中, 标签可能价值不大,在这些应用中,创建特定 CBOR 数据项的实现 和解码该流的实现 都知道数据流中每一项的语义含义。本规范中标签的主要 目的,是定义日期等通用数据类型。次要目的 是在预见到 CBOR 数据项需要被转换为 另一种格式时提供转换提示,这需要关于项内容的提示。 理解标签语义对解码器而言是 可选的;它可以简单地将标签号和 标签内容一并呈现给应用,而不解释标签的附加 语义。

标签将语义应用于它所包围的数据项。 标签可以嵌套:如果标签 A 包围标签 B,而标签 B 包围数据项 C, 则标签 A 应用于将标签 B 应用于数据项 C 后的结果。

IANA 维护一个标签号注册表,如 第 9.2 节 所述。 表 5 给出了 [RFC7049] 中定义、并在本节其余部分 给出定义的标签号列表。 (标签号 35 也在 [RFC7049] 中定义;关于该 标签号的讨论见 第 3.4.5.3 节。) 请注意,自 [RFC7049] 发布以来,还定义了许多其他标签号; 完整列表请参见 第 9.2 节 中描述的注册表。

表 5RFC 7049 中定义的标签号
标签 数据项 语义
0 文本字符串 标准日期/时间字符串;见 第 3.4.1 节
1 整数或浮点数 基于纪元的日期/时间;见 第 3.4.2 节
2 字节串 无符号大数;见 第 3.4.3 节
3 字节串 负大数;见 第 3.4.3 节
4 数组 十进制小数;见 第 3.4.4 节
5 数组 大浮点数;见 第 3.4.4 节
21 (任意) 预期转换为 base64url 编码;见 第 3.4.5.2 节
22 (任意) 预期转换为 base64 编码;见 第 3.4.5.2 节
23 (任意) 预期转换为 base16 编码;见 第 3.4.5.2 节
24 字节串 已编码 CBOR 数据项;见 第 3.4.5.1 节
32 文本字符串 URI;见 第 3.4.5.3 节
33 文本字符串 base64url;见 第 3.4.5.3 节
34 文本字符串 base64;见 第 3.4.5.3 节
36 文本字符串 MIME 消息;见 第 3.4.5.3 节
55799 (任意) 自描述 CBOR;见 第 3.4.6 节

从概念上说,标签是在通用数据模型中解释的,而不是在 (反)序列化时解释的。少数标签(目前为标签 号 25 和标签号 29 [IANA.cbor-tags])已注册为具有可能 需要在(反)序列化时处理的语义:解码器需要 知道且编码器需要控制数据项被编码进 CBOR 数据项的确切 顺序。这意味着这些标签不能在任意通用 CBOR 编码器/解码器之上实现(因为该编码器/解码器可能不会在数据模型层级反映映射中 条目的序列化顺序,反之亦然);因此它们的 实现通常需要集成到 通用编码器/解码器中。NOT RECOMMENDED 定义具有此 属性的新标签。

IANA 分配了标签号 65535、4294967295 和 18446744073709551615(16 位、32 位和 64 位中的二进制全 1)。 这些可以作为实现者的便利手段, 当他们希望用单个整数数据结构来指示 是否存在特定标签或不存在标签时使用。 该分配在 [CBOR-TAGS] 第 10 节 中描述。 这些标签并不打算出现在实际的 CBOR 数据项中; 实现 MAY 将此类出现标记为错误。

协议可以通过使用标签号 0 和 1 来用表示时间点的数据 项扩展通用数据模型(第 2 节),通过使用标签号 2 和 3 来表示 任意大小的整数,并通过使用标签 号 4 和 5 来表示任意大小和精度的 浮点值。

3.4.1. 标准 日期/时间字符串

标签号 0 包含一个文本字符串,其格式为 [RFC3339]date-time 产生式所描述的标准格式,并由 [RFC4287] 第 3.3 节 进行细化,表示其中描述的时间点。 另一种类型的 嵌套项,或不匹配 [RFC4287] 中描述格式的文本字符串,均无效。

3.4.2. 基于纪元的 日期/时间

标签号 1 包含一个数值,表示从 UTC 时间 1970-01-01T00:00Z 到所表示的民用 时间点之间的秒数。

标签内容 MUST 是无符号或 负整数(主类型 0 和 1)或浮点数(主类型 7,附加 信息 25、26 或 27)。其他包含类型无效。

非负值(主类型 0 和非负浮点 数)表示 1970-01-01T00:00Z UTC 或之后的时间值,并 按 POSIX [TIME_T] 进行解释。 (POSIX 时间也称为“UNIX Epoch time”。) 闰秒 由 POSIX 时间特殊处理,这会导致每十年出现数次 1 秒不连续。 请注意,需要表达超过 2106 年初时间的应用,不能省略对标签内容的 64 位整数支持。

负值(主类型 1 和负浮点数) 按应用需求确定的方式解释,因为在 1970-01-01T00:00Z 之前不存在通用的 UTC 秒计数时间标准 (对于早于国家历法不连续点的时间点尤其如此)。 同样的情况也适用于 非有限值。

为了表示小数秒,可以在标签号 1 中使用 浮点值而不是整数值。请注意,这通常 需要 binary64 支持,因为 binary16 和 binary32 只在 1970 年初附近的一小段时间内提供非零 小数秒。需要支持标签号 1 的应用可以将 标签内容限制为只允许整数(或只允许浮点值)。

请注意,日期/时间的平台类型可能包括 nullundefined 值,这在应用协议层级也可能是可取的。 虽然发出带有非有限标签内容值的标签号 1 值 (例如,用 NaN 表示未定义的日期/时间值,或用 Infinity 表示 未设置的到期日期)看似是处理此问题的明显方式, 但使用未带标签的 nullundefined 可避免使用非有限值, 并得到更短的编码。 鼓励应用协议设计者考虑这些情况 并包含清晰的处理指南。

3.4.3. 大数

使用标签号 2 和 3 的协议,用表示任意大小 整数的“大数”扩展通用数据模型 (第 2 节)。在基本通用数据模型中,大数值不等于 同一模型中的整数,但由此标签定义创建的扩展通用数据模型 基于数值定义等价性,并且首选序列化(第 4.1 节) 从不使用 也能表示为基本整数的大数(见下文)。

大数被编码为一个字节串数据项,该字节串按 网络字节序解释为无符号整数 n。包含 其他类型的项无效。对于标签号 2, 大数的值为 n。对于标签号 3,大数的值为 -1 - n。字节串的首选序列化是省略任何 前导零(请注意,这意味着 n = 0 的首选序列化 是空字节串,但见下文)。 理解这些标签的解码器 MUST 能够解码 确实具有前导零的大数。 可以使用主类型 0 或 1 表示的整数的首选序列化,是以这种方式编码, 而不是作为大数编码 (这意味着在使用首选序列化时,空字符串永远不会出现在大数中)。 请注意,这意味着选择使用大数 表示而不是基本整数来编码某个数字这种非首选选择, 并不打算具有应用语义(就像选择比需要更长的 基本整数表示,例如用 0x1800 表示 0x00, 也没有应用语义一样)。

例如,数字 18446744073709551616(264)表示 为 0b110_00010(主类型 6,标签号 2),随后是 0b010_01001(主 类型 2,长度 9),再后跟 0x010000000000000000(一个字节 0x01 和 八个字节 0x00)。十六进制为:

C2                        -- 标签 2
   49                     -- 长度为 9 的字节串
      010000000000000000  -- 字节内容

3.4.4. 十进制 小数和大浮点数

使用标签号 4 的协议,用表示形如 m*(10e) 的任意长度十进制小数的 数据项来扩展通用数据模型。 使用标签号 5 的协议,用表示形如 m*(2e) 的任意长度二进制小数的 数据项来扩展通用数据模型。 与 大数一样,不同类型的值在通用数据 模型中不相等。

十进制小数将一个整数尾数与一个 10 进制缩放 因子结合起来。如果应用需要精确表示 像 1.1 这样的十进制小数,它们最有用,因为许多十进制小数在二进制 浮点表示中没有精确 表示。

“大浮点数”将一个整数尾数与一个 2 进制缩放 因子结合起来。 它们是可以超出 CBOR 支持的三种 IEEE 754 格式 (第 3.3 节)的范围或精度的二进制浮点值。 需要某些基本二进制浮点能力、但不需要支持 IEEE 754 的受限 应用也可以使用大浮点数。

十进制小数或大浮点数表示为一个带标签数组, 其中 正好包含两个整数:指数 e 和尾数 m。 十进制小数(标签号 4)使用 10 进制指数;十进制小数数据项的值为 m*(10e)。大浮点数(标签号 5)使用 2 进制指数;大浮点数数据项的值为 m*(2e)。 指数 e MUST 以主 类型 0 或 1 的整数表示, 而尾数也可以是大数(第 3.4.3 节)。包含 其他结构的项无效。

十进制小数的一个示例,是将数字 273.15 表示为 0b110_00100(标签的主类型 6,附加 信息 4 表示标签号),随后是 0b100_00010(数组的主 类型 4,附加信息 2 表示数组长度), 随后是 0b001_00001(第一个 整数的主类型 1,附加信息 1 表示值 -2),随后是 0b000_11001(第二个整数的主类型 0, 附加信息 25 表示双字节值),随后是 0b0110101010110011(两字节中的 27315)。十六进制为:

C4             -- 标签 4
   82          -- 长度为 2 的数组
      21       -- -2
      19 6ab3  -- 27315

大浮点数的一个示例,是将数字 1.5 表示为 0b110_00101(标签的主类型 6,附加信息 5 表示标签号),随后是 0b100_00010(数组的主类型 4, 附加信息 2 表示数组长度), 随后是 0b001_00000(第一个整数的主类型 1, 附加信息 0 表示值 -1),随后是 0b000_00011(第二个整数的主类型 0, 附加信息 3 表示值 3)。十六进制为:

C5             -- 标签 5
   82          -- 长度为 2 的数组
      20       -- -1
      03       -- 3

十进制小数和大浮点数不提供 Infinity、 -Infinity 或 NaN 的表示;如果需要用这些值代替十进制小数 或大浮点数,可以使用 第 3.3 节 中的 IEEE 754 半精度表示。

3.4.5. 内容提示

本节中的标签用于可能被通用 CBOR 处理器使用的内容提示。这些内容提示不会扩展通用 数据模型。

3.4.5.1. 已编码 CBOR 数据项

有时携带一个嵌入的 CBOR 数据 项是有益的,该项 并不打算在解码包含它的数据 项时立即解码。标签号 24(CBOR 数据项)可用于将 嵌入的字节串标记为一个以 CBOR 格式编码的单个数据项。所包含的 非字节串项无效。所包含的字节串 如果编码了良构 CBOR 数据项,则有效;为判断标签有效性, 不要求检查解码后的 CBOR 项的有效性(但通用解码器可以 将其作为特殊选项提供)。

3.4.5.2. CBOR 到 JSON 转换器预期的后续编码

标签号 21 到 23 指示字节串在与基于文本的表示进行互操作时 可能需要特定 编码。当编码器知道其正在写入的字节串数据 很可能稍后会被转换为某个特定的基于 JSON 的 用法时,这些 标签很有用。该用法规定某些字符串编码为 base64、 base64url 等。编码器使用字节串,而不是自己执行 编码,以减少消息大小、减少编码器代码 大小,或两者兼顾。编码器不知道 转换器是否是通用的,因此希望说明它 认为将二进制字符串转换为 JSON 的正确方式。

被标记的数据项可以是字节串或任何其他 数据项。在 后一种情况下,该标签适用于该数据项中包含的所有字节串数据项, 但不包括包含在带有预期转换标签的嵌套 数据项中的那些字节串数据项。

这三个标签号建议转换为 [RFC4648] 中定义的三种 基础数据编码。 标签号 21 建议转换为 base64url 编码 ([RFC4648] 第 5 节), 其中不使用填充(参见 [RFC4648] 第 3.2 节); 也就是说,已编码字符串中所有尾随等号(“=”)都被移除。 标签号 22 建议转换为传统 base64 编码 ([RFC4648] 第 4 节), 并使用 RFC 4648 中定义的填充。 对于 base64url 和 base64,填充位均设为零(参见 [RFC4648] 第 3.5 节), 并且转换为替代编码 是对字节串内容执行的(也就是说,不添加任何 换行、空白或 其他附加字符)。标签号 23 建议转换为 使用大写字母表的 base16(hex)编码(参见 [RFC4648] 第 8 节)。 请注意,对于这三个标签号,空字节串的 编码都是空文本字符串。

3.4.5.3. 已编码文本

某些文本字符串保存着互联网上广泛使用的格式的数据, 有时解码器可以验证这些格式并以适当形式 呈现给 应用。针对其中一些格式提供了标签。

  • 标签号 32 用于 URI,如 [RFC3986] 中所定义。如果 文本字符串 不匹配 URI-reference 产生式,则该字符串 无效。
  • 标签号 33 和 34 分别用于 base64url 和 base64 编码文本 字符串, 如 [RFC4648] 中所定义。如果出现以下任一情况:

    • 已编码文本字符串 包含非字母表字符,或在最后一个 4 字符块中只有 1 个 字母表字符(其中字母表对于标签号 33 由 [RFC4648] 第 5 节 定义, 对于标签号 34 由 [RFC4648] 第 4 节 定义),或
    • 2 字符或 3 字符块中的 填充位不是 0,或
    • base64 编码的 填充字符数量错误,或
    • base64url 编码 具有填充字符,

    则该字符串无效。

  • 标签号 36 用于 MIME 消息 (包括所有头部),如 [RFC2045] 中所定义。不是有效 MIME 消息的文本字符串 无效。(对于此标签,有效性检查 对通用解码器可能特别繁重,因此 可能不会提供。请注意,许多 MIME 消息是通用 二进制数据,因此不能表示在文本字符串中; [IANA.cbor-tags] 列出了标签号 257 的注册,它 类似于标签号 36,但使用字节串作为其标签内容。)

请注意,标签号 33 和 34 与 21 和 22 的不同之处在于: 前者以基础编码形式传输数据, 而后者以原始字节串 形式传输数据。

[RFC7049] 还定义了一个标签号 35,用于 Perl Compatible Regular Expressions(PCRE/PCRE2)形式 [PCRE] 或 JavaScript 正则表达式语法 [ECMA262] 的正则表达式。 这些正则表达式规范的技术水平 此后已经发展并持续发展,因此本 规范不尝试更新引用。 相反,该标签仍然可供(如 [RFC7049] 中所注册) 那些以带外方式指定其使用的特定正则表达式变体的 应用使用(可能通过将用法限制到 PCRE 和 ECMA262 的 一个已定义公共子集)。 由于本规范澄清了超出 [RFC7049] 的标签有效性,我们注意到,由于 [RFC7049] 中该标签以开放方式定义, 任何所包含的字符串值都需要在 CBOR 标签层级有效(但随后在应用层级可能不是“预期的”)。

3.4.6. 自描述 CBOR

在许多应用中,从上下文可以清楚看出 CBOR 正被用于编码某个数据项。例如,某个特定 协议可能规定使用 CBOR,或者指示了一个 指定其用法的媒体类型。然而,也可能存在一些应用缺少此类 上下文信息,例如当 CBOR 数据存储在 一个没有区分性元数据的文件中。在这种情况下, 数据本身具有某些区分特征可能会有帮助。

标签号 55799 为此目的而定义,专门用于 按应用指定,在已存储的已编码 CBOR 数据项开头使用。 它不会赋予其所包围的数据项任何特殊 语义;也就是说,标签号 55799 所包围的 标签内容的语义与标签内容本身的语义完全相同。

该标签头部的序列化为 0xd9d9f7,它看起来 未被任何常用文件类型用作区分标记。 特别是,如果后面跟随一个有效的 CBOR 数据项, 0xd9d9f7 在任何 Unicode 编码中都不是 Unicode 文本的有效开头。

例如,解码器可能能够同时解码 CBOR 和 JSON。这样的解码器需要以机械方式区分这两种 格式。编码器帮助解码器的一种简单方式是 用标签号 55799 标记整个 CBOR 项,该标签的序列化 永远不会出现在 JSON 文本的开头。

4. 序列化考虑事项

4.1. 首选 序列化

对于数据模型层级的一些值,CBOR 提供了多种 序列化。 对许多应用来说,期望编码器始终选择 首选序列化(首选编码);然而,本规范并不 将强制执行这种偏好的负担加在编码器或解码器身上。

一些受限解码器解码 非首选序列化的能力可能有限:例如,如果某个应用中只预期 小于 1_000_000_000(十亿)的整数, 解码器可以省略解码整数中 64 位 实参所需的代码。始终使用首选 序列化的编码器(“首选编码器”)可以与此解码器针对该应用中可能出现的 数字进行互操作。一般 来说,首选编码器比那种例如始终使用 64 位 整数的编码器更具有普遍互操作性(并且 也更少浪费)。

类似地,受限编码器所支持的 表示变体种类可能有限,以至于它不会 发出首选序列化(“变体编码器”)。例如,受限编码器可以 被设计为 对其编码的整数始终使用 32 位变体,即使 有更短的表示可用(假定应用并不需要只能 用 64 位变体表示的整数)。 因此,不依赖于 只接收首选序列化的解码器(“容忍变体的解码器”)可以说 具有更 普遍的互操作性(尽管它很可能仍然针对 接收首选序列化的情况进行优化)。 CBOR 解码器的完整实现按定义 容忍变体;只有在受限 CBOR 解码器实现遇到变体编码器时,这一区分才相关。

首选序列化始终使用表示实参的最短形式 (第 3 节);它还使用能够保留被编码值的最短 浮点编码。

浮点值的首选序列化,是能够保留其值的最短 浮点编码,例如数字 5.5 为 0xf94580, 数字 5555.5 为 0xfa45ad9c00。对于 NaN 值,如果将较短有效数向右用零填充能够重构原始 NaN 值, 则首选较短编码 (对于许多应用,单一 NaN 编码 0xf97e00 即已足够)。

只要在项的序列化开始时长度已知, 就首选定长编码。

4.2. 确定性编码的 CBOR

一些协议可能希望编码器只发出某种特定 确定性格式的 CBOR;这些协议也可能让解码器检查 其输入是否处于该确定性格式中。这些协议 可以自由定义它们所说的“确定性格式”是什么, 以及期望编码器和解码器 做什么。本节定义了一组可 作为这种确定性格式基础的限制。

4.2.1. 核心 确定性编码要求

如果 CBOR 编码满足以下限制, 则它满足“核心确定性编码 要求”:

  • 必须使用首选序列化。特别是,这意味着 整数、主类型 2 到 5 中的长度以及标签的实参(见 第 3 节MUST 尽可能短, 例如:

    • 0 到 23 和 -1 到 -24 MUST 与 主类型表示在同一字节中;
    • 24 到 255 和 -25 到 -256 MUST 仅用一个 附加 uint8_t 表示;
    • 256 到 65535 和 -257 到 -65536 MUST 仅用一个 附加 uint16_t 表示;
    • 65536 到 4294967295 和 -65537 到 -4294967296 MUST 仅用一个附加 uint32_t 表示。

    浮点值也 MUST 使用能够保留 其值的最短形式,例如,1.5 编码为 0xf93e00(binary16),1000000.5 编码为 0xfa49742408(binary32)。 (一种实现方式是让所有浮点数先作为 64 位 浮点数开始,然后测试转换为 32 位浮点数;如果结果是 相同数值,则使用较短形式,并以测试转换为 16 位浮点数 重复该过程。这同样适用于为正 Infinity 和负 Infinity 选择 16 位浮点数。)

  • 不定长项 MUST NOT 出现。它们可以改为编码为 定长项。
  • 每个映射中的键 MUST 按其确定性 编码的逐字节字典序排序。例如,以下键 排序正确:

    1. 10,编码为 0x0a。
    2. 100,编码为 0x1864。
    3. -1,编码为 0x20。
    4. "z",编码为 0x617a。
    5. "aa",编码为 0x626161。
    6. [100],编码为 0x811864。
    7. [-1],编码为 0x8120。
    8. false,编码为 0xf4。

4.2.2. 其他 确定性编码考虑事项

CBOR 标签为确定性 编码带来额外考虑。如果基于 CBOR 的协议要为 某个特定标签的存在和不存在提供相同语义 (例如,允许日期/时间位置中既出现标签 1 数据项,也出现原始数字, 并将后者视为带有标签),那么基于“最短形式”原则, 确定性格式将不允许该标签存在。 例如,协议可能让编码器选择将 URL 表示为 文本字符串,或者使用 第 3.4.5.3 节 中包含文本字符串的标签号 32。 该协议的确定性编码需要要么 要求该标签存在,要么要求其不存在,而不能 允许两者任选其一。

在确实要求某些位置存在标签以 获得特定语义的协议中,该标签也需要出现在 确定性格式中。确定性编码考虑事项 也适用于标签内容。

如果协议包含一个字段,可以使用标签号 2 或 3 表达绝对值为 264 或更大的整数 (第 3.4.3 节),则该协议的确定性 编码需要指定 较小整数是否也使用这些标签表示,还是使用 主类型 0 和 1。首选序列化使用后一种选择, 因此建议采用后一种。

包含浮点值的协议,无论这些浮点值是用 基本浮点值(第 3.3 节)表示,还是 使用标签(或 二者兼有)表示,都可能需要为其确定性 编码定义额外要求,例如:

  • 虽然 IEEE 浮点值可以 将正零和负零表示为 不同值,但应用可能不区分二者,并可能 决定用正号表示所有零值,从而禁止 负零。 (应用也可能希望以某种方式限制 浮点值的精度,使其永远不需要表示 64 位——甚至 32 位——浮点值。)
  • 如果协议包含一个能够表达 浮点值的字段, 且其特定数据模型声明整数值和 浮点值可互换,则该协议的 确定性编码需要指定, 例如,整数 1.0 是编码为 0x01(无符号 整数)、0xf93c00(binary16)、0xfa3f800000(binary32), 还是 0xfb3ff0000000000000(binary64)。示例规则如下:

    1. 将适合 64 位的整数值编码为 主类型 0 和 1 的值,并将其他值编码为能够准确表示该 值的首选(16 位、32 位或 64 位中最小的) 浮点表示,
    2. 将所有值都编码为能够准确表示该 值的首选 浮点表示,即使是整数值也是如此,或
    3. 将所有值都编码为 64 位浮点 表示。

    规则 1 跨越整数和浮点 值之间的边界,而规则 3 不使用首选序列化,因此规则 2 在许多情况下可能是 一个不错的选择。

  • 如果 NaN 是允许值,并且没有 支持 NaN payload 或 signaling NaN 的意图,则协议需要选择单一 表示,通常为 0xf97e00。如果这种简单选择 不可行,则需要特别关注 NaN 处理。
  • 次正规数(在给定 IEEE 754 数字格式中 具有最低可能指数的非零数)在某些浮点实现中可能被刷新为零输出, 或作为零输入处理。 协议的确定性编码可能希望专门 适应此类实现,同时通过从交换中排除次正规数、 改为交换零而使其他 实现承担相应负担。
  • 同一个数字可以由 不同的十进制小数、 不同的大浮点数,以及在可能被定义为表达数值的其他标签下的不同形式 来表示。根据 实现,确定这些形式中的任一种(或基本通用数据模型中的形式)是否 等价,并不总是实际可行。呈现这种 数字表示格式选择的应用协议,需要明确说明 如何选择用于确定性编码的格式。

4.2.3. 长度优先的 映射键排序

核心确定性编码要求(第 4.2.1 节)以不同于 [RFC7049] 第 3.9 节(其中称为 “Canonical CBOR”)所建议顺序的方式对映射键排序。需要 与 [RFC7049] 中指定顺序兼容的协议,可以改为用 本规范的“长度优先核心确定性编码 要求”来规定:

如果 CBOR 编码满足核心确定性编码要求, 但每个映射中的键 MUST 按如下方式排序, 则它满足“长度优先核心确定性编码 要求”:

  1. 如果两个键长度不同,较短者排在 前面;
  2. 如果两个键长度相同,则(逐字节)词典序中 值较低者 排在前面。

例如,在长度优先核心确定性编码 要求下,以下键排序正确:

  1. 10,编码为 0x0a。
  2. -1,编码为 0x20。
  3. false,编码为 0xf4。
  4. 100,编码为 0x1864。
  5. "z",编码为 0x617a。
  6. [-1],编码为 0x8120。
  7. "aa",编码为 0x626161。
  8. [100],编码为 0x811864。

5. 创建基于 CBOR 的协议

CBOR 这样的数据格式经常用于没有 格式协商的环境。CBOR 的一个特定设计目标是 不需要任何包含的或假定的模式:解码器可以取得一个 CBOR 项, 并在没有其他知识的情况下解码它。

当然,在现实世界实现中,编码器和解码器 会对 CBOR 数据项中应当有什么拥有共同理解。例如, 约定的格式可能是“该项是一个数组,其 第一个值是 UTF-8 字符串,第二个值是整数,后续 值是零个或多个浮点数”,或“该 项是一个映射,使用字节串作为键,并包含一个 键为 0xab01 的对”。

基于 CBOR 的协议 MUST 指定其解码器如何处理 无效数据和其他意外数据。基于 CBOR 的协议 MAY 指定它们将任意有效数据视为意外数据。 基于 CBOR 的协议的编码器 MUST 只生成有效项, 也就是说,协议不能被设计为利用无效项。编码器 可以具备编码其所用协议要求的尽可能多或尽可能少类型值的能力; 解码器可以具备理解其所用协议要求的尽可能多或尽可能少类型值的 能力。这种缺乏 限制使 CBOR 能用于极其受限的 环境。

本节其余部分讨论创建基于 CBOR 的 协议时的一些考虑事项。除少数例外外,这些内容仅具建议性,并明确排除 BCP 14 [RFC2119] [RFC8174] 中除可按 BCP 14 含义解释为“MAY”的词语之外的任何用语。 这些例外旨在促进 基于 CBOR 的协议的互操作性,同时利用多种 通用和应用特定的编码器与解码器。

5.1. 流式 应用中的 CBOR

在流式应用中,数据流可以由 一系列首尾相接串接的 CBOR 数据项组成。在这样的 环境中,如果在前一个数据项结束之后发现数据, 解码器会立即开始解码新的数据项。

构成数据项的字节并不一定全部立即 可供解码器使用;一些解码器会缓冲额外数据, 直到可以向应用呈现完整数据项。其他 解码器可以向应用呈现顶层数据项的部分信息, 例如已经能够被解码的嵌套数据项, 甚至是尚未完全到达的字节串的部分内容。 这样的应用也 MUST 具有匹配的流式安全 机制,其中 所需保护可用于呈现给 应用的增量数据。

请注意,一些应用和协议不会想要使用 不定长编码。使用不定长编码允许 编码器无需整理全部数据以便计数,但它 要求解码器在等待项结束时分配越来越多的内存。 这对某些应用可能没问题, 但对其他应用则不然。

5.2. 通用编码器和 解码器

通用 CBOR 解码器可以解码所有良构的已编码 CBOR 数据项, 并将这些数据项呈现给应用。参见 附录 C。 (诊断记法,第 8 节,可用于 向人呈现良构的 CBOR 值。)

通用 CBOR 编码器提供一个应用接口,允许 应用指定任何良构值以编码为 CBOR 数据项,包括编码器未知的简单值和标签。

虽然 CBOR 试图最小化这些情况,但并非所有良构的 CBOR 数据都是有效的:例如,已编码文本字符串 0x62c0ae 不包含有效 UTF-8(因为 [RFC3629] 要求始终使用最短 形式),因此不是有效的 CBOR 项。 此外,特定标签可能 设置可能被违反的语义约束,例如,大数标签 包围另一个标签,或标签号 0 的实例包含字节 串,或包含内容不匹配 [RFC3339]date-time 产生式的文本字符串。 不要求通用编码器和解码器为其应用接口作出不自然的 选择,以便处理 无效数据。通用编码器和解码器应转发 简单值和标签,即使其特定码点在编写编码器/解码器时 尚未注册 (第 5.4 节)。

5.3. 项的有效性

良构但无效的 CBOR 数据项(第 1.2 节)会在 解释其中编码的数据在 CBOR 数据模型中的含义时造成问题。基于 CBOR 的协议可以分若干层指定,其中 较低层不处理其转发的某些 CBOR 数据的语义。 这些层无法注意到它们不处理的数据中的任何有效性错误, 并且 MUST 按原样转发这些数据。第一个 处理无效 CBOR 项语义的层 MUST 选择以下两种 方案之一:

  1. 用错误标记替换有问题的项,并继续处理 下一项,或
  2. 发出错误并完全停止处理。

基于 CBOR 的协议 MUST 指定其解码器 对可能遇到的每一种无效项采用这些选项中的哪一种。

此类问题可能发生在 CBOR 的基本有效性层级, 或发生在标签的上下文中(标签有效性)。

5.3.1. 基本有效性

在基本通用数据模型中,可能发生两类有效性 错误:

映射中的重复键:
通用解码器(第 5.2 节)使用原生 CBOR 数据模型 向应用提供数据。该数据模型包括映射 (具有唯一键的键-值映射),而不是多重映射(多个条目可以具有相同键的键-值 映射)。因此,获得具有重复键的 CBOR 映射项的 通用解码器会解码成仅含该键一个实例的映射,或者它可能 完全停止处理。另一方面,“流式 解码器”甚至可能无法注意到。关于映射中的键,更多 讨论见 第 5.6 节
无效的 UTF-8 字符串:
解码器可能希望,也可能不希望验证 UTF-8 字符串(主类型 3)中的 字节序列是否确实为有效 UTF-8,并作出适当 反应。

5.3.2. 标签有效性

通过向基本通用数据模型添加 标签,会引入另外两类有效性错误:

标签内容的不允许类型:
标签号(第 3.4 节)指定其标签内容应该 使用哪种类型的数据项;例如,无符号或负 大数的标签号应当放在字节串上。将 带标签数据项解码为原生表示(在此例中为原生大整数)的解码器, 预期会检查被标记数据项的类型。 即使环境中没有此类原生 表示可用的解码器,也可以对其已知的标签执行检查 并作出适当反应。
标签内容的不允许值:
数据项的类型可能允许作为某标签的内容,但其 具体值可能不允许;例如,值“yesterday”对于标签 0 的内容 不可接受,即使它确实是一个 文本字符串。通常将此类标签吸收到 等价平台类型中的解码器,可能会以类似于呈现未知标签 号标签的方式,将此标签呈现给应用 (第 5.4 节)。

5.4. 有效性与演进

带有效性检查的解码器会付出努力来可靠地 检测具有有效性错误的数据项。例如,这样的 解码器需要有一个 API,用于对包含前一小节所列任一有效性 错误的 CBOR 数据项报告错误(且不 返回数据)。

“Concise Binary Object Representation (CBOR) Tags”注册表(第 9.2 节)中定义的标签集合, 以及“Concise Binary Object Representation (CBOR) Simple Values” 注册表(第 9.1 节)中定义的简单值集合, 可以在任何时候增长,超出通用解码器所理解的集合。当 有效性检查解码器遇到 其不识别的此类情况时,可以采取以下两种做法之一:

  • 它可以报告错误(并且不返回数据)。 请注意,将此情况视为错误可能导致僵化,因此 不鼓励这样做。此错误本身 并不是有效性错误。这类错误更可能 由在已知情况下会执行有效性检查的解码器 提出。
  • 它可以将未知项(类型、值,以及对于 标签而言, 已解码的带标签数据项)发出给调用解码器的应用, 然后向应用 指示解码器不识别该标签 号或简单值。

后一种做法同样适用于不 支持有效性检查的解码器,它提供了对 新注册标签和简单值的前向兼容性,而不要求 编码器与调用应用同时更新。(为此, 解码器的 API 需要具备标记未知 项的能力,使调用应用能够以适合程序的方式 处理它们。)

由于有效性检查所需的某些处理可能具有 可观成本(尤其是映射的重复检测), 支持有效性检查并不是对所有 CBOR 解码器提出的要求。

一些编码器会依赖其应用以 使编码器产生有效 CBOR 的方式提供输入数据。通用 编码器也可能希望提供一种有效性检查模式,在其中它 可靠地将其输出限制为有效 CBOR,而不取决于 其应用是否确实提供符合 API 的数据。

5.5.

基于 CBOR 的协议应考虑到不同语言 环境对可表示数字的范围和精度 有不同限制。例如,基本 JavaScript 数字 系统将所有数字视为浮点值,这可能导致 解码超过 53 个有效位的整数时静默 丢失精度。 另一个例子是,由于 CBOR 将其整数 表示中的符号位保留在主类型中,因此,对于某一长度的有符号 数字,它比同长度的典型平台有符号整数表示 多一位(例如,1+8 字节 整数为 -264..264-1,而 8 字节 int64_t 为 -263..263-1)。 使用数字的协议应定义其 对解码器和接收应用处理中非平凡数字的 期望。

包含浮点数的基于 CBOR 的协议可以 限制三种格式(半精度、单精度 和双精度)中的哪些需要被支持。对于仅整数 应用,协议可能希望完全排除 浮点值的使用。

为紧凑性设计的基于 CBOR 的协议,可能希望排除 对应用来说长于必要长度的特定整数编码, 例如为了省去实现 64 位整数的需要。 预期编码器会使用能够表示给定值的最紧凑 整数表示。然而,不要求确定性编码的 紧凑应用应接受使用长于必要长度 编码的值(例如,将“0”编码为 0b000_11001 后跟两个字节 0x00),只要该应用能够解码给定 大小的整数。 类似考虑也适用于浮点值;建议同时解码 首选序列化和长于必要长度的序列化。

面向受限应用的基于 CBOR 的协议,如果在将特定数字表示为整数 与表示为十进制小数或大浮点数之间提供 选择(例如,当指数较小且非负时),可能会表达一种实现质量 期望,即直接使用整数表示。

5.6. 为映射指定 键

编码和解码应用需要就映射中将使用哪些类型的 键达成一致。在需要与基于 JSON 的应用 互操作的应用中,通过将键限制为仅文本字符串,可以简化转换; 否则,就必须指定从其他 CBOR 类型到文本字符串的 映射,而这 经常导致实现错误。在键本质上是 数值,并且键的数值排序对应用很重要的应用中, 直接使用数字作为键是有用的。

如果要使用多种类型的键,应考虑 这些类型如何在将要使用的特定 编程环境中表示。例如,在 JavaScript Maps [ECMA262] 中,整数键 1 不能与浮点键 1.0 区分。这意味着,如果使用整数 键,则协议需要避免在同一映射中使用 值恰好为整数的浮点键。

在解码嵌套于 CBOR 数据项中的数据项时立即交付这些项的解码器 (“流式解码器”)通常不保留 用于确定映射中键唯一性所必需的状态。 类似地,能够在包含的数据项尚未完全可用之前 开始编码数据项的编码器(“流式编码器”)可能 希望通过依赖其数据 源维护唯一性来显著降低其开销。

基于 CBOR 的协议 MUST 定义接收应用在 映射中看到多个相同键时 要做什么。协议中的结果规则 MUST 尊重 CBOR 数据模型:它不能规定对具有 相同键的条目进行特定处理,除非它可以规定 映射中具有相同键表示该映射格式错误,且解码器 必须以错误停止。 在处理表现出重复键条目的映射时,通用 解码器可能会执行以下操作之一:

  • 不接受具有重复键的映射(即,对映射强制 有效性, 另见 第 5.4 节)。这些通用解码器 具有普遍用途。应用可能仍需要根据应用规则执行自己的 重复检查(例如,如果应用在特定映射的键位置将 整数和浮点值视为相等)。
  • 将所有映射条目传递给应用,包括具有 重复键的条目。这要求应用处理(检查) 重复键,即使应用规则与通用数据模型规则相同 也是如此。
  • 丢失一些具有重复键的条目,例如,只交付 具有相同键的条目中的最后 (或第一个)条目。使用 这样的通用解码器时,应用在不同运行中、以及使用不同通用解码器时,可能会针对某个 特定键得到不同结果,返回哪个值取决于通用解码器 实现和映射中键的实际顺序。特别是, 应用无法自行验证键唯一性, 因为它们不一定能看到所有条目;如果它们需要验证键 唯一性,就可能无法使用这样的通用解码器。 这些通用解码器只能用于数据源和传输始终 提供有效映射的情况;如果数据源和传输可能受到攻击, 这就不可能。

通用解码器需要记录它们实现了这三种方法中的哪一种。

CBOR 的映射数据模型不允许给映射表示中 键/值对的顺序赋予语义。因此, 基于 CBOR 的协议 MUST NOT 指定改变映射中键/值对的 顺序会改变语义,除非是指定某些 顺序被禁止,例如它们不满足确定性 编码(第 4.2 节) 的要求。 (映射排序的任何次要影响,例如对时序、缓存使用 和其他潜在侧信道的影响,并不被视为 语义的一部分,但其本身可能足以成为协议要求 确定性编码格式的理由。)

受限设备上的应用应考虑使用小 整数作为键,如果它们的映射具有少量经常 使用的键;例如,一组 24 个或更少的 键可以作为无符号整数编码在单个字节中,如果还使用负 整数,则最多可达 48 个。不太频繁 出现的键随后可以使用更长编码的整数。

5.6.1. 键的等价性

适用于 CBOR 数据项的特定数据模型用于 确定映射中出现的键是重复还是不同。

在通用数据模型层级,数值等价的整数和 浮点值彼此不同,也不同于 各种大数(标签 2 到 5)。类似地,文本字符串 与字节串不同,即使由相同字节组成。带标签值 不同于无标签值,也不同于带有不同标签号的值。

在每个这些组内,数值只有在数值相等时才不相同 (具体而言,-0.0 等于 0.0);对于 映射键等价性的目的,NaN 值在将两个有效数向右零扩展到 64 位后 具有相同有效数时等价。

字节串和文本字符串都逐字节比较, 数组逐元素比较;如果它们具有相同数量的字节/元素,并且相同 位置上的值相同,则它们相等。两个映射如果具有相同的对集合, 不论顺序如何,都相等;如果键和值均相等,则对相等。

带标签值在标签号和标签内容 都相等时相等。 (请注意,提供特定标签处理的通用解码器可能无法区分 某些语义等价 值,例如,如果标签 2 或标签 3 的内容中出现前导零 (第 3.4.3 节)。) 简单值在仅具有相同值时相等。 在通用数据模型中没有其他东西相等;简单值 2 不等价于整数 2,数组也永远不等价于映射。

第 2.2 节 所讨论,特定数据模型可以 出于比较映射键的目的,使在通用数据模型中不同的值 等价。请注意,这意味着 通用解码器可能会向应用交付一个已解码映射,而该应用需要 检查其中是否存在重复映射键 (或者,解码器可以提供一个编程接口, 为应用执行此服务)。特定数据模型 无法区分在通用数据模型层级上就此目的而言相等的映射键值。

5.7. 未定义值

在一些基于 CBOR 的协议中, undefined 的简单值(第 3.3 节)可能被编码器用作存在编码问题的数据项的替代物, 以允许其余包含数据项 在无害的情况下继续编码。

6. 在 CBOR 和 JSON 之间转换数据

本节给出关于在 CBOR 和 JSON 之间转换的非规范性建议。转换器实现 MAY 使用这里的 任何建议。

值得注意的是,JSON 文本是字符序列,而不是 已编码的字节序列;而 CBOR 数据项由 字节组成,而不是字符。

6.1. 从 CBOR 转换为 JSON

CBOR 中的大多数类型在 JSON 中都有直接对应物。然而,有些 没有,而实现 CBOR 到 JSON 转换器的人必须 考虑在这些情况下该怎么做。以下非规范性建议 通过将它们转换为单一替代值来处理这些情况,例如 JSON null。

  • 整数(主类型 0 或 1)变为 JSON 数字。
  • 未嵌入于 指定建议编码的标签中的字节串(主类型 2),会以无 填充的 base64url 编码,并变为 JSON 字符串。
  • UTF-8 字符串(主类型 3)变为 JSON 字符串。 请注意,JSON 要求转义某些字符([RFC8259], 第 7 节): 引号(U+0022)、反斜杠(U+005C)和“C0 控制字符”(U+0000 到 U+001F)。所有其他字符 都不加改变地复制到 JSON UTF-8 字符串中。
  • 数组(主类型 4)变为 JSON 数组。
  • 映射(主类型 5)变为 JSON 对象。这只有在 所有键都是 UTF-8 字符串时才可 直接实现。转换器也可以将其他键转换为 UTF-8 字符串 (例如,将整数转换为包含其十进制表示的 字符串); 然而,这样做会引入键冲突的危险。 另请注意,如果像下面建议的那样忽略 UTF-8 字符串上的标签, 那么如果标签不同但字符串相同, 这将导致键 冲突。
  • False(主类型 7,附加信息 20)变为 JSON false。
  • True(主类型 7,附加信息 21)变为 JSON true。
  • Null(主类型 7,附加信息 22)变为 JSON null。
  • 浮点值(主类型 7,附加 信息 25 到 27)如果是有限值(也就是说,可以 表示为 JSON 数字),则变为 JSON 数字;如果该值是非有限的(NaN, 或正 Infinity 或负 Infinity),则用 替代值表示。
  • 任何其他简单值(主类型 7,任何尚未 讨论的附加信息 值)均由替代值表示。
  • 大数(主类型 6,标签号 2 或 3)通过将 其字节串以无填充 base64url 编码来表示,并变为 JSON 字符串。对于标签号 3(负大数),会在基础编码值前插入 “~”(ASCII 波浪号)。(转换为二进制 blob 而非数字,是为了防止 JSON 解码器可能出现的数值溢出。)
  • 带有编码提示的字节串(主类型 6,标签 号 21 到 23)按该提示所描述的方式编码,并变为 JSON 字符串。
  • 对于所有其他标签(主类型 6,任何其他标签 号),标签 内容表示为 JSON 值;标签号会被忽略。
  • 不定长项在 转换之前会变为定长。

CBOR 到 JSON 转换器可能希望遵循 JSON 配置文件 I-JSON [RFC7493],以最大化互操作性并 增强信心, 使 JSON 输出能够以可预测结果处理。例如, 这会影响可被可靠表示的整数范围, 以及旧版 JSON 实现可能支持的顶层项。

6.2. 从 JSON 转换为 CBOR

所有 JSON 值一旦被解码,就会直接映射到一个或多个 CBOR 值。与任何类型的 CBOR 生成一样,必须 就数字表示作出决定。在一种建议的 转换中:

  • 没有小数部分的 JSON 数字(整数) 表示为整数(主类型 0 和 1,可能是主类型 6, 标签号 2 和 3),选择最短形式;长于 实现定义阈值的整数可以改为表示为 浮点值。默认表示为整数的 范围是 -253+1..253-1(充分利用通常用于解码 JSON 的 binary64 表示中 精确整数的范围 [RFC7493])。 基于 CBOR 的协议或通用转换器实现 可以选择 -232..232-1 或 -264..264-1(分别 充分使用 CBOR 中通过 uint32_t 或 uint64_t 可用的整数范围),甚至选择 -231..231-1 或 -263..263-1(使用二进制补码 有符号整数的常见范围)。 (如果 JSON 是由 JavaScript 实现生成的,则其 精度已经被限制为最多 53 位。)
  • 带有小数部分的数字表示为 浮点 值,并基于 IEEE 754 binary64 提供的 精度执行十进制到二进制的转换。 JSON 数字的数学值使用 [IEEE754] 第 4.3.1 节中的 roundTiesToEven 过程转换为 binary64。 然后,在以 CBOR 编码时,首选序列化使用能够精确表示该转换结果的最短浮点 表示;例如,1.5 表示为 16 位浮点值(不过 并非所有实现都能高效找到 最小形式)。除了使用默认 binary64 精度之外,也可能存在实现定义的转换 精度限制,这将影响所表示值的精度。 只有当协议中指定时,才应在 CBOR 侧使用十进制表示。

CBOR 的设计目标通常是提供比 JSON 更紧凑的编码。 一种可能想到的实现策略是 在单个缓冲区中就地执行 JSON 到 CBOR 编码。该 策略需要仔细考虑许多病态 情况,例如某些字符串在没有转义或转义很少的情况下表示, 且长度超过(或远远超过)255 字节,在 CBOR 中编码为 UTF-8 字符串时可能会扩展。类似地,少数二进制 浮点表示可能会从 JSON 中某些较短的 十进制表示(1.1、1e9)扩展。这可能很难 正确处理,随之产生的任何漏洞都可能被 攻击者利用。

7. CBOR 的未来演进

成功的协议会随时间演进。新想法出现, 实现平台改进,相关协议被开发并 演进,来自应用和协议的新需求也被 添加。因此,促进协议演进是任何协议开发中的重要 设计考虑事项。

对于将使用 CBOR 的协议,CBOR 提供了一些有用机制 来促进其演进。这方面的最佳实践众所 周知,尤其来自基于 JSON 协议的 JSON 格式开发。因此,此类最佳实践不属于 本规范的范围。

然而,促进 CBOR 本身的演进完全属于 其范围。CBOR 被设计为既为 基于 CBOR 的协议开发提供稳定基础,又能够演进。由于 成功的协议可能存在数十年,CBOR 需要被设计为可供 数十年使用和演进。本节为 CBOR 的演进提供一些指导。与 本文档的其他部分相比,它必然更加主观。它也必然是不完整的, 以免变成协议开发教科书。

7.1. 扩展点

在协议设计中,演进机会通常以 扩展点的形式包含在内。例如,可能有一个 从一开始并未完全分配的码点空间,并且 协议被设计为能够容忍并接纳开始使用比最初分配更多码点的 实现。

确定码点空间的大小可能很困难,因为所需范围 可能难以预测。协议设计应尝试使 码点空间足够大,以便它能够在协议预期寿命内 逐渐被填充。

CBOR 有三个主要扩展点:

“simple”空间(主类型 7 中的值):
在 24 个高效 (以及 224 个略低效)的值中,只有少数已经 分配。接收未知简单数据 项的实现可能很容易将其按简单项处理,前提是该值的结构 确实简单。 第 9.1 节 中的 IANA 注册表是处理此 码点空间可扩展性的适当方式。
“tag”空间(主类型 6 中的值):
总码点空间 十分充裕;只有极小一部分已经 分配。然而,并非所有这些码点都同样 高效:前 24 个只消耗一个(“1+0”)字节,并且 其中一半已经分配。接下来的 232 个值只 消耗两个(“1+1”)字节,其中近四分之一已经分配。 这些子空间需要一些整理,才能再持续数十年。 接收未知标签号的实现可以选择 只处理所包含的标签内容,或者最好 将该标签作为包裹 标签内容的未知标签号处理。第 9.2 节 中的 IANA 注册表是 处理此码点空间可扩展性的 适当方式。
“additional information”空间:
接收到未知 附加信息值的实现无法继续解码, 因此在此空间中分配码点是远超 使用扩展点的重大步骤。而且 剩余的码点也非常少。另见 第 7.2 节

7.2. 整理 附加信息空间

人类思维有时会倾向于填补感知到的小空隙, 以使事物整齐。我们预期附加信息值的码点 空间中剩余的空隙会成为新 想法的吸引物,仅仅因为它们在那里。

本规范并不通过 IANA 注册表管理附加信息 码点空间。相反,从该 空间进行分配只能通过更新本规范来完成。

对于 n >= 24 的附加信息值, 附加数据的大小通常为 2n-24 字节。因此,附加 信息值 28 和 29 应被视为 128 位和 256 位数量的候选项,以备需要将它们添加到 协议中。于是,附加信息值 30 是唯一 可供一般分配的附加信息值,并且在通过更新 本规范来分配它之前,应当有非常充分的理由。

8. 诊断记法

CBOR 是一种二进制交换格式。为了便于文档编写和 调试,特别是为了便于协作调试的 实体之间通信,本节定义一种简单的 人类可读诊断记法。所有实际交换始终 发生在二进制格式中。

请注意,这确实是一种诊断格式;并不打算 被解析。因此,本文档中没有给出 形式化定义(如 ABNF)。(寻找基于文本格式以便在配置文件中 表示 CBOR 数据项的实现者,也可能希望 考虑 YAML [YAML]。)

诊断记法松散地基于 RFC 8259 中定义的 JSON,并在需要时加以扩展。

该记法借用了 JSON 的数字(整数和 浮点)、True(>true<)、False(>false<)、Null(>null<)、UTF-8 字符串、数组和映射(映射在 JSON 中称为对象)的语法; 诊断记法在此处通过允许任何数据项出现在 键位置来扩展 JSON。Undefined 按 JavaScript 写作 >undefined<。 非有限浮点数 Infinity、-Infinity 和 NaN 完全按本句中的写法书写(这也是它们在 JavaScript 中可以 书写的一种方式,尽管 JSON 不允许它们)。标签 写作标签号的整数,后跟括号中的标签内容; 例如,按 RFC 3339(ISO 8601)指定格式的日期可以 记作:

0("2013-03-21T20:04:00Z")

或如下等价相对时间:

1(1363896240)

字节串以某种基础编码记写,无 填充,括在单引号中,并以 >h< 表示 base16, 以 >b32< 表示 base32,以 >h32< 表示 base32hex,以 >b64< 表示 base64 或 base64url(实际编码不重叠,因此字符串保持 无歧义)。例如,字节串 0x12345678 可以 写作 h'12345678'、b32'CI2FM6A' 或 b64'EjRWeA'。

未分配的简单值以 “simple()” 给出,括号中放入适当的 整数。例如,“simple(42)”表示主 类型 7,值 42。

此处定义的诊断记法有许多有用扩展, 在 [RFC8610] 的 附录 G“扩展诊断记法” (EDN)中提供。 类似地,该记法可以在单独文档中扩展, 以便为本文档未涵盖的 NaN payload 提供文档支持。

8.1. 编码指示符

有时在诊断记法中指示实际使用了 若干替代表示中的哪一种是有用的;例如,诊断解码器写作 >1.5< 的数据项可能已被 编码为半精度、单精度或双精度浮点数。

编码指示符的约定是:任何以下划线开头, 且后续所有字符都是字母数字或 下划线的内容,都是编码指示符;不对此信息感兴趣者 可以忽略它。例如,__3。 编码指示符始终是 可选的。

可以在映射左花括号或 数组左方括号之后写一个单独的下划线,以指示该数据项以 不定长格式表示。例如,[_ 1, 2] 包含一个指示符,表明使用了不定长表示来 表示数据项 [1, 2]。

下划线后跟十进制数字 n 表示 前面的项(或者,对于数组和映射,表示以前面的 方括号或花括号开始的项)使用了附加信息 值 24+n 编码。例如,1.5_1 是半精度浮点 数,而 1.5_3 编码为双精度。此编码 指示符没有在 附录 A 中显示。(请注意,编码 指示符 “_” 因此是完整形式 “_7” 的缩写,而后者 不使用。)

不定长字节串和文本字符串的详细块结构 可以用 (_ h'0123', h'4567') 和 (_ "foo", "bar") 的形式记写。 然而,对于内部没有块的不定长字符串,(_ ) 会在表示字节串(0x5fff)还是文本字符串 (0x7fff)方面产生歧义,因此不使用。 可以改用基本形式 ''_ 和 ""_,且它们仅保留用于 没有块的情况——不是仅含空块的(允许但 并不真正有用的)编码的短形式;后者 需要记写为 (_ '')、(_ "") 等, 以保留块结构。

9. IANA 考虑事项

IANA 已为新的 CBOR 值创建了两个注册表。这些注册表 是分开的,也就是说,并不位于总括注册表之下,并遵循 [RFC8126] 中的规则。IANA 还 分配了新的媒体类型、一个关联的 CoAP Content-Format 条目以及一个结构化语法后缀。

9.1. CBOR 简单值 注册表

IANA 已在 [IANA.cbor-simple-values] 创建了“Concise Binary Object Representation (CBOR) Simple Values”注册表。初始值见 表 4

范围 0 到 19 中的新条目由 Standards Action [RFC8126] 分配。 建议 IANA 从数字 16 开始分配值, 以便将较低数字保留给 连续块(如果有)。

范围 32 到 255 中的新条目由 Specification Required 分配。

9.2. CBOR 标签注册表

IANA 已在 [IANA.cbor-tags] 创建了“Concise Binary Object Representation (CBOR) Tags”注册表。 [RFC7049] 中定义的标签 在 第 3.4 节 中详细描述, 此后也已经定义了其他标签。

范围 0 到 23(“1+0”)中的新条目由 Standards Action 分配。 范围 24 到 255(“1+1”)和 256 到 32767(“1+2”的低 半部分)中的新条目由 Specification Required 分配。范围 32768 到 18446744073709551615 (“1+2”的高半部分、“1+4”和“1+8”)中的新条目 由 First Come First Served 分配。注册 请求模板为:

  • 数据项
  • 语义(短形式)

此外,First Come First Served 请求应包括:

  • 联系人
  • 语义描述(URL)——此描述是 可选的;URL 可以指向类似 Internet-Draft 或 网页的内容。

使用 First Come First Served 范围并提出 不能以 32 位表示的标签号建议的申请者 (即大于 4294967295) 应意识到,这可能降低与 不支持 64 位数字的实现的互操作性。

9.3. 媒体类型注册表

单个已编码 CBOR 数据 项的互联网媒体类型 [RFC6838](“MIME type”)是 “application/cbor”,如“Media Types”注册表 [IANA.media-types] 中所定义:

类型名:
application
子类型名:
cbor
必需参数:
n/a
可选参数:
n/a
编码考虑事项:
二进制
安全考虑事项:
见 RFC 8949 的 第 10 节
互操作性考虑事项:
n/a
已发布规范:
RFC 8949
使用此媒体类型的应用:
许多
附加信息:


魔数:
n/a
文件扩展名:
.cbor
Macintosh 文件类型代码:
n/a
联系人及电子邮件地址,用于获取更多信息:
IETF CBOR Working Group (cbor@ietf.org) 或 IETF Applications and Real-Time Area (art@ietf.org)
预期用途:
COMMON
使用限制:
none
作者:
IETF CBOR Working Group (cbor@ietf.org)
变更控制者:
IESG (iesg@ietf.org)

9.4. CoAP Content-Format 注册表

CBOR 的 CoAP Content-Format 已注册在 “Constrained RESTful Environments (CoRE) Parameters”注册表内的“CoAP Content-Formats”子注册表中 [IANA.core-parameters]

媒体类型:
application/cbor
编码:
-
ID:
60
参考:
RFC 8949

9.5. 结构化语法 后缀注册表

基于单个 已编码 CBOR 数据项的媒体类型的结构化语法后缀 [RFC6838] 是 +cbor,IANA 已将其注册在 “Structured Syntax Suffixes”注册表 [IANA.structured-suffix] 中:

名称:
简明二进制对象表示 (CBOR)
+suffix:
+cbor
参考:
RFC 8949
编码考虑事项:
CBOR 是二进制格式。
互操作性考虑事项:
n/a
片段标识符考虑事项:

为 +cbor 指定的片段标识符的语法和语义 SHOULD 与为 “application/cbor” 指定的相同。(在 RFC 8949 发布时,尚未为 “application/cbor” 定义片段标识 语法。)

特定 “xxx/yyy+cbor”的片段标识符的语法和语义 SHOULD 按如下方式处理:

  • 对于 +cbor 中定义的情况,如果 片段标识符按 +cbor 规则解析, 则按 +cbor 中的规定处理。
  • 对于 +cbor 中定义的情况,如果 片段标识符没有 按 +cbor 规则解析,则按 “xxx/yyy+cbor” 中的规定处理。
  • 对于 +cbor 中未定义的情况,则 按 “xxx/yyy+cbor” 中的规定处理。
安全考虑事项:
见 RFC 8949 的 第 10 节
联系人:
IETF CBOR Working Group (cbor@ietf.org) 或 IETF Applications and Real-Time Area (art@ietf.org)
作者/变更控制者:
IETF

10. 安全考虑事项

面向网络的应用在处理传入数据的 逻辑中可能表现出漏洞。复杂解析器众所周知 很可能成为此类漏洞的来源,例如能够 远程使节点崩溃,甚至在其上远程执行任意代码。 CBOR 试图通过降低解析器复杂性,并在可能时 赋予整个可编码值范围以含义,来缩小引入此类 漏洞的机会。

由于 CBOR 解码器经常用作处理 未验证输入的第一步,因此它们需要为各种类型的 恶意输入做好充分准备,这些输入可能被设计为破坏、溢出或取得 解码该 CBOR 数据项的系统控制权。CBOR 解码器需要 假定所有输入都可能是恶意的,即使它已经由 防火墙检查过、通过 TLS 等安全通道传来、已加密或 已签名, 或来自其他被假定可信的来源。

第 4.1 节 给出了在使用 受限 CBOR 解码器处理来自使用 非首选序列化的 CBOR 编码器的输入时互操作性受限的示例。 当同一个数据项同时由这样的 受限解码器和完整解码器消费时,可能导致可被能够注入或操纵内容的 攻击者利用的安全问题。

如本文档通篇所讨论,在某些情况下可以 被认为“等价”而在其他情况下“非等价”的值有很多。仅举一例, 数字“one”的数值可以表示为 整数或大数。解释 CBOR 输入的系统可能接受 数字“one”的任一形式,也可能拒绝其中一种(或两种)形式。此类接受 或拒绝可能对使用该解释输入的程序产生安全影响。

恶意输入可能被构造为溢出缓冲区、使 整数算术上溢或下溢,或造成其他解码中断。CBOR 数据项可能具有故意设置得 极大或过短的长度或大小。 资源耗尽攻击可能试图诱使解码器 分配非常大的数据项(字符串、数组、映射,甚至 任意精度数字),或通过设置深度嵌套项来耗尽 栈深度。解码器需要具备 适当的资源管理来缓解这些攻击。(给出 非常大大小的项也可能试图利用整数 溢出漏洞。)

CBOR 解码器按定义只接受良构 CBOR;这是 其健壮性的第一步。非良构 CBOR 输入 从检测到缺乏良构性的点开始不再 进行进一步处理。如果可能,直到 这一点为止已解码的任何数据都不应影响使用该 CBOR 解码器的应用。

除了确定良构性外,CBOR 解码器也可能 对 CBOR 数据执行有效性检查。或者,它可以将 这些检查留给使用该解码器的应用。该选择需要 在解码器中明确记录。除了 CBOR 层级的有效性外, 应用还需要确定输入与以 CBOR 序列化的 应用协议相一致。

输入检查本身可能消耗资源。这通常与 输入大小呈线性关系,这意味着攻击者必须花费 与防御者在输入验证上花费的资源相称的 资源。 然而,攻击者可能能够构造出使 目标解码器处理时间长于攻击者生成时间的输入。 任意精度数字的处理可能 超过线性工作量。此外,解码器用来构建映射内存表示的某些哈希表实现 可能被攻击而花费二次方工作量,除非采用秘密密钥 (见 [SIPHASH_LNCS] 第 7 节,另见 [SIPHASH_OPEN])或其他缓解措施。 这种超线性工作量可能被 攻击者利用,在输入验证器处或之前耗尽资源; 因此在 CBOR 解码器 实现中需要避免。请注意,标签号定义及其实现 可以增加此类安全考虑事项;随后应在 标签号定义的安全考虑事项中讨论。

CBOR 编码器不直接接收来自网络的输入, 因此不会像 CBOR 解码器那样被直接攻击。 然而,CBOR 编码器经常具有一个 API, 从实现中的另一个层级获取输入,并可能通过该 API 受到攻击。该 API 的设计和实现 应假定其调用者的行为可能基于恶意输入 或编码错误。它应检查缓冲区溢出、 整数算术上溢和下溢,以及其他此类旨在破坏编码器的 错误。

协议应以一种 可靠地将潜在多重解释缩减为单一解释的方式定义。例如,攻击者可以利用 映射中重复键等无效输入,或利用处理数字时的不同 精度,使一个应用基于 不同于第二个应用将使用的解释来作出 决策。为了促进一致解释, 编码器和解码器实现应 提供一种有效性检查操作模式 (第 5.4 节)。然而请注意,通用解码器 无法 知道应用对其输入数据提出的所有要求; 因此它并不能免除应用执行 自身输入检查的责任。此外,由于已定义标签号集合 会演进,应用可能采用通用解码器尚未 支持有效性检查的标签号。通用 解码器因此需要记录它们支持哪些标签号, 以及它们为这些标签号和基本 CBOR(UTF-8 检查、重复映射 键检查)提供何种有效性检查。

第 3.4.3 节 指出,使用非首选的 大数表示选择而不是基本整数来编码数字,并不打算 具有应用语义,但如果接收 CBOR 数据的应用 使用基本通用数据模型中的解码器,它就可能具有这样的语义。这种 差异在两组语义不同时会造成安全问题。因此, 使用 CBOR 的应用需要为 CBOR 数据的每种用途指定 其使用的数据模型。

将 CBOR 数据转换为其他格式很常见。在许多情况下,CBOR 具有比其他格式 更具表达力的类型;这对于常见的 转换为 JSON 尤其如此。类型信息的丢失可能给处理表达力较弱数据的系统 造成安全问题。

第 6.2 节 描述了一个可能常见的 在 CBOR 和 JSON 之间转换的使用场景;如果攻击者知道 应用正在执行该转换,则可能允许攻击。

来自 [RFC4648] 的 base16 和 base64 的使用安全考虑事项, 以及来自 [RFC3629] 的 UTF-8 的使用 安全考虑事项,也与 CBOR 相关。

11. 参考文献

11.1. 规范性参考文献

[C]
国际标准化组织“信息技术 - 程序设计语言 - C”第四版ISO/IEC 9899:2018<https://www.iso.org/standard/74528.html>
[Cplusplus20]
国际标准化组织“程序设计语言 - C++”第六 版ISO/IEC DIS 14882ISO/IEC ISO/IEC JTC1 SC22 WG21 N 4860<https://isocpp.org/files/papers/N4860.pdf>
[IEEE754]
IEEE“IEEE 浮点 算术标准”IEEE Std 754-2019DOI 10.1109/IEEESTD.2019.8766229<https://ieeexplore.ieee.org/document/8766229>
[RFC2045]
Freed, N. 和 N. Borenstein“多用途互联网邮件扩展(MIME)第一部分:互联网 消息体格式”RFC 2045DOI 10.17487/RFC2045<https://www.rfc-editor.org/info/rfc2045>
[RFC2119]
Bradner, S.“RFC 中用于 指示要求级别的关键词”BCP 14RFC 2119DOI 10.17487/RFC2119<https://www.rfc-editor.org/info/rfc2119>
[RFC3339]
Klyne, G. 和 C. Newman“互联网上的日期和时间:时间戳”RFC 3339DOI 10.17487/RFC3339<https://www.rfc-editor.org/info/rfc3339>
[RFC3629]
Yergeau, F.“UTF-8,ISO 10646 的一种转换格式”STD 63RFC 3629DOI 10.17487/RFC3629<https://www.rfc-editor.org/info/rfc3629>
[RFC3986]
Berners-Lee, T.、Fielding, R. 和 L. Masinter“统一资源标识符 (URI):通用语法”STD 66RFC 3986DOI 10.17487/RFC3986<https://www.rfc-editor.org/info/rfc3986>
[RFC4287]
Nottingham, M., Ed. 和 R. Sayre, Ed.“Atom 聚合格式”RFC 4287DOI 10.17487/RFC4287<https://www.rfc-editor.org/info/rfc4287>
[RFC4648]
Josefsson, S.“Base16、Base32 和 Base64 数据编码”RFC 4648DOI 10.17487/RFC4648<https://www.rfc-editor.org/info/rfc4648>
[RFC8126]
Cotton, M.、Leiba, B. 和 T. Narten“RFC 中 IANA 考虑事项章节的编写指南”BCP 26RFC 8126DOI 10.17487/RFC8126<https://www.rfc-editor.org/info/rfc8126>
[RFC8174]
Leiba, B.“RFC 2119 关键词中大写与 小写的歧义”BCP 14RFC 8174DOI 10.17487/RFC8174<https://www.rfc-editor.org/info/rfc8174>
[TIME_T]
The Open Group“The Open Group 基础 规范”第 4.16 节,“自纪元以来的秒数”第 7 版,2018 版IEEE Std 1003.1<https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16>

11.2. 资料性参考文献

[ASN.1]
国际电信联盟“信息技术 - ASN.1 编码规则:基本编码 规则(BER)、规范编码规则(CER)和可辨别编码规则(DER)规范”ITU-T Recommendation X.690<https://www.itu.int/rec/T-REC-X.690-201508-I/en>
[BSON]
Various“BSON - 二进制 JSON”<http://bsonspec.org/>
[CBOR-TAGS]
Bormann, C.“重要的 CBOR 标签”进行中的工作Internet-Draft, draft-bormann-cbor-notable-tags-02<https://tools.ietf.org/html/draft-bormann-cbor-notable-tags-02>
[ECMA262]
Ecma International“ECMAScript 2020 语言 规范”标准 ECMA-262,第 11 版<https://www.ecma-international.org/publications/standards/Ecma-262.htm>
[Err3764]
RFC Errata勘误 ID 3764RFC 7049<https://www.rfc-editor.org/errata/eid3764>
[Err3770]
RFC Errata勘误 ID 3770RFC 7049<https://www.rfc-editor.org/errata/eid3770>
[Err4294]
RFC Errata勘误 ID 4294RFC 7049<https://www.rfc-editor.org/errata/eid4294>
[Err4409]
RFC Errata勘误 ID 4409RFC 7049<https://www.rfc-editor.org/errata/eid4409>
[Err4963]
RFC Errata勘误 ID 4963RFC 7049<https://www.rfc-editor.org/errata/eid4963>
[Err4964]
RFC Errata勘误 ID 4964RFC 7049<https://www.rfc-editor.org/errata/eid4964>
[Err5434]
RFC Errata勘误 ID 5434RFC 7049<https://www.rfc-editor.org/errata/eid5434>
[Err5763]
RFC Errata勘误 ID 5763RFC 7049<https://www.rfc-editor.org/errata/eid5763>
[Err5917]
RFC Errata勘误 ID 5917RFC 7049<https://www.rfc-editor.org/errata/eid5917>
[IANA.cbor-simple-values]
IANA“简明二进制对象表示 (CBOR)简单值”<https://www.iana.org/assignments/cbor-simple-values>
[IANA.cbor-tags]
IANA“简明二进制对象表示 (CBOR)标签”<https://www.iana.org/assignments/cbor-tags>
[IANA.core-parameters]
IANA“受限 RESTful 环境(CoRE) 参数”<https://www.iana.org/assignments/core-parameters>
[IANA.media-types]
IANA“媒体类型”<https://www.iana.org/assignments/media-types>
[IANA.structured-suffix]
IANA“结构化语法后缀”<https://www.iana.org/assignments/media-type-structured-suffix>
[MessagePack]
Furuhashi, S.“MessagePack”<https://msgpack.org/>
[PCRE]
Hazel, P.“PCRE - Perl 兼容正则 表达式”<https://www.pcre.org/>
[RFC0713]
Haverty, J.“MSDTP-消息服务数据 传输协议”RFC 713DOI 10.17487/RFC0713<https://www.rfc-editor.org/info/rfc713>
[RFC6838]
Freed, N.、Klensin, J. 和 T. Hansen“媒体类型规范和 注册程序”BCP 13RFC 6838DOI 10.17487/RFC6838<https://www.rfc-editor.org/info/rfc6838>
[RFC7049]
Bormann, C. 和 P. Hoffman“简明二进制对象表示(CBOR)”RFC 7049DOI 10.17487/RFC7049<https://www.rfc-editor.org/info/rfc7049>
[RFC7228]
Bormann, C.、Ersue, M. 和 A. Keranen“受限节点网络术语”RFC 7228DOI 10.17487/RFC7228<https://www.rfc-editor.org/info/rfc7228>
[RFC7493]
Bray, T., Ed.“I-JSON 消息 格式”RFC 7493DOI 10.17487/RFC7493<https://www.rfc-editor.org/info/rfc7493>
[RFC7991]
Hoffman, P.““xml2rfc”第 3 版 词汇表”RFC 7991DOI 10.17487/RFC7991<https://www.rfc-editor.org/info/rfc7991>
[RFC8259]
Bray, T., Ed.“JavaScript 对象表示法 (JSON)数据交换格式”STD 90RFC 8259DOI 10.17487/RFC8259<https://www.rfc-editor.org/info/rfc8259>
[RFC8610]
Birkholz, H.、Vigano, C. 和 C. Bormann“简明数据定义 语言(CDDL):一种用于表达简明二进制对象表示(CBOR) 和 JSON 数据结构的记法约定”RFC 8610DOI 10.17487/RFC8610<https://www.rfc-editor.org/info/rfc8610>
[RFC8618]
Dickinson, J.、Hague, J.、Dickinson, S.、Manderson, T. 和 J. Bond“压缩 DNS(C-DNS):DNS 数据包捕获格式”RFC 8618DOI 10.17487/RFC8618<https://www.rfc-editor.org/info/rfc8618>
[RFC8742]
Bormann, C.“简明二进制对象 表示(CBOR)序列”RFC 8742DOI 10.17487/RFC8742<https://www.rfc-editor.org/info/rfc8742>
[RFC8746]
Bormann, C., Ed.“用于类型化数组的简明二进制对象 表示(CBOR)标签”RFC 8746DOI 10.17487/RFC8746<https://www.rfc-editor.org/info/rfc8746>
[SIPHASH_LNCS]
Aumasson, J. 和 D. Bernstein“SipHash:一种快速短输入 PRF”Progress in Cryptology - INDOCRYPT 2012,第 489-508 页DOI 10.1007/978-3-642-34931-7_28<https://doi.org/10.1007/978-3-642-34931-7_28>
[SIPHASH_OPEN]
Aumasson, J. 和 D.J. Bernstein“SipHash:一种快速短输入 PRF”<https://www.aumasson.jp/siphash/siphash.pdf>
[YAML]
Ben-Kiki, O.、Evans, C. 和 I.d. Net“YAML Ain't Markup Language (YAML[TM])1.2 版”第 3 版<https://www.yaml.org/spec/1.2/spec.html>

附录 A. 已编码 CBOR 数据 项示例

下表提供了一些以十六进制表示的 CBOR 编码值 (右列),以及这些值的诊断记法(左 列)。请注意,字符串“\u00fc”是包含单个 Unicode 字符 U+00FC(带分音符的拉丁 小写字母 U,“ü”) 的 UTF-8 字符串的一种诊断 记法形式。类似地,“\u6c34”是诊断 记法中包含单个字符 U+6C34(CJK 统一表意文字-6C34, “水”) 的 UTF-8 字符串, 通常表示“water”,而“\ud800\udd51”是 诊断记法中包含单个字符 U+10151(希腊阿克罗福尼克阿提卡 五十斯塔特,“𐅑”) 的 UTF-8 字符串。(请注意,所有这些单字符 字符串也可以在诊断 记法中以原生 UTF-8 表示,只是在要求仅 ASCII 规范时不行。) 在为 大数提供的诊断记法中,其预期数值显示为十进制数(例如 18446744073709551616),而不是带标签字节串(例如 2(h'010000000000000000'))。

表 6: 已编码 CBOR 数据 项示例
诊断记法 已编码
0 0x00
1 0x01
10 0x0a
23 0x17
24 0x1818
25 0x1819
100 0x1864
1000 0x1903e8
1000000 0x1a000f4240
1000000000000 0x1b000000e8d4a51000
18446744073709551615 0x1bffffffffffffffff
18446744073709551616 0xc249010000000000000000
-18446744073709551616 0x3bffffffffffffffff
-18446744073709551617 0xc349010000000000000000
-1 0x20
-10 0x29
-100 0x3863
-1000 0x3903e7
0.0 0xf90000
-0.0 0xf98000
1.0 0xf93c00
1.1 0xfb3ff199999999999a
1.5 0xf93e00
65504.0 0xf97bff
100000.0 0xfa47c35000
3.4028234663852886e+38 0xfa7f7fffff
1.0e+300 0xfb7e37e43c8800759c
5.960464477539063e-8 0xf90001
0.00006103515625 0xf90400
-4.0 0xf9c400
-4.1 0xfbc010666666666666
Infinity 0xf97c00
NaN 0xf97e00
-Infinity 0xf9fc00
Infinity 0xfa7f800000
NaN 0xfa7fc00000
-Infinity 0xfaff800000
Infinity 0xfb7ff0000000000000
NaN 0xfb7ff8000000000000
-Infinity 0xfbfff0000000000000
false 0xf4
true 0xf5
null 0xf6
undefined 0xf7
simple(16) 0xf0
simple(255) 0xf8ff
0("2013-03-21T20:04:00Z") 0xc074323031332d30332d32315432303a 30343a30305a
1(1363896240) 0xc11a514b67b0
1(1363896240.5) 0xc1fb41d452d9ec200000
23(h'01020304') 0xd74401020304
24(h'6449455446') 0xd818456449455446
32("http://www.example.com") 0xd82076687474703a2f2f7777772e6578 616d706c652e636f6d
h'' 0x40
h'01020304' 0x4401020304
"" 0x60
"a" 0x6161
"IETF" 0x6449455446
"\"\\" 0x62225c
"\u00fc" 0x62c3bc
"\u6c34" 0x63e6b0b4
"\ud800\udd51" 0x64f0908591
[] 0x80
[1, 2, 3] 0x83010203
[1, [2, 3], [4, 5]] 0x8301820203820405
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] 0x98190102030405060708090a0b0c0d0e 0f101112131415161718181819
{} 0xa0
{1: 2, 3: 4} 0xa201020304
{"a": 1, "b": [2, 3]} 0xa26161016162820203
["a", {"b": "c"}] 0x826161a161626163
{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"} 0xa5616161416162614261636143616461 4461656145
(_ h'0102', h'030405') 0x5f42010243030405ff
(_ "strea", "ming") 0x7f657374726561646d696e67ff
[_ ] 0x9fff
[_ 1, [2, 3], [_ 4, 5]] 0x9f018202039f0405ffff
[_ 1, [2, 3], [4, 5]] 0x9f01820203820405ff
[1, [2, 3], [_ 4, 5]] 0x83018202039f0405ff
[1, [_ 2, 3], [4, 5]] 0x83019f0203ff820405
[_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] 0x9f0102030405060708090a0b0c0d0e0f 101112131415161718181819ff
{_ "a": 1, "b": [_ 2, 3]} 0xbf61610161629f0203ffff
["a", {_ "b": "c"}] 0x826161bf61626163ff
{_ "Fun": true, "Amt": -2} 0xbf6346756ef563416d7421ff

附录 B. 初始 字节跳转表

为简洁起见,此跳转表不显示 保留供未来扩展使用的初始字节。它也只显示可用于 可选功能的初始字节中的一部分。(所有 无符号整数都采用网络字节序。)

表 7: 初始字节跳转表
字节 结构/语义
0x00..0x17 无符号整数 0x00..0x17(0..23)
0x18 无符号整数(后跟一个字节 uint8_t)
0x19 无符号整数(后跟两个字节 uint16_t)
0x1a 无符号整数(后跟四个字节 uint32_t)
0x1b 无符号整数(后跟八个字节 uint64_t)
0x20..0x37 负整数 -1-0x00..-1-0x17(-1..-24)
0x38 负整数 -1-n(后跟一个字节 uint8_t 表示 n)
0x39 负整数 -1-n(后跟两个字节 uint16_t 表示 n)
0x3a 负整数 -1-n(后跟四个字节 uint32_t 表示 n)
0x3b 负整数 -1-n(后跟八个字节 uint64_t 表示 n)
0x40..0x57 字节串(后跟 0x00..0x17 个字节)
0x58 字节串(一个字节 uint8_t 表示 n,然后 后跟 n 个字节)
0x59 字节串(两个字节 uint16_t 表示 n,然后 后跟 n 个字节)
0x5a 字节串(四个字节 uint32_t 表示 n,然后 后跟 n 个字节)
0x5b 字节串(八个字节 uint64_t 表示 n,然后 后跟 n 个字节)
0x5f 字节串,后跟若干字节串,并由 “break”终止
0x60..0x77 UTF-8 字符串(后跟 0x00..0x17 个字节)
0x78 UTF-8 字符串(一个字节 uint8_t 表示 n,然后 后跟 n 个字节)
0x79 UTF-8 字符串(两个字节 uint16_t 表示 n,然后 后跟 n 个字节)
0x7a UTF-8 字符串(四个字节 uint32_t 表示 n,然后 后跟 n 个字节)
0x7b UTF-8 字符串(八个字节 uint64_t 表示 n,然后 后跟 n 个字节)
0x7f UTF-8 字符串,后跟若干 UTF-8 字符串,并由 “break”终止
0x80..0x97 数组(后跟 0x00..0x17 个数据项)
0x98 数组(一个字节 uint8_t 表示 n,然后后跟 n 个 数据项)
0x99 数组(两个字节 uint16_t 表示 n,然后后跟 n 个 数据项)
0x9a 数组(四个字节 uint32_t 表示 n,然后后跟 n 个 数据项)
0x9b 数组(八个字节 uint64_t 表示 n,然后后跟 n 个 数据项)
0x9f 数组,后跟若干数据项,并由 “break”终止
0xa0..0xb7 映射(后跟 0x00..0x17 对数据项)
0xb8 映射(一个字节 uint8_t 表示 n,然后后跟 n 对 数据项)
0xb9 映射(两个字节 uint16_t 表示 n,然后后跟 n 对 数据项)
0xba 映射(四个字节 uint32_t 表示 n,然后后跟 n 对 数据项)
0xbb 映射(八个字节 uint64_t 表示 n,然后后跟 n 对 数据项)
0xbf 映射,后跟若干对数据项,并由 “break”终止
0xc0 基于文本的日期/时间(后跟数据项;见 第 3.4.1 节
0xc1 基于纪元的日期/时间(后跟数据项;见 第 3.4.2 节
0xc2 无符号大数(后跟数据项“字节串”)
0xc3 负大数(后跟数据项“字节串”)
0xc4 十进制小数(后跟数据项“数组”; 见 第 3.4.4 节
0xc5 大浮点数(后跟数据项“数组”;见 第 3.4.4 节
0xc6..0xd4 (标签)
0xd5..0xd7 预期转换(后跟数据项;见 第 3.4.5.2 节
0xd8..0xdb (更多标签;后跟 1/2/4/8 字节标签号, 然后后跟一个数据项)
0xe0..0xf3 (简单值)
0xf4 false
0xf5 true
0xf6 null
0xf7 undefined
0xf8 (简单值,后跟一个字节)
0xf9 半精度浮点数(两个字节 IEEE 754)
0xfa 单精度浮点数(四个字节 IEEE 754)
0xfb 双精度浮点数(八个字节 IEEE 754)
0xff “break”停止码

附录 C. 伪代码

CBOR 项的良构性可以通过 图 1 中的伪代码检查。当且仅当满足以下条件时,数据是良构的:

伪代码具有以下先决条件:

请注意,well_formed 会为良构的 定长项返回主类型,但会为不定长项返回 99(或为 “break”停止码返回 -1,仅当设置了 breakable 时)。这在 well_formed_indefinite 中用于确定不定长字符串 只包含定长字符串作为块。

well_formed(breakable = false) {
  // 处理初始字节
  ib = uint(take(1));
  mt = ib >> 5;
  val = ai = ib & 0x1f;
  switch (ai) {
    case 24: val = uint(take(1)); break;
    case 25: val = uint(take(2)); break;
    case 26: val = uint(take(4)); break;
    case 27: val = uint(take(8)); break;
    case 28: case 29: case 30: fail();
    case 31:
      return well_formed_indefinite(mt, breakable);
  }
  // 处理内容
  switch (mt) {
    // case 0、1、7 没有内容;只使用 val
    case 2: case 3: take(val); break; // 字节/UTF-8
    case 4: for (i = 0; i < val; i++) well_formed(); break;
    case 5: for (i = 0; i < val*2; i++) well_formed(); break;
    case 6: well_formed(); break;     // 1 个嵌入数据项
    case 7: if (ai == 24 && val < 32) fail(); // 错误简单值
  }
  return mt;                    // 定长数据项
}

well_formed_indefinite(mt, breakable) {
  switch (mt) {
    case 2: case 3:
      while ((it = well_formed(true)) != -1)
        if (it != mt)           // 需要同一类型的
          fail();               //    定长块
      break;
    case 4: while (well_formed(true) != -1); break;
    case 5: while (well_formed(true) != -1) well_formed(); break;
    case 7:
      if (breakable)
        return -1;              // 发出 break out 信号
      else fail();              // 没有外层不定长项
    default: fail();            // 错误 mt
  }
  return 99;                    // 不定长数据项
}
图 1: 良构性 检查伪代码

请注意,完整 CBOR 解码器的剩余复杂性大致在于 以适当形式向应用呈现已解码的数据。

主类型 0 和 1 的设计方式使得它们可以 在 C 中从有符号整数编码,而无需实际执行 针对正/负的 if-then-else(图 2)。这利用了 这样一个事实:(-1-n),即主类型 1 的转换,与 C 无符号算术中的 ~n(按位取反)相同;随后可以将 ~n 表示为负数情况下的 (-1)^n,而 0^n 会让非负数情况下的 n 保持不变。一个数的符号可以通过对该数进行 比该数位长少一位的算术右移, 转换为负数时的 -1 和非负数(0 或正数)时的 0 (例如,对于 64 位数右移 63 位)。

void encode_sint(int64_t n) {
  uint64t ui = n >> 63;    // 将符号扩展到整个长度
  unsigned mt = ui & 0x20; // 提取(已移位的)主类型
  ui ^= n;                 // 对负数取反
  if (ui < 24)
    *p++ = mt + ui;
  else if (ui < 256) {
    *p++ = mt + 24;
    *p++ = ui;
  } else
       ...
图 2: 编码有符号 整数的伪代码

关于这些代码片段所使用 C 语言配置文件的一些特定 假设,请参见 第 1.2 节

附录 D. 半精度

由于半精度浮点数直到 2008 年才加入 IEEE 754 [IEEE754],今天的编程平台往往 仍然只对其提供有限 支持。即使没有这种支持,也很容易至少包含 对它们的解码支持。图 3 展示了一个用 C 语言编写的 小型半精度浮点数解码器示例。 图 4 中给出了类似的 Python 程序;此代码假定 2 字节值已经 按网络字节序解码为(unsigned short)整数 (如 附录 C 中的伪代码会做的那样)。

#include <math.h>

double decode_half(unsigned char *halfp) {
  unsigned half = (halfp[0] << 8) + halfp[1];
  unsigned exp = (half >> 10) & 0x1f;
  unsigned 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;
}
图 3: 半精度 解码器的 C 代码
import struct
from math import ldexp

def decode_single(single):
    return struct.unpack("!f", struct.pack("!I", single))[0]

def decode_half(half):
    valu = (half & 0x7fff) << 13 | (half & 0x8000) << 16
    if ((half & 0x7c00) != 0x7c00):
        return ldexp(decode_single(valu), 112)
    return decode_single(valu | 0x7f800000)
图 4: 半精度 解码器的 Python 代码

附录 E. 其他二进制 格式与 CBOR 设计目标的比较

CBOR 提案沿袭了一段与 计算机本身历史一样长的二进制格式历史。不同格式具有 不同目标。在多数情况下,格式的目标 从未被明确说明,尽管有时可以从 该格式最初使用的上下文中推断出来。某些格式旨在 普遍可用,尽管历史已经证明,没有任何二进制格式 能满足所有协议和应用的需求。

CBOR 与其中许多格式不同,因为它从一组 目标出发,并试图只满足这些目标。本节将 数十种格式中的几种与 CBOR 的目标进行比较,以帮助 读者决定是否在某个特定协议或应用中使用 CBOR 或其他格式。

请注意,这里的讨论并不是对任何 格式的批评:据我们所知,在 CBOR 之前没有任何格式旨在 按照我们为 CBOR 目标分配的优先级来覆盖这些目标。来自 第 1.1 节 的 目标简要回顾如下:

  1. 对互联网 标准中的多数常见数据格式进行无歧义编码
  2. 编码器或解码器的代码紧凑性
  3. 不需要模式描述
  4. 相当紧凑的序列化
  5. 适用于受限和非受限应用
  6. 良好的 JSON 转换
  7. 可扩展性

[RFC8618]第 5 节 和附录 C 提供了关于 CBOR 和其他格式相对于另一组设计目标的 讨论。

E.1. ASN.1 DER、BER 和 PER

[ASN.1] 有许多 序列化格式。在 IETF 中,DER 和 BER 是 最常见的。对许多项而言,序列化输出并不特别紧凑, 并且在受限设备上解码数字项所需的代码可能很复杂。

很少(如果有的话)IETF 协议采用 Packed Encoding Rules (PER)的若干变体之一。原因可能有很多, 但常见说法之一是 PER 即使在解析数据项的表层结构时 也会使用模式, 因而需要大量工具支持。目前使用着 不同版本的 ASN.1 模式语言,这也阻碍了采用。

E.2. MessagePack

[MessagePack] 是一种 简明、广泛实现的计数式二进制 序列化格式,在许多属性上与 CBOR 相似,尽管 稍微不那么规则。虽然其数据模型可以用于表示 JSON 数据,MessagePack 也已用于许多远程过程 调用(RPC)应用以及数据的长期存储。

MessagePack 自约 2011 年首次发布以来基本保持稳定; 它尚未经历过一次迁移。MessagePack 的演进 受制于保持与已有存储数据完全向后 兼容的强制要求,而可供扩展的字节码只剩很少。 多年来,MessagePack 用户社区反复请求 在编码中分离二进制字符串和文本字符串, 最近促成了一个扩展提案,但该提案会让 MessagePack 的“raw”数据在二进制 和文本数据用途之间保持歧义。MessagePack 的扩展机制仍然 不清楚。

E.3. BSON

[BSON] 是一种为在 MongoDB 数据库中存储类 JSON 映射(JSON 对象) 而开发的数据格式。它的主要 区分特征是支持就地更新, 这阻止了紧凑表示。除了映射键之外,BSON 使用计数式 表示,而映射键以空字节终止。 虽然 BSON 可用于在线路上表示类 JSON 对象, 但其规范受数据库应用需求支配,并已变得有些繁复。 BSON 扩展将如何实现的状态仍不清楚。

E.4. MSDTP:RFC 713

Message Services Data Transmission(MSDTP)是 紧凑消息格式的一个非常早期示例;它在 1976 年编写的 [RFC0713] 中描述。 它包含在这里是出于历史价值,而不是因为它 曾经被广泛使用。

E.5. 在线路上的 简洁性

虽然 CBOR 的编码器和 解码器代码紧凑性设计目标优先级高于其在线路上简洁性的 目标,许多人仍关注线路大小。表 8 展示了 简单嵌套数组 [1, [2, 3]] 的一些编码示例;对于 编码支持某种形式不定长编码的情况, 也展示 [_ 1, [2, 3]](外层数组为不定长)。

表 8: 不同 简洁程度示例
格式 [1, [2, 3]] [_ 1, [2, 3]]
RFC 713 c2 05 81 c2 02 82 83
ASN.1 BER 30 0b 02 01 01 30 06 02 01 02 02 01 03 30 80 02 01 01 30 06 02 01 02 02 01 03 00 00
MessagePack 92 01 92 02 03
BSON 22 00 00 00 10 30 00 01 00 00 00 04 31 00 13 00 00 00 10 30 00 02 00 00 00 10 31 00 03 00 00 00 00 00
CBOR 82 01 82 02 03 9f 01 82 02 03 ff

附录 F. 良构性错误和 示例

解码 CBOR 数据项时可能出现三种基本 良构性错误:

数据过多:
存在尚未 消耗的输入字节。 只有在应用假定输入 字节正好跨越一个数据项时,这才是错误。当应用 使用 CBOR 编码的自定界性质以允许 数据项之后有额外数据时,例如在 CBOR 序列 [RFC8742] 中所做的那样,CBOR 解码器 可以简单地 指示输入的哪一部分尚未被消耗。
数据过少:
可用输入数据需要 在其末尾添加 附加字节,才能形成完整的 CBOR 数据项。这可能 表示输入被截断;当尝试将随机数据解码为 CBOR 时,这也是一种常见错误。然而,对于某些 应用,这可能实际上不是错误,因为 应用可能尚不确定它拥有所有数据,并且可以 获取或等待更多输入字节。其中一些 应用可能对可出现的附加数据量设有上限; 在这里,解码器可能能够指示该 已编码 CBOR 数据项无法在此限制内完成。
语法错误:
输入数据不符合 CBOR 编码的要求,并且无法通过 在末尾添加(或移除)数据来补救。

附录 C 中,第一类错误 在第一段和项目列表中处理(要求“没有字节留下”),第二类错误 在第二段/项目列表中处理 (“如果 n 个字节不再可用”则失败)。第三类 错误通过伪代码中调用 fail() 的具体实例来识别,按顺序为:

F.1. 非良构 CBOR 数据 项示例

本小节展示若干非 良构 CBOR 数据项示例。每个示例都是一个字节序列,每个字节以 十六进制显示;列表中的多个示例用逗号分隔。

良构性错误类型 1(数据过多)的示例,可以很容易地 通过向良构的已编码 CBOR 数据项添加数据来形成。

类似地,良构性错误类型 2(数据过少)的示例 可以通过截断良构的已编码 CBOR 数据项来形成。在 测试套件中,专门用需要大量 补充才能完成的不完整数据项进行测试可能是有益的 (例如,从编码一个非常 大大小的字符串开始)。

输入过早结束可能发生在头部中,也可能发生在所含 数据中,后者可以是裸字符串,或被包含的数据项,这些项要么 是计数的,要么本应由“break”停止码结束。

输入在头部中结束:
18, 19, 1a, 1b, 19 01, 1a 01 02, 1b 01 02 03 04 05 06 07, 38, 58, 78, 98, 9a 01 ff 00, b8, d8, f8, f9 00, fa 00 00, fb 00 00 00
数据过短的定长字符串:
41, 61, 5a ff ff ff ff 00, 5b ff ff ff ff ff ff ff ff 01 02 03, 7a ff ff ff ff 00, 7b 7f ff ff ff ff ff ff ff 01 02 03
定长映射和数组没有以足够项关闭:
81, 81 81 81 81 81 81 81 81 81, 82 00, a1, a2 01 02, a1 00, a2 00 00 00
标签号之后没有标签内容:
c0
不定长字符串未由“break”停止码关闭:
5f 41 00, 7f 61 00
不定长映射和数组未由“break”停止码关闭:
9f, 9f 01 02, bf, bf 01 02 01 02, 81 9f, 9f 80 00, 9f 9f 9f 9f 9f ff ff ff ff, 9f 81 9f 81 9f 9f ff ff ff

下面展示了良构性错误类型 3 (语法错误)五个子类的一些示例。

子类 1:

保留的附加信息值:
1c, 1d, 1e, 3c, 3d, 3e, 5c, 5d, 5e, 7c, 7d, 7e, 9c, 9d, 9e, bc, bd, be, dc, dd, de, fc, fd, fe,
子类 2:

简单值的保留双字节编码:
f8 00, f8 01, f8 18, f8 1f
子类 3:

不定长字符串块不是正确类型:
5f 00 ff, 5f 21 ff, 5f 61 00 ff, 5f 80 ff, 5f a0 ff, 5f c0 00 ff, 5f e0 ff, 7f 41 00 ff
不定长字符串块不是定长:
5f 5f 41 00 ff ff, 7f 7f 61 00 ff ff
子类 4:

Break 单独出现在不定长 项之外:
ff
Break 出现在定长数组或映射或标签中:
81 ff, 82 00 ff, a1 ff, a1 ff 00, a1 00 ff, a2 00 00 ff, 9f 81 ff, 9f 82 9f 81 9f 9f ff ff ff ff
Break 出现在不定长映射中,会导致项数为奇数 (break 处于值位置):
bf 00 ff, bf 00 00 00 ff
子类 5:

主类型 0、1、6 搭配附加信息 31:
1f, 3f, df

附录 G. 相对 RFC 7049 的变更

如引言中所讨论, 本文档正式废止 RFC 7049,同时保持与 RFC 7049 交换格式 完全兼容。本文档提供编辑性 改进、增加细节并修正勘误。 本文档不会创建该格式的新版本。

G.1. 勘误处理和 文书性变更

RFC 7049 上的两个已验证勘误 [Err3764][Err3770],涉及 文本中的两个编码示例,这些示例已经更正 (第 3.4.3 节:“29” -> “49”, 第 5.5 节:“0b000_11101” -> “0b000_11001”)。此外,RFC 7049 包含一个使用数值 24 作为简单值的示例 [Err5917], 该示例不是良构的;此示例 已被移除。勘误报告 5763 [Err5763] 指出了标签定义措辞中的 错误;这在重写 第 3.4 节 期间得到解决。勘误报告 5434 [Err5434] 指出, 附录 E 中的 Universal Binary JSON (UBJSON)示例 已不再符合勘误报告提交时的 UBJSON 当前版本。 结果发现 UBJSON 规范自 2013 年以来已完全改变;因此该示例 被移除。其他勘误报告 [Err4409] [Err4963] [Err4964] 抱怨规范编码的映射键排序规则 过于繁重;这些报告促使重新考虑规范编码 建议,并由确定性编码建议取代 (如下所述)。勘误报告 4294 中的编辑性建议 [Err4294] 也已 实施(通过在 第 3.2.2 节 最后一个示例的注释中添加“Second value”, 改进对称性)。

其他文书性变更包括:

  • 使用新的 xml2rfc 功能 [RFC7991]
  • 对所用记法提供更多解释;
  • 更新参考文献,例如从 RFC 4627 更新到 [RFC8259], 从 CNN-TERMS 更新到 [RFC7228],以及 从 [ECMA262] 第 5.1 版更新到第 11 版; 添加对 [IEEE754] 的引用 并导入所需定义; 添加对 [C][Cplusplus20] 的引用; 以及添加对 [RFC8618] 的引用,该引用进一步说明了 附录 E 中的讨论;
  • 在诊断记法的讨论中(第 8 节), 现在提到了 [RFC8610] 中定义的“扩展诊断记法”(EDN), 现在突出了表示 NaN payload 的空白, 并添加了对表示没有块的不定长字符串的说明 (第 8.1 节);
  • 添加本附录。

G.2. IANA 考虑事项中的变更

IANA 考虑事项已整体更新(文书性变更, 例如现在指向 CBOR 工作组作为规范作者)。 各相应 IANA 注册表的引用被 添加到资料性参考文献中。

在“Concise Binary Object Representation (CBOR) Tags”注册表 [IANA.cbor-tags] 中, 从 256 到 32767(“1+2”的低 半部分)空间中的标签不再按 First Come First Served 分配;该范围 现在为 Specification Required。

G.3. 建议和其他 资料性组成部分中的变更

在修订本文档时,除了处理勘误报告之外, 工作组还借鉴了 CBOR 在多种应用中近七年的经验。 这导致了一些编辑性变更,包括添加用于说明的表格,同时也 强调了某些方面并淡化了其他方面。

一项重要新增内容是 第 2 节,它 讨论 CBOR 数据模型及 CBOR 处理中涉及的小变体。 为这些变体(基本通用、 扩展通用、特定)引入术语,使文档其他 位置的语言更加简洁,也有助于澄清对 实现和格式可扩展性特性的期望。

作为一种源自 JSON 生态系统的格式,RFC 7049 受到 当时从 JavaScript 继承而来的 JSON 数字系统影响。 JSON 不提供不同的整数和浮点 值(后者在格式中是十进制的)。CBOR 提供数字的二进制表示,并且确实区分 整数和浮点值。来自实现和使用的经验 表明,文档中应更清楚地划定这两个数字 域之间的分离;暗示整数可以无缝替代浮点 值的语言已被移除。此外,还添加了一个(基于 I-JSON [RFC7493] 的)建议, 用于在 JSON 转换为 CBOR 时处理这些类型,并 推荐使用特定舍入机制。

对于数据模型中的单个值,CBOR 通常提供多个 编码选项。新增章节(第 4 节)引入术语 “首选序列化”(第 4.1 节),并为 各类 数据项定义该术语。基于此术语,该章节 随后讨论基于 CBOR 的协议如何定义“确定性 编码”(第 4.2 节),并避免使用 RFC 7049 中的术语“canonical”和“canonicalization”。“核心 确定性编码要求”(第 4.2.1 节) 的建议使得可对这种由协议定义的编码要求提供通用 支持。本文档进一步通过将 RFC 7049 中建议的映射排序 简化为已编码键的简单 字典序排序,降低了确定性编码的实现难度。较旧 建议的描述作为替代保留,现在称为“长度优先映射键 排序”(第 4.2.3 节)。

良构和有效数据的术语得到强化并被更 严格地使用,避免在示例之外使用 “syntax error”、“decoding error”和“strict mode”等定义较差的替代术语。 此外,现在明确指出了 应用对其输入数据提出的、超出 CBOR 层级有效性的第三层要求。 良构(总体上可处理)、有效(由 有效性检查通用解码器检查)和预期输入(由 应用检查)被视为可接受性层级体系。

非良构简单值的处理已在文本 和伪代码中澄清。添加了 附录 F 以讨论 良构性 错误并为其提供示例。伪代码已更新为 更具可移植性,并添加了一些可移植性考虑。

有效性讨论在两个方面得到强化。映射 有效性(重复键处理)得到澄清,并解释了某些实现选择的适用 范围。此外, 在精简标签、标签号和标签 内容术语的同时,添加了关于标签有效性的讨论,并澄清了 标签内容的一般限制以及专门针对标签 1 的限制。

第 3.4 节 中添加了一条实现说明 (以及面向未来标签定义的说明),关于定义语义依赖于 序列化顺序的标签。

标签 35 不由本文档定义;基于 RFC 7049 中定义的注册仍然保留。

第 3 节 中引入了 “实参”和“头部” 术语,简化了后续讨论。

安全考虑事项(第 10 节) 基本被重写并显著 扩展;在多个其他位置,本文档现在更明确地说明 解码器不能简单地容忍良构性错误。

致谢

CBOR 受 MessagePack 启发。MessagePack 由 Sadayuki Furuhashi(“frsyuki”)开发并推广。此处对 MessagePack 的引用仅用于署名;CBOR 并不打算作为 MessagePack 的一个版本或替代品,因为它具有不同的设计 目标和要求。

大约在 2012 年前后,许多人同时明显意识到 需要超出原始 MessagePack 规范的功能。BinaryPack 是 MessagePack 的一个小型派生, 由 Eric Zhang 为 binaryjs 项目开发。Tim Caswell 为他的 msgpack-js 和 msgpack-js-browser 项目制作了一个类似但不同的扩展。 许多人参与了关于扩展 MessagePack 以将文本字符串 表示与字节串表示分离的 讨论。

CBOR 中附加信息的编码受 Klaus Hartke 为 CoAP 设计的长度信息编码启发。

本文档还吸收了许多人提出的建议, 尤其包括 Dan FrostJames MangerJeffrey YasskinJoe HildebrandKeith MooreLaurence LundbladeMatthew LepinskiMichael RichardsonNico WilliamsPeter OccilPhillip Hallam-BakerRay PolkStuart CheshireTim BrayTony FinchTony HansenYaron ShefferBenjamin Kaduk 在 IESG 处理期间提供了广泛审阅。 Éric VynckeErik KlineRobert WiltonRoman Danyliw 提供了进一步的 IESG 评论,其中包括 Eve Schooler 的 IoT directorate 审阅。

作者地址

Carsten Bormann
Universität Bremen TZI
Postfach 330440
D-28359 Bremen
德国
Paul Hoffman
ICANN