-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvectors_test.go
More file actions
143 lines (125 loc) · 4.93 KB
/
Copy pathvectors_test.go
File metadata and controls
143 lines (125 loc) · 4.93 KB
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package cose
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
)
// --- shared test helpers ---------------------------------------------------
// hexDec decodes a hex string into bytes, failing the test on bad input.
func hexDec(t *testing.T, s string) []byte {
t.Helper()
b, err := hex.DecodeString(s)
require.NoError(t, err)
return b
}
// cborBstr returns the canonical CBOR encoding of a byte string (header +
// payload). cborHead emits the 8/16/32-bit length forms, so this handles byte
// strings far larger than the test vectors need. It is an independent
// re-implementation used to cross-check the encoder, so the tests don't
// validate cose against itself.
func cborBstr(b []byte) []byte {
return append(cborHead(2, uint64(len(b))), b...)
}
// cborText returns the canonical CBOR encoding of a text string.
func cborText(s string) []byte {
return append(cborHead(3, uint64(len(s))), s...)
}
// cborHead encodes a CBOR type header for the given major type and argument.
func cborHead(major byte, arg uint64) []byte {
mt := major << 5
switch {
case arg < 24:
return []byte{mt | byte(arg)}
case arg < 1<<8:
return []byte{mt | 24, byte(arg)}
case arg < 1<<16:
return []byte{mt | 25, byte(arg >> 8), byte(arg)}
default:
return []byte{mt | 26, byte(arg >> 24), byte(arg >> 16), byte(arg >> 8), byte(arg)}
}
}
// expectedEncStructure independently assembles the Enc_structure bytes:
//
// [ "Encrypt", protected : bstr, external_aad : bstr ]
func expectedEncStructure(protected, externalAAD []byte) []byte {
out := []byte{0x83} // array(3)
out = append(out, cborText(contextEncrypt)...)
out = append(out, cborBstr(protected)...)
out = append(out, cborBstr(externalAAD)...)
return out
}
// --- golden vectors --------------------------------------------------------
// The minimal detached COSE_Encrypt: empty body headers, a null payload, and a
// single recipient with empty headers and a one-byte wrapped key (0xAA).
//
// 96([ h'', {}, null, [ [ h'', {}, h'AA' ] ] ])
// d8 60 84 40 a0 f6 81 83 40 a0 41 aa
const minimalEnvelopeHex = "d8608440a0f6818340a041aa"
// A richer hand-encoded vector exercising protected/unprotected parsing:
//
// protected = { 1: 3 } ; alg = A256GCM
// unprotected = { 5: h'00..bb' } ; iv (12 bytes)
// ciphertext = null ; detached
// recipients = [ [ {1:-31}, {4:h'01'}, h'deadbeef' ] ]
const richEnvelopeHex = "d8608443a10103a1054c00112233445566778899aabbf6818344a101381ea104410144deadbeef"
// TestVectors checks the encoder and decoder against hand-encoded golden byte
// sequences, cross-checked by the independent CBOR re-implementation above so
// the tests don't validate cose against itself.
func TestVectors(t *testing.T) {
t.Run("encode minimal", func(t *testing.T) {
env := &Envelope{Recipients: []*Recipient{{Ciphertext: []byte{0xAA}}}}
got, err := env.Encode()
require.NoError(t, err)
want := hexDec(t, minimalEnvelopeHex)
require.Equal(t, want, got)
})
t.Run("decode minimal", func(t *testing.T) {
env, rest, err := Decode(hexDec(t, minimalEnvelopeHex))
require.NoError(t, err)
require.Len(t, rest, 0)
require.Len(t, env.Headers.Protected, 0)
require.Len(t, env.Headers.Unprotected, 0)
require.Len(t, env.Recipients, 1)
require.Equal(t, []byte{0xAA}, env.Recipients[0].Ciphertext)
})
t.Run("decode rich", func(t *testing.T) {
data := hexDec(t, richEnvelopeHex)
env, rest, err := Decode(data)
require.NoError(t, err)
require.Len(t, rest, 0)
alg, ok := env.Headers.Protected.Int(HeaderLabelAlg)
require.True(t, ok)
require.Equal(t, AlgA256GCM, alg)
wantIV := hexDec(t, "00112233445566778899aabb")
iv, ok := env.Headers.Unprotected.Bytes(HeaderLabelIV)
require.True(t, ok)
require.Equal(t, wantIV, iv)
require.Len(t, env.Recipients, 1)
r := env.Recipients[0]
ralg, ok := r.Headers.Protected.Int(HeaderLabelAlg)
require.True(t, ok)
require.Equal(t, AlgECDHESA256KW, ralg)
kid, ok := r.Headers.Unprotected.Bytes(HeaderLabelKID)
require.True(t, ok)
require.Equal(t, []byte{0x01}, kid)
require.Equal(t, hexDec(t, "deadbeef"), r.Ciphertext)
// RawProtected must preserve the on-wire protected bytes, and the
// Enc_structure must be built from them.
require.Equal(t, hexDec(t, "a10103"), env.Headers.RawProtected)
aad, err := env.EncStructure(nil)
require.NoError(t, err)
require.Equal(t, expectedEncStructure(hexDec(t, "a10103"), nil), aad)
})
t.Run("enc_structure empty protected", func(t *testing.T) {
env := &Envelope{Recipients: []*Recipient{{Ciphertext: []byte{0xAA}}}}
aad, err := env.EncStructure(nil)
require.NoError(t, err)
// [ "Encrypt", h'', h'' ] = 83 67 "Encrypt" 40 40
want := hexDec(t, "8367456e63727970744040")
require.Equal(t, want, aad)
require.Equal(t, expectedEncStructure(nil, nil), aad)
withAAD, err := env.EncStructure([]byte{0x01, 0x02})
require.NoError(t, err)
require.Equal(t, expectedEncStructure(nil, []byte{0x01, 0x02}), withAAD)
})
}