From bc66b8a56ee7ae4f703cf30502aff8b7d68953d0 Mon Sep 17 00:00:00 2001 From: "Joe Richey joerichey@google.com" Date: Tue, 23 May 2017 18:41:36 -0700 Subject: crypto: tests, errors, and descriptor computation This changes the crypto package so it now builds in light of the changes to the util and metadata package. This commit also improves the error handling, adds tests, and makes it so recovery keys now correspond to Policy keys (as they are used to recover a directory in the absence of any metadata). The only feature addition here is the ability to compute descriptors. For backwards compatibility, we keep the same descriptor algorithm used before (double SHA512). Change-Id: Ia2b53c6e85ce65c57595e6823d3c4c92219bc8dc --- crypto/key.go | 60 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 20 deletions(-) (limited to 'crypto/key.go') diff --git a/crypto/key.go b/crypto/key.go index 611b453..bc5ec0f 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -22,9 +22,10 @@ package crypto import ( "bytes" + "crypto/subtle" "encoding/base32" - "fmt" "io" + "log" "os" "runtime" @@ -101,7 +102,8 @@ func newBlankKey(length int) (*Key, error) { if length == 0 { return &Key{data: nil}, nil } else if length < 0 { - return nil, util.InvalidInputF("requested key length %d is negative", length) + log.Printf("key length of %d is invalid", length) + return nil, ErrNegitiveLength } flags := keyMmapFlags @@ -112,7 +114,8 @@ func newBlankKey(length int) (*Key, error) { // See MAP_ANONYMOUS in http://man7.org/linux/man-pages/man2/mmap.2.html data, err := unix.Mmap(-1, 0, length, keyProtection, flags) if err != nil { - return nil, util.SystemErrorF("could not mmap() buffer: %v", err) + log.Printf("unix.Mmap() with length=%d failed: %v", length, err) + return nil, ErrKeyAlloc } key := &Key{data: data} @@ -135,7 +138,8 @@ func (key *Key) Wipe() error { } if err := unix.Munmap(data); err != nil { - return util.SystemErrorF("could not munmap() buffer: %v", err) + log.Printf("unix.Munmap() failed: %v", err) + return ErrKeyFree } } return nil @@ -153,6 +157,12 @@ func (key *Key) UnsafeData() []byte { return key.data } +// Equals compares the contents of two keys, returning true if they have the same +// key data. This function runs in constant time. +func (key *Key) Equals(key2 *Key) bool { + return subtle.ConstantTimeCompare(key.data, key2.data) == 1 +} + // resize returns a new key with size requestedSize and the appropriate data // copied over. The original data is wiped. This method does nothing and returns // itself if the key's length equals requestedSize. @@ -219,8 +229,8 @@ func NewFixedLengthKeyFromReader(reader io.Reader, length int) (*Key, error) { } // addPayloadToSessionKeyring adds the payload to the current session keyring as -// type logon, returning the key's new ID. -func addPayloadToSessionKeyring(payload []byte, description string) (int, error) { +// 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 @@ -228,18 +238,25 @@ func addPayloadToSessionKeyring(payload []byte, description string) (int, error) // keyring if a session keyring does not exist. keyringID, err := unix.KeyctlGetKeyringID(unix.KEY_SPEC_SESSION_KEYRING, 0) if err != nil { - return 0, err + log.Printf("unix.KeyctlGetKeyringID failed: %v", err) + log.Print("could not get keyring ID of KEY_SPEC_SESSION_KEYRING") + return ErrKeyringLocate } - return unix.AddKey("logon", description, payload, keyringID) + if _, err = unix.AddKey("logon", description, payload, keyringID); err != nil { + log.Printf("unix.AddKey failed: %v", err) + log.Printf("could not insert %q into keyring (ID = %d)", description, keyringID) + return ErrKeyringInsert + } + return nil } // InsertPolicyKey puts the provided policy key into the kernel keyring with the // provided descriptor, provided service prefix, and type logon. The key and // descriptor must have the appropriate lengths. func InsertPolicyKey(key *Key, descriptor string, service string) error { - if key.Len() != PolicyKeyLen { - return util.InvalidLengthError("Policy Key", PolicyKeyLen, key.Len()) + if key.Len() != metadata.PolicyKeyLen { + return util.InvalidLengthError("Policy Key", metadata.PolicyKeyLen, key.Len()) } if len(descriptor) != metadata.DescriptorLen { @@ -257,11 +274,11 @@ func InsertPolicyKey(key *Key, descriptor string, service string) error { fscryptKey := (*unix.FscryptKey)(util.Ptr(payload.data)) // Mode is ignored by the kernel fscryptKey.Mode = 0 - fscryptKey.Size = PolicyKeyLen + fscryptKey.Size = metadata.PolicyKeyLen copy(fscryptKey.Raw[:], key.data) - if _, err := addPayloadToSessionKeyring(payload.data, service+descriptor); err != nil { - return util.SystemErrorF("inserting key - %s: %v", descriptor, err) + if err := addPayloadToSessionKeyring(payload.data, service+descriptor); err != nil { + return err } return nil @@ -272,7 +289,7 @@ var ( encoding = base32.StdEncoding blockSize = 8 separator = []byte("-") - encodedLength = encoding.EncodedLen(InternalKeyLen) + encodedLength = encoding.EncodedLen(metadata.PolicyKeyLen) decodedLength = encoding.DecodedLen(encodedLength) // RecoveryCodeLength is the number of bytes in every recovery code RecoveryCodeLength = (encodedLength/blockSize)*(blockSize+len(separator)) - len(separator) @@ -282,8 +299,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() != InternalKeyLen { - return util.InvalidLengthError("key", InternalKeyLen, key.Len()) + if key.Len() != metadata.PolicyKeyLen { + return util.InvalidLengthError("key", metadata.PolicyKeyLen, key.Len()) } // We store the base32 encoded data (without separators) in a temp key @@ -330,7 +347,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) { - return nil, fmt.Errorf("invalid separator: %q", inputSeparator) + log.Printf("separator of %q is invalid", inputSeparator) + return nil, ErrRecoveryCode } blockEnd := util.MinInt(blockStart+blockSize, encodedLength) @@ -339,7 +357,8 @@ func ReadRecoveryCode(reader io.Reader) (*Key, error) { // If any reads have failed, return the error if r.Err() != nil { - return nil, r.Err() + log.Printf("error while reading recovery code: %v", r.Err()) + return nil, ErrRecoveryCode } // Now we decode the key, resizing if necessary @@ -349,7 +368,8 @@ func ReadRecoveryCode(reader io.Reader) (*Key, error) { } if _, err = encoding.Decode(decodedKey.data, encodedKey.data); err != nil { decodedKey.Wipe() - return nil, err + log.Printf("error decoding recovery code: %v", err) + return nil, ErrRecoveryCode } - return decodedKey.resize(InternalKeyLen) + return decodedKey.resize(metadata.PolicyKeyLen) } -- cgit v1.2.3