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