|
OLD | NEW |
---|---|
(Empty) | |
1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
4 /* $Id: cts.c,v 1.27 2012/04/25 14:49:43 gerv%gerv.net Exp $ */ | |
5 | |
6 #ifdef FREEBL_NO_DEPEND | |
7 #include "stubs.h" | |
8 #endif | |
9 #include "blapit.h" | |
10 #include "blapii.h" | |
11 #include "cts.h" | |
12 #include "secerr.h" | |
13 | |
14 struct CTSContextStr { | |
15 freeblCipherFunc cipher; | |
16 void *context; | |
17 unsigned char iv[MAX_BLOCK_SIZE]; | |
18 }; | |
19 | |
20 CTSContext * | |
21 CTS_CreateContext(void *context, freeblCipherFunc cipher, | |
22 const unsigned char *iv, unsigned int blocksize) | |
23 { | |
24 CTSContext *cts; | |
25 | |
26 cts = PORT_ZNew(CTSContext); | |
27 if (cts == NULL) { | |
28 return NULL; | |
29 } | |
30 PORT_Memcpy(cts->iv, iv, blocksize); | |
31 cts->cipher = cipher; | |
32 cts->context = context; | |
33 return cts; | |
34 } | |
35 | |
36 void | |
37 CTS_DestroyContext(CTSContext *cts, PRBool freeit) | |
38 { | |
39 if (freeit) { | |
40 PORT_Free(cts); | |
41 } | |
42 } | |
43 | |
44 /* | |
45 * See addemdum to NIST SP 800-38A | |
46 * Generically handle cipher text stealing. Basically this is doing CBC | |
47 * operations except someone can pass us a partial block. | |
48 * | |
49 * Output Order: | |
50 * CS-1: C1||C2||C3..Cn-1(could be partial)||Cn (NIST) | |
51 * CS-2: pad == 0 C1||C2||C3...Cn-1(is full)||Cn (Schneier) | |
52 * CS-2: pad != 0 C1||C2||C3...Cn||Cn-1(is partial)(Schneier) | |
53 * CS-3: C1||C2||C3...Cn||Cn-1(could be partial) (Kerberos) | |
54 * | |
55 * The characteristics of these three options: | |
56 * - NIST & Schneier (CS-1 & CS-2) are identical to CBC if there are no | |
57 * partial blocks on input. | |
58 * - Scheier and Kerberos (CS-2 and CS-3) have no embedded partial blocks, | |
59 * which make decoding easier. | |
60 * - NIST & Kerberos (CS-1 and CS-3) have consistant block order independent | |
61 * of padding. | |
62 * | |
63 * PKCS #11 did not specify which version to implement, but points to the NIST | |
64 * spec, so this code implements CTS-CS-1 from NIST. | |
65 * | |
66 * To convert the returned buffer to: | |
67 * CS-2 (Schneier): do | |
68 * unsigned char tmp[MAX_BLOCK_SIZE]; | |
69 * pad = *outlen % blocksize; | |
70 * if (pad) { | |
71 * memcpy(tmp, outbuf+*outlen-blocksize, blocksize); | |
72 * memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad); | |
73 * memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize); | |
74 * } | |
75 * CS-3 (Kerberos): do | |
76 * unsigned char tmp[MAX_BLOCK_SIZE]; | |
77 * pad = *outlen % blocksize; | |
78 * if (pad == 0) { | |
79 * pad = blocksize; | |
80 * } | |
81 * memcpy(tmp, outbuf+*outlen-blocksize, blocksize); | |
82 * memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad); | |
83 * memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize); | |
84 */ | |
85 SECStatus | |
86 CTS_EncryptUpdate(CTSContext *cts, unsigned char *outbuf, | |
87 unsigned int *outlen, unsigned int maxout, | |
88 const unsigned char *inbuf, unsigned int inlen, | |
89 unsigned int blocksize) | |
90 { | |
91 unsigned char lastBlock[MAX_BLOCK_SIZE]; | |
92 unsigned int tmp; | |
93 int fullblocks; | |
94 int written; | |
95 SECStatus rv; | |
96 | |
97 if (inlen < blocksize) { | |
98 PORT_SetError(SEC_ERROR_INPUT_LEN); | |
99 return SECFailure; | |
100 } | |
101 | |
102 if (maxout < inlen) { | |
103 *outlen = inlen; | |
104 PORT_SetError(SEC_ERROR_OUTPUT_LEN); | |
105 return SECFailure; | |
106 } | |
107 fullblocks = (inlen/blocksize)*blocksize; | |
Ryan Sleevi
2012/09/11 19:34:30
nit: The rest of the file uses spaces between oper
| |
108 rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, | |
109 fullblocks, blocksize); | |
110 if (rv != SECSuccess) { | |
111 return SECFailure; | |
112 } | |
113 inbuf += fullblocks; | |
114 inlen -= fullblocks; | |
115 if (inlen == 0) { | |
116 return SECSuccess; | |
117 } | |
118 written = *outlen - (blocksize - inlen); | |
119 outbuf += written; | |
120 maxout -= written; | |
121 | |
122 /* | |
123 * here's the CTS magic, we pad our final block with zeros, | |
Ryan Sleevi
2012/09/11 19:34:30
From merely inspecting the code (I've not yet fire
rjrejyea
2012/09/19 21:56:02
So your current understanding any my initial under
Ryan Sleevi
2012/09/19 22:21:35
I'm not convinced that's the case.
Calls to C_Enc
rjrejyea
2012/09/19 22:52:48
You are misreading the table. If you see the AES c
Ryan Sleevi
2012/09/19 23:04:53
See linked bug - looks like Nelson read the spec t
| |
124 * then do a CBC encrypt. CBC will xor our plain text with | |
125 * the previous block (Cn-1), capturing part of that block (Cn-1**) as it | |
126 * xors with the zero pad. We then write this full block, overwritting | |
127 * (Cn-1**) in our buffer. This allows us to have input data == output | |
128 * data since Cn contains enough information to reconver Cn-1** when | |
129 * we decrypt (at the cost of some complexity as you can see in decrypt | |
130 * below */ | |
131 PORT_Memset(lastBlock, 0, blocksize); | |
Ryan Sleevi
2012/09/11 19:34:30
In the "premature optimization" department, it's w
| |
132 PORT_Memcpy(lastBlock, inbuf, inlen); | |
133 rv = (*cts->cipher)(cts->context, outbuf, &tmp, maxout, lastBlock, | |
134 blocksize, blocksize); | |
135 PORT_Memset(lastBlock, 0, blocksize); | |
136 return rv; | |
137 } | |
138 | |
139 | |
140 #define XOR_BLOCK(x,y,count) for(i=0; i < count; i++) x[i] = x[i] ^ y[i] | |
Ryan Sleevi
2012/09/11 19:34:30
In CTR and GCM, you defined this as a function (ct
rjrejyea
2012/09/19 21:56:02
Because we really need a freebl version of xor. Al
| |
141 | |
142 /* | |
143 * See addemdum to NIST SP 800-38A | |
144 * Decrypt, Expect CS-1: input. See the comment on the encrypt side | |
145 * to understand what CS-2 and CS-3 mean. | |
146 * | |
147 * To convert the input buffer to CS-1 from ... | |
148 * CS-2 (Schneier): do | |
149 * unsigned char tmp[MAX_BLOCK_SIZE]; | |
150 * pad = inlen % blocksize; | |
151 * if (pad) { | |
152 * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); | |
153 * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); | |
154 * memcpy(inbuf+inlen-blocksize, tmp, blocksize); | |
155 * } | |
156 * CS-3 (Kerberos): do | |
157 * unsigned char tmp[MAX_BLOCK_SIZE]; | |
158 * pad = inlen % blocksize; | |
159 * if (pad == 0) { | |
160 * pad = blocksize; | |
161 * } | |
162 * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); | |
163 * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); | |
164 * memcpy(inbuf+inlen-blocksize, tmp, blocksize); | |
165 */ | |
166 SECStatus | |
167 CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf, | |
168 unsigned int *outlen, unsigned int maxout, | |
169 const unsigned char *inbuf, unsigned int inlen, | |
170 unsigned int blocksize) | |
171 { | |
172 unsigned char *Pn; | |
173 unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */ | |
174 unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */ | |
175 unsigned char Cn[MAX_BLOCK_SIZE]; /* block Cn */ | |
176 unsigned char lastBlock[MAX_BLOCK_SIZE]; | |
177 const unsigned char *tmp; | |
178 unsigned int tmpLen; | |
179 int fullblocks, pad, i; | |
180 SECStatus rv; | |
181 | |
182 if (inlen < blocksize) { | |
183 PORT_SetError(SEC_ERROR_INPUT_LEN); | |
184 return SECFailure; | |
185 } | |
186 | |
187 if (maxout < inlen) { | |
188 *outlen = inlen; | |
189 PORT_SetError(SEC_ERROR_OUTPUT_LEN); | |
190 return SECFailure; | |
191 } | |
192 | |
193 fullblocks = (inlen/blocksize)*blocksize; | |
Ryan Sleevi
2012/09/11 19:34:30
This is a misleading variable name, as it's not th
| |
194 | |
195 /* even though we expect the input to be CS-1, CS-2 is easier to parse, | |
196 * so convert to CS-2 immediately. NOTE: this is the same code as in | |
197 * the comment for encrypt. NOTE2: since we can't modify inbuf unless | |
198 * inbuf and outbuf overlap, just copy inbuf to outbuf and modify it there | |
199 */ | |
200 pad = blocksize - (fullblocks - inlen); | |
201 if (pad != blocksize) { | |
202 if (inbuf != outbuf) { | |
203 memcpy(outbuf, inbuf, inlen); | |
Ryan Sleevi
2012/09/11 19:34:30
This seems rather sub-optimal that an entire copy
wtc
2012/09/14 01:16:42
I didn't study the cts.c code, so I don't know how
| |
204 /* keep the names so we logically know how we are using the | |
205 * buffers */ | |
206 inbuf = outbuf; | |
207 } | |
208 memcpy(lastBlock, inbuf+inlen-blocksize-pad, blocksize); | |
209 /* we know inbuf == outbuf now, inbuf is declared const and can't | |
210 * be the target, so use outbuf for the target here */ | |
211 memcpy(outbuf+inlen-blocksize-pad, inbuf+inlen-pad, pad); | |
212 memcpy(outbuf+inlen-blocksize, lastBlock, blocksize); | |
213 } | |
214 /* save the previous to last block so we can undo the misordered chaining*/ | |
215 tmp = (fullblocks < blocksize*2) ? cts->iv : | |
216 inbuf+fullblocks-blocksize*2; | |
217 PORT_Memcpy(Cn_2, tmp, blocksize); | |
218 PORT_Memcpy(Cn, inbuf+fullblocks-blocksize, blocksize); | |
219 rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, | |
220 fullblocks, blocksize); | |
221 if (rv != SECSuccess) { | |
222 return SECFailure; | |
223 } | |
224 inbuf += fullblocks; | |
225 inlen -= fullblocks; | |
226 if (inlen == 0) { | |
227 return SECSuccess; | |
228 } | |
229 outbuf += fullblocks; | |
230 maxout -= fullblocks; | |
231 | |
232 /* recover the stolen text */ | |
233 PORT_Memset(lastBlock, 0, blocksize); | |
234 PORT_Memcpy(lastBlock, inbuf, inlen); | |
235 PORT_Memcpy(Cn_1, inbuf, inlen); | |
236 Pn = outbuf-blocksize; | |
237 /* inbuf points to Cn-1* in the input buffer */ | |
238 /* NOTE: below there are 2 sections marked "make up for the out of order | |
239 * cbc decryption". You may ask, what is going on here. | |
240 * Short answer: CBC automatically xors the plain text with the previous | |
241 * encrypted block. We are decrypting the last 2 blocks out of order, so | |
242 * we have to 'back out' the decrypt xor and 'add back' the encrypt xor. | |
243 * Long answer: When we encrypted, we encrypted as follows: | |
244 * Pn-2, Pn-1, (Pn || 0), but on decryption we can't | |
245 * decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in | |
246 * Cn (see below). So above we decrypted all the full blocks: | |
247 * Cn-2, Cn, | |
248 * to get: | |
249 * Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we | |
250 * xor'd Pn || 0 with Cn-1, but on decrypt we xor'd it with Cn-2 | |
251 * To recover Pn, we xor the block with Cn-1* || 0 (in last block) and | |
252 * Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer | |
253 * and we can now reunite Cn-1. With the full Cn-1 we can decrypt it, | |
254 * but now decrypt is going to xor the decrypted data with Cn instead of | |
255 * Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now | |
256 * write that oout to the buffer */ | |
257 | |
258 /* make up for the out of order CBC decryption */ | |
259 XOR_BLOCK(lastBlock, Cn_2, blocksize); | |
260 XOR_BLOCK(lastBlock, Pn, blocksize); | |
261 /* last buf now has Pn || Cn-1**, copy out Pn */ | |
262 PORT_Memcpy(outbuf, lastBlock, inlen); | |
263 *outlen += inlen; | |
264 /* copy Cn-1* into last buf to recover Cn-1 */ | |
265 PORT_Memcpy(lastBlock, Cn-1, inlen); | |
266 /* note: because Cn and Cn-1 were out of order, our pointer to Pn also | |
267 * points to where Pn-1 needs to reside. From here on out read Pn in | |
268 * the code as really Pn-1. */ | |
269 rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock, | |
270 blocksize, blocksize); | |
271 if (rv != SECSuccess) { | |
272 return SECFailure; | |
273 } | |
274 /* make up for the out of order CBC decryption */ | |
275 XOR_BLOCK(Pn, Cn_2, blocksize); | |
276 XOR_BLOCK(Pn, Cn, blocksize); | |
277 /* reset iv to Cn */ | |
278 PORT_Memcpy(cts->iv, Cn, blocksize); | |
279 /* This makes Cn the last block for the next decrypt operation, which | |
280 * matches the encrypt. We don't care about the contexts of last block, | |
281 * only the side effect of setting the internal IV */ | |
282 (void) (*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn, | |
283 blocksize, blocksize); | |
284 /* clear last block. At this point last block contains Pn xor Cn_1 xor | |
285 * Cn_2, both of with an attacker would know, so we need to clear this | |
286 * buffer out */ | |
287 PORT_Memset(lastBlock, 0, blocksize); | |
Ryan Sleevi
2012/09/11 19:34:30
nit: Does NSS rely on PORT_Memset for cleansing?
rjrejyea
2012/09/19 21:56:02
Any data on the stack is still on that stack until
| |
288 /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */ | |
289 return SECSuccess; | |
290 } | |
291 | |
OLD | NEW |