SRW: a huge encryption key doesn't overflow keyed()

The maker note's key (0xa020) can be LONG8, so a value past i64::MAX
made `-1 * key` overflow: a panic in debug and test builds. Use a
wrapping multiply; only the low 32 bits are kept, so real files decode
the same.
This commit is contained in:
Bruno-Alumn
2026-10-30 16:25:20 +01:00
parent 5194cc20de
commit 7597810299
+35 -7
View File
@@ -62,7 +62,8 @@ fn keyed(mn: &Ifd, tag: u16, sign: i64) -> Option<Vec<i64>> {
let key = mn.u64s(ENCRYPTION_KEY).filter(|k| k.len() == 11)?;
let vals = mn.f64s(tag)?;
// 32-bit entries: the stored value wraps modulo 2^32 (a negative number stored as a LONG)
Some(vals.iter().enumerate().map(|(i, &v)| i64::from((v as i64).wrapping_add(sign * key[i % 11] as i64) as i32)).collect())
// (a key value past i64::MAX must not overflow the multiply: only the low 32 bits are kept)
Some(vals.iter().enumerate().map(|(i, &v)| i64::from((v as i64).wrapping_add((key[i % 11] as i64).wrapping_mul(sign)) as i32)).collect())
}
/// The raw image's IFD: the one with a private compression value.
@@ -283,19 +284,25 @@ mod tests {
/// A maker note in the Samsung layout: a big-endian IFD of LONG vectors, offsets relative to the note's start.
fn note(entries: &[(u16, Vec<u32>)]) -> Vec<u8> {
let typed: Vec<_> = entries.iter().map(|(tag, v)| (*tag, 4, v.len() as u32, v.iter().flat_map(|x| x.to_be_bytes()).collect())).collect();
note_typed(&typed)
}
/// [`note`] with any entry types: `(tag, type, count, big-endian value bytes)`.
fn note_typed(entries: &[(u16, u16, u32, Vec<u8>)]) -> Vec<u8> {
let mut head = Vec::new();
let mut tail = Vec::new();
head.extend_from_slice(&(entries.len() as u16).to_be_bytes());
let values_at = 2 + 12 * entries.len() + 4;
for (tag, v) in entries {
for (tag, kind, count, v) in entries {
head.extend_from_slice(&tag.to_be_bytes());
head.extend_from_slice(&4u16.to_be_bytes());
head.extend_from_slice(&(v.len() as u32).to_be_bytes());
if v.len() == 1 {
head.extend_from_slice(&v[0].to_be_bytes());
head.extend_from_slice(&kind.to_be_bytes());
head.extend_from_slice(&count.to_be_bytes());
if v.len() <= 4 {
head.extend(v.iter().copied().chain(std::iter::repeat(0)).take(4));
} else {
head.extend_from_slice(&((values_at + tail.len()) as u32).to_be_bytes());
tail.extend(v.iter().flat_map(|x| x.to_be_bytes()));
tail.extend_from_slice(v);
}
}
head.extend_from_slice(&[0; 4]);
@@ -511,6 +518,27 @@ mod tests {
}
/// A framing rectangle outside the array, or reversed, is ignored; truncated files fail without panicking.
/// An encryption key value past i64::MAX (LONG8) overflowed `-1 * key` in debug builds.
#[test]
fn a_huge_encryption_key_does_not_panic() {
let key: Vec<u8> = std::iter::repeat_n((1u64 << 63).to_be_bytes(), 11).flatten().collect();
let note = note_typed(&[(ENCRYPTION_KEY, 16, 11, key), (WB_UNCORRECTED, 4, 4, [0u8; 16].to_vec()), (WB_BLACK, 4, 4, [0u8; 16].to_vec())]);
let bytes = srw(Spec {
w: 64,
h: 32,
compression: 32770,
strip: pack12(&ramp(64, 32, 4096)),
pattern: Some([2, 1, 1, 0]),
note: Some(note),
picture: None,
});
let mn = maker_note(&bytes, &Tiff::parse(&bytes).unwrap()).unwrap();
assert_eq!(mn.u64s(ENCRYPTION_KEY).map(|k| k[0]), Some(1 << 63), "the key reaches keyed()");
assert_eq!(keyed(&mn, WB_BLACK, -1), Some(vec![0; 4]), "(2^63 mod 2^32 is 0)");
assert!(decode(&bytes).is_ok());
assert!(probe_info(&bytes).is_ok());
}
#[test]
fn hostile_framing_and_truncation_do_not_panic() {
let v = ramp(64, 32, 4096);