fxamacker/cbor

GitHub: fxamacker/cbor

一个符合 RFC 8949 标准的高性能 Go 语言 CBOR 编解码库,提供安全、紧凑的二进制序列化能力。

Stars: 1069 | Forks: 81

[fxamacker/cbor](https://github.com/fxamacker/cbor) 是一个用于编码和解码 [CBOR](https://www.rfc-editor.org/info/std94) 和 [CBOR Sequences](https://www.rfc-editor.org/rfc/rfc8742.html) 的库。 CBOR 是 JSON、MessagePack、Protocol Buffers 等格式的一个[可靠替代方案](https://www.rfc-editor.org/rfc/rfc8949.html#name-comparison-of-other-binary-)。CBOR 是由 [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94) 定义的互联网标准,其设计旨在保持数十年的适用性。 `fxamacker/cbor` 被应用于 Arm Ltd.、EdgeX Foundry、Flow Foundation、Fraunhofer-AISEC、IBM、Kubernetes[*](https://github.com/search?q=org%3Akubernetes%20fxamacker%2Fcbor&type=code)、Let's Encrypt、Linux Foundation、Microsoft、Oasis Protocol、Red Hat[*](https://github.com/search?q=org%3Aopenshift+fxamacker%2Fcbor&type=code)、Tailscale[*](https://github.com/search?q=org%3Atailscale+fxamacker%2Fcbor&type=code)、Veraison[*](https://github.com/search?q=org%3Averaison+fxamacker%2Fcbor&type=code) [等](https://github.com/fxamacker/cbor#who-uses-fxamackercbor) 的项目中。 请参阅[快速开始](#quick-start)和[发布版本](https://github.com/fxamacker/cbor/releases/)。 🆕 `UnmarshalFirst` 和 `DiagnoseFirst` 可以解码 CBOR Sequences。 `MarshalToBuffer` 和 `UserBufferEncMode` 接受用户指定的 buffer。 ## fxamacker/cbor [![](https://static.pigsec.cn/wp-content/uploads/repos/cas/99/993938d8ce5e902ccfb9d6747725c320d855dea3235ed9a304cedf0d94c9321f.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3Aci) [![](https://static.pigsec.cn/wp-content/uploads/repos/cas/83/8395f88d2b369c8c01d83916daeec28849fabeaa23423fc183805f167e1ba5c9.svg)](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A597%25%22) [![CodeQL](https://static.pigsec.cn/wp-content/uploads/repos/cas/53/539e9a6bf48ad24469a4363bff3aa68124154549e26592783d3d8577f2acbbfc.svg)](https://github.com/fxamacker/cbor/actions/workflows/codeql-analysis.yml) [![](https://img.shields.io/badge/fuzzing-passing-44c010)](#fuzzing-and-code-coverage) [![Go Report Card](https://goreportcard.com/badge/github.com/fxamacker/cbor)](https://goreportcard.com/report/github.com/fxamacker/cbor) [![](https://img.shields.io/ossf-scorecard/github.com/fxamacker/cbor?label=openssf%20scorecard)](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage) `fxamacker/cbor` 是一个完全符合 [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94) 的 CBOR 编解码器。它还支持 CBOR Sequences([RFC 8742](https://www.rfc-editor.org/rfc/rfc8742.html))和扩展诊断表示法([Appendix G of RFC 8610](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G))。 API 与 `encoding/json` 基本相同,并增加了简化并发和 CBOR 选项的接口。 设计在安全性、速度、并发性、编码数据大小、可用性等方面进行了平衡与取舍。
🔎  亮点

__🚀  速度__ 在不使用 Go 的 `unsafe` 包的情况下,编码和解码速度非常快。较慢的设置是可选的。默认限制允许非常快速且高效地拒绝格式错误的 CBOR 数据。 __🔒  安全性__ 解码器具有可配置的限制,可以防御恶意输入。支持重复 map key 检测。相比之下,`encoding/gob` [并未针对对抗性输入进行强化设计](https://pkg.go.dev/encoding/gob#hdr-Security)。 该编解码器在 2022 年通过了多项机密安全评估。在 NCC Group 为 Microsoft Corporation 准备的[非机密安全评估](https://github.com/veraison/go-cose/blob/v1.0.0-rc.1/reports/NCC_Microsoft-go-cose-Report_2022-05-26_v1.0.pdf)中,该编解码器的子集未发现漏洞。 __🗜️  数据大小__ Struct tag 选项(`toarray`、`keyasint`、`omitempty`、`omitzero`)和字段 tag "-" 会自动减小编码后 struct 的大小。在值匹配的情况下,编码可以选择性地将 float64 缩小为 float32→16。 __:jigsaw:  可用性__ API 与 `encoding/json` 基本相同,并增加了简化 CBOR 选项并发的接口。可以在启动时创建编码和解码模式,并由任意 goroutine 复用。 预设包括核心确定性编码、首选序列化、CTAP2 Canonical CBOR 等。 __📆  可扩展性__ 功能包括 CBOR [扩展点](https://www.rfc-editor.org/rfc/rfc8949.html#section-7.1)(例如 CBOR tags)和丰富的设置。API 提供了接口,允许用户在不修改本库的情况下创建自定义的编码和解码。


### 通过可配置设置进行安全解码 `fxamacker/cbor` 具有可配置的限制等功能,可防御恶意的 CBOR 数据。 值得注意的是,`fxamacker/cbor` 在拒绝格式错误的 CBOR 数据时速度非常快。 相比之下,某些编解码器在解码坏数据时可能会崩溃或占用过多资源。 ### 使用 Struct Tag 选项实现更小的编码 Struct tag 会自动减小 struct 的编码大小并提高速度。 我们可以通过使用 struct tag 选项来减少代码编写量: - `toarray`:编码时不包含字段名(解码还原为原始 struct) - `keyasint`:将字段名编码为整数(解码还原为原始 struct) - `omitempty`:编码时忽略空字段 - `omitzero`:编码时忽略零值字段 作为一种特殊情况,struct 字段 tag "-" 会忽略该字段。 注意:当 struct 使用 `toarray` 时,编码器将忽略 `omitempty` 和 `omitzero`,以防止编码后数组元素的位置发生改变。这使得解码器能够将编码后的元素与其 Go struct 字段匹配。 ![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Go Struct Tags") ## 快速开始 __安装__:`go get github.com/fxamacker/cbor/v2` 并 `import "github.com/fxamacker/cbor/v2"`。 ### 关键点 该库可以编码和解码 CBOR (RFC 8949) 以及 CBOR Sequences (RFC 8742)。 - __CBOR data item__ 是单个 CBOR 数据,其结构可能包含 0 个或多个嵌套的 data item。 - __CBOR sequence__ 是 0 个或多个已编码 CBOR data item 的拼接。 可配置的限制和选项可用于平衡各项取舍。 - 编码和解码模式由选项(设置)创建。 - 可以在启动时创建模式并重复使用。 - 模式对于并发使用是安全的。 ### 默认模式 包级别的函数仅使用该库的默认设置。 它们提供了编码和解码的“默认模式”。 ``` // API matches encoding/json for Marshal, Unmarshal, Encode, Decode, etc. b, err = cbor.Marshal(v) // encode v to []byte b err = cbor.Unmarshal(b, &v) // decode []byte b to v decoder = cbor.NewDecoder(r) // create decoder with io.Reader r err = decoder.Decode(&v) // decode a CBOR data item to v // v2.7.0 added MarshalToBuffer() and UserBufferEncMode interface. err = cbor.MarshalToBuffer(v, b) // encode v to b instead of using built-in buf pool. // v2.5.0 added new functions that return remaining bytes. // UnmarshalFirst decodes first CBOR data item and returns remaining bytes. rest, err = cbor.UnmarshalFirst(b, &v) // decode []byte b to v // DiagnoseFirst translates first CBOR data item to text and returns remaining bytes. text, rest, err = cbor.DiagnoseFirst(b) // decode []byte b to Diagnostic Notation text // NOTE: Unmarshal() returns ExtraneousDataError if there are remaining bytes, but // UnmarshalFirst() and DiagnoseFirst() allow trailing bytes. ``` ### 预设 预设可以直接使用,也可以作为自定义设置的起点。 ``` // EncOptions is a struct of encoder settings. func CoreDetEncOptions() EncOptions // RFC 8949 Core Deterministic Encoding func PreferredUnsortedEncOptions() EncOptions // RFC 8949 Preferred Serialization func CanonicalEncOptions() EncOptions // RFC 7049 Canonical CBOR func CTAP2EncOptions() EncOptions // FIDO2 CTAP2 Canonical CBOR ``` 预设用于创建自定义模式。 ### 自定义模式 模式由设置创建。一旦创建,模式的设置将不可变。 💡 在启动时创建模式并重复使用它。它对于并发使用是安全的。 ``` // Create encoding mode. opts := cbor.CoreDetEncOptions() // use preset options as a starting point opts.Time = cbor.TimeUnix // change any settings if needed em, err := opts.EncMode() // create an immutable encoding mode // Reuse the encoding mode. It is safe for concurrent use. // API matches encoding/json. b, err := em.Marshal(v) // encode v to []byte b encoder := em.NewEncoder(w) // create encoder with io.Writer w err := encoder.Encode(v) // encode v to io.Writer w ``` 默认模式和自定义模式会自动应用 struct tag。 ### 用户指定的编码 Buffer (v2.7.0) `UserBufferEncMode` 接口扩展了 `EncMode` 接口,增加了 `MarshalToBuffer()`。它接受用户指定的 buffer,而不是使用内置的 buffer 池。 ``` em, err := myEncOptions.UserBufferEncMode() // create UserBufferEncMode mode var buf bytes.Buffer err = em.MarshalToBuffer(v, &buf) // encode v to provided buf ``` ### Struct Tags Struct tag 选项(`toarray`、`keyasint`、`omitempty`、`omitzero`)可减小 struct 的编码大小。 作为一种特殊情况,struct 字段 tag "-" 会忽略该字段。
🔎  使用 struct 字段 tag "-" 的编码示例

https://go.dev/play/p/aWEIFxd7InX ``` // https://github.com/fxamacker/cbor/issues/652 package main import ( "encoding/json" "fmt" "github.com/fxamacker/cbor/v2" ) // The `cbor:"-"` tag omits the Type field when encoding to CBOR. type Entity struct { _ struct{} `cbor:",toarray"` ID uint64 `json:"id"` Type string `cbor:"-" json:"typeOf"` Name string `json:"name"` } func main() { entity := Entity{ ID: 1, Type: "int64", Name: "Identifier", } c, _ := cbor.Marshal(entity) diag, _ := cbor.Diagnose(c) fmt.Printf("CBOR in hex: %x\n", c) fmt.Printf("CBOR in edn: %s\n", diag) j, _ := json.Marshal(entity) fmt.Printf("JSON: %s\n", string(j)) fmt.Printf("JSON encoding is %d bytes\n", len(j)) fmt.Printf("CBOR encoding is %d bytes\n", len(c)) // Output: // CBOR in hex: 82016a4964656e746966696572 // CBOR in edn: [1, "Identifier"] // JSON: {"id":1,"typeOf":"int64","name":"Identifier"} // JSON encoding is 45 bytes // CBOR encoding is 13 bytes } ```

🔎  将 3 层嵌套 Go struct 编码为 1 字节 CBOR 的示例

https://go.dev/play/p/YxwvfPdFQG2 ``` // Example encoding nested struct (with omitempty tag) // - encoding/json: 18 byte JSON // - fxamacker/cbor: 1 byte CBOR package main import ( "encoding/hex" "encoding/json" "fmt" "github.com/fxamacker/cbor/v2" ) type GrandChild struct { Quux int `json:",omitempty"` } type Child struct { Baz int `json:",omitempty"` Qux GrandChild `json:",omitempty"` } type Parent struct { Foo Child `json:",omitempty"` Bar int `json:",omitempty"` } func cb() { results, _ := cbor.Marshal(Parent{}) fmt.Println("hex(CBOR): " + hex.EncodeToString(results)) text, _ := cbor.Diagnose(results) // Diagnostic Notation fmt.Println("DN: " + text) } func js() { results, _ := json.Marshal(Parent{}) fmt.Println("hex(JSON): " + hex.EncodeToString(results)) text := string(results) // JSON fmt.Println("JSON: " + text) } func main() { cb() fmt.Println("-------------") js() } ``` 输出(DN 指诊断表示法,Diagnostic Notation): ``` hex(CBOR): a0 DN: {} ------------- hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d JSON: {"Foo":{"Qux":{}}} ```


🔎  使用 struct tag 选项的示例

![alt text](https://github.com/fxamacker/images/raw/master/cbor/v2.3.0/cbor_struct_tags_api.svg?sanitize=1 "CBOR API and Go Struct Tags")

Struct tag 选项简化了基于 CBOR 且需要 CBOR 数组或带有整数 key 的 map 的协议的使用。 ### CBOR Tags CBOR tags 在 `TagSet` 中指定。 可以使用 `TagSet` 创建自定义模式来处理 CBOR tags。 ``` em, err := opts.EncMode() // no CBOR tags em, err := opts.EncModeWithTags(ts) // immutable CBOR tags em, err := opts.EncModeWithSharedTags(ts) // mutable shared CBOR tags ``` `TagSet` 及使用它的模式对于并发使用是安全的。 `DecMode` 也提供了等效的 API。
🔎  使用 TagSet 和 TagOptions 的示例

``` // Use signedCWT struct defined in "Decoding CWT" example. // Create TagSet (safe for concurrency). tags := cbor.NewTagSet() // Register tag COSE_Sign1 18 with signedCWT type. tags.Add( cbor.TagOptions{EncTag: cbor.EncTagRequired, DecTag: cbor.DecTagRequired}, reflect.TypeOf(signedCWT{}), 18) // Create DecMode with immutable tags. dm, _ := cbor.DecOptions{}.DecModeWithTags(tags) // Unmarshal to signedCWT with tag support. var v signedCWT if err := dm.Unmarshal(data, &v); err != nil { return err } // Create EncMode with immutable tags. em, _ := cbor.EncOptions{}.EncModeWithTags(tags) // Marshal signedCWT with tag number. if data, err := em.Marshal(v); err != nil { return err } ```

👉 `fxamacker/cbor` 允许用户应用通过实现 `cbor.Marshaler` 和 `cbor.Unmarshaler` 接口,来使用几乎所有当前或未来的 CBOR tag 编号。 基本上,用户应用可以实现 `MarshalCBOR` 和 `UnmarshalCBOR` 函数,这些函数将被此 CBOR 编解码器的 `Marshal`、`Unmarshal` 等方法自动调用。 以下[示例](https://github.com/fxamacker/cbor/blob/master/example_embedded_json_tag_for_cbor_test.go)展示了如何编码和解码 tag 编号为 262 的带标签 CBOR data item。该 tag 内容是一个作为 CBOR byte string(major type 2)“嵌入”的 JSON 对象。
🔎  使用 Embedded JSON Tag for CBOR (tag 262) 的示例 ``` // https://github.com/fxamacker/cbor/issues/657 package cbor_test // NOTE: RFC 8949 does not mention tag number 262. IANA assigned // CBOR tag number 262 as "Embedded JSON Object" specified by the // document Embedded JSON Tag for CBOR: // // "Tag 262 can be applied to a byte string (major type 2) to indicate // that the byte string is a JSON Object. The length of the byte string // indicates the content." // // For more info, see Embedded JSON Tag for CBOR at: // https://github.com/toravir/CBOR-Tag-Specs/blob/master/embeddedJSON.md import ( "bytes" "encoding/json" "fmt" "github.com/fxamacker/cbor/v2" ) // cborTagNumForEmbeddedJSON is the CBOR tag number 262. const cborTagNumForEmbeddedJSON = 262 // EmbeddedJSON represents a Go value to be encoded as a tagged CBOR data item // with tag number 262 and the tag content is a JSON object "embedded" as a // CBOR byte string (major type 2). type EmbeddedJSON struct { any } func NewEmbeddedJSON(val any) EmbeddedJSON { return EmbeddedJSON{val} } // MarshalCBOR encodes EmbeddedJSON to a tagged CBOR data item with the // tag number 262 and the tag content is a JSON object that is // "embedded" as a CBOR byte string. func (v EmbeddedJSON) MarshalCBOR() ([]byte, error) { // Encode v to JSON object. data, err := json.Marshal(v) if err != nil { return nil, err } // Create cbor.Tag representing a tagged CBOR data item. tag := cbor.Tag{ Number: cborTagNumForEmbeddedJSON, Content: data, } // Marshal to a tagged CBOR data item. return cbor.Marshal(tag) } // UnmarshalCBOR decodes a tagged CBOR data item to EmbeddedJSON. // The byte slice provided to this function must contain a single // tagged CBOR data item with the tag number 262 and tag content // must be a JSON object "embedded" as a CBOR byte string. func (v *EmbeddedJSON) UnmarshalCBOR(b []byte) error { // Unmarshal tagged CBOR data item. var tag cbor.Tag if err := cbor.Unmarshal(b, &tag); err != nil { return err } // Check tag number. if tag.Number != cborTagNumForEmbeddedJSON { return fmt.Errorf("got tag number %d, expect tag number %d", tag.Number, cborTagNumForEmbeddedJSON) } // Check tag content. jsonData, isByteString := tag.Content.([]byte) if !isByteString { return fmt.Errorf("got tag content type %T, expect tag content []byte", tag.Content) } // Unmarshal JSON object. return json.Unmarshal(jsonData, v) } // MarshalJSON encodes EmbeddedJSON to a JSON object. func (v EmbeddedJSON) MarshalJSON() ([]byte, error) { return json.Marshal(v.any) } // UnmarshalJSON decodes a JSON object. func (v *EmbeddedJSON) UnmarshalJSON(b []byte) error { dec := json.NewDecoder(bytes.NewReader(b)) dec.UseNumber() return dec.Decode(&v.any) } func Example_embeddedJSONTagForCBOR() { value := NewEmbeddedJSON(map[string]any{ "name": "gopher", "id": json.Number("42"), }) data, err := cbor.Marshal(value) if err != nil { panic(err) } fmt.Printf("cbor: %x\n", data) var v EmbeddedJSON err = cbor.Unmarshal(data, &v) if err != nil { panic(err) } fmt.Printf("%+v\n", v.any) for k, v := range v.any.(map[string]any) { fmt.Printf(" %s: %v (%T)\n", k, v, v) } } ```
### 函数与接口
🔎  函数和接口一览

与 `encoding/json` API 相同的常用函数: - `Marshal`, `Unmarshal` - `NewEncoder`, `(*Encoder).Encode` - `NewDecoder`, `(*Decoder).Decode` 注意:如果还有剩余字节,`Unmarshal` 将返回 `ExtraneousDataError` 因为 RFC 8949 将带有剩余字节的 CBOR data item 视为格式错误。 - 💡 使用 `UnmarshalFirst` 解码第一个 CBOR data item 并返回剩余的任何字节。 其他有用的函数: - `Diagnose`, `DiagnoseFirst` 从 CBOR 数据生成人类可读的[扩展诊断表示法](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G)。 - `UnmarshalFirst` 解码第一个 CBOR data item 并返回剩余的任何字节。 - 如果 CBOR data item 格式良好,`Wellformed` 将返回 nil 错误。 与 Go `encoding` 包中完全相同或类似的接口包括: `Marshaler`, `Unmarshaler`, `BinaryMarshaler` 和 `BinaryUnmarshaler`。 `RawMessage` 类型可用于延迟 CBOR 解码或预计算 CBOR 编码。

### 安全提示 🔒 在解码非常大或不确定大小的数据时,使用 Go 的 `io.LimitReader` 来限制大小。 对于处理超大数据(例如区块链)的系统,可能需要增加默认限制。 可以使用 `DecOptions` 来修改 `MaxArrayElements`、`MaxMapPairs` 和 `MaxNestedLevels` 的默认限制。 ## 状态 v2.9.2(2026年5月3日,星期日)通过为编码到 CBOR indefinite-length 数据添加更严格的检查,重构并强化了流式编码器。这防止了对本库的不当使用生成出会被解码器拒绝的格式错误的 CBOR indefinite-length 数据。 此版本包含其他 bug 修复、防御性检查,并增加了更多测试。 v2.9.0 通过了 fuzz 测试(数十亿次执行),具备生产级质量。 更多详情,请参阅 [v2.9.2 发布说明](https://github.com/fxamacker/cbor/releases)。 ### 早期版本 发布和提交通常在星期日进行,因为我的工作日程使得我在工作日(有时甚至是连续的周末和连续的圣诞假期)没有时间处理这个项目。星期一早上的发布是为了在点击发布按钮之前,允许进行更多的夜间 fuzz 测试。 [v2.9.1](https://github.com/fxamacker/cbor/releases/tag/v2.9.1)(2026年3月30日,星期一)包含重要的 bug 修复、防御性检查、改进的代码质量以及更多测试。虽然没有公开,但 fuzzer 也通过添加更多 fuzz 测试得到了改进。它通过了 fuzz 测试(数十亿次执行),具备生产级质量。 [v2.9.0](https://github.com/fxamacker/cbor/releases/tag/v2.9.0)(2025年7月13日,星期日)改进了 CBOR 和 JSON 之间的互操作性/转码,重构了测试,并改进了文档。它通过了 fuzz 测试(数十亿次执行),具备生产级质量。 [v2.8.0](https://github.com/fxamacker/cbor/releases/tag/v2.8.0)(2025年3月30日,星期日)是一个小版本发布,主要是为了向 struct 字段 tag 添加 `omitzero` 选项并修复 bug。它通过了 fuzz 测试(数十亿次执行),具备生产级质量。 [v2.7.0](https://github.com/fxamacker/cbor/releases/tag/v2.7.0)(2024年6月23日,星期一)增加了帮助大型项目(例如 Kubernetes)将 CBOR 用作 JSON 和 Protocol Buffers 替代方案的功能和改进。其他改进包括加速、改善内存使用、修复 bug、新的序列化等。它通过了 fuzz 测试(超过 50 亿次执行),具备生产级质量。 [v2.6.0](https://github.com/fxamacker/cbor/releases/tag/v2.6.0)(2024年2月11日,星期日)增加了重要的新功能、优化和 bug 修复。它对于需要在 CBOR 和 JSON 之间转换数据的系统尤其有用。新的选项和优化改进了对 bignum、整数、map 和 string 的处理。 [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0)(2023年8月13日,星期日)增加了新功能和重要的 bug 修复。在经过长期测试的 beta 版本 [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta)(2022年12月) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0)(2023年8月)之后,它经过了 fuzz 测试并具备生产级质量。 __重要__: 👉 在从 v2.4 或更早版本升级之前,请阅读发布说明中强调的重大变更。 v2.5.0 是一个大版本发布,修复了 `Unmarshal` 中处理冗余数据等行为的 bug,在升级前应进行审查。 有关新功能、改进和 bug 修复的列表,请参阅 [v2.5.0 发布说明](https://github.com/fxamacker/cbor/releases/tag/v2.5.0)。 有关版本号等的更多信息,请参阅[“版本与 API 变更”](https://github.com/fxamacker/cbor#versions-and-api-changes)部分。