blob: ad56ecd1a8dd9454c2ba293d4bbbf0b9bda7ccdf [file] [log] [blame]
Lorenz Brunf95909d2019-09-11 19:48:26 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package storage
18
19import (
20 "encoding/binary"
21 "encoding/hex"
22 "fmt"
Leopold Schabel40ab4b42019-10-22 15:35:52 +020023 "git.monogon.dev/source/smalltown.git/pkg/devicemapper"
Lorenz Brunf95909d2019-09-11 19:48:26 +020024 "os"
Lorenz Brunf95909d2019-09-11 19:48:26 +020025 "syscall"
26
27 "golang.org/x/sys/unix"
28)
29
30func readDataSectors(path string) (uint64, error) {
31 integrityPartition, err := os.Open(path)
32 if err != nil {
33 return 0, err
34 }
35 defer integrityPartition.Close()
36 // Based on structure defined in
37 // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/md/dm-integrity.c#n59
38 if _, err := integrityPartition.Seek(16, 0); err != nil {
39 return 0, err
40 }
41 var providedDataSectors uint64
42 if err := binary.Read(integrityPartition, binary.LittleEndian, &providedDataSectors); err != nil {
43 return 0, err
44 }
45 return providedDataSectors, nil
46}
47
48// MapEncryptedBlockDevice maps an encrypted device (node) at baseName to a
49// decrypted device at /dev/$name using the given encryptionKey
50func MapEncryptedBlockDevice(name string, baseName string, encryptionKey []byte) error {
51 integritySectors, err := readDataSectors(baseName)
52 if err != nil {
53 return fmt.Errorf("failed to read the number of usable sectors on the integrity device: %w", err)
54 }
55
56 integrityDevName := fmt.Sprintf("/dev/%v-integrity", name)
57 integrityDMName := fmt.Sprintf("%v-integrity", name)
58 integrityDev, err := devicemapper.CreateActiveDevice(integrityDMName, []devicemapper.Target{
59 devicemapper.Target{
60 Length: integritySectors,
61 Type: "integrity",
62 Parameters: fmt.Sprintf("%v 0 28 J 1 journal_sectors:1024", baseName),
63 },
64 })
65 if err != nil {
66 return fmt.Errorf("failed to create Integrity device: %w", err)
67 }
68 if err := unix.Mknod(integrityDevName, 0600|unix.S_IFBLK, int(integrityDev)); err != nil {
69 unix.Unlink(integrityDevName)
70 devicemapper.RemoveDevice(integrityDMName)
71 return fmt.Errorf("failed to create integrity device node: %w", err)
72 }
73
74 cryptDevName := fmt.Sprintf("/dev/%v", name)
75 cryptDev, err := devicemapper.CreateActiveDevice(name, []devicemapper.Target{
76 devicemapper.Target{
77 Length: integritySectors,
78 Type: "crypt",
79 Parameters: fmt.Sprintf("capi:gcm(aes)-random %v 0 %v 0 1 integrity:28:aead", hex.EncodeToString(encryptionKey), integrityDevName),
80 },
81 })
82 if err != nil {
83 unix.Unlink(integrityDevName)
84 devicemapper.RemoveDevice(integrityDMName)
85 return fmt.Errorf("failed to create crypt device: %w", err)
86 }
87 if err := unix.Mknod(cryptDevName, 0600|unix.S_IFBLK, int(cryptDev)); err != nil {
88 unix.Unlink(cryptDevName)
89 devicemapper.RemoveDevice(name)
90
91 unix.Unlink(integrityDevName)
92 devicemapper.RemoveDevice(integrityDMName)
93 return fmt.Errorf("failed to create crypt device node: %w", err)
94 }
95 return nil
96}
97
98// InitializeEncryptedBlockDevice initializes a new encrypted block device. This can take a long
99// time since all bytes on the mapped block device need to be zeroed.
100func InitializeEncryptedBlockDevice(name, baseName string, encryptionKey []byte) error {
101 integrityPartition, err := os.OpenFile(baseName, os.O_WRONLY, 0)
102 if err != nil {
103 return err
104 }
105 defer integrityPartition.Close()
106 zeroed512BBuf := make([]byte, 4096)
107 if _, err := integrityPartition.Write(zeroed512BBuf); err != nil {
108 return fmt.Errorf("failed to wipe header: %w", err)
109 }
110 integrityPartition.Close()
111
112 integrityDMName := fmt.Sprintf("%v-integrity", name)
113 _, err = devicemapper.CreateActiveDevice(integrityDMName, []devicemapper.Target{
114 devicemapper.Target{
115 Length: 1,
116 Type: "integrity",
117 Parameters: fmt.Sprintf("%v 0 28 J 1 journal_sectors:1024", baseName),
118 },
119 })
120 if err != nil {
121 return fmt.Errorf("failed to create discovery integrity device: %w", err)
122 }
123 if err := devicemapper.RemoveDevice(integrityDMName); err != nil {
124 return fmt.Errorf("failed to remove discovery integrity device: %w", err)
125 }
126
127 if err := MapEncryptedBlockDevice(name, baseName, encryptionKey); err != nil {
128 return err
129 }
130
131 blkdev, err := os.OpenFile(fmt.Sprintf("/dev/%v", name), unix.O_DIRECT|os.O_WRONLY, 0000)
132 if err != nil {
133 return fmt.Errorf("failed to open new encrypted device for zeroing: %w", err)
134 }
135 defer blkdev.Close()
136 blockSize, err := unix.IoctlGetUint32(int(blkdev.Fd()), unix.BLKSSZGET)
137 zeroedBuf := make([]byte, blockSize*100) // Make it faster
138 for {
139 _, err := blkdev.Write(zeroedBuf)
140 if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC {
141 break
142 }
143 if err != nil {
144 return fmt.Errorf("failed to zero-initalize new encrypted device: %w", err)
145 }
146 }
147 return nil
148}