From defd27f75df3a6eef84ac33adf89b1ce255e738c Mon Sep 17 00:00:00 2001 From: "Joe Richey joerichey@google.com" Date: Wed, 31 May 2017 17:54:35 -0700 Subject: actions: Simplify the callback mechanism This commit makes the callbacks for getting keys easier to understand. Functions which need keys now take a KeyFunc callback. This callback contains a ProtectorInfo parameter (basically a read-only version of metadata.ProtectorData) and a boolean which indicates if the call is being retried. The documentation is also updated to say which functions will retry the KeyFunc. For selecting a protector, there is now an OptionFunc callback which takes a slice of ProtectorOptions. A ProtectorOption is a ProtectorInfo along with additional information about a linked filesystem (if applicable). This commit also adds in methods for getting the protector options for a specific filesystem or policy. It also adds a function for getting the policy descriptor for a specific path. Change-Id: I41e0d94ffd44e7166b0c5cf1b5d18437960bdf90 --- actions/callback.go | 105 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 39 deletions(-) (limited to 'actions/callback.go') diff --git a/actions/callback.go b/actions/callback.go index c1d2c8a..18be670 100644 --- a/actions/callback.go +++ b/actions/callback.go @@ -22,67 +22,83 @@ package actions import ( "fscrypt/crypto" + "fscrypt/filesystem" "fscrypt/metadata" "log" ) -// ProtectorData is the information a caller will receive about a Protector +// ProtectorInfo is the information a caller will receive about a Protector // before they have to return the corresponding key. This is currently a // read-only view of metadata.ProtectorData. -type ProtectorData interface { - GetProtectorDescriptor() string - GetSource() metadata.SourceType - GetName() string - GetUid() int64 +type ProtectorInfo struct { + data *metadata.ProtectorData } -// KeyCallback is passed to a function that will require a key from the caller. -// For passphrase sources, the returned key should be a password. For raw -// sources, the returned key should be a standard cryptographic key. Consumers -// of the callback will wipe the provided key. If the callback returns an error, -// the function to which the callback is passed returns that error. Note that -// when using the key to unwrap a known key, the callback will be executed until -// the correct key is returned or an error is returned. -type KeyCallback func(data ProtectorData) (*crypto.Key, error) +// Descriptor is the Protector's descriptor used to uniquely identify it. +func (pi *ProtectorInfo) Descriptor() string { return pi.data.GetProtectorDescriptor() } + +// Source indicates the type of the descriptor (how it should be unlocked). +func (pi *ProtectorInfo) Source() metadata.SourceType { return pi.data.GetSource() } + +// Name is used to describe custom passphrase and raw key descriptors. +func (pi *ProtectorInfo) Name() string { return pi.data.GetName() } + +// UID is used to identify the user for login passphrases. +func (pi *ProtectorInfo) UID() int64 { return pi.data.GetUid() } + +// KeyFunc is passed to a function that will require some type of key. +// The info parameter is provided so the callback knows which key to provide. +// The retry parameter indicates that a previous key provided by this callback +// was incorrect (this allows for user feedback like "incorrect passphrase"). +// +// For passphrase sources, the returned key should be a passphrase. For raw +// sources, the returned key should be a 256-bit cryptographic key. Consumers +// of the callback will wipe the returned key. An error returned by the callback +// will be propagated back to the caller. +type KeyFunc func(info ProtectorInfo, retry bool) (*crypto.Key, error) // getWrappingKey uses the provided callback to get the wrapping key -// corresponding to the ProtectorData. This runs the passphrase hash for +// corresponding to the ProtectorInfo. This runs the passphrase hash for // passphrase sources or just relays the callback for raw sources. -func getWrappingKey(data *metadata.ProtectorData, callback KeyCallback) (*crypto.Key, error) { - // We don't need to go anything for raw keys - if data.Source == metadata.SourceType_raw_key { - return callback(data) +func getWrappingKey(info ProtectorInfo, keyFn KeyFunc, retry bool) (*crypto.Key, error) { + // For raw key sources, we can just use the key directly. + if info.Source() == metadata.SourceType_raw_key { + return keyFn(info, retry) } // Run the passphrase hash for other sources. - passphrase, err := callback(data) + passphrase, err := keyFn(info, retry) if err != nil { return nil, err } defer passphrase.Wipe() - log.Printf("running passphrase hash for protector %s", data.ProtectorDescriptor) - return crypto.PassphraseHash(passphrase, data.Salt, data.Costs) + log.Printf("running passphrase hash for protector %s", info.Descriptor()) + return crypto.PassphraseHash(passphrase, info.data.Salt, info.data.Costs) } -// unwrapProtectorKey uses the provided callback and protector data to return -// the unwrapped protector key. This will repeatedly use the callback to get the -// wrapping key until the correct key is returned or an error is returned. -func unwrapProtectorKey(data *metadata.ProtectorData, callback KeyCallback) (*crypto.Key, error) { +// unwrapProtectorKey uses the provided callback and ProtectorInfo to return +// the unwrapped protector key. This will repeatedly call keyFn to get the +// wrapping key until the correct key is returned by the callback or the +// callback returns an error. +func unwrapProtectorKey(info ProtectorInfo, keyFn KeyFunc) (*crypto.Key, error) { + retry := false for { - wrappingKey, err := getWrappingKey(data, callback) + wrappingKey, err := getWrappingKey(info, keyFn, retry) if err != nil { return nil, err } - protectorKey, err := crypto.Unwrap(wrappingKey, data.WrappedKey) + protectorKey, err := crypto.Unwrap(wrappingKey, info.data.WrappedKey) wrappingKey.Wipe() switch err { case nil: - log.Printf("valid wrapping key for protector %s", data.ProtectorDescriptor) + log.Printf("valid wrapping key for protector %s", info.Descriptor()) return protectorKey, nil case crypto.ErrBadAuth: - log.Printf("invalid wrapping key for protector %s", data.ProtectorDescriptor) + // After the first failure, we let the callback know we are retrying. + log.Printf("invalid wrapping key for protector %s", info.Descriptor()) + retry = true continue default: return nil, err @@ -90,12 +106,23 @@ func unwrapProtectorKey(data *metadata.ProtectorData, callback KeyCallback) (*cr } } -// PolicyCallback is passed to a function that needs to unlock a policy. The -// callback is used so that the caller can specify which protector they wish to -// use to unlock a policy. The descriptor is the KeyDescriptor for the Policy, -// while for each Protector protecting the policy there is either an entry in -// protectors (if we were able to read the Protector's data). The PolicyCallback -// should either return a valid index into protectors corresponding to the -// desired protector, or an error. If the callback returns an error, the -// function to which the callback is passed returns that error. -type PolicyCallback func(descriptor string, protectors []ProtectorData) (int, error) +// ProtectorOption is information about a protector relative to a Policy. +type ProtectorOption struct { + ProtectorInfo + // LinkedMount is the mountpoint for a linked protector. It is nil if + // the protector is not a linked protector (or there is a LoadError). + LinkedMount *filesystem.Mount + // LoadError is non-nil if there was an error in getting the data for + // the protector. + LoadError error +} + +// OptionFunc is passed to a function that needs to unlock a Policy. +// The callback is used to specify which protector should be used to unlock a +// Policy. The descriptor indicates which Policy we are using, while the options +// correspond to the valid Protectors protecting the Policy. +// +// The OptionFunc should either return a valid index into options, which +// corresponds to the desired protector, or an error (which will be propagated +// back to the caller). +type OptionFunc func(policyDescriptor string, options []*ProtectorOption) (int, error) -- cgit v1.2.3