| RFC 8949 | CBOR | 2020年12月 |
| Bormann & Hoffman | 标准轨道 | [页] |
简明二进制对象表示(CBOR)是一种数据格式,其设计 目标包括 实现极小代码大小、相当小的消息大小,以及 无需版本协商的 可扩展性。这些设计目标使其不同于早期的 二进制 序列化格式,例如 ASN.1 和 MessagePack。¶
本文档废止 RFC 7049,在保持与 RFC 7049 交换格式完全兼容的同时,提供编辑性改进、新的 细节和勘误修正。它不会创建该格式的 新版本。¶
这是一份互联网标准轨道文档。¶
本文档是互联网工程任务组 (IETF)的产物。它代表了 IETF 社区的共识。它已经 接受公开审查,并已由 互联网工程指导组(IESG)批准发布。关于 互联网标准的更多信息见 RFC 7841 第 2 节。¶
关于本文档的当前状态、任何 勘误以及如何提供反馈的信息,可以在 https://www.rfc-editor.org/info/rfc8949 获取。¶
Copyright (c) 2020 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.¶
用于结构化数据的二进制表示的标准化格式 有数百种(也称为二进制序列化格式)。在 这些格式中,有些面向特定信息领域,而另一些则 泛化用于任意数据。在 IETF 中,后者类别中可能最知名的 格式是 ASN.1 的 BER 和 DER [ASN.1]。¶
此处定义的格式遵循一些特定设计目标,而这些目标 目前的格式并未很好满足。底层数据模型是 JSON 数据模型 [RFC8259] 的扩展版本。需要 注意的是, 这并不是建议普遍扩展 RFC 8259 中的语法, 因为这样做会与已经部署的 JSON 文档产生重大的向后不兼容。相反,本文档只是 定义了自己的数据模型, 该模型从 JSON 出发。¶
附录 E 列出了一些现有的二进制格式 并讨论 它们在多大程度上适合或不适合简明 二进制对象表示(CBOR)的设计目标。¶
本文档废止 [RFC7049], 在保持与 RFC 7049 交换格式完全兼容的同时,提供编辑性改进、新的 细节和勘误修正。它不会创建该格式的 新版本。¶
CBOR 的目标大致按重要性递减顺序 如下:¶
该表示必须能够无歧义地编码大多数 互联网标准中使用的 常见数据格式。¶
编码器或解码器的代码必须能够足够紧凑, 以支持内存、处理器能力 和指令集都非常受限的系统。¶
数据必须能够在没有模式描述的情况下被解码。¶
序列化必须相当紧凑,但对于编码器和解码器而言, 数据紧凑性次于代码紧凑性。¶
该格式必须既适用于受限节点,也适用于 高容量应用。¶
该格式必须支持所有 JSON 数据类型,以便在 JSON 之间 相互转换。¶
该格式必须是可扩展的,并且扩展后的数据必须能够被 早期解码器解码。¶
本文档中的关键词“MUST”、“MUST NOT”、 “REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、 “RECOMMENDED”、“NOT RECOMMENDED”、 “MAY”和“OPTIONAL”应按照 BCP 14 [RFC2119] [RFC8174] 中所述进行解释,但仅当它们像这里所示以全大写形式出现时才如此。¶
术语“byte”按其现在的惯常含义使用,作为 “octet”的同义词。所有多字节值都按网络字节序编码 (即最高有效字节在前,也称为“big-endian”)。¶
本规范使用以下术语:¶
诸如 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 字体;在纯
文本中,这通过用双引号包围
文本来近似表示,但这种方式有些歧义(双引号也保留其通常含义)。¶
CBOR 明确说明其通用数据模型,该模型定义了 CBOR 中可表示的所有数据项的集合。其基本通用 数据模型可以通过注册“简单值”和 标签来扩展。应用随后可以创建所得扩展通用 数据模型的一个子集,以构建其特定数据模型。¶
在能够表示通用数据模型中数据项的环境中, 可以实现通用 CBOR 编码器和解码器 (这通常涉及为那些在环境中尚无自然表示的 数据项定义额外的实现数据类型)。 提供通用编码器和 解码器的能力是 CBOR 的一个明确设计目标;然而,许多应用 会提供自己的应用特定编码器和/或解码器。¶
在 第 3 节 中定义的基本(未扩展)通用数据模型中,数据项是 以下之一:¶
请注意,在此模型中,整数和浮点值是不同的, 即使它们具有相同的数值。¶
还请注意,序列化变体在通用 数据模型层级不可见。这种刻意的不可见性包括已编码 浮点值的字节数。它也包括“实参”(参见 第 3 节)的编码选择,例如 整数的编码、文本或字节串长度的编码、数组中元素数 或映射中成对数的编码,或标签号的编码。¶
本文档已经通过注册 一些简单值和标签号来扩展此基本通用数据模型,例如:¶
false、true、null 和 undefined
(由 20..23 标识的简单值,第 3.3 节)¶
扩展通用数据模型的其他元素可以(并且已经) 通过为 CBOR 创建的 IANA 注册表来定义。即使 通用编码器或解码器不知道这样的扩展, 使用该扩展的数据项也可以通过 在应用接口处将它们表示在基本 通用数据模型内,即作为通用简单值或 通用标签,在应用之间传递。¶
换言之,基本通用数据模型按 本文档定义是稳定的,而扩展通用数据模型通过 注册新的简单值或标签号而扩展,但永不缩小。¶
虽然强烈期望通用编码器和解码器
能够以适合其编程环境的形式表示 false、true 和 null
(undefined 被有意
省略),但由标签创建的数据模型扩展的实现确实是
可选的,并且属于实现质量问题。¶
基于 CBOR 的协议的特定数据模型通常会取 扩展通用数据模型的 一个子集,并为该子集及其组成部分中的 数据项分配应用语义。 在记录此类特定数据模型并指定数据项的类型时, 最好使用其 通用数据模型名称(“负整数”、“数组”)来识别类型,而不是 引用其 CBOR 表示的方面(“主类型 1”、 “主类型 4”)。¶
特定数据模型还可以指定值等价性(包括
不同类型的值),用于映射键和编码器自由度。例如,
在通用数据模型中,有效映射 MAY 同时具有
0 和
0.0 作为键,并且编码器 MUST NOT 将
0.0 编码为整数
(主类型 0,第 3.1 节)。然而,如果特定数据
模型
声明整数值的浮点表示和整数表示
是等价的,则在单个
映射中同时使用映射键 0 和 0.0 会被认为
是重复的,即使它们被编码为不同主类型,因此是无效的;并且编码器可以
将整数值
浮点数编码为整数,反之亦然,也许是为了节省已编码字节。¶
CBOR 数据项(第 2 节)被编码为 或从 承载良构已编码数据项的字节串中解码,如本节所述。编码在 附录 B 的 表 7 中按初始字节索引进行了总结。 编码器 MUST 只生成良构的 已编码数据项。解码器 MUST NOT 在遇到非良构的已编码 CBOR 数据项输入时 返回已解码的数据项(这并不 削弱诊断和恢复工具的有用性,这些工具 可能从损坏的已编码 CBOR 数据项中提供一些信息)。¶
每个已编码数据项的初始字节同时包含 关于主类型的信息(高 3 位,见 第 3.1 节)和附加信息(低 5 位)。 除少数例外外,附加信息的值 描述如何装载一个无符号整数“实参”:¶
初始字节以及为构造 实参而消耗的任何附加字节统称为数据项的头部。¶
此实参的含义取决于主类型。 例如,在主类型 0 中,实参就是数据 项本身的值(而在主类型 1 中,数据项的值 由实参计算得出);在主类型 2 和 3 中,它给出随后 字符串数据的字节长度;而在主类型 4 和 5 中,它用于 确定所包含的数据项数量。¶
如果已编码字节序列在数据项结束前终止, 则该项不是良构的。如果最外层已编码项被解码后, 已编码 字节序列仍有剩余字节,则该编码不是 单个良构 CBOR 项。根据应用,解码器可以 将该编码视为非良构,或者只是向应用标识 剩余字节的起始位置。¶
CBOR 解码器实现可以基于一个包含 初始字节所有 256 个已定义值的跳转表(表 7)。受限实现中的解码器 也可以改为使用初始字节和后续字节的结构, 以获得更紧凑的代码(参见 附录 C 以大致了解其可能的样子)。¶
下面列出主类型以及与该类型关联的附加信息和 其他字节。¶
这八个主类型形成一个简单表,显示数据项初始字节 256 个可能值中的哪些被使用 (表 7)。¶
在主类型 6 和 7 中,许多可能值保留供 未来规范使用。有关这些 值的更多信息,请参见 第 9 节。¶
表 1 总结了 CBOR 定义的主 类型, 暂时忽略 第 3.2 节。此表中的数字 N 表示实参。¶
| 主类型 | 含义 | 内容 |
|---|---|---|
| 0 | 无符号整数 N | - |
| 1 | 负整数 -1-N | - |
| 2 | 字节串 | N 字节 |
| 3 | 文本字符串 | N 字节(UTF-8 文本) |
| 4 | 数组 | N 个数据项(元素) |
| 5 | 映射 | 2N 个数据项(键/值对) |
| 6 | 编号为 N 的标签 | 1 个数据项 |
| 7 | 简单/浮点 | - |
四种 CBOR 项(数组、映射、字节串和文本字符串)可以 使用附加信息值 31 以不定长度进行编码。 如果某个项的编码需要在数组或映射内部项数, 或字符串总长度尚未知之前开始, 这会很有用。(在该数据项内部,能够在 全部内容已知之前开始发送数据 项的能力通常称为“流式传输”。)¶
不定长数组和映射的处理方式不同于 不定长字符串(字节串和文本字符串)。¶
“break”停止码用主类型 7 和附加 信息值 31(0b111_11111)编码。它本身不是数据项: 它只是用于关闭不定长项的语法特性。¶
如果“break”停止码出现在预期为数据项的位置, 而不是直接位于不定长字符串、数组或 映射内部——例如,直接位于定长数组或映射 内部——则包含它的项不是良构的。¶
不定长数组和映射使用其主 类型加附加信息值 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”
¶
不定长字符串由一个字节表示,该字节包含 字节串或文本字符串的主类型,并带有附加 信息值 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。¶
表 2 总结了 CBOR 定义的主类型在 不定长编码中(附加信息设为 31)如何使用。¶
| 主类型 | 含义 | 包含到“break”停止 码为止 |
|---|---|---|
| 0 | (非良构) | - |
| 1 | (非良构) | - |
| 2 | 字节串 | 定长字节串 |
| 3 | 文本字符串 | 定长文本字符串 |
| 4 | 数组 | 数据项(元素) |
| 5 | 映射 | 数据项(键/值对) |
| 6 | (非良构) | - |
| 7 | “break”停止码 | - |
主类型 7 用于两类数据:浮点数和 不需要任何内容的“简单值”。初始字节中 5 位 附加信息的每个值都有其各自的 含义,如 表 3 所定义。与整数的主类型一样, 此主类型的项不携带内容数据;所有 信息都在初始字节(头部)中。¶
| 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 列出 已分配和可用于简单值的数值。¶
编码器 MUST NOT 发出以
0xf8(主类型 7,附加信息 24)开头,并继续
以小于 0x20(十进制 32)的字节结尾的双字节序列。这样的序列不是
良构的。(这意味着编码器不能将 false、true、
null 或 undefined 编码为双字节序列,并且这些值只有单字节
变体是良构的;更一般地说,每个
简单值只有一个表示变体。)¶
5 位值 25、26 和 27 分别用于 16 位、32 位和 64 位 IEEE 754 二进制浮点值 [IEEE754]。这些浮点值 被编码在适当大小的附加字节中。(有关 16 位浮点数的一些信息,参见 附录 D。)¶
对于数据模型层级的一些值,CBOR 提供了多种 序列化。 对许多应用来说,期望编码器始终选择 首选序列化(首选编码);然而,本规范并不 将强制执行这种偏好的负担加在编码器或解码器身上。¶
一些受限解码器解码 非首选序列化的能力可能有限:例如,如果某个应用中只预期 小于 1_000_000_000(十亿)的整数, 解码器可以省略解码整数中 64 位 实参所需的代码。始终使用首选 序列化的编码器(“首选编码器”)可以与此解码器针对该应用中可能出现的 数字进行互操作。一般 来说,首选编码器比那种例如始终使用 64 位 整数的编码器更具有普遍互操作性(并且 也更少浪费)。¶
类似地,受限编码器所支持的 表示变体种类可能有限,以至于它不会 发出首选序列化(“变体编码器”)。例如,受限编码器可以 被设计为 对其编码的整数始终使用 32 位变体,即使 有更短的表示可用(假定应用并不需要只能 用 64 位变体表示的整数)。 因此,不依赖于 只接收首选序列化的解码器(“容忍变体的解码器”)可以说 具有更 普遍的互操作性(尽管它很可能仍然针对 接收首选序列化的情况进行优化)。 CBOR 解码器的完整实现按定义 容忍变体;只有在受限 CBOR 解码器实现遇到变体编码器时,这一区分才相关。¶
首选序列化始终使用表示实参的最短形式 (第 3 节);它还使用能够保留被编码值的最短 浮点编码。¶
浮点值的首选序列化,是能够保留其值的最短 浮点编码,例如数字 5.5 为 0xf94580, 数字 5555.5 为 0xfa45ad9c00。对于 NaN 值,如果将较短有效数向右用零填充能够重构原始 NaN 值, 则首选较短编码 (对于许多应用,单一 NaN 编码 0xf97e00 即已足够)。¶
只要在项的序列化开始时长度已知, 就首选定长编码。¶
一些协议可能希望编码器只发出某种特定 确定性格式的 CBOR;这些协议也可能让解码器检查 其输入是否处于该确定性格式中。这些协议 可以自由定义它们所说的“确定性格式”是什么, 以及期望编码器和解码器 做什么。本节定义了一组可 作为这种确定性格式基础的限制。¶
如果 CBOR 编码满足以下限制, 则它满足“核心确定性编码 要求”:¶
必须使用首选序列化。特别是,这意味着 整数、主类型 2 到 5 中的长度以及标签的实参(见 第 3 节) MUST 尽可能短, 例如:¶
浮点值也 MUST 使用能够保留 其值的最短形式,例如,1.5 编码为 0xf93e00(binary16),1000000.5 编码为 0xfa49742408(binary32)。 (一种实现方式是让所有浮点数先作为 64 位 浮点数开始,然后测试转换为 32 位浮点数;如果结果是 相同数值,则使用较短形式,并以测试转换为 16 位浮点数 重复该过程。这同样适用于为正 Infinity 和负 Infinity 选择 16 位浮点数。)¶
每个映射中的键 MUST 按其确定性 编码的逐字节字典序排序。例如,以下键 排序正确:¶
CBOR 标签为确定性 编码带来额外考虑。如果基于 CBOR 的协议要为 某个特定标签的存在和不存在提供相同语义 (例如,允许日期/时间位置中既出现标签 1 数据项,也出现原始数字, 并将后者视为带有标签),那么基于“最短形式”原则, 确定性格式将不允许该标签存在。 例如,协议可能让编码器选择将 URL 表示为 文本字符串,或者使用 第 3.4.5.3 节 中包含文本字符串的标签号 32。 该协议的确定性编码需要要么 要求该标签存在,要么要求其不存在,而不能 允许两者任选其一。¶
在确实要求某些位置存在标签以 获得特定语义的协议中,该标签也需要出现在 确定性格式中。确定性编码考虑事项 也适用于标签内容。¶
如果协议包含一个字段,可以使用标签号 2 或 3 表达绝对值为 264 或更大的整数 (第 3.4.3 节),则该协议的确定性 编码需要指定 较小整数是否也使用这些标签表示,还是使用 主类型 0 和 1。首选序列化使用后一种选择, 因此建议采用后一种。¶
包含浮点值的协议,无论这些浮点值是用 基本浮点值(第 3.3 节)表示,还是 使用标签(或 二者兼有)表示,都可能需要为其确定性 编码定义额外要求,例如:¶
如果协议包含一个能够表达 浮点值的字段, 且其特定数据模型声明整数值和 浮点值可互换,则该协议的 确定性编码需要指定, 例如,整数 1.0 是编码为 0x01(无符号 整数)、0xf93c00(binary16)、0xfa3f800000(binary32), 还是 0xfb3ff0000000000000(binary64)。示例规则如下:¶
规则 1 跨越整数和浮点 值之间的边界,而规则 3 不使用首选序列化,因此规则 2 在许多情况下可能是 一个不错的选择。¶
CBOR 这样的数据格式经常用于没有 格式协商的环境。CBOR 的一个特定设计目标是 不需要任何包含的或假定的模式:解码器可以取得一个 CBOR 项, 并在没有其他知识的情况下解码它。¶
当然,在现实世界实现中,编码器和解码器 会对 CBOR 数据项中应当有什么拥有共同理解。例如, 约定的格式可能是“该项是一个数组,其 第一个值是 UTF-8 字符串,第二个值是整数,后续 值是零个或多个浮点数”,或“该 项是一个映射,使用字节串作为键,并包含一个 键为 0xab01 的对”。¶
基于 CBOR 的协议 MUST 指定其解码器如何处理 无效数据和其他意外数据。基于 CBOR 的协议 MAY 指定它们将任意有效数据视为意外数据。 基于 CBOR 的协议的编码器 MUST 只生成有效项, 也就是说,协议不能被设计为利用无效项。编码器 可以具备编码其所用协议要求的尽可能多或尽可能少类型值的能力; 解码器可以具备理解其所用协议要求的尽可能多或尽可能少类型值的 能力。这种缺乏 限制使 CBOR 能用于极其受限的 环境。¶
本节其余部分讨论创建基于 CBOR 的 协议时的一些考虑事项。除少数例外外,这些内容仅具建议性,并明确排除 BCP 14 [RFC2119] [RFC8174] 中除可按 BCP 14 含义解释为“MAY”的词语之外的任何用语。 这些例外旨在促进 基于 CBOR 的协议的互操作性,同时利用多种 通用和应用特定的编码器与解码器。¶
在流式应用中,数据流可以由 一系列首尾相接串接的 CBOR 数据项组成。在这样的 环境中,如果在前一个数据项结束之后发现数据, 解码器会立即开始解码新的数据项。¶
构成数据项的字节并不一定全部立即 可供解码器使用;一些解码器会缓冲额外数据, 直到可以向应用呈现完整数据项。其他 解码器可以向应用呈现顶层数据项的部分信息, 例如已经能够被解码的嵌套数据项, 甚至是尚未完全到达的字节串的部分内容。 这样的应用也 MUST 具有匹配的流式安全 机制,其中 所需保护可用于呈现给 应用的增量数据。¶
请注意,一些应用和协议不会想要使用 不定长编码。使用不定长编码允许 编码器无需整理全部数据以便计数,但它 要求解码器在等待项结束时分配越来越多的内存。 这对某些应用可能没问题, 但对其他应用则不然。¶
通用 CBOR 解码器可以解码所有良构的已编码 CBOR 数据项, 并将这些数据项呈现给应用。参见 附录 C。 (诊断记法,第 8 节,可用于 向人呈现良构的 CBOR 值。)¶
通用 CBOR 编码器提供一个应用接口,允许 应用指定任何良构值以编码为 CBOR 数据项,包括编码器未知的简单值和标签。¶
虽然 CBOR 试图最小化这些情况,但并非所有良构的
CBOR 数据都是有效的:例如,已编码文本字符串 0x62c0ae
不包含有效 UTF-8(因为 [RFC3629]
要求始终使用最短
形式),因此不是有效的 CBOR 项。
此外,特定标签可能
设置可能被违反的语义约束,例如,大数标签
包围另一个标签,或标签号 0 的实例包含字节
串,或包含内容不匹配
[RFC3339] 的 date-time 产生式的文本字符串。
不要求通用编码器和解码器为其应用接口作出不自然的
选择,以便处理
无效数据。通用编码器和解码器应转发
简单值和标签,即使其特定码点在编写编码器/解码器时
尚未注册
(第 5.4 节)。¶
良构但无效的 CBOR 数据项(第 1.2 节)会在 解释其中编码的数据在 CBOR 数据模型中的含义时造成问题。基于 CBOR 的协议可以分若干层指定,其中 较低层不处理其转发的某些 CBOR 数据的语义。 这些层无法注意到它们不处理的数据中的任何有效性错误, 并且 MUST 按原样转发这些数据。第一个 处理无效 CBOR 项语义的层 MUST 选择以下两种 方案之一:¶
基于 CBOR 的协议 MUST 指定其解码器 对可能遇到的每一种无效项采用这些选项中的哪一种。¶
此类问题可能发生在 CBOR 的基本有效性层级, 或发生在标签的上下文中(标签有效性)。¶
通过向基本通用数据模型添加 标签,会引入另外两类有效性错误:¶
带有效性检查的解码器会付出努力来可靠地 检测具有有效性错误的数据项。例如,这样的 解码器需要有一个 API,用于对包含前一小节所列任一有效性 错误的 CBOR 数据项报告错误(且不 返回数据)。¶
“Concise Binary Object Representation (CBOR) Tags”注册表(第 9.2 节)中定义的标签集合, 以及“Concise Binary Object Representation (CBOR) Simple Values” 注册表(第 9.1 节)中定义的简单值集合, 可以在任何时候增长,超出通用解码器所理解的集合。当 有效性检查解码器遇到 其不识别的此类情况时,可以采取以下两种做法之一:¶
后一种做法同样适用于不 支持有效性检查的解码器,它提供了对 新注册标签和简单值的前向兼容性,而不要求 编码器与调用应用同时更新。(为此, 解码器的 API 需要具备标记未知 项的能力,使调用应用能够以适合程序的方式 处理它们。)¶
由于有效性检查所需的某些处理可能具有 可观成本(尤其是映射的重复检测), 支持有效性检查并不是对所有 CBOR 解码器提出的要求。¶
一些编码器会依赖其应用以 使编码器产生有效 CBOR 的方式提供输入数据。通用 编码器也可能希望提供一种有效性检查模式,在其中它 可靠地将其输出限制为有效 CBOR,而不取决于 其应用是否确实提供符合 API 的数据。¶
基于 CBOR 的协议应考虑到不同语言 环境对可表示数字的范围和精度 有不同限制。例如,基本 JavaScript 数字 系统将所有数字视为浮点值,这可能导致 解码超过 53 个有效位的整数时静默 丢失精度。 另一个例子是,由于 CBOR 将其整数 表示中的符号位保留在主类型中,因此,对于某一长度的有符号 数字,它比同长度的典型平台有符号整数表示 多一位(例如,1+8 字节 整数为 -264..264-1,而 8 字节 int64_t 为 -263..263-1)。 使用数字的协议应定义其 对解码器和接收应用处理中非平凡数字的 期望。¶
包含浮点数的基于 CBOR 的协议可以 限制三种格式(半精度、单精度 和双精度)中的哪些需要被支持。对于仅整数 应用,协议可能希望完全排除 浮点值的使用。¶
为紧凑性设计的基于 CBOR 的协议,可能希望排除 对应用来说长于必要长度的特定整数编码, 例如为了省去实现 64 位整数的需要。 预期编码器会使用能够表示给定值的最紧凑 整数表示。然而,不要求确定性编码的 紧凑应用应接受使用长于必要长度 编码的值(例如,将“0”编码为 0b000_11001 后跟两个字节 0x00),只要该应用能够解码给定 大小的整数。 类似考虑也适用于浮点值;建议同时解码 首选序列化和长于必要长度的序列化。¶
面向受限应用的基于 CBOR 的协议,如果在将特定数字表示为整数 与表示为十进制小数或大浮点数之间提供 选择(例如,当指数较小且非负时),可能会表达一种实现质量 期望,即直接使用整数表示。¶
编码和解码应用需要就映射中将使用哪些类型的 键达成一致。在需要与基于 JSON 的应用 互操作的应用中,通过将键限制为仅文本字符串,可以简化转换; 否则,就必须指定从其他 CBOR 类型到文本字符串的 映射,而这 经常导致实现错误。在键本质上是 数值,并且键的数值排序对应用很重要的应用中, 直接使用数字作为键是有用的。¶
如果要使用多种类型的键,应考虑 这些类型如何在将要使用的特定 编程环境中表示。例如,在 JavaScript Maps [ECMA262] 中,整数键 1 不能与浮点键 1.0 区分。这意味着,如果使用整数 键,则协议需要避免在同一映射中使用 值恰好为整数的浮点键。¶
在解码嵌套于 CBOR 数据项中的数据项时立即交付这些项的解码器 (“流式解码器”)通常不保留 用于确定映射中键唯一性所必需的状态。 类似地,能够在包含的数据项尚未完全可用之前 开始编码数据项的编码器(“流式编码器”)可能 希望通过依赖其数据 源维护唯一性来显著降低其开销。¶
基于 CBOR 的协议 MUST 定义接收应用在 映射中看到多个相同键时 要做什么。协议中的结果规则 MUST 尊重 CBOR 数据模型:它不能规定对具有 相同键的条目进行特定处理,除非它可以规定 映射中具有相同键表示该映射格式错误,且解码器 必须以错误停止。 在处理表现出重复键条目的映射时,通用 解码器可能会执行以下操作之一:¶
通用解码器需要记录它们实现了这三种方法中的哪一种。¶
CBOR 的映射数据模型不允许给映射表示中 键/值对的顺序赋予语义。因此, 基于 CBOR 的协议 MUST NOT 指定改变映射中键/值对的 顺序会改变语义,除非是指定某些 顺序被禁止,例如它们不满足确定性 编码(第 4.2 节) 的要求。 (映射排序的任何次要影响,例如对时序、缓存使用 和其他潜在侧信道的影响,并不被视为 语义的一部分,但其本身可能足以成为协议要求 确定性编码格式的理由。)¶
受限设备上的应用应考虑使用小 整数作为键,如果它们的映射具有少量经常 使用的键;例如,一组 24 个或更少的 键可以作为无符号整数编码在单个字节中,如果还使用负 整数,则最多可达 48 个。不太频繁 出现的键随后可以使用更长编码的整数。¶
适用于 CBOR 数据项的特定数据模型用于 确定映射中出现的键是重复还是不同。¶
在通用数据模型层级,数值等价的整数和 浮点值彼此不同,也不同于 各种大数(标签 2 到 5)。类似地,文本字符串 与字节串不同,即使由相同字节组成。带标签值 不同于无标签值,也不同于带有不同标签号的值。¶
在每个这些组内,数值只有在数值相等时才不相同 (具体而言,-0.0 等于 0.0);对于 映射键等价性的目的,NaN 值在将两个有效数向右零扩展到 64 位后 具有相同有效数时等价。¶
字节串和文本字符串都逐字节比较, 数组逐元素比较;如果它们具有相同数量的字节/元素,并且相同 位置上的值相同,则它们相等。两个映射如果具有相同的对集合, 不论顺序如何,都相等;如果键和值均相等,则对相等。¶
带标签值在标签号和标签内容 都相等时相等。 (请注意,提供特定标签处理的通用解码器可能无法区分 某些语义等价 值,例如,如果标签 2 或标签 3 的内容中出现前导零 (第 3.4.3 节)。) 简单值在仅具有相同值时相等。 在通用数据模型中没有其他东西相等;简单值 2 不等价于整数 2,数组也永远不等价于映射。¶
如 第 2.2 节 所讨论,特定数据模型可以 出于比较映射键的目的,使在通用数据模型中不同的值 等价。请注意,这意味着 通用解码器可能会向应用交付一个已解码映射,而该应用需要 检查其中是否存在重复映射键 (或者,解码器可以提供一个编程接口, 为应用执行此服务)。特定数据模型 无法区分在通用数据模型层级上就此目的而言相等的映射键值。¶
本节给出关于在 CBOR 和 JSON 之间转换的非规范性建议。转换器实现 MAY 使用这里的 任何建议。¶
值得注意的是,JSON 文本是字符序列,而不是 已编码的字节序列;而 CBOR 数据项由 字节组成,而不是字符。¶
CBOR 中的大多数类型在 JSON 中都有直接对应物。然而,有些 没有,而实现 CBOR 到 JSON 转换器的人必须 考虑在这些情况下该怎么做。以下非规范性建议 通过将它们转换为单一替代值来处理这些情况,例如 JSON null。¶
CBOR 到 JSON 转换器可能希望遵循 JSON 配置文件 I-JSON [RFC7493],以最大化互操作性并 增强信心, 使 JSON 输出能够以可预测结果处理。例如, 这会影响可被可靠表示的整数范围, 以及旧版 JSON 实现可能支持的顶层项。¶
所有 JSON 值一旦被解码,就会直接映射到一个或多个 CBOR 值。与任何类型的 CBOR 生成一样,必须 就数字表示作出决定。在一种建议的 转换中:¶
CBOR 的设计目标通常是提供比 JSON 更紧凑的编码。 一种可能想到的实现策略是 在单个缓冲区中就地执行 JSON 到 CBOR 编码。该 策略需要仔细考虑许多病态 情况,例如某些字符串在没有转义或转义很少的情况下表示, 且长度超过(或远远超过)255 字节,在 CBOR 中编码为 UTF-8 字符串时可能会扩展。类似地,少数二进制 浮点表示可能会从 JSON 中某些较短的 十进制表示(1.1、1e9)扩展。这可能很难 正确处理,随之产生的任何漏洞都可能被 攻击者利用。¶
成功的协议会随时间演进。新想法出现, 实现平台改进,相关协议被开发并 演进,来自应用和协议的新需求也被 添加。因此,促进协议演进是任何协议开发中的重要 设计考虑事项。¶
对于将使用 CBOR 的协议,CBOR 提供了一些有用机制 来促进其演进。这方面的最佳实践众所 周知,尤其来自基于 JSON 协议的 JSON 格式开发。因此,此类最佳实践不属于 本规范的范围。¶
然而,促进 CBOR 本身的演进完全属于 其范围。CBOR 被设计为既为 基于 CBOR 的协议开发提供稳定基础,又能够演进。由于 成功的协议可能存在数十年,CBOR 需要被设计为可供 数十年使用和演进。本节为 CBOR 的演进提供一些指导。与 本文档的其他部分相比,它必然更加主观。它也必然是不完整的, 以免变成协议开发教科书。¶
在协议设计中,演进机会通常以 扩展点的形式包含在内。例如,可能有一个 从一开始并未完全分配的码点空间,并且 协议被设计为能够容忍并接纳开始使用比最初分配更多码点的 实现。¶
确定码点空间的大小可能很困难,因为所需范围 可能难以预测。协议设计应尝试使 码点空间足够大,以便它能够在协议预期寿命内 逐渐被填充。¶
CBOR 有三个主要扩展点:¶
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 提供文档支持。¶
有时在诊断记法中指示实际使用了 若干替代表示中的哪一种是有用的;例如,诊断解码器写作 >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)方面产生歧义,因此不使用。 可以改用基本形式 ''_ 和 ""_,且它们仅保留用于 没有块的情况——不是仅含空块的(允许但 并不真正有用的)编码的短形式;后者 需要记写为 (_ '')、(_ "") 等, 以保留块结构。¶
IANA 已为新的 CBOR 值创建了两个注册表。这些注册表 是分开的,也就是说,并不位于总括注册表之下,并遵循 [RFC8126] 中的规则。IANA 还 分配了新的媒体类型、一个关联的 CoAP Content-Format 条目以及一个结构化语法后缀。¶
IANA 已在 [IANA.cbor-simple-values] 创建了“Concise Binary Object Representation (CBOR) Simple Values”注册表。初始值见 表 4。¶
范围 0 到 19 中的新条目由 Standards Action [RFC8126] 分配。 建议 IANA 从数字 16 开始分配值, 以便将较低数字保留给 连续块(如果有)。¶
范围 32 到 255 中的新条目由 Specification Required 分配。¶
单个已编码 CBOR 数据 项的互联网媒体类型 [RFC6838](“MIME type”)是 “application/cbor”,如“Media Types”注册表 [IANA.media-types] 中所定义:¶
CBOR 的 CoAP Content-Format 已注册在 “Constrained RESTful Environments (CoRE) Parameters”注册表内的“CoAP Content-Formats”子注册表中 [IANA.core-parameters]:¶
基于单个 已编码 CBOR 数据项的媒体类型的结构化语法后缀 [RFC6838] 是 +cbor,IANA 已将其注册在 “Structured Syntax Suffixes”注册表 [IANA.structured-suffix] 中:¶
为 +cbor 指定的片段标识符的语法和语义 SHOULD 与为 “application/cbor” 指定的相同。(在 RFC 8949 发布时,尚未为 “application/cbor” 定义片段标识 语法。)¶
特定 “xxx/yyy+cbor”的片段标识符的语法和语义 SHOULD 按如下方式处理:¶
面向网络的应用在处理传入数据的 逻辑中可能表现出漏洞。复杂解析器众所周知 很可能成为此类漏洞的来源,例如能够 远程使节点崩溃,甚至在其上远程执行任意代码。 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 相关。¶
下表提供了一些以十六进制表示的 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'))。¶
| 诊断记法 | 已编码 |
|---|---|
| 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 |
为简洁起见,此跳转表不显示 保留供未来扩展使用的初始字节。它也只显示可用于 可选功能的初始字节中的一部分。(所有 无符号整数都采用网络字节序。)¶
| 字节 | 结构/语义 |
|---|---|
| 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”停止码 |
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; // 不定长数据项
}
请注意,完整 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
...
由于半精度浮点数直到 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;
}
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)
CBOR 提案沿袭了一段与 计算机本身历史一样长的二进制格式历史。不同格式具有 不同目标。在多数情况下,格式的目标 从未被明确说明,尽管有时可以从 该格式最初使用的上下文中推断出来。某些格式旨在 普遍可用,尽管历史已经证明,没有任何二进制格式 能满足所有协议和应用的需求。¶
CBOR 与其中许多格式不同,因为它从一组 目标出发,并试图只满足这些目标。本节将 数十种格式中的几种与 CBOR 的目标进行比较,以帮助 读者决定是否在某个特定协议或应用中使用 CBOR 或其他格式。¶
请注意,这里的讨论并不是对任何 格式的批评:据我们所知,在 CBOR 之前没有任何格式旨在 按照我们为 CBOR 目标分配的优先级来覆盖这些目标。来自 第 1.1 节 的 目标简要回顾如下:¶
[RFC8618] 的 第 5 节 和附录 C 提供了关于 CBOR 和其他格式相对于另一组设计目标的 讨论。¶
[ASN.1] 有许多 序列化格式。在 IETF 中,DER 和 BER 是 最常见的。对许多项而言,序列化输出并不特别紧凑, 并且在受限设备上解码数字项所需的代码可能很复杂。¶
很少(如果有的话)IETF 协议采用 Packed Encoding Rules (PER)的若干变体之一。原因可能有很多, 但常见说法之一是 PER 即使在解析数据项的表层结构时 也会使用模式, 因而需要大量工具支持。目前使用着 不同版本的 ASN.1 模式语言,这也阻碍了采用。¶
[MessagePack] 是一种 简明、广泛实现的计数式二进制 序列化格式,在许多属性上与 CBOR 相似,尽管 稍微不那么规则。虽然其数据模型可以用于表示 JSON 数据,MessagePack 也已用于许多远程过程 调用(RPC)应用以及数据的长期存储。¶
MessagePack 自约 2011 年首次发布以来基本保持稳定; 它尚未经历过一次迁移。MessagePack 的演进 受制于保持与已有存储数据完全向后 兼容的强制要求,而可供扩展的字节码只剩很少。 多年来,MessagePack 用户社区反复请求 在编码中分离二进制字符串和文本字符串, 最近促成了一个扩展提案,但该提案会让 MessagePack 的“raw”数据在二进制 和文本数据用途之间保持歧义。MessagePack 的扩展机制仍然 不清楚。¶
[BSON] 是一种为在 MongoDB 数据库中存储类 JSON 映射(JSON 对象) 而开发的数据格式。它的主要 区分特征是支持就地更新, 这阻止了紧凑表示。除了映射键之外,BSON 使用计数式 表示,而映射键以空字节终止。 虽然 BSON 可用于在线路上表示类 JSON 对象, 但其规范受数据库应用需求支配,并已变得有些繁复。 BSON 扩展将如何实现的状态仍不清楚。¶
Message Services Data Transmission(MSDTP)是 紧凑消息格式的一个非常早期示例;它在 1976 年编写的 [RFC0713] 中描述。 它包含在这里是出于历史价值,而不是因为它 曾经被广泛使用。¶
虽然 CBOR 的编码器和 解码器代码紧凑性设计目标优先级高于其在线路上简洁性的 目标,许多人仍关注线路大小。表 8 展示了 简单嵌套数组 [1, [2, 3]] 的一些编码示例;对于 编码支持某种形式不定长编码的情况, 也展示 [_ 1, [2, 3]](外层数组为不定长)。¶
| 格式 | [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 |
解码 CBOR 数据项时可能出现三种基本 良构性错误:¶
在 附录 C 中,第一类错误 在第一段和项目列表中处理(要求“没有字节留下”),第二类错误 在第二段/项目列表中处理 (“如果 n 个字节不再可用”则失败)。第三类 错误通过伪代码中调用 fail() 的具体实例来识别,按顺序为:¶
本小节展示若干非 良构 CBOR 数据项示例。每个示例都是一个字节序列,每个字节以 十六进制显示;列表中的多个示例用逗号分隔。¶
良构性错误类型 1(数据过多)的示例,可以很容易地 通过向良构的已编码 CBOR 数据项添加数据来形成。¶
类似地,良构性错误类型 2(数据过少)的示例 可以通过截断良构的已编码 CBOR 数据项来形成。在 测试套件中,专门用需要大量 补充才能完成的不完整数据项进行测试可能是有益的 (例如,从编码一个非常 大大小的字符串开始)。¶
输入过早结束可能发生在头部中,也可能发生在所含 数据中,后者可以是裸字符串,或被包含的数据项,这些项要么 是计数的,要么本应由“break”停止码结束。¶
下面展示了良构性错误类型 3 (语法错误)五个子类的一些示例。¶
如引言中所讨论, 本文档正式废止 RFC 7049,同时保持与 RFC 7049 交换格式 完全兼容。本文档提供编辑性 改进、增加细节并修正勘误。 本文档不会创建该格式的新版本。¶
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”, 改进对称性)。¶
其他文书性变更包括:¶
IANA 考虑事项已整体更新(文书性变更, 例如现在指向 CBOR 工作组作为规范作者)。 各相应 IANA 注册表的引用被 添加到资料性参考文献中。¶
在“Concise Binary Object Representation (CBOR) Tags”注册表 [IANA.cbor-tags] 中, 从 256 到 32767(“1+2”的低 半部分)空间中的标签不再按 First Come First Served 分配;该范围 现在为 Specification Required。¶
在修订本文档时,除了处理勘误报告之外, 工作组还借鉴了 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 Frost、James Manger、 Jeffrey Yasskin、Joe Hildebrand、 Keith Moore、Laurence Lundblade、 Matthew Lepinski、Michael Richardson、 Nico Williams、Peter Occil、 Phillip Hallam-Baker、Ray Polk、 Stuart Cheshire、Tim Bray、 Tony Finch、Tony Hansen 和 Yaron Sheffer。Benjamin Kaduk 在 IESG 处理期间提供了广泛审阅。 Éric Vyncke、Erik Kline、 Robert Wilton 和 Roman Danyliw 提供了进一步的 IESG 评论,其中包括 Eve Schooler 的 IoT directorate 审阅。¶