-
Notifications
You must be signed in to change notification settings - Fork 308
/
key_store.go
147 lines (123 loc) · 4.06 KB
/
key_store.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
// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package v1manifest
import (
"fmt"
"github.com/pingcap/errors"
"github.com/pingcap/tiup/pkg/repository/crypto"
)
// KeyStore tracks roles, keys, etc. and verifies signatures against this metadata.
type KeyStore map[string]roleKeys
type roleKeys struct {
threshold uint
expiry string
// key id -> public key
keys map[string]crypto.PubKey
}
// NewKeyStore return a KeyStore
func NewKeyStore() *KeyStore {
return &KeyStore{}
}
// AddKeys clears all keys for role, then adds all supplied keys and stores the threshold value.
func (s *KeyStore) AddKeys(role string, threshold uint, expiry string, keys map[string]*KeyInfo) error {
if threshold == 0 {
return errors.Errorf("invalid threshold (0)")
}
(*s)[role] = roleKeys{threshold: threshold, expiry: expiry, keys: map[string]crypto.PubKey{}}
for id, info := range keys {
pub, err := info.publicKey()
if err != nil {
return err
}
(*s)[role].keys[id] = pub
}
return nil
}
func newSignatureError(fname string, err error) *SignatureError {
return &SignatureError{
fname: fname,
err: err,
}
}
// IsSignatureError check if the err is SignatureError.
func IsSignatureError(err error) bool {
_, ok := err.(*SignatureError)
return ok
}
// SignatureError the signature of a file is incorrect.
type SignatureError struct {
fname string
err error
}
func (s *SignatureError) Error() string {
return fmt.Sprintf("invalid signature for file %s: %s", s.fname, s.err.Error())
}
// transitionRoot checks that signed is verified by signatures using newThreshold, and if so, updates the keys for the root
// role in the key store.
func (s *KeyStore) transitionRoot(signed []byte, newThreshold uint, expiry string, signatures []Signature, newKeys map[string]*KeyInfo) error {
oldKeys := (*s)[ManifestTypeRoot]
err := s.AddKeys(ManifestTypeRoot, newThreshold, expiry, newKeys)
if err != nil {
return err
}
err = s.verifySignature(signed, ManifestTypeRoot, signatures, ManifestFilenameRoot)
if err != nil {
// Restore the old root keys.
(*s)[ManifestTypeRoot] = oldKeys
return err
}
return nil
}
// verifySignature verifies all supplied signatures are correct for signed. Also, that there are at least threshold signatures,
// and that they all belong to the correct role and are correct for signed. It is permissible for signature keys to not
// exist (they will be ignored, and not count towards the threshold) but not for a signature to be incorrect.
func (s *KeyStore) verifySignature(signed []byte, role string, signatures []Signature, filename string) error {
if s == nil {
return nil
}
// Check for duplicate signatures.
has := make(map[string]struct{})
for _, sig := range signatures {
if _, ok := has[sig.KeyID]; ok {
return errors.Errorf("signature section of %s contains duplicate signatures", filename)
}
has[sig.KeyID] = struct{}{}
}
keys, ok := (*s)[role]
if !ok {
return errors.Errorf("Unknown role %s", role)
}
var validSigs uint = 0
for _, sig := range signatures {
key, ok := keys.keys[sig.KeyID]
if !ok {
continue
}
err := key.VerifySignature(signed, sig.Sig)
if err != nil {
return newSignatureError(filename, err)
}
validSigs++
}
// We may need to verify the root manifest with old keys. Once the most up to date root is found and verified, then
// the keys used to do so should be checked for expiry.
if role != ManifestTypeRoot {
if err := CheckExpiry(keys.expiry); err != nil {
return err
}
}
if validSigs < keys.threshold {
return errors.Errorf("not enough signatures (%v) for threshold %v in %s", validSigs, keys.threshold, filename)
}
return nil
}