github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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  }