1/*-
2 * Copyright 2014 Square Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package jose
18
19import (
20	"encoding/base64"
21	"errors"
22	"fmt"
23	"strings"
24
25	"gopkg.in/square/go-jose.v2/json"
26)
27
28// rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
29type rawJSONWebSignature struct {
30	Payload    *byteBuffer        `json:"payload,omitempty"`
31	Signatures []rawSignatureInfo `json:"signatures,omitempty"`
32	Protected  *byteBuffer        `json:"protected,omitempty"`
33	Header     *rawHeader         `json:"header,omitempty"`
34	Signature  *byteBuffer        `json:"signature,omitempty"`
35}
36
37// rawSignatureInfo represents a single JWS signature over the JWS payload and protected header.
38type rawSignatureInfo struct {
39	Protected *byteBuffer `json:"protected,omitempty"`
40	Header    *rawHeader  `json:"header,omitempty"`
41	Signature *byteBuffer `json:"signature,omitempty"`
42}
43
44// JSONWebSignature represents a signed JWS object after parsing.
45type JSONWebSignature struct {
46	payload []byte
47	// Signatures attached to this object (may be more than one for multi-sig).
48	// Be careful about accessing these directly, prefer to use Verify() or
49	// VerifyMulti() to ensure that the data you're getting is verified.
50	Signatures []Signature
51}
52
53// Signature represents a single signature over the JWS payload and protected header.
54type Signature struct {
55	// Merged header fields. Contains both protected and unprotected header
56	// values. Prefer using Protected and Unprotected fields instead of this.
57	// Values in this header may or may not have been signed and in general
58	// should not be trusted.
59	Header Header
60
61	// Protected header. Values in this header were signed and
62	// will be verified as part of the signature verification process.
63	Protected Header
64
65	// Unprotected header. Values in this header were not signed
66	// and in general should not be trusted.
67	Unprotected Header
68
69	// The actual signature value
70	Signature []byte
71
72	protected *rawHeader
73	header    *rawHeader
74	original  *rawSignatureInfo
75}
76
77// ParseSigned parses a signed message in compact or full serialization format.
78func ParseSigned(input string) (*JSONWebSignature, error) {
79	input = stripWhitespace(input)
80	if strings.HasPrefix(input, "{") {
81		return parseSignedFull(input)
82	}
83
84	return parseSignedCompact(input)
85}
86
87// Get a header value
88func (sig Signature) mergedHeaders() rawHeader {
89	out := rawHeader{}
90	out.merge(sig.protected)
91	out.merge(sig.header)
92	return out
93}
94
95// Compute data to be signed
96func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
97	var serializedProtected string
98
99	if signature.original != nil && signature.original.Protected != nil {
100		serializedProtected = signature.original.Protected.base64()
101	} else if signature.protected != nil {
102		serializedProtected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON(signature.protected))
103	} else {
104		serializedProtected = ""
105	}
106
107	return []byte(fmt.Sprintf("%s.%s",
108		serializedProtected,
109		base64.RawURLEncoding.EncodeToString(payload)))
110}
111
112// parseSignedFull parses a message in full format.
113func parseSignedFull(input string) (*JSONWebSignature, error) {
114	var parsed rawJSONWebSignature
115	err := json.Unmarshal([]byte(input), &parsed)
116	if err != nil {
117		return nil, err
118	}
119
120	return parsed.sanitized()
121}
122
123// sanitized produces a cleaned-up JWS object from the raw JSON.
124func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
125	if parsed.Payload == nil {
126		return nil, fmt.Errorf("square/go-jose: missing payload in JWS message")
127	}
128
129	obj := &JSONWebSignature{
130		payload:    parsed.Payload.bytes(),
131		Signatures: make([]Signature, len(parsed.Signatures)),
132	}
133
134	if len(parsed.Signatures) == 0 {
135		// No signatures array, must be flattened serialization
136		signature := Signature{}
137		if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
138			signature.protected = &rawHeader{}
139			err := json.Unmarshal(parsed.Protected.bytes(), signature.protected)
140			if err != nil {
141				return nil, err
142			}
143		}
144
145		// Check that there is not a nonce in the unprotected header
146		if parsed.Header != nil && parsed.Header.getNonce() != "" {
147			return nil, ErrUnprotectedNonce
148		}
149
150		signature.header = parsed.Header
151		signature.Signature = parsed.Signature.bytes()
152		// Make a fake "original" rawSignatureInfo to store the unprocessed
153		// Protected header. This is necessary because the Protected header can
154		// contain arbitrary fields not registered as part of the spec. See
155		// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
156		// If we unmarshal Protected into a rawHeader with its explicit list of fields,
157		// we cannot marshal losslessly. So we have to keep around the original bytes.
158		// This is used in computeAuthData, which will first attempt to use
159		// the original bytes of a protected header, and fall back on marshaling the
160		// header struct only if those bytes are not available.
161		signature.original = &rawSignatureInfo{
162			Protected: parsed.Protected,
163			Header:    parsed.Header,
164			Signature: parsed.Signature,
165		}
166
167		var err error
168		signature.Header, err = signature.mergedHeaders().sanitized()
169		if err != nil {
170			return nil, err
171		}
172
173		if signature.header != nil {
174			signature.Unprotected, err = signature.header.sanitized()
175			if err != nil {
176				return nil, err
177			}
178		}
179
180		if signature.protected != nil {
181			signature.Protected, err = signature.protected.sanitized()
182			if err != nil {
183				return nil, err
184			}
185		}
186
187		// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
188		jwk := signature.Header.JSONWebKey
189		if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
190			return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key")
191		}
192
193		obj.Signatures = append(obj.Signatures, signature)
194	}
195
196	for i, sig := range parsed.Signatures {
197		if sig.Protected != nil && len(sig.Protected.bytes()) > 0 {
198			obj.Signatures[i].protected = &rawHeader{}
199			err := json.Unmarshal(sig.Protected.bytes(), obj.Signatures[i].protected)
200			if err != nil {
201				return nil, err
202			}
203		}
204
205		// Check that there is not a nonce in the unprotected header
206		if sig.Header != nil && sig.Header.getNonce() != "" {
207			return nil, ErrUnprotectedNonce
208		}
209
210		var err error
211		obj.Signatures[i].Header, err = obj.Signatures[i].mergedHeaders().sanitized()
212		if err != nil {
213			return nil, err
214		}
215
216		if obj.Signatures[i].header != nil {
217			obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
218			if err != nil {
219				return nil, err
220			}
221		}
222
223		if obj.Signatures[i].protected != nil {
224			obj.Signatures[i].Protected, err = obj.Signatures[i].protected.sanitized()
225			if err != nil {
226				return nil, err
227			}
228		}
229
230		obj.Signatures[i].Signature = sig.Signature.bytes()
231
232		// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
233		jwk := obj.Signatures[i].Header.JSONWebKey
234		if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
235			return nil, errors.New("square/go-jose: invalid embedded jwk, must be public key")
236		}
237
238		// Copy value of sig
239		original := sig
240
241		obj.Signatures[i].header = sig.Header
242		obj.Signatures[i].original = &original
243	}
244
245	return obj, nil
246}
247
248// parseSignedCompact parses a message in compact format.
249func parseSignedCompact(input string) (*JSONWebSignature, error) {
250	parts := strings.Split(input, ".")
251	if len(parts) != 3 {
252		return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts")
253	}
254
255	rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
256	if err != nil {
257		return nil, err
258	}
259
260	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
261	if err != nil {
262		return nil, err
263	}
264
265	signature, err := base64.RawURLEncoding.DecodeString(parts[2])
266	if err != nil {
267		return nil, err
268	}
269
270	raw := &rawJSONWebSignature{
271		Payload:   newBuffer(payload),
272		Protected: newBuffer(rawProtected),
273		Signature: newBuffer(signature),
274	}
275	return raw.sanitized()
276}
277
278// CompactSerialize serializes an object using the compact serialization format.
279func (obj JSONWebSignature) CompactSerialize() (string, error) {
280	if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil {
281		return "", ErrNotSupported
282	}
283
284	serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
285
286	return fmt.Sprintf(
287		"%s.%s.%s",
288		base64.RawURLEncoding.EncodeToString(serializedProtected),
289		base64.RawURLEncoding.EncodeToString(obj.payload),
290		base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)), nil
291}
292
293// FullSerialize serializes an object using the full JSON serialization format.
294func (obj JSONWebSignature) FullSerialize() string {
295	raw := rawJSONWebSignature{
296		Payload: newBuffer(obj.payload),
297	}
298
299	if len(obj.Signatures) == 1 {
300		if obj.Signatures[0].protected != nil {
301			serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
302			raw.Protected = newBuffer(serializedProtected)
303		}
304		raw.Header = obj.Signatures[0].header
305		raw.Signature = newBuffer(obj.Signatures[0].Signature)
306	} else {
307		raw.Signatures = make([]rawSignatureInfo, len(obj.Signatures))
308		for i, signature := range obj.Signatures {
309			raw.Signatures[i] = rawSignatureInfo{
310				Header:    signature.header,
311				Signature: newBuffer(signature.Signature),
312			}
313
314			if signature.protected != nil {
315				raw.Signatures[i].Protected = newBuffer(mustSerializeJSON(signature.protected))
316			}
317		}
318	}
319
320	return string(mustSerializeJSON(raw))
321}
322