blob: b2e677a9ea0987046e211ba5ac810de230faeb1b [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 data_model::DataInit;
24use std::io::Write;
25use std::mem::size_of;
26use std::path::Path;
27
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,
79 // TODO(b/238179332) Extend this to include opt_params, in particular 'integrity'
80}
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,
91 }
92 }
93}
94
95impl<'a> DmCryptTargetBuilder<'a> {
96 /// Sets the device that will be used as the data device (i.e. providing actual data).
97 pub fn data_device(&mut self, p: &'a Path, size: u64) -> &mut Self {
98 self.device_path = Some(p);
99 self.device_size = size;
100 self
101 }
102
103 /// Sets the encryption cipher.
104 pub fn cipher(&mut self, cipher: CipherType) -> &mut Self {
105 self.cipher = cipher;
106 self
107 }
108
109 /// Sets the key used for encryption. Input is byte array.
110 pub fn key(&mut self, key: &'a [u8]) -> &mut Self {
111 self.key = Some(key);
112 self
113 }
114
115 /// The IV offset is a sector count that is added to the sector number before creating the IV.
116 pub fn iv_offset(&mut self, iv_offset: u64) -> &mut Self {
117 self.iv_offset = iv_offset;
118 self
119 }
120
121 /// Starting sector within the device where the encrypted data begins
122 pub fn offset(&mut self, offset: u64) -> &mut Self {
123 self.offset = offset;
124 self
125 }
126
127 /// Constructs a `DmCryptTarget`.
128 pub fn build(&self) -> Result<DmCryptTarget> {
129 // The `DmCryptTarget` struct actually is a flattened data consisting of a header and
130 // body. The format of the header is `dm_target_spec` as defined in
131 // include/uapi/linux/dm-ioctl.h.
132 let device_path = self
133 .device_path
134 .context("data device is not set")?
135 .to_str()
136 .context("data device path is not encoded in utf8")?;
137
Shikha Panwar8e48a172022-11-25 19:01:28 +0000138 ensure!(self.key.is_some(), "key is not set");
139 // Unwrap is safe because we already made sure key.is_some()
140 ensure!(
141 self.cipher.validata_key_size(self.key.unwrap().len()),
142 format!("Invalid key size for cipher:{}", self.cipher.get_kernel_crypto_name())
143 );
144 let key = hex::encode(self.key.unwrap());
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000145
146 // Step2: serialize the information according to the spec, which is ...
147 // DmTargetSpec{...}
148 // <cipher> <key> <iv_offset> <device path> \
149 // <offset> [<#opt_params> <opt_params>]
150 let mut body = String::new();
151 use std::fmt::Write;
Shikha Panwar195f89c2022-11-23 16:20:34 +0000152 write!(&mut body, "{} ", self.cipher.get_kernel_crypto_name())?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000153 write!(&mut body, "{} ", key)?;
154 write!(&mut body, "{} ", self.iv_offset)?;
155 write!(&mut body, "{} ", device_path)?;
156 write!(&mut body, "{} ", self.offset)?;
157 write!(&mut body, "\0")?; // null terminator
158
159 let size = size_of::<DmTargetSpec>() + body.len();
160 let aligned_size = (size + 7) & !7; // align to 8 byte boundaries
161 let padding = aligned_size - size;
162
163 let mut header = DmTargetSpec::new("crypt")?;
164 header.sector_start = 0;
165 header.length = self.device_size / SECTOR_SIZE; // number of 512-byte sectors
166 header.next = aligned_size as u32;
167
168 let mut buf = Vec::with_capacity(aligned_size);
169 buf.write_all(header.as_slice())?;
170 buf.write_all(body.as_bytes())?;
171 buf.write_all(vec![0; padding].as_slice())?;
172
173 Ok(DmCryptTarget(buf.into_boxed_slice()))
174 }
175}