forked from veraison/go-cose
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sign1.go
236 lines (214 loc) · 5.8 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
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) {
if m == nil {
return nil, errors.New("cbor: MarshalCBOR on nil Sign1Message pointer")
}
if len(m.Signature) == 0 {
return nil, ErrEmptySignature
}
protected, unprotected, err := m.Headers.marshal()
if err != nil {
return nil, err
}
content := sign1Message{
Protected: protected,
Unprotected: unprotected,
Payload: m.Payload,
Signature: m.Signature,
}
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")
}
// decode to sign1Message and parse
var raw sign1Message
if err := decModeWithTagsForbidden.Unmarshal(data[1:], &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
}
// Sign signs a Sign1Message using the provided Signer.
//
// 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
digest, err := m.digestToBeSigned(alg, external)
if err != nil {
return err
}
sig, err := signer.Sign(rand, digest)
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
digest, err := m.digestToBeSigned(alg, external)
if err != nil {
return err
}
return verifier.Verify(digest, m.Signature)
}
// digestToBeSigned constructs Sig_structure, computes ToBeSigned, and returns
// the digest of ToBeSigned.
// If the signing algorithm does not have a hash algorithm associated,
// ToBeSigned is returned instead.
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-4.4
func (m *Sign1Message) digestToBeSigned(alg Algorithm, 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
}
if external == nil {
external = []byte{}
}
sigStructure := []interface{}{
"Signature1", // context
protected, // body_protected
external, // external_aad
m.Payload, // payload
}
// create the value ToBeSigned by encoding the Sig_structure to a byte
// string.
toBeSigned, err := encMode.Marshal(sigStructure)
if err != nil {
return nil, err
}
// hash toBeSigned if there is a hash algorithm associated with the signing
// algorithm.
return alg.computeHash(toBeSigned)
}
// 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()
}