1// Copyright 2009 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 asn1
6
7import (
8	"bytes"
9	"encoding/hex"
10	"math/big"
11	"reflect"
12	"strings"
13	"testing"
14	"time"
15)
16
17type intStruct struct {
18	A int
19}
20
21type twoIntStruct struct {
22	A int
23	B int
24}
25
26type bigIntStruct struct {
27	A *big.Int
28}
29
30type nestedStruct struct {
31	A intStruct
32}
33
34type rawContentsStruct struct {
35	Raw RawContent
36	A   int
37}
38
39type implicitTagTest struct {
40	A int `asn1:"implicit,tag:5"`
41}
42
43type explicitTagTest struct {
44	A int `asn1:"explicit,tag:5"`
45}
46
47type flagTest struct {
48	A Flag `asn1:"tag:0,optional"`
49}
50
51type generalizedTimeTest struct {
52	A time.Time `asn1:"generalized"`
53}
54
55type ia5StringTest struct {
56	A string `asn1:"ia5"`
57}
58
59type printableStringTest struct {
60	A string `asn1:"printable"`
61}
62
63type genericStringTest struct {
64	A string
65}
66
67type optionalRawValueTest struct {
68	A RawValue `asn1:"optional"`
69}
70
71type omitEmptyTest struct {
72	A []string `asn1:"omitempty"`
73}
74
75type defaultTest struct {
76	A int `asn1:"optional,default:1"`
77}
78
79type applicationTest struct {
80	A int `asn1:"application,tag:0"`
81	B int `asn1:"application,tag:1,explicit"`
82}
83
84type privateTest struct {
85	A int `asn1:"private,tag:0"`
86	B int `asn1:"private,tag:1,explicit"`
87	C int `asn1:"private,tag:31"`  // tag size should be 2 octet
88	D int `asn1:"private,tag:128"` // tag size should be 3 octet
89}
90
91type numericStringTest struct {
92	A string `asn1:"numeric"`
93}
94
95type testSET []int
96
97var PST = time.FixedZone("PST", -8*60*60)
98
99type marshalTest struct {
100	in  any
101	out string // hex encoded
102}
103
104func farFuture() time.Time {
105	t, err := time.Parse(time.RFC3339, "2100-04-05T12:01:01Z")
106	if err != nil {
107		panic(err)
108	}
109	return t
110}
111
112var marshalTests = []marshalTest{
113	{10, "02010a"},
114	{127, "02017f"},
115	{128, "02020080"},
116	{-128, "020180"},
117	{-129, "0202ff7f"},
118	{intStruct{64}, "3003020140"},
119	{bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},
120	{twoIntStruct{64, 65}, "3006020140020141"},
121	{nestedStruct{intStruct{127}}, "3005300302017f"},
122	{[]byte{1, 2, 3}, "0403010203"},
123	{implicitTagTest{64}, "3003850140"},
124	{explicitTagTest{64}, "3005a503020140"},
125	{flagTest{true}, "30028000"},
126	{flagTest{false}, "3000"},
127	{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
128	{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
129	{time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
130	{farFuture(), "180f32313030303430353132303130315a"},
131	{generalizedTimeTest{time.Unix(1258325776, 0).UTC()}, "3011180f32303039313131353232353631365a"},
132	{BitString{[]byte{0x80}, 1}, "03020780"},
133	{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
134	{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
135	{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"},
136	{ObjectIdentifier([]int{2, 100, 3}), "0603813403"},
137	{"test", "130474657374"},
138	{
139		"" +
140			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
141			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
142			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
143			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x'
144		"137f" +
145			"7878787878787878787878787878787878787878787878787878787878787878" +
146			"7878787878787878787878787878787878787878787878787878787878787878" +
147			"7878787878787878787878787878787878787878787878787878787878787878" +
148			"78787878787878787878787878787878787878787878787878787878787878",
149	},
150	{
151		"" +
152			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
153			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
154			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
155			"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x'
156		"138180" +
157			"7878787878787878787878787878787878787878787878787878787878787878" +
158			"7878787878787878787878787878787878787878787878787878787878787878" +
159			"7878787878787878787878787878787878787878787878787878787878787878" +
160			"7878787878787878787878787878787878787878787878787878787878787878",
161	},
162	{ia5StringTest{"test"}, "3006160474657374"},
163	{optionalRawValueTest{}, "3000"},
164	{printableStringTest{"test"}, "3006130474657374"},
165	{printableStringTest{"test*"}, "30071305746573742a"},
166	{genericStringTest{"test"}, "3006130474657374"},
167	{genericStringTest{"test*"}, "30070c05746573742a"},
168	{genericStringTest{"test&"}, "30070c057465737426"},
169	{rawContentsStruct{nil, 64}, "3003020140"},
170	{rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"},
171	{RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"},
172	{testSET([]int{10}), "310302010a"},
173	{omitEmptyTest{[]string{}}, "3000"},
174	{omitEmptyTest{[]string{"1"}}, "30053003130131"},
175	{"Σ", "0c02cea3"},
176	{defaultTest{0}, "3003020100"},
177	{defaultTest{1}, "3000"},
178	{defaultTest{2}, "3003020102"},
179	{applicationTest{1, 2}, "30084001016103020102"},
180	{privateTest{1, 2, 3, 4}, "3011c00101e103020102df1f0103df81000104"},
181	{numericStringTest{"1 9"}, "30051203312039"},
182}
183
184func TestMarshal(t *testing.T) {
185	for i, test := range marshalTests {
186		data, err := Marshal(test.in)
187		if err != nil {
188			t.Errorf("#%d failed: %s", i, err)
189		}
190		out, _ := hex.DecodeString(test.out)
191		if !bytes.Equal(out, data) {
192			t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
193
194		}
195	}
196}
197
198type marshalWithParamsTest struct {
199	in     any
200	params string
201	out    string // hex encoded
202}
203
204var marshalWithParamsTests = []marshalWithParamsTest{
205	{intStruct{10}, "set", "310302010a"},
206	{intStruct{10}, "application", "600302010a"},
207	{intStruct{10}, "private", "e00302010a"},
208}
209
210func TestMarshalWithParams(t *testing.T) {
211	for i, test := range marshalWithParamsTests {
212		data, err := MarshalWithParams(test.in, test.params)
213		if err != nil {
214			t.Errorf("#%d failed: %s", i, err)
215		}
216		out, _ := hex.DecodeString(test.out)
217		if !bytes.Equal(out, data) {
218			t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
219
220		}
221	}
222}
223
224type marshalErrTest struct {
225	in  any
226	err string
227}
228
229var marshalErrTests = []marshalErrTest{
230	{bigIntStruct{nil}, "empty integer"},
231	{numericStringTest{"a"}, "invalid character"},
232	{ia5StringTest{"\xb0"}, "invalid character"},
233	{printableStringTest{"!"}, "invalid character"},
234}
235
236func TestMarshalError(t *testing.T) {
237	for i, test := range marshalErrTests {
238		_, err := Marshal(test.in)
239		if err == nil {
240			t.Errorf("#%d should fail, but success", i)
241			continue
242		}
243
244		if !strings.Contains(err.Error(), test.err) {
245			t.Errorf("#%d got: %v want %v", i, err, test.err)
246		}
247	}
248}
249
250func TestInvalidUTF8(t *testing.T) {
251	_, err := Marshal(string([]byte{0xff, 0xff}))
252	if err == nil {
253		t.Errorf("invalid UTF8 string was accepted")
254	}
255}
256
257func TestMarshalOID(t *testing.T) {
258	var marshalTestsOID = []marshalTest{
259		{[]byte("\x06\x01\x30"), "0403060130"}, // bytes format returns a byte sequence \x04
260		// {ObjectIdentifier([]int{0}), "060100"}, // returns an error as OID 0.0 has the same encoding
261		{[]byte("\x06\x010"), "0403060130"},                // same as above "\x06\x010" = "\x06\x01" + "0"
262		{ObjectIdentifier([]int{2, 999, 3}), "0603883703"}, // Example of ITU-T X.690
263		{ObjectIdentifier([]int{0, 0}), "060100"},          // zero OID
264	}
265	for i, test := range marshalTestsOID {
266		data, err := Marshal(test.in)
267		if err != nil {
268			t.Errorf("#%d failed: %s", i, err)
269		}
270		out, _ := hex.DecodeString(test.out)
271		if !bytes.Equal(out, data) {
272			t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
273		}
274	}
275}
276
277func TestIssue11130(t *testing.T) {
278	data := []byte("\x06\x010") // == \x06\x01\x30 == OID = 0 (the figure)
279	var v any
280	// v has Zero value here and Elem() would panic
281	_, err := Unmarshal(data, &v)
282	if err != nil {
283		t.Errorf("%v", err)
284		return
285	}
286	if reflect.TypeOf(v).String() != reflect.TypeOf(ObjectIdentifier{}).String() {
287		t.Errorf("marshal OID returned an invalid type")
288		return
289	}
290
291	data1, err := Marshal(v)
292	if err != nil {
293		t.Errorf("%v", err)
294		return
295	}
296
297	if !bytes.Equal(data, data1) {
298		t.Errorf("got: %q, want: %q \n", data1, data)
299		return
300	}
301
302	var v1 any
303	_, err = Unmarshal(data1, &v1)
304	if err != nil {
305		t.Errorf("%v", err)
306		return
307	}
308	if !reflect.DeepEqual(v, v1) {
309		t.Errorf("got: %#v data=%q, want : %#v data=%q\n ", v1, data1, v, data)
310	}
311}
312
313func BenchmarkMarshal(b *testing.B) {
314	b.ReportAllocs()
315
316	for i := 0; i < b.N; i++ {
317		for _, test := range marshalTests {
318			Marshal(test.in)
319		}
320	}
321}
322
323func TestSetEncoder(t *testing.T) {
324	testStruct := struct {
325		Strings []string `asn1:"set"`
326	}{
327		Strings: []string{"a", "aa", "b", "bb", "c", "cc"},
328	}
329
330	// Expected ordering of the SET should be:
331	// a, b, c, aa, bb, cc
332
333	output, err := Marshal(testStruct)
334	if err != nil {
335		t.Errorf("%v", err)
336	}
337
338	expectedOrder := []string{"a", "b", "c", "aa", "bb", "cc"}
339	var resultStruct struct {
340		Strings []string `asn1:"set"`
341	}
342	rest, err := Unmarshal(output, &resultStruct)
343	if err != nil {
344		t.Errorf("%v", err)
345	}
346	if len(rest) != 0 {
347		t.Error("Unmarshal returned extra garbage")
348	}
349	if !reflect.DeepEqual(expectedOrder, resultStruct.Strings) {
350		t.Errorf("Unexpected SET content. got: %s, want: %s", resultStruct.Strings, expectedOrder)
351	}
352}
353
354func TestSetEncoderSETSliceSuffix(t *testing.T) {
355	type testSetSET []string
356	testSet := testSetSET{"a", "aa", "b", "bb", "c", "cc"}
357
358	// Expected ordering of the SET should be:
359	// a, b, c, aa, bb, cc
360
361	output, err := Marshal(testSet)
362	if err != nil {
363		t.Errorf("%v", err)
364	}
365
366	expectedOrder := testSetSET{"a", "b", "c", "aa", "bb", "cc"}
367	var resultSet testSetSET
368	rest, err := Unmarshal(output, &resultSet)
369	if err != nil {
370		t.Errorf("%v", err)
371	}
372	if len(rest) != 0 {
373		t.Error("Unmarshal returned extra garbage")
374	}
375	if !reflect.DeepEqual(expectedOrder, resultSet) {
376		t.Errorf("Unexpected SET content. got: %s, want: %s", resultSet, expectedOrder)
377	}
378}
379
380func BenchmarkUnmarshal(b *testing.B) {
381	b.ReportAllocs()
382
383	type testCase struct {
384		in  []byte
385		out any
386	}
387	var testData []testCase
388	for _, test := range unmarshalTestData {
389		pv := reflect.New(reflect.TypeOf(test.out).Elem())
390		inCopy := make([]byte, len(test.in))
391		copy(inCopy, test.in)
392		outCopy := pv.Interface()
393
394		testData = append(testData, testCase{
395			in:  inCopy,
396			out: outCopy,
397		})
398	}
399
400	b.ResetTimer()
401	for i := 0; i < b.N; i++ {
402		for _, testCase := range testData {
403			_, _ = Unmarshal(testCase.in, testCase.out)
404		}
405	}
406}
407