-
Notifications
You must be signed in to change notification settings - Fork 26
/
sign1.go
319 lines (283 loc) · 8.31 KB
/
sign1.go
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
package cose
import (
"bytes"
"errors"
"io"
"github.com/fxamacker/cbor/v2"
)
// sign1Message represents a COSE_Sign1 CBOR object:
//
// COSE_Sign1 = [
// Headers,
// payload : bstr / nil,
// signature : bstr
// ]
//
// Reference: https://tools.ietf.org/html/rfc8152#section-4.2
type sign1Message struct {
_ struct{} `cbor:",toarray"`
Protected cbor.RawMessage
Unprotected cbor.RawMessage
Payload byteString
Signature byteString
}
// sign1MessagePrefix represents the fixed prefix of COSE_Sign1_Tagged.
var sign1MessagePrefix = []byte{
0xd2, // #6.18
0x84, // Array of length 4
}
// Sign1Message represents a decoded COSE_Sign1 message.
//
// Reference: https://tools.ietf.org/html/rfc8152#section-4.2
type Sign1Message struct {
Headers Headers
Payload []byte
Signature []byte
}
// NewSign1Message returns a Sign1Message with header initialized.
func NewSign1Message() *Sign1Message {
return &Sign1Message{
Headers: Headers{
Protected: ProtectedHeader{},
Unprotected: UnprotectedHeader{},
},
}
}
// MarshalCBOR encodes Sign1Message into a COSE_Sign1_Tagged object.
func (m *Sign1Message) MarshalCBOR() ([]byte, error) {
content, err := m.getContent()
if err != nil {
return nil, err
}
return encMode.Marshal(cbor.Tag{
Number: CBORTagSign1Message,
Content: content,
})
}
// UnmarshalCBOR decodes a COSE_Sign1_Tagged object into Sign1Message.
func (m *Sign1Message) UnmarshalCBOR(data []byte) error {
if m == nil {
return errors.New("cbor: UnmarshalCBOR on nil Sign1Message pointer")
}
// fast message check
if !bytes.HasPrefix(data, sign1MessagePrefix) {
return errors.New("cbor: invalid COSE_Sign1_Tagged object")
}
return m.doUnmarshal(data[1:])
}
// Sign signs a Sign1Message using the provided Signer.
// The signature is stored in m.Signature.
//
// Note that m.Signature is only valid as long as m.Headers.Protected and
// m.Payload remain unchanged after calling this method.
// It is possible to modify m.Headers.Unprotected after signing,
// i.e., add counter signatures or timestamps.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (m *Sign1Message) Sign(rand io.Reader, external []byte, signer Signer) error {
if m == nil {
return errors.New("signing nil Sign1Message")
}
if m.Payload == nil {
return ErrMissingPayload
}
if len(m.Signature) > 0 {
return errors.New("Sign1Message signature already has signature bytes")
}
// check algorithm if present.
// `alg` header MUST be present if there is no externally supplied data.
alg := signer.Algorithm()
err := m.Headers.ensureSigningAlgorithm(alg, external)
if err != nil {
return err
}
// sign the message
toBeSigned, err := m.toBeSigned(external)
if err != nil {
return err
}
sig, err := signer.Sign(rand, toBeSigned)
if err != nil {
return err
}
m.Signature = sig
return nil
}
// Verify verifies the signature on the Sign1Message returning nil on success or
// a suitable error if verification fails.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (m *Sign1Message) Verify(external []byte, verifier Verifier) error {
if m == nil {
return errors.New("verifying nil Sign1Message")
}
if m.Payload == nil {
return ErrMissingPayload
}
if len(m.Signature) == 0 {
return ErrEmptySignature
}
// check algorithm if present.
// `alg` header MUST present if there is no externally supplied data.
alg := verifier.Algorithm()
err := m.Headers.ensureVerificationAlgorithm(alg, external)
if err != nil {
return err
}
// verify the message
toBeSigned, err := m.toBeSigned(external)
if err != nil {
return err
}
return verifier.Verify(toBeSigned, m.Signature)
}
// toBeSigned constructs Sig_structure, computes and returns ToBeSigned.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (m *Sign1Message) toBeSigned(external []byte) ([]byte, error) {
// create a Sig_structure and populate it with the appropriate fields.
//
// Sig_structure = [
// context : "Signature1",
// body_protected : empty_or_serialized_map,
// external_aad : bstr,
// payload : bstr
// ]
var protected cbor.RawMessage
protected, err := m.Headers.MarshalProtected()
if err != nil {
return nil, err
}
protected, err = deterministicBinaryString(protected)
if err != nil {
return nil, err
}
if external == nil {
external = []byte{}
}
sigStructure := []any{
"Signature1", // context
protected, // body_protected
external, // external_aad
m.Payload, // payload
}
// create the value ToBeSigned by encoding the Sig_structure to a byte
// string.
return encMode.Marshal(sigStructure)
}
func (m *Sign1Message) getContent() (sign1Message, error) {
if m == nil {
return sign1Message{}, errors.New("cbor: MarshalCBOR on nil Sign1Message pointer")
}
if len(m.Signature) == 0 {
return sign1Message{}, ErrEmptySignature
}
protected, unprotected, err := m.Headers.marshal()
if err != nil {
return sign1Message{}, err
}
content := sign1Message{
Protected: protected,
Unprotected: unprotected,
Payload: m.Payload,
Signature: m.Signature,
}
return content, nil
}
func (m *Sign1Message) doUnmarshal(data []byte) error {
// decode to sign1Message and parse
var raw sign1Message
if err := decModeWithTagsForbidden.Unmarshal(data, &raw); err != nil {
return err
}
if len(raw.Signature) == 0 {
return ErrEmptySignature
}
msg := Sign1Message{
Headers: Headers{
RawProtected: raw.Protected,
RawUnprotected: raw.Unprotected,
},
Payload: raw.Payload,
Signature: raw.Signature,
}
if err := msg.Headers.UnmarshalFromRaw(); err != nil {
return err
}
*m = msg
return nil
}
// Sign1 signs a Sign1Message using the provided Signer.
//
// This method is a wrapper of `Sign1Message.Sign()`.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func Sign1(rand io.Reader, signer Signer, headers Headers, payload []byte, external []byte) ([]byte, error) {
msg := Sign1Message{
Headers: headers,
Payload: payload,
}
err := msg.Sign(rand, external, signer)
if err != nil {
return nil, err
}
return msg.MarshalCBOR()
}
type UntaggedSign1Message Sign1Message
// MarshalCBOR encodes UntaggedSign1Message into a COSE_Sign1 object.
func (m *UntaggedSign1Message) MarshalCBOR() ([]byte, error) {
content, err := (*Sign1Message)(m).getContent()
if err != nil {
return nil, err
}
return encMode.Marshal(content)
}
// UnmarshalCBOR decodes a COSE_Sign1 object into an UntaggedSign1Message.
func (m *UntaggedSign1Message) UnmarshalCBOR(data []byte) error {
if m == nil {
return errors.New("cbor: UnmarshalCBOR on nil UntaggedSign1Message pointer")
}
if len(data) == 0 {
return errors.New("cbor: zero length data")
}
// fast message check - ensure the frist byte indicates a four-element array
if data[0] != sign1MessagePrefix[1] {
return errors.New("cbor: invalid COSE_Sign1 object")
}
return (*Sign1Message)(m).doUnmarshal(data)
}
// Sign signs an UnttaggedSign1Message using the provided Signer.
// The signature is stored in m.Signature.
//
// Note that m.Signature is only valid as long as m.Headers.Protected and
// m.Payload remain unchanged after calling this method.
// It is possible to modify m.Headers.Unprotected after signing,
// i.e., add counter signatures or timestamps.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (m *UntaggedSign1Message) Sign(rand io.Reader, external []byte, signer Signer) error {
return (*Sign1Message)(m).Sign(rand, external, signer)
}
// Verify verifies the signature on the UntaggedSign1Message returning nil on success or
// a suitable error if verification fails.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (m *UntaggedSign1Message) Verify(external []byte, verifier Verifier) error {
return (*Sign1Message)(m).Verify(external, verifier)
}
// Sign1Untagged signs an UntaggedSign1Message using the provided Signer.
//
// This method is a wrapper of `UntaggedSign1Message.Sign()`.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func Sign1Untagged(rand io.Reader, signer Signer, headers Headers, payload []byte, external []byte) ([]byte, error) {
msg := UntaggedSign1Message{
Headers: headers,
Payload: payload,
}
err := msg.Sign(rand, external, signer)
if err != nil {
return nil, err
}
return msg.MarshalCBOR()
}