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