1 /**
2 * AES Key Wrap (RFC 3394)
3 * 
4 * Copyright:
5 * (C) 2011 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.rfc3394;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_RFC3394_KEYWRAP):
15 
16 import botan.algo_base.symkey;
17 import botan.algo_factory.algo_factory;
18 import botan.block.block_cipher;
19 import botan.utils.loadstor;
20 import botan.utils.exceptn;
21 import botan.utils.xor_buf;
22 import botan.utils.mem_ops;
23 import botan.algo_factory.algo_factory;
24 import botan.utils.types;
25 
26 /**
27 * Encrypt a key under a key encryption key using the algorithm
28 * described in RFC 3394
29 *
30 * Params:
31 *  key = the plaintext key to encrypt
32 *  kek = the key encryption key
33 *  af = an algorithm factory
34 * Returns: key encrypted under kek
35 */
36 SecureVector!ubyte rfc3394Keywrap()(auto const ref SecureVector!ubyte key,
37                                     in SymmetricKey kek,
38                                     AlgorithmFactory af)
39 {
40     if (key.length % 8 != 0)
41         throw new InvalidArgument("Bad input key size for NIST key wrap");
42     
43     Unique!BlockCipher aes = makeAes(kek.length, af);
44     aes.setKey(kek);
45     
46     const size_t n = key.length / 8;
47     
48     SecureVector!ubyte R = SecureVector!ubyte((n + 1) * 8);
49     SecureVector!ubyte A = SecureVector!ubyte(16);
50     
51     foreach (size_t i; 0 .. 8)
52         A[i] = 0xA6;
53     
54     copyMem(&R[8], key.ptr, key.length);
55     
56     foreach (size_t j; 0 .. 5 + 1)
57     {
58         foreach (size_t i; 1 .. n + 1)
59         {
60             const uint t = cast(uint) ((n * j) + i);
61             
62             copyMem(&A[8], &R[8*i], 8);
63             
64             aes.encrypt(A.ptr);
65             copyMem(&R[8*i], &A[8], 8);
66             
67             ubyte[4] t_buf;
68             storeBigEndian(t, t_buf.ptr);
69             xorBuf(&A[4], t_buf.ptr, 4);
70         }
71     }
72     
73     copyMem(R.ptr, A.ptr, 8);
74     
75     return R;
76 }
77 
78 /**
79 * Decrypt a key under a key encryption key using the algorithm
80 * described in RFC 3394
81 *
82 * Params:
83 *  key = the encrypted key to decrypt
84 *  kek = the key encryption key
85 *  af = an algorithm factory
86 * Returns: key decrypted under kek
87 */
88 SecureVector!ubyte rfc3394Keyunwrap()(auto const ref SecureVector!ubyte key,
89                                       in SymmetricKey kek,
90                                       AlgorithmFactory af)
91 {
92     if (key.length < 16 || key.length % 8 != 0)
93         throw new InvalidArgument("Bad input key size for NIST key unwrap");
94     
95     Unique!BlockCipher aes = makeAes(kek.length, af);
96     aes.setKey(kek);
97     
98     const size_t n = (key.length - 8) / 8;
99     
100     SecureVector!ubyte R = SecureVector!ubyte(n * 8);
101     SecureVector!ubyte A = SecureVector!ubyte(16);
102     
103     foreach (size_t i; 0 .. 8)
104         A[i] = key[i];
105     
106     copyMem(R.ptr, &key[8], key.length - 8);
107     
108     foreach (size_t j; 0 .. 5 + 1)
109     {
110         for (size_t i = n; i != 0; --i)
111         {
112             const uint t = cast(uint)( (5 - j) * n + i );
113             
114             ubyte[4] t_buf;
115             storeBigEndian(t, &t_buf);
116             
117             xorBuf(&A[4], t_buf.ptr, 4);
118             
119             copyMem(&A[8], &R[8*(i-1)], 8);
120             
121             aes.decrypt(A.ptr);
122             
123             copyMem(&R[8*(i-1)], &A[8], 8);
124         }
125     }
126     
127     if (loadBigEndian!ulong(A.ptr, 0) != 0xA6A6A6A6A6A6A6A6)
128         throw new IntegrityFailure("NIST key unwrap failed");
129     
130     return R;
131 }
132 
133 private:
134 
135 BlockCipher makeAes(size_t keylength, AlgorithmFactory af)
136 {
137     if (keylength == 16)
138         return af.makeBlockCipher("AES-128");
139     else if (keylength == 24)
140         return af.makeBlockCipher("AES-192");
141     else if (keylength == 32)
142         return af.makeBlockCipher("AES-256");
143     else
144         throw new InvalidArgument("Bad KEK length for NIST keywrap");
145 }
146 
147 
148 static if (BOTAN_TEST):
149 
150 import botan.test;
151 import botan.codec.hex;
152 import botan.libstate.libstate;
153 
154 size_t keywrapTest(string key_str,
155                    string expected_str,
156                    string kek_str)
157 {
158     size_t fail = 0;
159     
160     try
161     {
162         SymmetricKey key = SymmetricKey(key_str);
163         SymmetricKey expected = SymmetricKey(expected_str);
164         SymmetricKey kek = SymmetricKey(kek_str);
165         
166         AlgorithmFactory af = globalState().algorithmFactory();
167         
168         SecureVector!ubyte enc = rfc3394Keywrap(key.bitsOf(), kek, af);
169         
170         if (enc != expected.bitsOf())
171         {
172             logTrace("NIST key wrap encryption failure: ", hexEncode(enc), " != ", hexEncode(expected.bitsOf()));
173             fail++;
174         }
175         
176         SecureVector!ubyte dec = rfc3394Keyunwrap(expected.bitsOf(), kek, af);
177         
178         if (dec != key.bitsOf())
179         {
180             logTrace("NIST key wrap decryption failure: " ~ hexEncode(dec) ~ " != " ~ hexEncode(key.bitsOf()));
181             fail++;
182         }
183     }
184     catch(Exception e)
185     {
186         logTrace(e.msg);
187         fail++;
188     }
189     
190     return fail;
191 }
192 
193 static if (BOTAN_HAS_TESTS && !SKIP_RFC3394_TEST) unittest
194 {
195     logDebug("Testing rfc3394.d ...");
196 
197     size_t fails = 0;
198     
199     fails += keywrapTest("00112233445566778899AABBCCDDEEFF",
200                          "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5",
201                          "000102030405060708090A0B0C0D0E0F");
202     
203     fails += keywrapTest("00112233445566778899AABBCCDDEEFF",
204                          "96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D",
205                          "000102030405060708090A0B0C0D0E0F1011121314151617");
206     
207     fails += keywrapTest("00112233445566778899AABBCCDDEEFF",
208                          "64E8C3F9CE0F5BA263E9777905818A2A93C8191E7D6E8AE7",
209                          "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
210     
211     fails += keywrapTest("00112233445566778899AABBCCDDEEFF0001020304050607",
212                          "031D33264E15D33268F24EC260743EDCE1C6C7DDEE725A936BA814915C6762D2",
213                          "000102030405060708090A0B0C0D0E0F1011121314151617");
214     
215     fails += keywrapTest("00112233445566778899AABBCCDDEEFF0001020304050607",
216                          "A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1",
217                          "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
218     
219     fails += keywrapTest("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F",
220                          "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21",
221                          "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
222     
223     testReport("rfc3394", 6, fails);
224 
225 }