use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use crate::af::{LUKS1_AF_STRIPES, Luks2Af};
use crate::hash::Luks2HashAlg;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct KeySlotId(String);
impl KeySlotId {
pub fn new<S: Into<String>>(s: S) -> Self {
KeySlotId(s.into())
}
}
impl From<u32> for KeySlotId {
fn from(n: u32) -> Self {
KeySlotId(n.to_string())
}
}
impl From<String> for KeySlotId {
fn from(s: String) -> Self {
KeySlotId(s)
}
}
impl From<&str> for KeySlotId {
fn from(s: &str) -> Self {
KeySlotId(s.to_string())
}
}
impl fmt::Display for KeySlotId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for KeySlotId {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(KeySlotId(s.to_string()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(into = "u64", try_from = "u64")]
pub enum Luks2KeySize {
Size32 = 32,
Size64 = 64,
}
impl From<Luks2KeySize> for u64 {
fn from(val: Luks2KeySize) -> Self {
val as u64
}
}
impl fmt::Display for Luks2KeySize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Luks2KeySize::Size32 => write!(f, "32"),
Luks2KeySize::Size64 => write!(f, "64"),
}
}
}
impl TryFrom<u64> for Luks2KeySize {
type Error = String;
fn try_from(val: u64) -> Result<Self, Self::Error> {
match val {
32 => Ok(Luks2KeySize::Size32),
64 => Ok(Luks2KeySize::Size64),
_ => Err(format!("Unsupported key size: {}", val)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Luks2AreaEncryption {
#[serde(rename = "aes-xts-plain64")]
AesXtsPlain64,
}
impl fmt::Display for Luks2AreaEncryption {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Luks2AreaEncryption::AesXtsPlain64 => write!(f, "aes-xts-plain64"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum Luks2Area {
Raw {
encryption: Luks2AreaEncryption,
key_size: Luks2KeySize,
offset: crate::Luks2U64,
size: crate::Luks2U64,
},
None {
offset: crate::Luks2U64,
size: crate::Luks2U64,
},
Journal {
offset: crate::Luks2U64,
size: crate::Luks2U64,
},
Checksum {
offset: crate::Luks2U64,
size: crate::Luks2U64,
hash: Luks2HashAlg,
sector_size: u32,
},
Datashift {
offset: crate::Luks2U64,
size: crate::Luks2U64,
shift_size: crate::Luks2U64,
},
#[serde(rename = "datashift-journal")]
DatashiftJournal {
offset: crate::Luks2U64,
size: crate::Luks2U64,
shift_size: crate::Luks2U64,
},
#[serde(rename = "datashift-checksum")]
DatashiftChecksum {
offset: crate::Luks2U64,
size: crate::Luks2U64,
hash: Luks2HashAlg,
sector_size: u32,
shift_size: crate::Luks2U64,
},
}
impl Luks2Area {
pub fn offset(&self) -> u64 {
match self {
Luks2Area::Raw { offset, .. } => offset.0,
Luks2Area::None { offset, .. } => offset.0,
Luks2Area::Journal { offset, .. } => offset.0,
Luks2Area::Checksum { offset, .. } => offset.0,
Luks2Area::Datashift { offset, .. } => offset.0,
Luks2Area::DatashiftJournal { offset, .. } => offset.0,
Luks2Area::DatashiftChecksum { offset, .. } => offset.0,
}
}
pub fn size(&self) -> u64 {
match self {
Luks2Area::Raw { size, .. } => size.0,
Luks2Area::None { size, .. } => size.0,
Luks2Area::Journal { size, .. } => size.0,
Luks2Area::Checksum { size, .. } => size.0,
Luks2Area::Datashift { size, .. } => size.0,
Luks2Area::DatashiftJournal { size, .. } => size.0,
Luks2Area::DatashiftChecksum { size, .. } => size.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(try_from = "i32", into = "i32")]
pub enum Luks2KeyslotPriority {
Ignore = 0,
Normal = 1,
High = 2,
}
impl TryFrom<i32> for Luks2KeyslotPriority {
type Error = String;
fn try_from(val: i32) -> Result<Self, Self::Error> {
match val {
0 => Ok(Luks2KeyslotPriority::Ignore),
1 => Ok(Luks2KeyslotPriority::Normal),
2 => Ok(Luks2KeyslotPriority::High),
_ => Err(format!("Unsupported keyslot priority: {}", val)),
}
}
}
impl From<Luks2KeyslotPriority> for i32 {
fn from(val: Luks2KeyslotPriority) -> Self {
val as i32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Luks2ReencryptMode {
Reencrypt,
Encrypt,
Decrypt,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Luks2ReencryptDirection {
Forward,
Backward,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum Luks2Keyslot {
Luks2 {
key_size: Luks2KeySize,
priority: Option<Luks2KeyslotPriority>,
af: Luks2Af,
area: Luks2Area,
kdf: crate::kdf::Luks2Kdf,
},
Reencrypt {
mode: Luks2ReencryptMode,
direction: Luks2ReencryptDirection,
key_size: String,
priority: Option<Luks2KeyslotPriority>,
af: Luks2Af,
area: Luks2Area,
kdf: crate::kdf::Luks2Kdf,
},
}
impl Luks2Keyslot {
pub fn area(&self) -> &Luks2Area {
match self {
Luks2Keyslot::Luks2 { area, .. } => area,
Luks2Keyslot::Reencrypt { area, .. } => area,
}
}
pub fn validate(&self) -> Result<(), String> {
match self {
Luks2Keyslot::Luks2 { area, af, .. } => {
if !matches!(area, Luks2Area::Raw { .. }) {
return Err("LUKS2 keyslot must have area type 'raw'".to_string());
}
if af.stripes != LUKS1_AF_STRIPES {
return Err(format!("AF stripes must be {}", LUKS1_AF_STRIPES));
}
}
Luks2Keyslot::Reencrypt {
area, key_size, af, ..
} => {
if matches!(area, Luks2Area::Raw { .. }) {
return Err("Reencrypt keyslot cannot have area type 'raw'".to_string());
}
if key_size != "1" {
return Err("Reencrypt keyslot must have key_size 1".to_string());
}
if af.stripes != LUKS1_AF_STRIPES {
return Err(format!("AF stripes must be {}", LUKS1_AF_STRIPES));
}
}
}
Ok(())
}
}
pub(crate) fn deserialize_and_validate_keyslots<'de, D>(
deserializer: D,
) -> Result<HashMap<KeySlotId, Luks2Keyslot>, D::Error>
where
D: serde::Deserializer<'de>,
{
let keyslots: HashMap<KeySlotId, Luks2Keyslot> = HashMap::deserialize(deserializer)?;
for (id, slot) in &keyslots {
slot.validate()
.map_err(|e| serde::de::Error::custom(format!("Validation failed for keyslot {}: {}", id, e)))?;
}
Ok(keyslots)
}