blob: 75105ce7536c4d1b171d7b05c0aa0999da83d9b5 [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.
20use crate::util::*;
21use crate::DmTargetSpec;
22
23use anyhow::{bail, Context, Result};
24use data_model::DataInit;
25use std::io::Write;
26use std::mem::size_of;
27use std::path::Path;
28
29const SECTOR_SIZE: u64 = 512;
30
31// The UAPI for the crypt target is at:
32// Documentation/admin-guide/device-mapper/dm-crypt.rst
33
34/// Supported ciphers
35pub 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 }
50}
Shikha Panwar27cb7e72022-10-13 20:34:45 +000051
52pub struct DmCryptTarget(Box<[u8]>);
53
54impl DmCryptTarget {
55 /// Flatten into slice
56 pub fn as_slice(&self) -> &[u8] {
57 self.0.as_ref()
58 }
59}
60
61pub struct DmCryptTargetBuilder<'a> {
62 cipher: CipherType,
63 key: Option<&'a [u8]>,
64 iv_offset: u64,
65 device_path: Option<&'a Path>,
66 offset: u64,
67 device_size: u64,
68 // TODO(b/238179332) Extend this to include opt_params, in particular 'integrity'
69}
70
71impl<'a> Default for DmCryptTargetBuilder<'a> {
72 fn default() -> Self {
73 DmCryptTargetBuilder {
Shikha Panwar195f89c2022-11-23 16:20:34 +000074 cipher: CipherType::AES256HCTR2,
Shikha Panwar27cb7e72022-10-13 20:34:45 +000075 key: None,
76 iv_offset: 0,
77 device_path: None,
78 offset: 0,
79 device_size: 0,
80 }
81 }
82}
83
84impl<'a> DmCryptTargetBuilder<'a> {
85 /// Sets the device that will be used as the data device (i.e. providing actual data).
86 pub fn data_device(&mut self, p: &'a Path, size: u64) -> &mut Self {
87 self.device_path = Some(p);
88 self.device_size = size;
89 self
90 }
91
92 /// Sets the encryption cipher.
93 pub fn cipher(&mut self, cipher: CipherType) -> &mut Self {
94 self.cipher = cipher;
95 self
96 }
97
98 /// Sets the key used for encryption. Input is byte array.
99 pub fn key(&mut self, key: &'a [u8]) -> &mut Self {
100 self.key = Some(key);
101 self
102 }
103
104 /// The IV offset is a sector count that is added to the sector number before creating the IV.
105 pub fn iv_offset(&mut self, iv_offset: u64) -> &mut Self {
106 self.iv_offset = iv_offset;
107 self
108 }
109
110 /// Starting sector within the device where the encrypted data begins
111 pub fn offset(&mut self, offset: u64) -> &mut Self {
112 self.offset = offset;
113 self
114 }
115
116 /// Constructs a `DmCryptTarget`.
117 pub fn build(&self) -> Result<DmCryptTarget> {
118 // The `DmCryptTarget` struct actually is a flattened data consisting of a header and
119 // body. The format of the header is `dm_target_spec` as defined in
120 // include/uapi/linux/dm-ioctl.h.
121 let device_path = self
122 .device_path
123 .context("data device is not set")?
124 .to_str()
125 .context("data device path is not encoded in utf8")?;
126
127 let key =
128 if let Some(key) = self.key { hexstring_from(key) } else { bail!("key is not set") };
129
130 // Step2: serialize the information according to the spec, which is ...
131 // DmTargetSpec{...}
132 // <cipher> <key> <iv_offset> <device path> \
133 // <offset> [<#opt_params> <opt_params>]
134 let mut body = String::new();
135 use std::fmt::Write;
Shikha Panwar195f89c2022-11-23 16:20:34 +0000136 write!(&mut body, "{} ", self.cipher.get_kernel_crypto_name())?;
Shikha Panwar27cb7e72022-10-13 20:34:45 +0000137 write!(&mut body, "{} ", key)?;
138 write!(&mut body, "{} ", self.iv_offset)?;
139 write!(&mut body, "{} ", device_path)?;
140 write!(&mut body, "{} ", self.offset)?;
141 write!(&mut body, "\0")?; // null terminator
142
143 let size = size_of::<DmTargetSpec>() + body.len();
144 let aligned_size = (size + 7) & !7; // align to 8 byte boundaries
145 let padding = aligned_size - size;
146
147 let mut header = DmTargetSpec::new("crypt")?;
148 header.sector_start = 0;
149 header.length = self.device_size / SECTOR_SIZE; // number of 512-byte sectors
150 header.next = aligned_size as u32;
151
152 let mut buf = Vec::with_capacity(aligned_size);
153 buf.write_all(header.as_slice())?;
154 buf.write_all(body.as_bytes())?;
155 buf.write_all(vec![0; padding].as_slice())?;
156
157 Ok(DmCryptTarget(buf.into_boxed_slice()))
158 }
159}