3 * Key loading and storing
5 * (c) 2001 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Trivial IP Encryption (TrIPE).
12 * TrIPE is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
30 /*----- Algswitch stuff ---------------------------------------------------*/
32 /* --- @algs_get@ --- *
34 * Arguments: @algswitch *a@ = where to put the algorithms
35 * @dstr *e@ = where to write error tokens
36 * @key_file *kf@ = key file
37 * @key *k@ = key to inspect
39 * Returns: Zero if OK; nonzero on error.
41 * Use: Extracts an algorithm choice from a key.
44 static int algs_get(algswitch
*a
, dstr
*e
, key_file
*kf
, key
*k
)
48 dstr d
= DSTR_INIT
, dd
= DSTR_INIT
;
51 /* --- Hash function --- */
53 if ((p
= key_getattr(kf
, k
, "hash")) == 0) p
= "rmd160";
54 if ((a
->h
= ghash_byname(p
)) == 0) {
55 a_format(e
, "unknown-hash", "%s", p
, A_END
);
59 /* --- Symmetric encryption for key derivation --- */
61 if ((p
= key_getattr(kf
, k
, "mgf")) == 0) {
63 dstr_putf(&d
, "%s-mgf", a
->h
->name
);
66 if ((a
->mgf
= gcipher_byname(p
)) == 0) {
67 a_format(e
, "unknown-mgf-cipher", "%s", p
, A_END
);
71 /* --- Bulk crypto transform --- */
73 if ((p
= key_getattr(kf
, k
, "bulk")) == 0) p
= "v0";
74 for (bops
= bulktab
; bops
->name
&& strcmp(p
, bops
->name
) != 0; bops
++);
76 a_format(e
, "unknown-bulk-transform", "%s", p
, A_END
);
79 if ((a
->bulk
= bops
->getalgs(a
, e
, kf
, k
)) == 0) goto done
;
82 /* --- All done --- */
91 /* --- @algs_check@ --- *
93 * Arguments: @algswitch *a@ = a choice of algorithms
94 * @dstr *e@ = where to write error tokens
95 * @const dhgrp *grp@ = the group we're working in
97 * Returns: Zero if OK; nonzero on error.
99 * Use: Checks an algorithm choice for sensibleness. This also
100 * derives some useful information from the choices, and you
101 * must call this before committing the algorithm selection
102 * for use by @keyset@ functions.
105 static int algs_check(algswitch
*a
, dstr
*e
, const dhgrp
*grp
)
107 a
->hashsz
= a
->h
->hashsz
;
109 if (keysz(a
->hashsz
, a
->mgf
->keysz
) != a
->hashsz
) {
110 a_format(e
, "mgf", "%s", a
->mgf
->name
,
111 "restrictive-key-schedule",
116 if (a
->bulk
->ops
->checkalgs(a
->bulk
, a
, e
)) return (-1);
121 /* --- @km_samealgsp@ --- *
123 * Arguments: @const kdata *kdx, *kdy@ = two key data objects
125 * Returns: Nonzero if their two algorithm selections are the same.
127 * Use: Checks sameness of algorithm selections: used to ensure that
128 * peers are using sensible algorithms.
131 int km_samealgsp(const kdata
*kdx
, const kdata
*kdy
)
133 const algswitch
*a
= &kdx
->algs
, *aa
= &kdy
->algs
;
135 return (kdx
->grp
->ops
== kdy
->grp
->ops
&&
136 kdx
->grp
->ops
->samegrpp(kdx
->grp
, kdy
->grp
) &&
137 a
->mgf
== aa
->mgf
&& a
->h
== aa
->h
&&
138 a
->bulk
->ops
== aa
->bulk
->ops
&&
139 a
->bulk
->ops
->samealgsp(a
->bulk
, aa
->bulk
));
142 /*----- Key data and key nodes --------------------------------------------*/
144 typedef struct keyhalf
{
146 int (*load
)(key_file
*, key
*, key_data
*,
147 const dhops
*, kdata
*, dstr
*, dstr
*);
154 /* --- @kh_loadpub@, @kh_loadpriv@ --- *
156 * Arguments: @const dhops *dh@ = Diffie--Hellman operations for key type
157 * @key_file *kf@ = key file from which the key was loaded
158 * @key *k@ = the key object we're loading
159 * @key_data *d@ = the key data to load
160 * @kdata *kd@ = our key-data object to fill in
161 * @dstr *t@ = the key tag name
162 * @dstr *e@ = a string to write error tokens to
164 * Returns: Zero on success, @-1@ on error.
166 * Use: These functions handle the main difference between public and
167 * private key halves. They are responsible for setting @grp@,
168 * @k@ and @K@ appropriately in all keys, handling the mismatch
169 * between the largely half-indifferent calling code and the
170 * group-specific loading functions.
172 * The function @kh_loadpriv@ is also responsible for checking
173 * the group for goodness. We don't bother checking public
174 * keys, because each public key we actually end up using must
175 * share a group with a private key which we'll already have
179 static int kh_loadpub(key_file
*kf
, key
*k
, key_data
*d
,
180 const dhops
*dh
, kdata
*kd
, dstr
*t
, dstr
*e
)
184 if ((rc
= dh
->ldpub(kf
, k
, d
, kd
, t
, e
)) != 0)
187 if (kd
->grp
->ops
->checkge(kd
->grp
, kd
->K
)) {
188 a_format(e
, "bad-public-group-element", A_END
);
194 kd
->grp
->ops
->freege(kd
->grp
, kd
->K
);
195 kd
->grp
->ops
->freegrp(kd
->grp
);
200 static int kh_loadpriv(key_file
*kf
, key
*k
, key_data
*d
,
201 const dhops
*dh
, kdata
*kd
, dstr
*t
, dstr
*e
)
208 if ((rc
= dh
->ldpriv(kf
, k
, d
, kd
, t
, e
)) != 0)
211 if ((err
= kd
->grp
->ops
->checkgrp(kd
->grp
)) != 0) {
212 a_format(e
, "bad-group", "%s", err
, A_END
);
215 K
= kd
->grp
->ops
->mul(kd
->grp
, kd
->k
, 0);
216 ok
= kd
->grp
->ops
->eq(kd
->grp
, kd
->K
, K
);
217 kd
->grp
->ops
->freege(kd
->grp
, K
);
219 a_format(e
, "incorrect-public-key", A_END
);
225 kd
->grp
->ops
->freesc(kd
->grp
, kd
->k
);
226 kd
->grp
->ops
->freege(kd
->grp
, kd
->K
);
227 kd
->grp
->ops
->freegrp(kd
->grp
);
232 static struct keyhalf
233 priv
= { "private", kh_loadpriv
},
234 pub
= { "public", kh_loadpub
};
236 /* --- @keymoan@ --- *
238 * Arguments: @const char *file@ = name of the file
239 * @int line@ = line number in file
240 * @const char *msg@ = error message
241 * @void *p@ = argument pointer (indicates which keyring)
245 * Use: Reports an error message about loading a key file.
248 static void keymoan(const char *file
, int line
, const char *msg
, void *p
)
253 a_warn("KEYMGMT", "%s-keyring", kh
->kind
, "%s", file
,
254 "io-error", "?ERRNO", A_END
);
256 a_warn("KEYMGMT", "%s-keyring", kh
->kind
, "%s", file
, "line", "%d", line
,
261 /* --- @kh_reopen@ --- *
263 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure
265 * Returns: Zero on success, @-1@ on error.
267 * Use: Reopens the key file for the appropriate key half. If this
268 * fails, everything is left as it was; if it succeeds, then the
269 * old file is closed (if it was non-null) and the new one put
273 static int kh_reopen(keyhalf
*kh
)
275 key_file
*kf
= CREATE(key_file
);
277 if (key_open(kf
, kh
->kr
, KOPEN_READ
, keymoan
, kh
)) {
289 /* --- @kh_init@ --- *
291 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure to set up
292 * @const char *kr@ = name of the keyring file
294 * Returns: Zero on success, @-1@ on error.
296 * Use: Initialize a keyhalf structure, maintaining the private or
297 * public keys. Intended to be called during initialization:
298 * exits if there's some kind of problem.
301 static int kh_init(keyhalf
*kh
, const char *kr
)
303 if (kh
->kf
) return (0);
304 kh
->kr
= xstrdup(kr
);
305 if (kh_reopen(kh
)) return (-1);
306 fwatch_init(&kh
->w
, kr
);
307 sym_create(&kh
->tab
);
311 /* --- @kh_load@ --- *
313 * Arguments: @keyhalf *kh@ = pointer to keyhalf
314 * @const char *tag@ = key tag to be loaded
315 * @int complainp@ = whether to complain about missing keys
317 * Returns: Pointer to a @kdata@ structure if successful, or null on
320 * Use: Attempts to load a key from the current key file. This
321 * function always reads data from the file: it's used when
322 * there's a cache miss from @kh_find@, and when refreshing the
323 * known keys in @kh_refresh@. The returned kdata has a
324 * reference count of exactly 1, and has no home knode.
327 static kdata
*kh_load(keyhalf
*kh
, const char *tag
, int complainp
)
338 /* --- Find the key and grab its tag --- */
340 if (key_qtag(kh
->kf
, tag
, &t
, &k
, &d
)) {
342 a_warn("KEYMGMT", "%s-keyring", kh
->kind
, "%s", kh
->kr
,
343 "key-not-found", "%s", tag
, A_END
);
348 /* --- Find the key's group type and the appropriate operations --- *
350 * There are several places to look for the key type. The most obvious is
351 * the `kx-group' key attribute. But there's also the key type itself, for
352 * compatibility reasons.
355 ty
= key_getattr(kh
->kf
, k
, "kx-group");
356 if (!ty
&& strncmp(k
->type
, "tripe-", 6) == 0) ty
= k
->type
+ 6;
359 for (dh
= dhtab
; dh
->name
; dh
++)
360 if (strcmp(dh
->name
, ty
) == 0) goto founddh
;
361 a_warn("KEYMGMT", "%s-keyring", kh
->kind
,
362 "%s", kh
->kr
, "key", "%s", t
.buf
,
363 "unknown-group-type", "%s", ty
, A_END
);
368 if (kh
->load(kh
->kf
, k
, *d
, dh
, kd
, &t
, &e
)) {
369 a_warn("KEYMGMT", "%s-keyring", kh
->kind
,
370 "%s", kh
->kr
, "key", "%s", t
.buf
,
371 "*%s", e
.buf
, A_END
);
375 if (algs_get(&kd
->algs
, &e
, kh
->kf
, k
) ||
376 algs_check(&kd
->algs
, &e
, kd
->grp
)) {
377 a_warn("KEYMGMT", "%s-keyring", kh
->kind
,
378 "%s", kh
->kr
, "key", "%s", t
.buf
,
379 "*%s", e
.buf
, A_END
);
383 kd
->tag
= xstrdup(t
.buf
);
389 IF_TRACING(T_KEYMGMT
, {
390 trace(T_KEYMGMT
, "keymgmt: loaded %s key `%s'", kh
->kind
, t
.buf
);
391 IF_TRACING(T_CRYPTO
, {
395 trace(T_CRYPTO
, "crypto: k = %s", g
->ops
->scstr(g
, kd
->k
));
396 trace(T_CRYPTO
, "crypto: K = %s", g
->ops
->gestr(g
, kd
->K
));
397 trace(T_CRYPTO
, "crypto: bulk transform = %s",
398 kd
->algs
.bulk
->ops
->name
);
399 kd
->algs
.bulk
->ops
->tracealgs(kd
->algs
.bulk
);
406 if (kd
->k
) kd
->grp
->ops
->freesc(kd
->grp
, kd
->k
);
407 kd
->grp
->ops
->freege(kd
->grp
, kd
->K
);
408 kd
->grp
->ops
->freegrp(kd
->grp
);
419 /* --- @kh_find@ --- *
421 * Arguments: @keyhalf *kh@ = pointer to the keyhalf
422 * @const char *tag@ = key to be obtained
423 * @int complainp@ = whether to complain about missing keys
425 * Returns: A pointer to the kdata, or null on error.
427 * Use: Obtains kdata, maybe from the cache. This won't update a
428 * stale cache entry, though @kh_refresh@ ought to have done
429 * that already. The returned kdata object may be shared with
430 * other users. (One of this function's responsibilities, over
431 * @kh_load@, is to set the home knode of a freshly loaded
435 static kdata
*kh_find(keyhalf
*kh
, const char *tag
, int complainp
)
441 kn
= sym_find(&kh
->tab
, tag
, -1, sizeof(knode
), &f
);
444 if (kn
->f
& KNF_BROKEN
) {
446 trace(T_KEYMGMT
, "keymgmt: key `%s' marked as broken", tag
); )
452 T( trace(T_KEYMGMT
, "keymgmt: %scache hit for key `%s'",
453 kd ?
"" : "negative ", tag
); )
456 kd
= kh_load(kh
, tag
, complainp
);
470 /* --- @kh_refresh@ --- *
472 * Arguments: @keyhalf *kh@ = pointer to the keyhalf
474 * Returns: Zero if nothing needs to be done; nonzero if peers should
475 * refresh their keys.
477 * Use: Refreshes cached keys from files.
479 * Each active knode is examined to see if a new key is
480 * available: the return value is nonzero if any new keys are.
481 * A key is considered new if its algorithms, public key, or
482 * expiry time are/is different.
484 * Stub knodes (with no kdata attached) are removed, so that a
485 * later retry can succeed if the file has been fixed. (This
486 * doesn't count as a change, since no peers should be relying
487 * on a nonexistent key.)
490 static int kh_refresh(keyhalf
*kh
)
497 if (!fwatch_update(&kh
->w
, kh
->kr
) || kh_reopen(kh
))
500 T( trace(T_KEYMGMT
, "keymgmt: rescan %s keyring `%s'", kh
->kind
, kh
->kr
); )
501 for (sym_mkiter(&i
, &kh
->tab
); (kn
= sym_next(&i
)) != 0; ) {
503 T( trace(T_KEYMGMT
, "keymgmt: discard stub entry for key `%s'",
505 sym_remove(&kh
->tab
, kn
);
508 if ((kd
= kh_load(kh
, SYM_NAME(kn
), 1)) == 0) {
509 if (!(kn
->f
& KNF_BROKEN
)) {
510 T( trace(T_KEYMGMT
, "keymgmt: failed to load new key `%s': "
511 "marking it as broken",
517 kn
->f
&= ~KNF_BROKEN
;
518 if (kd
->t_exp
== kn
->kd
->t_exp
&&
519 km_samealgsp(kd
, kn
->kd
) &&
520 kd
->grp
->ops
->eq(kd
->grp
, kd
->K
, kn
->kd
->K
)) {
521 T( trace(T_KEYMGMT
, "keymgmt: key `%s' unchanged", SYM_NAME(kn
)); )
524 T( trace(T_KEYMGMT
, "keymgmt: loaded new version of key `%s'",
535 /* --- @kh_clear@ --- *
537 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure
541 * Use: Clears out the keyhalf's keyring and flushes the cache.
544 static void kh_clear(keyhalf
*kh
)
550 for (sym_mkiter(&i
, &kh
->tab
); (kn
= sym_next(&i
)) != 0; )
551 if (kn
->kd
) km_unref(kn
->kd
);
552 sym_destroy(&kh
->tab
);
558 /*----- Main code ---------------------------------------------------------*/
563 /* --- @km_init@ --- *
565 * Arguments: @const char *privkr@ = private keyring file
566 * @const char *pubkr@ = public keyring file
567 * @const char *ptag@ = default private-key tag
569 * Returns: Zero on success, @-1@ on failure.
571 * Use: Initializes the key-management machinery, loading the
572 * keyrings and so on.
575 int km_init(const char *privkr
, const char *pubkr
, const char *ptag
)
577 const gchash
*const *hh
;
580 for (hh
= ghashtab
; *hh
; hh
++) {
581 if ((*hh
)->hashsz
> MAXHASHSZ
) {
582 a_warn("ABORT", "hash-size-too-large", "hash",
583 "%s", (*hh
)->name
, "size", "%lu", (*hh
)->hashsz
,
584 "limit", "%d", MAXHASHSZ
, A_END
);
589 if (kh_init(&priv
, privkr
) || kh_init(&pub
, pubkr
))
592 tag_priv
= ptag ?
xstrdup(ptag
) : 0;
595 if ((kd
= km_findpriv(tag_priv
)) == 0) return (-1);
596 if (master
) km_unref(master
);
602 /* --- @km_reload@ --- *
606 * Returns: Zero if OK, nonzero to force reloading of keys.
608 * Use: Checks the keyrings to see if they need reloading.
616 if (kh_refresh(&priv
)) {
625 if (kh_refresh(&pub
))
630 /* --- @km_clear@ --- *
636 * Use: Forget the currently loaded keyrings. The @master@ key will
637 * be cleared, but other keys already loaded will continue to
638 * exist until their reference count drops to zero. Call
639 * @km_init@ to make everything work again.
646 if (master
) { km_unref(master
); master
= 0; }
647 if (tag_priv
) { xfree(tag_priv
); tag_priv
= 0; }
650 /* --- @km_findpub@, @km_findpriv@ --- *
652 * Arguments: @const char *tag@ = key tag to load
654 * Returns: Pointer to the kdata object if successful, or null on error.
656 * Use: Fetches a public or private key from the keyring.
659 kdata
*km_findpub(const char *tag
) { return (kh_find(&pub
, tag
, 1)); }
661 kdata
*km_findpriv(const char *tag
)
665 /* Unpleasantness for the sake of compatibility. */
666 if (!tag
&& (kd
= kh_find(&priv
, "tripe", 0)) != 0) return (kd
);
667 else return (kh_find(&priv
, tag ? tag
: "tripe-dh", 1));
670 /* --- @km_findpubbyid@, @km_findprivbyid@ --- *
672 * Arguments: @uint32 id@ = key id to load
674 * Returns: Pointer to the kdata object if successful, or null on error.
676 * Use: Fetches a public or private key from the keyring given its
680 static kdata
*findbyid(keyhalf
*kh
, uint32 id
)
685 k
= key_byid(kh
->kf
, id
); if (!k
) goto notfound
;
686 kd
= kh_find(kh
, k
->tag
, 1); if (!kd
) goto notfound
;
687 if (kd
->id
!= id
) { km_unref(kd
); goto notfound
; }
691 a_warn("KX", "%s-keyring", kh
->kind
, "%s", kh
->kr
,
692 "unknown-key-id", "0x%08lx", (unsigned long)id
,
697 kdata
*km_findpubbyid(uint32 id
) { return (findbyid(&pub
, id
)); }
699 kdata
*km_findprivbyid(uint32 id
)
701 if (id
== master
->id
) { km_ref(master
); return (master
); }
702 else return findbyid(&priv
, id
);
705 /* --- @km_tag@ --- *
707 * Arguments: @kdata *kd@ - pointer to the kdata object
709 * Returns: A pointer to the short tag by which the kdata was loaded.
712 const char *km_tag(kdata
*kd
) { return (SYM_NAME(kd
->kn
)); }
714 /* --- @km_ref@ --- *
716 * Arguments: @kdata *kd@ = pointer to the kdata object
720 * Use: Claim a new reference to a kdata object.
723 void km_ref(kdata
*kd
) { kd
->ref
++; }
725 /* --- @km_unref@ --- *
727 * Arguments: @kdata *kd@ = pointer to the kdata object
731 * Use: Releases a reference to a kdata object.
734 void km_unref(kdata
*kd
)
736 if (--kd
->ref
) return;
737 if (kd
->k
) kd
->grp
->ops
->freesc(kd
->grp
, kd
->k
);
738 kd
->grp
->ops
->freege(kd
->grp
, kd
->K
);
739 kd
->grp
->ops
->freegrp(kd
->grp
);
744 /*----- That's all, folks -------------------------------------------------*/