Formatação (cargo fmt) e ajustes do clippy para o CI da 1.0.0
Release / Cria a release (push) Canceled after 0s
Release / CLI (linux-aarch64) (push) Canceled after 0s
Release / CLI (linux-x86_64) (push) Canceled after 0s
Release / CLI (macos-aarch64) (push) Canceled after 0s
Release / CLI (macos-x86_64) (push) Canceled after 0s
Release / CLI (windows-x86_64) (push) Canceled after 0s
Release / App desktop (linux-x86_64) (push) Canceled after 0s
Release / App desktop (macos-aarch64) (push) Canceled after 0s
Release / App desktop (macos-x86_64) (push) Canceled after 0s
Release / App desktop (windows-x86_64) (push) Canceled after 0s
Release / Publica a release (push) Canceled after 0s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Opus 5.5 committed 2026-10-19 11:41:58 -03:00
1 parent 74218e7beb
commit beb83fef92
49 files changed
+3806 -854

No files matched your search

+74 -15
View File
@@ -16,8 +16,12 @@ fn le64(b: &[u8], o: usize) -> u64 {
u64::from_le_bytes(b[o..o + 8].try_into().unwrap())
}
const GUID_NORMAL: [u8; 16] = [0x3b, 0xd6, 0x67, 0x49, 0x29, 0x2e, 0xd8, 0x4a, 0x83, 0x99, 0xf6, 0xa3, 0x39, 0xe3, 0xd0, 0x01];
const GUID_EOW: [u8; 16] = [0x3b, 0x4d, 0xa8, 0x92, 0x80, 0xdd, 0x0e, 0x4d, 0x9e, 0x4e, 0xb1, 0xe3, 0x28, 0x4e, 0xae, 0xd8];
const GUID_NORMAL: [u8; 16] = [
0x3b, 0xd6, 0x67, 0x49, 0x29, 0x2e, 0xd8, 0x4a, 0x83, 0x99, 0xf6, 0xa3, 0x39, 0xe3, 0xd0, 0x01,
];
const GUID_EOW: [u8; 16] = [
0x3b, 0x4d, 0xa8, 0x92, 0x80, 0xdd, 0x0e, 0x4d, 0x9e, 0x4e, 0xb1, 0xe3, 0x28, 0x4e, 0xae, 0xd8,
];
/// Posição da tabela de deslocamentos FVE, se for BitLocker (normal ou To Go).
fn offsets_pos(vbr: &[u8]) -> Option<usize> {
@@ -45,7 +49,9 @@ const RA: [u32; 4] = [9, 0, 13, 0];
const RB: [u32; 4] = [0, 10, 0, 25];
fn words(buf: &[u8]) -> Vec<u32> {
buf.chunks_exact(4).map(|c| u32::from_le_bytes(c.try_into().unwrap())).collect()
buf.chunks_exact(4)
.map(|c| u32::from_le_bytes(c.try_into().unwrap()))
.collect()
}
// Decifragem (ordem do dm-crypt do Linux): somas em ordem crescente.
@@ -219,7 +225,11 @@ impl Vmk {
self.nested.iter().find(|e| e.vtype == 5)
}
fn stretch_salt(&self) -> Option<&[u8]> {
self.nested.iter().find(|e| e.vtype == 3).filter(|e| e.data.len() >= 20).map(|e| &e.data[4..20])
self.nested
.iter()
.find(|e| e.vtype == 3)
.filter(|e| e.data.len() >= 20)
.map(|e| &e.data[4..20])
}
}
@@ -366,8 +376,13 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
}
}
}
let fve = fve.ok_or_else(|| UnlockError::Unsupported("metadados FVE ilegíveis ou do Windows Vista".into()))?;
let fvek_entry = fve.fvek.as_ref().ok_or_else(|| UnlockError::Corrupt("sem FVEK".into()))?;
let fve = fve.ok_or_else(|| {
UnlockError::Unsupported("metadados FVE ilegíveis ou do Windows Vista".into())
})?;
let fvek_entry = fve
.fvek
.as_ref()
.ok_or_else(|| UnlockError::Corrupt("sem FVEK".into()))?;
let mut volume_key: Option<Vec<u8>> = None;
for s in secrets {
@@ -393,7 +408,8 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
k
} else {
let vmk = vmk_found.ok_or(UnlockError::WrongSecret)?;
decrypt_key_entry(&vmk, fvek_entry).ok_or_else(|| UnlockError::Corrupt("FVEK não decifra com a VMK".into()))?
decrypt_key_entry(&vmk, fvek_entry)
.ok_or_else(|| UnlockError::Corrupt("FVEK não decifra com a VMK".into()))?
};
let k = &fvek_bytes;
let bad = || UnlockError::Corrupt("tamanho de chave incompatível com o método".into());
@@ -408,7 +424,11 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
key: AesAny::new(&k[..n]).ok_or_else(bad)?,
tweak: AesAny::new(&k[32..32 + n]).ok_or_else(bad)?,
},
if n == 16 { "AES-128-CBC + difusor" } else { "AES-256-CBC + difusor" },
if n == 16 {
"AES-128-CBC + difusor"
} else {
"AES-256-CBC + difusor"
},
)
}
0x8002 | 0x8003 => {
@@ -417,35 +437,69 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
return Err(bad());
}
let key = AesAny::new(&k[..n]).ok_or_else(bad)?;
(SectorCipher::Cbc { key: key.clone(), iv: Iv::Eboiv(key) }, if n == 16 { "AES-128-CBC" } else { "AES-256-CBC" })
(
SectorCipher::Cbc {
key: key.clone(),
iv: Iv::Eboiv(key),
},
if n == 16 {
"AES-128-CBC"
} else {
"AES-256-CBC"
},
)
}
0x8004 | 0x8005 => {
let n = if fve.method == 0x8004 { 16 } else { 32 };
if k.len() < 2 * n {
return Err(bad());
}
let iv = if bps > 512 { Iv::Unit(bps) } else { Iv::Plain64 };
let iv = if bps > 512 {
Iv::Unit(bps)
} else {
Iv::Plain64
};
(
SectorCipher::Xts {
k1: AesAny::new(&k[..n]).ok_or_else(bad)?,
k2: AesAny::new(&k[n..2 * n]).ok_or_else(bad)?,
iv,
},
if n == 16 { "AES-128-XTS" } else { "AES-256-XTS" },
if n == 16 {
"AES-128-XTS"
} else {
"AES-256-XTS"
},
)
}
m => return Err(UnlockError::Unsupported(format!("método de cifragem BitLocker {m:#x}"))),
m => {
return Err(UnlockError::Unsupported(format!(
"método de cifragem BitLocker {m:#x}"
)))
}
};
let len = src.size();
let mut cs = CryptSource::new(src.clone(), cipher, 0, len, bps);
let mut patches = vec![];
// Áreas de metadados FVE: o Windows não as expõe como dados.
for off in meta_offsets {
patches.push(Patch { at: off, len: fve.meta_size, from: 0, decrypt_from: None, zero: true });
patches.push(Patch {
at: off,
len: fve.meta_size,
from: 0,
decrypt_from: None,
zero: true,
});
}
if fve.volume_header_size > 0 && fve.volume_header_offset > 0 {
// O início do volume original foi movido para outro lugar e cifrado lá.
patches.push(Patch { at: fve.volume_header_offset, len: fve.volume_header_size, from: 0, decrypt_from: None, zero: true });
patches.push(Patch {
at: fve.volume_header_offset,
len: fve.volume_header_size,
from: 0,
decrypt_from: None,
zero: true,
});
patches.push(Patch {
at: 0,
len: fve.volume_header_size,
@@ -465,6 +519,11 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
})
}
pub fn unlock_window(base: Arc<dyn Source>, start: u64, len: u64, secrets: &[Secret]) -> UnlockResult {
pub fn unlock_window(
base: Arc<dyn Source>,
start: u64,
len: u64,
secrets: &[Secret],
) -> UnlockResult {
unlock(Arc::new(SubSource::new(base, start, len)), secrets)
}
+39 -11
View File
@@ -80,12 +80,18 @@ fn plist_value(xml: &str, key: &str) -> Option<String> {
while let Some(p) = xml[from..].find(&k) {
let rest = &xml[from + p + k.len()..];
from += p + k.len();
let Some(open) = rest.find('<').filter(|&o| o == 0) else { continue };
let Some(gt) = rest[open..].find('>') else { continue };
let Some(open) = rest.find('<').filter(|&o| o == 0) else {
continue;
};
let Some(gt) = rest[open..].find('>') else {
continue;
};
if rest[open..open + gt].ends_with('/') {
continue; // <reference .../>
}
let Some(te) = rest[open + 1..].find(|c: char| c == ' ' || c == '>') else { continue };
let Some(te) = rest[open + 1..].find([' ', '>']) else {
continue;
};
let tag = &rest[open + 1..open + 1 + te];
let vstart = open + gt + 1;
if let Some(e) = rest[vstart..].find(&format!("</{tag}>")) {
@@ -116,20 +122,31 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
let block_size = le32(&h, 96) as u64;
let disklbl = le64(&h, 104);
if block_size == 0 || block_size > 1 << 20 || le32(&h, 168) != 16 {
return Err(UnlockError::Unsupported("Core Storage com chave de metadados que não é AES-128".into()));
return Err(UnlockError::Unsupported(
"Core Storage com chave de metadados que não é AES-128".into(),
));
}
let mut md_key = [0u8; 32];
md_key[..16].copy_from_slice(&h[176..192]);
md_key[16..].copy_from_slice(&h[304..320]);
let (k1, k2) = (AesAny::new(&md_key[..16]).unwrap(), AesAny::new(&md_key[16..]).unwrap());
let md_cipher = SectorCipher::Xts { k1, k2, iv: Iv::Plain64 };
let (k1, k2) = (
AesAny::new(&md_key[..16]).unwrap(),
AesAny::new(&md_key[16..]).unwrap(),
);
let md_cipher = SectorCipher::Xts {
k1,
k2,
iv: Iv::Plain64,
};
let label = read_vec(src.as_ref(), disklbl * block_size, MD_BLOCK).map_err(|e| corrupt(&e.to_string()))?;
let label = read_vec(src.as_ref(), disklbl * block_size, MD_BLOCK)
.map_err(|e| corrupt(&e.to_string()))?;
if label.len() < MD_BLOCK {
return Err(corrupt("rótulo do disco truncado"));
}
let vgd_off = le32(&label, 220) as u64;
let vgd = read_vec(src.as_ref(), disklbl * block_size + vgd_off, 40).map_err(|e| corrupt(&e.to_string()))?;
let vgd = read_vec(src.as_ref(), disklbl * block_size + vgd_off, 40)
.map_err(|e| corrupt(&e.to_string()))?;
if vgd.len() < 40 {
return Err(corrupt("descritor de grupos truncado"));
}
@@ -141,7 +158,8 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
let mut family = [0u8; 16];
let mut got = 0u8;
for i in 0..md_blocks.min(64) {
let mut b = read_vec(src.as_ref(), md_start + i * MD_BLOCK as u64, MD_BLOCK).map_err(|e| corrupt(&e.to_string()))?;
let mut b = read_vec(src.as_ref(), md_start + i * MD_BLOCK as u64, MD_BLOCK)
.map_err(|e| corrupt(&e.to_string()))?;
if b.len() < MD_BLOCK || b.iter().all(|&x| x == 0) {
break;
}
@@ -192,7 +210,12 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
let finish = |vk16: [u8; 16]| -> UnlockResult {
let mut full = [0u8; 32];
full[..16].copy_from_slice(&vk16);
full[16..].copy_from_slice(&Sha256::new().chain_update(vk16).chain_update(family).finalize()[..16]);
full[16..].copy_from_slice(
&Sha256::new()
.chain_update(vk16)
.chain_update(family)
.finalize()[..16],
);
let cipher = SectorCipher::Xts {
k1: AesAny::new(&full[..16]).unwrap(),
k2: AesAny::new(&full[16..]).unwrap(),
@@ -228,6 +251,11 @@ pub fn unlock(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
Err(UnlockError::WrongSecret)
}
pub fn unlock_window(base: Arc<dyn Source>, start: u64, len: u64, secrets: &[Secret]) -> UnlockResult {
pub fn unlock_window(
base: Arc<dyn Source>,
start: u64,
len: u64,
secrets: &[Secret],
) -> UnlockResult {
unlock(Arc::new(SubSource::new(base, start, len)), secrets)
}
+79 -23
View File
@@ -89,9 +89,13 @@ pub fn af_merge(src: &[u8], block: usize, stripes: usize, h: Hash) -> Vec<u8> {
/// Cifra descrita por texto como "aes-xts-plain64" ou "aes-cbc-essiv:sha256".
pub fn parse_cipher(spec: &str, key: &[u8]) -> std::result::Result<SectorCipher, UnlockError> {
let (alg, rest) = spec.split_once('-').ok_or_else(|| UnlockError::Unsupported(spec.into()))?;
let (alg, rest) = spec
.split_once('-')
.ok_or_else(|| UnlockError::Unsupported(spec.into()))?;
if !alg.eq_ignore_ascii_case("aes") {
return Err(UnlockError::Unsupported(format!("cifra {alg} (apenas AES)")));
return Err(UnlockError::Unsupported(format!(
"cifra {alg} (apenas AES)"
)));
}
let (mode, ivs) = rest.split_once('-').unwrap_or((rest, "plain"));
let bad = || UnlockError::Unsupported(format!("chave inválida para {spec}"));
@@ -102,7 +106,8 @@ pub fn parse_cipher(spec: &str, key: &[u8]) -> std::result::Result<SectorCipher,
"plain" | "plain32" => Iv::Plain32,
"plain64be" => Iv::Plain64Be,
s if s.starts_with("essiv:") => {
let h = Hash::parse(&s[6..]).ok_or_else(|| UnlockError::Unsupported(spec.into()))?;
let h =
Hash::parse(&s[6..]).ok_or_else(|| UnlockError::Unsupported(spec.into()))?;
Iv::Essiv(AesAny::new(&h.digest(&[k])).ok_or_else(bad)?)
}
_ => return Err(UnlockError::Unsupported(format!("IV {ivs}"))),
@@ -181,7 +186,8 @@ fn unlock_v1(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
}
let cipher_name = cstr(&h[8..40]);
let cipher_mode = cstr(&h[40..72]);
let hash = Hash::parse(&cstr(&h[72..104])).ok_or_else(|| UnlockError::Unsupported(format!("hash {}", cstr(&h[72..104]))))?;
let hash = Hash::parse(&cstr(&h[72..104]))
.ok_or_else(|| UnlockError::Unsupported(format!("hash {}", cstr(&h[72..104]))))?;
let payload = be32(&h, 104) as u64 * 512;
let key_bytes = be32(&h, 108) as usize;
let mk_digest = &h[112..132];
@@ -221,7 +227,9 @@ fn unlock_v1(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
continue;
}
let km_len = (key_bytes * stripes).div_ceil(512) * 512;
let Ok(mut km) = read_vec(src.as_ref(), km_off, km_len) else { continue };
let Ok(mut km) = read_vec(src.as_ref(), km_off, km_len) else {
continue;
};
if km.len() < km_len {
continue;
}
@@ -284,25 +292,38 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
}
let hdr_size = u64::from_be_bytes(h[8..16].try_into().unwrap());
if !(16384..=(4 << 20)).contains(&hdr_size) {
return Err(UnlockError::Corrupt("tamanho de cabeçalho LUKS2 inválido".into()));
return Err(UnlockError::Corrupt(
"tamanho de cabeçalho LUKS2 inválido".into(),
));
}
let area = read_vec(src.as_ref(), 4096, (hdr_size - 4096) as usize).map_err(|e| UnlockError::Corrupt(e.to_string()))?;
let area = read_vec(src.as_ref(), 4096, (hdr_size - 4096) as usize)
.map_err(|e| UnlockError::Corrupt(e.to_string()))?;
let end = area.iter().position(|&c| c == 0).unwrap_or(area.len());
let json: serde_json::Value = serde_json::from_slice(&area[..end]).map_err(|e| UnlockError::Corrupt(format!("JSON: {e}")))?;
let segments = json.get("segments").and_then(|s| s.as_object()).ok_or_else(|| UnlockError::Corrupt("sem segments".into()))?;
let json: serde_json::Value = serde_json::from_slice(&area[..end])
.map_err(|e| UnlockError::Corrupt(format!("JSON: {e}")))?;
let segments = json
.get("segments")
.and_then(|s| s.as_object())
.ok_or_else(|| UnlockError::Corrupt("sem segments".into()))?;
let (_, seg) = segments
.iter()
.find(|(_, s)| jstr(s, "type") == Some("crypt"))
.ok_or_else(|| UnlockError::Unsupported("sem segmento cifrado".into()))?;
let seg_off = jnum(seg, "offset").ok_or_else(|| UnlockError::Corrupt("offset do segmento".into()))?;
let seg_off =
jnum(seg, "offset").ok_or_else(|| UnlockError::Corrupt("offset do segmento".into()))?;
let seg_len = match seg.get("size").and_then(|s| s.as_str()) {
Some("dynamic") | None => src.size().saturating_sub(seg_off),
Some(n) => n.parse().unwrap_or(0),
};
let seg_enc = jstr(seg, "encryption").unwrap_or("aes-xts-plain64").to_string();
let seg_enc = jstr(seg, "encryption")
.unwrap_or("aes-xts-plain64")
.to_string();
let sector = jnum(seg, "sector_size").unwrap_or(512);
let iv_tweak = jnum(seg, "iv_tweak").unwrap_or(0);
let digests = json.get("digests").and_then(|d| d.as_object()).ok_or_else(|| UnlockError::Corrupt("sem digests".into()))?;
let digests = json
.get("digests")
.and_then(|d| d.as_object())
.ok_or_else(|| UnlockError::Corrupt("sem digests".into()))?;
let check = |vk: &[u8]| -> bool {
for (_, d) in digests {
@@ -325,7 +346,8 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
};
let finish = |vk: Vec<u8>| -> UnlockResult {
let cipher = parse_cipher(&seg_enc, &vk)?;
let cs = CryptSource::new(src.clone(), cipher, seg_off, seg_len, sector).with_iv_tweak(iv_tweak);
let cs =
CryptSource::new(src.clone(), cipher, seg_off, seg_len, sector).with_iv_tweak(iv_tweak);
Ok(Unlocked {
source: Arc::new(cs),
description: format!("LUKS2 ({seg_enc})"),
@@ -338,7 +360,10 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
}
}
let pws = passwords(secrets);
let slots = json.get("keyslots").and_then(|d| d.as_object()).ok_or_else(|| UnlockError::Corrupt("sem keyslots".into()))?;
let slots = json
.get("keyslots")
.and_then(|d| d.as_object())
.ok_or_else(|| UnlockError::Corrupt("sem keyslots".into()))?;
let mut unsupported: Option<String> = None;
for (_, slot) in slots {
if jstr(slot, "type") != Some("luks2") {
@@ -347,10 +372,20 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
let key_size = jnum(slot, "key_size").unwrap_or(0) as usize;
let af = slot.get("af");
let stripes = af.and_then(|a| jnum(a, "stripes")).unwrap_or(4000) as usize;
let af_hash = af.and_then(|a| jstr(a, "hash")).and_then(Hash::parse).unwrap_or(Hash::Sha256);
let af_hash = af
.and_then(|a| jstr(a, "hash"))
.and_then(Hash::parse)
.unwrap_or(Hash::Sha256);
let ar = slot.get("area");
let (Some(a_off), Some(a_size)) = (ar.and_then(|a| jnum(a, "offset")), ar.and_then(|a| jnum(a, "size"))) else { continue };
let a_enc = ar.and_then(|a| jstr(a, "encryption")).unwrap_or("aes-xts-plain64");
let (Some(a_off), Some(a_size)) = (
ar.and_then(|a| jnum(a, "offset")),
ar.and_then(|a| jnum(a, "size")),
) else {
continue;
};
let a_enc = ar
.and_then(|a| jstr(a, "encryption"))
.unwrap_or("aes-xts-plain64");
let a_ksize = ar.and_then(|a| jnum(a, "key_size")).unwrap_or(64) as usize;
let kdf = slot.get("kdf");
let Some(kdf) = kdf else { continue };
@@ -359,18 +394,31 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
if key_size == 0 || stripes == 0 || km_len as u64 > a_size {
continue;
}
let Ok(km) = read_vec(src.as_ref(), a_off, km_len) else { continue };
let Ok(km) = read_vec(src.as_ref(), a_off, km_len) else {
continue;
};
if km.len() < km_len {
continue;
}
for pw in &pws {
let dk: Vec<u8> = match jstr(kdf, "type") {
Some("pbkdf2") => {
let hh = jstr(kdf, "hash").and_then(Hash::parse).unwrap_or(Hash::Sha256);
hh.pbkdf2(pw, &salt, jnum(kdf, "iterations").unwrap_or(1000) as u32, a_ksize)
let hh = jstr(kdf, "hash")
.and_then(Hash::parse)
.unwrap_or(Hash::Sha256);
hh.pbkdf2(
pw,
&salt,
jnum(kdf, "iterations").unwrap_or(1000) as u32,
a_ksize,
)
}
Some(t @ ("argon2i" | "argon2id")) => {
let alg = if t == "argon2i" { argon2::Algorithm::Argon2i } else { argon2::Algorithm::Argon2id };
let alg = if t == "argon2i" {
argon2::Algorithm::Argon2i
} else {
argon2::Algorithm::Argon2id
};
let params = match argon2::Params::new(
jnum(kdf, "memory").unwrap_or(65536) as u32,
jnum(kdf, "time").unwrap_or(4) as u32,
@@ -384,7 +432,10 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
}
};
let mut out = vec![0u8; a_ksize];
if argon2::Argon2::new(alg, argon2::Version::V0x13, params).hash_password_into(pw, &salt, &mut out).is_err() {
if argon2::Argon2::new(alg, argon2::Version::V0x13, params)
.hash_password_into(pw, &salt, &mut out)
.is_err()
{
continue;
}
out
@@ -413,6 +464,11 @@ fn unlock_v2(src: Arc<dyn Source>, secrets: &[Secret]) -> UnlockResult {
}
/// Atalho: abre uma janela da origem como LUKS.
pub fn unlock_window(base: Arc<dyn Source>, start: u64, len: u64, secrets: &[Secret]) -> UnlockResult {
pub fn unlock_window(
base: Arc<dyn Source>,
start: u64,
len: u64,
secrets: &[Secret],
) -> UnlockResult {
unlock(Arc::new(SubSource::new(base, start, len)), secrets)
}
+25 -6
View File
@@ -98,10 +98,20 @@ impl Iv {
/// Cifra aplicada setor a setor.
#[derive(Clone)]
pub enum SectorCipher {
Xts { k1: AesAny, k2: AesAny, iv: Iv },
Cbc { key: AesAny, iv: Iv },
Xts {
k1: AesAny,
k2: AesAny,
iv: Iv,
},
Cbc {
key: AesAny,
iv: Iv,
},
/// BitLocker AES-CBC com difusor Elephant (chave de dados + chave de ajuste).
Elephant { key: AesAny, tweak: AesAny },
Elephant {
key: AesAny,
tweak: AesAny,
},
}
fn gf_double(t: &mut [u8; 16]) {
@@ -200,7 +210,13 @@ pub struct Patch {
}
impl CryptSource {
pub fn new(inner: Arc<dyn Source>, cipher: SectorCipher, data_offset: u64, len: u64, sector: u64) -> Self {
pub fn new(
inner: Arc<dyn Source>,
cipher: SectorCipher,
data_offset: u64,
len: u64,
sector: u64,
) -> Self {
let sector = sector.max(512);
Self {
inner,
@@ -245,7 +261,8 @@ impl CryptSource {
}
crate::source::read_exact_at(self.inner.as_ref(), self.data_offset + rel, out)?;
if rel < self.plain_from {
self.cipher.decrypt_sector(out, rel / 512 + self.iv_tweak, rel);
self.cipher
.decrypt_sector(out, rel / 512 + self.iv_tweak, rel);
}
Ok(())
}
@@ -342,5 +359,7 @@ pub fn from_hex(s: &str) -> Option<Vec<u8>> {
if s.len() % 2 != 0 {
return None;
}
(0..s.len() / 2).map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).ok()).collect()
(0..s.len() / 2)
.map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).ok())
.collect()
}