tonhowtf/omniget · error · anyhow::Error

AES init

Error message

AES init: {:?}

What it means

write_segments_ordered creates an AES-128-CBC decryptor from the EXT-X-KEY key bytes and a computed IV. If the key or IV is not exactly 16 bytes, Aes128CbcDec::new_from_slices fails and this error is raised. It means the encryption metadata is malformed, not that decryption data was wrong.

Solutions

  1. Log enc.key_bytes.len() and the IV; both must be exactly 16 bytes for AES-128-CBC.
  2. Verify the key response is binary key data (16 bytes), not HTML/text — re-check the key URI and its response content-type.
  3. If the playlist declares an IV attribute, use it verbatim instead of computing one from media sequence.
  4. Re-fetch the playlist/KEY tag if the stream switched variants with a different key.

Example fix

// before
let decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)
    .map_err(|e| anyhow::anyhow!("AES init: {:?}", e))?;
// after
if enc.key_bytes.len() != 16 {
    anyhow::bail!("AES key is {} bytes, expected 16 (bad key response?)", enc.key_bytes.len());
}
let decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)
    .map_err(|e| anyhow::anyhow!("AES init: {e:?} (key {} bytes, iv {} bytes)", enc.key_bytes.len(), iv.len()))?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_aes128_material(key: &[u8], iv: &[u8]) -> bool {
    key.len() == 16 && iv.len() == 16
}
if !valid_aes128_material(&enc.key_bytes, &iv) {
    anyhow::bail!("bad AES material: key={}B iv={}B", enc.key_bytes.len(), iv.len());
}

Try / catch

let decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)
    .map_err(|e| anyhow::anyhow!("AES init: {e:?} (key len {}, iv len {})",
        enc.key_bytes.len(), iv.len()))?;

Prevention

When it happens

Trigger: Key fetched from the key URI is not 16 bytes (e.g., HTML error body saved as the key, or key URL serves a wrapper), or the computed IV/declared IV has the wrong length.

Common situations: Key endpoint returned an error page with 200 status; METHOD=AES-128 playlist with an unusual/implicit IV the compute_iv logic mishandles; playlist variant switching to a different KEY mid-download.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/bed42af5af630bda. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:677

    while let Some((idx, data)) = rx.recv().await {
        pending.insert(idx, data);

        while let Some(segment_data) = pending.remove(&next_expected) {
            // The image wrapper, when present, sits outside the encryption:
            // it has to come off before the AES-128 block decryption runs.
            let payload_start = image_wrapper_offset(&segment_data);

            if let Some(enc) = encryption {
                use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
                type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;

                let iv = compute_iv(enc, next_expected, media_sequence);
                let mut buf = segment_data;
                if payload_start > 0 {
                    buf.drain(..payload_start);
                }
                let decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)
                    .map_err(|e| anyhow::anyhow!("AES init: {:?}", e))?;
                let decrypted = decryptor
                    .decrypt_padded_mut::<Pkcs7>(&mut buf)
                    .map_err(|e| anyhow::anyhow!("AES decrypt: {:?}", e))?;
                file.write_all(decrypted)?;
            } else {
                file.write_all(&segment_data[payload_start..])?;
            }
            next_expected += 1;
        }
    }

    file.flush()?;

    if next_expected < total_segments {
        anyhow::bail!(
            "Only {} of {} segments were written",
            next_expected,
            total_segments

View on GitHub (pinned to 8600b91f42)