diff options
| author | Joe Richey joerichey@google.com <joerichey@google.com> | 2017-06-21 09:52:40 -0700 |
|---|---|---|
| committer | Joe Richey joerichey@google.com <joerichey@google.com> | 2017-06-28 14:06:52 -0700 |
| commit | 77b226a90ef70b77ca556830528c013a23b01e57 (patch) | |
| tree | b351dbb427ed62550f2440b8d56249bdcbbca96a /crypto | |
| parent | 07341f3966675e4875f8cad3c8d86ae502de6d4d (diff) | |
Change error handling to new package
This commit changes the error handing for the crypto, filesystem,
metadata, pam, and util packages to use the error handling library
github.com/pkg/errors. This means elimination of the FSError type, an
increased use of wrapping errors (as opposed to logging), switching
on the Cause() of an error (as opposed to its value), and improving our
integration tests involving TEST_FILESYSTEM_ROOT.
This commit also fixes a few bugs with the keyring code to ensure that
our {Find|Remove|Insert}PolicyKey functions are always operating on the
same keyring. The check for filesystem support has been moved from the
filesystem package to the metadata package. Finally, the API for the
filesystem package has been slightly modified:
* filesystem.AllFilesystems() now returns all the filesystems in
sorted order
* certain path methods are now public
O_SYNC is also removed for writing the metadata. We don't get that much
from syncing the metadata, as the actual file data could also be
corrupted by and IO error. The sync operation is also occasionally very
slow (~3 seconds) and can be unfriendly to battery life.
Change-Id: I392c2655141714b16dfdbc84ac09780072be2cf0
Diffstat (limited to 'crypto')
| -rw-r--r-- | crypto/crypto.go | 46 | ||||
| -rw-r--r-- | crypto/key.go | 95 | ||||
| -rw-r--r-- | crypto/rand.go | 9 |
3 files changed, 74 insertions, 76 deletions
diff --git a/crypto/crypto.go b/crypto/crypto.go index c6d6619..967243d 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -46,9 +46,9 @@ import ( "crypto/sha256" "crypto/sha512" "encoding/hex" - "errors" "unsafe" + "github.com/pkg/errors" "golang.org/x/crypto/hkdf" "fscrypt/metadata" @@ -57,34 +57,29 @@ import ( // Crypto error values var ( - ErrBadAuth = errors.New("key authentication check failed") - ErrNegitiveLength = errors.New("negative length requested for key") - ErrKeyAlloc = util.SystemError("could not allocate memory for key") - ErrKeyFree = util.SystemError("could not free memory of key") - ErrKeyringLocate = util.SystemError("could not locate the session keyring") - ErrKeyringInsert = util.SystemError("could not insert key into the session keyring") - ErrKeyringSearch = util.SystemError("could not find key in the session keyring") - ErrKeyringDelete = util.SystemError("could not delete key from the session keyring") - ErrRecoveryCode = errors.New("provided recovery code had incorrect format") - ErrLowEntropy = util.SystemError("insufficient entropy in pool to generate random bytes") - ErrRandNotSupported = util.SystemError("getrandom() not implemented; kernel must be v3.17 or later") - ErrRandFailed = util.SystemError("cannot get random bytes") + ErrBadAuth = errors.New("key authentication check failed") + ErrNegitiveLength = errors.New("keys cannot have negative lengths") + ErrRecoveryCode = errors.New("invalid recovery code") + ErrGetrandomFail = util.SystemError("getrandom() failed") + ErrKeyAlloc = util.SystemError("could not allocate memory for key") + ErrKeyFree = util.SystemError("could not free memory of key") + ErrKeyringLocate = util.SystemError("could not locate the session keyring") + ErrKeyringInsert = util.SystemError("could not insert key into the session keyring") + ErrKeyringSearch = errors.New("could not find key with descriptor") + ErrKeyringDelete = util.SystemError("could not delete key from the session keyring") ) // panicInputLength panics if "name" has invalid length (expected != actual) func panicInputLength(name string, expected, actual int) { - if expected != actual { - util.NeverError(util.InvalidLengthError(name, expected, actual)) + if err := util.CheckValidLength(expected, actual); err != nil { + panic(errors.Wrap(err, name)) } } // checkWrappingKey returns an error if the wrapping key has the wrong length func checkWrappingKey(wrappingKey *Key) error { - l := wrappingKey.Len() - if l != metadata.InternalKeyLen { - return util.InvalidLengthError("wrapping key", metadata.InternalKeyLen, l) - } - return nil + err := util.CheckValidLength(metadata.InternalKeyLen, wrappingKey.Len()) + return errors.Wrap(err, "wrapping key") } // stretchKey stretches a key of length KeyLen using unsalted HKDF to make two @@ -140,14 +135,14 @@ func getHMAC(key *Key, data ...[]byte) []byte { // and an HMAC to verify the wrapping key was correct. All of this is included // in the returned WrappedKeyData structure. func Wrap(wrappingKey, secretKey *Key) (*metadata.WrappedKeyData, error) { - err := checkWrappingKey(wrappingKey) - if err != nil { + if err := checkWrappingKey(wrappingKey); err != nil { return nil, err } data := &metadata.WrappedKeyData{EncryptedKey: make([]byte, secretKey.Len())} // Get random IV + var err error if data.IV, err = NewRandomBuffer(metadata.IVLen); err != nil { return nil, err } @@ -251,8 +246,11 @@ use it in "id" mode to provide extra protection against side-channel attacks. For more info see: https://github.com/P-H-C/phc-winner-argon2 */ func PassphraseHash(passphrase *Key, salt []byte, costs *metadata.HashingCosts) (*Key, error) { - if len(salt) != metadata.SaltLen { - return nil, util.InvalidLengthError("salt", metadata.SaltLen, len(salt)) + if err := util.CheckValidLength(metadata.SaltLen, len(salt)); err != nil { + return nil, errors.Wrap(err, "passphrase hashing salt") + } + if err := costs.CheckValidity(); err != nil { + return nil, errors.Wrap(err, "passphrase hashing costs") } // This key will hold the hashing output diff --git a/crypto/key.go b/crypto/key.go index 852b213..2394eef 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -30,6 +30,8 @@ import ( "runtime" "unsafe" + "github.com/pkg/errors" + "golang.org/x/sys/unix" "fscrypt/metadata" @@ -98,8 +100,7 @@ func newBlankKey(length int) (*Key, error) { if length == 0 { return &Key{data: nil}, nil } else if length < 0 { - log.Printf("key length of %d is invalid", length) - return nil, ErrNegitiveLength + return nil, errors.Wrapf(ErrNegitiveLength, "length of %d requested", length) } flags := keyMmapFlags @@ -123,9 +124,11 @@ func newBlankKey(length int) (*Key, error) { // Wipe destroys a Key by zeroing and freeing the memory. The data is zeroed // even if Wipe returns an error, which occurs if we are unable to unlock or -// free the key memory. Calling Wipe() multiple times on a key has no effect. +// free the key memory. Wipe does nothing if the key is already wiped or is nil. func (key *Key) Wipe() error { - if key.data != nil { + // We do nothing if key or key.data is nil so that Wipe() is idempotent + // and so Wipe() can be called on keys which have already been cleared. + if key != nil && key.data != nil { data := key.data key.data = nil @@ -224,56 +227,50 @@ func NewFixedLengthKeyFromReader(reader io.Reader, length int) (*Key, error) { return key, nil } -// addPayloadToSessionKeyring adds the payload to the current session keyring as -// type logon, returning an error on failure. -func addPayloadToSessionKeyring(payload []byte, description string) error { - // We cannot add directly to KEY_SPEC_SESSION_KEYRING, as that will make - // a new session keyring if one does not exist, which will be garbage - // collected when the process terminates. Instead, we first get the ID - // of the KEY_SPEC_SESSION_KEYRING, which will return the user session - // keyring if a session keyring does not exist. +// getKeyring returns the id of the session keyring, or the id of the user +// session keyring if session keyring does not exist. We cannot directly use +// KEY_SPEC_SESSION_KEYRING, as that will make a new session keyring if one does +// not exist, which will be garbage collected when the process terminates. +func getKeyring() (int, error) { keyringID, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, false) log.Printf("unix.KeyctlGetKeyringID(KEY_SPEC_SESSION_KEYRING) = %d, %v", keyringID, err) if err != nil { - return ErrKeyringLocate + return 0, errors.Wrap(ErrKeyringLocate, err.Error()) } + return keyringID, nil +} - keyID, err := unix.AddKey(keyType, description, payload, keyringID) - log.Printf("unix.AddKey(%s, %s, <payload>, %d) = %d, %v", - keyType, description, keyringID, keyID, err) +// FindPolicyKey tries to locate a policy key in the kernel keyring with the +// provided descriptor and service. The keyring and key ids are returned if we +// can find the key. An error is returned if the key does not exist. +func FindPolicyKey(descriptor, service string) (keyringID, keyID int, err error) { + keyringID, err = getKeyring() if err != nil { - return ErrKeyringInsert + return } - return nil -} -// FindPolicyKey tries to locate a policy key in the kernel keyring with the -// provided descriptor and service. The key id is returned if we can find the -// key. An error is returned if the key does not exist. -func FindPolicyKey(descriptor, service string) (int, error) { description := service + descriptor - keyID, err := unix.KeyctlSearch(unix.KEY_SPEC_SESSION_KEYRING, keyType, description, 0) - log.Printf("unix.KeyctlSearch(KEY_SPEC_SESSION_KEYRING, %s, %s, 0) = %d, %v", - keyType, description, keyID, err) + keyID, err = unix.KeyctlSearch(keyringID, keyType, description, 0) + log.Printf("unix.KeyctlSearch(%d, %s, %s) = %d, %v", keyringID, keyType, description, keyID, err) if err != nil { - return 0, ErrKeyringSearch + err = errors.Wrap(ErrKeyringSearch, err.Error()) } - return keyID, nil + return } // RemovePolicyKey tries to remove a policy key from the kernel keyring with the // provided descriptor and service. An error is returned if the key does not // exist. func RemovePolicyKey(descriptor, service string) error { - keyID, err := FindPolicyKey(descriptor, service) + keyringID, keyID, err := FindPolicyKey(descriptor, service) if err != nil { return err } - _, err = unix.KeyctlInt(unix.KEYCTL_UNLINK, keyID, unix.KEY_SPEC_SESSION_KEYRING, 0, 0) - log.Printf("unix.KeyctlUnlink(%d, KEY_SPEC_SESSION_KEYRING) = %v", keyID, err) + _, err = unix.KeyctlInt(unix.KEYCTL_UNLINK, keyID, keyringID, 0, 0) + log.Printf("unix.KeyctlUnlink(%d, %d) = %v", keyID, keyringID, err) if err != nil { - return ErrKeyringDelete + return errors.Wrap(ErrKeyringDelete, err.Error()) } return nil } @@ -282,12 +279,11 @@ func RemovePolicyKey(descriptor, service string) error { // provided descriptor, provided service prefix, and type logon. The key and // descriptor must have the appropriate lengths. func InsertPolicyKey(key *Key, descriptor, service string) error { - if key.Len() != metadata.PolicyKeyLen { - return util.InvalidLengthError("Policy Key", metadata.PolicyKeyLen, key.Len()) + if err := util.CheckValidLength(metadata.PolicyKeyLen, key.Len()); err != nil { + return errors.Wrap(err, "policy key") } - - if len(descriptor) != metadata.DescriptorLen { - return util.InvalidLengthError("Descriptor", metadata.DescriptorLen, len(descriptor)) + if err := util.CheckValidLength(metadata.DescriptorLen, len(descriptor)); err != nil { + return errors.Wrap(err, "descriptor") } // Create our payload (containing an FscryptKey) @@ -304,10 +300,18 @@ func InsertPolicyKey(key *Key, descriptor, service string) error { fscryptKey.Size = metadata.PolicyKeyLen copy(fscryptKey.Raw[:], key.data) - if err := addPayloadToSessionKeyring(payload.data, service+descriptor); err != nil { + keyringID, err := getKeyring() + if err != nil { return err } + description := service + descriptor + keyID, err := unix.AddKey(keyType, description, payload.data, keyringID) + log.Printf("unix.AddKey(%s, %s, <payload>, %d) = %d, %v", + keyType, description, keyringID, keyID, err) + if err != nil { + return errors.Wrap(ErrKeyringInsert, err.Error()) + } return nil } @@ -326,8 +330,8 @@ var ( // WARNING: This recovery key is enough to derive the original key, so it must // be given the same level of protection as a raw cryptographic key. func WriteRecoveryCode(key *Key, writer io.Writer) error { - if key.Len() != metadata.PolicyKeyLen { - return util.InvalidLengthError("key", metadata.PolicyKeyLen, key.Len()) + if err := util.CheckValidLength(metadata.PolicyKeyLen, key.Len()); err != nil { + return errors.Wrap(err, "recovery key") } // We store the base32 encoded data (without separators) in a temp key @@ -374,8 +378,8 @@ func ReadRecoveryCode(reader io.Reader) (*Key, error) { for blockStart := blockSize; blockStart < encodedLength; blockStart += blockSize { r.Read(inputSeparator) if r.Err() == nil && !bytes.Equal(separator, inputSeparator) { - log.Printf("separator of %q is invalid", inputSeparator) - return nil, ErrRecoveryCode + err := errors.Wrapf(ErrRecoveryCode, "invalid seperator %q", inputSeparator) + return nil, err } blockEnd := util.MinInt(blockStart+blockSize, encodedLength) @@ -384,8 +388,7 @@ func ReadRecoveryCode(reader io.Reader) (*Key, error) { // If any reads have failed, return the error if r.Err() != nil { - log.Printf("error while reading recovery code: %v", r.Err()) - return nil, ErrRecoveryCode + return nil, errors.Wrapf(ErrRecoveryCode, "read error %v", r.Err()) } // Now we decode the key, resizing if necessary @@ -394,9 +397,7 @@ func ReadRecoveryCode(reader io.Reader) (*Key, error) { return nil, err } if _, err = encoding.Decode(decodedKey.data, encodedKey.data); err != nil { - decodedKey.Wipe() - log.Printf("error decoding recovery code: %v", err) - return nil, ErrRecoveryCode + return nil, errors.Wrap(ErrRecoveryCode, err.Error()) } return decodedKey.resize(metadata.PolicyKeyLen) } diff --git a/crypto/rand.go b/crypto/rand.go index d2948d0..0778ebd 100644 --- a/crypto/rand.go +++ b/crypto/rand.go @@ -21,8 +21,8 @@ package crypto import ( "io" - "log" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -58,11 +58,10 @@ func (r randReader) Read(buffer []byte) (int, error) { case nil: return n, nil case unix.EAGAIN: - return 0, ErrLowEntropy + return 0, errors.Wrap(ErrGetrandomFail, "insufficient entropy in pool") case unix.ENOSYS: - return 0, ErrRandNotSupported + return 0, errors.Wrap(ErrGetrandomFail, "kernel must be v3.17 or later") default: - log.Printf("unix.Getrandom failed: %v", err) - return 0, ErrRandFailed + return 0, errors.Wrap(ErrGetrandomFail, err.Error()) } } |