|  | 
|  | 1 | +use cipher::{ | 
|  | 2 | +    BlockSizeUser, IvSizeUser, KeyIvInit, KeySizeUser, ParBlocksSizeUser, StreamCipherBackend, | 
|  | 3 | +    StreamCipherClosure, StreamCipherCore, StreamCipherSeekCore, | 
|  | 4 | +    consts::{U1, U4, U16}, | 
|  | 5 | +}; | 
|  | 6 | +use hex_literal::hex; | 
|  | 7 | + | 
|  | 8 | +const KEY: [u8; 4] = hex!("00010203"); | 
|  | 9 | +const IV: [u8; 4] = hex!("04050607"); | 
|  | 10 | + | 
|  | 11 | +/// Core of dummy insecure stream cipher. | 
|  | 12 | +pub struct DummyStreamCipherCore { | 
|  | 13 | +    key_iv: u64, | 
|  | 14 | +    pos: u64, | 
|  | 15 | +} | 
|  | 16 | + | 
|  | 17 | +impl KeySizeUser for DummyStreamCipherCore { | 
|  | 18 | +    type KeySize = U4; | 
|  | 19 | +} | 
|  | 20 | + | 
|  | 21 | +impl IvSizeUser for DummyStreamCipherCore { | 
|  | 22 | +    type IvSize = U4; | 
|  | 23 | +} | 
|  | 24 | + | 
|  | 25 | +impl KeyIvInit for DummyStreamCipherCore { | 
|  | 26 | +    fn new(key: &cipher::Key<Self>, iv: &cipher::Iv<Self>) -> Self { | 
|  | 27 | +        let mut key_iv = [0u8; 8]; | 
|  | 28 | +        key_iv[..4].copy_from_slice(key); | 
|  | 29 | +        key_iv[4..].copy_from_slice(iv); | 
|  | 30 | +        Self { | 
|  | 31 | +            key_iv: u64::from_le_bytes(key_iv), | 
|  | 32 | +            pos: 0, | 
|  | 33 | +        } | 
|  | 34 | +    } | 
|  | 35 | +} | 
|  | 36 | + | 
|  | 37 | +impl BlockSizeUser for DummyStreamCipherCore { | 
|  | 38 | +    type BlockSize = U16; | 
|  | 39 | +} | 
|  | 40 | + | 
|  | 41 | +impl StreamCipherCore for DummyStreamCipherCore { | 
|  | 42 | +    fn remaining_blocks(&self) -> Option<usize> { | 
|  | 43 | +        let rem = u64::MAX - self.pos; | 
|  | 44 | +        usize::try_from(rem).ok() | 
|  | 45 | +    } | 
|  | 46 | + | 
|  | 47 | +    fn process_with_backend(&mut self, f: impl StreamCipherClosure<BlockSize = U16>) { | 
|  | 48 | +        f.call(self); | 
|  | 49 | +    } | 
|  | 50 | +} | 
|  | 51 | + | 
|  | 52 | +impl ParBlocksSizeUser for DummyStreamCipherCore { | 
|  | 53 | +    type ParBlocksSize = U1; | 
|  | 54 | +} | 
|  | 55 | + | 
|  | 56 | +impl StreamCipherBackend for DummyStreamCipherCore { | 
|  | 57 | +    fn gen_ks_block(&mut self, block: &mut cipher::Block<Self>) { | 
|  | 58 | +        const C1: u64 = 0x87c3_7b91_1142_53d5; | 
|  | 59 | +        const C2: u64 = 0x4cf5_ad43_2745_937f; | 
|  | 60 | + | 
|  | 61 | +        let a = self.key_iv ^ C1; | 
|  | 62 | +        let b = self.pos ^ C2; | 
|  | 63 | +        let a = a.rotate_left(13).wrapping_mul(b); | 
|  | 64 | +        let b = b.rotate_left(13).wrapping_mul(a); | 
|  | 65 | + | 
|  | 66 | +        block[..8].copy_from_slice(&a.to_le_bytes()); | 
|  | 67 | +        block[8..].copy_from_slice(&b.to_le_bytes()); | 
|  | 68 | +        self.pos = self.pos.wrapping_add(1); | 
|  | 69 | +    } | 
|  | 70 | +} | 
|  | 71 | + | 
|  | 72 | +impl StreamCipherSeekCore for DummyStreamCipherCore { | 
|  | 73 | +    type Counter = u64; | 
|  | 74 | + | 
|  | 75 | +    fn get_block_pos(&self) -> Self::Counter { | 
|  | 76 | +        self.pos | 
|  | 77 | +    } | 
|  | 78 | + | 
|  | 79 | +    fn set_block_pos(&mut self, pos: Self::Counter) { | 
|  | 80 | +        self.pos = pos; | 
|  | 81 | +    } | 
|  | 82 | +} | 
|  | 83 | + | 
|  | 84 | +#[test] | 
|  | 85 | +fn dummy_stream_cipher_core() { | 
|  | 86 | +    let mut cipher = DummyStreamCipherCore::new(&KEY.into(), &IV.into()); | 
|  | 87 | +    assert_eq!(cipher.get_block_pos(), 0); | 
|  | 88 | + | 
|  | 89 | +    let mut block = [0u8; 16].into(); | 
|  | 90 | +    cipher.write_keystream_block(&mut block); | 
|  | 91 | +    assert_eq!(block, hex!("e82393543cc96089305116003a417acc")); | 
|  | 92 | +    assert_eq!(cipher.get_block_pos(), 1); | 
|  | 93 | + | 
|  | 94 | +    cipher.set_block_pos(200); | 
|  | 95 | +    assert_eq!(cipher.get_block_pos(), 200); | 
|  | 96 | + | 
|  | 97 | +    cipher.write_keystream_block(&mut block); | 
|  | 98 | +    assert_eq!(block, hex!("28a96998fe4874ffb0ce9b046c6a9ddb")); | 
|  | 99 | +    assert_eq!(cipher.get_block_pos(), 201); | 
|  | 100 | +} | 
|  | 101 | + | 
|  | 102 | +#[cfg(feature = "stream-wrapper")] | 
|  | 103 | +mod wrapper { | 
|  | 104 | +    use super::*; | 
|  | 105 | +    use cipher::{StreamCipher, StreamCipherCoreWrapper, StreamCipherSeek}; | 
|  | 106 | + | 
|  | 107 | +    /// Dummy insecure stream cipher. | 
|  | 108 | +    pub type DummyStreamCipher = StreamCipherCoreWrapper<DummyStreamCipherCore>; | 
|  | 109 | + | 
|  | 110 | +    #[test] | 
|  | 111 | +    fn dummy_stream_cipher_basic() { | 
|  | 112 | +        let mut cipher = DummyStreamCipher::new(&KEY.into(), &IV.into()); | 
|  | 113 | +        assert_eq!(cipher.current_pos::<u64>(), 0); | 
|  | 114 | + | 
|  | 115 | +        let mut buf = [0u8; 20]; | 
|  | 116 | +        cipher.apply_keystream(&mut buf); | 
|  | 117 | +        assert_eq!(buf, hex!("e82393543cc96089305116003a417accd073384a")); | 
|  | 118 | +        assert_eq!(cipher.current_pos::<usize>(), buf.len()); | 
|  | 119 | + | 
|  | 120 | +        const SEEK_POS: usize = 500; | 
|  | 121 | +        cipher.seek(SEEK_POS); | 
|  | 122 | +        cipher.apply_keystream(&mut buf); | 
|  | 123 | +        assert_eq!(buf, hex!("6b014c6a3c376b13c4720590d26147c5ebf334c5")); | 
|  | 124 | +        assert_eq!(cipher.current_pos::<usize>(), SEEK_POS + buf.len()); | 
|  | 125 | +    } | 
|  | 126 | + | 
|  | 127 | +    #[test] | 
|  | 128 | +    fn dummy_stream_cipher_seek_limit() { | 
|  | 129 | +        let mut cipher = DummyStreamCipher::new(&KEY.into(), &IV.into()); | 
|  | 130 | + | 
|  | 131 | +        let pos = ((u64::MAX as u128) << 4) - 20; | 
|  | 132 | +        cipher.try_seek(pos).unwrap(); | 
|  | 133 | + | 
|  | 134 | +        let mut buf = [0u8; 30]; | 
|  | 135 | +        let res = cipher.try_apply_keystream(&mut buf); | 
|  | 136 | +        assert!(res.is_err()); | 
|  | 137 | +        let cur_pos: u128 = cipher.current_pos(); | 
|  | 138 | +        assert_eq!(cur_pos, pos); | 
|  | 139 | + | 
|  | 140 | +        let res = cipher.try_apply_keystream(&mut buf[..19]); | 
|  | 141 | +        assert!(res.is_ok()); | 
|  | 142 | +        let cur_pos: u128 = cipher.current_pos(); | 
|  | 143 | +        assert_eq!(cur_pos, pos + 19); | 
|  | 144 | + | 
|  | 145 | +        cipher.try_seek(pos).unwrap(); | 
|  | 146 | + | 
|  | 147 | +        // TODO: fix as part of https://github.com/RustCrypto/traits/issues/1808 | 
|  | 148 | +        // let res = cipher.try_apply_keystream(&mut buf[..20]); | 
|  | 149 | +        // assert!(res.is_err()); | 
|  | 150 | +    } | 
|  | 151 | + | 
|  | 152 | +    #[cfg(feature = "dev")] | 
|  | 153 | +    cipher::stream_cipher_test!( | 
|  | 154 | +        dummy_stream_cipher, | 
|  | 155 | +        "dummy_stream_cipher", | 
|  | 156 | +        DummyStreamCipher, | 
|  | 157 | +    ); | 
|  | 158 | +    #[cfg(feature = "dev")] | 
|  | 159 | +    cipher::stream_cipher_seek_test!(dummy_stream_cipher_seek, DummyStreamCipher); | 
|  | 160 | +} | 
0 commit comments