Skip to content

chacha20: Can't use full keystream of ChaCha20 IETF variant #444

@nstilt1

Description

@nstilt1

Off by one error: remaining_blocks() restricts the maximum number of blocks to u32::MAX when 0 is a valid block, raising the maximum number of blocks to u32::MAX + 1 or 1 << 32. This test passed, proving that there are 64 unusable bytes in the keystream:

    macro_rules! impl_chacha20_potential_counter_issue {
        ($name:ident, $num_blocks:literal) => {
            #[test]
            fn $name() {
                let mut cipher = ChaCha20::new(&KEY.into(), &IV.into());
                let mut first_4_blocks = [0u8; 256];
                assert_eq!(cipher.current_pos::<u64>(), 0);
                cipher.apply_keystream(&mut first_4_blocks);

                let mut buf_1 = [0u8; $num_blocks * 64];
                let mut buf_2 = [0u8; $num_blocks * 64 + 1];

                // seek to end of keystream
                let pos = (1 << 32) * 64 - $num_blocks * 64 - 64;
                cipher.try_seek(pos).unwrap();
                assert_eq!(cipher.current_pos::<u64>(), pos);

                // overshoot keystream length
                let applied_keystream = cipher.try_apply_keystream(&mut buf_2);
                assert_eq!(applied_keystream.is_err(), true);

                // exhaust keystream
                cipher.apply_keystream(&mut buf_1);

                // seek to beginning and check if the first block is the same as before
                cipher.seek(0);
                assert_eq!(cipher.current_pos::<u64>(), 0);
                cipher.apply_keystream(&mut first_4_blocks);

                // if this assert fails, exhausting the keystream increments
                // state[13], resulting in a different keystream when it
                // should be the same
                assert_eq!(first_4_blocks, [0u8; 256]);
            }
        };
    }

Because this test passes (specifically assert_eq!(applied_keystream.is_err()) and the pos value and size of the buffers), it proves that the seek_pos has to be 64 bytes less than expected to reach the end of the supposed keystream.

I ran into this error when working the counter, but it is easily fixable and has already been fixed in the PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions