security/nss/lib/freebl/cts.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial