crypto.cpp

Go to the documentation of this file.
00001 /*
00002  * crypto.cpp
00003  *
00004  * Copyright (C) 2007-2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of simple encryption helper functions.
00032  * See crypto.h
00033  */
00034 
00035 // includes --------------------------------------------------------------------
00036 #include "crypto.h"             // always include our own header file first!
00037 
00038 #include <openssl/des.h>
00039 #include <openssl/rsa.h>
00040 #include <openssl/sha.h>
00041 
00042 #include "common/wave_ex.h"
00043 #include "perf/perf.h"
00044 
00045 
00046 namespace crypto {
00047 
00048 
00049 static const int s_keyNumber            = 1536;
00050 static const long s_keyExponent         = 65537;
00051 static const int s_padding              = RSA_PKCS1_OAEP_PADDING;
00052 
00053 static const int32_t s_maxEncodeLength  = 0x007FFFFFF;
00054 
00055 static const int s_bytesPerDESBlock     = 8;
00056 
00057 typedef std::vector<byte_t> byte_vec_t;
00058 
00059 
00060 // interface virtual destructor implementations
00061 RSAPublicKey::~RSAPublicKey(void) throw() { }
00062 RSAKey::~RSAKey(void) throw() { }
00063 DESKey::~DESKey(void) throw() { }
00064 
00065 
00066 static char s_base64encode[65] =
00067         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
00068 
00069 static int s_base64decode[128];
00070 
00071 
00072 ////////////////////////////////////////////////////////////////////////////////
00073 //
00074 //      static helper methods
00075 //
00076 ////////////////////////////////////////////////////////////////////////////////
00077 
00078 static void
00079 initializeDecoding
00080 (
00081 void
00082 )
00083 throw()
00084 {
00085         for (int i = 0; i < 128; ++i) {
00086                 s_base64decode[i] = -1;
00087         }
00088         for (int i = 0; i < 26; ++i) {
00089                 s_base64decode['A' + i] = i;
00090                 s_base64decode['a' + i] = i + 26;
00091         }
00092         for (int i = 0; i < 10; ++i) {
00093                 s_base64decode['0' + i] = i + 52;
00094         }
00095         s_base64decode['-' + 0] = 62;
00096         s_base64decode['_' + 0] = 63;
00097 }
00098 
00099 
00100 
00101 static void
00102 encodeInt32
00103 (
00104 IO std::string& out,
00105 IN int32_t l
00106 )
00107 {
00108         ASSERT(l >= 0 && l < s_maxEncodeLength, "bad l: %ld", (long) l);
00109 
00110         // base64 these 3 bytes into 4 characters
00111         char buff[4];
00112         for (int i = 0; i < 4; ++i) {
00113                 int val = l % 64;
00114                 l = l / 64;
00115                 buff[i] = s_base64encode[val];
00116         }
00117 
00118         for (int i = 3; i >= 0; --i) {
00119                 out += buff[i];
00120         }
00121 }
00122 
00123 
00124 
00125 static int32_t
00126 decodeInt32
00127 (
00128 IN const char * p
00129 )
00130 {
00131         int32_t l = 0;
00132         for (int i = 0; i < 4; ++i, ++p) {
00133                 l = 64 * l;
00134                 l += s_base64decode[(int) *p];
00135         }
00136         return l;
00137 }
00138 
00139 
00140 
00141 /*
00142 static void
00143 dumpDCB
00144 (
00145 IN const char * title,
00146 IN const_DES_cblock * pdcb
00147 )
00148 {
00149         ASSERT(title, "null");
00150         ASSERT(pdcb, "null");
00151 
00152         DPRINTF("DES cblock: %s", title);
00153         for (int i = 0; i < s_bytesPerDESBlock; ++i) {
00154                 byte_t b = (*pdcb)[i];
00155                 char c = '?';
00156                 if (b > 'A' && b < 'z')
00157                         c = b;
00158                 DPRINTF("  byte[%d] = %3d ('%c')", i, b, c);
00159         }
00160 }
00161 */
00162 
00163 
00164 
00165 static long
00166 symmetricEncrypt
00167 (
00168 IN const byte_t * input,
00169 IN long bytes,
00170 IN DES_key_schedule * ks,
00171 IN int flag,
00172 OUT byte_vec_t& output
00173 )
00174 {
00175         ASSERT(input, "null");
00176         ASSERT(bytes > 0, "bad bytes: %ld", bytes);
00177         ASSERT(ks, "null");
00178         output.clear();
00179 
00180         // loop through input in N-byte blocks
00181         int remainder = bytes % s_bytesPerDESBlock;
00182         int nBlocks = bytes / s_bytesPerDESBlock;
00183 
00184         long output_size = ((nBlocks + 1) * s_bytesPerDESBlock) + 1;
00185         output.resize(output_size);
00186 
00187         long output_bytes = 0;
00188         byte_t * b = output.data();
00189         DES_cblock in_dcb;
00190         DES_cblock out_dcb;
00191         const byte_t * p = input;
00192         for (int i = 0; i < nBlocks; ++i, p += s_bytesPerDESBlock) {
00193                 // encrypt this block
00194                 DES_ecb_encrypt((const_DES_cblock *) p, &out_dcb, ks, flag);
00195                 //dumpDCB("output", &out_dcb);
00196 
00197                 // copy
00198                 memcpy(b, out_dcb, s_bytesPerDESBlock);
00199                 b += s_bytesPerDESBlock;
00200                 output_bytes += s_bytesPerDESBlock;
00201         }
00202 
00203         // final block
00204         if (remainder) {
00205                 byte_t * q = (byte_t *) in_dcb;
00206                 memcpy(q, p, remainder);
00207                 bzero(q + remainder, s_bytesPerDESBlock - remainder);
00208 //              dumpDCB("input", &in_dcb);
00209                 DES_ecb_encrypt(&in_dcb, &out_dcb, ks, flag);
00210                 memcpy(b, out_dcb, s_bytesPerDESBlock);
00211 //              dumpDCB("output", &out_dcb);
00212                 output_bytes += s_bytesPerDESBlock;
00213         }
00214 
00215         return output_bytes;
00216 }
00217 
00218 
00219 
00220 ////////////////////////////////////////////////////////////////////////////////
00221 //
00222 //      PublicKey -- class that implements the RSAPublicKey interface
00223 //
00224 ////////////////////////////////////////////////////////////////////////////////
00225 
00226 class PublicKey : public RSAPublicKey {
00227 public:
00228         PublicKey(void) throw();
00229         ~PublicKey(void) throw();
00230 
00231         // public class methods ------------------------------------------------
00232         void initialize(IN const char * serialized);
00233         void initialize(IN RSA * rsa);
00234 
00235         // crypto::PublicRSAKey class interface methods ------------------------
00236         std::string serialize(void);
00237         std::string encrypt(IN const char * plaintext);
00238 
00239 private:
00240         // private member data -------------------------------------------------
00241         RSA *           m_rsa;
00242 };
00243 
00244 
00245 
00246 PublicKey::PublicKey
00247 (
00248 void
00249 )
00250 throw()
00251 {
00252         m_rsa = NULL;
00253 }
00254 
00255 
00256 PublicKey::~PublicKey
00257 (
00258 void
00259 )
00260 throw()
00261 {
00262         if (m_rsa) {
00263                 RSA_free(m_rsa);
00264         }
00265 }
00266 
00267 
00268 
00269 void
00270 PublicKey::initialize
00271 (
00272 IN const char * serialized
00273 )
00274 {
00275         ASSERT(serialized, "null");
00276 
00277 //      DPRINTF("SERIALIZED: %s", serialized);
00278 
00279         // create empty key
00280         m_rsa = RSA_new();
00281         ASSERT(m_rsa, "failed to create empty RSA key");
00282         ASSERT(!m_rsa->n, "already have n?");
00283         ASSERT(!m_rsa->e, "already have e?");
00284 
00285         // attempt to extract N and E
00286         // (see PublicKey::serialize below for format, basically "n:e")
00287         const int buffsize = 1024;
00288         char buffer[buffsize];
00289         const char * s = strstr(serialized, ":");
00290         if (!s) {
00291                 WAVE_EX(wex);
00292                 wex << "Malformed serialized key";
00293         }
00294         int iN = s - serialized;
00295         if (iN < 1 || iN >= buffsize) {
00296                 WAVE_EX(wex);
00297                 wex << "Malformed serialized key";
00298         }
00299         strncpy(buffer, serialized, iN);
00300         buffer[iN] = 0;
00301 //      DPRINTF("N = %s", buffer);
00302         BN_hex2bn(&m_rsa->n, buffer);
00303 
00304         serialized += iN + 1;
00305         iN = strlen(serialized);
00306         if (iN < 1 || iN >= buffsize) {
00307                 WAVE_EX(wex);
00308                 wex << "Malformed serialized key";
00309         }
00310         strcpy(buffer, serialized);
00311 //      DPRINTF("e = %s", buffer);
00312         BN_hex2bn(&m_rsa->e, buffer);
00313 }
00314 
00315 
00316 
00317 void
00318 PublicKey::initialize
00319 (
00320 IN RSA * rsa
00321 )
00322 {
00323         ASSERT(rsa, "null");
00324         ASSERT(!m_rsa, "already have a key?");
00325         m_rsa = rsa;
00326 }
00327 
00328 
00329 
00330 std::string
00331 PublicKey::serialize
00332 (
00333 void
00334 )
00335 {
00336         std::string public_key;
00337 
00338         // WOW the RSA libraries SUCK
00339         // there is NO mechanism for getting/serializing the public key
00340         // of an RSA key.  Somehow they expect you to magically extract the
00341         // public bits of the key (!!??).  At the moment I don't have internet
00342         // access, so I'm basing this on the man documentation.  But it is
00343         // amazingly useless.
00344 
00345         // So I have rolled my OWN public key serialization.  Hopefully this
00346         // can be replaced by the real solution
00347         ASSERT(m_rsa, "null");
00348         ASSERT(m_rsa->n, "null");
00349         ASSERT(m_rsa->e, "null");
00350 
00351         char * n = BN_bn2hex(m_rsa->n);         // must be free()'d!
00352         char * e = BN_bn2hex(m_rsa->e);         // must be free()'d!
00353 
00354 //      DPRINTF("n = '%s'", n);
00355 //      DPRINTF("e = '%s'", e);
00356 
00357         public_key += n;
00358         public_key += ":";
00359         public_key += e;
00360 
00361         OPENSSL_free(n);
00362         OPENSSL_free(e);
00363 
00364         // that's it!
00365         return public_key;
00366 }
00367 
00368 
00369 
00370 std::string
00371 PublicKey::encrypt
00372 (
00373 IN const char * plaintext
00374 )
00375 {
00376         perf::Timer timer("crypto::RSAPublicKey::encrypt");
00377         ASSERT(m_rsa, "null");
00378         ASSERT(plaintext, "null");
00379 
00380         long bytes = strlen(plaintext);
00381 
00382         // construct array
00383         int rbytes = RSA_size(m_rsa);
00384 //      DPRINTF("encryption requires %d bytes", rbytes);
00385         ASSERT(bytes > 0, "bad byte count? %d", rbytes);
00386 
00387         std::vector<byte_t> out;
00388         out.resize(rbytes + 1);
00389 
00390         int ebytes = RSA_public_encrypt(bytes, (byte_t *) plaintext,
00391             out.data(), m_rsa, s_padding);
00392 
00393 //      DPRINTF("Encrypted %ld bytes, resulted in %d bytes",
00394 //          bytes, ebytes);
00395 
00396         if (ebytes < 1) {
00397                 WAVE_EX(wex);
00398                 wex << "Failed public key encryption";
00399         }
00400 
00401         return encodeBase64(out.data(), ebytes);
00402 }
00403 
00404 
00405 
00406 ////////////////////////////////////////////////////////////////////////////////
00407 //
00408 //      Key -- class that implements the RSAKey interface
00409 //
00410 ////////////////////////////////////////////////////////////////////////////////
00411 
00412 class Key : public RSAKey {
00413 public:
00414         Key(void) throw();
00415         ~Key(void) throw();
00416 
00417         // public class methods ------------------------------------------------
00418         void initialize(void);
00419 
00420         // crypto::RSAKey class interface methods ------------------------------
00421         smart_ptr<RSAPublicKey> getPublicKey(void);
00422         std::string decrypt(IN const char * encrypted);
00423 
00424 private:
00425         // private member data -------------------------------------------------
00426         RSA *           m_rsa;
00427 };
00428 
00429 
00430 
00431 Key::Key
00432 (
00433 void
00434 )
00435 throw()
00436 {
00437         m_rsa = NULL;
00438 }
00439 
00440 
00441 
00442 Key::~Key
00443 (
00444 void
00445 )
00446 throw()
00447 {
00448         if (m_rsa) {
00449                 RSA_free(m_rsa);
00450         }
00451 }
00452 
00453 
00454 
00455 void
00456 Key::initialize
00457 (
00458 void
00459 )
00460 {
00461         ASSERT(!m_rsa, "already have a key?");
00462         m_rsa = RSA_generate_key(s_keyNumber, s_keyExponent, NULL, NULL);
00463         ASSERT(m_rsa, "failed to generate RSA key");
00464 }
00465 
00466 
00467 
00468 smart_ptr<RSAPublicKey>
00469 Key::getPublicKey
00470 (
00471 void
00472 )
00473 {
00474         // See previous note.  Basically, RSA libraries suck.
00475         // There is no supported way to extract the public key (!?)
00476         // so I have rolled my own.  Sigh.
00477 
00478         RSA * rsa = RSA_new();
00479         ASSERT(rsa, "failed to create empty RSA key");
00480         ASSERT(!rsa->n, "already have n?");
00481         ASSERT(!rsa->e, "already have e?");
00482 
00483         // copy just the public parts (n and e)
00484         rsa->n = BN_dup(m_rsa->n);
00485         rsa->e = BN_dup(m_rsa->e);
00486         ASSERT(rsa->n, "null");
00487         ASSERT(rsa->e, "null");
00488 
00489         // okay, return public key from that
00490         smart_ptr<PublicKey> pkey = new PublicKey();
00491         ASSERT(pkey, "out of memory");
00492         pkey->initialize(rsa);          // public key takes ownership of rsa
00493         return pkey;
00494 }
00495 
00496 
00497 
00498 std::string
00499 Key::decrypt
00500 (
00501 IN const char * encrypted
00502 )
00503 {
00504         perf::Timer timer("crypto::RSAKey::decrypt");
00505         ASSERT(m_rsa, "null");
00506         ASSERT(encrypted, "null");
00507 
00508         byte_vec_t data;
00509         decodeBase64(encrypted, data);
00510 
00511         int ebytes = data.size();
00512 //      DPRINTF("Decoded string results in %d bytes of data", ebytes);
00513 
00514         byte_vec_t out;
00515         out.resize(RSA_size(m_rsa));
00516 
00517         int bytes = RSA_private_decrypt(ebytes, data.data(),
00518             out.data(), m_rsa, s_padding);
00519 //      DPRINTF("Decryption results in %d bytes", bytes);
00520         if (bytes < 1) {
00521                 WAVE_EX(wex);
00522                 wex << "Failed to decrypt data";
00523         }
00524 
00525         return (const char *) out.data();
00526 }
00527 
00528 
00529 
00530 ////////////////////////////////////////////////////////////////////////////////
00531 //
00532 //      SymKey - class that implements the DESKey interface
00533 //
00534 ////////////////////////////////////////////////////////////////////////////////
00535 
00536 class SymKey : public DESKey {
00537 public:
00538         SymKey(void) throw();
00539         ~SymKey(void) throw();
00540 
00541         // public class methods ------------------------------------------------
00542         void initialize(void);
00543         void initialize(IN const char * serialized);
00544 
00545         // crypto::DESKey class interface methods -----------------------------
00546         std::string serialize(void);
00547         std::string encrypt(IN const char * plaintext, IN int minSize);
00548         std::string decrypt(IN const char * encrypted);
00549 
00550 private:
00551         // private member data -------------------------------------------------
00552         DES_cblock              m_dcb;
00553         DES_key_schedule        m_sched;
00554 };
00555 
00556 
00557 
00558 SymKey::SymKey
00559 (
00560 void
00561 )
00562 throw()
00563 {
00564 }
00565 
00566 
00567 
00568 SymKey::~SymKey
00569 (
00570 void
00571 )
00572 throw()
00573 {
00574 }
00575 
00576 
00577 
00578 void
00579 SymKey::initialize
00580 (
00581 void
00582 )
00583 {
00584         // see the des documentation ("man DES_random_key")
00585         // have to create a key, then a schedule
00586         DES_random_key(&m_dcb);
00587 
00588         // create schedule
00589         if (DES_set_key_checked(&m_dcb, &m_sched)) {
00590                 WAVE_EX(wex);
00591                 wex << "Failed to create symmetric key (schedule)";
00592         }
00593 
00594 //      dumpDCB("new", &m_dcb);
00595 }
00596 
00597 
00598 
00599 void
00600 SymKey::initialize
00601 (
00602 IN const char * serialized
00603 )
00604 {
00605         ASSERT(serialized, "null");
00606 
00607         byte_vec_t data;
00608         decodeBase64(serialized, data);
00609         if (s_bytesPerDESBlock != (int) data.size()) {
00610                 WAVE_EX(wex);
00611                 wex << "Improperly encoded DES key";
00612         }
00613 
00614         memcpy(m_dcb, data.data(), s_bytesPerDESBlock);
00615         if (DES_set_key_checked(&m_dcb, &m_sched)) {
00616                 WAVE_EX(wex);
00617                 wex << "Failed to create symmetric key (schedule)";
00618         }
00619 
00620 //      dumpDCB("remote", &m_dcb);
00621 }
00622 
00623 
00624 
00625 std::string
00626 SymKey::serialize
00627 (
00628 void
00629 )
00630 {
00631         const byte_t * b = (const byte_t *) m_dcb;
00632 
00633         return encodeBase64(b, s_bytesPerDESBlock);
00634 }
00635 
00636 
00637 
00638 std::string
00639 SymKey::encrypt
00640 (
00641 IN const char * plaintext,
00642 IN int minSize
00643 )
00644 {
00645         perf::Timer timer("crypto::DESKey::encrypt");
00646         ASSERT(plaintext, "null");
00647         long bytes = strlen(plaintext);
00648         ASSERT(bytes >= 0, "Bad bytes: %ld", bytes);
00649 
00650         //DPRINTF("Encrypting '%s'", plaintext);
00651 
00652         // pad if necessary
00653         std::string base;
00654         int nPad = minSize - bytes;
00655         if (minSize > 0 && nPad > 0) {
00656                 for (int i = 0; i < nPad; ++i) {
00657                         base += 'a' + (rand() % 26);
00658                 }
00659                 bytes += nPad;
00660         }
00661         base += ':';
00662         bytes += 1;             // account for colon
00663         base += plaintext;
00664 
00665         //DPRINTF("  '%s'", base.c_str());
00666         //DPRINTF("  %ld bytes", bytes);
00667 
00668         // encrypt
00669         byte_vec_t raw;
00670         long obytes = symmetricEncrypt((const byte_t *) base.c_str(), bytes,
00671             &m_sched, DES_ENCRYPT, raw);
00672         //DPRINTF("  %ld output bytes", obytes);
00673 
00674         // convert
00675         return encodeBase64(raw.data(), obytes);
00676 }
00677 
00678 
00679 
00680 std::string
00681 SymKey::decrypt
00682 (
00683 IN const char * encrypted
00684 )
00685 {
00686         perf::Timer timer("crypto::DESKey::decrypt");
00687         ASSERT(encrypted, "null");
00688 
00689         //DPRINTF("Decrypting '%s'", encrypted);
00690 
00691         // decode first
00692         byte_vec_t raw;
00693         decodeBase64(encrypted, raw);
00694 
00695         long bytes = raw.size();
00696         ASSERT(bytes > 0, "Bad bytes: %ld", bytes);
00697         //DPRINTF("  Found %ld bytes", bytes);
00698 
00699         // decrypt
00700         byte_vec_t output;
00701         symmetricEncrypt(raw.data(), bytes, &m_sched, DES_DECRYPT,
00702             output);
00703         output.push_back(0);    // null-terminate
00704 
00705         // skip padding
00706         const char * p = (const char *) output.data();
00707         //DPRINTF("  '%s'", p);
00708         while (*p && *p != ':') { ++p; }
00709         if (!*p) {
00710                 WAVE_EX(wex);
00711                 wex << "Improperly encrypted string?";
00712         }
00713         ++p;    // skip colon
00714         //DPRINTF("  '%s'", p);
00715 
00716         // all done
00717         return p;
00718 }
00719 
00720 
00721 
00722 ////////////////////////////////////////////////////////////////////////////////
00723 //
00724 //      public API
00725 //
00726 ////////////////////////////////////////////////////////////////////////////////
00727 
00728 smart_ptr<RSAPublicKey>
00729 RSAPublicKey::create
00730 (
00731 IN const char * serialized
00732 )
00733 {
00734         perf::Timer timer("crypto::RSAPublicKey::create");
00735 
00736         smart_ptr<PublicKey> local = new PublicKey;
00737         ASSERT(local, "out of memory");
00738 
00739         local->initialize(serialized);
00740 
00741         return local;
00742 }
00743 
00744 
00745 
00746 smart_ptr<RSAKey>
00747 RSAKey::create
00748 (
00749 void
00750 )
00751 {
00752         perf::Timer timer("crypto::RSAKey::create");
00753 
00754         smart_ptr<Key> local = new Key;
00755         ASSERT(local, "out of memory");
00756 
00757         local->initialize();
00758 
00759         return local;
00760 }
00761 
00762 
00763 
00764 smart_ptr<DESKey>
00765 DESKey::create
00766 (
00767 void
00768 )
00769 {
00770         perf::Timer timer("crypto::DESKey::create");
00771 
00772         smart_ptr<SymKey> local = new SymKey;
00773         ASSERT(local, "out of memory");
00774 
00775         local->initialize();
00776 
00777         return local;
00778 }
00779 
00780 
00781 
00782 smart_ptr<DESKey>
00783 DESKey::create
00784 (
00785 IN const char * serialized
00786 )
00787 {
00788         smart_ptr<SymKey> local = new SymKey;
00789         ASSERT(local, "out of memory");
00790         local->initialize(serialized);
00791         return local;
00792 }
00793 
00794 
00795 
00796 std::string
00797 getSHA1
00798 (
00799 IN const char * data
00800 )
00801 {
00802         perf::Timer timer("crypto::getSHA1");
00803         ASSERT(data, "null");
00804 
00805         const int bufSize = SHA_DIGEST_LENGTH;
00806         byte_t buffer[bufSize];
00807         int length = strlen(data);
00808 
00809         SHA1((const unsigned char *) data, length, buffer);
00810         return encodeBase64(buffer, SHA_DIGEST_LENGTH, false);
00811 }
00812 
00813 
00814 
00815 std::string
00816 encodeBase64
00817 (
00818 IN const byte_t * data,
00819 IN long bytes,
00820 IN bool encodeLength
00821 )
00822 {
00823         perf::Timer timer("crypto::encodeBase64");
00824         ASSERT(data, "null");
00825         ASSERT(bytes > 0, "bad bytes: %ld", bytes);
00826         if (bytes >= s_maxEncodeLength) {
00827                 WAVE_EX(wex);
00828                 wex << "Cannot encode " << bytes << " bytes.";
00829                 wex << "  Max encode length is " << s_maxEncodeLength;
00830         }
00831 
00832         // uses a simple base64 encoding
00833         // uses "modified base64" so it is not necessary to URL encode
00834         // see http://en.wikipedia.org/wiki/Base64
00835 
00836         std::string out;
00837 
00838         // if you don't encode the length, it can't be decoded!
00839         if (encodeLength) {
00840                 // DPRINTF("Encoding %ld bytes", bytes);
00841                 encodeInt32(out, bytes);
00842         }
00843 
00844         char buf[4];
00845         buf[3] = 0;     // most significant byte is empty
00846         const byte_t * p = data;
00847         while (bytes > 0) {
00848                 // walk through data in chunks of 3 bytes
00849                 // pad with last valid byte if necessary
00850                 for (int i = 0; i < 3; ++i, --bytes) {
00851                         buf[i] = *p;
00852                         if (bytes > 1) {
00853                                 ++p;
00854                         }
00855                 }
00856 
00857                 int32_t * pl = (int32_t *) &buf;
00858                 int32_t l = *pl;
00859                 ASSERT(l >= 0, "Bad l: %ld", (long) l);
00860 //              DPRINTF("l = 0x%08lx", l);
00861                 encodeInt32(out, l);
00862         }
00863 //      DPRINTF("out: %s", out.c_str());
00864         return out;
00865 }
00866 
00867 
00868 
00869 void
00870 decodeBase64
00871 (
00872 IN const char * encoded,
00873 OUT byte_vec_t& data
00874 )
00875 {
00876         perf::Timer timer("crypto::decodeBase64");
00877         data.clear();
00878 
00879         int N = strlen(encoded);
00880         if (N % 4) {
00881                 WAVE_EX(wex);
00882                 wex << "Improperly encoded data (bad length)";
00883         }
00884 
00885         static bool s_init = false;
00886         if (!s_init) {
00887                 initializeDecoding();
00888                 s_init = true;
00889         }
00890 
00891         const char * p = encoded;
00892         int32_t bytes = decodeInt32(p);
00893         // DPRINTF("Decoding %ld bytes", bytes);
00894         p += 4;
00895 
00896         // decrypt 4 characters at a time, populate 3 bytes
00897         for (; N > 0; N -= 4) {
00898 
00899                 int32_t l = decodeInt32(p);
00900                 p += 4;
00901                 byte_t * pb = (byte_t *) &l;
00902                 for (int i = 0; i < 3; ++i ) {
00903                         if (bytes > 0) {
00904                                 data.push_back(pb[i]);
00905                                 bytes--;
00906                         }
00907                 }
00908         }
00909         // DPRINTF("  Decoded %d bytes", data.size());
00910 }
00911 
00912 
00913 
00914 };      // crypto namespace
00915