diff options
| -rw-r--r-- | metadata/checks.go | 197 | ||||
| -rw-r--r-- | metadata/config.go | 41 | ||||
| -rw-r--r-- | metadata/config_test.go | 19 | ||||
| -rw-r--r-- | metadata/constants.go | 51 | ||||
| -rw-r--r-- | metadata/metadata.pb.go | 151 | ||||
| -rw-r--r-- | metadata/metadata.proto | 31 | ||||
| -rw-r--r-- | metadata/policy.go | 46 | ||||
| -rw-r--r-- | metadata/policy_test.go | 9 |
8 files changed, 412 insertions, 133 deletions
diff --git a/metadata/checks.go b/metadata/checks.go new file mode 100644 index 0000000..5d0ce59 --- /dev/null +++ b/metadata/checks.go @@ -0,0 +1,197 @@ +/* + * checks.go - Some sanity check methods for our metadata structures + * + * Copyright 2017 Google Inc. + * Author: Joe Richey (joerichey@google.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package metadata + +import ( + "log" + + "github.com/golang/protobuf/proto" + + "fscrypt/util" +) + +// Metadata is the interface to all of the protobuf structures that can be +// checked with the IsValid method. +type Metadata interface { + IsValid() bool + proto.Message +} + +// checkValidLength returns true if expected == actual, otherwise it logs an +// InvalidLengthError. +func checkValidLength(name string, expected int, actual int) bool { + if expected != actual { + log.Print(util.InvalidLengthError(name, expected, actual)) + return false + } + return true +} + +// IsValid ensures the mode has a name and isn't empty. +func (m EncryptionOptions_Mode) IsValid() bool { + if m.String() == "" { + log.Print("Encryption mode cannot be the empty string") + return false + } + if m == EncryptionOptions_default { + log.Print("Encryption mode must be set to a non-default value") + return false + } + return true +} + +// IsValid ensures the source has a name and isn't empty. +func (s SourceType) IsValid() bool { + if s.String() == "" { + log.Print("SourceType cannot be the empty string") + return false + } + if s == SourceType_default { + log.Print("SourceType must be set to a non-default value") + return false + } + return true +} + +// IsValid ensures the hash costs will be accepted by Argon2. +func (h *HashingCosts) IsValid() bool { + if h == nil { + log.Print("HashingCosts not initialized") + return false + } + if h.Time == 0 { + log.Print("Hashing time cost not initialized") + return false + } + if h.Parallelism == 0 { + log.Print("Hashing parallelism cost not initialized") + return false + } + minMemory := 8 * h.Parallelism + if h.Memory < minMemory { + log.Printf("Hashing memory cost must be at least %d", minMemory) + return false + } + return true +} + +// IsValid ensures our buffers are the correct length (or just exist). +func (w *WrappedKeyData) IsValid() bool { + if w == nil { + log.Print("WrappedKeyData not initialized") + return false + } + if len(w.EncryptedKey) == 0 { + log.Print("EncryptedKey not initialized") + return false + } + return checkValidLength("IV", IVLen, len(w.IV)) && + checkValidLength("HMAC", HMACLen, len(w.Hmac)) +} + +// IsValid ensures our ProtectorData has the correct fields for its source. +func (p *ProtectorData) IsValid() bool { + if p == nil { + log.Print("ProtectorData not initialized") + return false + } + + // Source specific checks + switch p.Source { + case SourceType_pam_passphrase: + if p.Uid < 0 { + log.Print("The UID should never be negative") + return false + } + fallthrough + case SourceType_custom_passphrase: + if !p.Costs.IsValid() || !checkValidLength("Salt", SaltLen, len(p.Salt)) { + return false + } + } + + // Generic checks + return p.Source.IsValid() && + p.WrappedKey.IsValid() && + checkValidLength("EncryptedKey", InternalKeyLen, len(p.WrappedKey.EncryptedKey)) && + checkValidLength("ProtectorDescriptor", DescriptorLen, len(p.ProtectorDescriptor)) + +} + +// IsValid ensures each of the options is valid. +func (e *EncryptionOptions) IsValid() bool { + if e == nil { + log.Print("EncryptionOptions not initialized") + return false + } + if _, ok := util.Index(e.Padding, paddingArray); !ok { + log.Printf("Padding of %d is invalid", e.Padding) + return false + } + + return e.Contents.IsValid() && e.Filenames.IsValid() +} + +// IsValid ensures the fields are valid and have the correct lengths. +func (w *WrappedPolicyKey) IsValid() bool { + if w == nil { + log.Print("WrappedPolicyKey not initialized") + return false + } + return w.WrappedKey.IsValid() && + checkValidLength("EncryptedKey", PolicyKeyLen, len(w.WrappedKey.EncryptedKey)) && + checkValidLength("ProtectorDescriptor", DescriptorLen, len(w.ProtectorDescriptor)) +} + +// IsValid ensures the fields and each wrapped key are valid. +func (p *PolicyData) IsValid() bool { + if p == nil { + log.Print("PolicyData not initialized") + return false + } + // Check each wrapped key + for _, w := range p.WrappedPolicyKeys { + if !w.IsValid() { + return false + } + } + return p.Options.IsValid() && + checkValidLength("KeyDescriptor", DescriptorLen, len(p.KeyDescriptor)) +} + +// IsValid ensures the Config has all the necessary info for its Source. +func (c *Config) IsValid() bool { + // General checks + if c == nil { + log.Print("Config not initialized") + return false + } + if !c.Source.IsValid() || !c.Options.IsValid() { + return false + } + + // Source specific checks + switch c.Source { + case SourceType_pam_passphrase, SourceType_custom_passphrase: + return c.HashCosts.IsValid() + default: + return true + } +} diff --git a/metadata/config.go b/metadata/config.go index 47b6cce..8c7be55 100644 --- a/metadata/config.go +++ b/metadata/config.go @@ -19,7 +19,7 @@ */ // Package metadata contains all of the on disk structures. -// These structures are definied in meatadata.proto. The package also +// These structures are defined in metadata.proto. The package also // contains functions for manipulating these structures, specifically: // * Reading and Writing the Config file to disk // * Getting and Setting Policies for directories @@ -27,21 +27,48 @@ package metadata //go:generate protoc --go_out=. metadata.proto -import "github.com/golang/protobuf/jsonpb" +import ( + "io" + "strings" + + "github.com/golang/protobuf/jsonpb" +) // WriteConfig outputs the Config data as nicely formatted JSON -func WriteConfig(config *Config) (string, error) { +func WriteConfig(config *Config, out io.Writer) error { m := jsonpb.Marshaler{ - EmitDefaults: false, + EmitDefaults: true, EnumsAsInts: false, Indent: "\t", OrigName: true, } - return m.MarshalToString(config) + if err := m.Marshal(out, config); err != nil { + return err + } + + _, err := out.Write([]byte{'\n'}) + return err } // ReadConfig writes the JSON data into the config structure -func ReadConfig(input string) (*Config, error) { +func ReadConfig(in io.Reader) (*Config, error) { config := new(Config) - return config, jsonpb.UnmarshalString(input, config) + // Allow (and ignore) unknown fields for forwards compatibility. + u := jsonpb.Unmarshaler{ + AllowUnknownFields: true, + } + return config, u.Unmarshal(in, config) +} + +// HasCompatibilityOption returns true if the specified string is in the list of +// compatibility options. This assumes the compatibility options are in a comma +// separated string. +func (c *Config) HasCompatibilityOption(option string) bool { + options := strings.Split(c.Compatibility, ",") + for _, o := range options { + if o == option { + return true + } + } + return false } diff --git a/metadata/config_test.go b/metadata/config_test.go index 1903785..206098e 100644 --- a/metadata/config_test.go +++ b/metadata/config_test.go @@ -20,6 +20,7 @@ package metadata import ( + "bytes" "reflect" "testing" ) @@ -42,27 +43,31 @@ var testConfigString = `{ "memory": "4096", "parallelism": "8" }, + "compatibility": "", "options": { "padding": "32", - "contents_mode": "XTS", - "filenames_mode": "CTS" + "contents": "XTS", + "filenames": "CTS" } -}` +} +` // Makes sure that writing a config and reading it back gives the same thing. func TestWrite(t *testing.T) { - str, err := WriteConfig(testConfig) + var b bytes.Buffer + err := WriteConfig(testConfig, &b) if err != nil { t.Fatal(err) } - t.Logf("json encoded config:\n%s", str) - if str != testConfigString { + t.Logf("json encoded config:\n%s", b.String()) + if b.String() != testConfigString { t.Errorf("did not match: %s", testConfigString) } } func TestRead(t *testing.T) { - cfg, err := ReadConfig(testConfigString) + buf := bytes.NewBufferString(testConfigString) + cfg, err := ReadConfig(buf) if err != nil { t.Fatal(err) } diff --git a/metadata/constants.go b/metadata/constants.go new file mode 100644 index 0000000..6e4a08d --- /dev/null +++ b/metadata/constants.go @@ -0,0 +1,51 @@ +/* + * constants.go - Some metadata constants used throughout fscrypt + * + * Copyright 2017 Google Inc. + * Author: Joe Richey (joerichey@google.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package metadata + +import ( + "crypto/sha256" + + "golang.org/x/sys/unix" +) + +// Lengths for our keys, buffers, and strings used in fscrypt. +const ( + // DescriptorLen is the length of all Protector and Policy descriptors. + DescriptorLen = 2 * unix.FS_KEY_DESCRIPTOR_SIZE + // We always use 256-bit keys internally (compared to 512-bit policy keys). + InternalKeyLen = 32 + IVLen = 16 + SaltLen = 16 + // We use SHA256 for the HMAC, and len(HMAC) == len(hash size). + HMACLen = sha256.Size + // PolicyKeyLen is the length of all keys passed directly to the Keyring + PolicyKeyLen = unix.FS_MAX_KEY_SIZE +) + +var ( + // DefaultOptions use the supported encryption modes and max padding. + DefaultOptions = &EncryptionOptions{ + Padding: 32, + Contents: EncryptionOptions_XTS, + Filenames: EncryptionOptions_CTS, + } + // DefaultSource is the source we use if none is specified. + DefaultSource = SourceType_custom_passphrase +) diff --git a/metadata/metadata.pb.go b/metadata/metadata.pb.go index bf30309..7173eb2 100644 --- a/metadata/metadata.pb.go +++ b/metadata/metadata.pb.go @@ -38,20 +38,20 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type SourceType int32 const ( - SourceType_none SourceType = 0 + SourceType_default SourceType = 0 SourceType_pam_passphrase SourceType = 1 SourceType_custom_passphrase SourceType = 2 SourceType_raw_key SourceType = 3 ) var SourceType_name = map[int32]string{ - 0: "none", + 0: "default", 1: "pam_passphrase", 2: "custom_passphrase", 3: "raw_key", } var SourceType_value = map[string]int32{ - "none": 0, + "default": 0, "pam_passphrase": 1, "custom_passphrase": 2, "raw_key": 3, @@ -62,25 +62,25 @@ func (x SourceType) String() string { } func (SourceType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } -// Type of encryption, should match the declarations of unix.FS_ENCRYPTION_MODE -type EncryptionMode int32 +// Type of encryption; should match declarations of unix.FS_ENCRYPTION_MODE +type EncryptionOptions_Mode int32 const ( - EncryptionMode_default EncryptionMode = 0 - EncryptionMode_XTS EncryptionMode = 1 - EncryptionMode_GCM EncryptionMode = 2 - EncryptionMode_CBC EncryptionMode = 3 - EncryptionMode_CTS EncryptionMode = 4 + EncryptionOptions_default EncryptionOptions_Mode = 0 + EncryptionOptions_XTS EncryptionOptions_Mode = 1 + EncryptionOptions_GCM EncryptionOptions_Mode = 2 + EncryptionOptions_CBC EncryptionOptions_Mode = 3 + EncryptionOptions_CTS EncryptionOptions_Mode = 4 ) -var EncryptionMode_name = map[int32]string{ +var EncryptionOptions_Mode_name = map[int32]string{ 0: "default", 1: "XTS", 2: "GCM", 3: "CBC", 4: "CTS", } -var EncryptionMode_value = map[string]int32{ +var EncryptionOptions_Mode_value = map[string]int32{ "default": 0, "XTS": 1, "GCM": 2, @@ -88,10 +88,10 @@ var EncryptionMode_value = map[string]int32{ "CTS": 4, } -func (x EncryptionMode) String() string { - return proto.EnumName(EncryptionMode_name, int32(x)) +func (x EncryptionOptions_Mode) String() string { + return proto.EnumName(EncryptionOptions_Mode_name, int32(x)) } -func (EncryptionMode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (EncryptionOptions_Mode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} } // Cost parameters to be used in our hashing functions. type HashingCosts struct { @@ -162,9 +162,9 @@ func (m *WrappedKeyData) GetHmac() []byte { // The associated data for each protector type ProtectorData struct { ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor" json:"protector_descriptor,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` - Source SourceType `protobuf:"varint,3,opt,name=source,enum=metadata.SourceType" json:"source,omitempty"` + Source SourceType `protobuf:"varint,2,opt,name=source,enum=metadata.SourceType" json:"source,omitempty"` // These are only used by some of the protector types + Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` Costs *HashingCosts `protobuf:"bytes,4,opt,name=costs" json:"costs,omitempty"` Salt []byte `protobuf:"bytes,5,opt,name=salt,proto3" json:"salt,omitempty"` Uid int64 `protobuf:"varint,6,opt,name=uid" json:"uid,omitempty"` @@ -183,18 +183,18 @@ func (m *ProtectorData) GetProtectorDescriptor() string { return "" } -func (m *ProtectorData) GetName() string { +func (m *ProtectorData) GetSource() SourceType { if m != nil { - return m.Name + return m.Source } - return "" + return SourceType_default } -func (m *ProtectorData) GetSource() SourceType { +func (m *ProtectorData) GetName() string { if m != nil { - return m.Source + return m.Name } - return SourceType_none + return "" } func (m *ProtectorData) GetCosts() *HashingCosts { @@ -225,11 +225,11 @@ func (m *ProtectorData) GetWrappedKey() *WrappedKeyData { return nil } -// Encryption policy specifics, should match struct fscrypt_policy +// Encryption policy specifics, corresponds to the fscrypt_policy struct type EncryptionOptions struct { - Padding int64 `protobuf:"varint,1,opt,name=padding" json:"padding,omitempty"` - ContentsMode EncryptionMode `protobuf:"varint,2,opt,name=contents_mode,json=contentsMode,enum=metadata.EncryptionMode" json:"contents_mode,omitempty"` - FilenamesMode EncryptionMode `protobuf:"varint,3,opt,name=filenames_mode,json=filenamesMode,enum=metadata.EncryptionMode" json:"filenames_mode,omitempty"` + Padding int64 `protobuf:"varint,1,opt,name=padding" json:"padding,omitempty"` + Contents EncryptionOptions_Mode `protobuf:"varint,2,opt,name=contents,enum=metadata.EncryptionOptions_Mode" json:"contents,omitempty"` + Filenames EncryptionOptions_Mode `protobuf:"varint,3,opt,name=filenames,enum=metadata.EncryptionOptions_Mode" json:"filenames,omitempty"` } func (m *EncryptionOptions) Reset() { *m = EncryptionOptions{} } @@ -244,18 +244,18 @@ func (m *EncryptionOptions) GetPadding() int64 { return 0 } -func (m *EncryptionOptions) GetContentsMode() EncryptionMode { +func (m *EncryptionOptions) GetContents() EncryptionOptions_Mode { if m != nil { - return m.ContentsMode + return m.Contents } - return EncryptionMode_default + return EncryptionOptions_default } -func (m *EncryptionOptions) GetFilenamesMode() EncryptionMode { +func (m *EncryptionOptions) GetFilenames() EncryptionOptions_Mode { if m != nil { - return m.FilenamesMode + return m.Filenames } - return EncryptionMode_default + return EncryptionOptions_default } type WrappedPolicyKey struct { @@ -332,7 +332,7 @@ func (m *Config) GetSource() SourceType { if m != nil { return m.Source } - return SourceType_none + return SourceType_default } func (m *Config) GetHashCosts() *HashingCosts { @@ -365,51 +365,50 @@ func init() { proto.RegisterType((*PolicyData)(nil), "metadata.PolicyData") proto.RegisterType((*Config)(nil), "metadata.Config") proto.RegisterEnum("metadata.SourceType", SourceType_name, SourceType_value) - proto.RegisterEnum("metadata.EncryptionMode", EncryptionMode_name, EncryptionMode_value) + proto.RegisterEnum("metadata.EncryptionOptions_Mode", EncryptionOptions_Mode_name, EncryptionOptions_Mode_value) } func init() { proto.RegisterFile("metadata.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 629 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x51, 0x6f, 0xd3, 0x30, - 0x10, 0xc7, 0x97, 0xa4, 0x6b, 0xd7, 0x6b, 0x1b, 0x65, 0xde, 0x98, 0x22, 0x78, 0xa9, 0x0a, 0x48, - 0xd3, 0x34, 0x4d, 0x62, 0x68, 0x0f, 0x3c, 0x20, 0x04, 0x1d, 0x82, 0x31, 0x0d, 0x26, 0x6f, 0x1a, - 0x20, 0x21, 0x55, 0x5e, 0xe2, 0xad, 0x56, 0x93, 0xd8, 0xb2, 0x5d, 0x55, 0x79, 0xe3, 0x3b, 0xf0, - 0x39, 0x78, 0xe5, 0x43, 0xf0, 0xa9, 0x90, 0x9d, 0x34, 0x4d, 0x37, 0x04, 0x83, 0x97, 0xe8, 0xee, - 0x7c, 0xb9, 0xff, 0xdd, 0xcf, 0x27, 0x83, 0x9f, 0x52, 0x4d, 0x62, 0xa2, 0xc9, 0x9e, 0x90, 0x5c, - 0x73, 0xb4, 0x36, 0xf7, 0x07, 0x5f, 0xa0, 0xfb, 0x96, 0xa8, 0x31, 0xcb, 0xae, 0x87, 0x5c, 0x69, - 0x85, 0x10, 0x34, 0x34, 0x4b, 0x69, 0xe8, 0xf6, 0x9d, 0x6d, 0x0f, 0x5b, 0x1b, 0x6d, 0x41, 0x33, - 0xa5, 0x29, 0x97, 0x79, 0xe8, 0xd9, 0x68, 0xe9, 0xa1, 0x3e, 0x74, 0x04, 0x91, 0x24, 0x49, 0x68, - 0xc2, 0x54, 0x1a, 0x36, 0xec, 0x61, 0x3d, 0x34, 0xf8, 0x0c, 0xfe, 0x47, 0x49, 0x84, 0xa0, 0xf1, - 0x31, 0xcd, 0x0f, 0x89, 0x26, 0xc8, 0x07, 0xf7, 0xe8, 0x22, 0x74, 0xfa, 0xce, 0x76, 0x17, 0xbb, - 0x47, 0x17, 0xe8, 0x21, 0xf4, 0x68, 0x16, 0xc9, 0x5c, 0x68, 0x1a, 0x8f, 0x26, 0x34, 0xb7, 0xc2, - 0x5d, 0xdc, 0xad, 0x82, 0xc7, 0x34, 0x37, 0x4d, 0x8d, 0x53, 0x12, 0x59, 0xf9, 0x2e, 0xb6, 0xf6, - 0xe0, 0x9b, 0x0b, 0xbd, 0x53, 0xc9, 0x35, 0x8d, 0x34, 0x97, 0xb6, 0xf4, 0x13, 0xd8, 0x14, 0xf3, - 0xc0, 0x28, 0xa6, 0x2a, 0x92, 0x4c, 0x68, 0x2e, 0xad, 0x58, 0x1b, 0x6f, 0x54, 0x67, 0x87, 0xd5, - 0x91, 0x29, 0x9c, 0x91, 0x72, 0xda, 0x36, 0xb6, 0x36, 0xda, 0x85, 0xa6, 0xe2, 0x53, 0x19, 0x51, - 0x2b, 0xe7, 0xef, 0x6f, 0xee, 0x55, 0xf0, 0xce, 0x6c, 0xfc, 0x3c, 0x17, 0x14, 0x97, 0x39, 0x68, - 0x17, 0x56, 0x23, 0x03, 0xce, 0x4e, 0xdf, 0xd9, 0xdf, 0x5a, 0x24, 0xd7, 0xb1, 0xe2, 0x22, 0xc9, - 0xe8, 0x29, 0x92, 0xe8, 0x70, 0xb5, 0x18, 0xc4, 0xd8, 0x28, 0x00, 0x6f, 0xca, 0xe2, 0xb0, 0x69, - 0xe9, 0x19, 0x13, 0x3d, 0x83, 0xce, 0xac, 0xa0, 0x66, 0x89, 0xb4, 0x6c, 0xe5, 0x70, 0x51, 0x79, - 0x19, 0x29, 0x86, 0x59, 0xe5, 0x0f, 0xbe, 0x3b, 0xb0, 0xfe, 0xba, 0x40, 0xc7, 0x78, 0xf6, 0xc1, - 0x7e, 0x15, 0x0a, 0xa1, 0x25, 0x48, 0x1c, 0xb3, 0xec, 0xda, 0xc2, 0xf0, 0xf0, 0xdc, 0x45, 0xcf, - 0xa1, 0x17, 0xf1, 0x4c, 0xd3, 0x4c, 0xab, 0x51, 0xca, 0xe3, 0x82, 0x84, 0x5f, 0x17, 0x5b, 0x54, - 0x3b, 0xe1, 0x31, 0xc5, 0xdd, 0x79, 0xba, 0xf1, 0xd0, 0x0b, 0xf0, 0xaf, 0x58, 0x42, 0x0d, 0xb7, - 0xf2, 0x7f, 0xef, 0x2f, 0xff, 0xf7, 0xaa, 0x7c, 0xe3, 0x0e, 0xbe, 0x3a, 0x10, 0x94, 0xe3, 0x9c, - 0xf2, 0x84, 0x45, 0xb9, 0xb9, 0xee, 0xff, 0xb8, 0xc8, 0x1b, 0xc8, 0xdc, 0x7f, 0x40, 0xf6, 0xc3, - 0x01, 0x28, 0xb4, 0xed, 0x16, 0x3d, 0x06, 0x7f, 0x42, 0xf3, 0xdb, 0xb2, 0xbd, 0x09, 0xcd, 0x6b, - 0x82, 0x07, 0xd0, 0xe2, 0x05, 0xdd, 0x52, 0xec, 0xc1, 0xef, 0x46, 0x2e, 0x2f, 0x00, 0xcf, 0x73, - 0xd1, 0x3b, 0xd8, 0x98, 0xf7, 0x29, 0xac, 0xa6, 0x69, 0x57, 0x85, 0x5e, 0xdf, 0xdb, 0xee, 0xec, - 0xdf, 0xbf, 0xd5, 0x6f, 0xc5, 0x04, 0xaf, 0xcf, 0x6e, 0x44, 0xd4, 0xe0, 0xa7, 0x03, 0xcd, 0x21, - 0xcf, 0xae, 0xd8, 0x75, 0x6d, 0x67, 0x9d, 0x3b, 0xec, 0xec, 0x01, 0xc0, 0x98, 0xa8, 0xf1, 0xa8, - 0x58, 0x5c, 0xf7, 0x8f, 0x8b, 0xdb, 0x36, 0x99, 0xc5, 0xd3, 0xf0, 0xc8, 0xec, 0x4a, 0x2a, 0x88, - 0x66, 0x97, 0x2c, 0x61, 0xba, 0x78, 0x0d, 0xda, 0x78, 0x39, 0x58, 0x07, 0xd3, 0xb8, 0x3b, 0x98, - 0x9d, 0xf7, 0x00, 0x8b, 0x4e, 0xd1, 0x1a, 0x34, 0x32, 0x9e, 0xd1, 0x60, 0x05, 0x21, 0xf0, 0x05, - 0x49, 0x47, 0x82, 0x28, 0x25, 0xc6, 0x92, 0x28, 0x1a, 0x38, 0xe8, 0x1e, 0xac, 0x47, 0x53, 0xa5, - 0xf9, 0x52, 0xd8, 0x45, 0x1d, 0x68, 0x49, 0x32, 0x33, 0x40, 0x03, 0x6f, 0xe7, 0x25, 0xf8, 0xcb, - 0x9b, 0x67, 0x8e, 0x63, 0x7a, 0x45, 0xa6, 0x89, 0x0e, 0x56, 0x50, 0x0b, 0xbc, 0x4f, 0xe7, 0x67, - 0x81, 0x63, 0x8c, 0x37, 0xc3, 0x93, 0xc0, 0x35, 0xc6, 0xf0, 0xd5, 0x30, 0xf0, 0xac, 0x71, 0x7e, - 0x16, 0x34, 0x2e, 0x9b, 0xf6, 0xad, 0x7c, 0xfa, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x53, 0xcd, 0x67, - 0x08, 0x3d, 0x05, 0x00, 0x00, + // 615 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xd1, 0x6a, 0xdb, 0x3c, + 0x14, 0xc7, 0x3f, 0xdb, 0x69, 0xd2, 0x9c, 0x24, 0xc6, 0x55, 0xfb, 0x15, 0xb3, 0xdd, 0x04, 0x6f, + 0x83, 0x32, 0x4a, 0x61, 0x1d, 0x65, 0x0c, 0xc6, 0x2e, 0x96, 0x8e, 0xad, 0x2b, 0x65, 0x9d, 0x5a, + 0xba, 0x0d, 0x06, 0x41, 0xb5, 0xd5, 0x46, 0xc4, 0xb6, 0x84, 0xa4, 0x10, 0x7c, 0xb7, 0x77, 0xd8, + 0xbb, 0xec, 0x21, 0xf6, 0x1c, 0x7b, 0x90, 0x21, 0xd9, 0x71, 0x92, 0x06, 0x4a, 0xb6, 0x9b, 0x70, + 0xf4, 0x97, 0xce, 0xf9, 0x1f, 0xfd, 0x72, 0x2c, 0xf0, 0x33, 0xaa, 0x49, 0x42, 0x34, 0x39, 0x10, + 0x92, 0x6b, 0x8e, 0x36, 0x67, 0xeb, 0xe8, 0x1b, 0x74, 0xdf, 0x13, 0x35, 0x62, 0xf9, 0xed, 0x80, + 0x2b, 0xad, 0x10, 0x82, 0x86, 0x66, 0x19, 0x0d, 0xdd, 0xbe, 0xb3, 0xe7, 0x61, 0x1b, 0xa3, 0x5d, + 0x68, 0x66, 0x34, 0xe3, 0xb2, 0x08, 0x3d, 0xab, 0x56, 0x2b, 0xd4, 0x87, 0x8e, 0x20, 0x92, 0xa4, + 0x29, 0x4d, 0x99, 0xca, 0xc2, 0x86, 0xdd, 0x5c, 0x94, 0xa2, 0xaf, 0xe0, 0x7f, 0x96, 0x44, 0x08, + 0x9a, 0x9c, 0xd2, 0xe2, 0x98, 0x68, 0x82, 0x7c, 0x70, 0x4f, 0xae, 0x42, 0xa7, 0xef, 0xec, 0x75, + 0xb1, 0x7b, 0x72, 0x85, 0x1e, 0x41, 0x8f, 0xe6, 0xb1, 0x2c, 0x84, 0xa6, 0xc9, 0x70, 0x4c, 0x0b, + 0x6b, 0xdc, 0xc5, 0xdd, 0x5a, 0x3c, 0xa5, 0x85, 0x69, 0x6a, 0x94, 0x91, 0xd8, 0xda, 0x77, 0xb1, + 0x8d, 0xa3, 0x1f, 0x2e, 0xf4, 0xce, 0x25, 0xd7, 0x34, 0xd6, 0x5c, 0xda, 0xd2, 0xcf, 0x60, 0x47, + 0xcc, 0x84, 0x61, 0x42, 0x55, 0x2c, 0x99, 0xd0, 0x5c, 0x5a, 0xb3, 0x36, 0xde, 0xae, 0xf7, 0x8e, + 0xeb, 0x2d, 0xb4, 0x0f, 0x4d, 0xc5, 0x27, 0x32, 0x2e, 0xef, 0xeb, 0x1f, 0xee, 0x1c, 0xd4, 0xa0, + 0x2e, 0xac, 0x7e, 0x59, 0x08, 0x8a, 0xab, 0x33, 0xa6, 0x8d, 0x9c, 0x64, 0xd4, 0xb6, 0xd1, 0xc6, + 0x36, 0x46, 0xfb, 0xb0, 0x11, 0x1b, 0x70, 0xf6, 0xf6, 0x9d, 0xc3, 0xdd, 0x79, 0x81, 0x45, 0xac, + 0xb8, 0x3c, 0x64, 0x2a, 0x28, 0x92, 0xea, 0x70, 0xa3, 0xbc, 0x88, 0x89, 0x51, 0x00, 0xde, 0x84, + 0x25, 0x61, 0xd3, 0xd2, 0x33, 0x21, 0x7a, 0x09, 0x9d, 0x69, 0x49, 0xcd, 0x12, 0x69, 0xd9, 0xca, + 0xe1, 0xbc, 0xf2, 0x32, 0x52, 0x0c, 0xd3, 0x7a, 0x1d, 0xfd, 0x76, 0x60, 0xeb, 0x6d, 0x89, 0x8e, + 0xf1, 0xfc, 0xa3, 0xfd, 0x55, 0x28, 0x84, 0x96, 0x20, 0x49, 0xc2, 0xf2, 0x5b, 0x0b, 0xc3, 0xc3, + 0xb3, 0x25, 0x7a, 0x05, 0x9b, 0x31, 0xcf, 0x35, 0xcd, 0xb5, 0xaa, 0x10, 0xf4, 0xe7, 0x3e, 0x2b, + 0x85, 0x0e, 0xce, 0x78, 0x42, 0x71, 0x9d, 0x81, 0x5e, 0x43, 0xfb, 0x86, 0xa5, 0xd4, 0x80, 0x50, + 0x96, 0xca, 0x3a, 0xe9, 0xf3, 0x94, 0xe8, 0x05, 0x34, 0x8c, 0x84, 0x3a, 0xd0, 0x4a, 0xe8, 0x0d, + 0x99, 0xa4, 0x3a, 0xf8, 0x0f, 0xb5, 0xc0, 0xfb, 0x72, 0x79, 0x11, 0x38, 0x26, 0x78, 0x37, 0x38, + 0x0b, 0x5c, 0x13, 0x0c, 0xde, 0x0c, 0x02, 0xcf, 0x06, 0x97, 0x17, 0x41, 0x23, 0xfa, 0xee, 0x40, + 0x50, 0x51, 0x38, 0xe7, 0x29, 0x8b, 0x0b, 0x33, 0x25, 0xff, 0xf0, 0xff, 0xdf, 0x21, 0xed, 0xfe, + 0x05, 0xe9, 0x9f, 0x0e, 0x40, 0xe9, 0x6d, 0x87, 0xef, 0x09, 0xf8, 0x63, 0x5a, 0xac, 0xda, 0xf6, + 0xc6, 0xb4, 0x58, 0x30, 0x3c, 0x82, 0x16, 0x2f, 0x61, 0x54, 0x66, 0x0f, 0xef, 0xe1, 0x85, 0x67, + 0x67, 0xd1, 0x07, 0xd8, 0x9e, 0xf5, 0x29, 0xac, 0xa7, 0x69, 0xd7, 0x20, 0xf7, 0xf6, 0x3a, 0x87, + 0x0f, 0x56, 0xfa, 0xad, 0x99, 0xe0, 0xad, 0xe9, 0x1d, 0x45, 0x45, 0xbf, 0x1c, 0x68, 0x0e, 0x78, + 0x7e, 0xc3, 0x6e, 0x17, 0xc6, 0xdf, 0x59, 0x63, 0xfc, 0x8f, 0x00, 0x46, 0x44, 0x8d, 0x86, 0xe5, + 0xbc, 0xbb, 0xf7, 0xce, 0x7b, 0xdb, 0x9c, 0x2c, 0x5f, 0x94, 0xc7, 0xd0, 0x8b, 0x79, 0x26, 0x88, + 0x66, 0xd7, 0x2c, 0x65, 0xba, 0xa8, 0x3e, 0x9f, 0x65, 0x71, 0x11, 0x4c, 0x63, 0x7d, 0x30, 0x4f, + 0x3f, 0x01, 0xcc, 0x3b, 0x5d, 0x9e, 0x23, 0x04, 0xbe, 0x20, 0xd9, 0x50, 0x10, 0xa5, 0xc4, 0x48, + 0x12, 0x45, 0x03, 0x07, 0xfd, 0x0f, 0x5b, 0xf1, 0x44, 0x69, 0xbe, 0x24, 0xbb, 0x26, 0x4f, 0x92, + 0xa9, 0x61, 0x1a, 0x78, 0xd7, 0x4d, 0xfb, 0x44, 0x3e, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xfc, + 0x6e, 0x6d, 0x55, 0x34, 0x05, 0x00, 0x00, } diff --git a/metadata/metadata.proto b/metadata/metadata.proto index f3103f8..f402fa1 100644 --- a/metadata/metadata.proto +++ b/metadata/metadata.proto @@ -38,7 +38,7 @@ message WrappedKeyData { // Specifies the method in which an outside secret is obtained for a Protector enum SourceType { - none = 0; + default = 0; pam_passphrase = 1; custom_passphrase = 2; raw_key = 3; @@ -47,10 +47,10 @@ enum SourceType { // The associated data for each protector message ProtectorData { string protector_descriptor = 1; - string name = 2; - SourceType source = 3; + SourceType source = 2; // These are only used by some of the protector types + string name = 3; HashingCosts costs = 4; bytes salt = 5; int64 uid = 6; @@ -58,20 +58,21 @@ message ProtectorData { WrappedKeyData wrapped_key = 7; } -// Type of encryption, should match the declarations of unix.FS_ENCRYPTION_MODE -enum EncryptionMode { - default = 0; - XTS = 1; - GCM = 2; - CBC = 3; - CTS = 4; -} - -// Encryption policy specifics, should match struct fscrypt_policy +// Encryption policy specifics, corresponds to the fscrypt_policy struct message EncryptionOptions { int64 padding = 1; - EncryptionMode contents_mode = 2; - EncryptionMode filenames_mode = 3; + + // Type of encryption; should match declarations of unix.FS_ENCRYPTION_MODE + enum Mode { + default = 0; + XTS = 1; + GCM = 2; + CBC = 3; + CTS = 4; + } + + Mode contents = 2; + Mode filenames = 3; } message WrappedPolicyKey { diff --git a/metadata/policy.go b/metadata/policy.go index 8c67f52..102c587 100644 --- a/metadata/policy.go +++ b/metadata/policy.go @@ -23,6 +23,8 @@ package metadata import ( "encoding/hex" "errors" + "fmt" + "log" "os" "unsafe" @@ -31,16 +33,14 @@ import ( "fscrypt/util" ) -// DescriptorLen is the length of all Protector and Policy descriptors. -const DescriptorLen = 2 * unix.FS_KEY_DESCRIPTOR_SIZE - // Encryption specific errors var ( - ErrEncryptionNotSupported = errors.New("filesystem encryption is not supported") - ErrEncryptionDisabled = errors.New("filesystem encryption has been disabled in the kernel config") - ErrNotEncrypted = errors.New("file or directory not encrypted") - ErrEncrypted = errors.New("file or directory already encrypted") - ErrBadEncryptionOptions = errors.New("invalid encryption options provided") + prefix = "filesystem encryption: " + ErrEncryptionNotSupported = errors.New(prefix + "not supported") + ErrEncryptionDisabled = errors.New(prefix + "disabled in the kernel config") + ErrNotEncrypted = errors.New(prefix + "file or directory not encrypted") + ErrEncrypted = errors.New(prefix + "file or directory already encrypted") + ErrBadEncryptionOptions = errors.New(prefix + "invalid options provided") ) // policyIoctl is a wrapper for the ioctl syscall. If opens the file at the path @@ -76,13 +76,6 @@ func policyIoctl(path string, request uintptr, policy *unix.FscryptPolicy) error } } -// DefaultOptions use the only supported encryption modes and maximum padding. -var DefaultOptions = &EncryptionOptions{ - Padding: 32, - ContentsMode: EncryptionMode_XTS, - FilenamesMode: EncryptionMode_CTS, -} - // Maps EncryptionOptions.Padding <-> FscryptPolicy.Flags var ( paddingArray = []int64{4, 8, 16, 32} @@ -101,17 +94,20 @@ func GetPolicy(path string) (*PolicyData, error) { // Convert the padding flag into an amount of padding paddingFlag := int64(policy.Flags & unix.FS_POLICY_FLAGS_PAD_MASK) + + // This lookup should always succeed padding, ok := util.Lookup(paddingFlag, flagsArray, paddingArray) if !ok { - return nil, util.SystemErrorF("invalid padding flag: %x", paddingFlag) + log.Printf("padding flag of %x not found", paddingFlag) + util.NeverError(util.SystemError("invalid padding flag")) } return &PolicyData{ KeyDescriptor: hex.EncodeToString(policy.Master_key_descriptor[:]), Options: &EncryptionOptions{ - Padding: padding, - ContentsMode: EncryptionMode(policy.Contents_encryption_mode), - FilenamesMode: EncryptionMode(policy.Filenames_encryption_mode), + Padding: padding, + Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode), + Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode), }, }, nil } @@ -123,23 +119,25 @@ func SetPolicy(path string, data *PolicyData) error { // Convert the padding value to a flag paddingFlag, ok := util.Lookup(data.Options.Padding, paddingArray, flagsArray) if !ok { - return util.InvalidInputF("padding of %d", data.Options.Padding) + return util.InvalidInput(fmt.Sprintf("padding of %d", data.Options.Padding)) } // Convert the policyDescriptor to a byte array if len(data.KeyDescriptor) != DescriptorLen { - return util.InvalidLengthError("policy descriptor", DescriptorLen, len(data.KeyDescriptor)) + return util.InvalidLengthError( + "policy descriptor", DescriptorLen, len(data.KeyDescriptor)) } descriptorBytes, err := hex.DecodeString(data.KeyDescriptor) if err != nil { - return util.InvalidInputF("policy descriptor of %s: %v", data.KeyDescriptor, err) + return util.InvalidInput( + fmt.Sprintf("policy descriptor of %s: %v", data.KeyDescriptor, err)) } policy := unix.FscryptPolicy{ Version: 0, // Version must always be zero - Contents_encryption_mode: uint8(data.Options.ContentsMode), - Filenames_encryption_mode: uint8(data.Options.FilenamesMode), + Contents_encryption_mode: uint8(data.Options.Contents), + Filenames_encryption_mode: uint8(data.Options.Filenames), Flags: uint8(paddingFlag), } copy(policy.Master_key_descriptor[:], descriptorBytes) diff --git a/metadata/policy_test.go b/metadata/policy_test.go index 593f3da..6dc2567 100644 --- a/metadata/policy_test.go +++ b/metadata/policy_test.go @@ -34,12 +34,13 @@ var goodPolicy = &PolicyData{ Options: DefaultOptions, } -// Creates a temporary directory in BASE_TEST_DIR for testing. Fails if the -// base directory is not specified. +// Creates a temporary directory in TEST_FILESYSTEM_ROOT for testing. Fails if +// the root directory is not specified. func createTestDirectory() (directory string, err error) { - baseDirectory := os.Getenv("BASE_TEST_DIR") + baseDirectory := os.Getenv("TEST_FILESYSTEM_ROOT") if s, err := os.Stat(baseDirectory); err != nil || !s.IsDir() { - return "", fmt.Errorf("invalid directory %q. Set BASE_TEST_DIR to be a valid directory", baseDirectory) + return "", fmt.Errorf("invalid directory %q: "+ + "set TEST_FILESYSTEM_ROOT to be a valid directory", baseDirectory) } directoryPath := filepath.Join(baseDirectory, "test") |