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