Merge branch 'dev' of feistymeow.org:feisty_meow into dev
[feisty_meow.git] / octopi / library / tentacles / encryption_tentacle.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : encryption_tentacle                                               *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2004-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "encryption_tentacle.h"
16 #include "encryption_wrapper.h"
17 #include "key_repository.h"
18
19 #include <crypto/blowfish_crypto.h>
20 #include <crypto/rsa_crypto.h>
21 #include <loggers/program_wide_logger.h>
22 #include <structures/symbol_table.h>
23 #include <textual/byte_formatter.h>
24
25 using namespace basis;
26 using namespace crypto;
27 using namespace loggers;
28 using namespace structures;
29 using namespace textual;
30
31 namespace octopi {
32
33 #undef LOG
34 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
35
36 #define DEBUG_ENCRYPTION_TENTACLE
37   // uncomment for noisier code.
38
39 //////////////
40
41 encryption_tentacle::encryption_tentacle()
42 : tentacle_helper<encryption_infoton>
43     (encryption_infoton::encryption_classifier(), false),
44   _server_side(true),
45   _keys(new key_repository),
46   _rsa_private(NULL_POINTER)
47 {
48 }
49
50 encryption_tentacle::encryption_tentacle(const byte_array &private_key)
51 : tentacle_helper<encryption_infoton>
52     (encryption_infoton::encryption_classifier(), false),
53   _server_side(false),
54   _keys(new key_repository),
55   _rsa_private(new rsa_crypto(private_key))
56 {
57 }
58
59 encryption_tentacle::encryption_tentacle(int key_size)
60 : tentacle_helper<encryption_infoton>
61     (encryption_infoton::encryption_classifier(), false),
62   _server_side(false),
63   _keys(new key_repository),
64   _rsa_private(new rsa_crypto(key_size))
65 {
66 }
67
68 encryption_tentacle::~encryption_tentacle()
69 {
70   WHACK(_rsa_private);
71   WHACK(_keys);
72 }
73
74 key_repository &encryption_tentacle::keys() const { return *_keys; }
75
76 const rsa_crypto &encryption_tentacle::private_key() const
77 { return *_rsa_private; }
78
79 outcome encryption_tentacle::reconstitute(const string_array &classifier,
80     byte_array &packed_form, infoton * &reformed)
81 {
82   if (classifier != encryption_infoton::encryption_classifier()) 
83     return NO_HANDLER;
84
85   return reconstituter(classifier, packed_form, reformed,
86       (encryption_infoton *)NULL_POINTER);
87 }
88
89 void encryption_tentacle::expunge(const octopus_entity &formal(to_remove))
90 {
91 ////  _keys->whack(to_remove);
92 //we need a better approach.  it seems there are places where an entity
93 //can get reused and it still expects its key to be present.
94 }
95
96 outcome encryption_tentacle::consume(infoton &to_chow,
97     const octopus_request_id &item_id, byte_array &transformed)
98 {
99   FUNCDEF("consume");
100   transformed.reset();
101   encryption_infoton *inf = dynamic_cast<encryption_infoton *>(&to_chow);
102   if (!inf) {
103     // this package is not explicitly an encryption infoton.  we need to
104     // decrypt it using what we already know.
105
106     encryption_wrapper *wrap = dynamic_cast<encryption_wrapper *>(&to_chow);
107     if (!wrap) {
108 #ifdef DEBUG_ENCRYPTION_TENTACLE
109 //      LOG(astring("got a stray infoton that was not encrypted: ")
110 //          + to_chow.text_form());
111 #endif
112       // this signals that we were expecting an encrypted package.
113       return ENCRYPTION_MISMATCH;
114     }
115
116     octenc_key_record record;
117     octenc_key_record *rec = _keys->lock(item_id._entity);
118     if (!rec) {
119 #ifdef DEBUG_ENCRYPTION_TENTACLE
120       LOG(astring("no key stored for entity ")
121           + item_id._entity.mangled_form()
122           + "; rejecting packet.");
123 #endif
124       return DISALLOWED;
125     }
126     record = *rec;
127     _keys->unlock(rec);
128
129     byte_array decro;
130     bool decrypts_properly = record._key.decrypt(wrap->_wrapped, decro);
131     if (decrypts_properly) {
132       // this package seems to be intact.  we need to reconstitute the
133       // original infoton.
134       transformed = decro;  // set the decrypted blob.
135       return PARTIAL;
136     }
137
138 #ifdef DEBUG_ENCRYPTION_TENTACLE
139     LOG(astring("denying client ") + item_id._entity.mangled_form()
140         + " due to erroneous decryption");
141 #endif
142
143     // the infoton's client is not authorized; it needs to be dropped.
144     return DISALLOWED;
145   }
146
147   // reaching here means this is explicitly an encryption startup request.
148
149   if (!_server_side) {
150     // client's side must track the key we were given for decryption.  we'll
151     // use that from now on.
152     blowfish_crypto new_key(blowfish_crypto::minimum_key_size());  // bogus.
153     outcome ret = inf->extract_response(*_rsa_private, new_key);
154     if (ret != OKAY) {
155 #ifdef DEBUG_ENCRYPTION_TENTACLE
156       LOG(astring("client failed to process encrypted blowfish key for ")
157           + item_id._entity.mangled_form());
158 #endif
159     } else {
160       _keys->add(item_id._entity, new_key);  // add our key for this guy.
161     }
162     // we do not store a copy of the infoton; it's just done now.
163     return ret;
164   } else {
165     // server's side need to process a key request and send it back using
166     // the public key the requester provided.
167     blowfish_crypto agreed_key(blowfish_crypto::minimum_key_size());
168       // initialized with junk.
169     outcome worked = inf->prepare_blowfish_key(agreed_key);
170     if (worked != OKAY) {
171 #ifdef DEBUG_ENCRYPTION_TENTACLE
172       LOG(astring("server failed to encrypt blowfish key for ")
173           + item_id._entity.mangled_form());
174 #endif
175     } else {
176       _keys->add(item_id._entity, agreed_key);  // add our key for this guy.
177     }
178   }
179
180   if (!store_product(dynamic_cast<infoton *>(inf->clone()), item_id))
181     return NO_SPACE;
182   return OKAY;
183 }
184
185 } //namespace.
186