podman

Форк
0
657 строк · 17.3 Кб
1
package libtrust
2

3
import (
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

16
var (
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

28
type jsHeader struct {
29
	JWK       PublicKey `json:"jwk,omitempty"`
30
	Algorithm string    `json:"alg"`
31
	Chain     []string  `json:"x5c,omitempty"`
32
}
33

34
type jsSignature struct {
35
	Header    jsHeader `json:"header"`
36
	Signature string   `json:"signature"`
37
	Protected string   `json:"protected,omitempty"`
38
}
39

40
type jsSignaturesSorted []jsSignature
41

42
func (jsbkid jsSignaturesSorted) Swap(i, j int) { jsbkid[i], jsbkid[j] = jsbkid[j], jsbkid[i] }
43
func (jsbkid jsSignaturesSorted) Len() int      { return len(jsbkid) }
44

45
func (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

56
type signKey struct {
57
	PrivateKey
58
	Chain []*x509.Certificate
59
}
60

61
// JSONSignature represents a signature of a json object.
62
type JSONSignature struct {
63
	payload      string
64
	signatures   []jsSignature
65
	indent       string
66
	formatLength int
67
	formatTail   []byte
68
}
69

70
func 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
78
func (js *JSONSignature) Payload() ([]byte, error) {
79
	return joseBase64UrlDecode(js.payload)
80
}
81

82
func (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

96
func (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.
105
func (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.
134
func (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.
173
func (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.
218
func (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
283
func (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

298
func notSpace(r rune) bool {
299
	return !unicode.IsSpace(r)
300
}
301

302
func 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

312
type jsParsedHeader struct {
313
	JWK       json.RawMessage `json:"jwk"`
314
	Algorithm string          `json:"alg"`
315
	Chain     []string        `json:"x5c"`
316
}
317

318
type 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.
325
func 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.
378
func 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.
441
func 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

465
func 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

480
func 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.
493
func 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.
578
func (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.
627
func (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.
646
func (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
}
658

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.