mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-11 10:20:19 +01:00
98d377b37b
bug920719, bug1026148, bug1028647, bug963150, bug1030486, bug1025729, bug836658, bug1028582, bug1038728, bug1038526, bug1042634, bug1047210, bug1043891, bug1043108, bug1046735, bug1043082, bug1036735, bug1046718, bug1050107, bug1054625, bug1057465, bug1057476, bug1041326, bug1058933, bug1064636, bug1057161, bug1078669, bug1049435, bug1070493, bug1083360, bug1028764, bug1065990, bug1073330, bug1064670, bug1094650
302 lines
10 KiB
C
302 lines
10 KiB
C
/* 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;
|
|
|
|
/* 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;
|
|
}
|