2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 * BlockCryptor.cpp - common context for block-oriented encryption algorithms
22 * Created March 5 2001 by dmitch
25 #include "BlockCryptor.h"
26 #include "BinaryKey.h"
27 #include "AppleCSPSession.h"
28 #include <security_utilities/alloc.h>
29 #include <Security/cssmerr.h>
31 #include <security_utilities/debugging.h>
32 #include <security_cdsa_utilities/cssmdata.h>
34 #define BlockCryptDebug(args...) secdebug("blockCrypt", ## args)
35 #define bprintf(args...) secdebug("blockCryptBuf", ## args)
36 #define ioprintf(args...) secdebug("blockCryptIo", ## args)
38 BlockCryptor::~BlockCryptor()
41 memset(mInBuf
, 0, mInBlockSize
);
42 session().free(mInBuf
);
46 memset(mChainBuf
, 0, mInBlockSize
);
47 session().free(mChainBuf
);
54 * Reusable setup functions called from subclass's init.
55 * This is the general purpose one....
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
66 if(pkcsPad
&& needsFinal
) {
67 BlockCryptDebug("BlockCryptor::setup pkcsPad && needsFinal");
68 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);
70 mPkcsPadding
= pkcsPad
;
72 mNeedFinalData
= needsFinal
;
74 /* set up inBuf, all configurations */
76 /* only reuse if same size */
77 if(mInBlockSize
!= blockSizeIn
) {
78 session().free(mInBuf
);
83 mInBuf
= (uint8
*)session().malloc(blockSizeIn
);
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
);
95 if(mChainBuf
== NULL
) {
96 mChainBuf
= (uint8
*)session().malloc(blockSizeIn
);
100 /* IV iff CBC mode, and ensure IV is big enough */
104 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
109 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
111 if(blockSizeIn
!= blockSizeOut
) {
112 /* no can do, must be same block sizes */
113 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE
);
115 if(iv
->Length
< blockSizeIn
) {
117 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
119 /* save IV as appropriate */
122 memmove(mInBuf
, iv
->Data
, blockSizeIn
);
125 assert(mChainBuf
!= NULL
);
126 memmove(mChainBuf
, iv
->Data
, blockSizeIn
);
132 mInBlockSize
= blockSizeIn
;
134 mOutBlockSize
= blockSizeOut
;
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.
143 void BlockCryptor::setup(
144 size_t blockSize
, // block size of input and output
145 const Context
&context
)
147 bool padEnable
= false;
148 bool chainEnable
= false;
149 bool ivEnable
= false;
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.
159 CSSM_ENCRYPT_MODE cssmMode
= context
.getInt(CSSM_ATTRIBUTE_MODE
);
162 /* no mode attr --> 0 == CSSM_ALGMODE_NONE, not currently supported */
163 case CSSM_ALGMODE_CBCPadIV8
:
169 case CSSM_ALGMODE_CBC_IV8
:
174 case CSSM_ALGMODE_ECB
:
177 case CSSM_ALGMODE_ECBPad
:
182 errorLog1("DESContext::init: illegal mode (%d)\n", (int)cssmMode
);
183 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE
);
187 /* validate padding type */
188 uint32 padding
= context
.getInt(CSSM_ATTRIBUTE_PADDING
); // 0 ==> PADDING_NONE
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
198 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING
);
203 case CSSM_PADDING_PKCS5
: // this goes away soon
204 case CSSM_PADDING_PKCS7
:
208 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING
);
213 /* make sure there's an IV in the context of sufficient length */
214 iv
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
);
216 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
218 if(iv
->Length
< blockSize
) {
219 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
226 chainEnable
? BCM_CBC
: BCM_ECB
,
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).
236 void BlockCryptor::update(
238 size_t &inSize
, // in/out
240 size_t &outSize
) // in/out
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
250 bool needLeftOver
= mNeedFinalData
|| (!encoding() && mPkcsPadding
);
251 bool doCbc
= (mMode
== BCM_CBC
) && !mCbcCapable
;
253 assert(mInBuf
!= NULL
);
257 /* attempt to fill mInBuf from inp */
258 toMove
= mInBlockSize
- mInBufSize
;
259 if(toMove
> uInSize
) {
262 if(encoding() && doCbc
) {
263 /* xor into last cipherblock or IV */
264 for(i
=0; i
<toMove
; i
++) {
265 mInBuf
[mInBufSize
+ i
] ^= *uInp
++;
269 /* use incoming data as is */
270 memmove(mInBuf
+mInBufSize
, uInp
, toMove
);
274 mInBufSize
+= toMove
;
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
280 if((mInBufSize
== mInBlockSize
) && !((uInSize
== 0) && needLeftOver
)) {
283 encryptBlock(mInBuf
, mInBlockSize
, uOutp
, actMoved
, false);
285 /* save ciphertext for chaining next block */
286 assert(mInBlockSize
== actMoved
);
287 memmove(mInBuf
, uOutp
, mInBlockSize
);
291 decryptBlock(mInBuf
, mInBlockSize
, uOutp
, actMoved
, false);
293 /* xor in last ciphertext */
294 assert(mInBlockSize
== actMoved
);
295 for(i
=0; i
<mInBlockSize
; i
++) {
296 uOutp
[i
] ^= mChainBuf
[i
];
298 /* save this ciphertext for next chain */
299 memmove(mChainBuf
, mInBuf
, mInBlockSize
);
302 uOutSize
+= actMoved
;
304 uOutLeft
-= actMoved
;
306 assert(uOutSize
<= outSize
);
308 } /* processing mInBuf */
312 ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
313 encoding() ? 1 : 0, inSize
, outSize
);
319 * en/decrypt even blocks in (remaining) inp.
321 unsigned leftOver
= uInSize
% mInBlockSize
;
322 if((leftOver
== 0) && needLeftOver
) {
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.
328 leftOver
= mInBlockSize
;
330 toMove
= uInSize
- leftOver
;
331 size_t blocks
= toMove
/ mInBlockSize
;
332 if(mMultiBlockCapable
&& !doCbc
&& (blocks
!= 0)) {
334 * Optimization for algorithms that are multi-block capable and that
335 * can do their own CBC (if necessary).
337 size_t thisMove
= blocks
* mInBlockSize
;
340 encryptBlock(uInp
, thisMove
, uOutp
, actMoved
, false);
343 decryptBlock(uInp
, thisMove
, uOutp
, actMoved
, false);
345 uOutSize
+= actMoved
;
348 uOutLeft
-= actMoved
;
350 assert(uOutSize
<= outSize
);
352 else if(encoding()) {
356 /* encrypt directly from input to output */
357 encryptBlock(uInp
, mInBlockSize
, uOutp
, actMoved
, false);
360 /* xor into last ciphertext, encrypt the result */
361 for(i
=0; i
<mInBlockSize
; i
++) {
362 mInBuf
[i
] ^= uInp
[i
];
364 encryptBlock(mInBuf
, mInBlockSize
, uOutp
, actMoved
, false);
366 /* save new ciphertext for next chain */
367 assert(actMoved
== mInBlockSize
);
368 memmove(mInBuf
, uOutp
, mInBlockSize
);
370 uOutSize
+= actMoved
;
372 uInp
+= mInBlockSize
;
373 uOutLeft
-= actMoved
;
374 toMove
-= mInBlockSize
;
375 assert(uOutSize
<= outSize
);
376 } /* main encrypt loop */
384 /* save this ciphertext for chain; don't assume in != out */
385 memmove(mInBuf
, uInp
, mInBlockSize
);
386 decryptBlock(uInp
, mInBlockSize
, uOutp
, actMoved
, false);
388 /* chain in previous ciphertext */
389 assert(mInBlockSize
== actMoved
);
390 for(i
=0; i
<mInBlockSize
; i
++) {
391 uOutp
[i
] ^= mChainBuf
[i
];
394 /* save current ciphertext for next block */
395 memmove(mChainBuf
, mInBuf
, mInBlockSize
);
399 decryptBlock(uInp
, mInBlockSize
, uOutp
, actMoved
, false);
401 uOutSize
+= actMoved
;
403 uInp
+= mInBlockSize
;
404 uOutLeft
-= actMoved
;
405 toMove
-= mInBlockSize
;
406 assert(uOutSize
<= outSize
);
407 } /* main decrypt loop */
411 /* leftover bytes from inp --> mInBuf */
413 if(encoding() && doCbc
) {
414 /* xor into last cipherblock or IV */
415 for(i
=0; i
<leftOver
; i
++) {
416 mInBuf
[i
] ^= *uInp
++;
420 if(mInBuf
&& uInp
&& leftOver
) memmove(mInBuf
, uInp
, leftOver
);
424 mInBufSize
= leftOver
;
426 ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
427 encoding() ? 1 : 0, inSize
, outSize
);
430 void BlockCryptor::final(
433 size_t uOutSize
= 0; // ouput bytes generated
435 size_t uOutLeft
= out
.Length
; // bytes remaining in out
437 bool doCbc
= (mMode
== BCM_CBC
) && !mCbcCapable
;
439 assert(mInBuf
!= NULL
);
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
);
448 uint8
*ctext
= out
.Data
;
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.
456 assert(mInBufSize
< mInBlockSize
);
457 size_t padSize
= mInBlockSize
- mInBufSize
;
458 uint8
*padPtr
= mInBuf
+ mInBufSize
;
460 for(i
=0; i
<padSize
; i
++) {
465 for(i
=0; i
<padSize
; i
++) {
466 *padPtr
++ ^= padSize
;
469 mInBufSize
= mInBlockSize
;
473 * Encrypt final mInBuf. If it's not full, the BlockCryptObject better know
478 encryptBlock(mInBuf
, mInBufSize
, ctext
, actMoved
, true);
479 uOutSize
+= actMoved
;
481 assert(uOutSize
<= out
.length());
483 out
.length(uOutSize
);
487 if(mInBufSize
== 0) {
489 BlockCryptDebug("BlockCryptor::final decrypt/pad with no mInBuf data");
490 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR
);
493 /* simple decrypt op complete */
494 ioprintf("=== BlockCryptor::final encrypt 0 outSize 0");
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.
507 if(mInBufSize
!= mInBlockSize
) {
508 BlockCryptDebug("BlockCryptor::final unaligned ciphertext");
509 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR
);
512 uint8
*ptext
= out
.Data
;
514 decryptBlock(mInBuf
, mInBlockSize
, ptext
, actMoved
, true);
516 /* chain in previous ciphertext one more time */
517 assert(mInBlockSize
== actMoved
);
518 for(i
=0; i
<mInBlockSize
; i
++) {
519 ptext
[i
] ^= mChainBuf
[i
];
523 assert(actMoved
== mOutBlockSize
);
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
);
531 uint8
*padPtr
= ptext
+ mOutBlockSize
- padSize
;
532 for(unsigned i
=0; i
<padSize
; i
++) {
533 if(*padPtr
++ != padSize
) {
534 BlockCryptDebug("BlockCryptor::final malformed ciphertext "
536 CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA
);
541 assert(actMoved
<= out
.length());
542 out
.length(actMoved
);
544 ioprintf("=== BlockCryptor::final encrypt %d outSize 0x%lx",
545 encoding() ? 1 : 0, out
.Length
);
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
556 void BlockCryptor::minimumProgress(
560 /* each size = one block (including buffered input) */
561 inSize
= mInBlockSize
- mInBufSize
;
563 /* i.e., we're holding a whole buffer */
566 outSize
= mOutBlockSize
;
567 bprintf("--- BlockCryptor::minProgres inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
568 inSize
, outSize
, mInBufSize
);
571 size_t BlockCryptor::inputSize(
572 size_t outSize
) // input for given output size
576 if(outSize
< mOutBlockSize
) {
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).
583 inSize
= mInBlockSize
- mInBufSize
;
585 /* we have a full input buffer! How can this happen!? */
586 BlockCryptDebug("BlockCryptor::inputSize: HELP! zero inSize and outSize!\n");
590 /* more-or-less normal case */
591 size_t wholeBlocks
= outSize
/ mOutBlockSize
;
592 assert(wholeBlocks
>= 1);
593 inSize
= (wholeBlocks
* mInBlockSize
) - mInBufSize
;
595 /* i.e., we're holding a whole buffer */
599 bprintf("--- BlockCryptor::inputSize inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
600 inSize
, outSize
, mInBufSize
);
604 size_t BlockCryptor::outputSize(
606 size_t inSize
/*= 0*/) // output for given input size
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
;
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.
622 * decrypting: outsize always <= insize
624 if(encoding() && final
&& (mPkcsPadding
|| mNeedFinalData
)) {
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
);