OLD | NEW |
(Empty) | |
| 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, unsigned int blocksize) |
| 24 { |
| 25 CTSContext *cts; |
| 26 |
| 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 } |
| 40 |
| 41 void |
| 42 CTS_DestroyContext(CTSContext *cts, PRBool freeit) |
| 43 { |
| 44 if (freeit) { |
| 45 PORT_Free(cts); |
| 46 } |
| 47 } |
| 48 |
| 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; |
| 101 |
| 102 if (inlen < blocksize) { |
| 103 PORT_SetError(SEC_ERROR_INPUT_LEN); |
| 104 return SECFailure; |
| 105 } |
| 106 |
| 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 PORT_Assert(*outlen == fullblocks); |
| 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; |
| 127 |
| 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 PORT_Assert(tmp == blocksize); |
| 144 *outlen = written + blocksize; |
| 145 } |
| 146 return rv; |
| 147 } |
| 148 |
| 149 |
| 150 #define XOR_BLOCK(x,y,count) for(i=0; i < count; i++) x[i] = x[i] ^ y[i] |
| 151 |
| 152 /* |
| 153 * See addemdum to NIST SP 800-38A |
| 154 * Decrypt, Expect CS-1: input. See the comment on the encrypt side |
| 155 * to understand what CS-2 and CS-3 mean. |
| 156 * |
| 157 * To convert the input buffer to CS-1 from ... |
| 158 * CS-2 (Schneier): do |
| 159 * unsigned char tmp[MAX_BLOCK_SIZE]; |
| 160 * pad = inlen % blocksize; |
| 161 * if (pad) { |
| 162 * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); |
| 163 * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); |
| 164 * memcpy(inbuf+inlen-blocksize, tmp, blocksize); |
| 165 * } |
| 166 * CS-3 (Kerberos): do |
| 167 * unsigned char tmp[MAX_BLOCK_SIZE]; |
| 168 * pad = inlen % blocksize; |
| 169 * if (pad == 0) { |
| 170 * pad = blocksize; |
| 171 * } |
| 172 * memcpy(tmp, inbuf+inlen-blocksize-pad, blocksize); |
| 173 * memcpy(inbuf+inlen-blocksize-pad,inbuf+inlen-pad, pad); |
| 174 * memcpy(inbuf+inlen-blocksize, tmp, blocksize); |
| 175 */ |
| 176 SECStatus |
| 177 CTS_DecryptUpdate(CTSContext *cts, unsigned char *outbuf, |
| 178 unsigned int *outlen, unsigned int maxout, |
| 179 const unsigned char *inbuf, unsigned int inlen, |
| 180 unsigned int blocksize) |
| 181 { |
| 182 unsigned char *Pn; |
| 183 unsigned char Cn_2[MAX_BLOCK_SIZE]; /* block Cn-2 */ |
| 184 unsigned char Cn_1[MAX_BLOCK_SIZE]; /* block Cn-1 */ |
| 185 unsigned char Cn[MAX_BLOCK_SIZE]; /* block Cn */ |
| 186 unsigned char lastBlock[MAX_BLOCK_SIZE]; |
| 187 const unsigned char *tmp; |
| 188 unsigned int tmpLen; |
| 189 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 = blocksize + (inlen - fullblocks); |
| 212 if (pad != blocksize) { |
| 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-pad, 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-blocksize-pad, inbuf+inlen-pad, pad); |
| 223 memcpy(outbuf+inlen-blocksize, 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 : |
| 228 inbuf+fullblocks-blocksize*2; |
| 229 PORT_Memcpy(Cn_2, tmp, blocksize); |
| 230 PORT_Memcpy(Cn, inbuf+fullblocks-blocksize, blocksize); |
| 231 rv = (*cts->cipher)(cts->context, outbuf, outlen, maxout, inbuf, |
| 232 fullblocks, blocksize); |
| 233 if (rv != SECSuccess) { |
| 234 return SECFailure; |
| 235 } |
| 236 PORT_Assert(*outlen == fullblocks); |
| 237 inbuf += fullblocks; |
| 238 inlen -= fullblocks; |
| 239 if (inlen == 0) { |
| 240 return SECSuccess; |
| 241 } |
| 242 outbuf += fullblocks; |
| 243 maxout -= fullblocks; |
| 244 |
| 245 /* recover the stolen text */ |
| 246 PORT_Memset(lastBlock, 0, blocksize); |
| 247 PORT_Memcpy(lastBlock, inbuf, inlen); |
| 248 PORT_Memcpy(Cn_1, inbuf, inlen); |
| 249 Pn = outbuf-blocksize; |
| 250 /* inbuf points to Cn-1* in the input buffer */ |
| 251 /* NOTE: below there are 2 sections marked "make up for the out of order |
| 252 * cbc decryption". You may ask, what is going on here. |
| 253 * Short answer: CBC automatically xors the plain text with the previous |
| 254 * encrypted block. We are decrypting the last 2 blocks out of order, so |
| 255 * we have to 'back out' the decrypt xor and 'add back' the encrypt xor. |
| 256 * Long answer: When we encrypted, we encrypted as follows: |
| 257 * Pn-2, Pn-1, (Pn || 0), but on decryption we can't |
| 258 * decrypt Cn-1 until we decrypt Cn because part of Cn-1 is stored in |
| 259 * Cn (see below). So above we decrypted all the full blocks: |
| 260 * Cn-2, Cn, |
| 261 * to get: |
| 262 * Pn-2, Pn, Except that Pn is not yet corect. On encrypt, we |
| 263 * xor'd Pn || 0 with Cn-1, but on decrypt we xor'd it with Cn-2 |
| 264 * To recover Pn, we xor the block with Cn-1* || 0 (in last block) and |
| 265 * Cn-2 to get Pn || Cn-1**. Pn can then be written to the output buffer |
| 266 * and we can now reunite Cn-1. With the full Cn-1 we can decrypt it, |
| 267 * but now decrypt is going to xor the decrypted data with Cn instead of |
| 268 * Cn-2. xoring Cn and Cn-2 restores the original Pn-1 and we can now |
| 269 * write that oout to the buffer */ |
| 270 |
| 271 /* make up for the out of order CBC decryption */ |
| 272 XOR_BLOCK(lastBlock, Cn_2, blocksize); |
| 273 XOR_BLOCK(lastBlock, Pn, blocksize); |
| 274 /* last buf now has Pn || Cn-1**, copy out Pn */ |
| 275 PORT_Memcpy(outbuf, lastBlock, inlen); |
| 276 *outlen += inlen; |
| 277 /* copy Cn-1* into last buf to recover Cn-1 */ |
| 278 PORT_Memcpy(lastBlock, Cn-1, inlen); |
| 279 /* note: because Cn and Cn-1 were out of order, our pointer to Pn also |
| 280 * points to where Pn-1 needs to reside. From here on out read Pn in |
| 281 * the code as really Pn-1. */ |
| 282 rv = (*cts->cipher)(cts->context, Pn, &tmpLen, blocksize, lastBlock, |
| 283 blocksize, blocksize); |
| 284 if (rv != SECSuccess) { |
| 285 return SECFailure; |
| 286 } |
| 287 PORT_Assert(tmpLen == blocksize); |
| 288 /* make up for the out of order CBC decryption */ |
| 289 XOR_BLOCK(Pn, Cn_2, blocksize); |
| 290 XOR_BLOCK(Pn, Cn, blocksize); |
| 291 /* reset iv to Cn */ |
| 292 PORT_Memcpy(cts->iv, Cn, blocksize); |
| 293 /* This makes Cn the last block for the next decrypt operation, which |
| 294 * matches the encrypt. We don't care about the contexts of last block, |
| 295 * only the side effect of setting the internal IV */ |
| 296 (void) (*cts->cipher)(cts->context, lastBlock, &tmpLen, blocksize, Cn, |
| 297 blocksize, blocksize); |
| 298 /* clear last block. At this point last block contains Pn xor Cn_1 xor |
| 299 * Cn_2, both of with an attacker would know, so we need to clear this |
| 300 * buffer out */ |
| 301 PORT_Memset(lastBlock, 0, blocksize); |
| 302 /* Cn, Cn_1, and Cn_2 have encrypted data, so no need to clear them */ |
| 303 return SECSuccess; |
| 304 } |
OLD | NEW |