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