aboutsummaryrefslogtreecommitdiff
path: root/metadata
diff options
context:
space:
mode:
Diffstat (limited to 'metadata')
-rw-r--r--metadata/checks.go71
-rw-r--r--metadata/config.go54
-rw-r--r--metadata/config_test.go76
-rw-r--r--metadata/constants.go20
-rw-r--r--metadata/metadata.pb.go870
-rw-r--r--metadata/metadata.proto20
-rw-r--r--metadata/policy.go325
-rw-r--r--metadata/policy_test.go66
8 files changed, 1101 insertions, 401 deletions
diff --git a/metadata/checks.go b/metadata/checks.go
index 4fe4531..d7dea41 100644
--- a/metadata/checks.go
+++ b/metadata/checks.go
@@ -20,8 +20,11 @@
package metadata
import (
- "github.com/golang/protobuf/proto"
+ "log"
+ "math"
+
"github.com/pkg/errors"
+ "google.golang.org/protobuf/proto"
"github.com/google/fscrypt/util"
)
@@ -57,20 +60,37 @@ func (s SourceType) CheckValidity() error {
return nil
}
+// MaxParallelism is the maximum allowed value for HashingCosts.Parallelism.
+const MaxParallelism = math.MaxUint8
+
// CheckValidity ensures the hash costs will be accepted by Argon2.
func (h *HashingCosts) CheckValidity() error {
if h == nil {
return errNotInitialized
}
- if h.Time <= 0 {
- return errors.Errorf("time=%d is not positive", h.Time)
+
+ minP := int64(1)
+ p := uint8(h.Parallelism)
+ if h.Parallelism < minP || h.Parallelism > MaxParallelism {
+ if h.TruncationFixed || p == 0 {
+ return errors.Errorf("parallelism cost %d is not in range [%d, %d]",
+ h.Parallelism, minP, MaxParallelism)
+ }
+ // Previously we unconditionally casted costs.Parallelism to a uint8,
+ // so we replicate this behavior for backwards compatibility.
+ log.Printf("WARNING: Truncating parallelism cost of %d to %d", h.Parallelism, p)
}
- if h.Parallelism <= 0 {
- return errors.Errorf("parallelism=%d is not positive", h.Parallelism)
+
+ minT := int64(1)
+ maxT := int64(math.MaxUint32)
+ if h.Time < minT || h.Time > maxT {
+ return errors.Errorf("time cost %d is not in range [%d, %d]", h.Time, minT, maxT)
}
- minMemory := 8 * h.Parallelism
- if h.Memory < minMemory {
- return errors.Errorf("memory=%d is less than minimum (%d)", h.Memory, minMemory)
+
+ minM := 8 * int64(p)
+ maxM := int64(math.MaxUint32)
+ if h.Memory < minM || h.Memory > maxM {
+ return errors.Errorf("memory cost %d KiB is not in range [%d, %d]", h.Memory, minM, maxM)
}
return nil
}
@@ -119,7 +139,7 @@ func (p *ProtectorData) CheckValidity() error {
if err := p.WrappedKey.CheckValidity(); err != nil {
return errors.Wrap(err, "wrapped protector key")
}
- if err := util.CheckValidLength(DescriptorLen, len(p.ProtectorDescriptor)); err != nil {
+ if err := util.CheckValidLength(ProtectorDescriptorLen, len(p.ProtectorDescriptor)); err != nil {
return errors.Wrap(err, "protector descriptor")
}
@@ -138,7 +158,17 @@ func (e *EncryptionOptions) CheckValidity() error {
if err := e.Contents.CheckValidity(); err != nil {
return errors.Wrap(err, "contents encryption mode")
}
- return errors.Wrap(e.Filenames.CheckValidity(), "filenames encryption mode")
+ if err := e.Filenames.CheckValidity(); err != nil {
+ return errors.Wrap(err, "filenames encryption mode")
+ }
+ // If PolicyVersion is unset, treat it as 1.
+ if e.PolicyVersion == 0 {
+ e.PolicyVersion = 1
+ }
+ if e.PolicyVersion != 1 && e.PolicyVersion != 2 {
+ return errors.Errorf("policy version of %d is invalid", e.PolicyVersion)
+ }
+ return nil
}
// CheckValidity ensures the fields are valid and have the correct lengths.
@@ -152,7 +182,7 @@ func (w *WrappedPolicyKey) CheckValidity() error {
if err := util.CheckValidLength(PolicyKeyLen, len(w.WrappedKey.EncryptedKey)); err != nil {
return errors.Wrap(err, "encrypted key")
}
- err := util.CheckValidLength(DescriptorLen, len(w.ProtectorDescriptor))
+ err := util.CheckValidLength(ProtectorDescriptorLen, len(w.ProtectorDescriptor))
return errors.Wrap(err, "wrapping protector descriptor")
}
@@ -167,11 +197,26 @@ func (p *PolicyData) CheckValidity() error {
return errors.Wrapf(err, "policy key slot %d", i)
}
}
- if err := util.CheckValidLength(DescriptorLen, len(p.KeyDescriptor)); err != nil {
+
+ if err := p.Options.CheckValidity(); err != nil {
+ return errors.Wrap(err, "policy options")
+ }
+
+ var expectedLen int
+ switch p.Options.PolicyVersion {
+ case 1:
+ expectedLen = PolicyDescriptorLenV1
+ case 2:
+ expectedLen = PolicyDescriptorLenV2
+ default:
+ return errors.Errorf("policy version of %d is invalid", p.Options.PolicyVersion)
+ }
+
+ if err := util.CheckValidLength(expectedLen, len(p.KeyDescriptor)); err != nil {
return errors.Wrap(err, "policy key descriptor")
}
- return errors.Wrap(p.Options.CheckValidity(), "policy options")
+ return nil
}
// CheckValidity ensures the Config has all the necessary info for its Source.
diff --git a/metadata/config.go b/metadata/config.go
index 0f95fbe..65fd7b5 100644
--- a/metadata/config.go
+++ b/metadata/config.go
@@ -21,53 +21,47 @@
// Package metadata contains all of the on disk structures.
// 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
-// * Reasonable defaults for a Policy's EncryptionOptions
+// - Reading and Writing the Config file to disk
+// - Getting and Setting Policies for directories
+// - Reasonable defaults for a Policy's EncryptionOptions
package metadata
import (
"io"
- "strings"
- "github.com/golang/protobuf/jsonpb"
+ "google.golang.org/protobuf/encoding/protojson"
)
// WriteConfig outputs the Config data as nicely formatted JSON
func WriteConfig(config *Config, out io.Writer) error {
- m := jsonpb.Marshaler{
- EmitDefaults: true,
- EnumsAsInts: false,
- Indent: "\t",
- OrigName: true,
+ m := protojson.MarshalOptions{
+ Multiline: true,
+ Indent: "\t",
+ UseProtoNames: true,
+ UseEnumNumbers: false,
+ EmitUnpopulated: true,
}
- if err := m.Marshal(out, config); err != nil {
+ bytes, err := m.Marshal(config)
+ if err != nil {
return err
}
-
- _, err := out.Write([]byte{'\n'})
+ if _, err = out.Write(bytes); err != nil {
+ return err
+ }
+ _, err = out.Write([]byte{'\n'})
return err
}
// ReadConfig writes the JSON data into the config structure
func ReadConfig(in io.Reader) (*Config, error) {
- config := new(Config)
- // Allow (and ignore) unknown fields for forwards compatibility.
- u := jsonpb.Unmarshaler{
- AllowUnknownFields: true,
+ bytes, err := io.ReadAll(in)
+ if err != nil {
+ return nil, err
}
- 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
- }
+ config := new(Config)
+ // Discard unknown fields for forwards compatibility.
+ u := protojson.UnmarshalOptions{
+ DiscardUnknown: true,
}
- return false
+ return config, u.Unmarshal(bytes, config)
}
diff --git a/metadata/config_test.go b/metadata/config_test.go
index 95afa04..3048757 100644
--- a/metadata/config_test.go
+++ b/metadata/config_test.go
@@ -21,19 +21,21 @@ package metadata
import (
"bytes"
- "reflect"
+ "encoding/json"
"testing"
+
+ "google.golang.org/protobuf/proto"
)
var testConfig = &Config{
Source: SourceType_custom_passphrase,
HashCosts: &HashingCosts{
- Time: 10,
- Memory: 1 << 12,
- Parallelism: 8,
+ Time: 10,
+ Memory: 1 << 12,
+ Parallelism: 8,
+ TruncationFixed: true,
},
- Compatibility: "",
- Options: DefaultOptions,
+ Options: DefaultOptions,
}
var testConfigString = `{
@@ -41,17 +43,29 @@ var testConfigString = `{
"hash_costs": {
"time": "10",
"memory": "4096",
- "parallelism": "8"
+ "parallelism": "8",
+ "truncation_fixed": true
},
- "compatibility": "",
"options": {
"padding": "32",
"contents": "AES_256_XTS",
- "filenames": "AES_256_CTS"
- }
+ "filenames": "AES_256_CTS",
+ "policy_version": "1"
+ },
+ "use_fs_keyring_for_v1_policies": false,
+ "allow_cross_user_metadata": false
}
`
+// Used for JSON string comparison while ignoring whitespace
+func compact(t testing.TB, s string) string {
+ var b bytes.Buffer
+ if err := json.Compact(&b, []byte(s)); err != nil {
+ t.Fatalf("failed to compact json: %v", err)
+ }
+ return b.String()
+}
+
// Makes sure that writing a config and reading it back gives the same thing.
func TestWrite(t *testing.T) {
var b bytes.Buffer
@@ -60,7 +74,7 @@ func TestWrite(t *testing.T) {
t.Fatal(err)
}
t.Logf("json encoded config:\n%s", b.String())
- if b.String() != testConfigString {
+ if compact(t, b.String()) != compact(t, testConfigString) {
t.Errorf("did not match: %s", testConfigString)
}
}
@@ -72,7 +86,45 @@ func TestRead(t *testing.T) {
t.Fatal(err)
}
t.Logf("decoded config:\n%s", cfg)
- if !reflect.DeepEqual(cfg, testConfig) {
+ if !proto.Equal(cfg, testConfig) {
t.Errorf("did not match: %s", testConfig)
}
}
+
+// Makes sure we can parse a legacy config file that doesn't have the fields
+// that were added later and that has the removed "compatibility" field.
+func TestOptionalFields(t *testing.T) {
+ contents := `{
+ "source": "custom_passphrase",
+ "hash_costs": {
+ "time": "10",
+ "memory": "4096",
+ "parallelism": "8"
+ },
+ "compatibility": "legacy",
+ "options": {
+ "padding": "32",
+ "contents": "AES_256_XTS",
+ "filenames": "AES_256_CTS"
+ }
+ }
+ `
+ buf := bytes.NewBufferString(contents)
+ cfg, err := ReadConfig(buf)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if cfg.GetUseFsKeyringForV1Policies() {
+ t.Error("use_fs_keyring_for_v1_policies should be false, but was true")
+ }
+ if cfg.Options.PolicyVersion != 0 {
+ t.Errorf("policy version should be 0, but was %d", cfg.Options.PolicyVersion)
+ }
+ if err = cfg.CheckValidity(); err != nil {
+ t.Error(err)
+ }
+ // CheckValidity() should change an unset policy version to 1.
+ if cfg.Options.PolicyVersion != 1 {
+ t.Errorf("policy version should be 1 now, but was %d", cfg.Options.PolicyVersion)
+ }
+}
diff --git a/metadata/constants.go b/metadata/constants.go
index 344f955..fa6b8a7 100644
--- a/metadata/constants.go
+++ b/metadata/constants.go
@@ -27,8 +27,12 @@ import (
// 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
+ // Length of policy descriptor (in hex chars) for v1 encryption policies
+ PolicyDescriptorLenV1 = 2 * unix.FSCRYPT_KEY_DESCRIPTOR_SIZE
+ // Length of protector descriptor (in hex chars)
+ ProtectorDescriptorLen = PolicyDescriptorLenV1
+ // Length of policy descriptor (in hex chars) for v2 encryption policies
+ PolicyDescriptorLenV2 = 2 * unix.FSCRYPT_KEY_IDENTIFIER_SIZE
// We always use 256-bit keys internally (compared to 512-bit policy keys).
InternalKeyLen = 32
IVLen = 16
@@ -36,15 +40,17 @@ const (
// 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
+ PolicyKeyLen = unix.FSCRYPT_MAX_KEY_SIZE
)
var (
- // DefaultOptions use the supported encryption modes and max padding.
+ // DefaultOptions use the supported encryption modes, max padding, and
+ // policy version 1.
DefaultOptions = &EncryptionOptions{
- Padding: 32,
- Contents: EncryptionOptions_AES_256_XTS,
- Filenames: EncryptionOptions_AES_256_CTS,
+ Padding: 32,
+ Contents: EncryptionOptions_AES_256_XTS,
+ Filenames: EncryptionOptions_AES_256_CTS,
+ PolicyVersion: 1,
}
// 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 558e8f2..8b030f8 100644
--- a/metadata/metadata.pb.go
+++ b/metadata/metadata.pb.go
@@ -1,37 +1,48 @@
+//
+// metadata.proto - File which contains all of the metadata structures which we
+// write to metadata files. Must be compiled with protoc to use the library.
+// Compilation can be invoked with go generate.
+//
+// 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.
+
+// If the *.proto file is modified, be sure to run "make gen" (at the project
+// root) to recreate the *.pb.go file.
+
// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.10
+// protoc v3.6.1
// source: metadata/metadata.proto
-/*
-Package metadata is a generated protocol buffer package.
-
-It is generated from these files:
- metadata/metadata.proto
-
-It has these top-level messages:
- HashingCosts
- WrappedKeyData
- ProtectorData
- EncryptionOptions
- WrappedPolicyKey
- PolicyData
- Config
-*/
package metadata
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
+)
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
// Specifies the method in which an outside secret is obtained for a Protector
type SourceType int32
@@ -43,379 +54,728 @@ const (
SourceType_raw_key SourceType = 3
)
-var SourceType_name = map[int32]string{
- 0: "default",
- 1: "pam_passphrase",
- 2: "custom_passphrase",
- 3: "raw_key",
-}
-var SourceType_value = map[string]int32{
- "default": 0,
- "pam_passphrase": 1,
- "custom_passphrase": 2,
- "raw_key": 3,
+// Enum value maps for SourceType.
+var (
+ SourceType_name = map[int32]string{
+ 0: "default",
+ 1: "pam_passphrase",
+ 2: "custom_passphrase",
+ 3: "raw_key",
+ }
+ SourceType_value = map[string]int32{
+ "default": 0,
+ "pam_passphrase": 1,
+ "custom_passphrase": 2,
+ "raw_key": 3,
+ }
+)
+
+func (x SourceType) Enum() *SourceType {
+ p := new(SourceType)
+ *p = x
+ return p
}
func (x SourceType) String() string {
- return proto.EnumName(SourceType_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SourceType) Descriptor() protoreflect.EnumDescriptor {
+ return file_metadata_metadata_proto_enumTypes[0].Descriptor()
+}
+
+func (SourceType) Type() protoreflect.EnumType {
+ return &file_metadata_metadata_proto_enumTypes[0]
}
-func (SourceType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
-// Type of encryption; should match declarations of unix.FS_ENCRYPTION_MODE
+func (x SourceType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use SourceType.Descriptor instead.
+func (SourceType) EnumDescriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+// Type of encryption; should match declarations of unix.FSCRYPT_MODE
type EncryptionOptions_Mode int32
const (
- EncryptionOptions_default EncryptionOptions_Mode = 0
- EncryptionOptions_AES_256_XTS EncryptionOptions_Mode = 1
- EncryptionOptions_AES_256_GCM EncryptionOptions_Mode = 2
- EncryptionOptions_AES_256_CBC EncryptionOptions_Mode = 3
- EncryptionOptions_AES_256_CTS EncryptionOptions_Mode = 4
- EncryptionOptions_AES_128_CBC EncryptionOptions_Mode = 5
- EncryptionOptions_AES_128_CTS EncryptionOptions_Mode = 6
+ EncryptionOptions_default EncryptionOptions_Mode = 0
+ EncryptionOptions_AES_256_XTS EncryptionOptions_Mode = 1
+ EncryptionOptions_AES_256_GCM EncryptionOptions_Mode = 2
+ EncryptionOptions_AES_256_CBC EncryptionOptions_Mode = 3
+ EncryptionOptions_AES_256_CTS EncryptionOptions_Mode = 4
+ EncryptionOptions_AES_128_CBC EncryptionOptions_Mode = 5
+ EncryptionOptions_AES_128_CTS EncryptionOptions_Mode = 6
+ EncryptionOptions_Adiantum EncryptionOptions_Mode = 9
+ EncryptionOptions_AES_256_HCTR2 EncryptionOptions_Mode = 10
)
-var EncryptionOptions_Mode_name = map[int32]string{
- 0: "default",
- 1: "AES_256_XTS",
- 2: "AES_256_GCM",
- 3: "AES_256_CBC",
- 4: "AES_256_CTS",
- 5: "AES_128_CBC",
- 6: "AES_128_CTS",
-}
-var EncryptionOptions_Mode_value = map[string]int32{
- "default": 0,
- "AES_256_XTS": 1,
- "AES_256_GCM": 2,
- "AES_256_CBC": 3,
- "AES_256_CTS": 4,
- "AES_128_CBC": 5,
- "AES_128_CTS": 6,
+// Enum value maps for EncryptionOptions_Mode.
+var (
+ EncryptionOptions_Mode_name = map[int32]string{
+ 0: "default",
+ 1: "AES_256_XTS",
+ 2: "AES_256_GCM",
+ 3: "AES_256_CBC",
+ 4: "AES_256_CTS",
+ 5: "AES_128_CBC",
+ 6: "AES_128_CTS",
+ 9: "Adiantum",
+ 10: "AES_256_HCTR2",
+ }
+ EncryptionOptions_Mode_value = map[string]int32{
+ "default": 0,
+ "AES_256_XTS": 1,
+ "AES_256_GCM": 2,
+ "AES_256_CBC": 3,
+ "AES_256_CTS": 4,
+ "AES_128_CBC": 5,
+ "AES_128_CTS": 6,
+ "Adiantum": 9,
+ "AES_256_HCTR2": 10,
+ }
+)
+
+func (x EncryptionOptions_Mode) Enum() *EncryptionOptions_Mode {
+ p := new(EncryptionOptions_Mode)
+ *p = x
+ return p
}
func (x EncryptionOptions_Mode) String() string {
- return proto.EnumName(EncryptionOptions_Mode_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (EncryptionOptions_Mode) Descriptor() protoreflect.EnumDescriptor {
+ return file_metadata_metadata_proto_enumTypes[1].Descriptor()
+}
+
+func (EncryptionOptions_Mode) Type() protoreflect.EnumType {
+ return &file_metadata_metadata_proto_enumTypes[1]
+}
+
+func (x EncryptionOptions_Mode) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use EncryptionOptions_Mode.Descriptor instead.
+func (EncryptionOptions_Mode) EnumDescriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{3, 0}
}
-func (EncryptionOptions_Mode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3, 0} }
// Cost parameters to be used in our hashing functions.
type HashingCosts struct {
- Time int64 `protobuf:"varint,2,opt,name=time" json:"time,omitempty"`
- Memory int64 `protobuf:"varint,3,opt,name=memory" json:"memory,omitempty"`
- Parallelism int64 `protobuf:"varint,4,opt,name=parallelism" json:"parallelism,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"`
+ Memory int64 `protobuf:"varint,3,opt,name=memory,proto3" json:"memory,omitempty"`
+ Parallelism int64 `protobuf:"varint,4,opt,name=parallelism,proto3" json:"parallelism,omitempty"`
+ // If true, parallelism should no longer be truncated to 8 bits.
+ TruncationFixed bool `protobuf:"varint,5,opt,name=truncation_fixed,json=truncationFixed,proto3" json:"truncation_fixed,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *HashingCosts) Reset() {
+ *x = HashingCosts{}
+ mi := &file_metadata_metadata_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *HashingCosts) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *HashingCosts) Reset() { *m = HashingCosts{} }
-func (m *HashingCosts) String() string { return proto.CompactTextString(m) }
-func (*HashingCosts) ProtoMessage() {}
-func (*HashingCosts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (*HashingCosts) ProtoMessage() {}
-func (m *HashingCosts) GetTime() int64 {
- if m != nil {
- return m.Time
+func (x *HashingCosts) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use HashingCosts.ProtoReflect.Descriptor instead.
+func (*HashingCosts) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *HashingCosts) GetTime() int64 {
+ if x != nil {
+ return x.Time
}
return 0
}
-func (m *HashingCosts) GetMemory() int64 {
- if m != nil {
- return m.Memory
+func (x *HashingCosts) GetMemory() int64 {
+ if x != nil {
+ return x.Memory
}
return 0
}
-func (m *HashingCosts) GetParallelism() int64 {
- if m != nil {
- return m.Parallelism
+func (x *HashingCosts) GetParallelism() int64 {
+ if x != nil {
+ return x.Parallelism
}
return 0
}
+func (x *HashingCosts) GetTruncationFixed() bool {
+ if x != nil {
+ return x.TruncationFixed
+ }
+ return false
+}
+
// This structure is used for our authenticated wrapping/unwrapping of keys.
type WrappedKeyData struct {
- IV []byte `protobuf:"bytes,1,opt,name=IV,json=iV,proto3" json:"IV,omitempty"`
- EncryptedKey []byte `protobuf:"bytes,2,opt,name=encrypted_key,json=encryptedKey,proto3" json:"encrypted_key,omitempty"`
- Hmac []byte `protobuf:"bytes,3,opt,name=hmac,proto3" json:"hmac,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ IV []byte `protobuf:"bytes,1,opt,name=IV,proto3" json:"IV,omitempty"`
+ EncryptedKey []byte `protobuf:"bytes,2,opt,name=encrypted_key,json=encryptedKey,proto3" json:"encrypted_key,omitempty"`
+ Hmac []byte `protobuf:"bytes,3,opt,name=hmac,proto3" json:"hmac,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *WrappedKeyData) Reset() {
+ *x = WrappedKeyData{}
+ mi := &file_metadata_metadata_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *WrappedKeyData) Reset() { *m = WrappedKeyData{} }
-func (m *WrappedKeyData) String() string { return proto.CompactTextString(m) }
-func (*WrappedKeyData) ProtoMessage() {}
-func (*WrappedKeyData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (x *WrappedKeyData) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
-func (m *WrappedKeyData) GetIV() []byte {
- if m != nil {
- return m.IV
+func (*WrappedKeyData) ProtoMessage() {}
+
+func (x *WrappedKeyData) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use WrappedKeyData.ProtoReflect.Descriptor instead.
+func (*WrappedKeyData) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *WrappedKeyData) GetIV() []byte {
+ if x != nil {
+ return x.IV
}
return nil
}
-func (m *WrappedKeyData) GetEncryptedKey() []byte {
- if m != nil {
- return m.EncryptedKey
+func (x *WrappedKeyData) GetEncryptedKey() []byte {
+ if x != nil {
+ return x.EncryptedKey
}
return nil
}
-func (m *WrappedKeyData) GetHmac() []byte {
- if m != nil {
- return m.Hmac
+func (x *WrappedKeyData) GetHmac() []byte {
+ if x != nil {
+ return x.Hmac
}
return nil
}
// The associated data for each protector
type ProtectorData struct {
- ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor" json:"protector_descriptor,omitempty"`
- Source SourceType `protobuf:"varint,2,opt,name=source,enum=metadata.SourceType" json:"source,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor,proto3" json:"protector_descriptor,omitempty"`
+ Source SourceType `protobuf:"varint,2,opt,name=source,proto3,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"`
- WrappedKey *WrappedKeyData `protobuf:"bytes,7,opt,name=wrapped_key,json=wrappedKey" json:"wrapped_key,omitempty"`
+ Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+ Costs *HashingCosts `protobuf:"bytes,4,opt,name=costs,proto3" json:"costs,omitempty"`
+ Salt []byte `protobuf:"bytes,5,opt,name=salt,proto3" json:"salt,omitempty"`
+ Uid int64 `protobuf:"varint,6,opt,name=uid,proto3" json:"uid,omitempty"`
+ WrappedKey *WrappedKeyData `protobuf:"bytes,7,opt,name=wrapped_key,json=wrappedKey,proto3" json:"wrapped_key,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ProtectorData) Reset() { *m = ProtectorData{} }
-func (m *ProtectorData) String() string { return proto.CompactTextString(m) }
-func (*ProtectorData) ProtoMessage() {}
-func (*ProtectorData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (x *ProtectorData) Reset() {
+ *x = ProtectorData{}
+ mi := &file_metadata_metadata_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ProtectorData) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
-func (m *ProtectorData) GetProtectorDescriptor() string {
- if m != nil {
- return m.ProtectorDescriptor
+func (*ProtectorData) ProtoMessage() {}
+
+func (x *ProtectorData) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProtectorData.ProtoReflect.Descriptor instead.
+func (*ProtectorData) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ProtectorData) GetProtectorDescriptor() string {
+ if x != nil {
+ return x.ProtectorDescriptor
}
return ""
}
-func (m *ProtectorData) GetSource() SourceType {
- if m != nil {
- return m.Source
+func (x *ProtectorData) GetSource() SourceType {
+ if x != nil {
+ return x.Source
}
return SourceType_default
}
-func (m *ProtectorData) GetName() string {
- if m != nil {
- return m.Name
+func (x *ProtectorData) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *ProtectorData) GetCosts() *HashingCosts {
- if m != nil {
- return m.Costs
+func (x *ProtectorData) GetCosts() *HashingCosts {
+ if x != nil {
+ return x.Costs
}
return nil
}
-func (m *ProtectorData) GetSalt() []byte {
- if m != nil {
- return m.Salt
+func (x *ProtectorData) GetSalt() []byte {
+ if x != nil {
+ return x.Salt
}
return nil
}
-func (m *ProtectorData) GetUid() int64 {
- if m != nil {
- return m.Uid
+func (x *ProtectorData) GetUid() int64 {
+ if x != nil {
+ return x.Uid
}
return 0
}
-func (m *ProtectorData) GetWrappedKey() *WrappedKeyData {
- if m != nil {
- return m.WrappedKey
+func (x *ProtectorData) GetWrappedKey() *WrappedKeyData {
+ if x != nil {
+ return x.WrappedKey
}
return nil
}
// Encryption policy specifics, corresponds to the fscrypt_policy struct
type EncryptionOptions struct {
- 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"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Padding int64 `protobuf:"varint,1,opt,name=padding,proto3" json:"padding,omitempty"`
+ Contents EncryptionOptions_Mode `protobuf:"varint,2,opt,name=contents,proto3,enum=metadata.EncryptionOptions_Mode" json:"contents,omitempty"`
+ Filenames EncryptionOptions_Mode `protobuf:"varint,3,opt,name=filenames,proto3,enum=metadata.EncryptionOptions_Mode" json:"filenames,omitempty"`
+ PolicyVersion int64 `protobuf:"varint,4,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *EncryptionOptions) Reset() {
+ *x = EncryptionOptions{}
+ mi := &file_metadata_metadata_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *EncryptionOptions) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *EncryptionOptions) Reset() { *m = EncryptionOptions{} }
-func (m *EncryptionOptions) String() string { return proto.CompactTextString(m) }
-func (*EncryptionOptions) ProtoMessage() {}
-func (*EncryptionOptions) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (*EncryptionOptions) ProtoMessage() {}
-func (m *EncryptionOptions) GetPadding() int64 {
- if m != nil {
- return m.Padding
+func (x *EncryptionOptions) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use EncryptionOptions.ProtoReflect.Descriptor instead.
+func (*EncryptionOptions) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *EncryptionOptions) GetPadding() int64 {
+ if x != nil {
+ return x.Padding
}
return 0
}
-func (m *EncryptionOptions) GetContents() EncryptionOptions_Mode {
- if m != nil {
- return m.Contents
+func (x *EncryptionOptions) GetContents() EncryptionOptions_Mode {
+ if x != nil {
+ return x.Contents
}
return EncryptionOptions_default
}
-func (m *EncryptionOptions) GetFilenames() EncryptionOptions_Mode {
- if m != nil {
- return m.Filenames
+func (x *EncryptionOptions) GetFilenames() EncryptionOptions_Mode {
+ if x != nil {
+ return x.Filenames
}
return EncryptionOptions_default
}
+func (x *EncryptionOptions) GetPolicyVersion() int64 {
+ if x != nil {
+ return x.PolicyVersion
+ }
+ return 0
+}
+
type WrappedPolicyKey struct {
- ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor" json:"protector_descriptor,omitempty"`
- WrappedKey *WrappedKeyData `protobuf:"bytes,2,opt,name=wrapped_key,json=wrappedKey" json:"wrapped_key,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ProtectorDescriptor string `protobuf:"bytes,1,opt,name=protector_descriptor,json=protectorDescriptor,proto3" json:"protector_descriptor,omitempty"`
+ WrappedKey *WrappedKeyData `protobuf:"bytes,2,opt,name=wrapped_key,json=wrappedKey,proto3" json:"wrapped_key,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *WrappedPolicyKey) Reset() { *m = WrappedPolicyKey{} }
-func (m *WrappedPolicyKey) String() string { return proto.CompactTextString(m) }
-func (*WrappedPolicyKey) ProtoMessage() {}
-func (*WrappedPolicyKey) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+func (x *WrappedPolicyKey) Reset() {
+ *x = WrappedPolicyKey{}
+ mi := &file_metadata_metadata_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-func (m *WrappedPolicyKey) GetProtectorDescriptor() string {
- if m != nil {
- return m.ProtectorDescriptor
+func (x *WrappedPolicyKey) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WrappedPolicyKey) ProtoMessage() {}
+
+func (x *WrappedPolicyKey) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use WrappedPolicyKey.ProtoReflect.Descriptor instead.
+func (*WrappedPolicyKey) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *WrappedPolicyKey) GetProtectorDescriptor() string {
+ if x != nil {
+ return x.ProtectorDescriptor
}
return ""
}
-func (m *WrappedPolicyKey) GetWrappedKey() *WrappedKeyData {
- if m != nil {
- return m.WrappedKey
+func (x *WrappedPolicyKey) GetWrappedKey() *WrappedKeyData {
+ if x != nil {
+ return x.WrappedKey
}
return nil
}
// The associated data for each policy
type PolicyData struct {
- KeyDescriptor string `protobuf:"bytes,1,opt,name=key_descriptor,json=keyDescriptor" json:"key_descriptor,omitempty"`
- Options *EncryptionOptions `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"`
- WrappedPolicyKeys []*WrappedPolicyKey `protobuf:"bytes,3,rep,name=wrapped_policy_keys,json=wrappedPolicyKeys" json:"wrapped_policy_keys,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ KeyDescriptor string `protobuf:"bytes,1,opt,name=key_descriptor,json=keyDescriptor,proto3" json:"key_descriptor,omitempty"`
+ Options *EncryptionOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+ WrappedPolicyKeys []*WrappedPolicyKey `protobuf:"bytes,3,rep,name=wrapped_policy_keys,json=wrappedPolicyKeys,proto3" json:"wrapped_policy_keys,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *PolicyData) Reset() {
+ *x = PolicyData{}
+ mi := &file_metadata_metadata_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *PolicyData) Reset() { *m = PolicyData{} }
-func (m *PolicyData) String() string { return proto.CompactTextString(m) }
-func (*PolicyData) ProtoMessage() {}
-func (*PolicyData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
+func (x *PolicyData) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
-func (m *PolicyData) GetKeyDescriptor() string {
- if m != nil {
- return m.KeyDescriptor
+func (*PolicyData) ProtoMessage() {}
+
+func (x *PolicyData) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PolicyData.ProtoReflect.Descriptor instead.
+func (*PolicyData) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *PolicyData) GetKeyDescriptor() string {
+ if x != nil {
+ return x.KeyDescriptor
}
return ""
}
-func (m *PolicyData) GetOptions() *EncryptionOptions {
- if m != nil {
- return m.Options
+func (x *PolicyData) GetOptions() *EncryptionOptions {
+ if x != nil {
+ return x.Options
}
return nil
}
-func (m *PolicyData) GetWrappedPolicyKeys() []*WrappedPolicyKey {
- if m != nil {
- return m.WrappedPolicyKeys
+func (x *PolicyData) GetWrappedPolicyKeys() []*WrappedPolicyKey {
+ if x != nil {
+ return x.WrappedPolicyKeys
}
return nil
}
// Data stored in the config file
type Config struct {
- Source SourceType `protobuf:"varint,1,opt,name=source,enum=metadata.SourceType" json:"source,omitempty"`
- HashCosts *HashingCosts `protobuf:"bytes,2,opt,name=hash_costs,json=hashCosts" json:"hash_costs,omitempty"`
- Compatibility string `protobuf:"bytes,3,opt,name=compatibility" json:"compatibility,omitempty"`
- Options *EncryptionOptions `protobuf:"bytes,4,opt,name=options" json:"options,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Source SourceType `protobuf:"varint,1,opt,name=source,proto3,enum=metadata.SourceType" json:"source,omitempty"`
+ HashCosts *HashingCosts `protobuf:"bytes,2,opt,name=hash_costs,json=hashCosts,proto3" json:"hash_costs,omitempty"`
+ Options *EncryptionOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
+ UseFsKeyringForV1Policies bool `protobuf:"varint,5,opt,name=use_fs_keyring_for_v1_policies,json=useFsKeyringForV1Policies,proto3" json:"use_fs_keyring_for_v1_policies,omitempty"`
+ AllowCrossUserMetadata bool `protobuf:"varint,6,opt,name=allow_cross_user_metadata,json=allowCrossUserMetadata,proto3" json:"allow_cross_user_metadata,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Config) Reset() {
+ *x = Config{}
+ mi := &file_metadata_metadata_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Config) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Config) Reset() { *m = Config{} }
-func (m *Config) String() string { return proto.CompactTextString(m) }
-func (*Config) ProtoMessage() {}
-func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
+func (*Config) ProtoMessage() {}
-func (m *Config) GetSource() SourceType {
- if m != nil {
- return m.Source
+func (x *Config) ProtoReflect() protoreflect.Message {
+ mi := &file_metadata_metadata_proto_msgTypes[6]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Config.ProtoReflect.Descriptor instead.
+func (*Config) Descriptor() ([]byte, []int) {
+ return file_metadata_metadata_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *Config) GetSource() SourceType {
+ if x != nil {
+ return x.Source
}
return SourceType_default
}
-func (m *Config) GetHashCosts() *HashingCosts {
- if m != nil {
- return m.HashCosts
+func (x *Config) GetHashCosts() *HashingCosts {
+ if x != nil {
+ return x.HashCosts
}
return nil
}
-func (m *Config) GetCompatibility() string {
- if m != nil {
- return m.Compatibility
+func (x *Config) GetOptions() *EncryptionOptions {
+ if x != nil {
+ return x.Options
}
- return ""
+ return nil
}
-func (m *Config) GetOptions() *EncryptionOptions {
- if m != nil {
- return m.Options
+func (x *Config) GetUseFsKeyringForV1Policies() bool {
+ if x != nil {
+ return x.UseFsKeyringForV1Policies
}
- return nil
+ return false
}
-func init() {
- proto.RegisterType((*HashingCosts)(nil), "metadata.HashingCosts")
- proto.RegisterType((*WrappedKeyData)(nil), "metadata.WrappedKeyData")
- proto.RegisterType((*ProtectorData)(nil), "metadata.ProtectorData")
- proto.RegisterType((*EncryptionOptions)(nil), "metadata.EncryptionOptions")
- proto.RegisterType((*WrappedPolicyKey)(nil), "metadata.WrappedPolicyKey")
- proto.RegisterType((*PolicyData)(nil), "metadata.PolicyData")
- proto.RegisterType((*Config)(nil), "metadata.Config")
- proto.RegisterEnum("metadata.SourceType", SourceType_name, SourceType_value)
- proto.RegisterEnum("metadata.EncryptionOptions_Mode", EncryptionOptions_Mode_name, EncryptionOptions_Mode_value)
-}
-
-func init() { proto.RegisterFile("metadata/metadata.proto", fileDescriptor0) }
-
-var fileDescriptor0 = []byte{
- // 642 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xed, 0x6a, 0xdb, 0x3c,
- 0x14, 0xc7, 0x1f, 0xdb, 0x69, 0xd2, 0x9c, 0xbc, 0x3c, 0xae, 0xda, 0x75, 0x66, 0xfb, 0x12, 0xbc,
- 0x0d, 0xca, 0x28, 0x1d, 0xcd, 0xe8, 0xd8, 0x60, 0x0c, 0xb6, 0xb4, 0xec, 0xa5, 0x94, 0x75, 0x4a,
- 0xe8, 0x36, 0x18, 0x04, 0xd5, 0x56, 0x1b, 0x51, 0xdb, 0x12, 0x96, 0x42, 0xf0, 0xb7, 0xdd, 0xc3,
- 0xee, 0x61, 0x97, 0xb0, 0x8b, 0xd8, 0x55, 0x0d, 0xc9, 0xb1, 0xe3, 0xb4, 0x50, 0xb2, 0x7d, 0x31,
- 0x47, 0x7f, 0x49, 0xe7, 0x7f, 0xf4, 0x93, 0x8e, 0xe1, 0x6e, 0x4c, 0x15, 0x09, 0x89, 0x22, 0x4f,
- 0x8a, 0x60, 0x4f, 0xa4, 0x5c, 0x71, 0xb4, 0x5e, 0x8c, 0xfd, 0x6f, 0xd0, 0x7e, 0x47, 0xe4, 0x84,
- 0x25, 0x97, 0x03, 0x2e, 0x95, 0x44, 0x08, 0x6a, 0x8a, 0xc5, 0xd4, 0xb3, 0x7b, 0xd6, 0x8e, 0x83,
- 0x4d, 0x8c, 0xb6, 0xa1, 0x1e, 0xd3, 0x98, 0xa7, 0x99, 0xe7, 0x18, 0x75, 0x3e, 0x42, 0x3d, 0x68,
- 0x09, 0x92, 0x92, 0x28, 0xa2, 0x11, 0x93, 0xb1, 0x57, 0x33, 0x93, 0x55, 0xc9, 0xff, 0x0a, 0xdd,
- 0xcf, 0x29, 0x11, 0x82, 0x86, 0xc7, 0x34, 0x3b, 0x24, 0x8a, 0xa0, 0x2e, 0xd8, 0xef, 0xcf, 0x3c,
- 0xab, 0x67, 0xed, 0xb4, 0xb1, 0xcd, 0xce, 0xd0, 0x03, 0xe8, 0xd0, 0x24, 0x48, 0x33, 0xa1, 0x68,
- 0x38, 0xbe, 0xa2, 0x99, 0x31, 0x6e, 0xe3, 0x76, 0x29, 0x1e, 0xd3, 0x4c, 0x17, 0x35, 0x89, 0x49,
- 0x60, 0xec, 0xdb, 0xd8, 0xc4, 0xfe, 0x0f, 0x1b, 0x3a, 0xa7, 0x29, 0x57, 0x34, 0x50, 0x3c, 0x35,
- 0xa9, 0xf7, 0x61, 0x4b, 0x14, 0xc2, 0x38, 0xa4, 0x32, 0x48, 0x99, 0x50, 0x3c, 0x35, 0x66, 0x4d,
- 0xbc, 0x59, 0xce, 0x1d, 0x96, 0x53, 0x68, 0x17, 0xea, 0x92, 0x4f, 0xd3, 0x20, 0x3f, 0x6f, 0xb7,
- 0xbf, 0xb5, 0x57, 0x82, 0x1a, 0x1a, 0x7d, 0x94, 0x09, 0x8a, 0xe7, 0x6b, 0x74, 0x19, 0x09, 0x89,
- 0xa9, 0x29, 0xa3, 0x89, 0x4d, 0x8c, 0x76, 0x61, 0x2d, 0xd0, 0xe0, 0xcc, 0xe9, 0x5b, 0xfd, 0xed,
- 0x45, 0x82, 0x2a, 0x56, 0x9c, 0x2f, 0xd2, 0x19, 0x24, 0x89, 0x94, 0xb7, 0x96, 0x1f, 0x44, 0xc7,
- 0xc8, 0x05, 0x67, 0xca, 0x42, 0xaf, 0x6e, 0xe8, 0xe9, 0x10, 0xbd, 0x80, 0xd6, 0x2c, 0xa7, 0x66,
- 0x88, 0x34, 0x4c, 0x66, 0x6f, 0x91, 0x79, 0x19, 0x29, 0x86, 0x59, 0x39, 0xf6, 0x7f, 0xda, 0xb0,
- 0x71, 0x94, 0xa3, 0x63, 0x3c, 0xf9, 0x68, 0xbe, 0x12, 0x79, 0xd0, 0x10, 0x24, 0x0c, 0x59, 0x72,
- 0x69, 0x60, 0x38, 0xb8, 0x18, 0xa2, 0x97, 0xb0, 0x1e, 0xf0, 0x44, 0xd1, 0x44, 0xc9, 0x39, 0x82,
- 0xde, 0xc2, 0xe7, 0x46, 0xa2, 0xbd, 0x13, 0x1e, 0x52, 0x5c, 0xee, 0x40, 0xaf, 0xa0, 0x79, 0xc1,
- 0x22, 0xaa, 0x41, 0x48, 0x43, 0x65, 0x95, 0xed, 0x8b, 0x2d, 0x7e, 0x06, 0x35, 0x2d, 0xa1, 0x16,
- 0x34, 0x42, 0x7a, 0x41, 0xa6, 0x91, 0x72, 0xff, 0x43, 0xff, 0x43, 0xeb, 0xf5, 0xd1, 0x70, 0xdc,
- 0x3f, 0x78, 0x36, 0xfe, 0x32, 0x1a, 0xba, 0x56, 0x55, 0x78, 0x3b, 0x38, 0x71, 0xed, 0xaa, 0x30,
- 0x78, 0x33, 0x70, 0x9d, 0x25, 0x61, 0x34, 0x74, 0x6b, 0x85, 0xb0, 0xdf, 0x7f, 0x6e, 0x56, 0xac,
- 0x2d, 0x09, 0xa3, 0xa1, 0x5b, 0xf7, 0xbf, 0x5b, 0xe0, 0xce, 0x39, 0x9e, 0xf2, 0x88, 0x05, 0x99,
- 0x7e, 0x67, 0xff, 0xf0, 0x82, 0xae, 0xdd, 0x95, 0xfd, 0x17, 0x77, 0xf5, 0xcb, 0x02, 0xc8, 0xbd,
- 0xcd, 0xf3, 0x7d, 0x04, 0xdd, 0x2b, 0x9a, 0xdd, 0xb4, 0xed, 0x5c, 0xd1, 0xac, 0x62, 0x78, 0x00,
- 0x0d, 0x9e, 0xe3, 0x9c, 0x9b, 0xdd, 0xbf, 0x85, 0x38, 0x2e, 0xd6, 0xa2, 0x0f, 0xb0, 0x59, 0xd4,
- 0x29, 0x8c, 0xa7, 0x2e, 0x57, 0x5f, 0x9a, 0xb3, 0xd3, 0xea, 0xdf, 0xbb, 0x51, 0x6f, 0xc9, 0x04,
- 0x6f, 0xcc, 0xae, 0x29, 0xd2, 0xff, 0x6d, 0x41, 0x7d, 0xc0, 0x93, 0x0b, 0x76, 0x59, 0x69, 0x20,
- 0x6b, 0x85, 0x06, 0x3a, 0x00, 0x98, 0x10, 0x39, 0x19, 0xe7, 0x1d, 0x63, 0xdf, 0xda, 0x31, 0x4d,
- 0xbd, 0x32, 0xff, 0x27, 0x3d, 0x84, 0x4e, 0xc0, 0x63, 0x41, 0x14, 0x3b, 0x67, 0x11, 0x53, 0xd9,
- 0xbc, 0x01, 0x97, 0xc5, 0x2a, 0x98, 0xda, 0xea, 0x60, 0x1e, 0x7f, 0x02, 0x58, 0x54, 0xba, 0xfc,
- 0x12, 0x11, 0x74, 0x05, 0x89, 0xc7, 0x82, 0x48, 0x29, 0x26, 0x29, 0x91, 0xd4, 0xb5, 0xd0, 0x1d,
- 0xd8, 0x08, 0xa6, 0x52, 0xf1, 0x25, 0xd9, 0xd6, 0xfb, 0x52, 0x32, 0xd3, 0x4c, 0x5d, 0xe7, 0xbc,
- 0x6e, 0x7e, 0xb2, 0x4f, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x99, 0x65, 0x0d, 0x45, 0x7f, 0x05,
- 0x00, 0x00,
+func (x *Config) GetAllowCrossUserMetadata() bool {
+ if x != nil {
+ return x.AllowCrossUserMetadata
+ }
+ return false
+}
+
+var File_metadata_metadata_proto protoreflect.FileDescriptor
+
+const file_metadata_metadata_proto_rawDesc = "" +
+ "\n" +
+ "\x17metadata/metadata.proto\x12\bmetadata\"\x87\x01\n" +
+ "\fHashingCosts\x12\x12\n" +
+ "\x04time\x18\x02 \x01(\x03R\x04time\x12\x16\n" +
+ "\x06memory\x18\x03 \x01(\x03R\x06memory\x12 \n" +
+ "\vparallelism\x18\x04 \x01(\x03R\vparallelism\x12)\n" +
+ "\x10truncation_fixed\x18\x05 \x01(\bR\x0ftruncationFixed\"Y\n" +
+ "\x0eWrappedKeyData\x12\x0e\n" +
+ "\x02IV\x18\x01 \x01(\fR\x02IV\x12#\n" +
+ "\rencrypted_key\x18\x02 \x01(\fR\fencryptedKey\x12\x12\n" +
+ "\x04hmac\x18\x03 \x01(\fR\x04hmac\"\x93\x02\n" +
+ "\rProtectorData\x121\n" +
+ "\x14protector_descriptor\x18\x01 \x01(\tR\x13protectorDescriptor\x12,\n" +
+ "\x06source\x18\x02 \x01(\x0e2\x14.metadata.SourceTypeR\x06source\x12\x12\n" +
+ "\x04name\x18\x03 \x01(\tR\x04name\x12,\n" +
+ "\x05costs\x18\x04 \x01(\v2\x16.metadata.HashingCostsR\x05costs\x12\x12\n" +
+ "\x04salt\x18\x05 \x01(\fR\x04salt\x12\x10\n" +
+ "\x03uid\x18\x06 \x01(\x03R\x03uid\x129\n" +
+ "\vwrapped_key\x18\a \x01(\v2\x18.metadata.WrappedKeyDataR\n" +
+ "wrappedKey\"\xef\x02\n" +
+ "\x11EncryptionOptions\x12\x18\n" +
+ "\apadding\x18\x01 \x01(\x03R\apadding\x12<\n" +
+ "\bcontents\x18\x02 \x01(\x0e2 .metadata.EncryptionOptions.ModeR\bcontents\x12>\n" +
+ "\tfilenames\x18\x03 \x01(\x0e2 .metadata.EncryptionOptions.ModeR\tfilenames\x12%\n" +
+ "\x0epolicy_version\x18\x04 \x01(\x03R\rpolicyVersion\"\x9a\x01\n" +
+ "\x04Mode\x12\v\n" +
+ "\adefault\x10\x00\x12\x0f\n" +
+ "\vAES_256_XTS\x10\x01\x12\x0f\n" +
+ "\vAES_256_GCM\x10\x02\x12\x0f\n" +
+ "\vAES_256_CBC\x10\x03\x12\x0f\n" +
+ "\vAES_256_CTS\x10\x04\x12\x0f\n" +
+ "\vAES_128_CBC\x10\x05\x12\x0f\n" +
+ "\vAES_128_CTS\x10\x06\x12\f\n" +
+ "\bAdiantum\x10\t\x12\x11\n" +
+ "\rAES_256_HCTR2\x10\n" +
+ "\"\x80\x01\n" +
+ "\x10WrappedPolicyKey\x121\n" +
+ "\x14protector_descriptor\x18\x01 \x01(\tR\x13protectorDescriptor\x129\n" +
+ "\vwrapped_key\x18\x02 \x01(\v2\x18.metadata.WrappedKeyDataR\n" +
+ "wrappedKey\"\xb6\x01\n" +
+ "\n" +
+ "PolicyData\x12%\n" +
+ "\x0ekey_descriptor\x18\x01 \x01(\tR\rkeyDescriptor\x125\n" +
+ "\aoptions\x18\x02 \x01(\v2\x1b.metadata.EncryptionOptionsR\aoptions\x12J\n" +
+ "\x13wrapped_policy_keys\x18\x03 \x03(\v2\x1a.metadata.WrappedPolicyKeyR\x11wrappedPolicyKeys\"\xb7\x02\n" +
+ "\x06Config\x12,\n" +
+ "\x06source\x18\x01 \x01(\x0e2\x14.metadata.SourceTypeR\x06source\x125\n" +
+ "\n" +
+ "hash_costs\x18\x02 \x01(\v2\x16.metadata.HashingCostsR\thashCosts\x125\n" +
+ "\aoptions\x18\x04 \x01(\v2\x1b.metadata.EncryptionOptionsR\aoptions\x12A\n" +
+ "\x1euse_fs_keyring_for_v1_policies\x18\x05 \x01(\bR\x19useFsKeyringForV1Policies\x129\n" +
+ "\x19allow_cross_user_metadata\x18\x06 \x01(\bR\x16allowCrossUserMetadataJ\x04\b\x03\x10\x04R\rcompatibility*Q\n" +
+ "\n" +
+ "SourceType\x12\v\n" +
+ "\adefault\x10\x00\x12\x12\n" +
+ "\x0epam_passphrase\x10\x01\x12\x15\n" +
+ "\x11custom_passphrase\x10\x02\x12\v\n" +
+ "\araw_key\x10\x03B$Z\"github.com/google/fscrypt/metadatab\x06proto3"
+
+var (
+ file_metadata_metadata_proto_rawDescOnce sync.Once
+ file_metadata_metadata_proto_rawDescData []byte
+)
+
+func file_metadata_metadata_proto_rawDescGZIP() []byte {
+ file_metadata_metadata_proto_rawDescOnce.Do(func() {
+ file_metadata_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_metadata_metadata_proto_rawDesc), len(file_metadata_metadata_proto_rawDesc)))
+ })
+ return file_metadata_metadata_proto_rawDescData
+}
+
+var file_metadata_metadata_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_metadata_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_metadata_metadata_proto_goTypes = []any{
+ (SourceType)(0), // 0: metadata.SourceType
+ (EncryptionOptions_Mode)(0), // 1: metadata.EncryptionOptions.Mode
+ (*HashingCosts)(nil), // 2: metadata.HashingCosts
+ (*WrappedKeyData)(nil), // 3: metadata.WrappedKeyData
+ (*ProtectorData)(nil), // 4: metadata.ProtectorData
+ (*EncryptionOptions)(nil), // 5: metadata.EncryptionOptions
+ (*WrappedPolicyKey)(nil), // 6: metadata.WrappedPolicyKey
+ (*PolicyData)(nil), // 7: metadata.PolicyData
+ (*Config)(nil), // 8: metadata.Config
+}
+var file_metadata_metadata_proto_depIdxs = []int32{
+ 0, // 0: metadata.ProtectorData.source:type_name -> metadata.SourceType
+ 2, // 1: metadata.ProtectorData.costs:type_name -> metadata.HashingCosts
+ 3, // 2: metadata.ProtectorData.wrapped_key:type_name -> metadata.WrappedKeyData
+ 1, // 3: metadata.EncryptionOptions.contents:type_name -> metadata.EncryptionOptions.Mode
+ 1, // 4: metadata.EncryptionOptions.filenames:type_name -> metadata.EncryptionOptions.Mode
+ 3, // 5: metadata.WrappedPolicyKey.wrapped_key:type_name -> metadata.WrappedKeyData
+ 5, // 6: metadata.PolicyData.options:type_name -> metadata.EncryptionOptions
+ 6, // 7: metadata.PolicyData.wrapped_policy_keys:type_name -> metadata.WrappedPolicyKey
+ 0, // 8: metadata.Config.source:type_name -> metadata.SourceType
+ 2, // 9: metadata.Config.hash_costs:type_name -> metadata.HashingCosts
+ 5, // 10: metadata.Config.options:type_name -> metadata.EncryptionOptions
+ 11, // [11:11] is the sub-list for method output_type
+ 11, // [11:11] is the sub-list for method input_type
+ 11, // [11:11] is the sub-list for extension type_name
+ 11, // [11:11] is the sub-list for extension extendee
+ 0, // [0:11] is the sub-list for field type_name
+}
+
+func init() { file_metadata_metadata_proto_init() }
+func file_metadata_metadata_proto_init() {
+ if File_metadata_metadata_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_metadata_metadata_proto_rawDesc), len(file_metadata_metadata_proto_rawDesc)),
+ NumEnums: 2,
+ NumMessages: 7,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_metadata_metadata_proto_goTypes,
+ DependencyIndexes: file_metadata_metadata_proto_depIdxs,
+ EnumInfos: file_metadata_metadata_proto_enumTypes,
+ MessageInfos: file_metadata_metadata_proto_msgTypes,
+ }.Build()
+ File_metadata_metadata_proto = out.File
+ file_metadata_metadata_proto_goTypes = nil
+ file_metadata_metadata_proto_depIdxs = nil
}
diff --git a/metadata/metadata.proto b/metadata/metadata.proto
index 5e1b9dd..f2dd78f 100644
--- a/metadata/metadata.proto
+++ b/metadata/metadata.proto
@@ -19,15 +19,20 @@
* the License.
*/
-// If you modify this file, be sure to run "go generate" on this package.
+// If the *.proto file is modified, be sure to run "make gen" (at the project
+// root) to recreate the *.pb.go file.
syntax = "proto3";
package metadata;
+option go_package = "github.com/google/fscrypt/metadata";
+
// Cost parameters to be used in our hashing functions.
message HashingCosts {
int64 time = 2;
int64 memory = 3;
int64 parallelism = 4;
+ // If true, parallelism should no longer be truncated to 8 bits.
+ bool truncation_fixed = 5;
}
// This structure is used for our authenticated wrapping/unwrapping of keys.
@@ -63,7 +68,7 @@ message ProtectorData {
message EncryptionOptions {
int64 padding = 1;
- // Type of encryption; should match declarations of unix.FS_ENCRYPTION_MODE
+ // Type of encryption; should match declarations of unix.FSCRYPT_MODE
enum Mode {
default = 0;
AES_256_XTS = 1;
@@ -72,10 +77,14 @@ message EncryptionOptions {
AES_256_CTS = 4;
AES_128_CBC = 5;
AES_128_CTS = 6;
+ Adiantum = 9;
+ AES_256_HCTR2 = 10;
}
Mode contents = 2;
Mode filenames = 3;
+
+ int64 policy_version = 4;
}
message WrappedPolicyKey {
@@ -94,6 +103,11 @@ message PolicyData {
message Config {
SourceType source = 1;
HashingCosts hash_costs = 2;
- string compatibility = 3;
EncryptionOptions options = 4;
+ bool use_fs_keyring_for_v1_policies = 5;
+ bool allow_cross_user_metadata = 6;
+
+ // reserve the removed field 'string compatibility = 3;'
+ reserved 3;
+ reserved "compatibility";
}
diff --git a/metadata/policy.go b/metadata/policy.go
index 533d48a..fe6c38f 100644
--- a/metadata/policy.go
+++ b/metadata/policy.go
@@ -22,9 +22,13 @@ package metadata
import (
"encoding/hex"
+ "fmt"
"log"
"math"
"os"
+ "os/user"
+ "strconv"
+ "syscall"
"unsafe"
"github.com/pkg/errors"
@@ -33,80 +37,248 @@ import (
"github.com/google/fscrypt/util"
)
-// Encryption specific errors
var (
+ // ErrEncryptionNotSupported indicates that encryption is not supported
+ // on the given filesystem, and there is no way to enable it.
ErrEncryptionNotSupported = errors.New("encryption not supported")
- ErrEncryptionNotEnabled = errors.New("encryption not enabled")
- ErrNotEncrypted = errors.New("file or directory not encrypted")
- ErrEncrypted = errors.New("file or directory already encrypted")
- ErrBadEncryptionOptions = util.SystemError("invalid encryption options provided")
+
+ // ErrEncryptionNotEnabled indicates that encryption is not supported on
+ // the given filesystem, but there is a way to enable it.
+ ErrEncryptionNotEnabled = errors.New("encryption not enabled")
)
-// policyIoctl is a wrapper for the ioctl syscall. It passes the correct
-// pointers and file descriptors to the IOCTL syscall. This function also takes
-// some of the unclear errors returned by the syscall and translates then into
-// more specific error strings.
-func policyIoctl(file *os.File, request uintptr, policy *unix.FscryptPolicy) error {
- // The returned errno value can sometimes give strange errors, so we
- // return encryption specific errors.
- _, _, errno := unix.Syscall(unix.SYS_IOCTL, file.Fd(), request, uintptr(unsafe.Pointer(policy)))
- switch errno {
- case 0:
+// ErrAlreadyEncrypted indicates that the path is already encrypted.
+type ErrAlreadyEncrypted struct {
+ Path string
+}
+
+func (err *ErrAlreadyEncrypted) Error() string {
+ return fmt.Sprintf("file or directory %q is already encrypted", err.Path)
+}
+
+// ErrBadEncryptionOptions indicates that unsupported encryption options were given.
+type ErrBadEncryptionOptions struct {
+ Path string
+ Options *EncryptionOptions
+}
+
+func (err *ErrBadEncryptionOptions) Error() string {
+ return fmt.Sprintf(`cannot encrypt %q because the kernel doesn't support the requested encryption options.
+
+ The options are %s`, err.Path, err.Options)
+}
+
+// ErrDirectoryNotOwned indicates a directory can't be encrypted because it's
+// owned by another user.
+type ErrDirectoryNotOwned struct {
+ Path string
+ Owner uint32
+}
+
+func (err *ErrDirectoryNotOwned) Error() string {
+ owner := strconv.Itoa(int(err.Owner))
+ if u, e := user.LookupId(owner); e == nil && u.Username != "" {
+ owner = u.Username
+ }
+ return fmt.Sprintf(`cannot encrypt %q because it's owned by another user (%s).
+
+ Encryption can only be enabled on a directory you own, even if you have
+ write access to the directory.`, err.Path, owner)
+}
+
+// ErrLockedRegularFile indicates that the path is a locked regular file.
+type ErrLockedRegularFile struct {
+ Path string
+}
+
+func (err *ErrLockedRegularFile) Error() string {
+ return fmt.Sprintf("cannot operate on locked regular file %q", err.Path)
+}
+
+// ErrNotEncrypted indicates that the path is not encrypted.
+type ErrNotEncrypted struct {
+ Path string
+}
+
+func (err *ErrNotEncrypted) Error() string {
+ return fmt.Sprintf("file or directory %q is not encrypted", err.Path)
+}
+
+func getPolicyIoctl(file *os.File, request uintptr, arg unsafe.Pointer) error {
+ _, _, errno := unix.Syscall(unix.SYS_IOCTL, file.Fd(), request, uintptr(arg))
+ if errno == 0 {
return nil
- case unix.ENOTTY:
- return ErrEncryptionNotSupported
- case unix.EOPNOTSUPP:
- return ErrEncryptionNotEnabled
- case unix.ENODATA, unix.ENOENT:
- // ENOENT was returned instead of ENODATA on some filesystems before v4.11.
- return ErrNotEncrypted
- case unix.EEXIST:
- // EINVAL was returned instead of EEXIST on some filesystems before v4.11.
- return ErrEncrypted
- default:
+ }
+ return errno
+}
+
+func setPolicy(file *os.File, arg unsafe.Pointer) error {
+ _, _, errno := unix.Syscall(unix.SYS_IOCTL, file.Fd(), unix.FS_IOC_SET_ENCRYPTION_POLICY, uintptr(arg))
+ if errno != 0 {
return errno
}
+
+ if err := file.Sync(); err != nil {
+ return err
+ }
+
+ return nil
}
-// Maps EncryptionOptions.Padding <-> FscryptPolicy.Flags
+// Maps EncryptionOptions.Padding <-> FSCRYPT_POLICY_FLAGS
var (
paddingArray = []int64{4, 8, 16, 32}
- flagsArray = []int64{unix.FS_POLICY_FLAGS_PAD_4, unix.FS_POLICY_FLAGS_PAD_8,
- unix.FS_POLICY_FLAGS_PAD_16, unix.FS_POLICY_FLAGS_PAD_32}
+ flagsArray = []int64{unix.FSCRYPT_POLICY_FLAGS_PAD_4, unix.FSCRYPT_POLICY_FLAGS_PAD_8,
+ unix.FSCRYPT_POLICY_FLAGS_PAD_16, unix.FSCRYPT_POLICY_FLAGS_PAD_32}
)
+// flagsToPadding returns the amount of padding specified in the policy flags.
+func flagsToPadding(flags uint8) int64 {
+ paddingFlag := int64(flags & unix.FS_POLICY_FLAGS_PAD_MASK)
+
+ // This lookup should always succeed
+ padding, ok := util.Lookup(paddingFlag, flagsArray, paddingArray)
+ if !ok {
+ log.Panicf("padding flag of %x not found", paddingFlag)
+ }
+ return padding
+}
+
+func buildV1PolicyData(policy *unix.FscryptPolicyV1) *PolicyData {
+ return &PolicyData{
+ KeyDescriptor: hex.EncodeToString(policy.Master_key_descriptor[:]),
+ Options: &EncryptionOptions{
+ Padding: flagsToPadding(policy.Flags),
+ Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
+ Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
+ PolicyVersion: 1,
+ },
+ }
+}
+
+func buildV2PolicyData(policy *unix.FscryptPolicyV2) *PolicyData {
+ return &PolicyData{
+ KeyDescriptor: hex.EncodeToString(policy.Master_key_identifier[:]),
+ Options: &EncryptionOptions{
+ Padding: flagsToPadding(policy.Flags),
+ Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
+ Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
+ PolicyVersion: 2,
+ },
+ }
+}
+
// GetPolicy returns the Policy data for the given directory or file (includes
// the KeyDescriptor and the encryption options). Returns an error if the
// path is not encrypted or the policy couldn't be retrieved.
func GetPolicy(path string) (*PolicyData, error) {
file, err := os.Open(path)
if err != nil {
+ if err.(*os.PathError).Err == syscall.ENOKEY {
+ return nil, &ErrLockedRegularFile{path}
+ }
return nil, err
}
defer file.Close()
- var policy unix.FscryptPolicy
- if err := policyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY, &policy); err != nil {
- return nil, errors.Wrapf(err, "get encryption policy %s", path)
+ // First try the new version of the ioctl. This works for both v1 and v2 policies.
+ var arg unix.FscryptGetPolicyExArg
+ arg.Size = uint64(unsafe.Sizeof(arg.Policy))
+ policyPtr := util.Ptr(arg.Policy[:])
+ err = getPolicyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY_EX, unsafe.Pointer(&arg))
+ if err == unix.ENOTTY {
+ // Fall back to the old version of the ioctl. This works for v1 policies only.
+ err = getPolicyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY, policyPtr)
+ arg.Size = uint64(unsafe.Sizeof(unix.FscryptPolicyV1{}))
}
+ switch err {
+ case nil:
+ break
+ case unix.ENOTTY:
+ return nil, ErrEncryptionNotSupported
+ case unix.EOPNOTSUPP:
+ return nil, ErrEncryptionNotEnabled
+ case unix.ENODATA, unix.ENOENT:
+ // ENOENT was returned instead of ENODATA on some filesystems before v4.11.
+ return nil, &ErrNotEncrypted{path}
+ default:
+ return nil, errors.Wrapf(err, "failed to get encryption policy of %q", path)
+ }
+ switch arg.Policy[0] { // arg.policy.version
+ case unix.FSCRYPT_POLICY_V1:
+ if arg.Size != uint64(unsafe.Sizeof(unix.FscryptPolicyV1{})) {
+ // should never happen
+ return nil, errors.New("unexpected size for v1 policy")
+ }
+ return buildV1PolicyData((*unix.FscryptPolicyV1)(policyPtr)), nil
+ case unix.FSCRYPT_POLICY_V2:
+ if arg.Size != uint64(unsafe.Sizeof(unix.FscryptPolicyV2{})) {
+ // should never happen
+ return nil, errors.New("unexpected size for v2 policy")
+ }
+ return buildV2PolicyData((*unix.FscryptPolicyV2)(policyPtr)), nil
+ default:
+ return nil, errors.Errorf("unsupported encryption policy version [%d]",
+ arg.Policy[0])
+ }
+}
- // Convert the padding flag into an amount of padding
- paddingFlag := int64(policy.Flags & unix.FS_POLICY_FLAGS_PAD_MASK)
+// For improved performance, use the DIRECT_KEY flag when using ciphers that
+// support it, e.g. Adiantum. It is safe because fscrypt won't reuse the key
+// for any other policy. (Multiple directories with same policy are okay.)
+func shouldUseDirectKeyFlag(options *EncryptionOptions) bool {
+ // Contents and filenames encryption modes must be the same
+ if options.Contents != options.Filenames {
+ return false
+ }
+ // Currently only Adiantum supports DIRECT_KEY.
+ return options.Contents == EncryptionOptions_Adiantum
+}
- // This lookup should always succeed
- padding, ok := util.Lookup(paddingFlag, flagsArray, paddingArray)
+func buildPolicyFlags(options *EncryptionOptions) uint8 {
+ // This lookup should always succeed (as policy is valid)
+ flags, ok := util.Lookup(options.Padding, paddingArray, flagsArray)
if !ok {
- log.Panicf("padding flag of %x not found", paddingFlag)
+ log.Panicf("padding of %d was not found", options.Padding)
+ }
+ if shouldUseDirectKeyFlag(options) {
+ flags |= unix.FSCRYPT_POLICY_FLAG_DIRECT_KEY
}
+ return uint8(flags)
+}
- return &PolicyData{
- KeyDescriptor: hex.EncodeToString(policy.Master_key_descriptor[:]),
- Options: &EncryptionOptions{
- Padding: padding,
- Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
- Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
- },
- }, nil
+func setV1Policy(file *os.File, options *EncryptionOptions, descriptorBytes []byte) error {
+ policy := unix.FscryptPolicyV1{
+ Version: unix.FSCRYPT_POLICY_V1,
+ Contents_encryption_mode: uint8(options.Contents),
+ Filenames_encryption_mode: uint8(options.Filenames),
+ Flags: uint8(buildPolicyFlags(options)),
+ }
+
+ // The descriptor should always be the correct length (as policy is valid)
+ if len(descriptorBytes) != unix.FSCRYPT_KEY_DESCRIPTOR_SIZE {
+ log.Panic("wrong descriptor size for v1 policy")
+ }
+ copy(policy.Master_key_descriptor[:], descriptorBytes)
+
+ return setPolicy(file, unsafe.Pointer(&policy))
+}
+
+func setV2Policy(file *os.File, options *EncryptionOptions, descriptorBytes []byte) error {
+ policy := unix.FscryptPolicyV2{
+ Version: unix.FSCRYPT_POLICY_V2,
+ Contents_encryption_mode: uint8(options.Contents),
+ Filenames_encryption_mode: uint8(options.Filenames),
+ Flags: uint8(buildPolicyFlags(options)),
+ }
+
+ // The descriptor should always be the correct length (as policy is valid)
+ if len(descriptorBytes) != unix.FSCRYPT_KEY_IDENTIFIER_SIZE {
+ log.Panic("wrong descriptor size for v2 policy")
+ }
+ copy(policy.Master_key_identifier[:], descriptorBytes)
+
+ return setPolicy(file, unsafe.Pointer(&policy))
}
// SetPolicy sets up the specified directory to be encrypted with the specified
@@ -119,30 +291,24 @@ func SetPolicy(path string, data *PolicyData) error {
}
defer file.Close()
- if err := data.CheckValidity(); err != nil {
+ if err = data.CheckValidity(); err != nil {
return errors.Wrap(err, "invalid policy")
}
- // This lookup should always succeed (as policy is valid)
- paddingFlag, ok := util.Lookup(data.Options.Padding, paddingArray, flagsArray)
- if !ok {
- log.Panicf("padding of %d was not found", data.Options.Padding)
- }
-
descriptorBytes, err := hex.DecodeString(data.KeyDescriptor)
if err != nil {
- return errors.New("invalid descriptor: " + data.KeyDescriptor)
+ return errors.New("invalid key descriptor: " + data.KeyDescriptor)
}
- policy := unix.FscryptPolicy{
- Version: 0, // Version must always be zero
- Contents_encryption_mode: uint8(data.Options.Contents),
- Filenames_encryption_mode: uint8(data.Options.Filenames),
- Flags: uint8(paddingFlag),
+ switch data.Options.PolicyVersion {
+ case 1:
+ err = setV1Policy(file, data.Options, descriptorBytes)
+ case 2:
+ err = setV2Policy(file, data.Options, descriptorBytes)
+ default:
+ err = errors.Errorf("policy version of %d is invalid", data.Options.PolicyVersion)
}
- copy(policy.Master_key_descriptor[:], descriptorBytes)
-
- if err = policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, &policy); err == unix.EINVAL {
+ if err == unix.EINVAL {
// Before kernel v4.11, many different errors all caused unix.EINVAL to be returned.
// We try to disambiguate this error here. This disambiguation will not always give
// the correct error due to a potential race condition on path.
@@ -151,14 +317,27 @@ func SetPolicy(path string, data *PolicyData) error {
err = unix.ENOTDIR
} else if _, policyErr := GetPolicy(path); policyErr == nil {
// Checking if a policy is already set on this directory
- err = ErrEncrypted
- } else {
- // Default to generic "bad options".
- err = ErrBadEncryptionOptions
+ err = unix.EEXIST
}
}
-
- return errors.Wrapf(err, "set encryption policy %s", path)
+ switch err {
+ case nil:
+ return nil
+ case unix.EACCES:
+ var stat unix.Stat_t
+ if statErr := unix.Stat(path, &stat); statErr == nil && stat.Uid != uint32(os.Geteuid()) {
+ return &ErrDirectoryNotOwned{path, stat.Uid}
+ }
+ case unix.EEXIST:
+ return &ErrAlreadyEncrypted{path}
+ case unix.EINVAL:
+ return &ErrBadEncryptionOptions{path, data.Options}
+ case unix.ENOTTY:
+ return ErrEncryptionNotSupported
+ case unix.EOPNOTSUPP:
+ return ErrEncryptionNotEnabled
+ }
+ return errors.Wrapf(err, "failed to set encryption policy on %q", path)
}
// CheckSupport returns an error if the filesystem containing path does not
@@ -172,20 +351,24 @@ func CheckSupport(path string) error {
defer file.Close()
// On supported directories, giving a bad policy will return EINVAL
- badPolicy := unix.FscryptPolicy{
+ badPolicy := unix.FscryptPolicyV1{
Version: math.MaxUint8,
Contents_encryption_mode: math.MaxUint8,
Filenames_encryption_mode: math.MaxUint8,
- Flags: math.MaxUint8,
+ Flags: math.MaxUint8,
}
- err = policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, &badPolicy)
+ err = setPolicy(file, unsafe.Pointer(&badPolicy))
switch err {
case nil:
log.Panicf(`FS_IOC_SET_ENCRYPTION_POLICY succeeded when it should have failed.
Please open an issue, filesystem %q may be corrupted.`, path)
case unix.EINVAL, unix.EACCES:
return nil
+ case unix.ENOTTY:
+ return ErrEncryptionNotSupported
+ case unix.EOPNOTSUPP:
+ return ErrEncryptionNotEnabled
}
- return err
+ return errors.Wrapf(err, "unexpected error checking for encryption support on filesystem %q", path)
}
diff --git a/metadata/policy_test.go b/metadata/policy_test.go
index 783a784..7856ed3 100644
--- a/metadata/policy_test.go
+++ b/metadata/policy_test.go
@@ -23,19 +23,33 @@ import (
"fmt"
"os"
"path/filepath"
- "reflect"
"testing"
+ "golang.org/x/sys/unix"
+ "google.golang.org/protobuf/proto"
+
"github.com/google/fscrypt/util"
)
-const goodDescriptor = "0123456789abcdef"
+const goodV1Descriptor = "0123456789abcdef"
-var goodPolicy = &PolicyData{
- KeyDescriptor: goodDescriptor,
+var goodV1Policy = &PolicyData{
+ KeyDescriptor: goodV1Descriptor,
Options: DefaultOptions,
}
+var goodV2EncryptionOptions = &EncryptionOptions{
+ Padding: 32,
+ Contents: EncryptionOptions_AES_256_XTS,
+ Filenames: EncryptionOptions_AES_256_CTS,
+ PolicyVersion: 2,
+}
+
+var goodV2Policy = &PolicyData{
+ KeyDescriptor: "0123456789abcdef0123456789abcdef",
+ Options: goodV2EncryptionOptions,
+}
+
// Creates a temporary directory for testing.
func createTestDirectory(t *testing.T) (directory string, err error) {
baseDirectory, err := util.TestRoot()
@@ -82,7 +96,7 @@ func TestSetPolicyEmptyDirectory(t *testing.T) {
}
defer os.RemoveAll(directory)
- if err = SetPolicy(directory, goodPolicy); err != nil {
+ if err = SetPolicy(directory, goodV1Policy); err != nil {
t.Error(err)
}
}
@@ -95,7 +109,7 @@ func TestSetPolicyNonemptyDirectory(t *testing.T) {
}
defer os.RemoveAll(directory)
- if err = SetPolicy(directory, goodPolicy); err == nil {
+ if err = SetPolicy(directory, goodV1Policy); err == nil {
t.Error("should have failed to set policy on a nonempty directory")
}
}
@@ -108,7 +122,7 @@ func TestSetPolicyFile(t *testing.T) {
}
defer os.RemoveAll(directory)
- if err = SetPolicy(file, goodPolicy); err == nil {
+ if err = SetPolicy(file, goodV1Policy); err == nil {
t.Error("should have failed to set policy on a file")
}
}
@@ -140,15 +154,15 @@ func TestGetPolicyEmptyDirectory(t *testing.T) {
defer os.RemoveAll(directory)
var actualPolicy *PolicyData
- if err = SetPolicy(directory, goodPolicy); err != nil {
+ if err = SetPolicy(directory, goodV1Policy); err != nil {
t.Fatal(err)
}
if actualPolicy, err = GetPolicy(directory); err != nil {
t.Fatal(err)
}
- if !reflect.DeepEqual(actualPolicy, goodPolicy) {
- t.Errorf("policy %+v does not equal expected policy %+v", actualPolicy, goodPolicy)
+ if !proto.Equal(actualPolicy, goodV1Policy) {
+ t.Errorf("policy %+v does not equal expected policy %+v", actualPolicy, goodV1Policy)
}
}
@@ -164,3 +178,35 @@ func TestGetPolicyUnencrypted(t *testing.T) {
t.Error("should have failed to set policy on a file")
}
}
+
+func requireV2PolicySupport(t *testing.T, directory string) {
+ file, err := os.Open(directory)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer file.Close()
+
+ err = getPolicyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY_EX, nil)
+ if err == ErrEncryptionNotSupported {
+ t.Skip("No support for v2 encryption policies, skipping test")
+ }
+}
+
+// Tests that a non-root user cannot set a v2 encryption policy unless the key
+// has been added.
+func TestSetV2PolicyNoKey(t *testing.T) {
+ if util.IsUserRoot() {
+ t.Skip("This test cannot be run as root")
+ }
+ directory, err := createTestDirectory(t)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(directory)
+ requireV2PolicySupport(t, directory)
+
+ err = SetPolicy(directory, goodV2Policy)
+ if err == nil {
+ t.Error("shouldn't have been able to set v2 policy without key added")
+ }
+}