Skip to content

Commit d9beb31

Browse files
committed
Allow parallel encryption, reencryption and decryption operations
1 parent fa26072 commit d9beb31

File tree

6 files changed

+178
-111
lines changed

6 files changed

+178
-111
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ jobs:
149149
dnf install -y
150150
asciidoc
151151
clang
152+
cryptsetup
152153
cryptsetup-devel
153154
dbus-daemon
154155
dbus-tools

.github/workflows/ubuntu.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ jobs:
130130
apt-get install -y
131131
asciidoc
132132
clang
133+
cryptsetup
133134
curl
134135
git
135136
libblkid-dev

.github/workflows/valgrind.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ jobs:
7373
dnf install -y
7474
asciidoc
7575
clang
76+
cryptsetup
7677
cryptsetup-devel
7778
curl
7879
device-mapper-persistent-data

src/engine/strat_engine/cmd.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ const CLEVIS_EXEC_NAMES: &[&str] = &[
111111
CLEVIS_REGEN,
112112
JOSE,
113113
JQ,
114-
CRYPTSETUP,
115114
CURL,
116115
TPM2_CREATEPRIMARY,
117116
TPM2_UNSEAL,
@@ -132,6 +131,7 @@ static EXECUTABLES: LazyLock<HashMap<String, Option<PathBuf>>> = LazyLock::new(|
132131
THIN_METADATA_SIZE.to_string(),
133132
find_executable(THIN_METADATA_SIZE),
134133
),
134+
(CRYPTSETUP.to_string(), find_executable(CRYPTSETUP)),
135135
]
136136
.iter()
137137
.cloned()
@@ -579,3 +579,38 @@ pub fn thin_metadata_size(
579579
)))
580580
}
581581
}
582+
583+
pub fn run_encrypt(path: &Path) -> StratisResult<()> {
584+
get_persistent_keyring()?;
585+
let mut cmd = Command::new(get_executable(CRYPTSETUP));
586+
cmd.arg("reencrypt")
587+
.arg("--encrypt")
588+
.arg("--resume-only")
589+
.arg("--token-only")
590+
.arg(path);
591+
592+
execute_cmd(&mut cmd)
593+
}
594+
595+
pub fn run_reencrypt(path: &Path) -> StratisResult<()> {
596+
get_persistent_keyring()?;
597+
let mut cmd = Command::new(get_executable(CRYPTSETUP));
598+
cmd.arg("reencrypt")
599+
.arg("--resume-only")
600+
.arg("--token-only")
601+
.arg(path);
602+
603+
execute_cmd(&mut cmd)
604+
}
605+
606+
pub fn run_decrypt(path: &Path) -> StratisResult<()> {
607+
get_persistent_keyring()?;
608+
let mut cmd = Command::new(get_executable(CRYPTSETUP));
609+
cmd.arg("reencrypt")
610+
.arg("--decrypt")
611+
.arg("--resume-only")
612+
.arg("--token-only")
613+
.arg(path);
614+
615+
execute_cmd(&mut cmd)
616+
}

