]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_csp/lib/BlockCryptor.cpp
Security-55179.1.tar.gz
[apple/security.git] / libsecurity_apple_csp / lib / BlockCryptor.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 /*
20 * BlockCryptor.cpp - common context for block-oriented encryption algorithms
21 *
22 * Created March 5 2001 by dmitch
23 */
24
25 #include "BlockCryptor.h"
26 #include "BinaryKey.h"
27 #include "AppleCSPSession.h"
28 #include <security_utilities/alloc.h>
29 #include <Security/cssmerr.h>
30 #include <string.h>
31 #include <security_utilities/debugging.h>
32 #include <security_cdsa_utilities/cssmdata.h>
33
34 #define BlockCryptDebug(args...) secdebug("blockCrypt", ## args)
35 #define bprintf(args...) secdebug("blockCryptBuf", ## args)
36 #define ioprintf(args...) secdebug("blockCryptIo", ## args)
37
38 BlockCryptor::~BlockCryptor()
39 {
40 if(mInBuf) {
41 memset(mInBuf, 0, mInBlockSize);
42 session().free(mInBuf);
43 mInBuf = NULL;
44 }
45 if(mChainBuf) {
46 memset(mChainBuf, 0, mInBlockSize);
47 session().free(mChainBuf);
48 mChainBuf = NULL;
49 }
50 mInBufSize = 0;
51 }
52
53 /*
54 * Reusable setup functions called from subclass's init.
55 * This is the general purpose one....
56 */
57 void BlockCryptor::setup(
58 size_t blockSizeIn, // block size of input
59 size_t blockSizeOut, // block size of output
60 bool pkcsPad, // this class performs PKCS{5,7} padding
61 bool needsFinal, // needs final update with valid data
62 BC_Mode mode, // ECB, CBC
63 const CssmData *iv) // init vector, required for CBC
64 //Ê must be at least blockSizeIn bytes
65 {
66 if(pkcsPad && needsFinal) {
67 BlockCryptDebug("BlockCryptor::setup pkcsPad && needsFinal");
68 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
69 }
70 mPkcsPadding = pkcsPad;
71 mMode = mode;
72 mNeedFinalData = needsFinal;
73
74 /* set up inBuf, all configurations */
75 if(mInBuf != NULL) {
76 /* only reuse if same size */
77 if(mInBlockSize != blockSizeIn) {
78 session().free(mInBuf);
79 mInBuf = NULL;
80 }
81 }
82 if(mInBuf == NULL) {
83 mInBuf = (uint8 *)session().malloc(blockSizeIn);
84 }
85
86 /* set up chain buf, decrypt/CBC only; skip if algorithm does its own chaining */
87 if((mMode == BCM_CBC) && !encoding() && !mCbcCapable) {
88 if(mChainBuf != NULL) {
89 /* only reuse if same size */
90 if(mInBlockSize != blockSizeIn) {
91 session().free(mChainBuf);
92 mChainBuf = NULL;
93 }
94 }
95 if(mChainBuf == NULL) {
96 mChainBuf = (uint8 *)session().malloc(blockSizeIn);
97 }
98 }
99
100 /* IV iff CBC mode, and ensure IV is big enough */
101 switch(mMode) {
102 case BCM_ECB:
103 if(iv != NULL) {
104 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR);
105 }
106 break;
107 case BCM_CBC:
108 if(iv == NULL) {
109 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR);
110 }
111 if(blockSizeIn != blockSizeOut) {
112 /* no can do, must be same block sizes */
113 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE);
114 }
115 if(iv->Length < blockSizeIn) {
116 /* not enough IV */
117 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR);
118 }
119 /* save IV as appropriate */
120 if(!mCbcCapable) {
121 if(encoding()) {
122 memmove(mInBuf, iv->Data, blockSizeIn);
123 }
124 else {
125 assert(mChainBuf != NULL);
126 memmove(mChainBuf, iv->Data, blockSizeIn);
127 }
128 }
129 break;
130 }
131
132 mInBlockSize = blockSizeIn;
133 mInBufSize = 0;
134 mOutBlockSize = blockSizeOut;
135 mOpStarted = false;
136 }
137
138 /*
139 * This one is used by simple, well-behaved algorithms which don't do their own
140 * padding and which rely on us to do everything but one-block-at-a-time
141 * encrypt and decrypt.
142 */
143 void BlockCryptor::setup(
144 size_t blockSize, // block size of input and output
145 const Context &context)
146 {
147 bool padEnable = false;
148 bool chainEnable = false;
149 bool ivEnable = false;
150 CssmData *iv = NULL;
151
152 /*
153 * Validate context
154 * IV optional per mode
155 * pad optional per mode
156 * Currently we ignore extraneous attributes (e.g., it's OK to pass in
157 * an IV if the mode doesn't specify it), mainly for simplifying test routines.
158 */
159 CSSM_ENCRYPT_MODE cssmMode = context.getInt(CSSM_ATTRIBUTE_MODE);
160
161 switch (cssmMode) {
162 /* no mode attr --> 0 == CSSM_ALGMODE_NONE, not currently supported */
163 case CSSM_ALGMODE_CBCPadIV8:
164 padEnable = true;
165 ivEnable = true;
166 chainEnable = true;
167 break;
168
169 case CSSM_ALGMODE_CBC_IV8:
170 ivEnable = true;
171 chainEnable = true;
172 break;
173
174 case CSSM_ALGMODE_ECB:
175 break;
176
177 case CSSM_ALGMODE_ECBPad:
178 padEnable = true;
179 break;
180
181 default:
182 errorLog1("DESContext::init: illegal mode (%d)\n", (int)cssmMode);
183 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE);
184 }
185
186 if(padEnable) {
187 /* validate padding type */
188 uint32 padding = context.getInt(CSSM_ATTRIBUTE_PADDING); // 0 ==> PADDING_NONE
189 if(blockSize == 8) {
190 switch(padding) {
191 /* backwards compatibility - used to be PKCS1, should be PKCS5 or 7 */
192 case CSSM_PADDING_PKCS7:
193 case CSSM_PADDING_PKCS5:
194 case CSSM_PADDING_PKCS1: //Êthis goes away soon
195 /* OK */
196 break;
197 default:
198 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING);
199 }
200 }
201 else {
202 switch(padding) {
203 case CSSM_PADDING_PKCS5: // this goes away soon
204 case CSSM_PADDING_PKCS7:
205 /* OK */
206 break;
207 default:
208 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING);
209 }
210 }
211 }
212 if(ivEnable) {
213 /* make sure there's an IV in the context of sufficient length */
214 iv = context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR);
215 if(iv == NULL) {
216 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR);
217 }
218 if(iv->Length < blockSize) {
219 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR);
220 }
221 }
222 setup(blockSize,
223 blockSize,
224 padEnable,
225 false, // needsFinal
226 chainEnable ? BCM_CBC : BCM_ECB,
227 iv);
228 }
229
230 /*
231 * Update always leaves some data in mInBuf if:
232 * mNeedsFinalData is true, or
233 * decrypting and mPkcsPadding true.
234 * Also, we always process all of the input (except on error).
235 */
236 void BlockCryptor::update(
237 void *inp,
238 size_t &inSize, // in/out
239 void *outp,
240 size_t &outSize) // in/out
241 {
242 uint8 *uInp = (UInt8 *)inp;
243 uint8 *uOutp = (UInt8 *)outp;
244 size_t uInSize = inSize; // input bytes to go
245 size_t uOutSize = 0; // ouput bytes generated
246 size_t uOutLeft = outSize; // bytes remaining in outp
247 size_t toMove;
248 size_t actMoved;
249 unsigned i;
250 bool needLeftOver = mNeedFinalData || (!encoding() && mPkcsPadding);
251 bool doCbc = (mMode == BCM_CBC) && !mCbcCapable;
252
253 assert(mInBuf != NULL);
254 mOpStarted = true;
255
256 if(mInBufSize) {
257 /* attempt to fill mInBuf from inp */
258 toMove = mInBlockSize - mInBufSize;
259 if(toMove > uInSize) {
260 toMove = uInSize;
261 }
262 if(encoding() && doCbc) {
263 /* xor into last cipherblock or IV */
264 for(i=0; i<toMove; i++) {
265 mInBuf[mInBufSize + i] ^= *uInp++;
266 }
267 }
268 else {
269 /* use incoming data as is */
270 memmove(mInBuf+mInBufSize, uInp, toMove);
271 uInp += toMove;
272 }
273 uInSize -= toMove;
274 mInBufSize += toMove;
275 /*
276 * Process inBuf if it's full, but skip if no more data in uInp and
277 * inBuf might be needed (by us for unpadding on decrypt, or by
278 * subclass for anything) for a final call
279 */
280 if((mInBufSize == mInBlockSize) && !((uInSize == 0) && needLeftOver)) {
281 actMoved = uOutLeft;
282 if(encoding()) {
283 encryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
284 if(doCbc) {
285 /* save ciphertext for chaining next block */
286 assert(mInBlockSize == actMoved);
287 memmove(mInBuf, uOutp, mInBlockSize);
288 }
289 }
290 else {
291 decryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
292 if(doCbc) {
293 /* xor in last ciphertext */
294 assert(mInBlockSize == actMoved);
295 for(i=0; i<mInBlockSize; i++) {
296 uOutp[i] ^= mChainBuf[i];
297 }
298 /* save this ciphertext for next chain */
299 memmove(mChainBuf, mInBuf, mInBlockSize);
300 }
301 }
302 uOutSize += actMoved;
303 uOutp += actMoved;
304 uOutLeft -= actMoved;
305 mInBufSize = 0;
306 assert(uOutSize <= outSize);
307 }
308 } /* processing mInBuf */
309 if(uInSize == 0) {
310 /* done */
311 outSize = uOutSize;
312 ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
313 encoding() ? 1 : 0, inSize, outSize);
314 return;
315 }
316
317
318 /*
319 * en/decrypt even blocks in (remaining) inp.
320 */
321 unsigned leftOver = uInSize % mInBlockSize;
322 if((leftOver == 0) && needLeftOver) {
323 /*
324 * Even blocks coming in, but we really need to leave some data
325 * in the buffer (because the subclass asked for it, or we're decrypting
326 * with PKCS padding). Save one block for mInBuf.
327 */
328 leftOver = mInBlockSize;
329 }
330 toMove = uInSize - leftOver;
331 size_t blocks = toMove / mInBlockSize;
332 if(mMultiBlockCapable && !doCbc && (blocks != 0)) {
333 /*
334 * Optimization for algorithms that are multi-block capable and that
335 * can do their own CBC (if necessary).
336 */
337 size_t thisMove = blocks * mInBlockSize;
338 actMoved = uOutLeft;
339 if(encoding()) {
340 encryptBlock(uInp, thisMove, uOutp, actMoved, false);
341 }
342 else {
343 decryptBlock(uInp, thisMove, uOutp, actMoved, false);
344 }
345 uOutSize += actMoved;
346 uOutp += actMoved;
347 uInp += thisMove;
348 uOutLeft -= actMoved;
349 toMove -= thisMove;
350 assert(uOutSize <= outSize);
351 }
352 else if(encoding()) {
353 while(toMove) {
354 actMoved = uOutLeft;
355 if(!doCbc) {
356 /* encrypt directly from input to output */
357 encryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
358 }
359 else {
360 /* xor into last ciphertext, encrypt the result */
361 for(i=0; i<mInBlockSize; i++) {
362 mInBuf[i] ^= uInp[i];
363 }
364 encryptBlock(mInBuf, mInBlockSize, uOutp, actMoved, false);
365
366 /* save new ciphertext for next chain */
367 assert(actMoved == mInBlockSize);
368 memmove(mInBuf, uOutp, mInBlockSize);
369 }
370 uOutSize += actMoved;
371 uOutp += actMoved;
372 uInp += mInBlockSize;
373 uOutLeft -= actMoved;
374 toMove -= mInBlockSize;
375 assert(uOutSize <= outSize);
376 } /* main encrypt loop */
377
378 }
379 else {
380 /* decrypting */
381 while(toMove) {
382 actMoved = uOutLeft;
383 if(doCbc) {
384 /* save this ciphertext for chain; don't assume in != out */
385 memmove(mInBuf, uInp, mInBlockSize);
386 decryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
387
388 /* chain in previous ciphertext */
389 assert(mInBlockSize == actMoved);
390 for(i=0; i<mInBlockSize; i++) {
391 uOutp[i] ^= mChainBuf[i];
392 }
393
394 /* save current ciphertext for next block */
395 memmove(mChainBuf, mInBuf, mInBlockSize);
396 }
397 else {
398 /* ECB */
399 decryptBlock(uInp, mInBlockSize, uOutp, actMoved, false);
400 }
401 uOutSize += actMoved;
402 uOutp += actMoved;
403 uInp += mInBlockSize;
404 uOutLeft -= actMoved;
405 toMove -= mInBlockSize;
406 assert(uOutSize <= outSize);
407 } /* main decrypt loop */
408
409 }
410
411 /* leftover bytes from inp --> mInBuf */
412 if(leftOver) {
413 if(encoding() && doCbc) {
414 /* xor into last cipherblock or IV */
415 for(i=0; i<leftOver; i++) {
416 mInBuf[i] ^= *uInp++;
417 }
418 }
419 else {
420 if(mInBuf && uInp && leftOver) memmove(mInBuf, uInp, leftOver);
421 }
422 }
423
424 mInBufSize = leftOver;
425 outSize = uOutSize;
426 ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
427 encoding() ? 1 : 0, inSize, outSize);
428 }
429
430 void BlockCryptor::final(
431 CssmData &out)
432 {
433 size_t uOutSize = 0; // ouput bytes generated
434 size_t actMoved;
435 size_t uOutLeft = out.Length; // bytes remaining in out
436 unsigned i;
437 bool doCbc = (mMode == BCM_CBC) && !mCbcCapable;
438
439 assert(mInBuf != NULL);
440 mOpStarted = true;
441 if((mInBufSize == 0) && mNeedFinalData) {
442 /* only way this could happen: no update() called (at least not with
443 * non-zero input data sizes) */
444 BlockCryptDebug("BlockCryptor::final with no mInBuf data");
445 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
446 }
447 if(encoding()) {
448 uint8 *ctext = out.Data;
449
450 if(mPkcsPadding) {
451 /*
452 * PKCS5/7 padding: pad byte = size of padding.
453 * This assertion courtesy of the limitation on the mutual
454 * exclusivity of mPkcsPadding and mNeedFinalData.
455 */
456 assert(mInBufSize < mInBlockSize);
457 size_t padSize = mInBlockSize - mInBufSize;
458 uint8 *padPtr = mInBuf + mInBufSize;
459 if(!doCbc) {
460 for(i=0; i<padSize; i++) {
461 *padPtr++ = padSize;
462 }
463 }
464 else {
465 for(i=0; i<padSize; i++) {
466 *padPtr++ ^= padSize;
467 }
468 }
469 mInBufSize = mInBlockSize;
470 } /* PKCS padding */
471
472 /*
473 * Encrypt final mInBuf. If it's not full, the BlockCryptObject better know
474 * how to pad....
475 */
476 if(mInBufSize) {
477 actMoved = uOutLeft;
478 encryptBlock(mInBuf, mInBufSize, ctext, actMoved, true);
479 uOutSize += actMoved;
480 mInBufSize = 0;
481 assert(uOutSize <= out.length());
482 }
483 out.length(uOutSize);
484 } /* encrypting */
485
486 else {
487 if(mInBufSize == 0) {
488 if(mPkcsPadding) {
489 BlockCryptDebug("BlockCryptor::final decrypt/pad with no mInBuf data");
490 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
491 }
492 else {
493 /* simple decrypt op complete */
494 ioprintf("=== BlockCryptor::final encrypt 0 outSize 0");
495 out.length(0);
496 return;
497 }
498 }
499
500 /*
501 * Decrypt - must have exactly one block of ciphertext.
502 * We trust CSPContext, and our own outputSize(), to have set up
503 * the current output buffer with enough space to handle the
504 * full size of the decrypt, even though - due to padding - we
505 * might actually pass less than that amount back to caller.
506 */
507 if(mInBufSize != mInBlockSize) {
508 BlockCryptDebug("BlockCryptor::final unaligned ciphertext");
509 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR);
510 }
511
512 uint8 *ptext = out.Data;
513 actMoved = uOutLeft;
514 decryptBlock(mInBuf, mInBlockSize, ptext, actMoved, true);
515 if(doCbc) {
516 /* chain in previous ciphertext one more time */
517 assert(mInBlockSize == actMoved);
518 for(i=0; i<mInBlockSize; i++) {
519 ptext[i] ^= mChainBuf[i];
520 }
521 }
522 if(mPkcsPadding) {
523 assert(actMoved == mOutBlockSize);
524
525 /* ensure integrity of padding byte(s) */
526 unsigned padSize = ptext[mOutBlockSize - 1];
527 if(padSize > mOutBlockSize) {
528 BlockCryptDebug("BlockCryptor::final malformed ciphertext (1)");
529 CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA);
530 }
531 uint8 *padPtr = ptext + mOutBlockSize - padSize;
532 for(unsigned i=0; i<padSize; i++) {
533 if(*padPtr++ != padSize) {
534 BlockCryptDebug("BlockCryptor::final malformed ciphertext "
535 "(2)");
536 CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA);
537 }
538 }
539 actMoved -= padSize;
540 }
541 assert(actMoved <= out.length());
542 out.length(actMoved);
543 } /* decrypting */
544 ioprintf("=== BlockCryptor::final encrypt %d outSize 0x%lx",
545 encoding() ? 1 : 0, out.Length);
546 }
547
548 /*
549 * These three are only valid for algorithms for which encrypting one block
550 * of plaintext always yields exactly one block of ciphertext, and vice versa
551 * for decrypt. The block sizes for plaintext and ciphertext do NOT have to be
552 * the same. Subclasses (e.g. FEED) which do not meet this criterion will have
553 * to override.
554 */
555
556 void BlockCryptor::minimumProgress(
557 size_t &inSize,
558 size_t &outSize)
559 {
560 /* each size = one block (including buffered input) */
561 inSize = mInBlockSize - mInBufSize;
562 if(inSize == 0) {
563 /* i.e., we're holding a whole buffer */
564 inSize++;
565 }
566 outSize = mOutBlockSize;
567 bprintf("--- BlockCryptor::minProgres inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
568 inSize, outSize, mInBufSize);
569 }
570
571 size_t BlockCryptor::inputSize(
572 size_t outSize) // input for given output size
573 {
574 size_t inSize;
575
576 if(outSize < mOutBlockSize) {
577 /*
578 * Sometimes CSPFullPluginSession calls us like this....in this
579 * case the legal inSize is just the remainder of the input buffer,
580 * less one byte (in other words, the max we we gobble up without
581 * producing any output).
582 */
583 inSize = mInBlockSize - mInBufSize;
584 if(inSize == 0) {
585 /* we have a full input buffer! How can this happen!? */
586 BlockCryptDebug("BlockCryptor::inputSize: HELP! zero inSize and outSize!\n");
587 }
588 }
589 else {
590 /* more-or-less normal case */
591 size_t wholeBlocks = outSize / mOutBlockSize;
592 assert(wholeBlocks >= 1);
593 inSize = (wholeBlocks * mInBlockSize) - mInBufSize;
594 if(inSize == 0) {
595 /* i.e., we're holding a whole buffer */
596 inSize++;
597 }
598 }
599 bprintf("--- BlockCryptor::inputSize inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
600 inSize, outSize, mInBufSize);
601 return inSize;
602 }
603
604 size_t BlockCryptor::outputSize(
605 bool final,
606 size_t inSize /*= 0*/) // output for given input size
607 {
608 size_t rawBytes = inSize + mInBufSize;
609 // huh?Êdon't round this up!
610 //size_t rawBlocks = (rawBytes + mInBlockSize - 1) / mInBlockSize;
611 size_t rawBlocks = rawBytes / mInBlockSize;
612
613 /*
614 * encrypting: always get one additional block on final() if we're padding
615 * or (we presume) the subclass is padding. Note that we
616 * truncated when calculating rawBlocks; to finish out on the
617 * final block, we (or our subclass) will either have to pad
618 * out the current partial block, or cook up a full pad block if
619 * mInBufSize is currently zero. Subclasses which pad some other
620 * way need to override this method.
621 *
622 * decrypting: outsize always <= insize
623 */
624 if(encoding() && final && (mPkcsPadding || mNeedFinalData)) {
625 rawBlocks++;
626 }
627
628 /* FIXME - optimize for needFinalData? (can squeak by with smaller outSize) */
629 size_t rtn = rawBlocks * mOutBlockSize;
630 bprintf("--- BlockCryptor::outputSize inSize 0x%lx outSize 0x%lx final %d "
631 "inBufSize 0x%lx", inSize, rtn, final, mInBufSize);
632 return rtn;
633 }
634
635
636