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

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

Powered by Google App Engine
This is Rietveld 408576698