WvStreams
wvrsa.cc
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"
14
15/***** WvRSAKey *****/
16
17WvRSAKey::WvRSAKey()
18 : debug("RSA", WvLog::Debug5)
19{
20 rsa = NULL;
21}
22
23
24WvRSAKey::WvRSAKey(const WvRSAKey &k)
25 : debug("RSA", WvLog::Debug5)
26{
27 priv = k.priv;
28
29 if (!priv)
30 rsa = RSAPublicKey_dup(k.rsa);
31 else
32 rsa = RSAPrivateKey_dup(k.rsa);
33}
34
35
36WvRSAKey::WvRSAKey(struct rsa_st *_rsa, bool _priv)
37 : debug("RSA", WvLog::Debug5)
38{
39 if (_rsa == NULL)
40 {
41 rsa = NULL;
42 debug("Initializing with a NULL key.. are you insane?\n");
43 return;
44 }
45
46 rsa = _rsa;
47 priv = _priv;
48}
49
50
51WvRSAKey::WvRSAKey(WvStringParm keystr, bool _priv)
52 : debug("RSA", WvLog::Debug5)
53{
54 rsa = NULL;
55
56 if (_priv)
57 decode(RsaHex, keystr);
58 else
59 decode(RsaPubHex, keystr);
60
61 priv = _priv;
62}
63
64
65WvRSAKey::WvRSAKey(int bits)
66 : debug("RSA", WvLog::Debug5)
67{
68 rsa = RSA_generate_key(bits, 0x10001, NULL, NULL);
69 priv = true;
70}
71
72
73WvRSAKey::~WvRSAKey()
74{
75 if (rsa)
76 RSA_free(rsa);
77}
78
79
80bool WvRSAKey::isok() const
81{
82 return rsa && (!priv || RSA_check_key(rsa) == 1);
83}
84
85
87{
88 WvString nil;
89 WvDynBuf retval;
90 encode(mode, retval);
91 return retval.getstr();
92}
93
94
95void WvRSAKey::encode(const DumpMode mode, WvBuf &buf) const
96{
97 if (!rsa)
98 {
99 debug(WvLog::Warning, "Tried to encode RSA key, but RSA key is "
100 "blank!\n");
101 return;
102 }
103
104 if (mode == RsaHex || mode == RsaPubHex)
105 {
106 WvDynBuf keybuf;
107
108 if (mode == RsaHex && priv)
109 {
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);
114 }
115 else
116 {
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);
121 }
122
123 buf.putstr(WvString(WvHexEncoder().strflushbuf(keybuf, true)));
124 }
125 else
126 {
127 BIO *bufbio = BIO_new(BIO_s_mem());
128 BUF_MEM *bm;
129 const EVP_CIPHER *enc = EVP_get_cipherbyname("rsa");
130
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.");
139
140 BIO_get_mem_ptr(bufbio, &bm);
141 buf.put(bm->data, bm->length);
142 BIO_free(bufbio);
143 }
144}
145
146
147void WvRSAKey::decode(const DumpMode mode, WvStringParm encoded)
148{
149 if (!encoded)
150 return;
151
152 WvDynBuf buf;
153 buf.putstr(encoded);
154 decode(mode, buf);
155}
156
157
158void WvRSAKey::decode(const DumpMode mode, WvBuf &encoded)
159{
160 debug("Decoding RSA key.\n");
161
162 if (rsa)
163 {
164 debug("Replacing already existent RSA key.\n");
165 RSA_free(rsa);
166 rsa = NULL;
167 }
168 priv = false;
169
170 // we handle hexified keys a bit differently, since
171 // OpenSSL has no built-in support for them...
172 if (mode == RsaHex || mode == RsaPubHex)
173 {
174 // unhexify the supplied key
175 WvDynBuf keybuf;
176 if (!WvHexDecoder().flush(encoded, keybuf, true) ||
177 keybuf.used() == 0)
178 {
179 debug("Couldn't unhexify RSA key.\n");
180 return;
181 }
182
183 size_t keylen = keybuf.used();
184 const unsigned char *key = keybuf.get(keylen);
185
186 // create the RSA struct
187 if (mode == RsaHex)
188 {
189 rsa = wv_d2i_RSAPrivateKey(NULL, &key, keylen);
190 priv = true;
191 }
192 else
193 rsa = wv_d2i_RSAPublicKey(NULL, &key, keylen);
194
195 return;
196 }
197 else
198 {
199
200 BIO *membuf = BIO_new(BIO_s_mem());
201 BIO_write(membuf, encoded.get(encoded.used()), encoded.used());
202
203 if (mode == RsaPEM)
204 {
205 rsa = PEM_read_bio_RSAPrivateKey(membuf, NULL, NULL, NULL);
206 priv = true;
207 }
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.");
213
214 BIO_free_all(membuf);
215 }
216}
217
218
219/***** WvRSAEncoder *****/
220
222 mode(_mode), key(_key)
223{
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)
228}
229
230
231WvRSAEncoder::~WvRSAEncoder()
232{
233}
234
235
237{
238 return true;
239}
240
241
242bool WvRSAEncoder::_encode(WvBuf &in, WvBuf &out, bool flush)
243{
244 if (rsasize == 0)
245 {
246 // IGNORE BAD KEY!
247 in.zap();
248 return false;
249 }
250
251 bool success = true;
252 switch (mode)
253 {
254 case Encrypt:
255 case SignEncrypt:
256 {
257 // reserve space for PKCS1_PADDING
258 const size_t maxchunklen = rsasize - 12;
259 size_t chunklen;
260 while ((chunklen = in.used()) != 0)
261 {
262 if (chunklen >= maxchunklen)
263 chunklen = maxchunklen;
264 else if (! flush)
265 break;
266
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)
278 {
279 out.unalloc(rsasize);
280 success = false;
281 }
282 }
283 break;
284 }
285 case Decrypt:
286 case SignDecrypt:
287 {
288 const size_t chunklen = rsasize;
289 while (in.used() >= chunklen)
290 {
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)
302 {
303 out.unalloc(rsasize);
304 success = false;
305 }
306 else
307 out.unalloc(rsasize - cryptlen);
308 }
309 // flush does not make sense for us here
310 if (flush && in.used() != 0)
311 success = false;
312 break;
313 }
314 }
315 return success;
316}
317
318
319/***** WvRSAStream *****/
320
321WvRSAStream::WvRSAStream(WvStream *_cloned,
322 const WvRSAKey &_my_key, const WvRSAKey &_their_key,
323 WvRSAEncoder::Mode readmode, WvRSAEncoder::Mode writemode) :
324 WvEncoderStream(_cloned)
325{
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);
330}
const T * get(size_t count)
Reads exactly the specified number of elements and returns a pointer to a storage location owned by t...
Definition wvbufbase.h:114
T * alloc(size_t count)
Allocates exactly the specified number of elements and returns a pointer to an UNINITIALIZED storage ...
Definition wvbufbase.h:379
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition wvbufbase.h:92
WvEncoderStream chains a series of encoders on the input and output ports of the underlying stream to...
bool flush(WvBuf &inbuf, WvBuf &outbuf, bool finish=false)
Flushes the encoder and optionally finishes it.
Definition wvencoder.h:163
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
A hex decoder.
Definition wvhex.h:54
A hex encoder.
Definition wvhex.h:22
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition wvlog.h:57
An encoder implementing the RSA public key encryption method.
Definition wvrsa.h:85
virtual bool _encode(WvBuf &in, WvBuf &out, bool flush)
Template method implementation of encode().
Definition wvrsa.cc:242
WvRSAEncoder(Mode mode, const WvRSAKey &key)
Creates a new RSA cipher encoder.
Definition wvrsa.cc:221
@ SignEncrypt
Definition wvrsa.h:90
@ SignDecrypt
Definition wvrsa.h:91
virtual bool _reset()
Template method implementation of reset().
Definition wvrsa.cc:236
An RSA public key or public/private key pair that can be used for encryption.
Definition wvrsa.h:27
virtual WvString encode(const DumpMode mode) const
Return the information requested by mode.
Definition wvrsa.cc:86
DumpMode
Type for the encode() and decode() methods.
Definition wvrsa.h:36
virtual void decode(const DumpMode mode, WvStringParm encoded)
Load the information from the format requested by mode into the class - this overwrites the certifica...
Definition wvrsa.cc:147
Unified support for streams, that is, sequences of bytes that may or may not be ready for read/write ...
Definition wvstream.h:25
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330
Hex functions for compatibility with older code.