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

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: 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 /* $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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698