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/cssmalloc.h>
29 #include <Security/utilities.h>
30 #include <Security/cssmerr.h>
32 #include <Security/debugging.h>
33 #include <Security/cssmdata.h>
35 #define BlockCryptDebug(args...) secdebug("blockCrypt", ## args)
36 #define bprintf(args...) secdebug("blockCryptBuf", ## args)
37 #define ioprintf(args...) secdebug("blockCryptIo", ## args)
39 BlockCryptor::~BlockCryptor()
42 memset(mInBuf
, 0, mInBlockSize
);
43 session().free(mInBuf
);
47 memset(mChainBuf
, 0, mInBlockSize
);
48 session().free(mChainBuf
);
55 * Reusable setup functions called from subclass's init.
56 * This is the general purpose one....
58 void BlockCryptor::setup(
59 size_t blockSizeIn
, // block size of input
60 size_t blockSizeOut
, // block size of output
61 bool pkcsPad
, // this class performs PKCS{5,7} padding
62 bool needsFinal
, // needs final update with valid data
63 BC_Mode mode
, // ECB, CBC
64 const CssmData
*iv
) // init vector, required for CBC
65 //Ê must be at least blockSizeIn bytes
67 if(pkcsPad
&& needsFinal
) {
68 BlockCryptDebug("BlockCryptor::setup pkcsPad && needsFinal");
69 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);
71 mPkcsPadding
= pkcsPad
;
73 mNeedFinalData
= needsFinal
;
75 /* set up inBuf, all configurations */
77 /* only reuse if same size */
78 if(mInBlockSize
!= blockSizeIn
) {
79 session().free(mInBuf
);
84 mInBuf
= (uint8
*)session().malloc(blockSizeIn
);
87 /* set up chain buf, decrypt/CBC only */
88 if((mMode
== BCM_CBC
) && !encoding()) {
89 if(mChainBuf
!= NULL
) {
90 /* only reuse if same size */
91 if(mInBlockSize
!= blockSizeIn
) {
92 session().free(mChainBuf
);
96 if(mChainBuf
== NULL
) {
97 mChainBuf
= (uint8
*)session().malloc(blockSizeIn
);
101 /* IV iff CBC mode, and ensure IV is big enough */
105 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
110 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
112 if(blockSizeIn
!= blockSizeOut
) {
113 /* no can do, must be same block sizes */
114 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE
);
116 if(iv
->Length
< blockSizeIn
) {
118 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
120 /* save IV as appropriate */
122 memmove(mInBuf
, iv
->Data
, blockSizeIn
);
125 assert(mChainBuf
!= NULL
);
126 memmove(mChainBuf
, iv
->Data
, blockSizeIn
);
131 mInBlockSize
= blockSizeIn
;
133 mOutBlockSize
= blockSizeOut
;
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.
142 void BlockCryptor::setup(
143 size_t blockSize
, // block size of input and output
144 const Context
&context
)
146 bool padEnable
= false;
147 bool chainEnable
= false;
148 bool ivEnable
= false;
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.
158 CSSM_ENCRYPT_MODE cssmMode
= context
.getInt(CSSM_ATTRIBUTE_MODE
);
161 /* no mode attr --> 0 == CSSM_ALGMODE_NONE, not currently supported */
162 case CSSM_ALGMODE_CBCPadIV8
:
168 case CSSM_ALGMODE_CBC_IV8
:
173 case CSSM_ALGMODE_ECB
:
176 case CSSM_ALGMODE_ECBPad
:
181 errorLog1("DESContext::init: illegal mode (%d)\n", (int)cssmMode
);
182 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE
);
186 /* validate padding type */
187 uint32 padding
= context
.getInt(CSSM_ATTRIBUTE_PADDING
); // 0 ==> PADDING_NONE
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
197 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING
);
202 case CSSM_PADDING_PKCS5
: // this goes away soon
203 case CSSM_PADDING_PKCS7
:
207 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING
);
212 /* make sure there's an IV in the context of sufficient length */
213 iv
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
);
215 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
217 if(iv
->Length
< blockSize
) {
218 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_INIT_VECTOR
);
225 chainEnable
? BCM_CBC
: BCM_ECB
,
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).
235 void BlockCryptor::update(
237 size_t &inSize
, // in/out
239 size_t &outSize
) // in/out
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
249 bool needLeftOver
= mNeedFinalData
|| (!encoding() && mPkcsPadding
);
251 assert(mInBuf
!= NULL
);
255 /* attempt to fill mInBuf from inp */
256 toMove
= mInBlockSize
- mInBufSize
;
257 if(toMove
> uInSize
) {
260 if(encoding() && (mMode
== BCM_CBC
)) {
261 /* xor into last cipherblock or IV */
262 for(i
=0; i
<toMove
; i
++) {
263 mInBuf
[mInBufSize
+ i
] ^= *uInp
++;
267 /* use incoming data as is */
268 memmove(mInBuf
+mInBufSize
, uInp
, toMove
);
272 mInBufSize
+= toMove
;
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
279 if((mInBufSize
== mInBlockSize
) && !((uInSize
== 0) && needLeftOver
)) {
282 encryptBlock(mInBuf
, mInBlockSize
, uOutp
, actMoved
, false);
283 if(mMode
== BCM_CBC
) {
284 /* save ciphertext for chaining next block */
285 assert(mInBlockSize
== actMoved
);
286 memmove(mInBuf
, uOutp
, mInBlockSize
);
290 decryptBlock(mInBuf
, uOutp
, actMoved
, false);
291 if(mMode
== BCM_CBC
) {
292 /* xor in last ciphertext */
293 assert(mInBlockSize
== actMoved
);
294 for(i
=0; i
<mInBlockSize
; i
++) {
295 uOutp
[i
] ^= mChainBuf
[i
];
297 /* save this ciphertext for next chain */
298 memmove(mChainBuf
, mInBuf
, mInBlockSize
);
301 uOutSize
+= actMoved
;
303 uOutLeft
-= actMoved
;
305 assert(uOutSize
<= outSize
);
307 } /* processing mInBuf */
311 ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
312 encoding() ? 1 : 0, inSize
, outSize
);
317 * en/decrypt even blocks in (remaining) inp.
319 unsigned leftOver
= uInSize
% mInBlockSize
;
320 if((leftOver
== 0) && needLeftOver
) {
322 * Even blocks coming in, but we really need to leave some data
323 * in the buffer (because the subclass asked for it, or we're decrypting
324 * with PKCS padding). Save one block for mInBuf.
326 leftOver
= mInBlockSize
;
328 toMove
= uInSize
- leftOver
;
332 if(mMode
== BCM_ECB
) {
333 /* encrypt directly from input to output */
334 encryptBlock(uInp
, mInBlockSize
, uOutp
, actMoved
, false);
337 /* xor into last ciphertext, encrypt the result */
338 for(i
=0; i
<mInBlockSize
; i
++) {
339 mInBuf
[i
] ^= uInp
[i
];
341 encryptBlock(mInBuf
, mInBlockSize
, uOutp
, actMoved
, false);
343 /* save new ciphertext for next chain */
344 assert(actMoved
== mInBlockSize
);
345 memmove(mInBuf
, uOutp
, mInBlockSize
);
347 uOutSize
+= actMoved
;
349 uInp
+= mInBlockSize
;
350 uOutLeft
-= actMoved
;
351 toMove
-= mInBlockSize
;
352 assert(uOutSize
<= outSize
);
353 } /* main encrypt loop */
359 if(mMode
== BCM_CBC
) {
360 /* save this ciphertext for chain; don't assume in != out */
361 memmove(mInBuf
, uInp
, mInBlockSize
);
362 decryptBlock(uInp
, uOutp
, actMoved
, false);
364 /* chain in previous ciphertext */
365 assert(mInBlockSize
== actMoved
);
366 for(i
=0; i
<mInBlockSize
; i
++) {
367 uOutp
[i
] ^= mChainBuf
[i
];
370 /* save current ciphertext for next block */
371 memmove(mChainBuf
, mInBuf
, mInBlockSize
);
375 decryptBlock(uInp
, uOutp
, actMoved
, false);
377 uOutSize
+= actMoved
;
379 uInp
+= mInBlockSize
;
380 uOutLeft
-= actMoved
;
381 toMove
-= mInBlockSize
;
382 assert(uOutSize
<= outSize
);
383 } /* main decrypt loop */
386 /* leftover bytes from inp --> mInBuf */
388 if(encoding() && (mMode
== BCM_CBC
)) {
389 /* xor into last cipherblock or IV */
390 for(i
=0; i
<leftOver
; i
++) {
391 mInBuf
[i
] ^= *uInp
++;
395 memmove(mInBuf
, uInp
, leftOver
);
398 mInBufSize
= leftOver
;
400 ioprintf("=== BlockCryptor::update encrypt %d inSize 0x%lx outSize 0x%lx",
401 encoding() ? 1 : 0, inSize
, outSize
);
404 void BlockCryptor::final(
407 size_t uOutSize
= 0; // ouput bytes generated
409 size_t uOutLeft
= out
.Length
; // bytes remaining in out
412 assert(mInBuf
!= NULL
);
414 if((mInBufSize
== 0) && mNeedFinalData
) {
415 /* only way this could happen: no update() called (at least not with
416 * non-zero input data sizes) */
417 BlockCryptDebug("BlockCryptor::final with no mInBuf data");
418 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR
);
421 UInt8
*ctext
= out
.Data
;
425 Ê* PKCS5/7 padding: pad byte = size of padding.
426 * This assertion courtesy of the limitation on the mutual
427 * exclusivity of mPkcsPadding and mNeedFinalData.
429 assert(mInBufSize
< mInBlockSize
);
430 size_t padSize
= mInBlockSize
- mInBufSize
;
431 UInt8
*padPtr
= mInBuf
+ mInBufSize
;
432 if(mMode
== BCM_ECB
) {
433 for(i
=0; i
<padSize
; i
++) {
438 for(i
=0; i
<padSize
; i
++) {
439 *padPtr
++ ^= padSize
;
442 mInBufSize
= mInBlockSize
;
446 * Encrypt final mInBuf. If it's not full, the BlockCryptObject better know
451 encryptBlock(mInBuf
, mInBufSize
, ctext
, actMoved
, true);
452 uOutSize
+= actMoved
;
454 assert(uOutSize
<= out
.length());
456 out
.length(uOutSize
);
460 if(mInBufSize
== 0) {
462 BlockCryptDebug("BlockCryptor::final decrypt/pad with no mInBuf data");
463 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR
);
466 /* simple decrypt op complete */
467 ioprintf("=== BlockCryptor::final encrypt 0 outSize 0");
474 * Decrypt - must have exactly one block of ciphertext.
475 * We trust CSPContext, and our own outputSize(), to have set up
476 * the current output buffer with enough space to handle the
477 * full size of the decrypt, even though - due to padding - we
478 * might actually pass less than that amount back to caller.
480 if(mInBufSize
!= mInBlockSize
) {
481 BlockCryptDebug("BlockCryptor::final unaligned ciphertext");
482 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR
);
485 UInt8
*ptext
= out
.Data
;
487 decryptBlock(mInBuf
, ptext
, actMoved
, true);
488 if(mMode
== BCM_CBC
) {
489 /* chain in previous ciphertext one more time */
490 assert(mInBlockSize
== actMoved
);
491 for(i
=0; i
<mInBlockSize
; i
++) {
492 ptext
[i
] ^= mChainBuf
[i
];
496 assert(actMoved
== mOutBlockSize
);
498 /* ensure integrity of padding byte(s) */
499 unsigned padSize
= ptext
[mOutBlockSize
- 1];
500 if(padSize
> mOutBlockSize
) {
501 BlockCryptDebug("BlockCryptor::final malformed ciphertext (1)");
502 CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA
);
504 UInt8
*padPtr
= ptext
+ mOutBlockSize
- padSize
;
505 for(unsigned i
=0; i
<padSize
; i
++) {
506 if(*padPtr
++ != padSize
) {
507 BlockCryptDebug("BlockCryptor::final malformed ciphertext "
509 CssmError::throwMe(CSSM_ERRCODE_INVALID_DATA
);
514 assert(actMoved
<= out
.length());
515 out
.length(actMoved
);
517 ioprintf("=== BlockCryptor::final encrypt %d outSize 0x%lx",
518 encoding() ? 1 : 0, out
.Length
);
522 * These three are only valid for algorithms for which encrypting one block
523 * of plaintext always yields exactly one block of ciphertext, and vice versa
524 * for decrypt. The block sizes for plaintext and ciphertext do NOT have to be
525 * the same. Subclasses (e.g. FEED) which do not meet this criterion will have
529 void BlockCryptor::minimumProgress(
533 /* each size = one block (including buffered input) */
534 inSize
= mInBlockSize
- mInBufSize
;
536 /* i.e., we're holding a whole buffer */
539 outSize
= mOutBlockSize
;
540 bprintf("--- BlockCryptor::minProgres inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
541 inSize
, outSize
, mInBufSize
);
544 size_t BlockCryptor::inputSize(
545 size_t outSize
) // input for given output size
549 if(outSize
< mOutBlockSize
) {
551 * Sometimes CSPFullPluginSession calls us like this....in this
552 * case the legal inSize is just the remainder of the input buffer,
553 * less one byte (in other words, the max we we gobble up without
554 * producing any output).
556 inSize
= mInBlockSize
- mInBufSize
;
558 /* we have a full input buffer! How can this happen!? */
559 BlockCryptDebug("BlockCryptor::inputSize: HELP! zero inSize and outSize!\n");
563 /* more-or-less normal case */
564 size_t wholeBlocks
= outSize
/ mOutBlockSize
;
565 assert(wholeBlocks
>= 1);
566 inSize
= (wholeBlocks
* mInBlockSize
) - mInBufSize
;
568 /* i.e., we're holding a whole buffer */
572 bprintf("--- BlockCryptor::inputSize inSize 0x%lx outSize 0x%lx mInBufSize 0x%lx",
573 inSize
, outSize
, mInBufSize
);
577 size_t BlockCryptor::outputSize(
579 size_t inSize
/*= 0*/) // output for given input size
581 size_t rawBytes
= inSize
+ mInBufSize
;
582 // huh?Êdon't round this up!
583 //size_t rawBlocks = (rawBytes + mInBlockSize - 1) / mInBlockSize;
584 size_t rawBlocks
= rawBytes
/ mInBlockSize
;
587 * encrypting: always get one additional block on final() if we're padding
588 * or (we presume) the subclass is padding. Note that we
589 * truncated when calculating rawBlocks; to finish out on the
590 * final block, we (or our subclass) will either have to pad
591 * out the current partial block, or cook up a full pad block if
592 * mInBufSize is currently zero. Subclasses which pad some other
593 * way need to override this method.
595 * decrypting: outsize always <= insize
597 if(encoding() && final
&& (mPkcsPadding
|| mNeedFinalData
)) {
601 /* FIXME - optimize for needFinalData? (can squeak by with smaller outSize) */
602 size_t rtn
= rawBlocks
* mOutBlockSize
;
603 bprintf("--- BlockCryptor::outputSize inSize 0x%lx outSize 0x%lx final %d "
604 "inBufSize 0x%lx", inSize
, rtn
, final
, mInBufSize
);