blob: 3afd3746c3f629ffb53bea0f8e92b2f574a321f2 [file] [log] [blame]
Shikha Panwar27cb7e72022-10-13 20:34:45 +00001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/// `crypt` module implements the "crypt" target in the device mapper framework. Specifically,
Shikha Panwarbfb56d72024-03-12 17:30:52 +000018/// it provides `DmCryptTargetBuilder` struct which is used to construct a `DmCryptTarget`
19/// struct which is then given to `DeviceMapper` to create a mapper device.
Shikha Panwar27cb7e72022-10-13 20:34:45 +000020use crate::DmTargetSpec;
21
Shikha Panwar8e48a172022-11-25 19:01:28 +000022use anyhow::{ensure, Context, Result};
Shikha Panwar27cb7e72022-10-13 20:34:45 +000023use std::io::Write;
24use std::mem::size_of;
25use std::path::Path;
Frederick Mayle8f795902023-10-23 15:48:34 -070026use zerocopy::AsBytes;
Shikha Panwar27cb7e72022-10-13 20:34:45 +000027
28const SECTOR_SIZE: u64 = 512;
29
30// The UAPI for the crypt target is at:
31// Documentation/admin-guide/device-mapper/dm-crypt.rst
32
33/// Supported ciphers
Shikha Panwar8e48a172022-11-25 19:01:28 +000034#[derive(Clone, Copy, Debug)]
Shikha Panwar27cb7e72022-10-13 20:34:45 +000035pub enum CipherType {
Shikha Panwarbfb56d72024-03-12 17:30:52 +000036 /// AES256 with HCTR2 mode. HCTR2 is a tweakable super-pseudorandom permutation
37 /// length-preserving encryption mode. It is the preferred mode in absence of other
38 /// dedicated integrity primitives (such as for encryptedstore in pVM) since it is less
39 /// malleable than other modes.
Shikha Panwar195f89c2022-11-23 16:20:34 +000040 AES256HCTR2,
Shikha Panwarbfb56d72024-03-12 17:30:52 +000041 /// AES with XTS mode. This has slight performance benefits over HCTR2. In particular, XTS is
42 /// supported by inline encryption hardware. Note that (status quo) `encryptedstore` in VMs
43 /// is the only user of this module & inline encryption is not supported by guest kernel.
Shikha Panwar27cb7e72022-10-13 20:34:45 +000044 AES256XTS,
45}
Shikha Panwar195f89c2022-11-23 16:20:34 +000046impl CipherType {
47 fn get_kernel_crypto_name(&self) -> &str {
48 match *self {
49 // We use "plain64" as the IV/nonce generation algorithm -
50 // which basically is the sector number.
51 CipherType::AES256HCTR2 => "aes-hctr2-plain64",
52 CipherType::AES256XTS => "aes-xts-plain64",
53 }
54 }
Shikha Panwar8e48a172022-11-25 19:01:28 +000055
56 fn get_required_key_size(&self) -> usize {
57 match *self {
Shikha Panwarbfb56d72024-03-12 17:30:52 +000058 // AES-256-HCTR2 takes a 32-byte key
Shikha Panwar8e48a172022-11-25 19:01:28 +000059 CipherType::AES256HCTR2 => 32,
Shikha Panwarbfb56d72024-03-12 17:30:52 +000060 // XTS requires key of twice the length of the underlying block cipher
61 // i.e., 64B for AES256
Shikha Panwar8e48a172022-11-25 19:01:28 +000062 CipherType::AES256XTS => 64,
63 }
64 }
65
66 fn validata_key_size(&self, key_size: usize) -> bool {
67 key_size == self.get_required_key_size()
68 }
Shikha Panwar195f89c2022-11-23 16:20:34 +000069}
Shikha Panwar27cb7e72022-10-13 20:34:45 +000070
71pub struct DmCryptTarget(Box<[u8]>);
72
73impl DmCryptTarget {
74 /// Flatten into slice
75 pub fn as_slice(&self) -> &[u8] {
76 self.0.as_ref()
77 }
78}
79
80pub struct DmCryptTargetBuilder<'a> {
81 cipher: CipherType,
82 key: Option<&'a [u8]>,
83 iv_offset: u64,
84 device_path: Option<&'a Path>,
85 offset: u64,
86 device_size: u64,
Shikha Panwar6337d5b2023-02-09 13:02:33 +000087 opt_params: Vec<&'a str>,
Shikha Panwar27cb7e72022-10-13 20:34:45 +000088}
89
90impl<'a> Default for DmCryptTargetBuilder<'a> {
91 fn default() -> Self {
92 DmCryptTargetBuilder {
Shikha Panwar195f89c2022-11-23 16:20:34 +000093 cipher: CipherType::AES256HCTR2,
Shikha Panwar27cb7e72022-10-13 20:34:45 +000094 key: None,
95 iv_offset: 0,
96 device_path: None,
97 offset: 0,
98 device_size: 0,
Shikha Panwar6337d5b2023-02-09 13:02:33 +000099 opt_params: Vec::new(),
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000100 }
101 }
102}
103
104impl<'a> DmCryptTargetBuilder<'a> {
105 /// Sets the device that will be used as the data device (i.e. providing actual data).
106 pub fn data_device(&mut self, p: &'a Path, size: u64) -> &mut Self {
107 self.device_path = Some(p);
108 self.device_size = size;
109 self
110 }
111
112 /// Sets the encryption cipher.
113 pub fn cipher(&mut self, cipher: CipherType) -> &mut Self {
114 self.cipher = cipher;
115 self
116 }
117
118 /// Sets the key used for encryption. Input is byte array.
119 pub fn key(&mut self, key: &'a [u8]) -> &mut Self {
120 self.key = Some(key);
121 self
122 }
123
124 /// The IV offset is a sector count that is added to the sector number before creating the IV.
125 pub fn iv_offset(&mut self, iv_offset: u64) -> &mut Self {
126 self.iv_offset = iv_offset;
127 self
128 }
129
130 /// Starting sector within the device where the encrypted data begins
131 pub fn offset(&mut self, offset: u64) -> &mut Self {
132 self.offset = offset;
133 self
134 }
135
Shikha Panwar6337d5b2023-02-09 13:02:33 +0000136 /// Add additional optional parameter
137 pub fn opt_param(&mut self, param: &'a str) -> &mut Self {
138 self.opt_params.push(param);
139 self
140 }
141
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000142 /// Constructs a `DmCryptTarget`.
143 pub fn build(&self) -> Result<DmCryptTarget> {
144 // The `DmCryptTarget` struct actually is a flattened data consisting of a header and
145 // body. The format of the header is `dm_target_spec` as defined in
146 // include/uapi/linux/dm-ioctl.h.
147 let device_path = self
148 .device_path
149 .context("data device is not set")?
150 .to_str()
151 .context("data device path is not encoded in utf8")?;
152
Shikha Panwar8e48a172022-11-25 19:01:28 +0000153 ensure!(self.key.is_some(), "key is not set");
154 // Unwrap is safe because we already made sure key.is_some()
155 ensure!(
156 self.cipher.validata_key_size(self.key.unwrap().len()),
157 format!("Invalid key size for cipher:{}", self.cipher.get_kernel_crypto_name())
158 );
159 let key = hex::encode(self.key.unwrap());
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000160
161 // Step2: serialize the information according to the spec, which is ...
162 // DmTargetSpec{...}
163 // <cipher> <key> <iv_offset> <device path> \
164 // <offset> [<#opt_params> <opt_params>]
165 let mut body = String::new();
166 use std::fmt::Write;
Shikha Panwar195f89c2022-11-23 16:20:34 +0000167 write!(&mut body, "{} ", self.cipher.get_kernel_crypto_name())?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000168 write!(&mut body, "{} ", key)?;
169 write!(&mut body, "{} ", self.iv_offset)?;
170 write!(&mut body, "{} ", device_path)?;
171 write!(&mut body, "{} ", self.offset)?;
Shikha Panwar6337d5b2023-02-09 13:02:33 +0000172 write!(&mut body, "{} {} ", self.opt_params.len(), self.opt_params.join(" "))?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000173 write!(&mut body, "\0")?; // null terminator
174
175 let size = size_of::<DmTargetSpec>() + body.len();
176 let aligned_size = (size + 7) & !7; // align to 8 byte boundaries
177 let padding = aligned_size - size;
178
179 let mut header = DmTargetSpec::new("crypt")?;
180 header.sector_start = 0;
181 header.length = self.device_size / SECTOR_SIZE; // number of 512-byte sectors
182 header.next = aligned_size as u32;
183
184 let mut buf = Vec::with_capacity(aligned_size);
Frederick Mayle8f795902023-10-23 15:48:34 -0700185 buf.write_all(header.as_bytes())?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000186 buf.write_all(body.as_bytes())?;
187 buf.write_all(vec![0; padding].as_slice())?;
188
189 Ok(DmCryptTarget(buf.into_boxed_slice()))
190 }
191}