Make things const where appropriate.
[fides] / lib / fides.cc
1 /* fides.cc - Light-weight, decentralised trust and authorisation management
2    Copyright (C) 2008-2009  Guus Sliepen <guus@tinc-vpn.org>
3   
4    Fides is free software; you can redistribute it and/or modify
5    it under the terms of the GNU Lesser General Public License as
6    published by the Free Software Foundation; either version 2.1 of
7    the License, or (at your option) any later version.
8   
9    Fides is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12    GNU Lesser General Public License for more details.
13   
14    You should have received a copy of the GNU Lesser General Public
15    License along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <iostream>
19 #include <fstream>
20 #include <cstdlib>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <dirent.h>
25 #include <botan/types.h>
26 #include <botan/botan.h>
27 #include <botan/ecdsa.h>
28 #include <botan/look_pk.h>
29 #include <botan/lookup.h>
30 #include <botan/filters.h>
31 #include <botan/sha2_32.h>
32 #include <list>
33
34 #include "fides.h"
35
36 using namespace std;
37
38 // Global state
39
40 Botan::AutoSeeded_RNG fides::rng;
41
42 // Public key functions
43
44 fides::publickey::publickey(): pub(0), trust(0) {
45 }
46
47 fides::publickey::~publickey() {
48         delete pub;
49 }
50
51 void fides::publickey::load(istream &in) {
52         try {
53                 Botan::DataSource_Stream source(in);
54                 pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
55         } catch(Botan::Exception &e) {
56                 throw exception(e.what());
57         }
58 }
59
60 void fides::publickey::load(const std::string &filename) {
61         ifstream in(filename.c_str());
62         load(in);
63 }
64
65 void fides::publickey::save(ostream &out) const {
66         out << to_string();
67 }
68
69 void fides::publickey::save(const std::string &filename) const {
70         ofstream out(filename.c_str());
71         save(out);
72 }
73
74 void fides::publickey::from_string(const std::string &in) {
75         try {
76                 Botan::DataSource_Memory source(in);
77                 pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
78         } catch(Botan::Exception &e) {
79                 throw exception(e.what());
80         }
81 }
82
83 string fides::publickey::to_string() const {
84         return Botan::X509::PEM_encode(*pub);
85 }
86
87 string fides::publickey::fingerprint(unsigned int bits) const {
88         // TODO: find out if there is a standard way to get a hash of an ECDSA public key
89         Botan::SHA_256 sha256;
90         Botan::SecureVector<Botan::byte> hash = sha256.process(Botan::X509::PEM_encode(*pub));
91         return string((const char *)hash.begin(), bits / 8);
92 }
93
94 bool fides::publickey::verify(const std::string &statement, const std::string &signature) const {
95         auto_ptr<Botan::PK_Verifier> verifier(Botan::get_pk_verifier(*pub, "EMSA1(SHA-512)"));
96         verifier->update((const Botan::byte *)statement.data(), statement.size());
97         Botan::SecureVector<Botan::byte> sig;
98         sig.set((const Botan::byte *)signature.data(), signature.size());
99         return verifier->check_signature(sig);
100 }
101
102 // Private key functions
103
104 fides::privatekey::privatekey(): priv(0) {
105 }
106
107 fides::privatekey::~privatekey() {
108         delete priv;
109         pub = 0;
110 }
111
112 void fides::privatekey::generate(const std::string &field) {
113         Botan::EC_Domain_Params domain = Botan::get_EC_Dom_Pars_by_oid(field);
114         pub = priv = new Botan::ECDSA_PrivateKey(rng, domain);
115 }
116
117 void fides::privatekey::generate(unsigned int bits) {
118         switch(bits) {
119                 case 112: return generate("1.3.132.0.6");
120                 case 128: return generate("1.3.132.0.28");
121                 case 160: return generate("1.3.132.0.9");
122                 case 192: return generate("1.3.132.0.31");
123                 case 224: return generate("1.3.132.0.32");
124                 case 256: return generate("1.3.132.0.10");
125                 case 384: return generate("1.3.132.0.34");
126                 case 521: return generate("1.3.132.0.35");
127                 default: throw exception("Unsupported number of bits for private key");
128         }
129 }
130
131 void fides::privatekey::load_private(istream &in) {
132         try {
133                 Botan::DataSource_Stream stream(in);
134                 pub = priv = dynamic_cast<Botan::ECDSA_PrivateKey *>(Botan::PKCS8::load_key(stream, rng, ""));
135         } catch(Botan::Exception &e) {
136                 throw exception(e.what());
137         }
138 }
139
140 void fides::privatekey::load_private(const std::string &filename) {
141         ifstream in(filename.c_str());
142         load_private(in);
143 }
144
145 void fides::privatekey::save_private(ostream &out) const {
146         out << Botan::PKCS8::PEM_encode(*priv);
147 }
148
149 void fides::privatekey::save_private(const std::string &filename) const {
150         ofstream out(filename.c_str());
151         save_private(out);
152 }
153
154 string fides::privatekey::sign(const std::string &statement) const {
155         auto_ptr<Botan::PK_Signer> signer(Botan::get_pk_signer(*priv, "EMSA1(SHA-512)"));
156         Botan::SecureVector<Botan::byte> sig = signer->sign_message((const Botan::byte *)statement.data(), statement.size(), rng);
157         return string((const char *)sig.begin(), (size_t)sig.size());
158 }
159
160 // Base64 and hex encoding/decoding functions
161
162 string fides::hexencode(const string &in) {
163         Botan::Pipe pipe(new Botan::Hex_Encoder);
164         pipe.process_msg((Botan::byte *)in.data(), in.size());
165         return pipe.read_all_as_string();
166 }
167
168 string fides::hexdecode(const string &in) {
169         Botan::Pipe pipe(new Botan::Hex_Decoder);
170         pipe.process_msg((Botan::byte *)in.data(), in.size());
171         return pipe.read_all_as_string();
172 }
173
174 string fides::b64encode(const string &in) {
175         Botan::Pipe pipe(new Botan::Base64_Encoder);
176         pipe.process_msg((Botan::byte *)in.data(), in.size());
177         return pipe.read_all_as_string();
178 }
179
180 string fides::b64decode(const string &in) {
181         Botan::Pipe pipe(new Botan::Base64_Decoder);
182         pipe.process_msg((Botan::byte *)in.data(), in.size());
183         return pipe.read_all_as_string();
184 }
185
186 // Certificate functions
187
188 fides::certificate::certificate(const publickey *key, struct timeval timestamp, const std::string &statement, const std::string &signature): signer(key), timestamp(timestamp), statement(statement), signature(signature) {}
189
190 bool fides::certificate::validate() const {
191         string data = signer->fingerprint(256);
192         data += string((const char *)&timestamp, sizeof timestamp);
193         data += statement;
194         return signer->verify(data, signature);
195 }
196
197 fides::certificate::certificate(const privatekey *key, struct timeval timestamp, const std::string &statement): signer(key), timestamp(timestamp), statement(statement) {
198         string data = signer->fingerprint(256);
199         data += string((const char *)&timestamp, sizeof timestamp);
200         data += statement;
201         signature = key->sign(data);
202 }
203
204 string fides::certificate::fingerprint(unsigned int bits) const {
205         return signature.substr(signature.size() - bits / 8);   
206 }
207
208 string fides::certificate::to_string() const {
209         string data = fides::hexencode(signer->fingerprint());
210         data += ' ';
211         char ts[100];
212         snprintf(ts, sizeof ts, "%lu.%06lu", timestamp.tv_sec, timestamp.tv_usec);
213         data += ts;
214         data += ' ';
215         data += fides::b64encode(signature);
216         data += ' ';
217         data += statement;
218         return data;
219 }
220
221 // Utility functions
222
223 static vector<string> dirlist(const string &path) {
224         vector<string> files;
225
226         DIR *dir = opendir(path.c_str());
227         if(!dir)
228                 return files;
229
230         struct dirent entry, *result = &entry;
231         
232         while(result) {
233                 readdir_r(dir, &entry, &result);
234                 if(!result)
235                         break;
236                 struct stat st;
237                 if(result->d_type == DT_UNKNOWN) {
238                         if(stat((path + "/" + result->d_name).c_str(), &st))
239                                 continue;
240                         if(S_ISREG(st.st_mode))
241                                 files.push_back(result->d_name);
242                 } else if(result->d_type == DT_REG) {
243                         files.push_back(result->d_name);
244                 }
245         }
246
247         closedir(dir);
248
249         return files;
250 }
251
252 void fides::certificate_save(const certificate *cert, const string &filename) const {
253         ofstream file(filename.c_str());
254         file << cert->to_string() << '\n';
255 }
256
257 fides::certificate *fides::certificate_load(const string &filename) {
258         ifstream file(filename.c_str());
259         string data;
260         getline(file, data);
261         return certificate_from_string(data);
262 }
263
264 fides::certificate *fides::certificate_from_string(const string &data) {
265         size_t b, e;
266         e = data.find(' ', 0);
267         if(e == string::npos)
268                 throw exception("Invalid certificate");
269         string fingerprint = hexdecode(data.substr(0, e));
270         publickey *signer = find_key(fingerprint);
271         if(!signer)
272                 throw exception("Unknown public key");
273         b = e + 1;
274         e = data.find('.', b);
275         if(e == string::npos)
276                 throw exception("Invalid certificate");
277         struct timeval timestamp;
278         timestamp.tv_sec = atol(data.c_str() + b);
279         b = e + 1;
280         timestamp.tv_usec = atol(data.c_str() + b);
281         e = data.find(' ', b);
282         if(e == string::npos)
283                 throw exception("Invalid certificate");
284         b = e + 1;
285         e = data.find(' ', b);
286         if(e == string::npos)
287                 throw exception("Invalid certificate");
288         string signature = fides::b64decode(data.substr(b, e - b));
289         b = e + 1;
290         string statement = data.substr(b);
291
292         return new certificate(signer, timestamp, statement, signature);
293 }
294
295 // Fides main functions
296
297 fides::fides(const string &dir): homedir(dir) {
298         cerr << "Fides initialising\n";
299
300         // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
301         if(homedir.empty())
302                 homedir = getenv("FIDES_HOME") ?: "";
303         if(homedir.empty()) {
304                 char cwd[PATH_MAX];
305                 homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
306                 homedir += "/.fides";
307         }
308
309         // Derived directories
310         homedir += '/';
311         certdir = homedir + "certs/";
312         keydir = homedir + "keys/";
313         obsoletedir = homedir + ".obsolete_certs/";
314
315         // Ensure the homedir and its subdirectories exist
316         mkdir(homedir.c_str(), 0700);
317         mkdir(certdir.c_str(), 0700);
318         mkdir(keydir.c_str(), 0700);
319         mkdir(obsoletedir.c_str(), 0700);
320
321         try {
322                 mykey.load_private(homedir + "priv");
323                 firstrun = false;
324         } catch(fides::exception &e) {
325                 cerr << "Fides generating keypair\n";
326                 mykey.generate();
327                 mykey.save_private(homedir + "priv");
328                 mykey.save(keydir + hexencode(mykey.fingerprint()));
329                 firstrun = true;
330         }
331         vector<string> files = dirlist(keydir);
332         for(size_t i = 0; i < files.size(); ++i) {
333                 cerr << "Loading key " << files[i] << '\n';
334
335                 publickey *key = new publickey();
336                 key->load(keydir + files[i]);
337                 keys[hexdecode(files[i])] = key;
338         }
339
340         keys[mykey.fingerprint()] = &mykey;
341
342         files = dirlist(certdir);
343         for(size_t i = 0; i < files.size(); ++i) {
344                 cerr << "Loading certificate " << files[i] << '\n';
345                 certificate *cert = certificate_load(certdir + files[i]);
346                 if(false && !cert->validate()) {
347                         cerr << "Bad certificate!\n";
348                         continue;
349                 }
350                 certs[hexdecode(files[i])] = cert;
351         }
352
353         // TODO: save and load this value
354         latest.tv_sec = 0;
355         latest.tv_usec = 0;
356
357         update_trust();
358 }
359
360 fides::~fides() {
361         cerr << "Fides exitting\n";
362         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
363                 delete i->second;
364         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
365                 if(i->second != &mykey)
366                         delete i->second;
367 }
368
369 bool fides::fsck() const {
370         int errors = 0;
371
372         for(map<string, certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i) {
373                 if(!i->second->validate()) {
374                         cerr << "Validation of certificate failed: " << i->second->to_string() << '\n';
375                         errors++;
376                 }
377         }
378
379         cerr << errors << " errors in " << certs.size() << " certificates\n";
380         return !errors;
381 }
382
383 string fides::get_homedir() const {
384         return homedir;
385 }
386
387 bool fides::is_firstrun() const {
388         return firstrun;
389 }
390
391 fides::publickey *fides::find_key(const string &fingerprint) const {
392         map<string, publickey *>::const_iterator i;
393         i = keys.find(fingerprint);
394         if(i != keys.end())
395                 return i->second;
396         else
397                 return 0;
398 }
399
400 vector<fides::certificate *> fides::find_certificates(const publickey *signer, const string &regex) const {
401         vector<certificate *> found;
402         map<string, certificate *>::const_iterator i;
403         regexp regexp(regex);
404         for(i = certs.begin(); i != certs.end(); ++i) {
405                 if(!i->second) {
406                         cerr << "No certificate for " << hexencode(i->first) << '\n';
407                         continue;
408                 }
409                 if(i->second->signer == signer)
410                         if(regexp.match(i->second->statement))
411                                 found.push_back(i->second);
412         }
413         return found;
414 }
415
416 vector<fides::certificate *> fides::find_certificates(const string &regex) const {
417         vector<certificate *> found;
418         map<string, certificate *>::const_iterator i;
419         regexp regexp(regex);
420         for(i = certs.begin(); i != certs.end(); ++i)
421                 if(regexp.match(i->second->statement))
422                         found.push_back(i->second);
423         return found;
424 }
425
426 vector<fides::certificate *> fides::find_certificates(const publickey *signer) const {
427         vector<certificate *> found;
428         map<string, certificate *>::const_iterator i;
429         for(i = certs.begin(); i != certs.end(); ++i)
430                 if(i->second->signer == signer)
431                         found.push_back(i->second);
432         return found;
433 }
434
435 void fides::import_all(istream &in) {
436         string line, pem;
437         bool is_pem = false;
438
439         while(getline(in, line)) {
440                 if(line.empty())
441                         continue;
442
443                 if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
444                         pem += line + '\n';
445                         if(!line.compare(0, 9, "-----END ")) {
446                                 fides::publickey *key = new publickey();
447                                 key->from_string(pem);
448                                 cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
449                                 merge(key);
450                                 is_pem = false;
451                         } else {
452                                 is_pem = true;
453                         }
454                         continue;
455                 }
456
457                 fides::certificate *cert = certificate_from_string(line);
458                 cerr << "Importing certificate " << hexencode(cert->fingerprint()) << '\n';
459                 merge(cert);
460         }
461 }
462
463 void fides::export_all(ostream &out) const {
464         for(map<string, publickey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
465                 out << i->second->to_string();
466         for(map<string, certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i)
467                 out << i->second->to_string() << '\n';
468 }
469
470 void fides::trust(const publickey *key) {
471         string full = "t+ " + hexencode(key->fingerprint());
472         sign(full);
473 }
474
475 void fides::distrust(const publickey *key) {
476         string full = "t- " + hexencode(key->fingerprint());
477         sign(full);
478 }
479
480 void fides::dctrust(const publickey *key) {
481         string full = "t0 " + hexencode(key->fingerprint());
482         sign(full);
483 }
484
485 void fides::update_trust() {
486         // clear trust on all keys
487         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
488                 i->second->trust = 0;
489
490         // Start by checking all trust certificates from ourself.
491         // If another key is positively or negatively trusted, update its trust score
492         // and add it to the the list of new keys to check.
493         // Then add our own key to the list of already checked keys.
494         // Then check all the trust certificates of those on the tocheck list, etc.
495         // Already checked keys are never updated anymore (TODO: is that smart?)
496         // Certificates of keys with a zero or negative trust score are not processed.
497
498         set<publickey *> checked;
499         set<publickey *> tocheck;
500         set<publickey *> newkeys;
501         set<publickey *>::iterator i;
502
503         mykey.trust = 3;
504         tocheck.insert(&mykey);
505
506         while(tocheck.size()) {
507                 // add
508                 checked.insert(tocheck.begin(), tocheck.end());
509                 newkeys.clear();
510
511                 // loop over all keys whose certificates need to be checked
512
513                 for(i = tocheck.begin(); i != tocheck.end(); ++i) {
514                         cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
515
516                         // except if this key is not trusted
517
518                         if((*i)->trust <= 0)
519                                 continue;
520
521                         // find all non-zero trust certificates of this key
522
523                         vector<certificate *> matches = find_certificates(*i, "^t[+-] ");
524
525                         // update trust value of those keys
526
527                         for(size_t j = 0; j < matches.size(); j++) {
528                                 publickey *other = find_key(hexdecode(matches[j]->statement.substr(3)));        
529
530                                 if(!other) {
531                                         cerr << "Trust certificate for unknown key: " << matches[j]->to_string() << '\n';
532                                         continue;
533                                 }
534
535                                 // except for keys we already checked
536
537                                 if(checked.find(other) != checked.end()) {
538                                         cerr << "Skipping trust certificate for already checked key: " << matches[j]->to_string() << '\n';
539                                         continue;
540                                 }
541
542                                 // update trust
543
544                                 if(matches[j]->statement[1] == '+')
545                                         other->trust++;
546                                 else
547                                         other->trust--;
548
549                                 newkeys.insert(other);
550                         }
551                 }
552
553                 tocheck = newkeys;
554         }       
555 }       
556
557 void fides::merge(publickey *key) {
558         if(keys.find(key->fingerprint()) != keys.end()) {
559                 cerr << "Key already known\n";
560                 return;
561         }
562
563         keys[key->fingerprint()] = key;
564         key->save(keydir + hexencode(key->fingerprint()));
565 }
566
567 void fides::merge(certificate *cert) {
568         // TODO: check if cert is already in database
569         // TODO: check if cert obsoletes other certs
570
571         // If we already know this certificate, drop it.
572         if(certs.find(cert->fingerprint()) != certs.end()) {
573                 cerr << "Certificate already known\n";
574                 return;
575         }
576
577         // If the certificate does not validate, drop it.
578         if(!cert->validate()) {
579                 // TODO: this should not happen, be wary of DoS attacks
580                 cerr << "Certificate invalid\n";
581                 return;
582         }
583
584         // TODO: move these regexps to the class?
585         regexp authexp("^a[+0-] ");
586         regexp trustexp("^t[+0-] ");
587         vector<certificate *> others;
588
589         // Is this an authorisation cert?
590         if(authexp.match(cert->statement)) {
591                 // Find certs identical except for the +/-/0
592                 // TODO: escape statement in regexp
593                 others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
594                 if(others.size()) {
595                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
596                                 cerr << "Certificate is overruled by a newer certificate\n";
597                                 return;
598                         }
599                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
600                                 // TODO: this should not happen, be wary of DoS attacks
601                                 cerr << "Certificate has same timestamp as another timestamp!\n";
602                                 return;
603                         }
604                         cerr << "Certificate overrules an older certificate!\n";
605                         // save new cert first
606                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
607                         certs[cert->fingerprint()] = cert;
608
609                         // delete old one
610                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
611                         certs.erase(others[0]->fingerprint());
612                         delete others[0];
613                         return;
614                 }
615         }
616
617         // Is this a trust cert?
618         // TODO: it's just the same as above!
619         if(trustexp.match(cert->statement)) {
620                 // Find certs identical except for the +/-/0
621                 // TODO: escape statement in regexp
622                 others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
623                 if(others.size()) {
624                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
625                                 cerr << "Certificate is overruled by a newer certificate\n";
626                                 return;
627                         }
628                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
629                                 // TODO: this should not happen, be wary of DoS attacks
630                                 cerr << "Certificate has same timestamp as another timestamp!\n";
631                                 return;
632                         }
633                         cerr << "Certificate overrules an older certificate!\n";
634                         // delete old one
635                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
636                         certs.erase(others[0]->fingerprint());
637                         delete others[0];
638                         certs[cert->fingerprint()] = cert;
639                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
640                         return;
641                 }
642         }
643
644         // Did somebody sign the exact same statement twice?
645         // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
646         others = find_certificates(cert->signer, string("^") + cert->statement + '$');
647         if(others.size()) {
648                 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
649                         cerr << "Certificate is overruled by a newer certificate\n";
650                         return;
651                 }
652                 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
653                         // TODO: this should not happen, be wary of DoS attacks
654                         cerr << "Certificate has same timestamp as another timestamp!\n";
655                         return;
656                 }
657                 cerr << "Certificate overrules an older certificate!\n";
658                 // delete old one
659                 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
660                 certs.erase(others[0]->fingerprint());
661                 delete others[0];
662                 certs[cert->fingerprint()] = cert;
663                 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
664                 return;
665         }
666
667         cerr << "Certificate is new\n";
668         certs[cert->fingerprint()] = cert;
669         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
670 }
671
672 void fides::auth_stats(const string &statement, int &self, int &trusted, int &all) const {
673         self = trusted = all = 0;
674         vector<certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
675         for(size_t i = 0; i < matches.size(); ++i) {
676                 char code = matches[i]->statement[1];
677                 int diff = 0;
678                 if(code == '+')
679                         diff = 1;
680                 else if(code == '-')
681                         diff = -1;
682                 if(matches[i]->signer == &mykey)
683                         self += diff;
684                 if(matches[i]->signer->trust > 0)
685                         trusted += diff;
686                 all += diff;
687         }
688 }
689
690 bool fides::is_trusted(const publickey *key) const {
691         return key->trust > 0;
692 }
693
694 bool fides::is_distrusted(const publickey *key) const {
695         return key->trust < 0;
696 }
697
698 bool fides::is_allowed(const string &statement, const publickey *key) const {
699         int self, trusted, all;
700
701         if(key)
702                 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
703         else
704                 auth_stats(statement, self, trusted, all);
705                 
706         if(self)
707                 return self > 0;
708         else if(trusted)
709                 return trusted > 0;
710         else
711                 return false;
712 }
713
714 bool fides::is_denied(const string &statement, const publickey *key) const {
715         int self, trusted, all;
716
717         if(key)
718                 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
719         else
720                 auth_stats(statement, self, trusted, all);
721
722         if(self)
723                 return self < 0;
724         else if(trusted)
725                 return trusted < 0;
726         else
727                 return false;
728 }
729
730 void fides::sign(const string &statement) {
731         // Try to set "latest" to now, but ensure monoticity
732         struct timeval now;
733         gettimeofday(&now, 0);
734         if(timercmp(&latest, &now, >=)) {
735                 latest.tv_usec++;
736                 if(latest.tv_usec >= 1000000) {
737                         latest.tv_sec++;
738                         latest.tv_usec -= 1000000;
739                 }
740         } else {
741                 latest = now;
742         }
743
744         // Create a new certificate and merge it with our database
745         merge(new certificate(&mykey, latest, statement));
746 }
747
748 void fides::allow(const string &statement, const publickey *key) {
749         string full = "a+ ";
750         if(key)
751                 full += hexencode(key->fingerprint()) + ' ';
752         full += statement;
753         sign(full);
754 }
755
756 void fides::dontcare(const string &statement, const publickey *key) {
757         string full = "a0 ";
758         if(key)
759                 full += hexencode(key->fingerprint()) + ' ';
760         full += statement;
761         sign(full);
762 }
763
764 void fides::deny(const string &statement, const publickey *key) {
765         string full = "a- ";
766         if(key)
767                 full += hexencode(key->fingerprint()) + ' ';
768         full += statement;
769         sign(full);
770 }
771