Mercurial > trustbridge > nss-cmake-static
diff nss/lib/freebl/cts.c @ 0:1e5118fa0cb1
This is NSS with a Cmake Buildsyste
To compile a static NSS library for Windows we've used the
Chromium-NSS fork and added a Cmake buildsystem to compile
it statically for Windows. See README.chromium for chromium
changes and README.trustbridge for our modifications.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 28 Jul 2014 10:47:06 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nss/lib/freebl/cts.c Mon Jul 28 10:47:06 2014 +0200 @@ -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; +}