github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/account_key.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package asserts 21 22 import ( 23 "fmt" 24 "regexp" 25 "time" 26 ) 27 28 var validAccountKeyName = regexp.MustCompile(`^(?:[a-z0-9]+-?)*[a-z](?:-?[a-z0-9])*$`) 29 30 // AccountKey holds an account-key assertion, asserting a public key 31 // belonging to the account. 32 type AccountKey struct { 33 assertionBase 34 since time.Time 35 until time.Time 36 pubKey PublicKey 37 } 38 39 // AccountID returns the account-id of this account-key. 40 func (ak *AccountKey) AccountID() string { 41 return ak.HeaderString("account-id") 42 } 43 44 // Name returns the name of the account key. 45 func (ak *AccountKey) Name() string { 46 return ak.HeaderString("name") 47 } 48 49 func IsValidAccountKeyName(name string) bool { 50 return validAccountKeyName.MatchString(name) 51 } 52 53 // Since returns the time when the account key starts being valid. 54 func (ak *AccountKey) Since() time.Time { 55 return ak.since 56 } 57 58 // Until returns the time when the account key stops being valid. A zero time means the key is valid forever. 59 func (ak *AccountKey) Until() time.Time { 60 return ak.until 61 } 62 63 // PublicKeyID returns the key id used for lookup of the account key. 64 func (ak *AccountKey) PublicKeyID() string { 65 return ak.pubKey.ID() 66 } 67 68 // isKeyValidAt returns whether the account key is valid at 'when' time. 69 func (ak *AccountKey) isKeyValidAt(when time.Time) bool { 70 valid := when.After(ak.since) || when.Equal(ak.since) 71 if valid && !ak.until.IsZero() { 72 valid = when.Before(ak.until) 73 } 74 return valid 75 } 76 77 // publicKey returns the underlying public key of the account key. 78 func (ak *AccountKey) publicKey() PublicKey { 79 return ak.pubKey 80 } 81 82 func checkPublicKey(ab *assertionBase, keyIDName string) (PublicKey, error) { 83 pubKey, err := DecodePublicKey(ab.Body()) 84 if err != nil { 85 return nil, err 86 } 87 keyID, err := checkNotEmptyString(ab.headers, keyIDName) 88 if err != nil { 89 return nil, err 90 } 91 if keyID != pubKey.ID() { 92 return nil, fmt.Errorf("public key does not match provided key id") 93 } 94 return pubKey, nil 95 } 96 97 // Implement further consistency checks. 98 func (ak *AccountKey) checkConsistency(db RODatabase, acck *AccountKey) error { 99 if !db.IsTrustedAccount(ak.AuthorityID()) { 100 return fmt.Errorf("account-key assertion for %q is not signed by a directly trusted authority: %s", ak.AccountID(), ak.AuthorityID()) 101 } 102 _, err := db.Find(AccountType, map[string]string{ 103 "account-id": ak.AccountID(), 104 }) 105 if IsNotFound(err) { 106 return fmt.Errorf("account-key assertion for %q does not have a matching account assertion", ak.AccountID()) 107 } 108 if err != nil { 109 return err 110 } 111 // XXX: Make this unconditional once account-key assertions are required to have a name. 112 if ak.Name() != "" { 113 // Check that we don't end up with multiple keys with 114 // different IDs but the same account-id and name. 115 // Note that this is a non-transactional check-then-add, so 116 // is not a hard guarantee. Backstores that can implement a 117 // unique constraint should do so. 118 assertions, err := db.FindMany(AccountKeyType, map[string]string{ 119 "account-id": ak.AccountID(), 120 "name": ak.Name(), 121 }) 122 if err != nil && !IsNotFound(err) { 123 return err 124 } 125 for _, assertion := range assertions { 126 existingAccKey := assertion.(*AccountKey) 127 if ak.PublicKeyID() != existingAccKey.PublicKeyID() { 128 return fmt.Errorf("account-key assertion for %q with ID %q has the same name %q as existing ID %q", ak.AccountID(), ak.PublicKeyID(), ak.Name(), existingAccKey.PublicKeyID()) 129 } 130 } 131 } 132 return nil 133 } 134 135 // sanity 136 var _ consistencyChecker = (*AccountKey)(nil) 137 138 // Prerequisites returns references to this account-key's prerequisite assertions. 139 func (ak *AccountKey) Prerequisites() []*Ref { 140 return []*Ref{ 141 {Type: AccountType, PrimaryKey: []string{ak.AccountID()}}, 142 } 143 } 144 145 func assembleAccountKey(assert assertionBase) (Assertion, error) { 146 _, err := checkNotEmptyString(assert.headers, "account-id") 147 if err != nil { 148 return nil, err 149 } 150 151 // XXX: We should require name to be present after backfilling existing assertions. 152 _, ok := assert.headers["name"] 153 if ok { 154 _, err = checkStringMatches(assert.headers, "name", validAccountKeyName) 155 if err != nil { 156 return nil, err 157 } 158 } 159 160 since, err := checkRFC3339Date(assert.headers, "since") 161 if err != nil { 162 return nil, err 163 } 164 165 until, err := checkRFC3339DateWithDefault(assert.headers, "until", time.Time{}) 166 if err != nil { 167 return nil, err 168 } 169 if !until.IsZero() && until.Before(since) { 170 return nil, fmt.Errorf("'until' time cannot be before 'since' time") 171 } 172 173 pubk, err := checkPublicKey(&assert, "public-key-sha3-384") 174 if err != nil { 175 return nil, err 176 } 177 178 // ignore extra headers for future compatibility 179 return &AccountKey{ 180 assertionBase: assert, 181 since: since, 182 until: until, 183 pubKey: pubk, 184 }, nil 185 } 186 187 // AccountKeyRequest holds an account-key-request assertion, which is a self-signed request to prove that the requester holds the private key and wishes to create an account-key assertion for it. 188 type AccountKeyRequest struct { 189 assertionBase 190 since time.Time 191 until time.Time 192 pubKey PublicKey 193 } 194 195 // AccountID returns the account-id of this account-key-request. 196 func (akr *AccountKeyRequest) AccountID() string { 197 return akr.HeaderString("account-id") 198 } 199 200 // Name returns the name of the account key. 201 func (akr *AccountKeyRequest) Name() string { 202 return akr.HeaderString("name") 203 } 204 205 // Since returns the time when the requested account key starts being valid. 206 func (akr *AccountKeyRequest) Since() time.Time { 207 return akr.since 208 } 209 210 // Until returns the time when the requested account key stops being valid. A zero time means the key is valid forever. 211 func (akr *AccountKeyRequest) Until() time.Time { 212 return akr.until 213 } 214 215 // PublicKeyID returns the underlying public key ID of the requested account key. 216 func (akr *AccountKeyRequest) PublicKeyID() string { 217 return akr.pubKey.ID() 218 } 219 220 // signKey returns the underlying public key of the requested account key. 221 func (akr *AccountKeyRequest) signKey() PublicKey { 222 return akr.pubKey 223 } 224 225 // Implement further consistency checks. 226 func (akr *AccountKeyRequest) checkConsistency(db RODatabase, acck *AccountKey) error { 227 _, err := db.Find(AccountType, map[string]string{ 228 "account-id": akr.AccountID(), 229 }) 230 if IsNotFound(err) { 231 return fmt.Errorf("account-key-request assertion for %q does not have a matching account assertion", akr.AccountID()) 232 } 233 if err != nil { 234 return err 235 } 236 return nil 237 } 238 239 // sanity 240 var ( 241 _ consistencyChecker = (*AccountKeyRequest)(nil) 242 _ customSigner = (*AccountKeyRequest)(nil) 243 ) 244 245 // Prerequisites returns references to this account-key-request's prerequisite assertions. 246 func (akr *AccountKeyRequest) Prerequisites() []*Ref { 247 return []*Ref{ 248 {Type: AccountType, PrimaryKey: []string{akr.AccountID()}}, 249 } 250 } 251 252 func assembleAccountKeyRequest(assert assertionBase) (Assertion, error) { 253 _, err := checkNotEmptyString(assert.headers, "account-id") 254 if err != nil { 255 return nil, err 256 } 257 258 _, err = checkStringMatches(assert.headers, "name", validAccountKeyName) 259 if err != nil { 260 return nil, err 261 } 262 263 since, err := checkRFC3339Date(assert.headers, "since") 264 if err != nil { 265 return nil, err 266 } 267 268 until, err := checkRFC3339DateWithDefault(assert.headers, "until", time.Time{}) 269 if err != nil { 270 return nil, err 271 } 272 if !until.IsZero() && until.Before(since) { 273 return nil, fmt.Errorf("'until' time cannot be before 'since' time") 274 } 275 276 pubk, err := checkPublicKey(&assert, "public-key-sha3-384") 277 if err != nil { 278 return nil, err 279 } 280 281 // ignore extra headers for future compatibility 282 return &AccountKeyRequest{ 283 assertionBase: assert, 284 since: since, 285 until: until, 286 pubKey: pubk, 287 }, nil 288 }