fxamacker/cbor is a library for encoding and decoding CBOR and CBOR Sequences.
CBOR is a trusted alternative to JSON, MessagePack, Protocol Buffers, etc. CBOR is an Internet Standard defined by IETF STD 94 (RFC 8949) and is designed to be relevant for decades.
fxamacker/cbor is used in projects by Arm Ltd., EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, IBM, Kubernetes*, Let's Encrypt, Linux Foundation, Microsoft, Oasis Protocol, Red Hat*, Tailscale*, Veraison*, etc.
See Quick Start and Releases. 🆕 UnmarshalFirst and DiagnoseFirst can decode CBOR Sequences. MarshalToBuffer and UserBufferEncMode accepts user-specified buffer.
fxamacker/cbor is a CBOR codec in full conformance with IETF STD 94 (RFC 8949). It also supports CBOR Sequences (RFC 8742) and Extended Diagnostic Notation (Appendix G of RFC 8610).
Features include full support for CBOR tags, Core Deterministic Encoding, duplicate map key detection, etc.
API is mostly same as encoding/json, plus interfaces that simplify concurrency and CBOR options.
Design balances trade-offs between security, speed, concurrency, encoded data size, usability, etc.
🔎 Highlights
🚀 Speed
Encoding and decoding is fast without using Go's unsafe package. Slower settings are opt-in. Default limits allow very fast and memory efficient rejection of malformed CBOR data.
🔒 Security
Decoder has configurable limits that defend against malicious inputs. Duplicate map key detection is supported. By contrast, encoding/gob is not designed to be hardened against adversarial inputs.
Codec passed multiple confidential security assessments in 2022. No vulnerabilities found in subset of codec in a nonconfidential security assessment prepared by NCC Group for Microsoft Corporation.
🗜️ Data Size
Struct tag options (toarray, keyasint, omitempty, omitzero) and field tag "-" automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
:jigsaw: Usability
API is mostly same as encoding/json plus interfaces that simplify concurrency for CBOR options. Encoding and decoding modes can be created at startup and reused by any goroutines.
Presets include Core Deterministic Encoding, Preferred Serialization, CTAP2 Canonical CBOR, etc.
📆 Extensibility
Features include CBOR extension points (e.g. CBOR tags) and extensive settings. API has interfaces that allow users to create custom encoding and decoding without modifying this library.
fxamacker/cbor has configurable limits, etc. that defend against malicious CBOR data.
Notably, fxamacker/cbor is fast at rejecting malformed CBOR data.
[!NOTE]
Benchmarks rejecting 10 bytes of malicious CBOR data decoding to[]byte:
Codec Speed (ns/op) Memory Allocs fxamacker/cbor 2.7.0 47 ± 7% 32 B/op 2 allocs/op ugorji/go 1.2.12 5878187 ± 3% 67111556 B/op 13 allocs/op Faster hardware (overclocked DDR4 or DDR5) can reduce speed difference.
🔎 Benchmark details
Latest comparison for decoding CBOR data to Go
[]byte: - Input:[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}- go1.22.7, linux/amd64, i5-13600K (DDR4-2933, disabled e-cores) - go test -bench=. -benchmem -count=20Prior comparisons
Codec Speed (ns/op) Memory Allocs fxamacker/cbor 2.5.0-beta2 44.33 ± 2% 32 B/op 2 allocs/op fxamacker/cbor 0.1.0 - 2.4.0 ~44.68 ± 6% 32 B/op 2 allocs/op ugorji/go 1.2.10 5524792.50 ± 3% 67110491 B/op 12 allocs/op ugorji/go 1.1.0 - 1.2.6 💥 runtime: out of memory: cannot allocate
- Input:
[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}- go1.19.6, linux/amd64, i5-13600K (DDR4)
- go test -bench=. -benchmem -count=20
In contrast, some codecs can crash or use excessive resources while decoding bad data.
[!WARNING]
Go'sencoding/gobis not designed to be hardened against adversarial inputs.
🔎 gob fatal error (out of memory) 💥 decoding 181 bytes
```Go // Example of encoding/gob having "fatal error: runtime: out of memory" // while decoding 181 bytes (all Go versions as of Dec. 8, 2024). package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" )
// Example data is from https://github.com/golang/go/issues/24446 // (shortened to 181 bytes). const data = "4dffb503010102303001ff30000109010130010800010130010800010130" + "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" + "860001013001ff860001013001ffb80000001eff850401010e3030303030" + "30303030303030303001ff3000010c0104000016ffb70201010830303030" + "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" + "303030303030303030303030303030303030303030303030303030303030" + "30"
type X struct { J *X K map[string]int }
func main() { raw, _ := hex.DecodeString(data) decoder := gob.NewDecoder(bytes.NewReader(raw))
var x X decoder.Decode(&x) // fatal error: runtime: out of memory fmt.Println("Decoding finished.") } ```
Struct tags automatically reduce encoded size of structs and improve speed.
We can write less code by using struct tag options:
- toarray: encode without field names (decode back to original struct)
- keyasint: encode field names as integers (decode back to original struct)
- omitempty: omit empty field when encoding
- omitzero: omit zero-value field when encoding
As a special case, struct field tag "-" omits the field.
NOTE: When a struct uses toarray, the encoder will ignore omitempty and omitzero to prevent position of encoded array elements from changing. This allows decoder to match encoded elements to their Go struct field.
[!NOTE]
fxamacker/cborcan encode a 3-level nested Go struct to 1 byte! -encoding/json: 18 bytes of JSON -fxamacker/cbor: 1 byte of CBOR
🔎 Encoding 3-level nested Go struct with omitempty
https://go.dev/play/p/YxwvfPdFQG2
```Go // 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 GrandChildjson:",omitempty"}type Parent struct { Foo Child
json:",omitempty"Bar intjson:",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() } ```
Output (DN is Diagnostic Notation): ``` hex(CBOR): a0 DN: {}
hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d JSON: {"Foo":{"Qux":{}}} ```
Install: go get github.com/fxamacker/cbor/v2 and import "github.com/fxamacker/cbor/v2".
[!TIP]
Tinygo users can try beta/experimental branch feature/cbor-tinygo-beta.
🔎 More about tinygo feature branch
Tinygo
Branch feature/cbor-tinygo-beta is based on fxamacker/cbor v2.7.0 and it can be compiled using tinygo v0.33 (also compiles with golang/go).
It passes unit tests (with both go1.22 and tinygo v0.33) and is considered beta/experimental for tinygo.
:warning: The
feature/cbor-tinygo-betabranch does not get fuzz tested yet.Changes in this feature branch only affect tinygo compiled software. Summary of changes: - default
DecOptions.MaxNestedLevelsis reduced to 16 (was 32). User can specify higher limit but 24+ crashes tests when compiled with tinygo v0.33. - disabled decoding CBOR tag data to Go interface because tinygo v0.33 is missing needed feature. - encoding error message can be different when encoding function type.Related tinygo issues: - https://github.com/tinygo-org/tinygo/issues/4277 - https://github.com/tinygo-org/tinygo/issues/4458
This library can encode and decode CBOR (RFC 8949) and CBOR Sequences (RFC 8742).
Configurable limits and options can be used to balance trade-offs.
Package level functions only use this library's default settings.
They provide the "default mode" of encoding and decoding.
// 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.
[!IMPORTANT]
CBOR settings allow trade-offs between speed, security, encoding size, etc.
- Different CBOR libraries may use different default settings.
- CBOR-based formats or protocols usually require specific settings.
For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
Presets can be used as-is or as a starting point for custom settings.
// 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
Presets are used to create custom modes.
Modes are created from s