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` 19 /// struct which is then given to `DeviceMapper` to create a mapper device. 20 use crate::DmTargetSpec; 21 22 use anyhow::{ensure, Context, Result}; 23 use std::io::Write; 24 use std::mem::size_of; 25 use std::path::Path; 26 use zerocopy::AsBytes; 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 /// 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. 40 AES256HCTR2, 41 /// 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. 44 AES256XTS, 45 } 46 impl CipherType { get_kernel_crypto_name(&self) -> &str47 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 } 55 get_required_key_size(&self) -> usize56 fn get_required_key_size(&self) -> usize { 57 match *self { 58 // AES-256-HCTR2 takes a 32-byte key 59 CipherType::AES256HCTR2 => 32, 60 // XTS requires key of twice the length of the underlying block cipher 61 // i.e., 64B for AES256 62 CipherType::AES256XTS => 64, 63 } 64 } 65 validata_key_size(&self, key_size: usize) -> bool66 fn validata_key_size(&self, key_size: usize) -> bool { 67 key_size == self.get_required_key_size() 68 } 69 } 70 71 pub struct DmCryptTarget(Box<[u8]>); 72 73 impl DmCryptTarget { 74 /// Flatten into slice as_slice(&self) -> &[u8]75 pub fn as_slice(&self) -> &[u8] { 76 self.0.as_ref() 77 } 78 } 79 80 pub 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, 87 opt_params: Vec<&'a str>, 88 } 89 90 impl<'a> Default for DmCryptTargetBuilder<'a> { default() -> Self91 fn default() -> Self { 92 DmCryptTargetBuilder { 93 cipher: CipherType::AES256HCTR2, 94 key: None, 95 iv_offset: 0, 96 device_path: None, 97 offset: 0, 98 device_size: 0, 99 opt_params: Vec::new(), 100 } 101 } 102 } 103 104 impl<'a> DmCryptTargetBuilder<'a> { 105 /// 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 Self106 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. cipher(&mut self, cipher: CipherType) -> &mut Self113 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. key(&mut self, key: &'a [u8]) -> &mut Self119 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. iv_offset(&mut self, iv_offset: u64) -> &mut Self125 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 offset(&mut self, offset: u64) -> &mut Self131 pub fn offset(&mut self, offset: u64) -> &mut Self { 132 self.offset = offset; 133 self 134 } 135 136 /// Add additional optional parameter opt_param(&mut self, param: &'a str) -> &mut Self137 pub fn opt_param(&mut self, param: &'a str) -> &mut Self { 138 self.opt_params.push(param); 139 self 140 } 141 142 /// Constructs a `DmCryptTarget`. build(&self) -> Result<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 153 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()); 160 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; 167 write!(&mut body, "{} ", self.cipher.get_kernel_crypto_name())?; 168 write!(&mut body, "{} ", key)?; 169 write!(&mut body, "{} ", self.iv_offset)?; 170 write!(&mut body, "{} ", device_path)?; 171 write!(&mut body, "{} ", self.offset)?; 172 write!(&mut body, "{} {} ", self.opt_params.len(), self.opt_params.join(" "))?; 173 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); 185 buf.write_all(header.as_bytes())?; 186 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 } 192