{"record":{"id":"bed42af5af630bda","repo":"tonhowtf/omniget","slug":"aes-init","errorCode":null,"errorMessage":"AES init: {:?}","messagePattern":"AES init: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/hls_downloader.rs","lineNumber":677,"sourceCode":"    while let Some((idx, data)) = rx.recv().await {\n        pending.insert(idx, data);\n\n        while let Some(segment_data) = pending.remove(&next_expected) {\n            // The image wrapper, when present, sits outside the encryption:\n            // it has to come off before the AES-128 block decryption runs.\n            let payload_start = image_wrapper_offset(&segment_data);\n\n            if let Some(enc) = encryption {\n                use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};\n                type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;\n\n                let iv = compute_iv(enc, next_expected, media_sequence);\n                let mut buf = segment_data;\n                if payload_start > 0 {\n                    buf.drain(..payload_start);\n                }\n                let decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)\n                    .map_err(|e| anyhow::anyhow!(\"AES init: {:?}\", e))?;\n                let decrypted = decryptor\n                    .decrypt_padded_mut::<Pkcs7>(&mut buf)\n                    .map_err(|e| anyhow::anyhow!(\"AES decrypt: {:?}\", e))?;\n                file.write_all(decrypted)?;\n            } else {\n                file.write_all(&segment_data[payload_start..])?;\n            }\n            next_expected += 1;\n        }\n    }\n\n    file.flush()?;\n\n    if next_expected < total_segments {\n        anyhow::bail!(\n            \"Only {} of {} segments were written\",\n            next_expected,\n            total_segments","sourceCodeStart":659,"sourceCodeEnd":695,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/hls_downloader.rs#L659-L695","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Log enc.key_bytes.len() and the IV; both must be exactly 16 bytes for AES-128-CBC.","Verify the key response is binary key data (16 bytes), not HTML/text — re-check the key URI and its response content-type.","If the playlist declares an IV attribute, use it verbatim instead of computing one from media sequence.","Re-fetch the playlist/KEY tag if the stream switched variants with a different key."],"exampleFix":"// before\nlet decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)\n    .map_err(|e| anyhow::anyhow!(\"AES init: {:?}\", e))?;\n// after\nif enc.key_bytes.len() != 16 {\n    anyhow::bail!(\"AES key is {} bytes, expected 16 (bad key response?)\", enc.key_bytes.len());\n}\nlet decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)\n    .map_err(|e| anyhow::anyhow!(\"AES init: {e:?} (key {} bytes, iv {} bytes)\", enc.key_bytes.len(), iv.len()))?;","handlingStrategy":"validation","validationCode":"fn valid_aes128_material(key: &[u8], iv: &[u8]) -> bool {\n    key.len() == 16 && iv.len() == 16\n}\nif !valid_aes128_material(&enc.key_bytes, &iv) {\n    anyhow::bail!(\"bad AES material: key={}B iv={}B\", enc.key_bytes.len(), iv.len());\n}","typeGuard":null,"tryCatchPattern":"let decryptor = Aes128CbcDec::new_from_slices(&enc.key_bytes, &iv)\n    .map_err(|e| anyhow::anyhow!(\"AES init: {e:?} (key len {}, iv len {})\",\n        enc.key_bytes.len(), iv.len()))?;","preventionTips":["Validate the fetched key is exactly 16 bytes before constructing the decryptor.","Prefer the playlist's explicit IV attribute over computing one; log both when they differ.","Detect non-binary key responses (HTML/text) by checking length/content-type.","Re-read the active EXT-X-KEY when the playlist updates mid-stream."],"tags":["hls","aes","cbc","key-length"],"backgroundTag":"invalid-key-length","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}