mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-11-08 23:06:09 -05:00
cryptsetup: enable detached header (#3927)
* deps: update go-cryptsetup * cryptsetup: use detached headers when opening existing crypt devices * cryptsetup: only activate disks with detached header --------- Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
23fa3bb36e
commit
bb8d2c8a5c
10 changed files with 685 additions and 103 deletions
|
|
@ -14,6 +14,7 @@ go_test(
|
||||||
"@e2fsprogs//:bin/mkfs.ext4",
|
"@e2fsprogs//:bin/mkfs.ext4",
|
||||||
"@util-linux//:bin/blkid",
|
"@util-linux//:bin/blkid",
|
||||||
"@util-linux//:bin/fsck",
|
"@util-linux//:bin/fsck",
|
||||||
|
"@util-linux//:bin/losetup",
|
||||||
"@util-linux//:bin/mount",
|
"@util-linux//:bin/mount",
|
||||||
"@util-linux//:bin/umount",
|
"@util-linux//:bin/umount",
|
||||||
],
|
],
|
||||||
|
|
@ -23,6 +24,7 @@ go_test(
|
||||||
"DD": "$(rlocationpath @coreutils//:bin/dd)",
|
"DD": "$(rlocationpath @coreutils//:bin/dd)",
|
||||||
"FSCK": "$(rlocationpath @util-linux//:bin/fsck)",
|
"FSCK": "$(rlocationpath @util-linux//:bin/fsck)",
|
||||||
"FSCK_EXT4": "$(rlocationpath @e2fsprogs//:bin/fsck.ext4)",
|
"FSCK_EXT4": "$(rlocationpath @e2fsprogs//:bin/fsck.ext4)",
|
||||||
|
"LOSETUP": "$(rlocationpath @util-linux//:bin/losetup)",
|
||||||
"MKFS_EXT4": "$(rlocationpath @e2fsprogs//:bin/mkfs.ext4)",
|
"MKFS_EXT4": "$(rlocationpath @e2fsprogs//:bin/mkfs.ext4)",
|
||||||
"MOUNT": "$(rlocationpath @util-linux//:bin/mount)",
|
"MOUNT": "$(rlocationpath @util-linux//:bin/mount)",
|
||||||
"RM": "$(rlocationpath @coreutils//:bin/rm)",
|
"RM": "$(rlocationpath @coreutils//:bin/rm)",
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
devicePath string = "testDevice"
|
defaultBackingImage = "testDevice"
|
||||||
deviceName string = "testDeviceName"
|
deviceName = "testDeviceName"
|
||||||
)
|
)
|
||||||
|
|
||||||
var toolsEnvs = []string{"CP", "DD", "RM", "FSCK_EXT4", "MKFS_EXT4", "BLKID", "FSCK", "MOUNT", "UMOUNT"}
|
var toolsEnvs = []string{"CP", "DD", "RM", "FSCK_EXT4", "MKFS_EXT4", "BLKID", "FSCK", "MOUNT", "UMOUNT", "LOSETUP"}
|
||||||
|
|
||||||
// addToolsToPATH is used to update the PATH to contain necessary tool binaries for
|
// addToolsToPATH is used to update the PATH to contain necessary tool binaries for
|
||||||
// coreutils, util-linux and ext4.
|
// coreutils, util-linux and ext4.
|
||||||
|
|
@ -57,20 +57,35 @@ func addToolsToPATH() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(devicePath string) {
|
func setup(backingDisk string) string {
|
||||||
if err := exec.Command("dd", "if=/dev/zero", fmt.Sprintf("of=%s", devicePath), "bs=64M", "count=1").Run(); err != nil {
|
if err := exec.Command("dd", "if=/dev/zero", fmt.Sprintf("of=%s", backingDisk), "bs=64M", "count=1").Run(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
out, err := exec.Command("losetup", "-f", "--show", backingDisk).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func teardown(backingImage, devicePath string) {
|
||||||
|
if err := exec.Command("losetup", "-d", devicePath).Run(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := exec.Command("rm", "-f", backingImage).Run(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func teardown(devicePath string) {
|
func cp(source, target string) (string, error) {
|
||||||
if err := exec.Command("rm", "-f", devicePath).Run(); err != nil {
|
if err := exec.Command("cp", source, target).Run(); err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
out, err := exec.Command("losetup", "-f", "--show", target).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
func cp(source, target string) error {
|
return "", err
|
||||||
return exec.Command("cp", source, target).Run()
|
}
|
||||||
|
return strings.TrimSpace(string(out)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resize(devicePath string) {
|
func resize(devicePath string) {
|
||||||
|
|
@ -100,8 +115,8 @@ func TestMain(m *testing.M) {
|
||||||
func TestOpenAndClose(t *testing.T) {
|
func TestOpenAndClose(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
setup(devicePath)
|
devicePath := setup(defaultBackingImage)
|
||||||
defer teardown(devicePath)
|
defer teardown(defaultBackingImage, devicePath)
|
||||||
|
|
||||||
mapper := cryptmapper.New(&fakeKMS{})
|
mapper := cryptmapper.New(&fakeKMS{})
|
||||||
|
|
||||||
|
|
@ -124,7 +139,7 @@ func TestOpenAndClose(t *testing.T) {
|
||||||
assert.Equal(newPath, newPath2)
|
assert.Equal(newPath, newPath2)
|
||||||
|
|
||||||
// Resize the device
|
// Resize the device
|
||||||
resize(devicePath)
|
resize(defaultBackingImage)
|
||||||
|
|
||||||
resizedPath, err := mapper.ResizeCryptDevice(t.Context(), deviceName)
|
resizedPath, err := mapper.ResizeCryptDevice(t.Context(), deviceName)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
@ -145,8 +160,8 @@ func TestOpenAndClose(t *testing.T) {
|
||||||
func TestOpenAndCloseIntegrity(t *testing.T) {
|
func TestOpenAndCloseIntegrity(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
setup(devicePath)
|
devicePath := setup(defaultBackingImage)
|
||||||
defer teardown(devicePath)
|
defer teardown(defaultBackingImage, devicePath)
|
||||||
|
|
||||||
mapper := cryptmapper.New(&fakeKMS{})
|
mapper := cryptmapper.New(&fakeKMS{})
|
||||||
|
|
||||||
|
|
@ -167,7 +182,7 @@ func TestOpenAndCloseIntegrity(t *testing.T) {
|
||||||
assert.Equal(newPath, newPath2)
|
assert.Equal(newPath, newPath2)
|
||||||
|
|
||||||
// integrity devices do not support resizing
|
// integrity devices do not support resizing
|
||||||
resize(devicePath)
|
resize(defaultBackingImage)
|
||||||
_, err = mapper.ResizeCryptDevice(t.Context(), deviceName)
|
_, err = mapper.ResizeCryptDevice(t.Context(), deviceName)
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
|
||||||
|
|
@ -182,25 +197,26 @@ func TestOpenAndCloseIntegrity(t *testing.T) {
|
||||||
|
|
||||||
// check if we can reopen the device
|
// check if we can reopen the device
|
||||||
_, err = mapper.OpenCryptDevice(t.Context(), devicePath, deviceName, true)
|
_, err = mapper.OpenCryptDevice(t.Context(), devicePath, deviceName, true)
|
||||||
assert.NoError(err)
|
assert.NoError(err, "Failed to re-open crypt device")
|
||||||
assert.NoError(mapper.CloseCryptDevice(deviceName))
|
assert.NoError(mapper.CloseCryptDevice(deviceName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeviceCloning(t *testing.T) {
|
func TestDeviceCloning(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
setup(devicePath)
|
devicePath := setup(defaultBackingImage)
|
||||||
defer teardown(devicePath)
|
defer teardown(defaultBackingImage, devicePath)
|
||||||
|
|
||||||
mapper := cryptmapper.New(&dynamicKMS{})
|
mapper := cryptmapper.New(&dynamicKMS{})
|
||||||
|
|
||||||
_, err := mapper.OpenCryptDevice(t.Context(), devicePath, deviceName, false)
|
_, err := mapper.OpenCryptDevice(t.Context(), devicePath, deviceName, false)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
require.NoError(cp(devicePath, devicePath+"-copy"))
|
cpDevice, err := cp(defaultBackingImage, defaultBackingImage+"-copy")
|
||||||
defer teardown(devicePath + "-copy")
|
require.NoError(err)
|
||||||
|
defer teardown(defaultBackingImage+"-copy", cpDevice)
|
||||||
|
|
||||||
_, err = mapper.OpenCryptDevice(t.Context(), devicePath+"-copy", deviceName+"-copy", false)
|
_, err = mapper.OpenCryptDevice(t.Context(), cpDevice, deviceName+"-copy", false)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
assert.NoError(mapper.CloseCryptDevice(deviceName))
|
assert.NoError(mapper.CloseCryptDevice(deviceName))
|
||||||
|
|
@ -209,12 +225,12 @@ func TestDeviceCloning(t *testing.T) {
|
||||||
|
|
||||||
func TestConcurrency(t *testing.T) {
|
func TestConcurrency(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
setup(devicePath)
|
devicePath := setup(defaultBackingImage)
|
||||||
defer teardown(devicePath)
|
defer teardown(defaultBackingImage, devicePath)
|
||||||
|
|
||||||
device2 := devicePath + "-2"
|
backingImage2 := defaultBackingImage + "-2"
|
||||||
setup(device2)
|
device2 := setup(backingImage2)
|
||||||
defer teardown(device2)
|
defer teardown(backingImage2, device2)
|
||||||
|
|
||||||
mapper := cryptmapper.New(&fakeKMS{})
|
mapper := cryptmapper.New(&fakeKMS{})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ go_test(
|
||||||
data = [
|
data = [
|
||||||
"@coreutils//:bin/dd",
|
"@coreutils//:bin/dd",
|
||||||
"@coreutils//:bin/rm",
|
"@coreutils//:bin/rm",
|
||||||
|
"@util-linux//:bin/losetup",
|
||||||
],
|
],
|
||||||
env = {
|
env = {
|
||||||
"DD": "$(rlocationpath @coreutils//:bin/dd)",
|
"DD": "$(rlocationpath @coreutils//:bin/dd)",
|
||||||
|
"LOSETUP": "$(rlocationpath @util-linux//:bin/losetup)",
|
||||||
"RM": "$(rlocationpath @coreutils//:bin/rm)",
|
"RM": "$(rlocationpath @coreutils//:bin/rm)",
|
||||||
},
|
},
|
||||||
# keep
|
# keep
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
@ -31,13 +32,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
devicePath = "testDevice"
|
backingDisk = "testDevice"
|
||||||
mappedDevice = "mappedDevice"
|
mappedDevice = "mappedDevice"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var devicePath string
|
||||||
|
|
||||||
var diskPath = flag.String("disk", "", "Path to the disk to use for the benchmark")
|
var diskPath = flag.String("disk", "", "Path to the disk to use for the benchmark")
|
||||||
|
|
||||||
var toolsEnvs = []string{"DD", "RM"}
|
var toolsEnvs = []string{"DD", "RM", "LOSETUP"}
|
||||||
|
|
||||||
// addToolsToPATH is used to update the PATH to contain necessary tool binaries for
|
// addToolsToPATH is used to update the PATH to contain necessary tool binaries for
|
||||||
// coreutils.
|
// coreutils.
|
||||||
|
|
@ -64,11 +67,22 @@ func addToolsToPATH() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(sizeGB int) error {
|
func setup(sizeGB int) error {
|
||||||
return exec.Command("dd", "if=/dev/random", fmt.Sprintf("of=%s", devicePath), "bs=1G", fmt.Sprintf("count=%d", sizeGB)).Run()
|
if err := exec.Command("dd", "if=/dev/random", fmt.Sprintf("of=%s", backingDisk), "bs=1G", fmt.Sprintf("count=%d", sizeGB)).Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command("losetup", "-f", "--show", backingDisk)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("losetup failed: %w\nOutput: %s", err, out)
|
||||||
|
}
|
||||||
|
devicePath = strings.TrimSpace(string(out))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func teardown() error {
|
func teardown() error {
|
||||||
return exec.Command("rm", "-f", devicePath).Run()
|
err := exec.Command("losetup", "-d", devicePath).Run()
|
||||||
|
errors.Join(err, exec.Command("rm", "-f", backingDisk).Run())
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
|
@ -137,6 +151,27 @@ func TestMapper(t *testing.T) {
|
||||||
// Disk should still be marked as not initialized because token is set to false.
|
// Disk should still be marked as not initialized because token is set to false.
|
||||||
assert.False(mapper.IsInitialized())
|
assert.False(mapper.IsInitialized())
|
||||||
|
|
||||||
|
// Set disk as initialized
|
||||||
|
assert.NoError(ccrypt.SetConstellationStateDiskToken(ccryptsetup.SetDiskInitialized))
|
||||||
|
|
||||||
|
// Set up a new client and check if the client still sees the disk as initialized
|
||||||
|
ccrypt2 := ccryptsetup.New()
|
||||||
|
freeDevice2, err := ccrypt2.Init(devicePath)
|
||||||
|
require.NoError(err, "failed to initialize crypt device")
|
||||||
|
defer freeDevice2()
|
||||||
|
require.NoError(ccrypt2.LoadLUKS2(), "failed to load LUKS2")
|
||||||
|
|
||||||
|
tokenJSON, err = ccrypt2.TokenJSONGet(ccryptsetup.ConstellationStateDiskTokenID)
|
||||||
|
require.NoError(err, "token should have been set")
|
||||||
|
var token2 struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Keyslots []string `json:"keyslots"`
|
||||||
|
DiskIsInitialized bool `json:"diskIsInitialized"`
|
||||||
|
}
|
||||||
|
require.NoError(json.Unmarshal([]byte(tokenJSON), &token2))
|
||||||
|
assert.True(token2.DiskIsInitialized, "disk should be marked as initialized")
|
||||||
|
assert.True(ccrypt2.ConstellationStateDiskTokenIsInitialized(), "disk should be marked as initialized")
|
||||||
|
|
||||||
// Try to map disk with incorrect passphrase
|
// Try to map disk with incorrect passphrase
|
||||||
assert.Error(mapper.MapDisk(mappedDevice, "invalid-passphrase"), "was able to map disk with incorrect passphrase")
|
assert.Error(mapper.MapDisk(mappedDevice, "invalid-passphrase"), "was able to map disk with incorrect passphrase")
|
||||||
|
|
||||||
|
|
|
||||||
3
go.mod
3
go.mod
|
|
@ -2,8 +2,7 @@ module github.com/edgelesssys/constellation/v2
|
||||||
|
|
||||||
go 1.24.6
|
go 1.24.6
|
||||||
|
|
||||||
// TODO(daniel-weisse): revert after merging https://github.com/martinjungblut/go-cryptsetup/pull/16.
|
replace github.com/martinjungblut/go-cryptsetup => github.com/edgelesssys/go-cryptsetup v0.0.0-20250822075033-840d240dddf8
|
||||||
replace github.com/martinjungblut/go-cryptsetup => github.com/daniel-weisse/go-cryptsetup v0.0.0-20230705150314-d8c07bd1723c
|
|
||||||
|
|
||||||
// TODO(daniel-weisse): revert after merging https://github.com/google/go-sev-guest/pull/173.
|
// TODO(daniel-weisse): revert after merging https://github.com/google/go-sev-guest/pull/173.
|
||||||
replace github.com/google/go-sev-guest => github.com/daniel-weisse/go-sev-guest v0.0.0-20250728114912-0c2ba277c52b
|
replace github.com/google/go-sev-guest => github.com/daniel-weisse/go-sev-guest v0.0.0-20250728114912-0c2ba277c52b
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -237,8 +237,6 @@ github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 h
|
||||||
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||||
github.com/daniel-weisse/go-cryptsetup v0.0.0-20230705150314-d8c07bd1723c h1:ToajP6trZoiqlZ3Z4uoG1P02/wtqSw1AcowOXOYjATk=
|
|
||||||
github.com/daniel-weisse/go-cryptsetup v0.0.0-20230705150314-d8c07bd1723c/go.mod h1:gZoZ0+POlM1ge/VUxWpMmZVNPzzMJ7l436CgkQ5+qzU=
|
|
||||||
github.com/daniel-weisse/go-sev-guest v0.0.0-20250728114912-0c2ba277c52b h1:pElX9BS0PnYZS/tznradDYbo82kvG2yisWGvZGsDnVs=
|
github.com/daniel-weisse/go-sev-guest v0.0.0-20250728114912-0c2ba277c52b h1:pElX9BS0PnYZS/tznradDYbo82kvG2yisWGvZGsDnVs=
|
||||||
github.com/daniel-weisse/go-sev-guest v0.0.0-20250728114912-0c2ba277c52b/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760=
|
github.com/daniel-weisse/go-sev-guest v0.0.0-20250728114912-0c2ba277c52b/go.mod h1:SK9vW+uyfuzYdVN0m8BShL3OQCtXZe/JPF7ZkpD3760=
|
||||||
github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
|
github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
|
||||||
|
|
@ -271,6 +269,8 @@ github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU
|
||||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20250408071817-8c4457b235ff h1:V6A5kD0+c1Qg4X72Lg+zxhCZk+par436sQdgLvMCBBc=
|
github.com/edgelesssys/go-azguestattestation v0.0.0-20250408071817-8c4457b235ff h1:V6A5kD0+c1Qg4X72Lg+zxhCZk+par436sQdgLvMCBBc=
|
||||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20250408071817-8c4457b235ff/go.mod h1:Lz4QaomI4wU2YbatD4/W7vatW2Q35tnkoJezB1clscc=
|
github.com/edgelesssys/go-azguestattestation v0.0.0-20250408071817-8c4457b235ff/go.mod h1:Lz4QaomI4wU2YbatD4/W7vatW2Q35tnkoJezB1clscc=
|
||||||
|
github.com/edgelesssys/go-cryptsetup v0.0.0-20250822075033-840d240dddf8 h1:aZsuG/e0UNZSttE63TplTeTpYjpl8A2GXbNQYMRUktw=
|
||||||
|
github.com/edgelesssys/go-cryptsetup v0.0.0-20250822075033-840d240dddf8/go.mod h1:gZoZ0+POlM1ge/VUxWpMmZVNPzzMJ7l436CgkQ5+qzU=
|
||||||
github.com/edgelesssys/go-tdx-qpl v0.0.0-20250129202750-607ac61e2377 h1:5JMJiBhvOUUR7EZ0UyeSy7a1WrqB2eM+DX3odLSHAh4=
|
github.com/edgelesssys/go-tdx-qpl v0.0.0-20250129202750-607ac61e2377 h1:5JMJiBhvOUUR7EZ0UyeSy7a1WrqB2eM+DX3odLSHAh4=
|
||||||
github.com/edgelesssys/go-tdx-qpl v0.0.0-20250129202750-607ac61e2377/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw=
|
github.com/edgelesssys/go-tdx-qpl v0.0.0-20250129202750-607ac61e2377/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,14 @@ go_library(
|
||||||
visibility = ["//:__subpackages__"],
|
visibility = ["//:__subpackages__"],
|
||||||
deps = select({
|
deps = select({
|
||||||
"@io_bazel_rules_go//go/platform:android": [
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
|
"@com_github_google_uuid//:uuid",
|
||||||
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
||||||
|
"@org_golang_x_sys//unix",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:linux": [
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
|
"@com_github_google_uuid//:uuid",
|
||||||
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
"@com_github_martinjungblut_go_cryptsetup//:go-cryptsetup",
|
||||||
|
"@org_golang_x_sys//unix",
|
||||||
],
|
],
|
||||||
"//conditions:default": [],
|
"//conditions:default": [],
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -50,9 +51,18 @@ var (
|
||||||
|
|
||||||
// CryptSetup manages encrypted devices.
|
// CryptSetup manages encrypted devices.
|
||||||
type CryptSetup struct {
|
type CryptSetup struct {
|
||||||
nameInit func(name string) (cryptDevice, error)
|
nameInit func(name string) (deviceDetachedHeader, deviceAttachedHeader cryptDevice, headerDevice string, headerFile string, err error)
|
||||||
pathInit func(path string) (cryptDevice, error)
|
pathInit func(path string) (deviceDetachedHeader, deviceAttachedHeader cryptDevice, headerDevice string, headerFile string, err error)
|
||||||
device cryptDevice
|
// deviceWithDetachedHeader is the cryptsetup device with detached header we are working on.
|
||||||
|
deviceWithDetachedHeader cryptDevice
|
||||||
|
// deviceWithAttachedHeader is a cryptsetup device loaded without a separate, detached header.
|
||||||
|
// If this is not a fresh disk, this device is purely used to write back changes that affect the header to the original disk.
|
||||||
|
deviceWithAttachedHeader cryptDevice
|
||||||
|
// headerDevice is the name of the loopback device containing the detached cryptsetup header.
|
||||||
|
headerDevice string
|
||||||
|
// headerFile is the path to the file containing the detached cryptsetup header.
|
||||||
|
// The file is mounted as a loopback device on "headerDevice".
|
||||||
|
headerFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new CryptSetup.
|
// New creates a new CryptSetup.
|
||||||
|
|
@ -68,14 +78,17 @@ func New() *CryptSetup {
|
||||||
func (c *CryptSetup) Init(devicePath string) (free func(), err error) {
|
func (c *CryptSetup) Init(devicePath string) (free func(), err error) {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device != nil {
|
if c.hasDetachedHeaderDevice() || c.hasAttachedHeaderDevice() {
|
||||||
return nil, errDeviceAlreadyOpen
|
return nil, errDeviceAlreadyOpen
|
||||||
}
|
}
|
||||||
device, err := c.pathInit(devicePath)
|
deviceDetachedHeader, deviceAttachedHeader, headerDevice, headerFile, err := c.pathInit(devicePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init cryptsetup by device path %q: %w", devicePath, err)
|
return nil, fmt.Errorf("init cryptsetup by device path %q: %w", devicePath, err)
|
||||||
}
|
}
|
||||||
c.device = device
|
c.deviceWithDetachedHeader = deviceDetachedHeader
|
||||||
|
c.deviceWithAttachedHeader = deviceAttachedHeader
|
||||||
|
c.headerDevice = headerDevice
|
||||||
|
c.headerFile = headerFile
|
||||||
return c.Free, nil
|
return c.Free, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,14 +96,17 @@ func (c *CryptSetup) Init(devicePath string) (free func(), err error) {
|
||||||
func (c *CryptSetup) InitByName(name string) (free func(), err error) {
|
func (c *CryptSetup) InitByName(name string) (free func(), err error) {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device != nil {
|
if c.hasDetachedHeaderDevice() || c.hasAttachedHeaderDevice() {
|
||||||
return nil, errDeviceAlreadyOpen
|
return nil, errDeviceAlreadyOpen
|
||||||
}
|
}
|
||||||
device, err := c.nameInit(name)
|
deviceDetachedHeader, deviceAttachedHeader, headerDevice, headerFile, err := c.nameInit(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init cryptsetup by name %q: %w", name, err)
|
return nil, fmt.Errorf("init cryptsetup by name %q: %w", name, err)
|
||||||
}
|
}
|
||||||
c.device = device
|
c.deviceWithDetachedHeader = deviceDetachedHeader
|
||||||
|
c.deviceWithAttachedHeader = deviceAttachedHeader
|
||||||
|
c.headerDevice = headerDevice
|
||||||
|
c.headerFile = headerFile
|
||||||
return c.Free, nil
|
return c.Free, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,20 +114,19 @@ func (c *CryptSetup) InitByName(name string) (free func(), err error) {
|
||||||
func (c *CryptSetup) Free() {
|
func (c *CryptSetup) Free() {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device != nil {
|
c.free()
|
||||||
c.device.Free()
|
|
||||||
c.device = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActivateByPassphrase actives a crypt device using a passphrase.
|
// ActivateByPassphrase actives a crypt device using a passphrase.
|
||||||
func (c *CryptSetup) ActivateByPassphrase(deviceName string, keyslot int, passphrase string, flags int) error {
|
func (c *CryptSetup) ActivateByPassphrase(deviceName string, keyslot int, passphrase string, flags int) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
if !c.hasDetachedHeaderDevice() {
|
||||||
return errDeviceNotOpen
|
if err := c.reload(); err != nil {
|
||||||
|
return fmt.Errorf("re-loading crypt device for activation: %w", err)
|
||||||
}
|
}
|
||||||
if err := c.device.ActivateByPassphrase(deviceName, keyslot, passphrase, flags); err != nil {
|
}
|
||||||
|
if err := c.deviceWithDetachedHeader.ActivateByPassphrase(deviceName, keyslot, passphrase, flags); err != nil {
|
||||||
return fmt.Errorf("activating crypt device %q using passphrase: %w", deviceName, err)
|
return fmt.Errorf("activating crypt device %q using passphrase: %w", deviceName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -122,10 +137,12 @@ func (c *CryptSetup) ActivateByPassphrase(deviceName string, keyslot int, passph
|
||||||
func (c *CryptSetup) ActivateByVolumeKey(deviceName, volumeKey string, volumeKeySize, flags int) error {
|
func (c *CryptSetup) ActivateByVolumeKey(deviceName, volumeKey string, volumeKeySize, flags int) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
if !c.hasDetachedHeaderDevice() {
|
||||||
return errDeviceNotOpen
|
if err := c.reload(); err != nil {
|
||||||
|
return fmt.Errorf("re-loading crypt device for activation: %w", err)
|
||||||
}
|
}
|
||||||
if err := c.device.ActivateByVolumeKey(deviceName, volumeKey, volumeKeySize, flags); err != nil {
|
}
|
||||||
|
if err := c.deviceWithDetachedHeader.ActivateByVolumeKey(deviceName, volumeKey, volumeKeySize, flags); err != nil {
|
||||||
return fmt.Errorf("activating crypt device %q using volume key: %w", deviceName, err)
|
return fmt.Errorf("activating crypt device %q using volume key: %w", deviceName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -135,10 +152,10 @@ func (c *CryptSetup) ActivateByVolumeKey(deviceName, volumeKey string, volumeKey
|
||||||
func (c *CryptSetup) Deactivate(deviceName string) error {
|
func (c *CryptSetup) Deactivate(deviceName string) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
if !c.hasDetachedHeaderDevice() {
|
||||||
return errDeviceNotOpen
|
return errDeviceNotOpen
|
||||||
}
|
}
|
||||||
if err := c.device.Deactivate(deviceName); err != nil {
|
if err := c.deviceWithDetachedHeader.Deactivate(deviceName); err != nil {
|
||||||
return fmt.Errorf("deactivating crypt device %q: %w", deviceName, err)
|
return fmt.Errorf("deactivating crypt device %q: %w", deviceName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -149,18 +166,42 @@ func (c *CryptSetup) Deactivate(deviceName string) error {
|
||||||
func (c *CryptSetup) Format(integrity bool) error {
|
func (c *CryptSetup) Format(integrity bool) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
var device cryptDevice
|
||||||
|
|
||||||
|
// If we are re-formatting an existing device, we start from scratch without a detached header
|
||||||
|
if c.hasAttachedHeaderDevice() {
|
||||||
|
if c.deviceWithDetachedHeader != nil {
|
||||||
|
c.deviceWithDetachedHeader.Free()
|
||||||
|
c.deviceWithDetachedHeader = nil
|
||||||
|
}
|
||||||
|
if c.headerDevice != "" {
|
||||||
|
_ = detachLoopbackDevice(c.headerDevice)
|
||||||
|
c.headerDevice = ""
|
||||||
|
}
|
||||||
|
c.headerFile = ""
|
||||||
|
device = c.deviceWithAttachedHeader
|
||||||
|
} else {
|
||||||
return errDeviceNotOpen
|
return errDeviceNotOpen
|
||||||
}
|
}
|
||||||
if err := format(c.device, integrity); err != nil {
|
|
||||||
return fmt.Errorf("formatting crypt device %q: %w", c.device.GetDeviceName(), err)
|
if err := format(device, integrity); err != nil {
|
||||||
|
return fmt.Errorf("formatting crypt device %q: %w", device.GetDeviceName(), err)
|
||||||
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDeviceName gets the path to the underlying device.
|
// GetDeviceName gets the path to the underlying device.
|
||||||
func (c *CryptSetup) GetDeviceName() string {
|
func (c *CryptSetup) GetDeviceName() string {
|
||||||
return c.device.GetDeviceName()
|
packageLock.Lock()
|
||||||
|
defer packageLock.Unlock()
|
||||||
|
device, err := c.getActiveDevice()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return device.GetDeviceName()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUUID gets the device's LUKS2 UUID.
|
// GetUUID gets the device's LUKS2 UUID.
|
||||||
|
|
@ -168,12 +209,13 @@ func (c *CryptSetup) GetDeviceName() string {
|
||||||
func (c *CryptSetup) GetUUID() (string, error) {
|
func (c *CryptSetup) GetUUID() (string, error) {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return "", errDeviceNotOpen
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
uuid := c.device.GetUUID()
|
uuid := device.GetUUID()
|
||||||
if uuid == "" {
|
if uuid == "" {
|
||||||
return "", fmt.Errorf("unable to get UUID for device %q", c.device.GetDeviceName())
|
return "", fmt.Errorf("unable to get UUID for device %q", device.GetDeviceName())
|
||||||
}
|
}
|
||||||
return strings.ToLower(uuid), nil
|
return strings.ToLower(uuid), nil
|
||||||
}
|
}
|
||||||
|
|
@ -183,11 +225,15 @@ func (c *CryptSetup) GetUUID() (string, error) {
|
||||||
func (c *CryptSetup) KeyslotAddByVolumeKey(keyslot int, volumeKey string, passphrase string) error {
|
func (c *CryptSetup) KeyslotAddByVolumeKey(keyslot int, volumeKey string, passphrase string) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return errDeviceNotOpen
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err := c.device.KeyslotAddByVolumeKey(keyslot, volumeKey, passphrase); err != nil {
|
if err := device.KeyslotAddByVolumeKey(keyslot, volumeKey, passphrase); err != nil {
|
||||||
return fmt.Errorf("adding keyslot to device %q: %w", c.device.GetDeviceName(), err)
|
return fmt.Errorf("adding keyslot to device %q: %w", device.GetDeviceName(), err)
|
||||||
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -196,21 +242,30 @@ func (c *CryptSetup) KeyslotAddByVolumeKey(keyslot int, volumeKey string, passph
|
||||||
func (c *CryptSetup) KeyslotChangeByPassphrase(currentKeyslot, newKeyslot int, currentPassphrase, newPassphrase string) error {
|
func (c *CryptSetup) KeyslotChangeByPassphrase(currentKeyslot, newKeyslot int, currentPassphrase, newPassphrase string) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return errDeviceNotOpen
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err := c.device.KeyslotChangeByPassphrase(currentKeyslot, newKeyslot, currentPassphrase, newPassphrase); err != nil {
|
if err := device.KeyslotChangeByPassphrase(currentKeyslot, newKeyslot, currentPassphrase, newPassphrase); err != nil {
|
||||||
return fmt.Errorf("updating passphrase for device %q: %w", c.device.GetDeviceName(), err)
|
return fmt.Errorf("updating passphrase for device %q: %w", device.GetDeviceName(), err)
|
||||||
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadLUKS2 loads the device as LUKS2 crypt device.
|
// LoadLUKS2 loads the device as LUKS2 crypt device.
|
||||||
func (c *CryptSetup) LoadLUKS2() error {
|
func (c *CryptSetup) LoadLUKS2() error {
|
||||||
if err := loadLUKS2(c.device); err != nil {
|
packageLock.Lock()
|
||||||
return fmt.Errorf("loading LUKS2 crypt device %q: %w", c.device.GetDeviceName(), err)
|
defer packageLock.Unlock()
|
||||||
|
if c.hasDetachedHeaderDevice() {
|
||||||
|
if err := loadLUKS2(c.deviceWithDetachedHeader); err != nil {
|
||||||
|
return fmt.Errorf("loading LUKS2 crypt device %q: %w", c.deviceWithDetachedHeader.GetDeviceName(), err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("cannot load LUKS2 on device with attached header")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize resizes a device to the given size.
|
// Resize resizes a device to the given size.
|
||||||
|
|
@ -219,11 +274,15 @@ func (c *CryptSetup) LoadLUKS2() error {
|
||||||
func (c *CryptSetup) Resize(name string, newSize uint64) error {
|
func (c *CryptSetup) Resize(name string, newSize uint64) error {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return errDeviceNotOpen
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err := c.device.Resize(name, newSize); err != nil {
|
if err := device.Resize(name, newSize); err != nil {
|
||||||
return fmt.Errorf("resizing crypt device %q: %w", c.device.GetDeviceName(), err)
|
return fmt.Errorf("resizing crypt device %q: %w", device.GetDeviceName(), err)
|
||||||
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -232,10 +291,11 @@ func (c *CryptSetup) Resize(name string, newSize uint64) error {
|
||||||
func (c *CryptSetup) TokenJSONGet(token int) (string, error) {
|
func (c *CryptSetup) TokenJSONGet(token int) (string, error) {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return "", errDeviceNotOpen
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
json, err := c.device.TokenJSONGet(token)
|
json, err := device.TokenJSONGet(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("getting JSON data for token %d: %w", token, err)
|
return "", fmt.Errorf("getting JSON data for token %d: %w", token, err)
|
||||||
}
|
}
|
||||||
|
|
@ -252,18 +312,25 @@ func (c *CryptSetup) TokenJSONGet(token int) (string, error) {
|
||||||
func (c *CryptSetup) TokenJSONSet(token int, json string) (int, error) {
|
func (c *CryptSetup) TokenJSONSet(token int, json string) (int, error) {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return -1, errDeviceNotOpen
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
}
|
}
|
||||||
tokenID, err := c.device.TokenJSONSet(token, json)
|
|
||||||
|
tokenID, err := device.TokenJSONSet(token, json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, fmt.Errorf("setting JSON data for token %d: %w", token, err)
|
return -1, fmt.Errorf("setting JSON data for token %d: %w", token, err)
|
||||||
}
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
return tokenID, nil
|
return tokenID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConstellationStateDiskToken sets the Constellation state disk token.
|
// SetConstellationStateDiskToken sets the Constellation state disk token.
|
||||||
func (c *CryptSetup) SetConstellationStateDiskToken(diskIsInitialized bool) error {
|
func (c *CryptSetup) SetConstellationStateDiskToken(diskIsInitialized bool) error {
|
||||||
|
packageLock.Lock()
|
||||||
|
defer packageLock.Unlock()
|
||||||
token := constellationLUKS2Token{
|
token := constellationLUKS2Token{
|
||||||
Type: "constellation-state-disk",
|
Type: "constellation-state-disk",
|
||||||
Keyslots: []string{},
|
Keyslots: []string{},
|
||||||
|
|
@ -273,15 +340,31 @@ func (c *CryptSetup) SetConstellationStateDiskToken(diskIsInitialized bool) erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("marshaling token: %w", err)
|
return fmt.Errorf("marshaling token: %w", err)
|
||||||
}
|
}
|
||||||
if _, err := c.device.TokenJSONSet(ConstellationStateDiskTokenID, string(json)); err != nil {
|
|
||||||
|
device, err := c.getActiveDevice()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := device.TokenJSONSet(ConstellationStateDiskTokenID, string(json)); err != nil {
|
||||||
return fmt.Errorf("setting token: %w", err)
|
return fmt.Errorf("setting token: %w", err)
|
||||||
}
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstellationStateDiskTokenIsInitialized returns true if the Constellation state disk token is set to initialized.
|
// ConstellationStateDiskTokenIsInitialized returns true if the Constellation state disk token is set to initialized.
|
||||||
func (c *CryptSetup) ConstellationStateDiskTokenIsInitialized() bool {
|
func (c *CryptSetup) ConstellationStateDiskTokenIsInitialized() bool {
|
||||||
stateDiskToken, err := c.device.TokenJSONGet(ConstellationStateDiskTokenID)
|
packageLock.Lock()
|
||||||
|
defer packageLock.Unlock()
|
||||||
|
device, err := c.getActiveDevice()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
stateDiskToken, err := device.TokenJSONGet(ConstellationStateDiskTokenID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -298,17 +381,18 @@ func (c *CryptSetup) Wipe(
|
||||||
) (err error) {
|
) (err error) {
|
||||||
packageLock.Lock()
|
packageLock.Lock()
|
||||||
defer packageLock.Unlock()
|
defer packageLock.Unlock()
|
||||||
if c.device == nil {
|
device, err := c.getActiveDevice()
|
||||||
return errDeviceNotOpen
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Active temporary device to perform wipe on
|
// Active temporary device to perform wipe on
|
||||||
tmpDevice := tmpDevicePrefix + name
|
tmpDevice := tmpDevicePrefix + name
|
||||||
if err := c.device.ActivateByVolumeKey(tmpDevice, "", 0, wipeFlags); err != nil {
|
if err := device.ActivateByVolumeKey(tmpDevice, "", 0, wipeFlags); err != nil {
|
||||||
return fmt.Errorf("trying to activate temporary dm-crypt volume: %w", err)
|
return fmt.Errorf("trying to activate temporary dm-crypt volume: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if deactivateErr := c.device.Deactivate(tmpDevice); deactivateErr != nil {
|
if deactivateErr := device.Deactivate(tmpDevice); deactivateErr != nil {
|
||||||
err = errors.Join(err, fmt.Errorf("deactivating temporary device %q: %w", tmpDevice, deactivateErr))
|
err = errors.Join(err, fmt.Errorf("deactivating temporary device %q: %w", tmpDevice, deactivateErr))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -330,8 +414,89 @@ func (c *CryptSetup) Wipe(
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.device.Wipe(mappedDevicePath+tmpDevice, wipePattern, 0, 0, blockWipeSize, flags, progressCallback); err != nil {
|
if err := device.Wipe(mappedDevicePath+tmpDevice, wipePattern, 0, 0, blockWipeSize, flags, progressCallback); err != nil {
|
||||||
return fmt.Errorf("wiping disk of device %q: %w", c.device.GetDeviceName(), err)
|
return fmt.Errorf("wiping disk of device %q: %w", device.GetDeviceName(), err)
|
||||||
|
}
|
||||||
|
if err := c.createHeaderBackup(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CryptSetup) free() {
|
||||||
|
if c.hasDetachedHeaderDevice() {
|
||||||
|
c.deviceWithDetachedHeader.Free()
|
||||||
|
c.deviceWithDetachedHeader = nil
|
||||||
|
}
|
||||||
|
if c.hasAttachedHeaderDevice() {
|
||||||
|
c.deviceWithAttachedHeader.Free()
|
||||||
|
c.deviceWithAttachedHeader = nil
|
||||||
|
}
|
||||||
|
if c.headerDevice != "" {
|
||||||
|
_ = detachLoopbackDevice(c.headerDevice)
|
||||||
|
}
|
||||||
|
if c.headerFile != "" {
|
||||||
|
c.headerFile = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CryptSetup) reload() error {
|
||||||
|
if !c.hasAttachedHeaderDevice() {
|
||||||
|
return errDeviceNotOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
backingDevice := c.deviceWithAttachedHeader.GetDeviceName()
|
||||||
|
c.free()
|
||||||
|
var err error
|
||||||
|
c.deviceWithDetachedHeader, c.deviceWithAttachedHeader, c.headerDevice, c.headerFile, err = c.pathInit(backingDevice)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("re-loading crypt device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.hasDetachedHeaderDevice() {
|
||||||
|
return errors.New("failed to reload device without detached header")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := loadLUKS2(c.deviceWithDetachedHeader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getActiveDevice returns a handle to the active cryptsetup device with detached header if set,
|
||||||
|
// or one with attached header otherwise.
|
||||||
|
func (c *CryptSetup) getActiveDevice() (cryptDevice, error) {
|
||||||
|
if c.hasDetachedHeaderDevice() {
|
||||||
|
return c.deviceWithDetachedHeader, nil
|
||||||
|
}
|
||||||
|
if c.hasAttachedHeaderDevice() {
|
||||||
|
return c.deviceWithAttachedHeader, nil
|
||||||
|
}
|
||||||
|
return nil, errDeviceNotOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasDetachedHeaderDevice checks if the value of the [CryptSetup.deviceWithDetachedHeader] interface is not nil.
|
||||||
|
func (c *CryptSetup) hasDetachedHeaderDevice() bool {
|
||||||
|
return c.deviceWithDetachedHeader != nil && !reflect.ValueOf(c.deviceWithDetachedHeader).IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasAttachedHeaderDevice checks if the value of the [CryptSetup.deviceWithAttachedHeader] interface is not nil.
|
||||||
|
func (c *CryptSetup) hasAttachedHeaderDevice() bool {
|
||||||
|
return c.deviceWithAttachedHeader != nil && !reflect.ValueOf(c.deviceWithAttachedHeader).IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createHeaderBackup creates a backup of the detached header, and saves it back to the original device.
|
||||||
|
func (c *CryptSetup) createHeaderBackup() error {
|
||||||
|
if c.hasDetachedHeaderDevice() && c.headerFile != "" {
|
||||||
|
if err := headerBackup(c.deviceWithDetachedHeader, c.headerFile); err != nil {
|
||||||
|
return fmt.Errorf("creating header backup for device %q: %w", c.deviceWithDetachedHeader.GetDeviceName(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.hasAttachedHeaderDevice() && c.headerFile != "" {
|
||||||
|
if err := headerRestore(c.deviceWithAttachedHeader, c.headerFile); err != nil {
|
||||||
|
return fmt.Errorf("restoring header for device %q (with attached header): %w", c.deviceWithDetachedHeader.GetDeviceName(), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,16 @@ package cryptsetup
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/martinjungblut/go-cryptsetup"
|
"github.com/martinjungblut/go-cryptsetup"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -56,12 +63,103 @@ func format(device cryptDevice, integrity bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initByDevicePath(devicePath string) (cryptDevice, error) {
|
// headerRestore restores the header of the given device from the header in the given file.
|
||||||
return cryptsetup.Init(devicePath)
|
// Reloading the device is required for the changes to be reflected in the active [cryptDevice] struct.
|
||||||
|
func headerRestore(device cryptDevice, headerFile string) error {
|
||||||
|
switch d := device.(type) {
|
||||||
|
case cgoRestorer:
|
||||||
|
return d.HeaderRestore(cryptsetup.LUKS2{}, headerFile)
|
||||||
|
default:
|
||||||
|
return errInvalidType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initByName(name string) (cryptDevice, error) {
|
// headerBackup creates a backup of the cryptDevice's header to the given file.
|
||||||
return cryptsetup.InitByName(name)
|
func headerBackup(device cryptDevice, headerFile string) error {
|
||||||
|
switch d := device.(type) {
|
||||||
|
case cgoBackuper:
|
||||||
|
if err := os.Remove(headerFile); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return fmt.Errorf("removing existing header file %q: %w", headerFile, err)
|
||||||
|
}
|
||||||
|
if err := d.HeaderBackup(cryptsetup.LUKS2{}, headerFile); err != nil {
|
||||||
|
return fmt.Errorf("creating header backup: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errInvalidType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initByDevicePath(devicePath string) (deviceDetachedHeader, deviceAttachedHeader cryptDevice, headerDevice string, headerFile string, err error) {
|
||||||
|
tmpDevice, err := cryptsetup.Init(devicePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", "", fmt.Errorf("init device by path %s: %w", devicePath, err)
|
||||||
|
}
|
||||||
|
// If the device is not LUKS2 formatted, this is treated as a new device,
|
||||||
|
// meaning no header exists yet
|
||||||
|
if err := tmpDevice.Load(cryptsetup.LUKS2{}); err != nil {
|
||||||
|
return nil, tmpDevice, "", "", nil
|
||||||
|
}
|
||||||
|
defer tmpDevice.Free()
|
||||||
|
|
||||||
|
deviceAttachedHeader, err = cryptsetup.Init(devicePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", "", fmt.Errorf("init device by path %s: %w", devicePath, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && deviceAttachedHeader != nil {
|
||||||
|
deviceAttachedHeader.Free()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
headerDevice, headerFile, err = detachHeader(tmpDevice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", "", err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = detachLoopbackDevice(headerDevice)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cryptDevice, err := cryptsetup.InitDataDevice(headerDevice, devicePath)
|
||||||
|
return cryptDevice, deviceAttachedHeader, headerDevice, headerFile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func initByName(name string) (deviceDetachedHeader, deviceAttachedHeader cryptDevice, headerDevice string, headerFile string, err error) {
|
||||||
|
tmpDevice, err := cryptsetup.InitByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", "", fmt.Errorf("init device by name %s: %w", name, err)
|
||||||
|
}
|
||||||
|
// If the device is not LUKS2 formatted, this is treated as a new device,
|
||||||
|
// meaning no header exists yet
|
||||||
|
if err := tmpDevice.Load(cryptsetup.LUKS2{}); err != nil {
|
||||||
|
return nil, tmpDevice, "", "", nil
|
||||||
|
}
|
||||||
|
defer tmpDevice.Free()
|
||||||
|
|
||||||
|
deviceAttachedHeader, err = cryptsetup.InitByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", "", fmt.Errorf("init device by name %s: %w", name, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil && deviceAttachedHeader != nil {
|
||||||
|
deviceAttachedHeader.Free()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
headerDevice, headerFile, err = detachHeader(tmpDevice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", "", err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = detachLoopbackDevice(headerDevice)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cryptDevice, err := cryptsetup.InitByNameAndHeader(name, headerDevice)
|
||||||
|
return cryptDevice, deviceAttachedHeader, headerDevice, headerFile, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadLUKS2(device cryptDevice) error {
|
func loadLUKS2(device cryptDevice) error {
|
||||||
|
|
@ -73,6 +171,248 @@ func loadLUKS2(device cryptDevice) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// detachHeader loads reads the header from the given cryptsetup device and returns a loopback device with just the header.
|
||||||
|
func detachHeader(device *cryptsetup.Device) (headerDevice, headerFile string, err error) {
|
||||||
|
headerFile = filepath.Join(os.TempDir(), fmt.Sprintf("luks-header-%s", uuid.New().String()))
|
||||||
|
if err = headerBackup(device, headerFile); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
headerDevice, err = createLoopbackDevice(headerFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("create loopback device: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = detachLoopbackDevice(headerDevice)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
headerCryptDevice, err := cryptsetup.Init(headerDevice)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("init header device: %w", err)
|
||||||
|
}
|
||||||
|
defer headerCryptDevice.Free()
|
||||||
|
if err := headerCryptDevice.Load(cryptsetup.LUKS2{}); err != nil {
|
||||||
|
return "", "", fmt.Errorf("creating header backup: %w", err)
|
||||||
|
}
|
||||||
|
metadataJSON, err := headerCryptDevice.DumpJSON()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf("dumping device metadata: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadata cryptsetupMetadata
|
||||||
|
decoder := json.NewDecoder(strings.NewReader(metadataJSON))
|
||||||
|
decoder.DisallowUnknownFields() // Ensure no unknown fields are present in the JSON data
|
||||||
|
if err := decoder.Decode(&metadata); err != nil {
|
||||||
|
return "", "", fmt.Errorf("decoding LUKS header JSON from %s: %w", headerFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := verifyLUKS2Header(metadata); err != nil {
|
||||||
|
return "", "", fmt.Errorf("verifying LUKS2 header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerDevice, headerFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyLUKS2Header verifies a LUKS2 header contains the expected configuration for Constellation.
|
||||||
|
func verifyLUKS2Header(metadata cryptsetupMetadata) error {
|
||||||
|
if len(metadata.KeySlots) == 0 {
|
||||||
|
return errors.New("no key slots found in LUKS2 header")
|
||||||
|
}
|
||||||
|
for slotName, slot := range metadata.KeySlots {
|
||||||
|
if slot.Type != "luks2" {
|
||||||
|
return fmt.Errorf("unsupported key slot type %q for slot %q", slot.Type, slotName)
|
||||||
|
}
|
||||||
|
if slot.KeySize != 64 && slot.KeySize != 96 { // 64 for encryption, 96 if integrity is added
|
||||||
|
return fmt.Errorf("unsupported key size %d for slot %q", slot.KeySize, slotName)
|
||||||
|
}
|
||||||
|
if slot.AntiForensicSplitter.Type != "luks1" {
|
||||||
|
return fmt.Errorf("unsupported anti-forensic splitter type %q for slot %q", slot.AntiForensicSplitter.Type, slotName)
|
||||||
|
}
|
||||||
|
if slot.AntiForensicSplitter.Stripes != 4000 {
|
||||||
|
return fmt.Errorf("unsupported anti-forensic splitter stripes %d for slot %q", slot.AntiForensicSplitter.Stripes, slotName)
|
||||||
|
}
|
||||||
|
if slot.AntiForensicSplitter.Hash != "sha256" {
|
||||||
|
return fmt.Errorf("unsupported anti-forensic splitter hash %q for slot %q", slot.AntiForensicSplitter.Hash, slotName)
|
||||||
|
}
|
||||||
|
if slot.Area.Type != "raw" {
|
||||||
|
return fmt.Errorf("unsupported area type %q for slot %q", slot.Area.Type, slotName)
|
||||||
|
}
|
||||||
|
if slot.Area.Encryption != "aes-xts-plain64" {
|
||||||
|
return fmt.Errorf("unsupported area encryption %q for slot %q", slot.Area.Encryption, slotName)
|
||||||
|
}
|
||||||
|
if slot.Area.KeySize != 64 {
|
||||||
|
return fmt.Errorf("unsupported area key size %d for slot %q", slot.Area.KeySize, slotName)
|
||||||
|
}
|
||||||
|
if slot.KDF.Type != "argon2id" {
|
||||||
|
return fmt.Errorf("unsupported KDF type %q for slot %q", slot.KDF.Type, slotName)
|
||||||
|
}
|
||||||
|
if slot.KDF.Memory == 0 {
|
||||||
|
return fmt.Errorf("unsupported KDF memory %d for slot %q", slot.KDF.Memory, slotName)
|
||||||
|
}
|
||||||
|
if slot.KDF.Salt == "" {
|
||||||
|
return fmt.Errorf("unsupported KDF salt for slot %q", slotName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(metadata.Segments) == 0 {
|
||||||
|
return errors.New("no segments found in LUKS2 header")
|
||||||
|
}
|
||||||
|
for segmentName, segment := range metadata.Segments {
|
||||||
|
if segment.Type != "crypt" {
|
||||||
|
return fmt.Errorf("unsupported segment type %q for segment %q", segment.Type, segmentName)
|
||||||
|
}
|
||||||
|
if segment.SectorSize != 4096 {
|
||||||
|
return fmt.Errorf("unsupported segment sector size %d for segment %q", segment.SectorSize, segmentName)
|
||||||
|
}
|
||||||
|
if segment.IVTweak != "0" {
|
||||||
|
return fmt.Errorf("unsupported segment IV tweak %q for segment %q", segment.IVTweak, segmentName)
|
||||||
|
}
|
||||||
|
if segment.Encryption != "aes-xts-plain64" {
|
||||||
|
return fmt.Errorf("unsupported segment encryption %q for segment %q", segment.Encryption, segmentName)
|
||||||
|
}
|
||||||
|
switch segment.Integrity.Type {
|
||||||
|
case "hmac(sha256)":
|
||||||
|
if segment.Integrity.JournalEncryption != "none" {
|
||||||
|
return fmt.Errorf("unsupported segment integrity journal encryption %q for segment %q", segment.Integrity.JournalEncryption, segmentName)
|
||||||
|
}
|
||||||
|
if segment.Integrity.JournalIntegrity != "none" {
|
||||||
|
return fmt.Errorf("unsupported segment integrity journal integrity %q for segment %q", segment.Integrity.JournalIntegrity, segmentName)
|
||||||
|
}
|
||||||
|
case "":
|
||||||
|
if segment.Integrity.JournalEncryption != "" {
|
||||||
|
return fmt.Errorf("unsupported segment integrity journal encryption %q for segment %q", segment.Integrity.JournalEncryption, segmentName)
|
||||||
|
}
|
||||||
|
if segment.Integrity.JournalIntegrity != "" {
|
||||||
|
return fmt.Errorf("unsupported segment integrity journal integrity %q for segment %q", segment.Integrity.JournalIntegrity, segmentName)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported segment integrity type %q for segment %q", segment.Integrity.Type, segmentName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(metadata.Digests) == 0 {
|
||||||
|
return errors.New("no digests found in LUKS2 header")
|
||||||
|
}
|
||||||
|
for digestName, digest := range metadata.Digests {
|
||||||
|
if digest.Type != "pbkdf2" {
|
||||||
|
return fmt.Errorf("unsupported digest type %q for digest %q", digest.Type, digestName)
|
||||||
|
}
|
||||||
|
if digest.Hash != "sha256" {
|
||||||
|
return fmt.Errorf("unsupported digest hash %q for digest %q", digest.Hash, digestName)
|
||||||
|
}
|
||||||
|
if digest.Salt == "" {
|
||||||
|
return fmt.Errorf("unsupported digest salt for digest %q", digestName)
|
||||||
|
}
|
||||||
|
if digest.Digest == "" {
|
||||||
|
return fmt.Errorf("unsupported digest value for digest %q", digestName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createLoopbackDevice sets up a loop device for the given file and returns the loop device path (e.g., /dev/loop0).
|
||||||
|
func createLoopbackDevice(filePath string) (string, error) {
|
||||||
|
file, err := os.OpenFile(filePath, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("open backing file: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Get a free loop device number
|
||||||
|
ctrl, err := os.OpenFile("/dev/loop-control", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("open /dev/loop-control: %w", err)
|
||||||
|
}
|
||||||
|
defer ctrl.Close()
|
||||||
|
loopNum, _, errno := unix.Syscall(unix.SYS_IOCTL, ctrl.Fd(), unix.LOOP_CTL_GET_FREE, 0)
|
||||||
|
if errno != 0 {
|
||||||
|
return "", fmt.Errorf("LOOP_CTL_GET_FREE: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the loop device
|
||||||
|
loopDev := fmt.Sprintf("/dev/loop%d", loopNum)
|
||||||
|
loop, err := os.OpenFile(loopDev, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("open loop device: %w", err)
|
||||||
|
}
|
||||||
|
defer loop.Close()
|
||||||
|
|
||||||
|
// Associate the file with the loop device
|
||||||
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, loop.Fd(), unix.LOOP_SET_FD, file.Fd()); errno != 0 {
|
||||||
|
return "", fmt.Errorf("LOOP_SET_FD: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
return loopDev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// detachLoopbackDevice removes the specified loopback device.
|
||||||
|
func detachLoopbackDevice(loopDev string) error {
|
||||||
|
loop, err := os.OpenFile(loopDev, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("open loop device: %w", err)
|
||||||
|
}
|
||||||
|
defer loop.Close()
|
||||||
|
|
||||||
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, loop.Fd(), unix.LOOP_CLR_FD, 0); errno != 0 {
|
||||||
|
return fmt.Errorf("LOOP_CLR_FD: %v", errno)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cryptsetupMetadata struct {
|
||||||
|
KeySlots map[string]struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
KeySize int `json:"key_size"`
|
||||||
|
AntiForensicSplitter struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Stripes int `json:"stripes"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
} `json:"af"`
|
||||||
|
Area struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Offset string `json:"offset"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
Encryption string `json:"encryption"`
|
||||||
|
KeySize int `json:"key_size"`
|
||||||
|
} `json:"area"`
|
||||||
|
KDF struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Time int `json:"time"`
|
||||||
|
Memory int `json:"memory"`
|
||||||
|
CPUs int `json:"cpus"`
|
||||||
|
Salt string `json:"salt"`
|
||||||
|
} `json:"kdf"`
|
||||||
|
} `json:"keyslots"`
|
||||||
|
Tokens map[string]any `json:"tokens"`
|
||||||
|
Segments map[string]struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Offset string `json:"offset"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
Flags []string `json:"flags,omitempty"`
|
||||||
|
IVTweak string `json:"iv_tweak"`
|
||||||
|
Encryption string `json:"encryption"`
|
||||||
|
SectorSize int `json:"sector_size"`
|
||||||
|
Integrity struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
JournalEncryption string `json:"journal_encryption"`
|
||||||
|
JournalIntegrity string `json:"journal_integrity"`
|
||||||
|
}
|
||||||
|
} `json:"segments"`
|
||||||
|
Digests map[string]struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Keyslots []string `json:"keyslots"`
|
||||||
|
Segments []string `json:"segments"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Iterations int `json:"iterations"`
|
||||||
|
Salt string `json:"salt"`
|
||||||
|
Digest string `json:"digest"`
|
||||||
|
} `json:"digests"`
|
||||||
|
Config struct {
|
||||||
|
JSONSize string `json:"json_size"`
|
||||||
|
KeyslotsSize string `json:"keyslots_size"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type cgoFormatter interface {
|
type cgoFormatter interface {
|
||||||
Format(deviceType cryptsetup.DeviceType, genericParams cryptsetup.GenericParams) error
|
Format(deviceType cryptsetup.DeviceType, genericParams cryptsetup.GenericParams) error
|
||||||
}
|
}
|
||||||
|
|
@ -80,3 +420,10 @@ type cgoFormatter interface {
|
||||||
type cgoLoader interface {
|
type cgoLoader interface {
|
||||||
Load(deviceType cryptsetup.DeviceType) error
|
Load(deviceType cryptsetup.DeviceType) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cgoRestorer interface {
|
||||||
|
HeaderRestore(deviceType cryptsetup.DeviceType, headerFile string) error
|
||||||
|
}
|
||||||
|
type cgoBackuper interface {
|
||||||
|
HeaderBackup(deviceType cryptsetup.DeviceType, headerFile string) error
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,26 @@ func format(_ cryptDevice, _ bool) error {
|
||||||
return errCGONotSupported
|
return errCGONotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func initByDevicePath(_ string) (cryptDevice, error) {
|
func headerRestore(_ cryptDevice, _ string) error {
|
||||||
return nil, errCGONotSupported
|
return errCGONotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func initByName(_ string) (cryptDevice, error) {
|
func headerBackup(_ cryptDevice, _ string) error {
|
||||||
return nil, errCGONotSupported
|
return errCGONotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func initByDevicePath(_ string) (cryptDevice, cryptDevice, string, string, error) {
|
||||||
|
return nil, nil, "", "", errCGONotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func initByName(_ string) (cryptDevice, cryptDevice, string, string, error) {
|
||||||
|
return nil, nil, "", "", errCGONotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadLUKS2(_ cryptDevice) error {
|
func loadLUKS2(_ cryptDevice) error {
|
||||||
return errCGONotSupported
|
return errCGONotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func detachLoopbackDevice(_ string) error {
|
||||||
|
return errCGONotSupported
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue