1use aes::cipher::{BlockCipherDecrypt, KeyInit};
11use sha2::Digest;
12
13use crate::xlsx::cfb::Compound;
14use crate::xlsx::xml::El;
15
16const MAX_SPIN_COUNT: u32 = 10_000_000;
18const STANDARD_SPIN_COUNT: u32 = 50_000;
20pub(crate) const SEGMENT: usize = 4096;
22const BLOCK: usize = 16;
24
25pub(crate) const BLOCK_VERIFIER_INPUT: [u8; 8] = [0xFE, 0xA7, 0xD2, 0x76, 0x3B, 0x4B, 0x9E, 0x79];
27pub(crate) const BLOCK_VERIFIER_HASH: [u8; 8] = [0xD7, 0xAA, 0x0F, 0x6D, 0x30, 0x61, 0x34, 0x4E];
28pub(crate) const BLOCK_KEY: [u8; 8] = [0x14, 0x6E, 0x0B, 0xE7, 0xAB, 0xAC, 0xD0, 0xD6];
29pub(crate) const BLOCK_HMAC_KEY: [u8; 8] = [0x5F, 0xB2, 0xAD, 0x01, 0x0C, 0xB9, 0xE1, 0xF6];
31pub(crate) const BLOCK_HMAC_VALUE: [u8; 8] = [0xA0, 0x67, 0x7F, 0x02, 0xB2, 0x2C, 0x84, 0x33];
32
33pub(crate) const PASSWORD_ENCRYPTOR: &str = "http://schemas.microsoft.com/office/2006/keyEncryptor/password";
35
36pub(crate) const F_CRYPTOAPI: u32 = 0x04;
38const F_EXTERNAL: u32 = 0x10;
39pub(crate) const F_AES: u32 = 0x20;
40
41#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
43pub enum DecryptError {
44 #[error("the password is incorrect")]
45 WrongPassword,
46 #[error("{0} encryption isn't supported")]
48 Unsupported(String),
49 #[error("the encrypted file is damaged ({0})")]
50 Malformed(String),
51}
52
53fn malformed(m: impl Into<String>) -> DecryptError {
54 DecryptError::Malformed(m.into())
55}
56
57pub fn decrypt(bytes: &[u8], password: &str) -> Result<Vec<u8>, DecryptError> {
59 let (info, package) = streams(bytes)?;
60 decrypt_streams(&info, &package, password)
61}
62
63pub fn check_encryption(bytes: &[u8]) -> Result<(), DecryptError> {
67 let (info, _) = streams(bytes)?;
68 match scheme(&info)? {
69 Scheme::Agile(xml) => agile_descriptor(xml).map(|_| ()),
70 Scheme::Standard(info) => standard_header(info).map(|_| ()),
71 }
72}
73
74fn streams(bytes: &[u8]) -> Result<(Vec<u8>, Vec<u8>), DecryptError> {
77 let cf = Compound::open(bytes).map_err(malformed)?;
78 let info = cf.root_stream("EncryptionInfo").map_err(malformed)?;
79 let package = cf.root_stream("EncryptedPackage").map_err(malformed)?.ok_or_else(|| malformed("no EncryptedPackage stream"))?;
80 let info = info.ok_or_else(|| DecryptError::Unsupported("rights-management".into()))?;
81 Ok((info, package))
82}
83
84pub(crate) fn decrypt_streams(info: &[u8], package: &[u8], password: &str) -> Result<Vec<u8>, DecryptError> {
86 match scheme(info)? {
87 Scheme::Agile(xml) => agile(xml, package, password),
88 Scheme::Standard(info) => standard(info, package, password),
89 }
90}
91
92enum Scheme<'a> {
94 Agile(&'a [u8]),
96 Standard(&'a [u8]),
98}
99
100fn scheme(info: &[u8]) -> Result<Scheme<'_>, DecryptError> {
101 let short = || malformed("truncated EncryptionInfo");
102 let major = u16_at(info, 0).ok_or_else(short)?;
103 let minor = u16_at(info, 2).ok_or_else(short)?;
104 match (major, minor) {
105 (4, 4) => Ok(Scheme::Agile(info.get(8..).ok_or_else(short)?)),
106 (2..=4, 2) => Ok(Scheme::Standard(info.get(4..).ok_or_else(short)?)),
107 (3 | 4, 3) => Err(DecryptError::Unsupported("Extensible".into())),
108 _ => Err(DecryptError::Unsupported(format!("version {major}.{minor}"))),
109 }
110}
111
112fn u16_at(b: &[u8], at: usize) -> Option<u16> {
113 let s = b.get(at..at.checked_add(2)?)?;
114 Some(u16::from_le_bytes([*s.first()?, *s.get(1)?]))
115}
116
117fn u32_at(b: &[u8], at: usize) -> Option<u32> {
118 let s = b.get(at..at.checked_add(4)?)?;
119 Some(u32::from_le_bytes([*s.first()?, *s.get(1)?, *s.get(2)?, *s.get(3)?]))
120}
121
122fn package_parts(package: &[u8]) -> Result<(usize, &[u8]), DecryptError> {
125 let head: [u8; 8] = package.get(..8).and_then(|h| h.try_into().ok()).ok_or_else(|| malformed("truncated EncryptedPackage"))?;
126 let data = package.get(8..).unwrap_or_default();
127 let size = usize::try_from(u64::from_le_bytes(head)).unwrap_or(usize::MAX);
128 if size > data.len() {
129 return Err(malformed("the package is shorter than its stated size"));
130 }
131 Ok((size, data))
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137pub(crate) enum Hash {
138 Sha1,
139 Sha256,
140 Sha384,
141 Sha512,
142}
143
144impl Hash {
145 fn from_name(name: &str) -> Result<Hash, DecryptError> {
146 Ok(match name {
147 "SHA1" | "SHA-1" => Hash::Sha1,
148 "SHA256" | "SHA-256" => Hash::Sha256,
149 "SHA384" | "SHA-384" => Hash::Sha384,
150 "SHA512" | "SHA-512" => Hash::Sha512,
151 other => return Err(DecryptError::Unsupported(format!("{other} hash"))),
152 })
153 }
154
155 pub(crate) fn digest(self, parts: &[&[u8]]) -> Vec<u8> {
157 fn run<D: Digest>(parts: &[&[u8]]) -> Vec<u8> {
158 let mut d = D::new();
159 for p in parts {
160 d.update(p);
161 }
162 d.finalize().to_vec()
163 }
164 match self {
165 Hash::Sha1 => run::<sha1::Sha1>(parts),
166 Hash::Sha256 => run::<sha2::Sha256>(parts),
167 Hash::Sha384 => run::<sha2::Sha384>(parts),
168 Hash::Sha512 => run::<sha2::Sha512>(parts),
169 }
170 }
171
172 fn digest_len(self) -> usize {
174 match self {
175 Hash::Sha1 => 20,
176 Hash::Sha256 => 32,
177 Hash::Sha384 => 48,
178 Hash::Sha512 => 64,
179 }
180 }
181
182 fn block_size(self) -> usize {
184 match self {
185 Hash::Sha384 | Hash::Sha512 => 128,
186 Hash::Sha1 | Hash::Sha256 => 64,
187 }
188 }
189}
190
191pub(crate) fn iterated_hash(hash: Hash, password: &str, salt: &[u8], spin: u32) -> Vec<u8> {
193 let pw: Vec<u8> = password.encode_utf16().flat_map(u16::to_le_bytes).collect();
194 let mut h = hash.digest(&[salt, &pw]);
195 for i in 0..spin {
196 h = hash.digest(&[&i.to_le_bytes(), &h]);
197 }
198 h
199}
200
201pub(crate) fn derive(hash: Hash, a: &[u8], b: &[u8], len: usize) -> Vec<u8> {
203 let mut k = hash.digest(&[a, b]);
204 k.resize(len, 0x36);
205 k
206}
207
208pub(crate) fn hmac(hash: Hash, key: &[u8], message: &[u8]) -> Vec<u8> {
210 let bs = hash.block_size();
211 let mut k = key.to_vec();
212 if k.len() > bs {
213 k = hash.digest(&[&k]);
214 }
215 k.resize(bs, 0);
216 let pad = |x: u8| k.iter().map(|b| b ^ x).collect::<Vec<u8>>();
217 let inner = hash.digest(&[&pad(0x36), message]);
218 hash.digest(&[&pad(0x5C), &inner])
219}
220
221pub(crate) fn standard_key(password: &str, salt: &[u8], key_len: usize) -> Vec<u8> {
224 let h = iterated_hash(Hash::Sha1, password, salt, STANDARD_SPIN_COUNT);
225 let h = Hash::Sha1.digest(&[&h, &0u32.to_le_bytes()]);
226 let expand = |pad: u8| {
227 let mut buf = [pad; 64];
228 for (b, x) in buf.iter_mut().zip(&h) {
229 *b ^= x;
230 }
231 Hash::Sha1.digest(&[&buf])
232 };
233 let mut key = expand(0x36);
234 key.extend(expand(0x5C));
235 key.truncate(key_len);
236 key
237}
238
239enum Aes {
242 A128(aes::Aes128),
243 A192(aes::Aes192),
244 A256(aes::Aes256),
245}
246
247impl Aes {
248 fn new(key: &[u8]) -> Result<Aes, DecryptError> {
249 let bad = |_| malformed("bad AES key");
250 Ok(match key.len() {
251 16 => Aes::A128(aes::Aes128::new_from_slice(key).map_err(bad)?),
252 24 => Aes::A192(aes::Aes192::new_from_slice(key).map_err(bad)?),
253 32 => Aes::A256(aes::Aes256::new_from_slice(key).map_err(bad)?),
254 n => return Err(malformed(format!("a {n}-byte AES key"))),
255 })
256 }
257
258 fn decrypt_block(&self, block: &mut [u8; BLOCK]) {
259 let b = aes::Block::cast_from_core_mut(block);
260 match self {
261 Aes::A128(c) => c.decrypt_block(b),
262 Aes::A192(c) => c.decrypt_block(b),
263 Aes::A256(c) => c.decrypt_block(b),
264 }
265 }
266
267 fn decrypt_ecb(&self, data: &mut [u8]) -> Result<(), DecryptError> {
269 let (blocks, rest) = data.as_chunks_mut::<BLOCK>();
270 if !rest.is_empty() {
271 return Err(malformed("ciphertext isn't whole AES blocks"));
272 }
273 blocks.iter_mut().for_each(|b| self.decrypt_block(b));
274 Ok(())
275 }
276
277 fn decrypt_cbc(&self, iv: &[u8], data: &mut [u8]) -> Result<(), DecryptError> {
279 let mut prev: [u8; BLOCK] = iv.try_into().map_err(|_| malformed("an IV that isn't one AES block"))?;
280 let (blocks, rest) = data.as_chunks_mut::<BLOCK>();
281 if !rest.is_empty() {
282 return Err(malformed("ciphertext isn't whole AES blocks"));
283 }
284 for b in blocks {
285 let cipher = *b;
286 self.decrypt_block(b);
287 b.iter_mut().zip(prev).for_each(|(x, p)| *x ^= p);
288 prev = cipher;
289 }
290 Ok(())
291 }
292}
293
294struct StandardHeader<'a> {
298 key_len: usize,
299 salt: &'a [u8],
300 verifier: &'a [u8],
301 verifier_hash: &'a [u8],
302}
303
304fn standard_header(info: &[u8]) -> Result<StandardHeader<'_>, DecryptError> {
306 let short = || malformed("truncated Standard encryption header");
307 let flags = u32_at(info, 0).ok_or_else(short)?;
308 if flags & F_EXTERNAL != 0 {
309 return Err(DecryptError::Unsupported("external".into()));
310 }
311 if flags & F_AES == 0 || flags & F_CRYPTOAPI == 0 {
312 return Err(DecryptError::Unsupported("RC4".into()));
313 }
314 let header_size = usize::try_from(u32_at(info, 4).ok_or_else(short)?).map_err(|_| short())?;
315 let header = info.get(8..8usize.checked_add(header_size).ok_or_else(short)?).ok_or_else(short)?;
316 let alg_id = u32_at(header, 8).ok_or_else(short)?;
317 let alg_hash = u32_at(header, 12).ok_or_else(short)?;
318 let key_bits = u32_at(header, 16).ok_or_else(short)?;
319 let key_len = match (alg_id, key_bits) {
320 (0x660E | 0, 128) => 16,
321 (0x660F, 192) => 24,
322 (0x6610, 256) => 32,
323 (0x660E..=0x6610, _) => return Err(malformed(format!("AES with a {key_bits}-bit key"))),
324 _ => return Err(DecryptError::Unsupported(format!("cipher 0x{alg_id:04X}"))),
325 };
326 if alg_hash != 0x8004 && alg_hash != 0 {
327 return Err(DecryptError::Unsupported(format!("hash 0x{alg_hash:04X}")));
328 }
329 let v = info.get(8 + header_size..).ok_or_else(short)?;
331 if u32_at(v, 0) != Some(16) {
332 return Err(malformed("a Standard salt that isn't 16 bytes"));
333 }
334 let salt = v.get(4..20).ok_or_else(short)?;
335 let verifier = v.get(20..36).ok_or_else(short)?;
336 let verifier_hash = v.get(40..72).ok_or_else(short)?;
337 Ok(StandardHeader { key_len, salt, verifier, verifier_hash })
338}
339
340fn standard(info: &[u8], package: &[u8], password: &str) -> Result<Vec<u8>, DecryptError> {
341 let h = standard_header(info)?;
342 let (mut verifier, mut verifier_hash) = (h.verifier.to_vec(), h.verifier_hash.to_vec());
343 let aes = Aes::new(&standard_key(password, h.salt, h.key_len))?;
344 aes.decrypt_ecb(&mut verifier)?;
345 aes.decrypt_ecb(&mut verifier_hash)?;
346 if verifier_hash.get(..20) != Some(Hash::Sha1.digest(&[&verifier]).as_slice()) {
347 return Err(DecryptError::WrongPassword);
348 }
349 let (size, data) = package_parts(package)?;
350 let mut out = data.get(..size.div_ceil(BLOCK) * BLOCK).ok_or_else(|| malformed("the package isn't whole AES blocks"))?.to_vec();
351 aes.decrypt_ecb(&mut out)?;
352 out.truncate(size);
353 Ok(out)
354}
355
356pub(crate) struct Params {
360 pub(crate) hash: Hash,
361 pub(crate) salt: Vec<u8>,
362 pub(crate) key_len: usize,
363}
364
365impl Params {
366 fn read(e: &El) -> Result<Params, DecryptError> {
367 let cipher = e.attr("cipherAlgorithm").unwrap_or("AES");
368 if cipher != "AES" {
369 return Err(DecryptError::Unsupported(cipher.into()));
370 }
371 let chaining = e.attr("cipherChaining").unwrap_or("ChainingModeCBC");
372 if chaining != "ChainingModeCBC" {
373 return Err(DecryptError::Unsupported(format!("AES {chaining}")));
374 }
375 if e.attr_u32("blockSize") != Some(16) {
376 return Err(malformed("an AES block size that isn't 16"));
377 }
378 let key_len = match e.attr_u32("keyBits") {
379 Some(128) => 16,
380 Some(192) => 24,
381 Some(256) => 32,
382 other => return Err(malformed(format!("AES with keyBits {}", other.unwrap_or(0)))),
383 };
384 let hash = Hash::from_name(e.attr("hashAlgorithm").unwrap_or("SHA1"))?;
385 let salt = base64(e.attr("saltValue").ok_or_else(|| malformed("no saltValue"))?)?;
386 if salt.is_empty() || salt.len() > 64 {
387 return Err(malformed("a salt that isn't 1 to 64 bytes"));
388 }
389 Ok(Params { hash, salt, key_len })
390 }
391
392 pub(crate) fn iv(&self, block_key: Option<&[u8]>) -> Vec<u8> {
394 match block_key {
395 Some(k) => derive(self.hash, &self.salt, k, BLOCK),
396 None => {
397 let mut iv = self.salt.clone();
398 iv.resize(BLOCK, 0x36);
399 iv
400 }
401 }
402 }
403}
404
405struct AgileDescriptor {
409 key_data: Params,
410 key: Params,
411 spin: u32,
412 verifier_input: Vec<u8>,
413 verifier_hash: Vec<u8>,
414 key_value: Vec<u8>,
415 hmac_key: Vec<u8>,
416 hmac_value: Vec<u8>,
417}
418
419fn agile_descriptor(xml: &[u8]) -> Result<AgileDescriptor, DecryptError> {
421 let root = crate::xlsx::xml::parse(xml).map_err(|e| malformed(e.to_string()))?;
422 let key_data = Params::read(root.child("keyData").ok_or_else(|| malformed("no keyData"))?)?;
423 let encryptor = root
424 .child("keyEncryptors")
425 .and_then(|k| k.kids("keyEncryptor").find(|e| e.attr("uri") == Some(PASSWORD_ENCRYPTOR)))
426 .ok_or_else(|| DecryptError::Unsupported("certificate".into()))?
427 .child("encryptedKey")
428 .ok_or_else(|| malformed("no encryptedKey"))?;
429 let key = Params::read(encryptor)?;
430 let spin = encryptor.attr_u32("spinCount").ok_or_else(|| malformed("no spinCount"))?;
431 if spin > MAX_SPIN_COUNT {
432 return Err(malformed(format!("a spin count of {spin}, more than the {MAX_SPIN_COUNT} allowed")));
433 }
434 let field = |name: &str| base64(encryptor.attr(name).ok_or_else(|| malformed(format!("no {name}")))?);
435 let integrity = root.child("dataIntegrity").ok_or_else(|| malformed("no dataIntegrity"))?;
438 let hmac_field = |attr: &str| {
439 let v = base64(integrity.attr(attr).ok_or_else(|| malformed(format!("no {attr}")))?)?;
440 if v.is_empty() || v.len() % BLOCK != 0 {
441 return Err(malformed(format!("a whole-block {attr}")));
442 }
443 Ok(v)
444 };
445 Ok(AgileDescriptor {
446 key_data,
447 key,
448 spin,
449 verifier_input: field("encryptedVerifierHashInput")?,
450 verifier_hash: field("encryptedVerifierHashValue")?,
451 key_value: field("encryptedKeyValue")?,
452 hmac_key: hmac_field("encryptedHmacKey")?,
453 hmac_value: hmac_field("encryptedHmacValue")?,
454 })
455}
456
457fn agile(xml: &[u8], package: &[u8], password: &str) -> Result<Vec<u8>, DecryptError> {
460 let descriptor = agile_descriptor(xml)?;
461 let AgileDescriptor { key_data, key, spin, verifier_input, verifier_hash, key_value, hmac_key, hmac_value } = descriptor;
462 let h = iterated_hash(key.hash, password, &key.salt, spin);
463 let iv = key.iv(None);
464 let field = |data: &[u8], block_key: &[u8]| -> Result<Vec<u8>, DecryptError> {
465 let mut data = data.to_vec();
466 Aes::new(&derive(key.hash, &h, block_key, key.key_len))?.decrypt_cbc(&iv, &mut data)?;
467 Ok(data)
468 };
469 let input = field(&verifier_input, &BLOCK_VERIFIER_INPUT)?;
470 let expected = field(&verifier_hash, &BLOCK_VERIFIER_HASH)?;
471 let input = input.get(..key.salt.len()).unwrap_or(&input);
472 let actual = key.hash.digest(&[input]);
473 if expected.get(..actual.len()) != Some(actual.as_slice()) {
474 return Err(DecryptError::WrongPassword);
475 }
476 let secret = field(&key_value, &BLOCK_KEY)?;
477 let aes = Aes::new(secret.get(..key_data.key_len).ok_or_else(|| malformed("a short encryptedKeyValue"))?)?;
478 let mut mac_field = hmac_key;
484 aes.decrypt_cbc(&key_data.iv(Some(&BLOCK_HMAC_KEY)), &mut mac_field)?;
485 let mut declared = hmac_value;
486 aes.decrypt_cbc(&key_data.iv(Some(&BLOCK_HMAC_VALUE)), &mut declared)?;
487 let verified = [key_data.hash.digest_len(), key_data.salt.len()].into_iter().any(|len| {
488 let Some(key) = mac_field.get(..len) else { return false };
489 let mac = hmac(key_data.hash, key, package);
490 declared.get(..mac.len()) == Some(mac.as_slice())
491 });
492 if !verified {
493 return Err(malformed("the decrypted data failed its integrity check"));
494 }
495 let (size, data) = package_parts(package)?;
496 let mut out = Vec::with_capacity(size);
497 for (i, segment) in data.chunks(SEGMENT).enumerate() {
498 if out.len() >= size {
499 break;
500 }
501 let index = u32::try_from(i).map_err(|_| malformed("too many segments"))?;
502 let start = out.len();
503 out.extend_from_slice(segment);
504 let block = out.get_mut(start..).unwrap_or_default();
505 aes.decrypt_cbc(&key_data.iv(Some(&index.to_le_bytes())), block)?;
506 }
507 out.truncate(size);
508 Ok(out)
509}
510
511fn base64(s: &str) -> Result<Vec<u8>, DecryptError> {
515 let mut out = Vec::with_capacity(s.len() / 4 * 3);
516 let (mut buf, mut bits) = (0u32, 0u32);
517 for c in s.bytes().filter(|c| !c.is_ascii_whitespace() && *c != b'=') {
518 let v = match c {
519 b'A'..=b'Z' => c - b'A',
520 b'a'..=b'z' => c - b'a' + 26,
521 b'0'..=b'9' => c - b'0' + 52,
522 b'+' => 62,
523 b'/' => 63,
524 _ => return Err(malformed("invalid base64")),
525 };
526 buf = (buf << 6) | u32::from(v);
527 bits += 6;
528 if bits >= 8 {
529 bits -= 8;
530 out.push((buf >> bits) as u8);
531 buf &= (1 << bits) - 1;
532 }
533 }
534 Ok(out)
535}
536
537#[cfg(test)]
538mod tests;