Anope IRC Services  Version 2.0
m_sasl_dh-aes.cpp
Go to the documentation of this file.
1 /* RequiredLibraries: ssl,crypto */
2 /* RequiredWindowsLibraries: ssleay32,libeay32 */
3 
4 #include "module.h"
5 #include "modules/sasl.h"
6 
7 #include <openssl/bn.h>
8 #include <openssl/dh.h>
9 #include <openssl/aes.h>
10 
11 using namespace SASL;
12 
13 class DHAES : public Mechanism
14 {
15  void Err(Session* sess, BIGNUM* key = NULL)
16  {
17  if (key)
18  BN_free(key);
19 
20  sasl->Fail(sess);
21  delete sess;
22  }
23 
24  public:
26  {
27  DH* dh;
28  DHAESSession(Mechanism *m, const Anope::string &u, DH* dh_params) : SASL::Session(m, u)
29  {
30  if (!(dh = DH_new()))
31  return;
32 
33  dh->g = BN_dup(dh_params->g);
34  dh->p = BN_dup(dh_params->p);
35 
36  if (!DH_generate_key(dh))
37  {
38  DH_free(dh);
39  dh = NULL;
40  }
41  }
42 
44  {
45  if (dh)
46  DH_free(dh);
47  }
48  };
49 
50  DH* dh_params;
51  const size_t keysize;
53  {
54  return new DHAESSession(this, uid, dh_params);
55  }
56 
57  DHAES(Module *o) : Mechanism(o, "DH-AES"), keysize(256 / 8)
58  {
59  if (!(dh_params = DH_new()))
60  throw ModuleException("DH_new() failed!");
61 
62  if (!DH_generate_parameters_ex(dh_params, keysize * 8, 5, NULL))
63  {
64  DH_free(dh_params);
65  throw ModuleException("Could not generate DH-params");
66  }
67  }
68 
70  {
71  DH_free(dh_params);
72  }
73 
75  {
77 
78  if (!sess->dh)
79  {
80  sasl->SendMessage(sess, "D", "A");
81  delete sess;
82  return;
83  }
84 
85  if (m.type == "S")
86  {
87  // Format: [ss]<p>[ss]<g>[ss]<pub_key>
88  // Where ss is a unsigned short with the size of the key
89  const BIGNUM* dhval[] = { sess->dh->p, sess->dh->g, sess->dh->pub_key };
90 
91  // Find the size of our buffer - initialized at 6 because of string size data
92  size_t size = 6;
93  for (size_t i = 0; i < 3; i++)
94  size += BN_num_bytes(dhval[i]);
95 
96  // Fill in the DH data
97  std::vector<unsigned char> buffer(size);
98  for (size_t i = 0, pos = 0; i < 3; i++)
99  {
100  *reinterpret_cast<uint16_t*>(&buffer[pos]) = htons(BN_num_bytes(dhval[i]));
101  pos += 2;
102  BN_bn2bin(dhval[i], &buffer[pos]);
103  pos += BN_num_bytes(dhval[i]);
104  }
105 
106  Anope::string encoded;
107  Anope::B64Encode(Anope::string(buffer.begin(), buffer.end()), encoded);
108  sasl->SendMessage(sess, "C", encoded);
109  }
110  else if (m.type == "C")
111  {
112  // Make sure we have some data - actual size check is done later
113  if (m.data.length() < 10)
114  return Err(sess);
115 
116  // Format: [ss]<key>[ss]<iv>[ss]<encrypted>
117  // <encrypted> = <username>\0<password>\0
118 
119  Anope::string decoded;
120  Anope::B64Decode(m.data, decoded);
121 
122  // Make sure we have an IV and at least one encrypted block
123  if ((decoded.length() < keysize + 2 + (AES_BLOCK_SIZE * 2)) || ((decoded.length() - keysize - 2) % AES_BLOCK_SIZE))
124  return Err(sess);
125 
126  const unsigned char* data = reinterpret_cast<const unsigned char*>(decoded.data());
127 
128  // Control the size of the key
129  if (ntohs(*reinterpret_cast<const uint16_t*>(&data[0])) != keysize)
130  return Err(sess);
131 
132  // Convert pubkey from binary
133  size_t pos = 2;
134  BIGNUM* pubkey = BN_bin2bn(&data[pos], keysize, NULL);
135  if (!pubkey)
136  return Err(sess);
137 
138  // Find shared key
139  std::vector<unsigned char> secretkey(keysize);
140  if (DH_compute_key(&secretkey[0], pubkey, sess->dh) != static_cast<int>(keysize))
141  return Err(sess, pubkey);
142 
143  // Set decryption key
144  AES_KEY AESKey;
145  AES_set_decrypt_key(&secretkey[0], keysize * 8, &AESKey);
146 
147  // Fetch IV
148  pos += keysize;
149  std::vector<unsigned char> IV(data + pos, data + pos + AES_BLOCK_SIZE);
150 
151  // Find encrypted blocks, and decrypt
152  pos += AES_BLOCK_SIZE;
153  size_t size = decoded.length() - pos;
154  std::vector<char> decrypted(size + 2, 0);
155  AES_cbc_encrypt(&data[pos], reinterpret_cast<unsigned char*>(&decrypted[0]), size, &AESKey, &IV[0], AES_DECRYPT);
156 
157  std::string username = &decrypted[0];
158  std::string password = &decrypted[username.length() + 1];
159 
160  if (username.empty() || password.empty())
161  return Err(sess, pubkey);
162 
163  SASL::IdentifyRequest* req = new SASL::IdentifyRequest(this->owner, m.source, username, password);
164  FOREACH_MOD(OnCheckAuthentication, (NULL, req));
165  req->Dispatch();
166 
167  BN_free(pubkey);
168  }
169  }
170 };
171 
172 
173 class ModuleSASLDHAES : public Module
174 {
176 
177  public:
178  ModuleSASLDHAES(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA),
179  dhaes(this)
180  {
181  }
182 };
183 
const size_t keysize
const char * data() const
Definition: anope.h:118
void Dispatch()
Definition: account.cpp:57
SASL::Session * CreateSession(const Anope::string &uid) anope_override
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
ModuleSASLDHAES(const Anope::string &modname, const Anope::string &creator)
size_type length() const
Definition: anope.h:131
DH * dh_params
CoreExport void B64Encode(const string &src, string &target)
Definition: base64.cpp:82
static ServiceReference< SASL::Service > sasl("SASL::Service","sasl")
CoreExport void B64Decode(const string &src, string &target)
Definition: base64.cpp:123
void ProcessMessage(SASL::Session *session, const SASL::Message &m) anope_override
std::pair< const uint32_t *, size_t > IV
Definition: encryption.h:16
#define anope_override
Definition: services.h:56
DHAESSession(Mechanism *m, const Anope::string &u, DH *dh_params)
std::basic_string< char, ci_char_traits, std::allocator< char > > string
Definition: hashcomp.h:133
#define MODULE_INIT(x)
Definition: modules.h:45
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
void Err(Session *sess, BIGNUM *key=NULL)
DHAES(Module *o)
Definition: defs.h:55
Definition: modules.h:163