Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(345)

Side by Side Diff: nss/mozilla/security/nss/lib/freebl/cts.c

Issue 10919163: Add GCM, CTR, and CTS modes to AES. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/deps/third_party/
Patch Set: Fix comments as rsleevi suggested, fix a 32-bit bug and miscellaneous issues Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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
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 PORT_Assert(*outlen == fullblocks);
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 PORT_Assert(tmp == blocksize);
144 *outlen = written + blocksize;
145 }
146 return rv;
147 }
148
149
150 #define XOR_BLOCK(x,y,count) for(i=0; i < count; i++) x[i] = x[i] ^ y[i]
151
152 /*
153 * See addemdum to NIST SP 800-38A
154 * Decrypt, Expect CS-1: input. See the comment on the encrypt side
155 * to understand what CS-2 and CS-3 mean.
156 *
157 * To convert the input buffer to CS-1 from ...
158 * CS-2 (Schneier): do
159 * unsigned char tmp[MAX_BLOCK_SIZE];
160 * pad = inlen % blocksize;
161 * if (pad) {
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 * CS-3 (Kerberos): do
167 * unsigned char tmp[MAX_BLOCK_SIZE];
168 * pad = inlen % blocksize;
169 * if (pad == 0) {
170 * pad = blocksize;
171 * }
172 * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
173 * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
174 * memcpy(inbuf+inlen-blocksize, tmp, blocksize);
175 */
176 SECStatus
177 CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf,
178 unsigned int *outlen, unsigned int maxout,
179 const unsigned char *inbuf, unsigned int inlen,
180 unsigned int blocksize)
181 {
182 unsigned char *Pn;
183 unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */
184 unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */
185 unsigned char Cn[MAX_BLOCK_SIZE]; /* block Cn */
186 unsigned char lastBlock[MAX_BLOCK_SIZE];
187 const unsigned char *tmp;
188 unsigned int tmpLen;
189 int fullblocks, pad;
190 unsigned int i;
191 SECStatus rv;
192
193 if (inlen < blocksize) {
194 PORT_SetError(SEC_ERROR_INPUT_LEN);
195 return SECFailure;
196 }
197
198 if (maxout < inlen) {
199 *outlen = inlen;
200 PORT_SetError(SEC_ERROR_OUTPUT_LEN);
201 return SECFailure;
202 }
203
204 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 pad = blocksize + (inlen - fullblocks);
212 if (pad != blocksize) {
213 if (inbuf != outbuf) {
214 memcpy(outbuf, inbuf, inlen);
215 /* keep the names so we logically know how we are using the
216 * buffers */
217 inbuf = outbuf;
218 }
219 memcpy(lastBlock, inbuf+inlen-blocksize-pad, 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 memcpy(outbuf+inlen-blocksize-pad, inbuf+inlen-pad, pad);
223 memcpy(outbuf+inlen-blocksize, lastBlock, blocksize);
224 }
225 /* save the previous to last block so we can undo the misordered
226 * chaining */
227 tmp = (fullblocks < blocksize*2) ? cts->iv :
228 inbuf+fullblocks-blocksize*2;
229 PORT_Memcpy(Cn_2, tmp, blocksize);
230 PORT_Memcpy(Cn, inbuf+fullblocks-blocksize, blocksize);
231 rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf,
232 fullblocks, blocksize);
233 if (rv != SECSuccess) {
234 return SECFailure;
235 }
236 PORT_Assert(*outlen == fullblocks);
237 inbuf += fullblocks;
238 inlen -= fullblocks;
239 if (inlen == 0) {
240 return SECSuccess;
241 }
242 outbuf += fullblocks;
243 maxout -= fullblocks;
244
245 /* recover the stolen text */
246 PORT_Memset(lastBlock, 0, blocksize);
247 PORT_Memcpy(lastBlock, inbuf, inlen);
248 PORT_Memcpy(Cn_1, inbuf, inlen);
249 Pn = outbuf-blocksize;
250 /* inbuf points to Cn-1* in the input buffer */
251 /* NOTE: below there are 2 sections marked "make up for the out of order
252 * cbc decryption". You may ask, what is going on here.
253 * Short answer: CBC automatically xors the plain text with the previous
254 * encrypted block. We are decrypting the last 2 blocks out of order, so
255 * we have to 'back out' the decrypt xor and 'add back' the encrypt xor.
256 * Long answer: When we encrypted, we encrypted as follows:
257 * Pn-2, Pn-1, (Pn || 0), but on decryption we can't
258 * decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in
259 * Cn (see below). So above we decrypted all the full blocks:
260 * Cn-2, Cn,
261 * to get:
262 * Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we
263 * xor'd Pn || 0 with Cn-1, but on decrypt we xor'd it with Cn-2
264 * To recover Pn, we xor the block with Cn-1* || 0 (in last block) and
265 * Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer
266 * and we can now reunite Cn-1. With the full Cn-1 we can decrypt it,
267 * but now decrypt is going to xor the decrypted data with Cn instead of
268 * Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now
269 * write that oout to the buffer */
270
271 /* make up for the out of order CBC decryption */
272 XOR_BLOCK(lastBlock, Cn_2, blocksize);
273 XOR_BLOCK(lastBlock, Pn, blocksize);
274 /* last buf now has Pn || Cn-1**, copy out Pn */
275 PORT_Memcpy(outbuf, lastBlock, inlen);
276 *outlen += inlen;
277 /* copy Cn-1* into last buf to recover Cn-1 */
278 PORT_Memcpy(lastBlock, Cn-1, inlen);
279 /* note: because Cn and Cn-1 were out of order, our pointer to Pn also
280 * points to where Pn-1 needs to reside. From here on out read Pn in
281 * the code as really Pn-1. */
282 rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock,
283 blocksize, blocksize);
284 if (rv != SECSuccess) {
285 return SECFailure;
286 }
287 PORT_Assert(tmpLen == blocksize);
288 /* make up for the out of order CBC decryption */
289 XOR_BLOCK(Pn, Cn_2, blocksize);
290 XOR_BLOCK(Pn, Cn, blocksize);
291 /* reset iv to Cn */
292 PORT_Memcpy(cts->iv, Cn, blocksize);
293 /* This makes Cn the last block for the next decrypt operation, which
294 * matches the encrypt. We don't care about the contexts of last block,
295 * only the side effect of setting the internal IV */
296 (void) (*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn,
297 blocksize, blocksize);
298 /* clear last block. At this point last block contains Pn xor Cn_1 xor
299 * Cn_2, both of with an attacker would know, so we need to clear this
300 * buffer out */
301 PORT_Memset(lastBlock, 0, blocksize);
302 /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */
303 return SECSuccess;
304 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698