package store
import (
"time"
bolt "go.etcd.io/bbolt"
)
// Certificates are stateless: once the CA signs one it stays cryptographically
// valid until it expires, even if the user is later deleted or the underlying
// key removed. This bucket is the revocation list that closes that window —
// SSH cert auth (internal/ssh) consults it on every handshake, so a revoked
// principal or key is refused immediately rather than only when the cert's
// short TTL runs out.
//
// Entries are keyed with a one-byte kind prefix so principals and key
// fingerprints share the bucket without colliding. The stored value is the
// revocation time (RFC3339), currently informational.
const (
revokedPrincipalPrefix = "p:"
revokedKeyPrefix = "k:"
)
func (s *Store) revoke(key string) error {
return s.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(bucketRevoked).Put([]byte(key), []byte(time.Now().UTC().Format(time.RFC3339)))
})
}
func (s *Store) unrevoke(key string) error {
return s.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(bucketRevoked).Delete([]byte(key))
})
}
func (s *Store) isRevoked(key string) (bool, error) {
var revoked bool
err := s.db.View(func(tx *bolt.Tx) error {
revoked = tx.Bucket(bucketRevoked).Get([]byte(key)) != nil
return nil
})
return revoked, err
}
// RevokePrincipal blocks every certificate bearing this principal (e.g. when
// the local user is deleted). UnrevokePrincipal lifts it, so re-creating a
// user with the same name restores access.
func (s *Store) RevokePrincipal(principal string) error {
return s.revoke(revokedPrincipalPrefix + principal)
}
func (s *Store) UnrevokePrincipal(principal string) error {
return s.unrevoke(revokedPrincipalPrefix + principal)
}
func (s *Store) IsPrincipalRevoked(principal string) (bool, error) {
return s.isRevoked(revokedPrincipalPrefix + principal)
}
// RevokeKey blocks any certificate issued for this public key fingerprint
// (e.g. when the key is removed from a user). UnrevokeKey lifts it.
func (s *Store) RevokeKey(fingerprint string) error { return s.revoke(revokedKeyPrefix + fingerprint) }
func (s *Store) UnrevokeKey(fingerprint string) error {
return s.unrevoke(revokedKeyPrefix + fingerprint)
}
func (s *Store) IsKeyRevoked(fingerprint string) (bool, error) {
return s.isRevoked(revokedKeyPrefix + fingerprint)
}