github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/kernel/fde/fde.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 // package fde implements helper used by low level parts like secboot 21 // in snap-bootstrap and high level parts like DeviceManager in snapd. 22 // 23 // Note that it must never import anything overlord related itself 24 // to avoid increasing the size of snap-bootstrap. 25 package fde 26 27 import ( 28 "bytes" 29 "encoding/json" 30 "fmt" 31 "os/exec" 32 ) 33 34 // HasRevealKey return true if the current system has a "fde-reveal-key" 35 // binary (usually used in the initrd). 36 // 37 // This will be setup by devicestate to support device-specific full 38 // disk encryption implementations. 39 func HasRevealKey() bool { 40 // XXX: should we record during initial sealing that the fde-setup 41 // was used and only use fde-reveal-key in that case? 42 _, err := exec.LookPath("fde-reveal-key") 43 return err == nil 44 } 45 46 func isV1Hook(hookOutput []byte) bool { 47 // This is the prefix of a tpm secboot v1 key as used in the 48 // "denver" project. So if we see this prefix we know it's 49 // v1 hook output. 50 return bytes.HasPrefix(hookOutput, []byte("USK$")) 51 } 52 53 func unmarshalInitialSetupResult(hookOutput []byte) (*InitialSetupResult, error) { 54 // We expect json output that fits InitalSetupResult 55 // hook at this point. However the "denver" project 56 // uses the old and deprecated v1 API that returns raw 57 // bytes and we still need to support this. 58 var res InitialSetupResult 59 if err := json.Unmarshal(hookOutput, &res); err != nil { 60 // If the outout is not json and looks like va 61 if !isV1Hook(hookOutput) { 62 return nil, fmt.Errorf("cannot decode hook output %q: %v", hookOutput, err) 63 } 64 // v1 hooks do not support a handle 65 handle := json.RawMessage(v1NoHandle) 66 res.Handle = &handle 67 res.EncryptedKey = hookOutput 68 } 69 70 return &res, nil 71 } 72 73 // SetupRequest carries the operation and parameters for the fde-setup hooks 74 // made available to them via the snapctl fde-setup-request command. 75 type SetupRequest struct { 76 // XXX: make "op" a type: "features", "initial-setup", "update" ? 77 Op string `json:"op"` 78 79 // This needs to be a []byte so that Go's standard library will base64 80 // encode it automatically for us 81 Key []byte `json:"key,omitempty"` 82 KeyName string `json:"key-name,omitempty"` 83 } 84 85 // A RunSetupHookFunc implements running the fde-setup kernel hook. 86 type RunSetupHookFunc func(req *SetupRequest) ([]byte, error) 87 88 // InitialSetupParams contains the inputs for the fde-setup hook 89 type InitialSetupParams struct { 90 Key []byte 91 KeyName string 92 } 93 94 // InitalSetupResult contains the outputs of the fde-setup hook 95 type InitialSetupResult struct { 96 // result when called with "initial-setup" 97 // XXX call this encrypted-key if possible? 98 EncryptedKey []byte `json:"sealed-key"` 99 Handle *json.RawMessage `json:"handle"` 100 } 101 102 // InitialSetup invokes the initial-setup op running the kernel hook via runSetupHook. 103 func InitialSetup(runSetupHook RunSetupHookFunc, params *InitialSetupParams) (*InitialSetupResult, error) { 104 req := &SetupRequest{ 105 Op: "initial-setup", 106 Key: params.Key, 107 KeyName: params.KeyName, 108 } 109 hookOutput, err := runSetupHook(req) 110 if err != nil { 111 return nil, err 112 } 113 res, err := unmarshalInitialSetupResult(hookOutput) 114 if err != nil { 115 return nil, err 116 } 117 return res, nil 118 }