Make WvStreams compile with gcc 4.4.
[wvstreams.git] / crypto / wvrsa.cc
blob170999c72e4a9a8db68ef0bcfb27382942493a81
1 /*
2 * Worldvisions Tunnel Vision Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * RSA cryptography abstractions.
6 */
7 #include <assert.h>
8 #include <openssl/rsa.h>
9 #include <openssl/pem.h>
10 #include "wvsslhacks.h"
11 #include "wvrsa.h"
12 #include "wvhex.h"
13 #include "wvfileutils.h"
15 /***** WvRSAKey *****/
17 WvRSAKey::WvRSAKey()
18 : debug("RSA", WvLog::Debug5)
20 rsa = NULL;
24 WvRSAKey::WvRSAKey(const WvRSAKey &k)
25 : debug("RSA", WvLog::Debug5)
27 priv = k.priv;
29 if (!priv)
30 rsa = RSAPublicKey_dup(k.rsa);
31 else
32 rsa = RSAPrivateKey_dup(k.rsa);
36 WvRSAKey::WvRSAKey(struct rsa_st *_rsa, bool _priv)
37 : debug("RSA", WvLog::Debug5)
39 if (_rsa == NULL)
41 rsa = NULL;
42 debug("Initializing with a NULL key.. are you insane?\n");
43 return;
46 rsa = _rsa;
47 priv = _priv;
51 WvRSAKey::WvRSAKey(WvStringParm keystr, bool _priv)
52 : debug("RSA", WvLog::Debug5)
54 rsa = NULL;
56 if (_priv)
57 decode(RsaHex, keystr);
58 else
59 decode(RsaPubHex, keystr);
61 priv = _priv;
65 WvRSAKey::WvRSAKey(int bits)
66 : debug("RSA", WvLog::Debug5)
68 rsa = RSA_generate_key(bits, 0x10001, NULL, NULL);
69 priv = true;
73 WvRSAKey::~WvRSAKey()
75 if (rsa)
76 RSA_free(rsa);
80 bool WvRSAKey::isok() const
82 return rsa && (!priv || RSA_check_key(rsa) == 1);
86 WvString WvRSAKey::encode(const DumpMode mode) const
88 WvString nil;
89 WvDynBuf retval;
90 encode(mode, retval);
91 return retval.getstr();
95 void WvRSAKey::encode(const DumpMode mode, WvBuf &buf) const
97 if (!rsa)
99 debug(WvLog::Warning, "Tried to encode RSA key, but RSA key is "
100 "blank!\n");
101 return;
104 if (mode == RsaHex || mode == RsaPubHex)
106 WvDynBuf keybuf;
108 if (mode == RsaHex && priv)
110 size_t size = i2d_RSAPrivateKey(rsa, NULL);
111 unsigned char *key = keybuf.alloc(size);
112 size_t newsize = i2d_RSAPrivateKey(rsa, & key);
113 assert(size == newsize);
115 else
117 size_t size = i2d_RSAPublicKey(rsa, NULL);
118 unsigned char *key = keybuf.alloc(size);
119 size_t newsize = i2d_RSAPublicKey(rsa, & key);
120 assert(size == newsize);
123 buf.putstr(WvString(WvHexEncoder().strflushbuf(keybuf, true)));
125 else
127 BIO *bufbio = BIO_new(BIO_s_mem());
128 BUF_MEM *bm;
129 const EVP_CIPHER *enc = EVP_get_cipherbyname("rsa");
131 if (mode == RsaPEM)
132 PEM_write_bio_RSAPrivateKey(bufbio, rsa, enc,
133 NULL, 0, NULL, NULL);
134 else if (mode == RsaPubPEM)
135 PEM_write_bio_RSAPublicKey(bufbio, rsa);
136 else
137 debug(WvLog::Warning, "Should never happen: tried to encode RSA "
138 "key with unsupported mode.");
140 BIO_get_mem_ptr(bufbio, &bm);
141 buf.put(bm->data, bm->length);
142 BIO_free(bufbio);
147 void WvRSAKey::decode(const DumpMode mode, WvStringParm encoded)
149 if (!encoded)
150 return;
152 WvDynBuf buf;
153 buf.putstr(encoded);
154 decode(mode, buf);
158 void WvRSAKey::decode(const DumpMode mode, WvBuf &encoded)
160 debug("Decoding RSA key.\n");
162 if (rsa)
164 debug("Replacing already existent RSA key.\n");
165 RSA_free(rsa);
166 rsa = NULL;
168 priv = false;
170 // we handle hexified keys a bit differently, since
171 // OpenSSL has no built-in support for them...
172 if (mode == RsaHex || mode == RsaPubHex)
174 // unhexify the supplied key
175 WvDynBuf keybuf;
176 if (!WvHexDecoder().flush(encoded, keybuf, true) ||
177 keybuf.used() == 0)
179 debug("Couldn't unhexify RSA key.\n");
180 return;
183 size_t keylen = keybuf.used();
184 const unsigned char *key = keybuf.get(keylen);
186 // create the RSA struct
187 if (mode == RsaHex)
189 rsa = wv_d2i_RSAPrivateKey(NULL, &key, keylen);
190 priv = true;
192 else
193 rsa = wv_d2i_RSAPublicKey(NULL, &key, keylen);
195 return;
197 else
200 BIO *membuf = BIO_new(BIO_s_mem());
201 BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
203 if (mode == RsaPEM)
205 rsa = PEM_read_bio_RSAPrivateKey(membuf, NULL, NULL, NULL);
206 priv = true;
208 else if (mode == RsaPubPEM)
209 rsa = PEM_read_bio_RSAPublicKey(membuf, NULL, NULL, NULL);
210 else
211 debug(WvLog::Warning, "Should never happen: tried to encode RSA "
212 "key with unsupported mode.");
214 BIO_free_all(membuf);
219 /***** WvRSAEncoder *****/
221 WvRSAEncoder::WvRSAEncoder(Mode _mode, const WvRSAKey & _key) :
222 mode(_mode), key(_key)
224 if (key.isok() && key.rsa != NULL)
225 rsasize = RSA_size(key.rsa);
226 else
227 rsasize = 0; // BAD KEY! (should assert but would break compatibility)
231 WvRSAEncoder::~WvRSAEncoder()
236 bool WvRSAEncoder::_reset()
238 return true;
242 bool WvRSAEncoder::_encode(WvBuf &in, WvBuf &out, bool flush)
244 if (rsasize == 0)
246 // IGNORE BAD KEY!
247 in.zap();
248 return false;
251 bool success = true;
252 switch (mode)
254 case Encrypt:
255 case SignEncrypt:
257 // reserve space for PKCS1_PADDING
258 const size_t maxchunklen = rsasize - 12;
259 size_t chunklen;
260 while ((chunklen = in.used()) != 0)
262 if (chunklen >= maxchunklen)
263 chunklen = maxchunklen;
264 else if (! flush)
265 break;
267 // encrypt a chunk
268 const unsigned char *data = in.get(chunklen);
269 unsigned char *crypt = out.alloc(rsasize);
270 size_t cryptlen = (mode == Encrypt) ?
271 RSA_public_encrypt(chunklen,
272 const_cast<unsigned char*>(data), crypt,
273 key.rsa, RSA_PKCS1_PADDING) :
274 RSA_private_encrypt(chunklen,
275 const_cast<unsigned char*>(data), crypt,
276 key.rsa, RSA_PKCS1_PADDING);
277 if (cryptlen != rsasize)
279 out.unalloc(rsasize);
280 success = false;
283 break;
285 case Decrypt:
286 case SignDecrypt:
288 const size_t chunklen = rsasize;
289 while (in.used() >= chunklen)
291 // decrypt a chunk
292 const unsigned char *crypt = in.get(chunklen);
293 unsigned char *data = out.alloc(rsasize);
294 int cryptlen = (mode == Decrypt) ?
295 RSA_private_decrypt(chunklen,
296 const_cast<unsigned char*>(crypt), data,
297 key.rsa, RSA_PKCS1_PADDING) :
298 RSA_public_decrypt(chunklen,
299 const_cast<unsigned char*>(crypt), data,
300 key.rsa, RSA_PKCS1_PADDING);
301 if (cryptlen == -1)
303 out.unalloc(rsasize);
304 success = false;
306 else
307 out.unalloc(rsasize - cryptlen);
309 // flush does not make sense for us here
310 if (flush && in.used() != 0)
311 success = false;
312 break;
315 return success;
319 /***** WvRSAStream *****/
321 WvRSAStream::WvRSAStream(WvStream *_cloned,
322 const WvRSAKey &_my_key, const WvRSAKey &_their_key,
323 WvRSAEncoder::Mode readmode, WvRSAEncoder::Mode writemode) :
324 WvEncoderStream(_cloned)
326 readchain.append(new WvRSAEncoder(readmode, _my_key), true);
327 writechain.append(new WvRSAEncoder(writemode, _their_key), true);
328 if (_my_key.isok() && _my_key.rsa)
329 min_readsize = RSA_size(_my_key.rsa);