blob: 36c45c721df7ce4c669f8af24cbec1c5ea708a79 [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,
18/// it provides `DmCryptTargetBuilder` struct which is used to construct a `DmCryptTarget` struct
19/// 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 Panwar195f89c2022-11-23 16:20:34 +000036 // AES-256-HCTR2 takes a 32-byte key
37 AES256HCTR2,
38 // XTS requires key of twice the length of the underlying block cipher i.e., 64B for AES256
Shikha Panwar27cb7e72022-10-13 20:34:45 +000039 AES256XTS,
40}
Shikha Panwar195f89c2022-11-23 16:20:34 +000041impl CipherType {
42 fn get_kernel_crypto_name(&self) -> &str {
43 match *self {
44 // We use "plain64" as the IV/nonce generation algorithm -
45 // which basically is the sector number.
46 CipherType::AES256HCTR2 => "aes-hctr2-plain64",
47 CipherType::AES256XTS => "aes-xts-plain64",
48 }
49 }
Shikha Panwar8e48a172022-11-25 19:01:28 +000050
51 fn get_required_key_size(&self) -> usize {
52 match *self {
53 CipherType::AES256HCTR2 => 32,
54 CipherType::AES256XTS => 64,
55 }
56 }
57
58 fn validata_key_size(&self, key_size: usize) -> bool {
59 key_size == self.get_required_key_size()
60 }
Shikha Panwar195f89c2022-11-23 16:20:34 +000061}
Shikha Panwar27cb7e72022-10-13 20:34:45 +000062
63pub struct DmCryptTarget(Box<[u8]>);
64
65impl DmCryptTarget {
66 /// Flatten into slice
67 pub fn as_slice(&self) -> &[u8] {
68 self.0.as_ref()
69 }
70}
71
72pub struct DmCryptTargetBuilder<'a> {
73 cipher: CipherType,
74 key: Option<&'a [u8]>,
75 iv_offset: u64,
76 device_path: Option<&'a Path>,
77 offset: u64,
78 device_size: u64,
Shikha Panwar6337d5b2023-02-09 13:02:33 +000079 opt_params: Vec<&'a str>,
Shikha Panwar27cb7e72022-10-13 20:34:45 +000080}
81
82impl<'a> Default for DmCryptTargetBuilder<'a> {
83 fn default() -> Self {
84 DmCryptTargetBuilder {
Shikha Panwar195f89c2022-11-23 16:20:34 +000085 cipher: CipherType::AES256HCTR2,
Shikha Panwar27cb7e72022-10-13 20:34:45 +000086 key: None,
87 iv_offset: 0,
88 device_path: None,
89 offset: 0,
90 device_size: 0,
Shikha Panwar6337d5b2023-02-09 13:02:33 +000091 opt_params: Vec::new(),
Shikha Panwar27cb7e72022-10-13 20:34:45 +000092 }
93 }
94}
95
96impl<'a> DmCryptTargetBuilder<'a> {
97 /// Sets the device that will be used as the data device (i.e. providing actual data).
98 pub fn data_device(&mut self, p: &'a Path, size: u64) -> &mut Self {
99 self.device_path = Some(p);
100 self.device_size = size;
101 self
102 }
103
104 /// Sets the encryption cipher.
105 pub fn cipher(&mut self, cipher: CipherType) -> &mut Self {
106 self.cipher = cipher;
107 self
108 }
109
110 /// Sets the key used for encryption. Input is byte array.
111 pub fn key(&mut self, key: &'a [u8]) -> &mut Self {
112 self.key = Some(key);
113 self
114 }
115
116 /// The IV offset is a sector count that is added to the sector number before creating the IV.
117 pub fn iv_offset(&mut self, iv_offset: u64) -> &mut Self {
118 self.iv_offset = iv_offset;
119 self
120 }
121
122 /// Starting sector within the device where the encrypted data begins
123 pub fn offset(&mut self, offset: u64) -> &mut Self {
124 self.offset = offset;
125 self
126 }
127
Shikha Panwar6337d5b2023-02-09 13:02:33 +0000128 /// Add additional optional parameter
129 pub fn opt_param(&mut self, param: &'a str) -> &mut Self {
130 self.opt_params.push(param);
131 self
132 }
133
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000134 /// Constructs a `DmCryptTarget`.
135 pub fn build(&self) -> Result<DmCryptTarget> {
136 // The `DmCryptTarget` struct actually is a flattened data consisting of a header and
137 // body. The format of the header is `dm_target_spec` as defined in
138 // include/uapi/linux/dm-ioctl.h.
139 let device_path = self
140 .device_path
141 .context("data device is not set")?
142 .to_str()
143 .context("data device path is not encoded in utf8")?;
144
Shikha Panwar8e48a172022-11-25 19:01:28 +0000145 ensure!(self.key.is_some(), "key is not set");
146 // Unwrap is safe because we already made sure key.is_some()
147 ensure!(
148 self.cipher.validata_key_size(self.key.unwrap().len()),
149 format!("Invalid key size for cipher:{}", self.cipher.get_kernel_crypto_name())
150 );
151 let key = hex::encode(self.key.unwrap());
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000152
153 // Step2: serialize the information according to the spec, which is ...
154 // DmTargetSpec{...}
155 // <cipher> <key> <iv_offset> <device path> \
156 // <offset> [<#opt_params> <opt_params>]
157 let mut body = String::new();
158 use std::fmt::Write;
Shikha Panwar195f89c2022-11-23 16:20:34 +0000159 write!(&mut body, "{} ", self.cipher.get_kernel_crypto_name())?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000160 write!(&mut body, "{} ", key)?;
161 write!(&mut body, "{} ", self.iv_offset)?;
162 write!(&mut body, "{} ", device_path)?;
163 write!(&mut body, "{} ", self.offset)?;
Shikha Panwar6337d5b2023-02-09 13:02:33 +0000164 write!(&mut body, "{} {} ", self.opt_params.len(), self.opt_params.join(" "))?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000165 write!(&mut body, "\0")?; // null terminator
166
167 let size = size_of::<DmTargetSpec>() + body.len();
168 let aligned_size = (size + 7) & !7; // align to 8 byte boundaries
169 let padding = aligned_size - size;
170
171 let mut header = DmTargetSpec::new("crypt")?;
172 header.sector_start = 0;
173 header.length = self.device_size / SECTOR_SIZE; // number of 512-byte sectors
174 header.next = aligned_size as u32;
175
176 let mut buf = Vec::with_capacity(aligned_size);
Frederick Mayle8f795902023-10-23 15:48:34 -0700177 buf.write_all(header.as_bytes())?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000178 buf.write_all(body.as_bytes())?;
179 buf.write_all(vec![0; padding].as_slice())?;
180
181 Ok(DmCryptTarget(buf.into_boxed_slice()))
182 }
183}