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 
17 WvRSAKey::WvRSAKey()
18  : debug("RSA", WvLog::Debug5)
19 {
20  rsa = NULL;
21 }
22 
23 
24 WvRSAKey::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 
36 WvRSAKey::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 
51 WvRSAKey::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 
65 WvRSAKey::WvRSAKey(int bits)
66  : debug("RSA", WvLog::Debug5)
67 {
68  rsa = RSA_generate_key(bits, 0x10001, NULL, NULL);
69  priv = true;
70 }
71 
72 
73 WvRSAKey::~WvRSAKey()
74 {
75  if (rsa)
76  RSA_free(rsa);
77 }
78 
79 
80 bool 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 
95 void 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 
147 void 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 
158 void 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 
231 WvRSAEncoder::~WvRSAEncoder()
232 {
233 }
234 
235 
237 {
238  return true;
239 }
240 
241 
242 bool 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 
321 WvRSAStream::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 }
WvRSAEncoder::WvRSAEncoder
WvRSAEncoder(Mode mode, const WvRSAKey &key)
Creates a new RSA cipher encoder.
Definition: wvrsa.cc:221
wvhex.h
WvBufBaseCommonImpl::get
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
WvEncoderStream
WvEncoderStream chains a series of encoders on the input and output ports of the underlying stream to...
Definition: wvencoderstream.h:37
WvRSAEncoder::Encrypt
@ Encrypt
Definition: wvrsa.h:88
WvBufBaseCommonImpl::alloc
T * alloc(size_t count)
Allocates exactly the specified number of elements and returns a pointer to an UNINITIALIZED storage ...
Definition: wvbufbase.h:379
WvBufBaseCommonImpl::unalloc
void unalloc(size_t count)
Unallocates exactly the specified number of elements by removing them from the buffer and releasing t...
Definition: wvbufbase.h:421
WvRSAKey::encode
virtual WvString encode(const DumpMode mode) const
Return the information requested by mode.
Definition: wvrsa.cc:86
WvBufBase< unsigned char >::getstr
WvString getstr()
Returns the entire buffer as a null-terminated WvString.
Definition: wvbuffer.cc:17
WvRSAEncoder::Mode
Mode
Definition: wvrsa.h:87
WvBufBase< unsigned char >::putstr
void putstr(WvStringParm str)
Copies a WvString into the buffer, excluding the null-terminator.
Definition: wvbuffer.cc:11
WvRSAEncoder::SignDecrypt
@ SignDecrypt
Definition: wvrsa.h:91
WvRSAEncoder::SignEncrypt
@ SignEncrypt
Definition: wvrsa.h:90
WvRSAKey
An RSA public key or public/private key pair that can be used for encryption.
Definition: wvrsa.h:26
WvRSAKey::DumpMode
DumpMode
Type for the encode() and decode() methods.
Definition: wvrsa.h:36
WvRSAEncoder::_reset
virtual bool _reset()
Template method implementation of reset().
Definition: wvrsa.cc:236
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvRSAEncoder::_encode
virtual bool _encode(WvBuf &in, WvBuf &out, bool flush)
Template method implementation of encode().
Definition: wvrsa.cc:242
WvLog
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:56
WvRSAKey::decode
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
WvBufBase< unsigned char >
Specialization of WvBufBase for unsigned char type buffers intended for use with raw memory buffers.
Definition: wvbuf.h:22
WvRSAEncoder::Decrypt
@ Decrypt
Definition: wvrsa.h:89
WvBufBaseCommonImpl::zap
void zap()
Clears the buffer.
Definition: wvbufbase.h:257
WvHexEncoder
A hex encoder.
Definition: wvhex.h:21
WvHexDecoder
A hex decoder.
Definition: wvhex.h:53
WvStream
Unified support for streams, that is, sequences of bytes that may or may not be ready for read/write ...
Definition: wvstream.h:24
WvEncoder::flush
bool flush(WvBuf &inbuf, WvBuf &outbuf, bool finish=false)
Flushes the encoder and optionally finishes it.
Definition: wvencoder.h:163
WvDynBufBase< unsigned char >
WvBufBaseCommonImpl::used
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92