• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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.
20 use crate::DmTargetSpec;
21 
22 use anyhow::{ensure, Context, Result};
23 use data_model::DataInit;
24 use std::io::Write;
25 use std::mem::size_of;
26 use std::path::Path;
27 
28 const 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
34 #[derive(Clone, Copy, Debug)]
35 pub enum CipherType {
36     // 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
39     AES256XTS,
40 }
41 impl CipherType {
get_kernel_crypto_name(&self) -> &str42     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     }
50 
get_required_key_size(&self) -> usize51     fn get_required_key_size(&self) -> usize {
52         match *self {
53             CipherType::AES256HCTR2 => 32,
54             CipherType::AES256XTS => 64,
55         }
56     }
57 
validata_key_size(&self, key_size: usize) -> bool58     fn validata_key_size(&self, key_size: usize) -> bool {
59         key_size == self.get_required_key_size()
60     }
61 }
62 
63 pub struct DmCryptTarget(Box<[u8]>);
64 
65 impl DmCryptTarget {
66     /// Flatten into slice
as_slice(&self) -> &[u8]67     pub fn as_slice(&self) -> &[u8] {
68         self.0.as_ref()
69     }
70 }
71 
72 pub 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     opt_params: Vec<&'a str>,
80 }
81 
82 impl<'a> Default for DmCryptTargetBuilder<'a> {
default() -> Self83     fn default() -> Self {
84         DmCryptTargetBuilder {
85             cipher: CipherType::AES256HCTR2,
86             key: None,
87             iv_offset: 0,
88             device_path: None,
89             offset: 0,
90             device_size: 0,
91             opt_params: Vec::new(),
92         }
93     }
94 }
95 
96 impl<'a> DmCryptTargetBuilder<'a> {
97     /// Sets the device that will be used as the data device (i.e. providing actual data).
data_device(&mut self, p: &'a Path, size: u64) -> &mut Self98     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.
cipher(&mut self, cipher: CipherType) -> &mut Self105     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.
key(&mut self, key: &'a [u8]) -> &mut Self111     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.
iv_offset(&mut self, iv_offset: u64) -> &mut Self117     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
offset(&mut self, offset: u64) -> &mut Self123     pub fn offset(&mut self, offset: u64) -> &mut Self {
124         self.offset = offset;
125         self
126     }
127 
128     /// Add additional optional parameter
opt_param(&mut self, param: &'a str) -> &mut Self129     pub fn opt_param(&mut self, param: &'a str) -> &mut Self {
130         self.opt_params.push(param);
131         self
132     }
133 
134     /// Constructs a `DmCryptTarget`.
build(&self) -> Result<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 
145         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());
152 
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;
159         write!(&mut body, "{} ", self.cipher.get_kernel_crypto_name())?;
160         write!(&mut body, "{} ", key)?;
161         write!(&mut body, "{} ", self.iv_offset)?;
162         write!(&mut body, "{} ", device_path)?;
163         write!(&mut body, "{} ", self.offset)?;
164         write!(&mut body, "{} {} ", self.opt_params.len(), self.opt_params.join(" "))?;
165         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);
177         buf.write_all(header.as_slice())?;
178         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 }
184