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