Documentation
¶
Overview ¶
Package crypt seals the per-device secrets a storage backend must retain on Apple's behalf with AES-256-GCM under a named key from a secrets.Provider.
Why ¶
The check-in protocol hands the server an UnlockToken in TokenUpdate and a BootstrapToken in SetBootstrapToken, and the push certificate store holds private keys; a copy of the database alone must not expose them. Phase 4 of the plan of record adds this package (decision record 0013): a Keyring with one active key and any number of retired ones, additional authenticated data that binds every blob to its table, column, and row id so a ciphertext cannot be moved to another row, and a Strict mode that refuses plaintext rows. The key name travels in the ciphertext header, which lets an operator rotate by naming a new active key while keeping the retired key in the accepted list until every stored value has been rewrapped.
Which columns are sealed, and the Rewrap that walks them, live in the SQL backends through sqlcommon. This package knows only bytes, keys, and AAD.
References ¶
- Decision record 0011: docs/research/decisions/0011-secrets-provider.md
- Decision record 0013: docs/research/decisions/0013-secrets-at-rest.md
- Plan of record: docs/research/implementation_plan.md (phase 4)
- Threat model: docs/security/threat-model.md (Storage disclosure and ciphertext row swap rows)
- Apple: https://developer.apple.com/documentation/devicemanagement/check-in
- Apple: https://developer.apple.com/documentation/devicemanagement/managing-certificates-for-device-management-services-and-devices
- Schema: third_party/device-management/mdm/checkin/tokenupdate.yaml (UnlockToken)
- Schema: third_party/device-management/mdm/checkin/setbootstraptoken.yaml (BootstrapToken)
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoKeyring is returned when Seal or Open is called on a nil Keyring. ErrNoKeyring = errors.New("crypt: no keyring configured") // ErrUnknownKey is returned when a header names a key the ring does not hold. ErrUnknownKey = errors.New("crypt: ciphertext uses an unknown key") // ErrTampered is returned when authentication fails, including a wrong AAD. ErrTampered = errors.New("crypt: authentication failed") // ErrUnsealed is returned by strict callers that meet a value without the header. ErrUnsealed = errors.New("crypt: value is not sealed") // ErrWeakKey is returned when a provider supplies fewer than 16 bytes of key material. ErrWeakKey = errors.New("crypt: key material shorter than 16 bytes") // ErrBadFormat is returned for input that is not a well formed sealed value. ErrBadFormat = errors.New("crypt: malformed ciphertext") // ErrNoActive is returned when Keys.Active is empty. ErrNoActive = errors.New("crypt: no active key name") )
Sentinel errors returned by this package. Callers should test them with errors.Is because the returned values may carry extra context.
Functions ¶
func AAD ¶
AAD builds the associated data that binds a value to its table column and row, as purpose, a NUL byte, then rowID. Moving a sealed value to another column or row therefore fails to open.
Types ¶
type Keyring ¶
type Keyring struct {
// contains filtered or unexported fields
}
Keyring holds the derived AEAD for each key name. It is safe for concurrent use once constructed.
func NewKeyring ¶
NewKeyring fetches every named key from the provider once, so a provider failure is a construction error rather than a surprise at the first Seal. Provider bytes are normalised to a 32 byte AES key with HKDF over SHA-256, salted with the key name, so two names backed by the same material still yield distinct keys.
func (*Keyring) Open ¶
Open decrypts a sealed value and reports which key name it used. It returns ErrBadFormat for input that is not sealed or is too short, ErrUnknownKey when the header names a key the ring does not hold, and ErrTampered when authentication fails, which includes a wrong aad.
func (*Keyring) Seal ¶
Seal encrypts plaintext under the active key and returns the sealed value: magic, key name length, key name, nonce, then the AES-GCM output including its tag. Empty plaintext is allowed and yields a sealed empty value. The aad is authenticated but not stored; Open must be given the same bytes.
type Keys ¶
type Keys struct {
// Active is the key name every Seal uses.
Active string
// Accepted lists retired key names Open still honours.
Accepted []string
// Strict makes callers refuse unsealed values (set once Rewrap has run everywhere).
Strict bool
}
Keys names the active key and the retired keys still accepted on read.