1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3
4 ARCFOUR cipher (based on a cipher posted on the Usenet in Spring-95).
5 This cipher is widely believed and has been tested to be equivalent
6 with the RC4 cipher from RSA Data Security, Inc. (RC4 is a trademark
7 of RSA Data Security)
8
9 */
10 #include "crypto_int.h"
11
12 #define CONFOUNDERLENGTH 8
13
14 const char l40[] = "fortybits";
15
16 krb5_keyusage
krb5int_arcfour_translate_usage(krb5_keyusage usage)17 krb5int_arcfour_translate_usage(krb5_keyusage usage)
18 {
19 switch (usage) {
20 case 1: return 1; /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, */
21 case 2: return 2; /* ticket from kdc */
22 case 3: return 8; /* as-rep encrypted part */
23 case 4: return 4; /* tgs-req authz data */
24 case 5: return 5; /* tgs-req authz data in subkey */
25 case 6: return 6; /* tgs-req authenticator cksum */
26 case 7: return 7; /* tgs-req authenticator */
27 case 8: return 8;
28 case 9: return 9; /* tgs-rep encrypted with subkey */
29 case 10: return 10; /* ap-rep authentication cksum (never used by MS) */
30 case 11: return 11; /* app-req authenticator */
31 case 12: return 12; /* app-rep encrypted part */
32 case 23: return 13; /* sign wrap token*/
33 default: return usage;
34 }
35 }
36
37 /* Derive a usage key from a session key and krb5 usage constant. */
38 static krb5_error_code
usage_key(const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,const krb5_keyblock * session_keyblock,krb5_keyusage usage,krb5_keyblock * out)39 usage_key(const struct krb5_enc_provider *enc,
40 const struct krb5_hash_provider *hash,
41 const krb5_keyblock *session_keyblock, krb5_keyusage usage,
42 krb5_keyblock *out)
43 {
44 char salt_buf[14];
45 unsigned int salt_len;
46 krb5_data out_data = make_data(out->contents, out->length);
47 krb5_crypto_iov iov;
48 krb5_keyusage ms_usage;
49
50 /* Generate the salt. */
51 ms_usage = krb5int_arcfour_translate_usage(usage);
52 if (session_keyblock->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
53 memcpy(salt_buf, l40, 10);
54 store_32_le(ms_usage, salt_buf + 10);
55 salt_len = 14;
56 } else {
57 store_32_le(ms_usage, salt_buf);
58 salt_len = 4;
59 }
60
61 /* Compute HMAC(key, salt) to produce the usage key. */
62 iov.flags = KRB5_CRYPTO_TYPE_DATA;
63 iov.data = make_data(salt_buf, salt_len);
64 return krb5int_hmac_keyblock(hash, session_keyblock, &iov, 1, &out_data);
65 }
66
67 /* Derive an encryption key from a usage key and (typically) checksum. */
68 static krb5_error_code
enc_key(const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,const krb5_keyblock * usage_keyblock,const krb5_data * checksum,krb5_keyblock * out)69 enc_key(const struct krb5_enc_provider *enc,
70 const struct krb5_hash_provider *hash,
71 const krb5_keyblock *usage_keyblock, const krb5_data *checksum,
72 krb5_keyblock *out)
73 {
74 krb5_keyblock *trunc_keyblock = NULL;
75 krb5_data out_data = make_data(out->contents, out->length);
76 krb5_crypto_iov iov;
77 krb5_error_code ret;
78
79 /* Copy usage_keyblock to trunc_keyblock and truncate if exportable. */
80 ret = krb5int_c_copy_keyblock(NULL, usage_keyblock, &trunc_keyblock);
81 if (ret != 0)
82 return ret;
83 if (trunc_keyblock->enctype == ENCTYPE_ARCFOUR_HMAC_EXP)
84 memset(trunc_keyblock->contents + 7, 0xab, 9);
85
86 /* Compute HMAC(trunc_key, checksum) to produce the encryption key. */
87 iov.flags = KRB5_CRYPTO_TYPE_DATA;
88 iov.data = *checksum;
89 ret = krb5int_hmac_keyblock(hash, trunc_keyblock, &iov, 1, &out_data);
90 krb5int_c_free_keyblock(NULL, trunc_keyblock);
91 return ret;
92 }
93
94 unsigned int
krb5int_arcfour_crypto_length(const struct krb5_keytypes * ktp,krb5_cryptotype type)95 krb5int_arcfour_crypto_length(const struct krb5_keytypes *ktp,
96 krb5_cryptotype type)
97 {
98 switch (type) {
99 case KRB5_CRYPTO_TYPE_HEADER:
100 return ktp->hash->hashsize + CONFOUNDERLENGTH;
101 case KRB5_CRYPTO_TYPE_PADDING:
102 case KRB5_CRYPTO_TYPE_TRAILER:
103 return 0;
104 case KRB5_CRYPTO_TYPE_CHECKSUM:
105 return ktp->hash->hashsize;
106 default:
107 assert(0 &&
108 "invalid cryptotype passed to krb5int_arcfour_crypto_length");
109 return 0;
110 }
111 }
112
113 /* Encrypt or decrypt using a keyblock. */
114 static krb5_error_code
keyblock_crypt(const struct krb5_enc_provider * enc,krb5_keyblock * keyblock,const krb5_data * ivec,krb5_crypto_iov * data,size_t num_data)115 keyblock_crypt(const struct krb5_enc_provider *enc, krb5_keyblock *keyblock,
116 const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data)
117 {
118 krb5_error_code ret;
119 krb5_key key;
120
121 ret = krb5_k_create_key(NULL, keyblock, &key);
122 if (ret != 0)
123 return ret;
124 /* Works for encryption or decryption since arcfour is a stream cipher. */
125 ret = enc->encrypt(key, ivec, data, num_data);
126 krb5_k_free_key(NULL, key);
127 return ret;
128 }
129
130 krb5_error_code
krb5int_arcfour_encrypt(const struct krb5_keytypes * ktp,krb5_key key,krb5_keyusage usage,const krb5_data * ivec,krb5_crypto_iov * data,size_t num_data)131 krb5int_arcfour_encrypt(const struct krb5_keytypes *ktp, krb5_key key,
132 krb5_keyusage usage, const krb5_data *ivec,
133 krb5_crypto_iov *data, size_t num_data)
134 {
135 const struct krb5_enc_provider *enc = ktp->enc;
136 const struct krb5_hash_provider *hash = ktp->hash;
137 krb5_error_code ret;
138 krb5_crypto_iov *header, *trailer;
139 krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL;
140 krb5_data checksum, confounder, header_data;
141 size_t i;
142
143 /*
144 * Caller must have provided space for the header, padding
145 * and trailer; per RFC 4757 we will arrange it as:
146 *
147 * Checksum | E(Confounder | Plaintext)
148 */
149
150 header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
151 if (header == NULL ||
152 header->data.length < hash->hashsize + CONFOUNDERLENGTH)
153 return KRB5_BAD_MSIZE;
154
155 header_data = header->data;
156
157 /* Trailer may be absent. */
158 trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
159 if (trailer != NULL)
160 trailer->data.length = 0;
161
162 /* Ensure that there is no padding. */
163 for (i = 0; i < num_data; i++) {
164 if (data[i].flags == KRB5_CRYPTO_TYPE_PADDING)
165 data[i].data.length = 0;
166 }
167
168 ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes,
169 &usage_keyblock);
170 if (ret != 0)
171 goto cleanup;
172 ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes,
173 &enc_keyblock);
174 if (ret != 0)
175 goto cleanup;
176
177 /* Derive a usage key from the session key and usage. */
178 ret = usage_key(enc, hash, &key->keyblock, usage, usage_keyblock);
179 if (ret != 0)
180 goto cleanup;
181
182 /* Generate a confounder in the header block, after the checksum. */
183 header->data.length = hash->hashsize + CONFOUNDERLENGTH;
184 confounder = make_data(header->data.data + hash->hashsize,
185 CONFOUNDERLENGTH);
186 ret = krb5_c_random_make_octets(0, &confounder);
187 if (ret != 0)
188 goto cleanup;
189 checksum = make_data(header->data.data, hash->hashsize);
190
191 /* Adjust pointers so confounder is at start of header. */
192 header->data.length -= hash->hashsize;
193 header->data.data += hash->hashsize;
194
195 /* Compute the checksum using the usage key. */
196 ret = krb5int_hmac_keyblock(hash, usage_keyblock, data, num_data,
197 &checksum);
198 if (ret != 0)
199 goto cleanup;
200
201 /* Derive the encryption key from the usage key and checksum. */
202 ret = enc_key(enc, hash, usage_keyblock, &checksum, enc_keyblock);
203 if (ret)
204 goto cleanup;
205
206 ret = keyblock_crypt(enc, enc_keyblock, ivec, data, num_data);
207
208 cleanup:
209 header->data = header_data; /* Restore header pointers. */
210 krb5int_c_free_keyblock(NULL, usage_keyblock);
211 krb5int_c_free_keyblock(NULL, enc_keyblock);
212 return ret;
213 }
214
215 krb5_error_code
krb5int_arcfour_decrypt(const struct krb5_keytypes * ktp,krb5_key key,krb5_keyusage usage,const krb5_data * ivec,krb5_crypto_iov * data,size_t num_data)216 krb5int_arcfour_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
217 krb5_keyusage usage, const krb5_data *ivec,
218 krb5_crypto_iov *data, size_t num_data)
219 {
220 const struct krb5_enc_provider *enc = ktp->enc;
221 const struct krb5_hash_provider *hash = ktp->hash;
222 krb5_error_code ret;
223 krb5_crypto_iov *header, *trailer;
224 krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL;
225 krb5_data checksum, header_data, comp_checksum = empty_data();
226
227 header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
228 if (header == NULL ||
229 header->data.length != hash->hashsize + CONFOUNDERLENGTH)
230 return KRB5_BAD_MSIZE;
231
232 header_data = header->data;
233
234 trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
235 if (trailer != NULL && trailer->data.length != 0)
236 return KRB5_BAD_MSIZE;
237
238 /* Allocate buffers. */
239 ret = alloc_data(&comp_checksum, hash->hashsize);
240 if (ret != 0)
241 goto cleanup;
242 ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes,
243 &usage_keyblock);
244 if (ret != 0)
245 goto cleanup;
246 ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes,
247 &enc_keyblock);
248 if (ret != 0)
249 goto cleanup;
250
251 checksum = make_data(header->data.data, hash->hashsize);
252
253 /* Adjust pointers so confounder is at start of header. */
254 header->data.length -= hash->hashsize;
255 header->data.data += hash->hashsize;
256
257 /* We may have to try two usage values; see below. */
258 do {
259 /* Derive a usage key from the session key and usage. */
260 ret = usage_key(enc, hash, &key->keyblock, usage, usage_keyblock);
261 if (ret != 0)
262 goto cleanup;
263
264 /* Derive the encryption key from the usage key and checksum. */
265 ret = enc_key(enc, hash, usage_keyblock, &checksum, enc_keyblock);
266 if (ret)
267 goto cleanup;
268
269 /* Decrypt the ciphertext. */
270 ret = keyblock_crypt(enc, enc_keyblock, ivec, data, num_data);
271 if (ret != 0)
272 goto cleanup;
273
274 /* Compute HMAC(usage key, plaintext) to get the checksum. */
275 ret = krb5int_hmac_keyblock(hash, usage_keyblock, data, num_data,
276 &comp_checksum);
277 if (ret != 0)
278 goto cleanup;
279
280 if (k5_bcmp(checksum.data, comp_checksum.data, hash->hashsize) != 0) {
281 if (usage == 9) {
282 /*
283 * RFC 4757 specifies usage 8 for TGS-REP encrypted parts
284 * encrypted in a subkey, but the value used by MS is actually
285 * 9. We now use 9 to start with, but fall back to 8 on
286 * failure in case we are communicating with a KDC using the
287 * value from the RFC. ivec is always NULL in this case.
288 * We need to re-encrypt the data in the wrong key first.
289 */
290 ret = keyblock_crypt(enc, enc_keyblock, NULL, data, num_data);
291 if (ret != 0)
292 goto cleanup;
293 usage = 8;
294 continue;
295 }
296 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
297 goto cleanup;
298 }
299
300 break;
301 } while (1);
302
303 cleanup:
304 header->data = header_data; /* Restore header pointers. */
305 krb5int_c_free_keyblock(NULL, usage_keyblock);
306 krb5int_c_free_keyblock(NULL, enc_keyblock);
307 zapfree(comp_checksum.data, comp_checksum.length);
308 return ret;
309 }
310
311 krb5_error_code
krb5int_arcfour_gsscrypt(const krb5_keyblock * keyblock,krb5_keyusage usage,const krb5_data * kd_data,krb5_crypto_iov * data,size_t num_data)312 krb5int_arcfour_gsscrypt(const krb5_keyblock *keyblock, krb5_keyusage usage,
313 const krb5_data *kd_data, krb5_crypto_iov *data,
314 size_t num_data)
315 {
316 const struct krb5_enc_provider *enc = &krb5int_enc_arcfour;
317 const struct krb5_hash_provider *hash = &krb5int_hash_md5;
318 krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL;
319 krb5_error_code ret;
320
321 ret = krb5int_c_init_keyblock(NULL, keyblock->enctype, enc->keybytes,
322 &usage_keyblock);
323 if (ret != 0)
324 goto cleanup;
325 ret = krb5int_c_init_keyblock(NULL, keyblock->enctype, enc->keybytes,
326 &enc_keyblock);
327 if (ret != 0)
328 goto cleanup;
329
330 /* Derive a usage key from the session key and usage. */
331 ret = usage_key(enc, hash, keyblock, usage, usage_keyblock);
332 if (ret != 0)
333 goto cleanup;
334
335 /* Derive the encryption key from the usage key and kd_data. */
336 ret = enc_key(enc, hash, usage_keyblock, kd_data, enc_keyblock);
337 if (ret != 0)
338 goto cleanup;
339
340 /* Encrypt or decrypt (encrypt_iov works for both) the input. */
341 ret = keyblock_crypt(enc, enc_keyblock, 0, data, num_data);
342
343 cleanup:
344 krb5int_c_free_keyblock(NULL, usage_keyblock);
345 krb5int_c_free_keyblock(NULL, enc_keyblock);
346 return ret;
347 }
348