Skip to content

Commit 7eada90

Browse files
Mikulas Patockasnitm
authored andcommitted
dm: add integrity target
The dm-integrity target emulates a block device that has additional per-sector tags that can be used for storing integrity information. A general problem with storing integrity tags with every sector is that writing the sector and the integrity tag must be atomic - i.e. in case of crash, either both sector and integrity tag or none of them is written. To guarantee write atomicity the dm-integrity target uses a journal. It writes sector data and integrity tags into a journal, commits the journal and then copies the data and integrity tags to their respective location. The dm-integrity target can be used with the dm-crypt target - in this situation the dm-crypt target creates the integrity data and passes them to the dm-integrity target via bio_integrity_payload attached to the bio. In this mode, the dm-crypt and dm-integrity targets provide authenticated disk encryption - if the attacker modifies the encrypted device, an I/O error is returned instead of random data. The dm-integrity target can also be used as a standalone target, in this mode it calculates and verifies the integrity tag internally. In this mode, the dm-integrity target can be used to detect silent data corruption on the disk or in the I/O path. Signed-off-by: Mikulas Patocka <[email protected]> Signed-off-by: Milan Broz <[email protected]> Signed-off-by: Mike Snitzer <[email protected]>
1 parent 400a0be commit 7eada90

File tree

4 files changed

+3285
-0
lines changed

4 files changed

