1 /**
2 * CFB mode
3 * 
4 * Copyright:
5 * (C) 1999-2007,2013 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.modes.cfb;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_MODE_CFB):
15 
16 import botan.modes.cipher_mode;
17 import botan.block.block_cipher;
18 import botan.modes.mode_pad;
19 import botan.utils.parsing;
20 import botan.utils.xor_buf;
21 import botan.utils.types;
22 import botan.utils.mem_ops;
23 import std.conv : to;
24 import std.algorithm : min;
25 
26 /**
27 * CFB Mode
28 */
29 abstract class CFBMode : CipherMode, Transformation
30 {
31 public:
32     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len)
33     {
34         if (!validNonceLength(nonce_len))
35             throw new InvalidIVLength(name, nonce_len);
36         
37         m_shift_register[] = nonce[0 .. nonce_len];
38         m_keystream_buf.length = m_shift_register.length;
39         cipher().encrypt(m_shift_register, m_keystream_buf);
40         
41         return SecureVector!ubyte();
42     }
43 
44     override @property string name() const
45     {
46         if (feedback() == cipher().blockSize())
47             return cipher().name ~ "/CFB";
48         else
49             return cipher().name ~ "/CFB(" ~ to!string(feedback()*8) ~ ")";
50     }
51 
52     override size_t updateGranularity() const
53     {
54         return feedback();
55     }
56 
57     override size_t minimumFinalSize() const
58     {
59         return 0;
60     }
61 
62     override KeyLengthSpecification keySpec() const
63     {
64         return cipher().keySpec();
65     }
66 
67     override size_t outputLength(size_t input_length) const
68     {
69         return input_length;
70     }
71 
72     override size_t defaultNonceLength() const
73     {
74         return cipher().blockSize();
75     }
76 
77     override bool validNonceLength(size_t n) const
78     {
79         return (n == cipher().blockSize());
80     }
81 
82     override void clear()
83     {
84         m_cipher.clear();
85         m_shift_register.clear();
86     }
87 
88     override bool authenticated() const { return true; }
89 protected:
90     this(BlockCipher cipher, size_t feedback_bits)
91     { 
92         m_cipher = cipher;
93         m_feedback_bytes = feedback_bits ? feedback_bits / 8 : m_cipher.blockSize();
94         if (feedback_bits % 8 || feedback() > m_cipher.blockSize())
95             throw new InvalidArgument(name() ~ ": feedback bits " ~
96                                        to!string(feedback_bits) ~ " not supported");
97     }
98 
99     final BlockCipher cipher() const { return cast()*m_cipher; }
100 
101     final size_t feedback() const { return m_feedback_bytes; }
102 
103     final ref SecureVector!ubyte shiftRegister() { return m_shift_register; }
104 
105     final ref SecureVector!ubyte keystreamBuf() { return m_keystream_buf; }
106 
107 protected:
108     final override void keySchedule(const(ubyte)* key, size_t length)
109     {
110         m_cipher.setKey(key, length);
111     }
112 
113     Unique!BlockCipher m_cipher;
114     SecureVector!ubyte m_shift_register;
115     SecureVector!ubyte m_keystream_buf;
116     size_t m_feedback_bytes;
117 }
118 
119 /**
120 * CFB Encryption
121 */
122 final class CFBEncryption : CFBMode, Transformation
123 {
124 public:
125     this(BlockCipher cipher, size_t feedback_bits)
126     {
127         super(cipher, feedback_bits);
128     }
129 
130     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
131     {
132         assert(buffer.length >= offset, "Offset is sane");
133         size_t sz = buffer.length - offset;
134         ubyte* buf = buffer.ptr + offset;
135         
136         const size_t BS = cipher().blockSize();
137         
138         SecureVector!ubyte* state = &shiftRegister();
139         const size_t shift = feedback();
140         
141         while (sz)
142         {
143             const size_t took = min(shift, sz);
144             xorBuf(buf, &keystreamBuf()[0], took);
145             // Assumes feedback-sized block except for last input
146             if (BS - shift > 0) copyMem(state.ptr, &(*state)[shift], BS - shift);
147             copyMem(&(*state)[BS-shift], buf, took);
148             cipher().encrypt(*state, keystreamBuf());
149             
150             buf += took;
151             sz -= took;
152         }
153     }
154 
155 
156     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
157     {
158         update(buffer, offset);
159     }
160 
161     // Interface fallthrough
162     override string provider() const { return "core"; }
163     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
164     override size_t updateGranularity() const { return super.updateGranularity(); }
165     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
166     override @property string name() const { return super.name; }
167     override void clear() { return super.clear(); }
168     override size_t outputLength(size_t input_length) const { return super.outputLength(input_length); }
169     override size_t minimumFinalSize() const { return super.minimumFinalSize(); }
170     override bool validNonceLength(size_t n) const {
171         return super.validNonceLength(n);
172     }
173 }
174 
175 /**
176 * CFB Decryption
177 */
178 final class CFBDecryption : CFBMode, Transformation
179 {
180 public:
181     this(BlockCipher cipher, size_t feedback_bits) 
182     {
183         super(cipher, feedback_bits);
184     }
185 
186     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
187     {
188         assert(buffer.length >= offset, "Offset is sane");
189         size_t sz = buffer.length - offset;
190         ubyte* buf = buffer.ptr + offset;
191         
192         const size_t BS = cipher().blockSize();
193         
194         SecureVector!ubyte* state = &shiftRegister();
195         const size_t shift = feedback();
196         
197         while (sz)
198         {
199             const size_t took = min(shift, sz);
200             
201             // first update shift register with ciphertext
202             if (BS - shift > 0) copyMem(state.ptr, &(*state)[shift], BS - shift);
203             copyMem(&(*state)[BS-shift], buf, took);
204             
205             // then decrypt
206             xorBuf(buf, &keystreamBuf()[0], took);
207             
208             // then update keystream
209             cipher().encrypt(*state, keystreamBuf());
210             
211             buf += took;
212             sz -= took;
213         }
214     }
215 
216     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
217     {
218         update(buffer, offset);
219     }
220 
221     // Interface fallthrough
222     override string provider() const { return "core"; }
223     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
224     override size_t updateGranularity() const { return super.updateGranularity(); }
225     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
226     override @property string name() const { return super.name; }
227     override void clear() { return super.clear(); }
228     override size_t outputLength(size_t input_length) const { return super.outputLength(input_length); }
229     override size_t minimumFinalSize() const { return super.minimumFinalSize(); }
230     override bool validNonceLength(size_t n) const {
231         return super.validNonceLength(n);
232     }
233 }