1 /* Copyright (c) 2014-2016, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
7 * \brief Functions and structures for associating routers' RSA key
8 * fingerprints with their ED25519 keys.
11 #define KEYPIN_PRIVATE
16 #include "crypto_format.h"
24 #include "util_format.h"
39 * @brief Key-pinning for RSA and Ed25519 identity keys at directory
42 * Many older clients, and many internal interfaces, still refer to relays by
43 * their RSA1024 identity keys. We can make this more secure, however:
44 * authorities use this module to track which RSA keys have been used along
45 * with which Ed25519 keys, and force such associations to be permanent.
47 * This module implements a key-pinning mechanism to ensure that it's safe
48 * to use RSA keys as identitifers even as we migrate to Ed25519 keys. It
49 * remembers, for every Ed25519 key we've seen, what the associated Ed25519
50 * key is. This way, if we see a different Ed25519 key with that RSA key,
51 * we'll know that there's a mismatch.
53 * (As of this writing, these key associations are advisory only, mostly
54 * because some relay operators kept mishandling their Ed25519 keys during
55 * the initial Ed25519 rollout. We should fix this problem, and then toggle
56 * the AuthDirPinKeys option.)
58 * We persist these entries to disk using a simple format, where each line
59 * has a base64-encoded RSA SHA1 hash, then a base64-endoded Ed25519 key.
60 * Empty lines, misformed lines, and lines beginning with # are
61 * ignored. Lines beginning with @ are reserved for future extensions.
63 * The dirserv.c module is the main user of these functions.
66 static int keypin_journal_append_entry(const uint8_t *rsa_id_digest
,
67 const uint8_t *ed25519_id_key
);
68 static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest
,
69 const uint8_t *ed25519_id_key
,
72 static int keypin_add_or_replace_entry_in_map(keypin_ent_t
*ent
);
74 static HT_HEAD(rsamap
, keypin_ent_st
) the_rsa_map
= HT_INITIALIZER();
75 static HT_HEAD(edmap
, keypin_ent_st
) the_ed_map
= HT_INITIALIZER();
77 /** Hashtable helper: compare two keypin table entries and return true iff
78 * they have the same RSA key IDs. */
80 keypin_ents_eq_rsa(const keypin_ent_t
*a
, const keypin_ent_t
*b
)
82 return tor_memeq(a
->rsa_id
, b
->rsa_id
, sizeof(a
->rsa_id
));
85 /** Hashtable helper: hash a keypin table entries based on its RSA key ID */
86 static inline unsigned
87 keypin_ent_hash_rsa(const keypin_ent_t
*a
)
89 return (unsigned) siphash24g(a
->rsa_id
, sizeof(a
->rsa_id
));
92 /** Hashtable helper: compare two keypin table entries and return true iff
93 * they have the same ed25519 keys */
95 keypin_ents_eq_ed(const keypin_ent_t
*a
, const keypin_ent_t
*b
)
97 return tor_memeq(a
->ed25519_key
, b
->ed25519_key
, sizeof(a
->ed25519_key
));
100 /** Hashtable helper: hash a keypin table entries based on its ed25519 key */
101 static inline unsigned
102 keypin_ent_hash_ed(const keypin_ent_t
*a
)
104 return (unsigned) siphash24g(a
->ed25519_key
, sizeof(a
->ed25519_key
));
107 HT_PROTOTYPE(rsamap
, keypin_ent_st
, rsamap_node
, keypin_ent_hash_rsa
,
109 HT_GENERATE2(rsamap
, keypin_ent_st
, rsamap_node
, keypin_ent_hash_rsa
,
110 keypin_ents_eq_rsa
, 0.6, tor_reallocarray
, tor_free_
)
112 HT_PROTOTYPE(edmap
, keypin_ent_st
, edmap_node
, keypin_ent_hash_ed
,
114 HT_GENERATE2(edmap
, keypin_ent_st
, edmap_node
, keypin_ent_hash_ed
,
115 keypin_ents_eq_ed
, 0.6, tor_reallocarray
, tor_free_
)
118 * Check whether we already have an entry in the key pinning table for a
119 * router with RSA ID digest <b>rsa_id_digest</b> or for ed25519 key
120 * <b>ed25519_id_key</b>. If we have an entry that matches both keys,
121 * return KEYPIN_FOUND. If we find an entry that matches one key but
122 * not the other, return KEYPIN_MISMATCH. If we have no entry for either
123 * key, add such an entry to the table and return KEYPIN_ADDED.
125 * If <b>replace_existing_entry</b> is true, then any time we would have said
126 * KEYPIN_FOUND, we instead add this entry anyway and return KEYPIN_ADDED.
129 keypin_check_and_add(const uint8_t *rsa_id_digest
,
130 const uint8_t *ed25519_id_key
,
131 const int replace_existing_entry
)
133 return keypin_check_and_add_impl(rsa_id_digest
, ed25519_id_key
, 0,
134 replace_existing_entry
);
138 * As keypin_check_and_add, but do not add. Return KEYPIN_NOT_FOUND if
142 keypin_check(const uint8_t *rsa_id_digest
,
143 const uint8_t *ed25519_id_key
)
145 return keypin_check_and_add_impl(rsa_id_digest
, ed25519_id_key
, 1, 0);
149 * Helper: implements keypin_check and keypin_check_and_add.
152 keypin_check_and_add_impl(const uint8_t *rsa_id_digest
,
153 const uint8_t *ed25519_id_key
,
154 const int do_not_add
,
157 keypin_ent_t search
, *ent
;
158 memset(&search
, 0, sizeof(search
));
159 memcpy(search
.rsa_id
, rsa_id_digest
, sizeof(search
.rsa_id
));
160 memcpy(search
.ed25519_key
, ed25519_id_key
, sizeof(search
.ed25519_key
));
162 /* Search by RSA key digest first */
163 ent
= HT_FIND(rsamap
, &the_rsa_map
, &search
);
165 tor_assert(fast_memeq(ent
->rsa_id
, rsa_id_digest
, sizeof(ent
->rsa_id
)));
166 if (tor_memeq(ent
->ed25519_key
, ed25519_id_key
,sizeof(ent
->ed25519_key
))) {
167 return KEYPIN_FOUND
; /* Match on both keys. Great. */
170 return KEYPIN_MISMATCH
; /* Found RSA with different Ed key */
174 /* See if we know a different RSA key for this ed key */
176 ent
= HT_FIND(edmap
, &the_ed_map
, &search
);
178 /* If we got here, then the ed key matches and the RSA doesn't */
179 tor_assert(fast_memeq(ent
->ed25519_key
, ed25519_id_key
,
180 sizeof(ent
->ed25519_key
)));
181 tor_assert(fast_memneq(ent
->rsa_id
, rsa_id_digest
, sizeof(ent
->rsa_id
)));
182 return KEYPIN_MISMATCH
;
186 /* Okay, this one is new to us. */
188 return KEYPIN_NOT_FOUND
;
190 ent
= tor_memdup(&search
, sizeof(search
));
191 int r
= keypin_add_or_replace_entry_in_map(ent
);
197 keypin_journal_append_entry(rsa_id_digest
, ed25519_id_key
);
202 * Helper: add <b>ent</b> to the hash tables.
204 MOCK_IMPL(STATIC
void,
205 keypin_add_entry_to_map
, (keypin_ent_t
*ent
))
207 HT_INSERT(rsamap
, &the_rsa_map
, ent
);
208 HT_INSERT(edmap
, &the_ed_map
, ent
);
212 * Helper: add 'ent' to the maps, replacing any entries that contradict it.
213 * Take ownership of 'ent', freeing it if needed.
215 * Return 0 if the entry was a duplicate, -1 if there was a conflict,
216 * and 1 if there was no conflict.
219 keypin_add_or_replace_entry_in_map(keypin_ent_t
*ent
)
222 keypin_ent_t
*ent2
= HT_FIND(rsamap
, &the_rsa_map
, ent
);
223 keypin_ent_t
*ent3
= HT_FIND(edmap
, &the_ed_map
, ent
);
225 fast_memeq(ent2
->ed25519_key
, ent
->ed25519_key
, DIGEST256_LEN
)) {
226 /* We already have this mapping stored. Ignore it. */
229 } else if (ent2
|| ent3
) {
230 /* We have a conflict. (If we had no entry, we would have ent2 == ent3
231 * == NULL. If we had a non-conflicting duplicate, we would have found
234 * We respond by having this entry (ent) supersede all entries that it
235 * contradicts (ent2 and/or ent3). In other words, if we receive
236 * <rsa,ed>, we remove all <rsa,ed'> and all <rsa',ed>, for rsa'!=rsa
239 const keypin_ent_t
*t
;
241 t
= HT_REMOVE(rsamap
, &the_rsa_map
, ent2
);
242 tor_assert(ent2
== t
);
243 t
= HT_REMOVE(edmap
, &the_ed_map
, ent2
);
244 tor_assert(ent2
== t
);
246 if (ent3
&& ent2
!= ent3
) {
247 t
= HT_REMOVE(rsamap
, &the_rsa_map
, ent3
);
248 tor_assert(ent3
== t
);
249 t
= HT_REMOVE(edmap
, &the_ed_map
, ent3
);
250 tor_assert(ent3
== t
);
258 keypin_add_entry_to_map(ent
);
263 * Check whether we already have an entry in the key pinning table for a
264 * router with RSA ID digest <b>rsa_id_digest</b>. If we have no such entry,
265 * return KEYPIN_NOT_FOUND. If we find an entry that matches the RSA key but
266 * which has an ed25519 key, return KEYPIN_MISMATCH.
269 keypin_check_lone_rsa(const uint8_t *rsa_id_digest
)
271 keypin_ent_t search
, *ent
;
272 memset(&search
, 0, sizeof(search
));
273 memcpy(search
.rsa_id
, rsa_id_digest
, sizeof(search
.rsa_id
));
275 /* Search by RSA key digest first */
276 ent
= HT_FIND(rsamap
, &the_rsa_map
, &search
);
278 return KEYPIN_MISMATCH
;
280 return KEYPIN_NOT_FOUND
;
284 /** Open fd to the keypinning journal file. */
285 static int keypin_journal_fd
= -1;
287 /** Open the key-pinning journal to append to <b>fname</b>. Return 0 on
288 * success, -1 on failure. */
290 keypin_open_journal(const char *fname
)
293 int fd
= tor_open_cloexec(fname
, O_WRONLY
|O_CREAT
|O_BINARY
, 0600);
297 if (tor_fd_seekend(fd
) < 0)
300 /* Add a newline in case the last line was only partially written */
301 if (write(fd
, "\n", 1) < 1)
304 /* Add something about when we opened this file. */
306 char tbuf
[ISO_TIME_LEN
+1];
307 format_iso_time(tbuf
, approx_time());
308 tor_snprintf(buf
, sizeof(buf
), "@opened-at %s\n", tbuf
);
309 if (write_all(fd
, buf
, strlen(buf
), 0) < 0)
312 keypin_journal_fd
= fd
;
320 /** Close the keypinning journal file. */
322 keypin_close_journal(void)
324 if (keypin_journal_fd
>= 0)
325 close(keypin_journal_fd
);
326 keypin_journal_fd
= -1;
330 /** Length of a keypinning journal line, including terminating newline. */
331 #define JOURNAL_LINE_LEN (BASE64_DIGEST_LEN + BASE64_DIGEST256_LEN + 2)
333 /** Add an entry to the keypinning journal to map <b>rsa_id_digest</b> and
334 * <b>ed25519_id_key</b>. */
336 keypin_journal_append_entry(const uint8_t *rsa_id_digest
,
337 const uint8_t *ed25519_id_key
)
339 if (keypin_journal_fd
== -1)
341 char line
[JOURNAL_LINE_LEN
];
342 digest_to_base64(line
, (const char*)rsa_id_digest
);
343 line
[BASE64_DIGEST_LEN
] = ' ';
344 digest256_to_base64(line
+ BASE64_DIGEST_LEN
+ 1,
345 (const char*)ed25519_id_key
);
346 line
[BASE64_DIGEST_LEN
+1+BASE64_DIGEST256_LEN
] = '\n';
348 if (write_all(keypin_journal_fd
, line
, JOURNAL_LINE_LEN
, 0)<0) {
349 log_warn(LD_DIRSERV
, "Error while adding a line to the key-pinning "
350 "journal: %s", strerror(errno
));
351 keypin_close_journal();
358 /** Load a journal from the <b>size</b>-byte region at <b>data</b>. Return 0
359 * on success, -1 on failure. */
361 keypin_load_journal_impl(const char *data
, size_t size
)
363 const char *start
= data
, *end
= data
+ size
, *next
;
365 int n_corrupt_lines
= 0;
367 int n_duplicates
= 0;
370 for (const char *cp
= start
; cp
< end
; cp
= next
) {
371 const char *eol
= memchr(cp
, '\n', end
-cp
);
372 const char *eos
= eol
? eol
: end
;
373 const size_t len
= eos
- cp
;
375 next
= eol
? eol
+ 1 : end
;
382 /* Lines that start with @ are reserved. Ignore for now. */
386 /* Lines that start with # are comments. */
390 /* Is it the right length? (The -1 here is for the newline.) */
391 if (len
!= JOURNAL_LINE_LEN
- 1) {
392 /* Lines with a bad length are corrupt unless they are empty.
393 * Ignore them either way */
394 for (const char *s
= cp
; s
< eos
; ++s
) {
395 if (! TOR_ISSPACE(*s
)) {
403 keypin_ent_t
*ent
= keypin_parse_journal_line(cp
);
410 const int r
= keypin_add_or_replace_entry_in_map(ent
);
413 } else if (r
== -1) {
420 int severity
= (n_corrupt_lines
|| n_duplicates
) ? LOG_WARN
: LOG_INFO
;
421 tor_log(severity
, LD_DIRSERV
,
422 "Loaded %d entries from keypin journal. "
423 "Found %d corrupt lines, %d duplicates, and %d conflicts.",
424 n_entries
, n_corrupt_lines
, n_duplicates
, n_conflicts
);
430 * Load a journal from the file called <b>fname</b>. Return 0 on success,
434 keypin_load_journal(const char *fname
)
436 tor_mmap_t
*map
= tor_mmap_file(fname
);
443 int r
= keypin_load_journal_impl(map
->data
, map
->size
);
444 tor_munmap_file(map
);
448 /** Parse a single keypinning journal line entry from <b>cp</b>. The input
449 * does not need to be NUL-terminated, but it <em>does</em> need to have
450 * KEYPIN_JOURNAL_LINE_LEN -1 bytes available to read. Return a new entry
451 * on success, and NULL on failure.
453 STATIC keypin_ent_t
*
454 keypin_parse_journal_line(const char *cp
)
456 /* XXXX assumes !USE_OPENSSL_BASE64 */
457 keypin_ent_t
*ent
= tor_malloc_zero(sizeof(keypin_ent_t
));
459 if (base64_decode((char*)ent
->rsa_id
, sizeof(ent
->rsa_id
),
460 cp
, BASE64_DIGEST_LEN
) != DIGEST_LEN
||
461 cp
[BASE64_DIGEST_LEN
] != ' ' ||
462 base64_decode((char*)ent
->ed25519_key
, sizeof(ent
->ed25519_key
),
463 cp
+BASE64_DIGEST_LEN
+1, BASE64_DIGEST256_LEN
) != DIGEST256_LEN
) {
471 /** Remove all entries from the keypinning table.*/
477 keypin_ent_t
**ent
, **next
, *this;
478 for (ent
= HT_START(rsamap
, &the_rsa_map
); ent
!= NULL
; ent
= next
) {
480 next
= HT_NEXT_RMV(rsamap
, &the_rsa_map
, ent
);
482 keypin_ent_t
*other_ent
= HT_REMOVE(edmap
, &the_ed_map
, this);
483 bad_entries
+= (other_ent
!= this);
488 bad_entries
+= HT_SIZE(&the_ed_map
);
490 HT_CLEAR(edmap
,&the_ed_map
);
491 HT_CLEAR(rsamap
,&the_rsa_map
);
494 log_warn(LD_BUG
, "Found %d discrepencies in the keypin database.",