+3285
-0
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
The dm-integrity target emulates a block device that has additional
2+
per-sector tags that can be used for storing integrity information.
3+
4+
A general problem with storing integrity tags with every sector is that
5+
writing the sector and the integrity tag must be atomic - i.e. in case of
6+
crash, either both sector and integrity tag or none of them is written.
7+
8+
To guarantee write atomicity, the dm-integrity target uses journal, it
9+
writes sector data and integrity tags into a journal, commits the journal
10+
and then copies the data and integrity tags to their respective location.
11+
12+
The dm-integrity target can be used with the dm-crypt target - in this
13+
situation the dm-crypt target creates the integrity data and passes them
14+
to the dm-integrity target via bio_integrity_payload attached to the bio.
15+
In this mode, the dm-crypt and dm-integrity targets provide authenticated
16+
disk encryption - if the attacker modifies the encrypted device, an I/O
17+
error is returned instead of random data.
18+
19+
The dm-integrity target can also be used as a standalone target, in this
20+
mode it calculates and verifies the integrity tag internally. In this
21+
mode, the dm-integrity target can be used to detect silent data
22+
corruption on the disk or in the I/O path.
23+
24+
25+
When loading the target for the first time, the kernel driver will format
26+
the device. But it will only format the device if the superblock contains
27+
zeroes. If the superblock is neither valid nor zeroed, the dm-integrity
28+
target can't be loaded.
29+
30+
To use the target for the first time:
31+
1. overwrite the superblock with zeroes
32+
2. load the dm-integrity target with one-sector size, the kernel driver
33+
will format the device
34+
3. unload the dm-integrity target
35+
4. read the "provided_data_sectors" value from the superblock
36+
5. load the dm-integrity target with the the target size
37+
"provided_data_sectors"
38+
6. if you want to use dm-integrity with dm-crypt, load the dm-crypt target
39+
with the size "provided_data_sectors"
40+
41+
42+
Target arguments:
43+
44+
1. the underlying block device
45+
46+
2. the number of reserved sector at the beginning of the device - the
47+
dm-integrity won't read of write these sectors
48+
49+
3. the size of the integrity tag (if "-" is used, the size is taken from
50+
the internal-hash algorithm)
51+
52+
4. mode:
53+
D - direct writes (without journal) - in this mode, journaling is
54+
not used and data sectors and integrity tags are written
55+
separately. In case of crash, it is possible that the data
56+
and integrity tag doesn't match.
57+
J - journaled writes - data and integrity tags are written to the
58+
journal and atomicity is guaranteed. In case of crash,
59+
either both data and tag or none of them are written. The
60+
journaled mode degrades write throughput twice because the
61+
data have to be written twice.
62+
63+
5. the number of additional arguments
64+
65+
Additional arguments:
66+
67+
journal-sectors:number
68+
The size of journal, this argument is used only if formatting the
69+
device. If the device is already formatted, the value from the
70+
superblock is used.
71+
72+
interleave-sectors:number
73+
The number of interleaved sectors. This values is rounded down to
74+
a power of two. If the device is already formatted, the value from
75+
the superblock is used.
76+
77+
buffer-sectors:number
78+
The number of sectors in one buffer. The value is rounded down to
79+
a power of two.
80+
81+
The tag area is accessed using buffers, the buffer size is
82+
configurable. The large buffer size means that the I/O size will
83+
be larger, but there could be less I/Os issued.
84+
85+
journal-watermark:number
86+
The journal watermark in percents. When the size of the journal
87+
exceeds this watermark, the thread that flushes the journal will
88+
be started.
89+
90+
commit-time:number
91+
Commit time in milliseconds. When this time passes, the journal is
92+
written. The journal is also written immediatelly if the FLUSH
93+
request is received.
94+
95+
internal-hash:algorithm(:key) (the key is optional)
96+
Use internal hash or crc.
97+
When this argument is used, the dm-integrity target won't accept
98+
integrity tags from the upper target, but it will automatically
99+
generate and verify the integrity tags.
100+
101+
You can use a crc algorithm (such as crc32), then integrity target
102+
will protect the data against accidental corruption.
103+
You can also use a hmac algorithm (for example
104+
"hmac(sha256):0123456789abcdef"), in this mode it will provide
105+
cryptographic authentication of the data without encryption.
106+
107+
When this argument is not used, the integrity tags are accepted
108+
from an upper layer target, such as dm-crypt. The upper layer
109+
target should check the validity of the integrity tags.
110+
111+
journal-crypt:algorithm(:key) (the key is optional)
112+
Encrypt the journal using given algorithm to make sure that the
113+
attacker can't read the journal. You can use a block cipher here
114+
(such as "cbc(aes)") or a stream cipher (for example "chacha20",
115+
"salsa20", "ctr(aes)" or "ecb(arc4)").
116+
117+
The journal contains history of last writes to the block device,
118+
an attacker reading the journal could see the last sector nubmers
119+
that were written. From the sector numbers, the attacker can infer
120+
the size of files that were written. To protect against this
121+
situation, you can encrypt the journal.
122+
123+
journal-mac:algorithm(:key) (the key is optional)
124+
Protect sector numbers in the journal from accidental or malicious
125+
modification. To protect against accidental modification, use a
126+
crc algorithm, to protect against malicious modification, use a
127+
hmac algorithm with a key.
128+
129+
This option is not needed when using internal-hash because in this
130+
mode, the integrity of journal entries is checked when replaying
131+
the journal. Thus, modified sector number would be detected at
132+
this stage.
133+
134+
135+
The journal mode (D/J), buffer-sectors, journal-watermark, commit-time can
136+
be changed when reloading the target (load an inactive table and swap the
137+
tables with suspend and resume). The other arguments should not be changed
138+
when reloading the target because the layout of disk data depend on them
139+
and the reloaded target would be non-functional.
140+
141+
142+
The layout of the formatted block device:
143+
* reserved sectors (they are not used by this target, they can be used for
144+
storing LUKS metadata or for other purpose), the size of the reserved
145+
area is specified in the target arguments
146+
* superblock (4kiB)
147+
* magic string - identifies that the device was formatted
148+
* version
149+
* log2(interleave sectors)
150+
* integrity tag size
151+
* the number of journal sections
152+
* provided data sectors - the number of sectors that this target
153+
provides (i.e. the size of the device minus the size of all
154+
metadata and padding). The user of this target should not send
155+
bios that access data beyond the "provided data sectors" limit.
156+
* flags - a flag is set if journal-mac is used
157+
* journal
158+
The journal is divided into sections, each section contains:
159+
* metadata area (4kiB), it contains journal entries
160+
every journal entry contains:
161+
* logical sector (specifies where the data and tag should
162+
be written)
163+
* last 8 bytes of data
164+
* integrity tag (the size is specified in the superblock)
165+
every metadata sector ends with
166+
* mac (8-bytes), all the macs in 8 metadata sectors form a
167+
64-byte value. It is used to store hmac of sector
168+
numbers in the journal section, to protect against a
169+
possibility that the attacker tampers with sector
170+
numbers in the journal.
171+
* commit id
172+
* data area (the size is variable; it depends on how many journal
173+
entries fit into the metadata area)
174+
every sector in the data area contains:
175+
* data (504 bytes of data, the last 8 bytes are stored in
176+
the journal entry)
177+
* commit id
178+
To test if the whole journal section was written correctly, every
179+
512-byte sector of the journal ends with 8-byte commit id. If the
180+
commit id matches on all sectors in a journal section, then it is
181+
assumed that the section was written correctly. If the commit id
182+
doesn't match, the section was written partially and it should not
183+
be replayed.
184+
* one or more runs of interleaved tags and data. Each run contains:
185+
* tag area - it contains integrity tags. There is one tag for each
186+
sector in the data area
187+
* data area - it contains data sectors. The number of data sectors
188+
in one run must be a power of two. log2 of this value is stored
189+
in the superblock.

drivers/md/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,14 @@ config DM_LOG_WRITES
500500

501501
If unsure, say N.
502502

503+
config DM_INTEGRITY
504+
tristate "Integrity target"
505+
depends on BLK_DEV_DM
506+
select BLK_DEV_INTEGRITY
507+
select DM_BUFIO
508+
select CRYPTO
509+
select ASYNC_XOR
510+
---help---
511+
This is the integrity target.
512+
503513
endif # MD

drivers/md/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ obj-$(CONFIG_DM_CACHE) += dm-cache.o
5959
obj-$(CONFIG_DM_CACHE_SMQ) += dm-cache-smq.o
6060
obj-$(CONFIG_DM_ERA) += dm-era.o
6161
obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
62+
obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
6263

6364
ifeq ($(CONFIG_DM_UEVENT),y)
6465
dm-mod-objs += dm-uevent.o

0 commit comments

Comments
 (0)