1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
//! The FF1 algorithm
//!
//! The FF1 algorithm supports key sizes of 128, 192, and 256 bits.
//! The (maximum possible) length of the tweak is supplied by the
//! caller and is essentially unbounded.
//!
//! This implementation contains a "context" structure, called FF1,
//! that holds the encryption key, the default tweak, and some other
//! parameters related to the algorithm. Once, this structure has
//! been created, it can be used to encrypt and decrypt data

use crate::ffx;
use crate::result::Result;

use byteorder::ByteOrder;
use num_traits::Euclid;

/// The FF1 context structure
pub struct FF1 {
    ffx: ffx::FFX,
}

impl FF1 {
    /// Create a new FF1 context
    ///
    /// The supplied key may be any of the lengths supported by AES.
    ///
    /// The default tweak is optional. If supplied, it's length
    /// must satisfy the constraints set by the `mintwk` and `maxtwk`
    /// parameters. `mintwk` and `maxtwk` may both be set to 0 to
    /// leave the tweak length unbounded.
    ///
    /// The radix must be less than or equal to the number of characters
    /// in the supplied alphabet (or the default alphabet) if no alphabet
    /// is supplied to this function
    pub fn new(
        key: &[u8],
        opt_t: Option<&[u8]>,
        mintwk: usize,
        maxtwk: usize,
        radix: usize,
        opt_alpha: Option<&str>,
    ) -> Result<Self> {
        Ok(FF1 {
            ffx: ffx::FFX::new(
                key,
                opt_t,
                // the maximum input length allowed by the
                // algorithm specification is 2**32 - 1
                (1 << 32) - 1,
                mintwk,
                maxtwk,
                radix,
                opt_alpha,
            )?,
        })
    }

    // the code wants to work with individual characters or letters.
    // this isn't possible with utf8, so the caller is expected to
    // convert Strings to sequences of chars
    fn cipher_chars(
        &self,
        inp: &[char],
        opt_t: Option<&[u8]>,
        which: ffx::CipherType,
    ) -> Result<Vec<char>> {
        let ffx = &self.ffx;
        let radix = ffx.get_radix();
        let blksz = ffx.get_cipher_block_size();

        let t = ffx.get_tweak(&opt_t);
        ffx.validate_tweak_length(t.len())?;

        let n = inp.len();
        ffx.validate_text_length(n)?;

        // (step 1)
        let u = n / 2;
        let v = n - u;

        // the algorithm, as specified, calls for "A" and "B", the
        // strings representing the two halves of the input to be
        // converted back and forth between strings and numbers. as
        // it turns out, those strings can be represented as numbers
        // for the duration of the algorithm and only converted back
        // to strings at the end. (step 2)
        let mut na = ffx.chars_to_bignum(&inp[..u])?;
        let mut nb = ffx.chars_to_bignum(&inp[u..])?;

        // the input string gets broken in half, and `b` is the
        // number of bytes required to represent the latter half
        // as a number converted from the specified radix. (step 3)
        let b =
            ((((radix as f64).log2() * (v as f64)).ceil() as usize) + 7) / 8;
        // d is the number of bytes extracted from the aes output
        // to be used as the number `y` in the algorithm (step 4)
        let d = 4 * ((b + 3) / 4) + 4;

        // p serves as the input to one of the aes operations, the
        // output of which eventually becomes `y`. The algorithm
        // also mentions a `q` slice which is populated by the tweak
        // and the latter half of the input (converted to a number).
        // this `q` is also contained as part of `p` as the two are
        // supposed to be concatenated before being input to the aes
        // operation. `p` is the first 16 bytes, and `q` is the rest.
        let mut p = Vec::<u8>::new();
        p.resize(16 + ((t.len() + 1 + b + (blksz - 1)) / blksz) * blksz, 0);

        // `r` is the output from the aes operations
        let mut r = Vec::<u8>::new();
        r.resize(((d + (blksz - 1)) / blksz) * blksz, 0);

        // p is initialized once and remains unchanged after the values
        // to be put in p are specified by the algorithm (step 5)
        p[0] = 1;
        p[1] = 2;
        // note that the radix is written starting at index 2, but
        // the algorithm only calls for the low order 3 bytes to be
        // written starting at index 3. hence, index 2 is immediately
        // overwritten with the correct value after this operation
        byteorder::BigEndian::write_u32(&mut p[2..6], radix as u32);
        p[2] = 1;
        p[6] = 10;
        p[7] = u as u8;
        byteorder::BigEndian::write_u32(&mut p[8..12], n as u32);
        byteorder::BigEndian::write_u32(&mut p[12..16], t.len() as u32);

        // the first "tweak length" bytes of q contain the tweak.
        // some number of bytes, used to pad q to a multiple of the
        // block size, follow and are to be filled with 0's. the rest
        // of q changes during the algorithm. (step 6i, partial)
        {
            // changes to q are scoped so that multiple mutable
            // references to p don't exist
            let q = &mut p[16..];
            q[0..t.len()].copy_from_slice(t);
            // the rest of q is already full of 0's
            // due to initialization of p
        }

        // later on radix**m where m is either u or v is needed.
        // just calculate them both here. note that u either equals
        // v or is one less than v. (step 6v, 6vi, partial)
        let mut mu: num_bigint::BigInt = radix.into();
        mu = mu.pow(u as u32);
        let mut mv = mu.clone();
        if u != v {
            mv *= radix;
        }

        // during decryption, the algorithm runs in "reverse".
        // swap these values so that during decryption we start
        // with the last ones used during the encryption
        if let ffx::CipherType::Decrypt = which {
            std::mem::swap(&mut na, &mut nb);
            std::mem::swap(&mut mu, &mut mv);
        }

        for i in 0..10 {
            // fill in the non-static portions of q (step 6i, partial)
            {
                // changes to q are scoped to avoid conflict with p.
                // use of q_len as opposed to q.len() also
                // avoids the borrow checker's wrath
                let q = &mut p[16..];
                let q_len = q.len();

                match which {
                    ffx::CipherType::Encrypt => q[q_len - b - 1] = i,
                    ffx::CipherType::Decrypt => q[q_len - b - 1] = 9 - i,
                }

                // the num_bigint library doesn't provide left padding,
                // but it does support little endian output which allows
                // us to do right-padding and then reverse the bytes
                let (_, mut v) = nb.to_bytes_le();
                v.resize(b, 0);
                v.reverse();
                q[q_len - b..].copy_from_slice(&v);
            }

            // (step 6ii)
            ffx.prf(&p, &mut r[..blksz])?;

            // (step 6iii)
            // this step is a little bit tricky, or at least the way
            // it is implemented is. this step calls for the output of
            // `prf()` to be concatenated with successive calls to `ciph()`
            // on that same output xor'd with a counter, something like this:
            // output || ciph(output^1) || ciph(output^2) || ...
            //
            // this code saves the bytes that would be modified by the xor,
            // updates the output with the xor, and then performs the ciph()
            // operation, placing each output in successive blocks following
            // the output. the original output in the first block is then
            // restored to its original value.
            //
            // the saving and restoration of the original value could be
            // moved outside of this loop, but in practice the input needs
            // to be very large to cause this loop to execute. therefore,
            // the operation happens inside the loop where it's unlikely
            // to be executed at all.
            for j in 1..r.len() / blksz {
                let (s, d) = r.split_at_mut(blksz);
                let l = (j - 1) * blksz;

                let w = byteorder::BigEndian::read_u32(&s[blksz - 4..]);
                byteorder::BigEndian::write_u32(
                    &mut s[blksz - 4..],
                    w ^ j as u32,
                );
                ffx.ciph(s, &mut d[l..l + blksz])?;
                byteorder::BigEndian::write_u32(&mut s[blksz - 4..], w);
            }

            // (step 6iv)
            let y = num_bigint::BigInt::from_bytes_be(
                num_bigint::Sign::Plus,
                &r[..d],
            );

            // (step 6vi, partial)
            match which {
                ffx::CipherType::Encrypt => na += y,
                ffx::CipherType::Decrypt => na -= y,
            }
            na = na.rem_euclid(&mu);
            // (step 6v, partial)
            std::mem::swap(&mut mu, &mut mv);

            // (step 6viii, 6ix. step 6vii is not necessary)
            std::mem::swap(&mut na, &mut nb);
        }

        // during decryption, the halves are reversed. put em back
        if let ffx::CipherType::Decrypt = which {
            std::mem::swap(&mut na, &mut nb);
        }

        // (step 7)
        Ok([
            ffx.bignum_to_chars(&na, Some(u))?,
            ffx.bignum_to_chars(&nb, Some(v))?,
        ]
        .concat())
    }

