github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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  }