df8745a1cee864b734387bb0e43a3683ec7a7d1f
[openafs.git] / src / rxgk / rxgk_crypto_rfc3961.c
1 /* rxgk/rxgk_crypto_rfc3961.c - Wrappers for RFC3961 crypto used in RXGK. */
2 /*
3  * Copyright (C) 2013, 2014 by the Massachusetts Institute of Technology.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /**
33  * @file
34  * Wrappers for the RFC3961 crypto routines used by RXGK, and
35  * helpers.  This implementation uses the in-tree rfc3961 library, but
36  * we do not expose those types in our interface so as to be
37  * compatible with other backends in the future.  It should be possible
38  * to backend to an out-of-tree krb5 library or the kernel's crypto
39  * framework using this API.
40  *
41  * Public functions in this file should return RXGK error codes, because
42  * error codes from these functions can end up on the wire.  This will
43  * entail converting from any krb5 error codes that are used internally.
44  */
45
46 #include <afsconfig.h>
47 #include <afs/param.h>
48 #include <afs/stds.h>
49
50 #ifdef KERNEL
51 # include "afs/sysincludes.h"
52 # include "afsincludes.h"
53 #else
54 # include <errno.h>
55 #endif
56
57 #include <rx/rx.h>
58 #include <rx/rxgk.h>
59 #include <afs/rfc3961.h>
60 #include <afs/opr.h>
61
62 #include "rxgk_private.h"
63
64 /*
65  * This is what an rxgk_key really is, but it's a 'struct rxgk_key_s' to consumers:
66  * typedef struct rxgk_keyblock * rxgk_key;
67  */
68 struct rxgk_keyblock {
69     /* A krb5 context for this key, only to be used for initialization and
70      * destruction of the key. Do not use this for actually using the key for
71      * crypto operations; we are supposed to avoid accessing it in multiple
72      * threads at the same time. */
73     krb5_context init_ctx;
74     krb5_keyblock key;
75 };
76
77 struct rxgk_key_s {
78     struct rxgk_keyblock keyblock;
79 };
80
81 /* Convenience macro; reduces the diff if an MIT krb5 backend is made. */
82 #define deref_keyblock_enctype(k)       krb5_keyblock_get_enctype(k)
83
84 /* Convenience functions to convert between the opaque rxgk_key type and our
85  * internal rxgk_keyblock struct. */
86 static_inline struct rxgk_keyblock *
87 key2keyblock(rxgk_key key)
88 {
89     return &key->keyblock;
90 }
91
92 static_inline rxgk_key
93 keyblock2key(struct rxgk_keyblock *keyblock)
94 {
95     return (rxgk_key)keyblock;
96 }
97
98 /**
99  * Convert krb5 error code to RXGK error code.  Don't let the krb5 codes escape.
100  */
101 static_inline afs_int32
102 ktor(afs_int32 err)
103 {
104
105     if (err >= ERROR_TABLE_BASE_RXGK && err < (ERROR_TABLE_BASE_RXGK + 256))
106         return err;
107     switch (err) {
108         case 0:
109             return 0;
110         default:
111             return RXGK_INCONSISTENCY;
112     }
113 }
114
115 /**
116  * Convert a krb5 enctype to a krb5 checksum type.
117  *
118  * Each enctype has a mandatory (to implement) checksum type, which can be
119  * chosen when computing a checksum by passing 0 for the type parameter.
120  * However, we must separately compute the length of a checksum on a message in
121  * order to extract the checksum from a packet at RXGK_LEVEL_AUTH, and Heimdal
122  * krb5 does not expose a way to get the mandatory checksum type for a given
123  * enctype.  So, we get to do it ourselves.
124  *
125  * @return -1 on failure, otherwise the checksum type.
126  */
127 static_inline afs_int32
128 etoc(afs_int32 etype)
129 {
130     switch(etype) {
131         case ETYPE_DES_CBC_CRC:
132             return CKSUMTYPE_RSA_MD5_DES;
133         case ETYPE_DES_CBC_MD4:
134             return CKSUMTYPE_RSA_MD4_DES;
135         case ETYPE_DES_CBC_MD5:
136             return CKSUMTYPE_RSA_MD5_DES;
137         case ETYPE_DES3_CBC_SHA1:
138             return CKSUMTYPE_HMAC_SHA1_DES3;
139         case ETYPE_ARCFOUR_MD4:
140             return CKSUMTYPE_HMAC_MD5_ENC;
141         case ETYPE_AES128_CTS_HMAC_SHA1_96:
142             return CKSUMTYPE_HMAC_SHA1_96_AES_128;
143         case ETYPE_AES256_CTS_HMAC_SHA1_96:
144             return CKSUMTYPE_HMAC_SHA1_96_AES_256;
145         default:
146             return -1;
147     }
148 }
149
150 /**
151  * Get the number of octets of input needed for a key of the given etype,
152  *
153  * @return -1 on error, or the number of octets of input needed on success.
154  */
155 ssize_t
156 rxgk_etype_to_len(int etype)
157 {
158     krb5_context ctx;
159     krb5_error_code code;
160     size_t bits;
161
162     code = krb5_init_context(&ctx);
163     if (code != 0)
164         return -1;
165     code = krb5_enctype_keybits(ctx, etype, &bits);
166     krb5_free_context(ctx);
167     if (code != 0)
168         return -1;
169     return (bits + 7) / 8;
170 }
171
172 /**
173  * Take a raw key from some external source and produce an rxgk_key from it.
174  *
175  * The raw_key and length are not an RXGK_Data because in some cases they will
176  * come from a gss_buffer and there's no real need to do the conversion.
177  * The caller must use rxgk_release_key to deallocate memory allocated for the
178  * new rxgk_key.
179  *
180  * This routine checks whether the length of the supplied key data matches the
181  * key generation seed length for the requested enctype, in which case the RFC
182  * 3961 random_to_key operation is performed, or if it is the actual (output)
183  * key length, in which case the key data is used as-is.
184  *
185  * @param key_out       the returned rxgk_key.
186  * @param raw_key       a pointer to the octet stream of the key input data.
187  * @param length        the length of raw_key (in octets).
188  * @param enctype       the RFC 3961 enctype of the key being constructed.
189  * @return rxgk error codes.
190  */
191 afs_int32
192 rxgk_make_key(rxgk_key *key_out, void *raw_key, afs_uint32 length,
193               afs_int32 enctype)
194 {
195     struct rxgk_keyblock *new_key = NULL;
196     krb5_error_code ret;
197     size_t full_length;
198     ssize_t input_length;
199
200     /* Must initialize before we return. */
201     *key_out = NULL;
202
203     new_key = rxi_Alloc(sizeof(*new_key));
204     if (new_key == NULL) {
205         ret = RXGK_INCONSISTENCY;
206         goto done;
207     }
208     ret = krb5_init_context(&new_key->init_ctx);
209     if (ret != 0)
210         goto done;
211     ret = krb5_enctype_keysize(new_key->init_ctx, enctype, &full_length);
212     if (ret != 0)
213         goto done;
214     input_length = rxgk_etype_to_len(enctype);
215     if (input_length < 0) {
216         ret = RXGK_INCONSISTENCY;
217         goto done;
218     }
219     if (length == full_length) {
220         /* free with krb5_free_keyblock_contents + rxi_Free */
221         ret = krb5_keyblock_init(new_key->init_ctx, enctype, raw_key, length,
222                                  &new_key->key);
223     } else if (length == input_length) {
224         /* free with krb5_free_keyblock_contents + rxi_Free */
225         ret = krb5_random_to_key(new_key->init_ctx, enctype, raw_key, length,
226                                  &new_key->key);
227     } else {
228         ret = RXGK_BADETYPE;
229     }
230     if (ret != 0)
231         goto done;
232     *key_out = keyblock2key(new_key);
233  done:
234     if (ret != 0 && new_key != NULL) {
235         if (new_key->init_ctx != NULL) {
236             krb5_free_context(new_key->init_ctx);
237         }
238         rxi_Free(new_key, sizeof(*new_key));
239     }
240     return ktor(ret);
241 }
242
243 /**
244  * Copy a given key.
245  *
246  * The caller must use rxgk_release_key to deallocate the memory allocated
247  * for the new rxgk_key.
248  *
249  * @param[in] key_in    The key to be copied.
250  * @param[out] key_out  A copy of key_in.
251  * @return rxgk error codes.
252  */
253 afs_int32
254 rxgk_copy_key(rxgk_key key_in, rxgk_key *key_out)
255 {
256     struct rxgk_keyblock *keyblock;
257
258     keyblock = key2keyblock(key_in);
259     return rxgk_make_key(key_out, keyblock->key.keyvalue.data,
260                          keyblock->key.keyvalue.length, keyblock->key.keytype);
261 }
262
263 /**
264  * Generate a random key.
265  *
266  * The caller must use rxgk_release_key to deallocate the memory allocated
267  * for the new rxgk_key.
268  *
269  * @param[inout] enctype  The RFC 3961 enctype of the key to be generated. If
270  *                        0, set this to a default enctype, and use that
271  *                        enctype for the generated key.
272  * @param[out] key_out  The random rxgk key.
273  * @return rxgk error codes.
274  */
275 afs_int32
276 rxgk_random_key(afs_int32 *enctype, rxgk_key *key_out)
277 {
278     void *buf;
279     krb5_error_code ret;
280     ssize_t len;
281
282     buf = NULL;
283     *key_out = NULL;
284
285     if (*enctype == 0)
286         *enctype = ETYPE_AES128_CTS_HMAC_SHA1_96;
287
288     len = rxgk_etype_to_len(*enctype);
289     if (len < 0)
290         return RXGK_INCONSISTENCY;
291     buf = rxi_Alloc(len);
292     if (buf == NULL)
293         return RXGK_INCONSISTENCY;
294     krb5_generate_random_block(buf, (size_t)len);
295     ret = rxgk_make_key(key_out, buf, len, *enctype);
296     rxi_Free(buf, len);
297     return ret;
298 }
299
300 /**
301  * Release the storage underlying an rxgk key
302  *
303  * Call into the underlying library to release any storage allocated for
304  * the rxgk_key, and null out the key pointer.
305  */
306 void
307 rxgk_release_key(rxgk_key *key)
308 {
309     struct rxgk_keyblock *keyblock;
310
311     if (*key == NULL)
312         return;
313     keyblock = key2keyblock(*key);
314
315     krb5_free_keyblock_contents(keyblock->init_ctx, &keyblock->key);
316     if (keyblock->init_ctx != NULL) {
317         krb5_free_context(keyblock->init_ctx);
318     }
319     rxi_Free(keyblock, sizeof(*keyblock));
320     *key = NULL;
321 }
322
323 /**
324  * Determine the length of a checksum (MIC) using the specified key.
325  *
326  * @param[in] key       The rxgk_key being queried.
327  * @param[out] out      The length of a checksum made using key.
328  * @return rxgk error codes.
329  */
330 afs_int32
331 rxgk_mic_length(rxgk_key key, size_t *out)
332 {
333     krb5_context ctx = NULL;
334     krb5_cksumtype cstype;
335     krb5_enctype enctype;
336     krb5_error_code ret;
337     struct rxgk_keyblock *keyblock = key2keyblock(key);
338     size_t len;
339
340     *out = 0;
341
342     ret = krb5_init_context(&ctx);
343     if (ret != 0)
344         goto done;
345
346     enctype = deref_keyblock_enctype(&keyblock->key);
347     cstype = etoc(enctype);
348     if (cstype == -1) {
349         ret = RXGK_BADETYPE;
350         goto done;
351     }
352     ret = krb5_checksumsize(ctx, cstype, &len);
353     if (ret != 0)
354         goto done;
355     *out = len;
356
357  done:
358     if (ctx != NULL) {
359         krb5_free_context(ctx);
360     }
361     return ktor(ret);
362 }
363
364 /**
365  * Obtain the RFC 3961 Message Integrity Check of a buffer
366  *
367  * Call into the RFC 3961 encryption framework to obtain a Message Integrity
368  * Check of a buffer using the specified key and key usage.  It is assumed
369  * that the rxgk_key structure includes the enctype information needed to
370  * determine which crypto routine to call.
371  *
372  * The output buffer is allocated with rx_opaque_populate() and must be freed
373  * by the caller (with rx_opaque_freeContents()).
374  *
375  * @param[in] key       The key used to key the MIC.
376  * @param[in] usage     The key usage value to use (from rxgk_int.h).
377  * @param[in] in        The input buffer to be MICd.
378  * @param[out] out      The MIC.
379  * @return rxgk error codes.
380  */
381 afs_int32
382 rxgk_mic_in_key(rxgk_key key, afs_int32 usage, RXGK_Data *in,
383                 RXGK_Data *out)
384 {
385     krb5_context ctx = NULL;
386     Checksum cksum;
387     krb5_cksumtype cstype;
388     krb5_crypto crypto = NULL;
389     krb5_enctype enctype;
390     krb5_error_code ret;
391     struct rxgk_keyblock *keyblock = key2keyblock(key);
392     size_t len;
393
394     memset(&cksum, 0, sizeof(cksum));
395     memset(out, 0, sizeof(*out));
396
397     ret = krb5_init_context(&ctx);
398     if (ret != 0)
399         goto done;
400
401     enctype = deref_keyblock_enctype(&keyblock->key);
402     cstype = etoc(enctype);
403     if (cstype == -1) {
404         ret = RXGK_BADETYPE;
405         goto done;
406     }
407     ret = krb5_checksumsize(ctx, cstype, &len);
408     if (ret != 0)
409         goto done;
410     ret = krb5_crypto_init(ctx, &keyblock->key, enctype, &crypto);
411     if (ret != 0)
412         goto done;
413     ret = krb5_create_checksum(ctx, crypto, usage, cstype, in->val,
414                                in->len, &cksum);
415     if (ret != 0)
416         goto done;
417     /* sanity check */
418     if (len != cksum.checksum.length) {
419         ret = RXGK_INCONSISTENCY;
420         goto done;
421     }
422     ret = rx_opaque_populate(out, cksum.checksum.data, len);
423
424  done:
425     free_Checksum(&cksum);
426     if (crypto != NULL)
427         krb5_crypto_destroy(ctx, crypto);
428     if (ctx != NULL) {
429         krb5_free_context(ctx);
430     }
431     return ktor(ret);
432 }
433
434 /**
435  * Verify the RFC 3961 Message Integrity Check on a message
436  *
437  * Call into the RFC 3961 encryption framework to verify a message integrity
438  * check on a message, using the specified key with the specified key usage.
439  * It is assumed that the rxgk_key structure includes the enctype information
440  * needed to determine which particular crypto routine to call.
441  *
442  * @param[in] key       The key keying the checksum.
443  * @param[in] usage     The key usage for the checksum.
444  * @param[in] in        The buffer which was checksummed.
445  * @param[in] mic       The MIC to be verified.
446  * @return rxgk error codes.
447  */
448 afs_int32
449 rxgk_check_mic_in_key(rxgk_key key, afs_int32 usage, RXGK_Data *in,
450                       RXGK_Data *mic)
451 {
452     krb5_context ctx = NULL;
453     Checksum cksum;
454     krb5_crypto crypto = NULL;
455     krb5_enctype enctype;
456     krb5_error_code ret;
457     struct rxgk_keyblock *keyblock = key2keyblock(key);
458
459     memset(&cksum, 0, sizeof(cksum));
460
461     ret = krb5_init_context(&ctx);
462     if (ret != 0)
463         goto done;
464
465     enctype = deref_keyblock_enctype(&keyblock->key);
466     cksum.cksumtype = etoc(enctype);
467     ret = krb5_crypto_init(ctx, &keyblock->key, enctype, &crypto);
468     if (ret != 0)
469         goto done;
470     cksum.checksum.data = mic->val;
471     cksum.checksum.length = mic->len;
472     ret = krb5_verify_checksum(ctx, crypto, usage, in->val, in->len,
473                                &cksum);
474     /* Un-alias the storage to avoid a double-free. */
475     cksum.checksum.data = NULL;
476     cksum.checksum.length = 0;
477     if (ret != 0) {
478         ret = RXGK_SEALED_INCON;
479     }
480
481  done:
482     free_Checksum(&cksum);
483     if (crypto != NULL)
484         krb5_crypto_destroy(ctx, crypto);
485     if (ctx != NULL) {
486         krb5_free_context(ctx);
487     }
488     return ktor(ret);
489 }
490
491 /**
492  * Encrypt a buffer in a key using the RFC 3961 framework
493  *
494  * Call into the RFC 3961 encryption framework to encrypt a buffer with
495  * specified key and key usage.  It is assumed that the rxgk_key structure
496  * includes the enctype information needed to determine which particular
497  * crypto routine to call.
498  *
499  * The output buffer is allocated with rx_opaque_populate() and must be freed
500  * by the caller (with rx_opaque_freeContents()).
501  *
502  * @param[in] key       The key used to encrypt the message.
503  * @param[in] usage     The key usage for the encryption.
504  * @param[in] in        The buffer being encrypted.
505  * @param[out] out      The encrypted form of the message.
506  * @return rxgk error codes.
507  */
508 afs_int32
509 rxgk_encrypt_in_key(rxgk_key key, afs_int32 usage, RXGK_Data *in,
510                     RXGK_Data *out)
511 {
512     krb5_context ctx = NULL;
513     krb5_crypto crypto = NULL;
514     krb5_data kd_out;
515     krb5_enctype enctype;
516     krb5_error_code ret;
517     struct rxgk_keyblock *keyblock = key2keyblock(key);
518
519     memset(&kd_out, 0, sizeof(kd_out));
520     memset(out, 0, sizeof(*out));
521
522     ret = krb5_init_context(&ctx);
523     if (ret != 0)
524         goto done;
525
526     enctype = deref_keyblock_enctype(&keyblock->key);
527     ret = krb5_crypto_init(ctx, &keyblock->key, enctype, &crypto);
528     if (ret != 0)
529         goto done;
530     ret = krb5_encrypt(ctx, crypto, usage, in->val, in->len, &kd_out);
531     if (ret != 0)
532         goto done;
533     ret = rx_opaque_populate(out, kd_out.data, kd_out.length);
534
535  done:
536     if (crypto != NULL)
537         krb5_crypto_destroy(ctx, crypto);
538     krb5_data_free(&kd_out);
539     if (ctx != NULL) {
540         krb5_free_context(ctx);
541     }
542     return ktor(ret);
543 }
544
545 /**
546  * Decrypt a buffer using a given key in the RFC 3961 framework
547  *
548  * Call into the RFC 3961 encryption framework to decrypt a buffer with the
549  * specified key with the specified key usage.  It is assumed that the
550  * rxgk_key structure includes the enctype information needed to determine
551  * which particular crypto routine to call.
552  *
553  * The output buffer is allocated with rx_opaque_populate() and must be freed
554  * by the caller (with rx_opaque_freeContents()).
555  *
556  * @param[in] key       The key to use for the decryption.
557  * @param[in] usage     The key usage used for the encryption.
558  * @param[in] in        The encrypted message.
559  * @param[out] out      The decrypted message.
560  * @return rxgk error codes.
561  */
562 afs_int32
563 rxgk_decrypt_in_key(rxgk_key key, afs_int32 usage, RXGK_Data *in,
564                     RXGK_Data *out)
565 {
566     krb5_context ctx = NULL;
567     krb5_crypto crypto = NULL;
568     krb5_data kd_out;
569     krb5_enctype enctype;
570     krb5_error_code ret;
571     struct rxgk_keyblock *keyblock = key2keyblock(key);
572
573     memset(out, 0, sizeof(*out));
574     memset(&kd_out, 0, sizeof(kd_out));
575
576     ret = krb5_init_context(&ctx);
577     if (ret != 0)
578         goto done;
579
580     enctype = deref_keyblock_enctype(&keyblock->key);
581     ret = krb5_crypto_init(ctx, &keyblock->key, enctype, &crypto);
582     if (ret != 0)
583         goto done;
584     ret = krb5_decrypt(ctx, crypto, usage, in->val, in->len, &kd_out);
585     if (ret != 0) {
586         ret = RXGK_SEALED_INCON;
587         goto done;
588     }
589     ret = rx_opaque_populate(out, kd_out.data, kd_out.length);
590
591  done:
592     if (crypto != NULL)
593         krb5_crypto_destroy(ctx, crypto);
594     krb5_data_free(&kd_out);
595     if (ctx != NULL) {
596         krb5_free_context(ctx);
597     }
598     return ktor(ret);
599 }
600
601 /*
602  * Helper for derive_tk.
603  * Assumes the caller has already allocated space in 'out'.
604  */
605 static afs_int32
606 PRFplus(krb5_data *out, krb5_enctype enctype, rxgk_key k0,
607         void *seed, size_t seed_len)
608 {
609     krb5_context ctx = NULL;
610     krb5_crypto crypto = NULL;
611     krb5_data prf_in, prf_out;
612     krb5_error_code ret;
613     struct rxgk_keyblock *keyblock = key2keyblock(k0);
614     unsigned char *pre_key = NULL;
615     size_t block_len;
616     size_t desired_len = out->length;
617     afs_uint32 n_iter, iterations, dummy;
618
619     memset(&prf_in, 0, sizeof(prf_in));
620     memset(&prf_out, 0, sizeof(prf_out));
621
622     ret = krb5_init_context(&ctx);
623     if (ret != 0)
624         goto done;
625
626     ret = krb5_crypto_init(ctx, &keyblock->key, enctype, &crypto);
627     if (ret != 0)
628         goto done;
629     prf_in.length = sizeof(n_iter) + seed_len;
630     prf_in.data = rxi_Alloc(prf_in.length);
631     if (prf_in.data == NULL) {
632         ret = RXGK_INCONSISTENCY;
633         goto done;
634     }
635     memcpy((unsigned char *)prf_in.data + sizeof(n_iter), seed, seed_len);
636     ret = krb5_crypto_prf_length(ctx, enctype, &block_len);
637     if (ret != 0)
638         goto done;
639     /* We need desired_len/block_len iterations, rounded up. */
640     iterations = (desired_len + block_len - 1) / block_len;
641     pre_key = rxi_Alloc(iterations * block_len);
642     if (pre_key == NULL) {
643         ret = RXGK_INCONSISTENCY;
644         goto done;
645     }
646
647     for (n_iter = 1; n_iter <= iterations; ++n_iter) {
648         dummy = htonl(n_iter);
649         memcpy(prf_in.data, &dummy, sizeof(dummy));
650         krb5_data_free(&prf_out);
651         ret = krb5_crypto_prf(ctx, crypto, &prf_in, &prf_out);
652         if (ret != 0)
653             goto done;
654         memcpy(pre_key + (n_iter - 1) * block_len, prf_out.data, block_len);
655     }
656     memcpy(out->data, pre_key, desired_len);
657     out->length = desired_len;
658
659  done:
660     if (crypto != NULL)
661         krb5_crypto_destroy(ctx, crypto);
662     krb5_data_free(&prf_out);
663     if (ctx != NULL) {
664         krb5_free_context(ctx);
665     }
666     rxi_Free(prf_in.data, prf_in.length);
667     if (pre_key != NULL)
668         rxi_Free(pre_key, iterations * block_len);
669     return ktor(ret);
670 }
671
672 struct seed_data {
673     afs_uint32 epoch;
674     afs_uint32 cid;
675     afs_uint32 time_hi;
676     afs_uint32 time_lo;
677     afs_uint32 key_number;
678 } __attribute__((packed));
679
680 /* Our seed_data buffer that we feed into the PRF+ algorithm to generate our
681  * transport key had better be exactly 20 bytes large, to match the format of
682  * the seed data in the rxgk spec. */
683 #define RXGK_SEED_DATA_SIZE 20
684
685 /**
686  * Compute a transport key tk given a master key k0
687  *
688  * Given a connection master key k0, derive a transport key tk from the master
689  * key and connection parameters.
690  *
691  * TK = random-to-key(PRF+(K0, L, epoch || cid || start_time || key_number))
692  * using the RFC4402 PRF+, i.e., the ordinal of the application of the
693  * pseudo-random() function is stored in a 32-bit field, not an 8-bit field
694  * as in RFC6112.
695  *
696  * @param[out] tk               The derived transport key.
697  * @param[in] k0                The token master key.
698  * @param[in] epoch             The rx epoch of the connection.
699  * @param[in] cid               The rx connection id of the connection.
700  * @param[in] start_time        The start_time of the connection.
701  * @param[in] key_number        The current key number of the connection.
702  * @return rxgk error codes.
703  */
704 afs_int32
705 rxgk_derive_tk(rxgk_key *tk, rxgk_key k0, afs_uint32 epoch, afs_uint32 cid,
706                rxgkTime start_time, afs_uint32 key_number)
707 {
708     krb5_enctype enctype;
709     krb5_data pre_key;
710     struct rxgk_keyblock *keyblock = key2keyblock(k0);
711     struct seed_data seed;
712     ssize_t ell;
713     afs_int32 ret;
714
715     memset(&pre_key, 0, sizeof(pre_key));
716     memset(&seed, 0, sizeof(seed));
717
718     opr_StaticAssert(sizeof(seed) == RXGK_SEED_DATA_SIZE);
719     enctype = deref_keyblock_enctype(&keyblock->key);
720     ell = rxgk_etype_to_len(enctype);
721     if (ell < 0)
722         return RXGK_INCONSISTENCY;
723
724     seed.epoch = htonl(epoch);
725     seed.cid = htonl(cid);
726     seed.time_hi = htonl((afs_int32)(start_time / ((afs_int64)1 << 32)));
727     seed.time_lo = htonl((afs_uint32)(start_time & (afs_uint64)0xffffffffu));
728     seed.key_number = htonl(key_number);
729
730     pre_key.data = rxi_Alloc(ell);
731     if (pre_key.data == NULL) {
732         ret = RXGK_INCONSISTENCY;
733         goto done;
734     }
735     pre_key.length = ell;
736     ret = PRFplus(&pre_key, enctype, k0, &seed, sizeof(seed));
737     if (ret != 0)
738         goto done;
739
740     ret = rxgk_make_key(tk, pre_key.data, ell, enctype);
741     if (ret != 0)
742         goto done;
743
744  done:
745     rxi_Free(pre_key.data, ell);
746     return ret;
747 }
748
749 /**
750  * Determine the maximum ciphertext expansion for a given enctype.
751  *
752  * @param[in] k0        The rxgk key to be used.
753  * @param[out] len_out  The maximum ciphertext expansion, in octets.
754  * @return rxgk error codes.
755  */
756 afs_int32
757 rxgk_cipher_expansion(rxgk_key k0, afs_uint32 *len_out)
758 {
759     krb5_context ctx = NULL;
760     krb5_crypto crypto = NULL;
761     krb5_enctype enctype;
762     krb5_error_code ret;
763     struct rxgk_keyblock *keyblock = key2keyblock(k0);
764     size_t len;
765
766     *len_out = 0;
767
768     enctype = deref_keyblock_enctype(&keyblock->key);
769     ret = krb5_init_context(&ctx);
770     if (ret != 0)
771         goto done;
772     ret = krb5_crypto_init(ctx, &keyblock->key, enctype, &crypto);
773     if (ret != 0)
774         goto done;
775     len = krb5_crypto_overhead(ctx, crypto);
776     *len_out = len;
777
778  done:
779     if (crypto != NULL)
780         krb5_crypto_destroy(ctx, crypto);
781     if (ctx != NULL) {
782         krb5_free_context(ctx);
783     }
784     return ktor(ret);
785 }
786
787 /**
788  * Allocate and fill the buffer in nonce with len bytes of random data.
789  *
790  * @param[out] nonce    The buffer of random data.
791  * @param[in] len       The number of octets of random data to produce.
792  * @return rx error codes.
793  */
794 afs_int32
795 rxgk_nonce(RXGK_Data *nonce, afs_uint32 len)
796 {
797     if (rx_opaque_alloc(nonce, len) != 0)
798         return RXGK_INCONSISTENCY;
799
800     krb5_generate_random_block(nonce->val, len);
801     return 0;
802 }