1 /**
2 * Rivest's Package Tranform
3 * 
4 * Copyright:
5 * (C) 2009 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.constructs.aont_package;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_AONT):
15 
16 // todo: write unit tests
17 
18 import botan.block.block_cipher;
19 import botan.rng.rng;
20 import botan.filters.filters;
21 import botan.stream.ctr;
22 import botan.utils.get_byte;
23 import botan.utils.xor_buf;
24 import botan.algo_base.symkey;
25 
26 /**
27 * Rivest's Package Tranform
28 * Params:
29 *  rng = the random number generator to use
30 *  cipher = the block cipher to use
31 *  input = the input data buffer
32 *  input_len = the length of the input data in bytes
33 *  output = the output data buffer (must be at least
34 *          input_len + cipher.BLOCK_SIZE bytes long)
35 */
36 void aontPackage(RandomNumberGenerator rng,
37                  BlockCipher cipher,
38                  const(ubyte)* input, size_t input_len,
39                  ubyte* output)
40 {
41     import std.algorithm : fill;
42     const size_t BLOCK_SIZE = cipher.blockSize();
43     
44     if (!cipher.validKeylength(BLOCK_SIZE))
45         throw new InvalidArgument("AONT::package: Invalid cipher");
46     
47     // The all-zero string which is used both as the CTR IV and as K0
48     Vector!ubyte all_zeros = Vector!ubyte(BLOCK_SIZE*2);
49     zeroise(all_zeros);
50     
51     SymmetricKey package_key = SymmetricKey(rng, BLOCK_SIZE);
52     
53     Pipe pipe = Pipe(new StreamCipherFilter(new CTRBE(cipher), package_key));
54     
55     pipe.processMsg(input, input_len);
56     pipe.read(output, pipe.remaining());
57     
58     // Set K0 (the all zero key)
59     cipher.setKey(SymmetricKey(all_zeros));
60     
61     SecureVector!ubyte buf = SecureVector!ubyte(BLOCK_SIZE);
62 
63     const size_t blocks = (input_len + BLOCK_SIZE - 1) / BLOCK_SIZE;
64     
65     ubyte* final_block = output + input_len;
66     clearMem(final_block, BLOCK_SIZE);
67     
68     // XOR the hash blocks into the final block
69     foreach (size_t i; 0 .. blocks)
70     {
71         const size_t left = std.algorithm.min(BLOCK_SIZE, input_len - BLOCK_SIZE * i);
72         
73         zeroise(buf);
74         copyMem(buf.ptr, output + (BLOCK_SIZE * i), left);
75         
76         for (size_t j = 0; j != i.sizeof; ++j)
77             buf[BLOCK_SIZE - 1 - j] ^= get_byte((i).sizeof-1-j, i);
78         
79         cipher.encrypt(buf.ptr);
80         
81         xorBuf(final_block, buf.ptr, BLOCK_SIZE);
82     }
83     
84     // XOR the random package key into the final block
85     xorBuf(final_block, package_key.ptr, BLOCK_SIZE);
86 }
87 
88 /**
89 * Rivest's Package Tranform (Inversion)
90 * Params:
91 *  cipher = the block cipher to use
92 *  input = the input data buffer
93 *  input_len = the length of the input data in bytes
94 *  output = the output data buffer (must be at least
95 *          input_len - cipher.BLOCK_SIZE bytes long)
96 */
97 void aontUnpackage(BlockCipher cipher,
98                     const(ubyte)* input, size_t input_len,
99                     ubyte* output)
100 {
101     const size_t BLOCK_SIZE = cipher.blockSize();
102     
103     if (!cipher.validKeylength(BLOCK_SIZE))
104         throw new InvalidArgument("AONT::unpackage: Invalid cipher");
105     
106     if (input_len < BLOCK_SIZE)
107         throw new InvalidArgument("AONT::unpackage: Input too short");
108     
109     // The all-zero string which is used both as the CTR IV and as K0
110     Vector!ubyte all_zeros = Vector!ubyte(BLOCK_SIZE*2);
111     all_zeros.zeroise();
112     
113     cipher.setKey(SymmetricKey(all_zeros));
114     
115     SecureVector!ubyte package_key = SecureVector!ubyte(BLOCK_SIZE);
116     SecureVector!ubyte buf = SecureVector!ubyte(BLOCK_SIZE);
117     
118     // Copy the package key (masked with the block hashes)
119     copyMem(package_key.ptr, input + (input_len - BLOCK_SIZE), BLOCK_SIZE);
120     
121     const size_t blocks = ((input_len - 1) / BLOCK_SIZE);
122     
123     // XOR the blocks into the package key bits
124     foreach (size_t i; 0 .. blocks)
125     {
126         const size_t left = std.algorithm.min(BLOCK_SIZE,
127                                               input_len - BLOCK_SIZE * (i+1));
128         
129         zeroise(buf);
130         copyMem(buf.ptr, input + (BLOCK_SIZE * i), left);
131         
132         for (size_t j = 0; j != (i).sizeof; ++j)
133             buf[BLOCK_SIZE - 1 - j] ^= get_byte((i).sizeof-1-j, i);
134         
135         cipher.encrypt(buf.ptr);
136         
137         xorBuf(package_key.ptr, buf.ptr, BLOCK_SIZE);
138     }
139     
140     Pipe pipe = Pipe(new StreamCipherFilter(new CTRBE(cipher), SymmetricKey(package_key)));
141     
142     pipe.processMsg(input, input_len - BLOCK_SIZE);
143     
144     pipe.read(output, pipe.remaining());
145 
146 
147 }