security/nss/lib/freebl/cts.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/freebl/cts.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,302 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#ifdef FREEBL_NO_DEPEND
     1.9 +#include "stubs.h"
    1.10 +#endif
    1.11 +#include "blapit.h"
    1.12 +#include "blapii.h"
    1.13 +#include "cts.h"
    1.14 +#include "secerr.h"
    1.15 +
    1.16 +struct CTSContextStr {
    1.17 +    freeblCipherFunc cipher;
    1.18 +    void *context;
    1.19 +    /* iv stores the last ciphertext block of the previous message.
    1.20 +     * Only used by decrypt. */
    1.21 +    unsigned char iv[MAX_BLOCK_SIZE];
    1.22 +};
    1.23 +
    1.24 +CTSContext *
    1.25 +CTS_CreateContext(void *context, freeblCipherFunc cipher,
    1.26 +		  const unsigned char *iv, unsigned int blocksize)
    1.27 +{
    1.28 +    CTSContext  *cts;
    1.29 +
    1.30 +    if (blocksize > MAX_BLOCK_SIZE) {
    1.31 +	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    1.32 +	return NULL;
    1.33 +    }
    1.34 +    cts = PORT_ZNew(CTSContext);
    1.35 +    if (cts == NULL) {
    1.36 +	return NULL;
    1.37 +    }
    1.38 +    PORT_Memcpy(cts->iv, iv, blocksize);
    1.39 +    cts->cipher = cipher;
    1.40 +    cts->context = context;
    1.41 +    return cts;
    1.42 +}
    1.43 +
    1.44 +void
    1.45 +CTS_DestroyContext(CTSContext *cts, PRBool freeit)
    1.46 +{
    1.47 +    if (freeit) {
    1.48 +	PORT_Free(cts);
    1.49 +    }
    1.50 +}
    1.51 +	
    1.52 +/*
    1.53 + * See addemdum to NIST SP 800-38A
    1.54 + * Generically handle cipher text stealing. Basically this is doing CBC
    1.55 + * operations except someone can pass us a partial block.
    1.56 + *
    1.57 + *  Output Order:
    1.58 + *  CS-1:  C1||C2||C3..Cn-1(could be partial)||Cn   (NIST)
    1.59 + *  CS-2: pad == 0 C1||C2||C3...Cn-1(is full)||Cn   (Schneier)
    1.60 + *  CS-2: pad != 0 C1||C2||C3...Cn||Cn-1(is partial)(Schneier)
    1.61 + *  CS-3: C1||C2||C3...Cn||Cn-1(could be partial)   (Kerberos)
    1.62 + *
    1.63 + * The characteristics of these three options:
    1.64 + *  - NIST & Schneier (CS-1 & CS-2) are identical to CBC if there are no
    1.65 + * partial blocks on input.
    1.66 + *  - Scheier and Kerberos (CS-2 and CS-3) have no embedded partial blocks,
    1.67 + * which make decoding easier.
    1.68 + *  - NIST & Kerberos (CS-1 and CS-3) have consistent block order independent
    1.69 + * of padding.
    1.70 + *
    1.71 + * PKCS #11 did not specify which version to implement, but points to the NIST
    1.72 + * spec, so this code implements CTS-CS-1 from NIST.
    1.73 + *
    1.74 + * To convert the returned buffer to:
    1.75 + *   CS-2 (Schneier): do
    1.76 + *       unsigned char tmp[MAX_BLOCK_SIZE];
    1.77 + *       pad = *outlen % blocksize;
    1.78 + *       if (pad) {
    1.79 + *          memcpy(tmp, outbuf+*outlen-blocksize, blocksize);
    1.80 + *          memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad);
    1.81 + *	    memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize);
    1.82 + *       }
    1.83 + *   CS-3 (Kerberos): do
    1.84 + *       unsigned char tmp[MAX_BLOCK_SIZE];
    1.85 + *       pad = *outlen % blocksize;
    1.86 + *       if (pad == 0) {
    1.87 + *           pad = blocksize;
    1.88 + *       }
    1.89 + *       memcpy(tmp, outbuf+*outlen-blocksize, blocksize);
    1.90 + *       memcpy(outbuf+*outlen-pad,outbuf+*outlen-blocksize-pad, pad);
    1.91 + *	 memcpy(outbuf+*outlen-blocksize-pad, tmp, blocksize);
    1.92 + */
    1.93 +SECStatus
    1.94 +CTS_EncryptUpdate(CTSContext *cts, unsigned char *outbuf,
    1.95 +		unsigned int *outlen, unsigned int maxout,
    1.96 +		const unsigned char *inbuf, unsigned int inlen,
    1.97 +		unsigned int blocksize)
    1.98 +{
    1.99 +    unsigned char lastBlock[MAX_BLOCK_SIZE];
   1.100 +    unsigned int tmp;
   1.101 +    int fullblocks;
   1.102 +    int written;
   1.103 +    SECStatus rv;
   1.104 +
   1.105 +    if (inlen < blocksize) {
   1.106 +	PORT_SetError(SEC_ERROR_INPUT_LEN);
   1.107 +	return SECFailure;
   1.108 +    }
   1.109 +
   1.110 +    if (maxout < inlen) {
   1.111 +	*outlen = inlen;
   1.112 +	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
   1.113 +	return SECFailure;
   1.114 +    }
   1.115 +    fullblocks = (inlen/blocksize)*blocksize;
   1.116 +    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf,
   1.117 +	 fullblocks, blocksize);
   1.118 +    if (rv != SECSuccess) {
   1.119 +	return SECFailure;
   1.120 +    }
   1.121 +    *outlen = fullblocks; /* AES low level doesn't set outlen */
   1.122 +    inbuf += fullblocks;
   1.123 +    inlen -= fullblocks;
   1.124 +    if (inlen == 0) {
   1.125 +	return SECSuccess;
   1.126 +    }
   1.127 +    written = *outlen - (blocksize - inlen);
   1.128 +    outbuf += written;
   1.129 +    maxout -= written;
   1.130 +
   1.131 +    /*
   1.132 +     * here's the CTS magic, we pad our final block with zeros,
   1.133 +     * then do a CBC encrypt. CBC will xor our plain text with
   1.134 +     * the previous block (Cn-1), capturing part of that block (Cn-1**) as it
   1.135 +     * xors with the zero pad. We then write this full block, overwritting
   1.136 +     * (Cn-1**) in our buffer. This allows us to have input data == output
   1.137 +     * data since Cn contains enough information to reconver Cn-1** when
   1.138 +     * we decrypt (at the cost of some complexity as you can see in decrypt
   1.139 +     * below */
   1.140 +    PORT_Memcpy(lastBlock, inbuf, inlen);
   1.141 +    PORT_Memset(lastBlock + inlen, 0, blocksize - inlen);
   1.142 +    rv = (*cts->cipher)(cts->context, outbuf, &tmp, maxout, lastBlock,
   1.143 +			blocksize, blocksize);
   1.144 +    PORT_Memset(lastBlock, 0, blocksize);
   1.145 +    if (rv == SECSuccess) {
   1.146 +	*outlen = written + blocksize;
   1.147 +    }
   1.148 +    return rv;
   1.149 +}
   1.150 +
   1.151 +
   1.152 +#define XOR_BLOCK(x,y,count) for(i=0; i < count; i++) x[i] = x[i] ^ y[i]
   1.153 +
   1.154 +/*
   1.155 + * See addemdum to NIST SP 800-38A
   1.156 + * Decrypt, Expect CS-1: input. See the comment on the encrypt side
   1.157 + * to understand what CS-2 and CS-3 mean.
   1.158 + *
   1.159 + * To convert the input buffer to CS-1 from ...
   1.160 + *   CS-2 (Schneier): do
   1.161 + *       unsigned char tmp[MAX_BLOCK_SIZE];
   1.162 + *       pad = inlen % blocksize;
   1.163 + *       if (pad) {
   1.164 + *          memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
   1.165 + *          memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
   1.166 + *	    memcpy(inbuf+inlen-blocksize, tmp, blocksize);
   1.167 + *       }
   1.168 + *   CS-3 (Kerberos): do
   1.169 + *       unsigned char tmp[MAX_BLOCK_SIZE];
   1.170 + *       pad = inlen % blocksize;
   1.171 + *       if (pad == 0) {
   1.172 + *           pad = blocksize;
   1.173 + *       }
   1.174 + *       memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize);
   1.175 + *       memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad);
   1.176 + *	 memcpy(inbuf+inlen-blocksize, tmp, blocksize);
   1.177 + */
   1.178 +SECStatus
   1.179 +CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf,
   1.180 +		unsigned int *outlen, unsigned int maxout,
   1.181 +		const unsigned char *inbuf, unsigned int inlen,
   1.182 +		unsigned int blocksize)
   1.183 +{
   1.184 +    unsigned char *Pn;
   1.185 +    unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */
   1.186 +    unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */
   1.187 +    unsigned char Cn[MAX_BLOCK_SIZE];   /* block Cn   */
   1.188 +    unsigned char lastBlock[MAX_BLOCK_SIZE];
   1.189 +    const unsigned char *tmp;
   1.190 +    unsigned int tmpLen;
   1.191 +    int fullblocks, pad;
   1.192 +    unsigned int i;
   1.193 +    SECStatus rv;
   1.194 +
   1.195 +    if (inlen < blocksize) {
   1.196 +	PORT_SetError(SEC_ERROR_INPUT_LEN);
   1.197 +	return SECFailure;
   1.198 +    }
   1.199 +
   1.200 +    if (maxout < inlen) {
   1.201 +	*outlen = inlen;
   1.202 +	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
   1.203 +	return SECFailure;
   1.204 +    }
   1.205 +
   1.206 +    fullblocks = (inlen/blocksize)*blocksize;
   1.207 +
   1.208 +    /* even though we expect the input to be CS-1, CS-2 is easier to parse,
   1.209 +     * so convert to CS-2 immediately. NOTE: this is the same code as in
   1.210 +     * the comment for encrypt. NOTE2: since we can't modify inbuf unless
   1.211 +     * inbuf and outbuf overlap, just copy inbuf to outbuf and modify it there
   1.212 +     */
   1.213 +    pad = inlen - fullblocks;
   1.214 +    if (pad != 0) {
   1.215 +	if (inbuf != outbuf) {
   1.216 +	    memcpy(outbuf, inbuf, inlen);
   1.217 +	    /* keep the names so we logically know how we are using the
   1.218 +	     * buffers */
   1.219 +	    inbuf = outbuf;
   1.220 +	}
   1.221 +	memcpy(lastBlock, inbuf+inlen-blocksize, blocksize);
   1.222 +	/* we know inbuf == outbuf now, inbuf is declared const and can't
   1.223 +	 * be the target, so use outbuf for the target here */
   1.224 +	memcpy(outbuf+inlen-pad, inbuf+inlen-blocksize-pad, pad);
   1.225 +	memcpy(outbuf+inlen-blocksize-pad, lastBlock, blocksize);
   1.226 +    }
   1.227 +    /* save the previous to last block so we can undo the misordered
   1.228 +     * chaining */
   1.229 +    tmp =  (fullblocks < blocksize*2) ? cts->iv :
   1.230 +			inbuf+fullblocks-blocksize*2;
   1.231 +    PORT_Memcpy(Cn_2, tmp, blocksize);
   1.232 +    PORT_Memcpy(Cn, inbuf+fullblocks-blocksize, blocksize);
   1.233 +    rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf,
   1.234 +	 fullblocks, blocksize);
   1.235 +    if (rv != SECSuccess) {
   1.236 +	return SECFailure;
   1.237 +    }
   1.238 +    *outlen = fullblocks; /* AES low level doesn't set outlen */
   1.239 +    inbuf += fullblocks;
   1.240 +    inlen -= fullblocks;
   1.241 +    if (inlen == 0) {
   1.242 +	return SECSuccess;
   1.243 +    }
   1.244 +    outbuf += fullblocks;
   1.245 +    maxout -= fullblocks;
   1.246 +
   1.247 +    /* recover the stolen text */
   1.248 +    PORT_Memset(lastBlock, 0, blocksize);
   1.249 +    PORT_Memcpy(lastBlock, inbuf, inlen);
   1.250 +    PORT_Memcpy(Cn_1, inbuf, inlen);
   1.251 +    Pn = outbuf-blocksize;
   1.252 +    /* inbuf points to Cn-1* in the input buffer */
   1.253 +    /* NOTE: below there are 2 sections marked "make up for the out of order
   1.254 +     * cbc decryption". You may ask, what is going on here.
   1.255 +     *   Short answer: CBC automatically xors the plain text with the previous
   1.256 +     * encrypted block. We are decrypting the last 2 blocks out of order, so
   1.257 +     * we have to 'back out' the decrypt xor and 'add back' the encrypt xor.
   1.258 +     *   Long answer: When we encrypted, we encrypted as follows:
   1.259 +     *       Pn-2, Pn-1, (Pn || 0), but on decryption we can't
   1.260 +     *  decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in
   1.261 +     *  Cn (see below).  So above we decrypted all the full blocks:
   1.262 +     *       Cn-2, Cn,
   1.263 +     *  to get:
   1.264 +     *       Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we
   1.265 +     *  xor'd Pn || 0  with Cn-1, but on decrypt we xor'd it with Cn-2
   1.266 +     *  To recover Pn, we xor the block with Cn-1* || 0 (in last block) and
   1.267 +     *  Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer
   1.268 +     *  and we can now reunite Cn-1. With the full Cn-1 we can decrypt it,
   1.269 +     *  but now decrypt is going to xor the decrypted data with Cn instead of
   1.270 +     *  Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now
   1.271 +     *  write that oout to the buffer */
   1.272 +
   1.273 +    /* make up for the out of order CBC decryption */
   1.274 +    XOR_BLOCK(lastBlock, Cn_2, blocksize);
   1.275 +    XOR_BLOCK(lastBlock, Pn, blocksize);
   1.276 +    /* last buf now has Pn || Cn-1**, copy out Pn */
   1.277 +    PORT_Memcpy(outbuf, lastBlock, inlen);
   1.278 +    *outlen += inlen;
   1.279 +    /* copy Cn-1* into last buf to recover Cn-1 */
   1.280 +    PORT_Memcpy(lastBlock, Cn_1, inlen);
   1.281 +    /* note: because Cn and Cn-1 were out of order, our pointer to Pn also
   1.282 +     * points to where Pn-1 needs to reside. From here on out read Pn in
   1.283 +     * the code as really Pn-1. */
   1.284 +    rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock,
   1.285 +	 blocksize, blocksize);
   1.286 +    if (rv != SECSuccess) {
   1.287 +	return SECFailure;
   1.288 +    }
   1.289 +    /* make up for the out of order CBC decryption */
   1.290 +    XOR_BLOCK(Pn, Cn_2, blocksize);
   1.291 +    XOR_BLOCK(Pn, Cn, blocksize);
   1.292 +    /* reset iv to Cn  */
   1.293 +    PORT_Memcpy(cts->iv, Cn, blocksize);
   1.294 +    /* This makes Cn the last block for the next decrypt operation, which
   1.295 +     * matches the encrypt. We don't care about the contexts of last block,
   1.296 +     * only the side effect of setting the internal IV */
   1.297 +    (void) (*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn,
   1.298 +		blocksize, blocksize);
   1.299 +    /* clear last block. At this point last block contains Pn xor Cn_1 xor
   1.300 +     * Cn_2, both of with an attacker would know, so we need to clear this
   1.301 +     * buffer out */
   1.302 +    PORT_Memset(lastBlock, 0, blocksize);
   1.303 +    /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */
   1.304 +    return SECSuccess;
   1.305 +}

mercurial