| Index: nss/mozilla/security/nss/lib/freebl/cts.c | 
| =================================================================== | 
| --- nss/mozilla/security/nss/lib/freebl/cts.c	(revision 0) | 
| +++ nss/mozilla/security/nss/lib/freebl/cts.c	(revision 0) | 
| @@ -0,0 +1,302 @@ | 
| +/* This Source Code Form is subject to the terms of the Mozilla Public | 
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | 
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
| + | 
| +#ifdef FREEBL_NO_DEPEND | 
| +#include "stubs.h" | 
| +#endif | 
| +#include "blapit.h" | 
| +#include "blapii.h" | 
| +#include "cts.h" | 
| +#include "secerr.h" | 
| + | 
| +struct CTSContextStr { | 
| +    freeblCipherFunc cipher; | 
| +    void *context; | 
| +    /* iv stores the last ciphertext block of the previous message. | 
| +     * Only used by decrypt. */ | 
| +    unsigned char iv[MAX_BLOCK_SIZE]; | 
| +}; | 
| + | 
| +CTSContext * | 
| +CTS_CreateContext(void *context, freeblCipherFunc cipher, | 
| +		  const unsigned char *iv, unsigned int blocksize) | 
| +{ | 
| +    CTSContext  *cts; | 
| + | 
| +    if (blocksize > MAX_BLOCK_SIZE) { | 
| +	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | 
| +	return NULL; | 
| +    } | 
| +    cts = PORT_ZNew(CTSContext); | 
| +    if (cts == NULL) { | 
| +	return NULL; | 
| +    } | 
| +    PORT_Memcpy(cts->iv, iv, blocksize); | 
| +    cts->cipher = cipher; | 
| +    cts->context = context; | 
| +    return cts; | 
| +} | 
| + | 
| +void | 
| +CTS_DestroyContext(CTSContext *cts, PRBool freeit) | 
| +{ | 
| +    if (freeit) { | 
| +	PORT_Free(cts); | 
| +    } | 
| +} | 
| + | 
| +/* | 
| + * See addemdum to NIST SP 800-38A | 
| + * Generically handle cipher text stealing. Basically this is doing CBC | 
| + * operations except someone can pass us a partial block. | 
| + * | 
| + *  Output Order: | 
| + *  CS-1:  C1||C2||C3..Cn-1(could be partial)||Cn   (NIST) | 
| + *  CS-2: pad == 0 C1||C2||C3...Cn-1(is full)||Cn   (Schneier) | 
| + *  CS-2: pad != 0 C1||C2||C3...Cn||Cn-1(is partial)(Schneier) | 
| + *  CS-3: C1||C2||C3...Cn||Cn-1(could be partial)   (Kerberos) | 
| + * | 
| + * The characteristics of these three options: | 
| + *  - NIST & Schneier (CS-1 & CS-2) are identical to CBC if there are no | 
| + * partial blocks on input. | 
| + *  - Scheier and Kerberos (CS-2 and CS-3) have no embedded partial blocks, | 
| + * which make decoding easier. | 
| + *  - NIST & Kerberos (CS-1 and CS-3) have consistent block order independent | 
| + * of padding. | 
| + * | 
| + * PKCS #11 did not specify which version to implement, but points to the NIST | 
| + * spec, so this code implements CTS-CS-1 from NIST. | 
| + * | 
| + * To convert the returned buffer to: | 
| + *   CS-2 (Schneier): do | 
| + *       unsigned char tmp[MAX_BLOCK_SIZE]; | 
| + *       pad = *outlen % blocksize; | 
| + *       if (pad) { | 
| + *          memcpy(tmp, outbuf+*outlen-blocksize, blocksize); | 
| + *          memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad); | 
| + *	    memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize); | 
| + *       } | 
| + *   CS-3 (Kerberos): do | 
| + *       unsigned char tmp[MAX_BLOCK_SIZE]; | 
| + *       pad = *outlen % blocksize; | 
| + *       if (pad == 0) { | 
| + *           pad = blocksize; | 
| + *       } | 
| + *       memcpy(tmp, outbuf+*outlen-blocksize, blocksize); | 
| + *       memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad); | 
| + *	 memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize); | 
| + */ | 
| +SECStatus | 
| +CTS_EncryptUpdate(CTSContext *cts, unsigned char *outbuf, | 
| +		unsigned int *outlen, unsigned int maxout, | 
| +		const unsigned char *inbuf, unsigned int inlen, | 
| +		unsigned int blocksize) | 
| +{ | 
| +    unsigned char lastBlock[MAX_BLOCK_SIZE]; | 
| +    unsigned int tmp; | 
| +    int fullblocks; | 
| +    int written; | 
| +    SECStatus rv; | 
| + | 
| +    if (inlen < blocksize) { | 
| +	PORT_SetError(SEC_ERROR_INPUT_LEN); | 
| +	return SECFailure; | 
| +    } | 
| + | 
| +    if (maxout < inlen) { | 
| +	*outlen = inlen; | 
| +	PORT_SetError(SEC_ERROR_OUTPUT_LEN); | 
| +	return SECFailure; | 
| +    } | 
| +    fullblocks = (inlen/blocksize)*blocksize; | 
| +    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, | 
| +	 fullblocks, blocksize); | 
| +    if (rv != SECSuccess) { | 
| +	return SECFailure; | 
| +    } | 
| +    *outlen = fullblocks; /* AES low level doesn't set outlen */ | 
| +    inbuf += fullblocks; | 
| +    inlen -= fullblocks; | 
| +    if (inlen == 0) { | 
| +	return SECSuccess; | 
| +    } | 
| +    written = *outlen - (blocksize - inlen); | 
| +    outbuf += written; | 
| +    maxout -= written; | 
| + | 
| +    /* | 
| +     * here's the CTS magic, we pad our final block with zeros, | 
| +     * then do a CBC encrypt. CBC will xor our plain text with | 
| +     * the previous block (Cn-1), capturing part of that block (Cn-1**) as it | 
| +     * xors with the zero pad. We then write this full block, overwritting | 
| +     * (Cn-1**) in our buffer. This allows us to have input data == output | 
| +     * data since Cn contains enough information to reconver Cn-1** when | 
| +     * we decrypt (at the cost of some complexity as you can see in decrypt | 
| +     * below */ | 
| +    PORT_Memcpy(lastBlock, inbuf, inlen); | 
| +    PORT_Memset(lastBlock + inlen, 0, blocksize - inlen); | 
| +    rv = (*cts->cipher)(cts->context, outbuf, &tmp, maxout, lastBlock, | 
| +			blocksize, blocksize); | 
| +    PORT_Memset(lastBlock, 0, blocksize); | 
| +    if (rv == SECSuccess) { | 
| +	*outlen = written + blocksize; | 
| +    } | 
| +    return rv; | 
| +} | 
| + | 
| + | 
| +#define XOR_BLOCK(x,y,count) for(i=0; i < count; i++) x[i] = x[i] ^ y[i] | 
| + | 
| +/* | 
| + * See addemdum to NIST SP 800-38A | 
| + * Decrypt, Expect CS-1: input. See the comment on the encrypt side | 
| + * to understand what CS-2 and CS-3 mean. | 
| + * | 
| + * To convert the input buffer to CS-1 from ... | 
| + *   CS-2 (Schneier): do | 
| + *       unsigned char tmp[MAX_BLOCK_SIZE]; | 
| + *       pad = inlen % blocksize; | 
| + *       if (pad) { | 
| + *          memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); | 
| + *          memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); | 
| + *	    memcpy(inbuf+inlen-blocksize, tmp, blocksize); | 
| + *       } | 
| + *   CS-3 (Kerberos): do | 
| + *       unsigned char tmp[MAX_BLOCK_SIZE]; | 
| + *       pad = inlen % blocksize; | 
| + *       if (pad == 0) { | 
| + *           pad = blocksize; | 
| + *       } | 
| + *       memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); | 
| + *       memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); | 
| + *	 memcpy(inbuf+inlen-blocksize, tmp, blocksize); | 
| + */ | 
| +SECStatus | 
| +CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf, | 
| +		unsigned int *outlen, unsigned int maxout, | 
| +		const unsigned char *inbuf, unsigned int inlen, | 
| +		unsigned int blocksize) | 
| +{ | 
| +    unsigned char *Pn; | 
| +    unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */ | 
| +    unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */ | 
| +    unsigned char Cn[MAX_BLOCK_SIZE];   /* block Cn   */ | 
| +    unsigned char lastBlock[MAX_BLOCK_SIZE]; | 
| +    const unsigned char *tmp; | 
| +    unsigned int tmpLen; | 
| +    int fullblocks, pad; | 
| +    unsigned int i; | 
| +    SECStatus rv; | 
| + | 
| +    if (inlen < blocksize) { | 
| +	PORT_SetError(SEC_ERROR_INPUT_LEN); | 
| +	return SECFailure; | 
| +    } | 
| + | 
| +    if (maxout < inlen) { | 
| +	*outlen = inlen; | 
| +	PORT_SetError(SEC_ERROR_OUTPUT_LEN); | 
| +	return SECFailure; | 
| +    } | 
| + | 
| +    fullblocks = (inlen/blocksize)*blocksize; | 
| + | 
| +    /* even though we expect the input to be CS-1, CS-2 is easier to parse, | 
| +     * so convert to CS-2 immediately. NOTE: this is the same code as in | 
| +     * the comment for encrypt. NOTE2: since we can't modify inbuf unless | 
| +     * inbuf and outbuf overlap, just copy inbuf to outbuf and modify it there | 
| +     */ | 
| +    pad = inlen - fullblocks; | 
| +    if (pad != 0) { | 
| +	if (inbuf != outbuf) { | 
| +	    memcpy(outbuf, inbuf, inlen); | 
| +	    /* keep the names so we logically know how we are using the | 
| +	     * buffers */ | 
| +	    inbuf = outbuf; | 
| +	} | 
| +	memcpy(lastBlock, inbuf+inlen-blocksize, blocksize); | 
| +	/* we know inbuf == outbuf now, inbuf is declared const and can't | 
| +	 * be the target, so use outbuf for the target here */ | 
| +	memcpy(outbuf+inlen-pad, inbuf+inlen-blocksize-pad, pad); | 
| +	memcpy(outbuf+inlen-blocksize-pad, lastBlock, blocksize); | 
| +    } | 
| +    /* save the previous to last block so we can undo the misordered | 
| +     * chaining */ | 
| +    tmp =  (fullblocks < blocksize*2) ? cts->iv : | 
| +			inbuf+fullblocks-blocksize*2; | 
| +    PORT_Memcpy(Cn_2, tmp, blocksize); | 
| +    PORT_Memcpy(Cn, inbuf+fullblocks-blocksize, blocksize); | 
| +    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, | 
| +	 fullblocks, blocksize); | 
| +    if (rv != SECSuccess) { | 
| +	return SECFailure; | 
| +    } | 
| +    *outlen = fullblocks; /* AES low level doesn't set outlen */ | 
| +    inbuf += fullblocks; | 
| +    inlen -= fullblocks; | 
| +    if (inlen == 0) { | 
| +	return SECSuccess; | 
| +    } | 
| +    outbuf += fullblocks; | 
| +    maxout -= fullblocks; | 
| + | 
| +    /* recover the stolen text */ | 
| +    PORT_Memset(lastBlock, 0, blocksize); | 
| +    PORT_Memcpy(lastBlock, inbuf, inlen); | 
| +    PORT_Memcpy(Cn_1, inbuf, inlen); | 
| +    Pn = outbuf-blocksize; | 
| +    /* inbuf points to Cn-1* in the input buffer */ | 
| +    /* NOTE: below there are 2 sections marked "make up for the out of order | 
| +     * cbc decryption". You may ask, what is going on here. | 
| +     *   Short answer: CBC automatically xors the plain text with the previous | 
| +     * encrypted block. We are decrypting the last 2 blocks out of order, so | 
| +     * we have to 'back out' the decrypt xor and 'add back' the encrypt xor. | 
| +     *   Long answer: When we encrypted, we encrypted as follows: | 
| +     *       Pn-2, Pn-1, (Pn || 0), but on decryption we can't | 
| +     *  decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in | 
| +     *  Cn (see below).  So above we decrypted all the full blocks: | 
| +     *       Cn-2, Cn, | 
| +     *  to get: | 
| +     *       Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we | 
| +     *  xor'd Pn || 0  with Cn-1, but on decrypt we xor'd it with Cn-2 | 
| +     *  To recover Pn, we xor the block with Cn-1* || 0 (in last block) and | 
| +     *  Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer | 
| +     *  and we can now reunite Cn-1. With the full Cn-1 we can decrypt it, | 
| +     *  but now decrypt is going to xor the decrypted data with Cn instead of | 
| +     *  Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now | 
| +     *  write that oout to the buffer */ | 
| + | 
| +    /* make up for the out of order CBC decryption */ | 
| +    XOR_BLOCK(lastBlock, Cn_2, blocksize); | 
| +    XOR_BLOCK(lastBlock, Pn, blocksize); | 
| +    /* last buf now has Pn || Cn-1**, copy out Pn */ | 
| +    PORT_Memcpy(outbuf, lastBlock, inlen); | 
| +    *outlen += inlen; | 
| +    /* copy Cn-1* into last buf to recover Cn-1 */ | 
| +    PORT_Memcpy(lastBlock, Cn_1, inlen); | 
| +    /* note: because Cn and Cn-1 were out of order, our pointer to Pn also | 
| +     * points to where Pn-1 needs to reside. From here on out read Pn in | 
| +     * the code as really Pn-1. */ | 
| +    rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock, | 
| +	 blocksize, blocksize); | 
| +    if (rv != SECSuccess) { | 
| +	return SECFailure; | 
| +    } | 
| +    /* make up for the out of order CBC decryption */ | 
| +    XOR_BLOCK(Pn, Cn_2, blocksize); | 
| +    XOR_BLOCK(Pn, Cn, blocksize); | 
| +    /* reset iv to Cn  */ | 
| +    PORT_Memcpy(cts->iv, Cn, blocksize); | 
| +    /* This makes Cn the last block for the next decrypt operation, which | 
| +     * matches the encrypt. We don't care about the contexts of last block, | 
| +     * only the side effect of setting the internal IV */ | 
| +    (void) (*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn, | 
| +		blocksize, blocksize); | 
| +    /* clear last block. At this point last block contains Pn xor Cn_1 xor | 
| +     * Cn_2, both of with an attacker would know, so we need to clear this | 
| +     * buffer out */ | 
| +    PORT_Memset(lastBlock, 0, blocksize); | 
| +    /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */ | 
| +    return SECSuccess; | 
| +} | 
|  | 
| Property changes on: nss/mozilla/security/nss/lib/freebl/cts.c | 
| ___________________________________________________________________ | 
| Added: svn:eol-style | 
| + LF | 
|  | 
|  |