blob: cb2ca9a76904b066829037e9f75f4c5b4dda4a03 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package libtrust
2
3import (
4 "bytes"
5 "crypto"
6 "crypto/x509"
7 "encoding/base64"
8 "encoding/json"
9 "errors"
10 "fmt"
11 "sort"
12 "time"
13 "unicode"
14)
15
16var (
17 // ErrInvalidSignContent is used when the content to be signed is invalid.
18 ErrInvalidSignContent = errors.New("invalid sign content")
19
20 // ErrInvalidJSONContent is used when invalid json is encountered.
21 ErrInvalidJSONContent = errors.New("invalid json content")
22
23 // ErrMissingSignatureKey is used when the specified signature key
24 // does not exist in the JSON content.
25 ErrMissingSignatureKey = errors.New("missing signature key")
26)
27
28type jsHeader struct {
29 JWK PublicKey `json:"jwk,omitempty"`
30 Algorithm string `json:"alg"`
31 Chain []string `json:"x5c,omitempty"`
32}
33
34type jsSignature struct {
35 Header jsHeader `json:"header"`
36 Signature string `json:"signature"`
37 Protected string `json:"protected,omitempty"`
38}
39
40type jsSignaturesSorted []jsSignature
41
42func (jsbkid jsSignaturesSorted) Swap(i, j int) { jsbkid[i], jsbkid[j] = jsbkid[j], jsbkid[i] }
43func (jsbkid jsSignaturesSorted) Len() int { return len(jsbkid) }
44
45func (jsbkid jsSignaturesSorted) Less(i, j int) bool {
46 ki, kj := jsbkid[i].Header.JWK.KeyID(), jsbkid[j].Header.JWK.KeyID()
47 si, sj := jsbkid[i].Signature, jsbkid[j].Signature
48
49 if ki == kj {
50 return si < sj
51 }
52
53 return ki < kj
54}
55
56type signKey struct {
57 PrivateKey
58 Chain []*x509.Certificate
59}
60
61// JSONSignature represents a signature of a json object.
62type JSONSignature struct {
63 payload string
64 signatures []jsSignature
65 indent string
66 formatLength int
67 formatTail []byte
68}
69
70func newJSONSignature() *JSONSignature {
71 return &JSONSignature{
72 signatures: make([]jsSignature, 0, 1),
73 }
74}
75
76// Payload returns the encoded payload of the signature. This
77// payload should not be signed directly
78func (js *JSONSignature) Payload() ([]byte, error) {
79 return joseBase64UrlDecode(js.payload)
80}
81
82func (js *JSONSignature) protectedHeader() (string, error) {
83 protected := map[string]interface{}{
84 "formatLength": js.formatLength,
85 "formatTail": joseBase64UrlEncode(js.formatTail),
86 "time": time.Now().UTC().Format(time.RFC3339),
87 }
88 protectedBytes, err := json.Marshal(protected)
89 if err != nil {
90 return "", err
91 }
92
93 return joseBase64UrlEncode(protectedBytes), nil
94}
95
96func (js *JSONSignature) signBytes(protectedHeader string) ([]byte, error) {
97 buf := make([]byte, len(js.payload)+len(protectedHeader)+1)
98 copy(buf, protectedHeader)
99 buf[len(protectedHeader)] = '.'
100 copy(buf[len(protectedHeader)+1:], js.payload)
101 return buf, nil
102}
103
104// Sign adds a signature using the given private key.
105func (js *JSONSignature) Sign(key PrivateKey) error {
106 protected, err := js.protectedHeader()
107 if err != nil {
108 return err
109 }
110 signBytes, err := js.signBytes(protected)
111 if err != nil {
112 return err
113 }
114 sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256)
115 if err != nil {
116 return err
117 }
118
119 js.signatures = append(js.signatures, jsSignature{
120 Header: jsHeader{
121 JWK: key.PublicKey(),
122 Algorithm: algorithm,
123 },
124 Signature: joseBase64UrlEncode(sigBytes),
125 Protected: protected,
126 })
127
128 return nil
129}
130
131// SignWithChain adds a signature using the given private key
132// and setting the x509 chain. The public key of the first element
133// in the chain must be the public key corresponding with the sign key.
134func (js *JSONSignature) SignWithChain(key PrivateKey, chain []*x509.Certificate) error {
135 // Ensure key.Chain[0] is public key for key
136 //key.Chain.PublicKey
137 //key.PublicKey().CryptoPublicKey()
138
139 // Verify chain
140 protected, err := js.protectedHeader()
141 if err != nil {
142 return err
143 }
144 signBytes, err := js.signBytes(protected)
145 if err != nil {
146 return err
147 }
148 sigBytes, algorithm, err := key.Sign(bytes.NewReader(signBytes), crypto.SHA256)
149 if err != nil {
150 return err
151 }
152
153 header := jsHeader{
154 Chain: make([]string, len(chain)),
155 Algorithm: algorithm,
156 }
157
158 for i, cert := range chain {
159 header.Chain[i] = base64.StdEncoding.EncodeToString(cert.Raw)
160 }
161
162 js.signatures = append(js.signatures, jsSignature{
163 Header: header,
164 Signature: joseBase64UrlEncode(sigBytes),
165 Protected: protected,
166 })
167
168 return nil
169}
170
171// Verify verifies all the signatures and returns the list of
172// public keys used to sign. Any x509 chains are not checked.
173func (js *JSONSignature) Verify() ([]PublicKey, error) {
174 keys := make([]PublicKey, len(js.signatures))
175 for i, signature := range js.signatures {
176 signBytes, err := js.signBytes(signature.Protected)
177 if err != nil {
178 return nil, err
179 }
180 var publicKey PublicKey
181 if len(signature.Header.Chain) > 0 {
182 certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0])
183 if err != nil {
184 return nil, err
185 }
186 cert, err := x509.ParseCertificate(certBytes)
187 if err != nil {
188 return nil, err
189 }
190 publicKey, err = FromCryptoPublicKey(cert.PublicKey)
191 if err != nil {
192 return nil, err
193 }
194 } else if signature.Header.JWK != nil {
195 publicKey = signature.Header.JWK
196 } else {
197 return nil, errors.New("missing public key")
198 }
199
200 sigBytes, err := joseBase64UrlDecode(signature.Signature)
201 if err != nil {
202 return nil, err
203 }
204
205 err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes)
206 if err != nil {
207 return nil, err
208 }
209
210 keys[i] = publicKey
211 }
212 return keys, nil
213}
214
215// VerifyChains verifies all the signatures and the chains associated
216// with each signature and returns the list of verified chains.
217// Signatures without an x509 chain are not checked.
218func (js *JSONSignature) VerifyChains(ca *x509.CertPool) ([][]*x509.Certificate, error) {
219 chains := make([][]*x509.Certificate, 0, len(js.signatures))
220 for _, signature := range js.signatures {
221 signBytes, err := js.signBytes(signature.Protected)
222 if err != nil {
223 return nil, err
224 }
225 var publicKey PublicKey
226 if len(signature.Header.Chain) > 0 {
227 certBytes, err := base64.StdEncoding.DecodeString(signature.Header.Chain[0])
228 if err != nil {
229 return nil, err
230 }
231 cert, err := x509.ParseCertificate(certBytes)
232 if err != nil {
233 return nil, err
234 }
235 publicKey, err = FromCryptoPublicKey(cert.PublicKey)
236 if err != nil {
237 return nil, err
238 }
239 intermediates := x509.NewCertPool()
240 if len(signature.Header.Chain) > 1 {
241 intermediateChain := signature.Header.Chain[1:]
242 for i := range intermediateChain {
243 certBytes, err := base64.StdEncoding.DecodeString(intermediateChain[i])
244 if err != nil {
245 return nil, err
246 }
247 intermediate, err := x509.ParseCertificate(certBytes)
248 if err != nil {
249 return nil, err
250 }
251 intermediates.AddCert(intermediate)
252 }
253 }
254
255 verifyOptions := x509.VerifyOptions{
256 Intermediates: intermediates,
257 Roots: ca,
258 }
259
260 verifiedChains, err := cert.Verify(verifyOptions)
261 if err != nil {
262 return nil, err
263 }
264 chains = append(chains, verifiedChains...)
265
266 sigBytes, err := joseBase64UrlDecode(signature.Signature)
267 if err != nil {
268 return nil, err
269 }
270
271 err = publicKey.Verify(bytes.NewReader(signBytes), signature.Header.Algorithm, sigBytes)
272 if err != nil {
273 return nil, err
274 }
275 }
276
277 }
278 return chains, nil
279}
280
281// JWS returns JSON serialized JWS according to
282// http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-7.2
283func (js *JSONSignature) JWS() ([]byte, error) {
284 if len(js.signatures) == 0 {
285 return nil, errors.New("missing signature")
286 }
287
288 sort.Sort(jsSignaturesSorted(js.signatures))
289
290 jsonMap := map[string]interface{}{
291 "payload": js.payload,
292 "signatures": js.signatures,
293 }
294
295 return json.MarshalIndent(jsonMap, "", " ")
296}
297
298func notSpace(r rune) bool {
299 return !unicode.IsSpace(r)
300}
301
302func detectJSONIndent(jsonContent []byte) (indent string) {
303 if len(jsonContent) > 2 && jsonContent[0] == '{' && jsonContent[1] == '\n' {
304 quoteIndex := bytes.IndexRune(jsonContent[1:], '"')
305 if quoteIndex > 0 {
306 indent = string(jsonContent[2 : quoteIndex+1])
307 }
308 }
309 return
310}
311
312type jsParsedHeader struct {
313 JWK json.RawMessage `json:"jwk"`
314 Algorithm string `json:"alg"`
315 Chain []string `json:"x5c"`
316}
317
318type jsParsedSignature struct {
319 Header jsParsedHeader `json:"header"`
320 Signature string `json:"signature"`
321 Protected string `json:"protected"`
322}
323
324// ParseJWS parses a JWS serialized JSON object into a Json Signature.
325func ParseJWS(content []byte) (*JSONSignature, error) {
326 type jsParsed struct {
327 Payload string `json:"payload"`
328 Signatures []jsParsedSignature `json:"signatures"`
329 }
330 parsed := &jsParsed{}
331 err := json.Unmarshal(content, parsed)
332 if err != nil {
333 return nil, err
334 }
335 if len(parsed.Signatures) == 0 {
336 return nil, errors.New("missing signatures")
337 }
338 payload, err := joseBase64UrlDecode(parsed.Payload)
339 if err != nil {
340 return nil, err
341 }
342
343 js, err := NewJSONSignature(payload)
344 if err != nil {
345 return nil, err
346 }
347 js.signatures = make([]jsSignature, len(parsed.Signatures))
348 for i, signature := range parsed.Signatures {
349 header := jsHeader{
350 Algorithm: signature.Header.Algorithm,
351 }
352 if signature.Header.Chain != nil {
353 header.Chain = signature.Header.Chain
354 }
355 if signature.Header.JWK != nil {
356 publicKey, err := UnmarshalPublicKeyJWK([]byte(signature.Header.JWK))
357 if err != nil {
358 return nil, err
359 }
360 header.JWK = publicKey
361 }
362 js.signatures[i] = jsSignature{
363 Header: header,
364 Signature: signature.Signature,
365 Protected: signature.Protected,
366 }
367 }
368
369 return js, nil
370}
371
372// NewJSONSignature returns a new unsigned JWS from a json byte array.
373// JSONSignature will need to be signed before serializing or storing.
374// Optionally, one or more signatures can be provided as byte buffers,
375// containing serialized JWS signatures, to assemble a fully signed JWS
376// package. It is the callers responsibility to ensure uniqueness of the
377// provided signatures.
378func NewJSONSignature(content []byte, signatures ...[]byte) (*JSONSignature, error) {
379 var dataMap map[string]interface{}
380 err := json.Unmarshal(content, &dataMap)
381 if err != nil {
382 return nil, err
383 }
384
385 js := newJSONSignature()
386 js.indent = detectJSONIndent(content)
387
388 js.payload = joseBase64UrlEncode(content)
389
390 // Find trailing } and whitespace, put in protected header
391 closeIndex := bytes.LastIndexFunc(content, notSpace)
392 if content[closeIndex] != '}' {
393 return nil, ErrInvalidJSONContent
394 }
395 lastRuneIndex := bytes.LastIndexFunc(content[:closeIndex], notSpace)
396 if content[lastRuneIndex] == ',' {
397 return nil, ErrInvalidJSONContent
398 }
399 js.formatLength = lastRuneIndex + 1
400 js.formatTail = content[js.formatLength:]
401
402 if len(signatures) > 0 {
403 for _, signature := range signatures {
404 var parsedJSig jsParsedSignature
405
406 if err := json.Unmarshal(signature, &parsedJSig); err != nil {
407 return nil, err
408 }
409
410 // TODO(stevvooe): A lot of the code below is repeated in
411 // ParseJWS. It will require more refactoring to fix that.
412 jsig := jsSignature{
413 Header: jsHeader{
414 Algorithm: parsedJSig.Header.Algorithm,
415 },
416 Signature: parsedJSig.Signature,
417 Protected: parsedJSig.Protected,
418 }
419
420 if parsedJSig.Header.Chain != nil {
421 jsig.Header.Chain = parsedJSig.Header.Chain
422 }
423
424 if parsedJSig.Header.JWK != nil {
425 publicKey, err := UnmarshalPublicKeyJWK([]byte(parsedJSig.Header.JWK))
426 if err != nil {
427 return nil, err
428 }
429 jsig.Header.JWK = publicKey
430 }
431
432 js.signatures = append(js.signatures, jsig)
433 }
434 }
435
436 return js, nil
437}
438
439// NewJSONSignatureFromMap returns a new unsigned JSONSignature from a map or
440// struct. JWS will need to be signed before serializing or storing.
441func NewJSONSignatureFromMap(content interface{}) (*JSONSignature, error) {
442 switch content.(type) {
443 case map[string]interface{}:
444 case struct{}:
445 default:
446 return nil, errors.New("invalid data type")
447 }
448
449 js := newJSONSignature()
450 js.indent = " "
451
452 payload, err := json.MarshalIndent(content, "", js.indent)
453 if err != nil {
454 return nil, err
455 }
456 js.payload = joseBase64UrlEncode(payload)
457
458 // Remove '\n}' from formatted section, put in protected header
459 js.formatLength = len(payload) - 2
460 js.formatTail = payload[js.formatLength:]
461
462 return js, nil
463}
464
465func readIntFromMap(key string, m map[string]interface{}) (int, bool) {
466 value, ok := m[key]
467 if !ok {
468 return 0, false
469 }
470 switch v := value.(type) {
471 case int:
472 return v, true
473 case float64:
474 return int(v), true
475 default:
476 return 0, false
477 }
478}
479
480func readStringFromMap(key string, m map[string]interface{}) (v string, ok bool) {
481 value, ok := m[key]
482 if !ok {
483 return "", false
484 }
485 v, ok = value.(string)
486 return
487}
488
489// ParsePrettySignature parses a formatted signature into a
490// JSON signature. If the signatures are missing the format information
491// an error is thrown. The formatted signature must be created by
492// the same method as format signature.
493func ParsePrettySignature(content []byte, signatureKey string) (*JSONSignature, error) {
494 var contentMap map[string]json.RawMessage
495 err := json.Unmarshal(content, &contentMap)
496 if err != nil {
497 return nil, fmt.Errorf("error unmarshalling content: %s", err)
498 }
499 sigMessage, ok := contentMap[signatureKey]
500 if !ok {
501 return nil, ErrMissingSignatureKey
502 }
503
504 var signatureBlocks []jsParsedSignature
505 err = json.Unmarshal([]byte(sigMessage), &signatureBlocks)
506 if err != nil {
507 return nil, fmt.Errorf("error unmarshalling signatures: %s", err)
508 }
509
510 js := newJSONSignature()
511 js.signatures = make([]jsSignature, len(signatureBlocks))
512
513 for i, signatureBlock := range signatureBlocks {
514 protectedBytes, err := joseBase64UrlDecode(signatureBlock.Protected)
515 if err != nil {
516 return nil, fmt.Errorf("base64 decode error: %s", err)
517 }
518 var protectedHeader map[string]interface{}
519 err = json.Unmarshal(protectedBytes, &protectedHeader)
520 if err != nil {
521 return nil, fmt.Errorf("error unmarshalling protected header: %s", err)
522 }
523
524 formatLength, ok := readIntFromMap("formatLength", protectedHeader)
525 if !ok {
526 return nil, errors.New("missing formatted length")
527 }
528 encodedTail, ok := readStringFromMap("formatTail", protectedHeader)
529 if !ok {
530 return nil, errors.New("missing formatted tail")
531 }
532 formatTail, err := joseBase64UrlDecode(encodedTail)
533 if err != nil {
534 return nil, fmt.Errorf("base64 decode error on tail: %s", err)
535 }
536 if js.formatLength == 0 {
537 js.formatLength = formatLength
538 } else if js.formatLength != formatLength {
539 return nil, errors.New("conflicting format length")
540 }
541 if len(js.formatTail) == 0 {
542 js.formatTail = formatTail
543 } else if bytes.Compare(js.formatTail, formatTail) != 0 {
544 return nil, errors.New("conflicting format tail")
545 }
546
547 header := jsHeader{
548 Algorithm: signatureBlock.Header.Algorithm,
549 Chain: signatureBlock.Header.Chain,
550 }
551 if signatureBlock.Header.JWK != nil {
552 publicKey, err := UnmarshalPublicKeyJWK([]byte(signatureBlock.Header.JWK))
553 if err != nil {
554 return nil, fmt.Errorf("error unmarshalling public key: %s", err)
555 }
556 header.JWK = publicKey
557 }
558 js.signatures[i] = jsSignature{
559 Header: header,
560 Signature: signatureBlock.Signature,
561 Protected: signatureBlock.Protected,
562 }
563 }
564 if js.formatLength > len(content) {
565 return nil, errors.New("invalid format length")
566 }
567 formatted := make([]byte, js.formatLength+len(js.formatTail))
568 copy(formatted, content[:js.formatLength])
569 copy(formatted[js.formatLength:], js.formatTail)
570 js.indent = detectJSONIndent(formatted)
571 js.payload = joseBase64UrlEncode(formatted)
572
573 return js, nil
574}
575
576// PrettySignature formats a json signature into an easy to read
577// single json serialized object.
578func (js *JSONSignature) PrettySignature(signatureKey string) ([]byte, error) {
579 if len(js.signatures) == 0 {
580 return nil, errors.New("no signatures")
581 }
582 payload, err := joseBase64UrlDecode(js.payload)
583 if err != nil {
584 return nil, err
585 }
586 payload = payload[:js.formatLength]
587
588 sort.Sort(jsSignaturesSorted(js.signatures))
589
590 var marshalled []byte
591 var marshallErr error
592 if js.indent != "" {
593 marshalled, marshallErr = json.MarshalIndent(js.signatures, js.indent, js.indent)
594 } else {
595 marshalled, marshallErr = json.Marshal(js.signatures)
596 }
597 if marshallErr != nil {
598 return nil, marshallErr
599 }
600
601 buf := bytes.NewBuffer(make([]byte, 0, len(payload)+len(marshalled)+34))
602 buf.Write(payload)
603 buf.WriteByte(',')
604 if js.indent != "" {
605 buf.WriteByte('\n')
606 buf.WriteString(js.indent)
607 buf.WriteByte('"')
608 buf.WriteString(signatureKey)
609 buf.WriteString("\": ")
610 buf.Write(marshalled)
611 buf.WriteByte('\n')
612 } else {
613 buf.WriteByte('"')
614 buf.WriteString(signatureKey)
615 buf.WriteString("\":")
616 buf.Write(marshalled)
617 }
618 buf.WriteByte('}')
619
620 return buf.Bytes(), nil
621}
622
623// Signatures provides the signatures on this JWS as opaque blobs, sorted by
624// keyID. These blobs can be stored and reassembled with payloads. Internally,
625// they are simply marshaled json web signatures but implementations should
626// not rely on this.
627func (js *JSONSignature) Signatures() ([][]byte, error) {
628 sort.Sort(jsSignaturesSorted(js.signatures))
629
630 var sb [][]byte
631 for _, jsig := range js.signatures {
632 p, err := json.Marshal(jsig)
633 if err != nil {
634 return nil, err
635 }
636
637 sb = append(sb, p)
638 }
639
640 return sb, nil
641}
642
643// Merge combines the signatures from one or more other signatures into the
644// method receiver. If the payloads differ for any argument, an error will be
645// returned and the receiver will not be modified.
646func (js *JSONSignature) Merge(others ...*JSONSignature) error {
647 merged := js.signatures
648 for _, other := range others {
649 if js.payload != other.payload {
650 return fmt.Errorf("payloads differ from merge target")
651 }
652 merged = append(merged, other.signatures...)
653 }
654
655 js.signatures = merged
656 return nil
657}