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