src/engine/strat_engine/crypt/handle/v2.rs

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ use crate::{
3535
engine::MAX_STRATIS_PASS_SIZE,
3636
strat_engine::{
3737
backstore::{backstore::v2, get_devno_from_path, get_logical_sector_size},
38-
cmd::{clevis_luks_bind, clevis_luks_regen, clevis_luks_unbind},
38+
cmd::{
39+
clevis_luks_bind, clevis_luks_regen, clevis_luks_unbind, run_decrypt, run_encrypt,
40+
},
3941
crypt::{
4042
consts::{
4143
DEFAULT_CRYPT_DATA_OFFSET_V2, DEFAULT_CRYPT_KEYSLOTS_SIZE,
@@ -818,38 +820,43 @@ impl CryptHandle {
818820
sector_size: u32,
819821
key_info: (u32, SizedKeyMemory),
820822
) -> StratisResult<()> {
821-
let mut device = acquire_crypt_device(unencrypted_path)?;
822-
let activation_name = &format_crypt_backstore_name(&pool_uuid).to_string();
823-
let (keyslot, key) = key_info;
824-
device.reencrypt_handle().reencrypt_init_by_passphrase(
825-
Some(activation_name),
826-
key.as_ref(),
827-
None,
828-
Some(keyslot),
829-
Some(("aes", "xts-plain64")),
830-
CryptParamsReencrypt {
831-
mode: CryptReencryptModeInfo::Encrypt,
832-
direction: CryptReencryptDirectionInfo::Forward,
833-
resilience: "checksum".to_string(),
834-
hash: "sha256".to_string(),
835-
data_shift: 0,
836-
max_hotzone_size: 0,
837-
device_size: 0,
838-
luks2: Some(CryptParamsLuks2 {
839-
data_alignment: 0,
840-
data_device: None,
841-
integrity: None,
842-
integrity_params: None,
843-
pbkdf: None,
844-
label: None,
845-
sector_size,
846-
subsystem: None,
847-
}),
848-
flags: CryptReencrypt::RESUME_ONLY,
849-
},
850-
)?;
823+
{
824+
let mut device = acquire_crypt_device(unencrypted_path)?;
825+
let activation_name = &format_crypt_backstore_name(&pool_uuid).to_string();
826+
let (keyslot, key) = key_info;
827+
device.reencrypt_handle().reencrypt_init_by_passphrase(
828+
Some(activation_name),
829+
key.as_ref(),
830+
None,
831+
Some(keyslot),
832+
Some(("aes", "xts-plain64")),
833+
CryptParamsReencrypt {
834+
mode: CryptReencryptModeInfo::Encrypt,
835+
direction: CryptReencryptDirectionInfo::Forward,
836+
resilience: "checksum".to_string(),
837+
hash: "sha256".to_string(),
838+
data_shift: 0,
839+
max_hotzone_size: 0,
840+
device_size: 0,
841+
luks2: Some(CryptParamsLuks2 {
842+
data_alignment: 0,
843+
data_device: None,
844+
integrity: None,
845+
integrity_params: None,
846+
pbkdf: None,
847+
label: None,
848+
sector_size,
849+
subsystem: None,
850+
}),
851+
flags: CryptReencrypt::RESUME_ONLY,
852+
},
853+
)?;
854+
}
855+
851856
info!("Starting encryption operation on pool with UUID {pool_uuid}; may take a while");
852-
device.reencrypt_handle().reencrypt2::<()>(None, None)?;
857+
// The corresponding libcryptsetup call is device.reencrypt_handle().reencrypt2::<()>(None, None)?;
858+
run_encrypt(unencrypted_path)?;
859+
853860
Ok(())
854861
}
855862

@@ -894,31 +901,35 @@ impl CryptHandle {
894901

895902
/// Decrypt the crypt device for an encrypted pool.
896903
pub fn decrypt(&self, pool_uuid: PoolUuid) -> StratisResult<()> {
897-
let activation_name = format_crypt_backstore_name(&pool_uuid);
898-
let mut device = acquire_crypt_device(self.luks2_device_path())?;
899-
let (keyslot, key) = get_passphrase(&mut device, self.encryption_info())?
900-
.either(|(keyslot, _, key)| (keyslot, key), |tup| tup);
904+
{
905+
let activation_name = format_crypt_backstore_name(&pool_uuid);
906+
let mut device = acquire_crypt_device(self.luks2_device_path())?;
907+
let (keyslot, key) = get_passphrase(&mut device, self.encryption_info())?
908+
.either(|(keyslot, _, key)| (keyslot, key), |tup| tup);
909+
910+
device.reencrypt_handle().reencrypt_init_by_passphrase(
911+
Some(&activation_name.to_string()),
912+
key.as_ref(),
913+
Some(keyslot),
914+
None,
915+
None,
916+
CryptParamsReencrypt {
917+
mode: CryptReencryptModeInfo::Decrypt,
918+
direction: CryptReencryptDirectionInfo::Forward,
919+
resilience: "checksum".to_string(),
920+
hash: "sha256".to_string(),
921+
data_shift: 0,
922+
max_hotzone_size: 0,
923+
device_size: 0,
924+
luks2: None,
925+
flags: CryptReencrypt::empty(),
926+
},
927+
)?;
928+
}
901929

902-
device.reencrypt_handle().reencrypt_init_by_passphrase(
903-
Some(&activation_name.to_string()),
904-
key.as_ref(),
905-
Some(keyslot),
906-
None,
907-
None,
908-
CryptParamsReencrypt {
909-
mode: CryptReencryptModeInfo::Decrypt,
910-
direction: CryptReencryptDirectionInfo::Forward,
911-
resilience: "checksum".to_string(),
912-
hash: "sha256".to_string(),
913-
data_shift: 0,
914-
max_hotzone_size: 0,
915-
device_size: 0,
916-
luks2: None,
917-
flags: CryptReencrypt::empty(),
918-
},
919-
)?;
920930
info!("Starting decryption operation on pool with UUID {pool_uuid}; may take a while");
921-
device.reencrypt_handle().reencrypt2::<()>(None, None)?;
931+
// the corresponding libcryptsetup call is device.reencrypt_handle().reencrypt2::<()>(None, None)?;
932+
run_decrypt(self.luks2_device_path())?;
922933
Ok(())
923934
}
924935

src/engine/strat_engine/crypt/shared.rs

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,33 @@ pub fn handle_setup_reencrypt(
10971097
luks2_path: &Path,
10981098
encryption_info: &EncryptionInfo,
10991099
) -> StratisResult<(u32, SizedKeyMemory, u32)> {
1100+
fn set_up_reencryption_token(
1101+
device: &mut CryptDevice,
1102+
new_keyslot: u32,
1103+
ts: u32,
1104+
mut token_contents: Value,
1105+
) -> StratisResult<()> {
1106+
if let Some(obj) = token_contents.as_object_mut() {
1107+
let tokens = match obj.remove(TOKEN_KEYSLOTS_KEY) {
1108+
Some(Value::Array(mut v)) => {
1109+
v.push(Value::String(new_keyslot.to_string()));
1110+
Value::Array(v)
1111+
}
1112+
Some(_) | None => {
1113+
return Err(StratisError::Msg(format!(
1114+
"Could not find appropriate formatted value for {TOKEN_KEYSLOTS_KEY}"
1115+
)));
1116+
}
1117+
};
1118+
obj.insert(TOKEN_KEYSLOTS_KEY.to_string(), tokens);
1119+
}
1120+
device
1121+
.token_handle()
1122+
.json_set(TokenInput::ReplaceToken(ts, &token_contents))?;
1123+
1124+
Ok(())
1125+
}
1126+
11001127
let mut device = acquire_crypt_device(luks2_path)?;
11011128

11021129
let mut keys = get_all_passphrases(&mut device, encryption_info)?;
@@ -1109,7 +1136,7 @@ pub fn handle_setup_reencrypt(
11091136
let (single_ts, single_key) = keys
11101137
.pop()
11111138
.ok_or_else(|| StratisError::Msg("No unlock methods found".to_string()))?;
1112-
let mut single_token_contents = device.token_handle().json_get(single_ts)?;
1139+
let single_token_contents = device.token_handle().json_get(single_ts)?;
11131140
let single_keyslot = get_keyslot_number(&mut device, single_ts)?.ok_or_else(|| {
11141141
StratisError::Msg(format!(
11151142
"Could not find keyslot associated with token slot {single_ts}"
@@ -1122,16 +1149,13 @@ pub fn handle_setup_reencrypt(
11221149
single_key.as_ref(),
11231150
CryptVolumeKey::NO_SEGMENT,
11241151
)?;
1125-
if let Some(obj) = single_token_contents.as_object_mut() {
1126-
obj.remove(TOKEN_KEYSLOTS_KEY);
1127-
obj.insert(
1128-
TOKEN_KEYSLOTS_KEY.to_string(),
1129-
Value::Array(vec![Value::String(single_new_keyslot.to_string())]),
1130-
);
1131-
}
1132-
device
1133-
.token_handle()
1134-
.json_set(TokenInput::ReplaceToken(single_ts, &single_token_contents))?;
1152+
1153+
set_up_reencryption_token(
1154+
&mut device,
1155+
single_new_keyslot,
1156+
single_ts,
1157+
single_token_contents,
1158+
)?;
11351159

11361160
let mut new_vk = SafeMemHandle::alloc(STRATIS_MEK_SIZE)?;
11371161
device.volume_key_handle().get(
@@ -1141,24 +1165,15 @@ pub fn handle_setup_reencrypt(
11411165
)?;
11421166

11431167
for (ts, key) in other_keys {
1144-
let mut token_contents = device.token_handle().json_get(ts)?;
1168+
let token_contents = device.token_handle().json_get(ts)?;
11451169

11461170
let new_keyslot = device.keyslot_handle().add_by_key(
11471171
None,
11481172
Some(Either::Left(new_vk.as_ref())),
11491173
key.as_ref(),
11501174
CryptVolumeKey::NO_SEGMENT | CryptVolumeKey::DIGEST_REUSE,
11511175
)?;
1152-
if let Some(obj) = token_contents.as_object_mut() {
1153-
obj.remove(TOKEN_KEYSLOTS_KEY);
1154-
obj.insert(
1155-
TOKEN_KEYSLOTS_KEY.to_string(),
1156-
Value::Array(vec![Value::String(new_keyslot.to_string())]),
1157-
);
1158-
}
1159-
device
1160-
.token_handle()
1161-
.json_set(TokenInput::ReplaceToken(ts, &token_contents))?;
1176+
set_up_reencryption_token(&mut device, new_keyslot, ts, token_contents)?;
11621177
}
11631178

11641179
Ok((single_keyslot, single_key, single_new_keyslot))
@@ -1175,41 +1190,44 @@ pub fn handle_do_reencrypt(
11751190
single_key: SizedKeyMemory,
11761191
single_new_keyslot: u32,
11771192
) -> StratisResult<()> {
1178-
let mut device = acquire_crypt_device(luks2_path)?;
1179-
1180-
let cipher = device.status_handle().get_cipher()?;
1181-
let cipher_mode = device.status_handle().get_cipher_mode()?;
1182-
let sector_size = convert_int!(get_sector_size(Some(&mut device)), i32, u32)?;
1183-
device.reencrypt_handle().reencrypt_init_by_passphrase(
1184-
Some(device_name),
1185-
single_key.as_ref(),
1186-
Some(single_keyslot),
1187-
Some(single_new_keyslot),
1188-
Some((&cipher, &cipher_mode)),
1189-
CryptParamsReencrypt {
1190-
mode: CryptReencryptModeInfo::Reencrypt,
1191-
direction: CryptReencryptDirectionInfo::Forward,
1192-
resilience: "checksum".to_string(),
1193-
hash: "sha256".to_string(),
1194-
data_shift: 0,
1195-
max_hotzone_size: 0,
1196-
device_size: 0,
1197-
luks2: Some(CryptParamsLuks2 {
1198-
data_alignment: 0,
1199-
data_device: None,
1200-
integrity: None,
1201-
integrity_params: None,
1202-
pbkdf: None,
1203-
label: None,
1204-
sector_size,
1205-
subsystem: None,
1206-
}),
1207-
flags: CryptReencrypt::empty(),
1208-
},
1209-
)?;
1193+
{
1194+
let mut device = acquire_crypt_device(luks2_path)?;
1195+
1196+
let cipher = device.status_handle().get_cipher()?;
1197+
let cipher_mode = device.status_handle().get_cipher_mode()?;
1198+
let sector_size = convert_int!(get_sector_size(Some(&mut device)), i32, u32)?;
1199+
device.reencrypt_handle().reencrypt_init_by_passphrase(
1200+
Some(device_name),
1201+
single_key.as_ref(),
1202+
Some(single_keyslot),
1203+
Some(single_new_keyslot),
1204+
Some((&cipher, &cipher_mode)),
1205+
CryptParamsReencrypt {
1206+
mode: CryptReencryptModeInfo::Reencrypt,
1207+
direction: CryptReencryptDirectionInfo::Forward,
1208+
resilience: "checksum".to_string(),
1209+
hash: "sha256".to_string(),
1210+
data_shift: 0,
1211+
max_hotzone_size: 0,
1212+
device_size: 0,
1213+
luks2: Some(CryptParamsLuks2 {
1214+
data_alignment: 0,
1215+
data_device: None,
1216+
integrity: None,
1217+
integrity_params: None,
1218+
pbkdf: None,
1219+
label: None,
1220+
sector_size,
1221+
subsystem: None,
1222+
}),
1223+
flags: CryptReencrypt::empty(),
1224+
},
1225+
)?;
1226+
}
12101227

12111228
info!("Starting reencryption operation on pool with UUID {pool_uuid}; may take a while");
1212-
device.reencrypt_handle().reencrypt2::<()>(None, None)?;
1229+
// The corresponding libcryptsetup call is device.reencrypt_handle().reencrypt2::<()>(None, None)?;
1230+
cmd::run_reencrypt(luks2_path)?;
12131231

12141232
Ok(())
12151233
}

0 commit comments

Comments
 (0)