tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cts.c (11316B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifdef FREEBL_NO_DEPEND
      6 #include "stubs.h"
      7 #endif
      8 #include "blapit.h"
      9 #include "blapii.h"
     10 #include "cts.h"
     11 #include "secerr.h"
     12 
     13 struct CTSContextStr {
     14    freeblCipherFunc cipher;
     15    void *context;
     16    /* iv stores the last ciphertext block of the previous message.
     17     * Only used by decrypt. */
     18    unsigned char iv[MAX_BLOCK_SIZE];
     19 };
     20 
     21 CTSContext *
     22 CTS_CreateContext(void *context, freeblCipherFunc cipher,
     23                  const unsigned char *iv)
     24 {
     25    CTSContext *cts;
     26 
     27    cts = PORT_ZNew(CTSContext);
     28    if (cts == NULL) {
     29        return NULL;
     30    }
     31    PORT_Memcpy(cts->iv, iv, MAX_BLOCK_SIZE);
     32    cts->cipher = cipher;
     33    cts->context = context;
     34    return cts;
     35 }
     36 
     37 void
     38 CTS_DestroyContext(CTSContext *cts, PRBool freeit)
     39 {
     40    if (freeit) {
     41        PORT_Free(cts);
     42    }
     43 }
     44 
     45 /*
     46 * See addemdum to NIST SP 800-38A
     47 * Generically handle cipher text stealing. Basically this is doing CBC
     48 * operations except someone can pass us a partial block.
     49 *
     50 *  Output Order:
     51 *  CS-1:  C1||C2||C3..Cn-1(could be partial)||Cn   (NIST)
     52 *  CS-2: pad == 0 C1||C2||C3...Cn-1(is full)||Cn   (Schneier)
     53 *  CS-2: pad != 0 C1||C2||C3...Cn||Cn-1(is partial)(Schneier)
     54 *  CS-3: C1||C2||C3...Cn||Cn-1(could be partial)   (Kerberos)
     55 *
     56 * The characteristics of these three options:
     57 *  - NIST & Schneier (CS-1 & CS-2) are identical to CBC if there are no
     58 * partial blocks on input.
     59 *  - Scheier and Kerberos (CS-2 and CS-3) have no embedded partial blocks,
     60 * which make decoding easier.
     61 *  - NIST & Kerberos (CS-1 and CS-3) have consistent block order independent
     62 * of padding.
     63 *
     64 * PKCS #11 did not specify which version to implement, but points to the NIST
     65 * spec, so this code implements CTS-CS-1 from NIST.
     66 *
     67 * To convert the returned buffer to:
     68 *   CS-2 (Schneier): do
     69 *       unsigned char tmp[MAX_BLOCK_SIZE];
     70 *       pad = *outlen % blocksize;
     71 *       if (pad) {
     72 *          memcpy(tmp, outbuf+*outlen-blocksize, blocksize);
     73 *          memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad);
     74 *      memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize);
     75 *       }
     76 *   CS-3 (Kerberos): do
     77 *       unsigned char tmp[MAX_BLOCK_SIZE];
     78 *       pad = *outlen % blocksize;
     79 *       if (pad == 0) {
     80 *           pad = blocksize;
     81 *       }
     82 *       memcpy(tmp, outbuf+*outlen-blocksize, blocksize);
     83 *       memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad);
     84 *   memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize);
     85 */
     86 SECStatus
     87 CTS_EncryptUpdate(CTSContext *cts, unsigned char *outbuf,
     88                  unsigned int *outlen, unsigned int maxout,
     89                  const unsigned char *inbuf, unsigned int inlen,
     90                  unsigned int blocksize)
     91 {
     92    unsigned char lastBlock[MAX_BLOCK_SIZE];
     93    unsigned int tmp;
     94    int fullblocks;
     95    int written;
     96    unsigned char *saveout = outbuf;
     97    SECStatus rv;
     98 
     99    if (inlen < blocksize) {
    100        PORT_SetError(SEC_ERROR_INPUT_LEN);
    101        return SECFailure;
    102    }
    103 
    104    if (maxout < inlen) {
    105        *outlen = inlen;
    106        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
    107        return SECFailure;
    108    }
    109    fullblocks = (inlen / blocksize) * blocksize;
    110    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf,
    111                        fullblocks, blocksize);
    112    if (rv != SECSuccess) {
    113        return SECFailure;
    114    }
    115    *outlen = fullblocks; /* AES low level doesn't set outlen */
    116    inbuf += fullblocks;
    117    inlen -= fullblocks;
    118    if (inlen == 0) {
    119        return SECSuccess;
    120    }
    121    written = *outlen - (blocksize - inlen);
    122    outbuf += written;
    123    maxout -= written;
    124 
    125    /*
    126     * here's the CTS magic, we pad our final block with zeros,
    127     * then do a CBC encrypt. CBC will xor our plain text with
    128     * the previous block (Cn-1), capturing part of that block (Cn-1**) as it
    129     * xors with the zero pad. We then write this full block, overwritting
    130     * (Cn-1**) in our buffer. This allows us to have input data == output
    131     * data since Cn contains enough information to reconver Cn-1** when
    132     * we decrypt (at the cost of some complexity as you can see in decrypt
    133     * below */
    134    PORT_Memcpy(lastBlock, inbuf, inlen);
    135    PORT_Memset(lastBlock + inlen, 0, blocksize - inlen);
    136    rv = (*cts->cipher)(cts->context, outbuf, &tmp, maxout, lastBlock,
    137                        blocksize, blocksize);
    138    PORT_Memset(lastBlock, 0, blocksize);
    139    if (rv == SECSuccess) {
    140        *outlen = written + blocksize;
    141    } else {
    142        PORT_Memset(saveout, 0, written + blocksize);
    143    }
    144    return rv;
    145 }
    146 
    147 #define XOR_BLOCK(x, y, count)  \
    148    for (i = 0; i < count; i++) \
    149    x[i] = x[i] ^ y[i]
    150 
    151 /*
    152 * See addemdum to NIST SP 800-38A
    153 * Decrypt, Expect CS-1: input. See the comment on the encrypt side
    154 * to understand what CS-2 and CS-3 mean.
    155 *
    156 * To convert the input buffer to CS-1 from ...
    157 *   CS-2 (Schneier): do
    158 *       unsigned char tmp[MAX_BLOCK_SIZE];
    159 *       pad = inlen % blocksize;
    160 *       if (pad) {
    161 *          memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
    162 *          memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
    163 *      memcpy(inbuf+inlen-blocksize, tmp, blocksize);
    164 *       }
    165 *   CS-3 (Kerberos): do
    166 *       unsigned char tmp[MAX_BLOCK_SIZE];
    167 *       pad = inlen % blocksize;
    168 *       if (pad == 0) {
    169 *           pad = blocksize;
    170 *       }
    171 *       memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
    172 *       memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
    173 *   memcpy(inbuf+inlen-blocksize, tmp, blocksize);
    174 */
    175 SECStatus
    176 CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf,
    177                  unsigned int *outlen, unsigned int maxout,
    178                  const unsigned char *inbuf, unsigned int inlen,
    179                  unsigned int blocksize)
    180 {
    181    unsigned char *Pn;
    182    unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */
    183    unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */
    184    unsigned char Cn[MAX_BLOCK_SIZE];   /* block Cn   */
    185    unsigned char lastBlock[MAX_BLOCK_SIZE];
    186    const unsigned char *tmp;
    187    unsigned char *saveout = outbuf;
    188    unsigned int tmpLen;
    189    unsigned int fullblocks, pad;
    190    unsigned int i;
    191    SECStatus rv;
    192 
    193    if (inlen < blocksize) {
    194        PORT_SetError(SEC_ERROR_INPUT_LEN);
    195        return SECFailure;
    196    }
    197 
    198    if (maxout < inlen) {
    199        *outlen = inlen;
    200        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
    201        return SECFailure;
    202    }
    203 
    204    fullblocks = (inlen / blocksize) * blocksize;
    205 
    206    /* even though we expect the input to be CS-1, CS-2 is easier to parse,
    207     * so convert to CS-2 immediately. NOTE: this is the same code as in
    208     * the comment for encrypt. NOTE2: since we can't modify inbuf unless
    209     * inbuf and outbuf overlap, just copy inbuf to outbuf and modify it there
    210     */
    211    pad = inlen - fullblocks;
    212    if (pad != 0) {
    213        if (inbuf != outbuf) {
    214            memcpy(outbuf, inbuf, inlen);
    215            /* keep the names so we logically know how we are using the
    216             * buffers */
    217            inbuf = outbuf;
    218        }
    219        memcpy(lastBlock, inbuf + inlen - blocksize, blocksize);
    220        /* we know inbuf == outbuf now, inbuf is declared const and can't
    221         * be the target, so use outbuf for the target here */
    222        memcpy(outbuf + inlen - pad, inbuf + inlen - blocksize - pad, pad);
    223        memcpy(outbuf + inlen - blocksize - pad, lastBlock, blocksize);
    224    }
    225    /* save the previous to last block so we can undo the misordered
    226     * chaining */
    227    tmp = (fullblocks < blocksize * 2) ? cts->iv : inbuf + fullblocks - blocksize * 2;
    228    PORT_Memcpy(Cn_2, tmp, blocksize);
    229    PORT_Memcpy(Cn, inbuf + fullblocks - blocksize, blocksize);
    230    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf,
    231                        fullblocks, blocksize);
    232    if (rv != SECSuccess) {
    233        return SECFailure;
    234    }
    235    *outlen = fullblocks; /* AES low level doesn't set outlen */
    236    inbuf += fullblocks;
    237    inlen -= fullblocks;
    238    if (inlen == 0) {
    239        return SECSuccess;
    240    }
    241    outbuf += fullblocks;
    242 
    243    /* recover the stolen text */
    244    PORT_Memset(lastBlock, 0, blocksize);
    245    PORT_Memcpy(lastBlock, inbuf, inlen);
    246    PORT_Memcpy(Cn_1, inbuf, inlen);
    247    Pn = outbuf - blocksize;
    248    /* inbuf points to Cn-1* in the input buffer */
    249    /* NOTE: below there are 2 sections marked "make up for the out of order
    250     * cbc decryption". You may ask, what is going on here.
    251     *   Short answer: CBC automatically xors the plain text with the previous
    252     * encrypted block. We are decrypting the last 2 blocks out of order, so
    253     * we have to 'back out' the decrypt xor and 'add back' the encrypt xor.
    254     *   Long answer: When we encrypted, we encrypted as follows:
    255     *       Pn-2, Pn-1, (Pn || 0), but on decryption we can't
    256     *  decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in
    257     *  Cn (see below).  So above we decrypted all the full blocks:
    258     *       Cn-2, Cn,
    259     *  to get:
    260     *       Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we
    261     *  xor'd Pn || 0  with Cn-1, but on decrypt we xor'd it with Cn-2
    262     *  To recover Pn, we xor the block with Cn-1* || 0 (in last block) and
    263     *  Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer
    264     *  and we can now reunite Cn-1. With the full Cn-1 we can decrypt it,
    265     *  but now decrypt is going to xor the decrypted data with Cn instead of
    266     *  Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now
    267     *  write that oout to the buffer */
    268 
    269    /* make up for the out of order CBC decryption */
    270    XOR_BLOCK(lastBlock, Cn_2, blocksize);
    271    XOR_BLOCK(lastBlock, Pn, blocksize);
    272    /* last buf now has Pn || Cn-1**, copy out Pn */
    273    PORT_Memcpy(outbuf, lastBlock, inlen);
    274    *outlen += inlen;
    275    /* copy Cn-1* into last buf to recover Cn-1 */
    276    PORT_Memcpy(lastBlock, Cn_1, inlen);
    277    /* note: because Cn and Cn-1 were out of order, our pointer to Pn also
    278     * points to where Pn-1 needs to reside. From here on out read Pn in
    279     * the code as really Pn-1. */
    280    rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock,
    281                        blocksize, blocksize);
    282    if (rv != SECSuccess) {
    283        PORT_Memset(lastBlock, 0, blocksize);
    284        PORT_Memset(saveout, 0, *outlen);
    285        return SECFailure;
    286    }
    287    /* make up for the out of order CBC decryption */
    288    XOR_BLOCK(Pn, Cn_2, blocksize);
    289    XOR_BLOCK(Pn, Cn, blocksize);
    290    /* reset iv to Cn  */
    291    PORT_Memcpy(cts->iv, Cn, blocksize);
    292    /* This makes Cn the last block for the next decrypt operation, which
    293     * matches the encrypt. We don't care about the contexts of last block,
    294     * only the side effect of setting the internal IV */
    295    (void)(*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn,
    296                         blocksize, blocksize);
    297    /* clear last block. At this point last block contains Pn xor Cn_1 xor
    298     * Cn_2, both of with an attacker would know, so we need to clear this
    299     * buffer out */
    300    PORT_Memset(lastBlock, 0, blocksize);
    301    /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */
    302    return SECSuccess;
    303 }