1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package cryptobyte_test
6
7import (
8	"errors"
9	"fmt"
10
11	"golang.org/x/crypto/cryptobyte"
12	"golang.org/x/crypto/cryptobyte/asn1"
13)
14
15func ExampleString_lengthPrefixed() {
16	// This is an example of parsing length-prefixed data (as found in, for
17	// example, TLS). Imagine a 16-bit prefixed series of 8-bit prefixed
18	// strings.
19
20	input := cryptobyte.String([]byte{0, 12, 5, 'h', 'e', 'l', 'l', 'o', 5, 'w', 'o', 'r', 'l', 'd'})
21	var result []string
22
23	var values cryptobyte.String
24	if !input.ReadUint16LengthPrefixed(&values) ||
25		!input.Empty() {
26		panic("bad format")
27	}
28
29	for !values.Empty() {
30		var value cryptobyte.String
31		if !values.ReadUint8LengthPrefixed(&value) {
32			panic("bad format")
33		}
34
35		result = append(result, string(value))
36	}
37
38	// Output: []string{"hello", "world"}
39	fmt.Printf("%#v\n", result)
40}
41
42func ExampleString_aSN1() {
43	// This is an example of parsing ASN.1 data that looks like:
44	//    Foo ::= SEQUENCE {
45	//      version [6] INTEGER DEFAULT 0
46	//      data OCTET STRING
47	//    }
48
49	input := cryptobyte.String([]byte{0x30, 12, 0xa6, 3, 2, 1, 2, 4, 5, 'h', 'e', 'l', 'l', 'o'})
50
51	var (
52		version                   int64
53		data, inner, versionBytes cryptobyte.String
54		haveVersion               bool
55	)
56	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
57		!input.Empty() ||
58		!inner.ReadOptionalASN1(&versionBytes, &haveVersion, asn1.Tag(6).Constructed().ContextSpecific()) ||
59		(haveVersion && !versionBytes.ReadASN1Integer(&version)) ||
60		(haveVersion && !versionBytes.Empty()) ||
61		!inner.ReadASN1(&data, asn1.OCTET_STRING) ||
62		!inner.Empty() {
63		panic("bad format")
64	}
65
66	// Output: haveVersion: true, version: 2, data: hello
67	fmt.Printf("haveVersion: %t, version: %d, data: %s\n", haveVersion, version, string(data))
68}
69
70func ExampleBuilder_aSN1() {
71	// This is an example of building ASN.1 data that looks like:
72	//    Foo ::= SEQUENCE {
73	//      version [6] INTEGER DEFAULT 0
74	//      data OCTET STRING
75	//    }
76
77	version := int64(2)
78	data := []byte("hello")
79	const defaultVersion = 0
80
81	var b cryptobyte.Builder
82	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
83		if version != defaultVersion {
84			b.AddASN1(asn1.Tag(6).Constructed().ContextSpecific(), func(b *cryptobyte.Builder) {
85				b.AddASN1Int64(version)
86			})
87		}
88		b.AddASN1OctetString(data)
89	})
90
91	result, err := b.Bytes()
92	if err != nil {
93		panic(err)
94	}
95
96	// Output: 300ca603020102040568656c6c6f
97	fmt.Printf("%x\n", result)
98}
99
100func ExampleBuilder_lengthPrefixed() {
101	// This is an example of building length-prefixed data (as found in,
102	// for example, TLS). Imagine a 16-bit prefixed series of 8-bit
103	// prefixed strings.
104	input := []string{"hello", "world"}
105
106	var b cryptobyte.Builder
107	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
108		for _, value := range input {
109			b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
110				b.AddBytes([]byte(value))
111			})
112		}
113	})
114
115	result, err := b.Bytes()
116	if err != nil {
117		panic(err)
118	}
119
120	// Output: 000c0568656c6c6f05776f726c64
121	fmt.Printf("%x\n", result)
122}
123
124func ExampleBuilder_lengthPrefixOverflow() {
125	// Writing more data that can be expressed by the length prefix results
126	// in an error from Bytes().
127
128	tooLarge := make([]byte, 256)
129
130	var b cryptobyte.Builder
131	b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
132		b.AddBytes(tooLarge)
133	})
134
135	result, err := b.Bytes()
136	fmt.Printf("len=%d err=%s\n", len(result), err)
137
138	// Output: len=0 err=cryptobyte: pending child length 256 exceeds 1-byte length prefix
139}
140
141func ExampleBuilderContinuation_errorHandling() {
142	var b cryptobyte.Builder
143	// Continuations that panic with a BuildError will cause Bytes to
144	// return the inner error.
145	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
146		b.AddUint32(0)
147		panic(cryptobyte.BuildError{Err: errors.New("example error")})
148	})
149
150	result, err := b.Bytes()
151	fmt.Printf("len=%d err=%s\n", len(result), err)
152
153	// Output: len=0 err=example error
154}
155