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