1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2021 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package fde
import (
"bytes"
"encoding/json"
"fmt"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/randutil"
)
// RevealKeyRequest carries the operation parameters to the fde-reavel-key
// helper that receives them serialized over stdin.
type RevealKeyRequest struct {
Op string `json:"op"`
SealedKey []byte `json:"sealed-key,omitempty"`
Handle *json.RawMessage `json:"handle,omitempty"`
// deprecated for v1
KeyName string `json:"key-name,omitempty"`
// TODO: add VolumeName,SourceDevicePath later
}
// runFDERevealKeyCommand returns the output of fde-reveal-key run
// with systemd.
//
// Note that systemd-run in the initrd can only talk to the private
// systemd bus so this cannot use "--pipe" or "--wait", see
// https://github.com/snapcore/core-initrd/issues/13
func runFDERevealKeyCommand(req *RevealKeyRequest) (output []byte, err error) {
stdin, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf(`cannot build request %v for fde-reveal-key: %v`, req, err)
}
return runFDEinitramfsHelper("fde-reveal-key", stdin)
}
var runFDERevealKey = runFDERevealKeyCommand
func MockRunFDERevealKey(mock func(*RevealKeyRequest) ([]byte, error)) (restore func()) {
osutil.MustBeTestBinary("fde-reveal-key can only be mocked in tests")
oldRunFDERevealKey := runFDERevealKey
runFDERevealKey = mock
return func() {
runFDERevealKey = oldRunFDERevealKey
}
}
func LockSealedKeys() error {
req := &RevealKeyRequest{
Op: "lock",
}
if _, err := runFDERevealKey(req); err != nil {
return err
}
return nil
}
// RevealParams contains the parameters for fde-reveal-key reveal operation.
type RevealParams struct {
SealedKey []byte
Handle *json.RawMessage
// V2Payload is set true if SealedKey is expected to contain a v2 payload
// (disk key + aux key)
V2Payload bool
}
type revealKeyResult struct {
Key []byte `json:"key"`
}
const (
v1keySize = 64
v1NoHandle = `{"v1-no-handle":true}`
)
// Reveal invokes the fde-reveal-key reveal operation.
func Reveal(params *RevealParams) (payload []byte, err error) {
handle := params.Handle
if params.V2Payload && handle != nil && bytes.Equal([]byte(*handle), []byte(v1NoHandle)) {
handle = nil
}
req := &RevealKeyRequest{
Op: "reveal",
SealedKey: params.SealedKey,
Handle: handle,
// deprecated but needed for v1 hooks
KeyName: "deprecated-" + randutil.RandomString(12),
}
output, err := runFDERevealKey(req)
if err != nil {
return nil, err
}
// We expect json output that fits the revealKeyResult json at
// this point. However the "denver" project uses the old and
// deprecated v1 API that returns raw bytes and we still need
// to support this.
var res revealKeyResult
if err := json.Unmarshal(output, &res); err != nil {
if params.V2Payload {
// We expect a v2 payload but not having json
// output from the hook means that either the
// hook is buggy or we have a v1 based hook
// (e.g. "denver" project) with v2 based json
// data on disk. This is supported but we let
// the higher levels unmarshaling of the
// payload deal with the buggy case.
return output, nil
}
// If the payload is not expected to be v2 and, the
// output is not json but matches the size of the
// "denver" project encrypton key (64 bytes) we assume
// we deal with a v1 API.
if len(output) != v1keySize {
return nil, fmt.Errorf(`cannot decode fde-reveal-key "reveal" result: %v`, err)
}
return output, nil
}
return res.Key, nil
}
|