    // common function to convert the input String to a sequence
    // of chars before the cipher operation and back again after
    fn cipher_string(
        &self,
        inp_s: &str,
        opt_t: Option<&[u8]>,
        which: ffx::CipherType,
    ) -> Result<String> {
        let mut inp_c = Vec::<char>::with_capacity(inp_s.chars().count());
        inp_s.chars().for_each(|c| inp_c.push(c));

        let out_c = self.cipher_chars(&inp_c, opt_t, which)?;
        Ok(String::from_iter(out_c))
    }

    /// Encrypt a string
    ///
    /// If the tweak is not None, then the specified tweak will be used
    /// instead of the default specified by the context structure.
    pub fn encrypt(&self, pt: &str, twk: Option<&[u8]>) -> Result<String> {
        self.cipher_string(pt, twk, ffx::CipherType::Encrypt)
    }

    /// Decrypt a string
    ///
    /// If the tweak is not None, then the specified tweak will be used
    /// instead of the default specified by the context structure. The
    /// tweak used must match that used during encryption.
    pub fn decrypt(&self, ct: &str, twk: Option<&[u8]>) -> Result<String> {
        self.cipher_string(ct, twk, ffx::CipherType::Decrypt)
    }
}

fn cipher(
    key: &[u8],
    twk: Option<&[u8]>,
    txt: &str,
    radix: usize,
    alpha: Option<&str>,
    op: fn(&FF1, &str, Option<&[u8]>) -> Result<String>,
) -> Result<String> {
    let ff1 = FF1::new(key, None, 0, 0, radix, alpha)?;
    return op(&ff1, txt, twk);
}

pub fn encrypt(
    key: &[u8],
    twk: Option<&[u8]>,
    pt: &str,
    radix: usize,
    alpha: Option<&str>,
) -> Result<String> {
    return cipher(key, twk, pt, radix, alpha, FF1::encrypt);
}

pub fn decrypt(
    key: &[u8],
    twk: Option<&[u8]>,
    ct: &str,
    radix: usize,
    alpha: Option<&str>,
) -> Result<String> {
    return cipher(key, twk, ct, radix, alpha, FF1::decrypt);
}

#[cfg(test)]
mod tests {}