aboutsummaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.go46
-rw-r--r--crypto/key.go95
-rw-r--r--crypto/rand.go9
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())
}
}