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