certificates/acme/db/nosql/authz.go
Mariano Cano 6ba20209c2
Verify CSR key fingerprint with attestation certificate key
This commit makes sure that the attestation certificate key matches the
key used on the CSR on an ACME device attestation flow.
2023-02-09 16:48:43 -08:00

156 lines
4.6 KiB
Go

package nosql
import (
"context"
"encoding/json"
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/acme"
"github.com/smallstep/nosql"
)
// dbAuthz is the base authz type that others build from.
type dbAuthz struct {
ID string `json:"id"`
AccountID string `json:"accountID"`
Identifier acme.Identifier `json:"identifier"`
Status acme.Status `json:"status"`
Token string `json:"token"`
Fingerprint string `json:"fingerprint,omitempty"`
ChallengeIDs []string `json:"challengeIDs"`
Wildcard bool `json:"wildcard"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
Error *acme.Error `json:"error"`
}
func (ba *dbAuthz) clone() *dbAuthz {
u := *ba
return &u
}
// getDBAuthz retrieves and unmarshals a database representation of the
// ACME Authorization type.
func (db *DB) getDBAuthz(ctx context.Context, id string) (*dbAuthz, error) {
data, err := db.db.Get(authzTable, []byte(id))
if nosql.IsErrNotFound(err) {
return nil, acme.NewError(acme.ErrorMalformedType, "authz %s not found", id)
} else if err != nil {
return nil, errors.Wrapf(err, "error loading authz %s", id)
}
var dbaz dbAuthz
if err = json.Unmarshal(data, &dbaz); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling authz %s into dbAuthz", id)
}
return &dbaz, nil
}
// GetAuthorization retrieves and unmarshals an ACME authz type from the database.
// Implements acme.DB GetAuthorization interface.
func (db *DB) GetAuthorization(ctx context.Context, id string) (*acme.Authorization, error) {
dbaz, err := db.getDBAuthz(ctx, id)
if err != nil {
return nil, err
}
var chs = make([]*acme.Challenge, len(dbaz.ChallengeIDs))
for i, chID := range dbaz.ChallengeIDs {
chs[i], err = db.GetChallenge(ctx, chID, id)
if err != nil {
return nil, err
}
}
return &acme.Authorization{
ID: dbaz.ID,
AccountID: dbaz.AccountID,
Identifier: dbaz.Identifier,
Status: dbaz.Status,
Challenges: chs,
Wildcard: dbaz.Wildcard,
ExpiresAt: dbaz.ExpiresAt,
Token: dbaz.Token,
Fingerprint: dbaz.Fingerprint,
Error: dbaz.Error,
}, nil
}
// CreateAuthorization creates an entry in the database for the Authorization.
// Implements the acme.DB.CreateAuthorization interface.
func (db *DB) CreateAuthorization(ctx context.Context, az *acme.Authorization) error {
var err error
az.ID, err = randID()
if err != nil {
return err
}
chIDs := make([]string, len(az.Challenges))
for i, ch := range az.Challenges {
chIDs[i] = ch.ID
}
now := clock.Now()
dbaz := &dbAuthz{
ID: az.ID,
AccountID: az.AccountID,
Status: az.Status,
CreatedAt: now,
ExpiresAt: az.ExpiresAt,
Identifier: az.Identifier,
ChallengeIDs: chIDs,
Token: az.Token,
Fingerprint: az.Fingerprint,
Wildcard: az.Wildcard,
}
return db.save(ctx, az.ID, dbaz, nil, "authz", authzTable)
}
// UpdateAuthorization saves an updated ACME Authorization to the database.
func (db *DB) UpdateAuthorization(ctx context.Context, az *acme.Authorization) error {
old, err := db.getDBAuthz(ctx, az.ID)
if err != nil {
return err
}
nu := old.clone()
nu.Status = az.Status
nu.Fingerprint = az.Fingerprint
nu.Error = az.Error
return db.save(ctx, old.ID, nu, old, "authz", authzTable)
}
// GetAuthorizationsByAccountID retrieves and unmarshals ACME authz types from the database.
func (db *DB) GetAuthorizationsByAccountID(ctx context.Context, accountID string) ([]*acme.Authorization, error) {
entries, err := db.db.List(authzTable)
if err != nil {
return nil, errors.Wrapf(err, "error listing authz")
}
authzs := []*acme.Authorization{}
for _, entry := range entries {
dbaz := new(dbAuthz)
if err = json.Unmarshal(entry.Value, dbaz); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling dbAuthz key '%s' into dbAuthz struct", string(entry.Key))
}
// Filter out all dbAuthzs that don't belong to the accountID. This
// could be made more efficient with additional data structures mapping the
// Account ID to authorizations. Not trivial to do, though.
if dbaz.AccountID != accountID {
continue
}
authzs = append(authzs, &acme.Authorization{
ID: dbaz.ID,
AccountID: dbaz.AccountID,
Identifier: dbaz.Identifier,
Status: dbaz.Status,
Challenges: nil, // challenges not required for current use case
Wildcard: dbaz.Wildcard,
ExpiresAt: dbaz.ExpiresAt,
Token: dbaz.Token,
Fingerprint: dbaz.Fingerprint,
Error: dbaz.Error,
})
}
return authzs, nil
}