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 +}