summaryrefslogtreecommitdiffstats
path: root/src/crypto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto.cpp')
-rw-r--r--src/crypto.cpp816
1 files changed, 816 insertions, 0 deletions
diff --git a/src/crypto.cpp b/src/crypto.cpp
new file mode 100644
index 0000000..a47e19d
--- /dev/null
+++ b/src/crypto.cpp
@@ -0,0 +1,816 @@
+/***************************************************************************
+** $Id: crypto.cpp,v 1.11 2008/07/31 19:56:26 hoganrobert Exp $
+ * Copyright (C) 2006 - 2008 Robert Hogan *
+ * robert@roberthogan.net *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
+ *
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ ***************************************************************************/
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. */
+/* See Tor LICENSE for licensing information */
+/* $Id: crypto.cpp,v 1.11 2008/07/31 19:56:26 hoganrobert Exp $ */
+#include <string.h>
+#include <kdebug.h>
+#include "crypto.h"
+#include "../config.h"
+
+#ifndef USE_OPENSSL
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+#else
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/opensslv.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/rsa.h>
+#include <openssl/dh.h>
+#include <openssl/conf.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdint.h>
+
+/* random numbers */
+
+/* This is how much entropy OpenSSL likes to add right now, so maybe it will
+ * work for us too. */
+#define ADD_ENTROPY 32
+
+
+typedef QMap<QString, QString> servermap;
+servermap serverTofp_identity;
+servermap fp_identityToServer;
+
+/** Intermediate information about the digest of a stream of data. */
+struct crypto_digest_env_t {
+#ifndef USE_OPENSSL
+ gcry_md_hd_t d;
+#else
+ SHA_CTX d;
+#endif
+};
+
+
+void clearServers()
+{
+ serverTofp_identity.clear();
+ fp_identityToServer.clear();
+}
+
+QString fp_identity(const QString &server)
+{
+
+ return serverTofp_identity[server];
+}
+
+QString server(const QString &fp_identity)
+{
+
+ return fp_identityToServer[fp_identity];
+}
+
+void storeServer(const QString &server,const QString &fp_identity)
+{
+
+ serverTofp_identity[server] = fp_identity;
+ fp_identityToServer[fp_identity] = server;
+
+}
+
+
+
+QString getFPDigestFromFP(const QString &fp)
+{
+ char identity64[BASE64_DIGEST_LEN+1];
+ char digest[DIGEST_LEN];
+ QString FP = fp;
+ FP.replace("$","");
+
+ base16_decode(digest, DIGEST_LEN, FP, strlen(FP));
+ digest_to_base64(identity64, digest);
+ return identity64;
+}
+
+QString getNickNameFromFPDigest(const QString &fpdigest)
+{
+
+ return fp_identityToServer[fpdigest];
+
+}
+
+QString getNickNameFromFP(const QString &fp)
+{
+ QString fpdigest = getFPDigestFromFP(fp);
+
+ return fp_identityToServer[fpdigest];
+}
+
+
+
+QString getFPFromNickName(const QString &nickname)
+{
+ char buf[256];
+ char hexdigest[HEX_DIGEST_LEN+1];
+
+ QString fp = serverTofp_identity[nickname];
+
+ if (fp.isEmpty())
+ return QString();
+ if (!digest_from_base64(buf, fp))
+ base16_encode(hexdigest, HEX_DIGEST_LEN+1, buf, DIGEST_LEN);
+
+ return hexdigest;
+}
+
+QString getFPFromFPDigest(const QString &fp)
+{
+ char buf[256];
+ char hexdigest[HEX_DIGEST_LEN+1];
+
+ digest_from_base64(buf, fp);
+ base16_encode(hexdigest, HEX_DIGEST_LEN+1, buf, DIGEST_LEN);
+
+ return hexdigest;
+}
+
+
+/** Seed OpenSSL's random number generator with bytes from the
+ * operating system. Return 0 on success, -1 on failure.
+ */
+int
+crypto_seed_rng(void)
+{
+ char buf[ADD_ENTROPY];
+ int rand_poll_status;
+
+ /* local variables */
+ static const char *filenames[] = {
+ "/dev/srandom", "/dev/urandom", "/dev/random", NULL
+ };
+ int fd;
+ int i, n;
+
+ rand_poll_status = 0;
+
+ for (i = 0; filenames[i]; ++i) {
+ fd = open(filenames[i], O_RDONLY, 0);
+ if (fd<0) continue;
+ n = read_all(fd, buf, sizeof(buf), 0);
+ close(fd);
+ if (n != sizeof(buf)) {
+ return -1;
+ }
+#ifndef USE_OPENSSL
+ gcry_create_nonce(buf, sizeof(buf));
+#else
+ RAND_seed(buf, sizeof(buf));
+#endif
+ return 0;
+ }
+
+ return rand_poll_status ? 0 : -1;
+}
+
+/** Write n bytes of strong random data to <b>to</b>. Return 0 on
+ * success, -1 on failure.
+ */
+int
+crypto_rand(char *to, size_t n)
+{
+ assert(to);
+#ifndef USE_OPENSSL
+ gcry_randomize((unsigned char*)to,n,GCRY_STRONG_RANDOM);
+ return 0;
+#else
+ int r;
+ r = RAND_bytes((unsigned char*)to, n);
+ return (r == 1) ? 0 : -1;
+#endif
+}
+
+/** Return a pseudorandom integer, chosen uniformly from the values
+ * between 0 and max-1. */
+int
+crypto_rand_int(unsigned int max)
+{
+ unsigned int val;
+ unsigned int cutoff;
+ assert(max < UINT_MAX);
+ assert(max > 0); /* don't div by 0 */
+
+ /* We ignore any values that are >= 'cutoff,' to avoid biasing the
+ * distribution with clipping at the upper end of unsigned int's
+ * range.
+ */
+ cutoff = UINT_MAX - (UINT_MAX%max);
+ while (1) {
+ crypto_rand((char*)&val, sizeof(val));
+ if (val < cutoff)
+ return val % max;
+ }
+}
+
+/** Generates a pseudorandom string of length <b>len</b> containing printable
+ * ASCII characters from the range '!' (0x21) to '~' (0x7e). */
+QString
+crypto_rand_string(int len)
+{
+ QString str;
+ Q_ASSERT(len >= 0);
+
+ for (int i = 0; i < len; i++)
+ str += QChar('!' + crypto_rand_int('~'-'!'+1));
+ return str;
+}
+
+/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes
+ * or reach the end of the file. <b>isSocket</b> must be 1 if fd
+ * was returned by socket() or accept(), and 0 if fd was returned by
+ * open(). Return the number of bytes read, or -1 on error. Only use
+ * if fd is a blocking fd. */
+int
+read_all(int fd, char *buf, size_t count, int isSocket)
+{
+ size_t numread = 0;
+ int result;
+
+ if (count > SIZE_T_CEILING)
+ return -1;
+
+ while (numread != count) {
+ if (isSocket)
+ result = recv(fd, buf+numread, count-numread, 0);
+ else
+ result = read(fd, buf+numread, count-numread);
+ if (result<0)
+ return -1;
+ else if (result == 0)
+ break;
+ numread += result;
+ }
+ return numread;
+}
+
+
+int
+digest_from_base64(char *digest, const char *d64)
+{
+
+ char buf_in[BASE64_DIGEST_LEN+3];
+ char buf[256];
+ if (strlen(d64) != BASE64_DIGEST_LEN)
+ return -1;
+ memcpy(buf_in, d64, BASE64_DIGEST_LEN);
+#ifndef USE_OPENSSL
+ memcpy(buf_in+BASE64_DIGEST_LEN, "=\0", 2);
+ if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST_LEN)
+ return -1;
+#else
+ memcpy(buf_in+BASE64_DIGEST_LEN, "=\n\0", 3);
+ if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST_LEN)
+ return -1;
+
+#endif
+
+
+ memcpy(digest, buf, DIGEST_LEN);
+ return 0;
+}
+
+int
+base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+
+#ifndef USE_OPENSSL
+// gnutls_datum_t data_in;
+ base64_decodestate state;
+#else
+ EVP_ENCODE_CTX ctx;
+ int len;
+#endif
+ int ret;
+
+ /* 64 bytes of input -> *up to* 48 bytes of output.
+ Plus one more byte, in case I'm wrong.
+ */
+ if (destlen < ((srclen/64)+1)*49)
+ return -1;
+ if (destlen > SIZE_T_CEILING)
+ return -1;
+
+#ifndef USE_OPENSSL
+/* data_in.data = (unsigned char*)src;
+ data_in.size = srclen;*/
+ base64_init_decodestate(&state);
+#endif
+
+#ifndef USE_OPENSSL
+// if (gnutls_srp_base64_decode(&data_in, dest, &destlen)
+// == GNUTLS_E_SHORT_MEMORY_BUFFER)
+// kdDebug() << "error decoding " << endl;
+// kdDebug() << "decoded " << dest << "len" << destlen << endl;
+
+ ret = base64_decode_block(src, srclen, dest, &state);
+ return ret;
+#else
+ EVP_DecodeInit(&ctx);
+ EVP_DecodeUpdate(&ctx, (unsigned char*)dest, &len,
+ (unsigned char*)src, srclen);
+ EVP_DecodeFinal(&ctx, (unsigned char*)dest, &ret);
+ ret += len;
+ return ret;
+#endif
+
+}
+
+int
+digest_to_base64(char *d64, const char *digest)
+{
+ char buf[256];
+ base64_encode(buf, sizeof(buf), digest, DIGEST_LEN);
+ buf[BASE64_DIGEST_LEN] = '\0';
+ memcpy(d64, buf, BASE64_DIGEST_LEN+1);
+ return 0;
+}
+
+int
+base64_encode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+#ifndef USE_OPENSSL
+// gnutls_datum_t data_in;
+ base64_encodestate state;
+#else
+ EVP_ENCODE_CTX ctx;
+ int len;
+#endif
+ int ret;
+
+
+ /* 48 bytes of input -> 64 bytes of output plus newline.
+ Plus one more byte, in case I'm wrong.
+ */
+ if (destlen < ((srclen/48)+1)*66)
+ return -1;
+ if (destlen > SIZE_T_CEILING)
+ return -1;
+
+#ifndef USE_OPENSSL
+/* data_in.data = (unsigned char*)src;
+ data_in.size = srclen;*/
+ base64_init_encodestate(&state);
+#endif
+
+#ifndef USE_OPENSSL
+// gnutls_srp_base64_encode(&data_in, dest, &destlen);
+// kdDebug() << "encoded " << dest << "len" << destlen << endl;
+// return destlen;
+ ret = base64_encode_block(src, srclen, dest, &state);
+ ret += base64_encode_blockend(dest+ret, &state);
+
+ return ret;
+#else
+ EVP_EncodeInit(&ctx);
+ EVP_EncodeUpdate(&ctx, (unsigned char*)dest, &len,
+ (unsigned char*)src, srclen);
+ EVP_EncodeFinal(&ctx, (unsigned char*)(dest+len), &ret);
+ ret += len;
+ return ret;
+#endif
+
+}
+
+static const char HEX_DIGITS[] = "0123456789ABCDEFabcdef";
+
+static int hex_decode_digit(char c)
+{
+ const char *cp;
+ int n;
+ cp = strchr(HEX_DIGITS, c);
+ if (!cp)
+ return -1;
+ n = cp-HEX_DIGITS;
+ if (n<=15)
+ return n; /* digit or uppercase */
+ else
+ return n-6; /* lowercase */
+}
+
+
+void
+base16_encode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+ const char *end;
+ char *cp;
+
+ assert(destlen >= srclen*2+1);
+ assert(destlen < SIZE_T_CEILING);
+
+ cp = dest;
+ end = src+srclen;
+ while (src<end) {
+ sprintf(cp,"%02X",*(const uint8_t*)src);
+ ++src;
+ cp += 2;
+ }
+ *cp = '\0';
+}
+
+
+int
+base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
+{
+ const char *end;
+ int v1,v2;
+ if ((srclen % 2) != 0)
+ return -1;
+ if (destlen < srclen/2 || destlen > SIZE_T_CEILING)
+ return -1;
+ end = src+srclen;
+ while (src<end) {
+ v1 = hex_decode_digit(*src);
+ v2 = hex_decode_digit(*(src+1));
+ if (v1<0||v2<0)
+ return -1;
+ *(uint8_t*)dest = (v1<<4)|v2;
+ ++dest;
+ src+=2;
+ }
+ return 0;
+}
+
+
+/** Deallocate a digest object.
+ */
+void
+crypto_free_digest_env(crypto_digest_env_t *digest)
+{
+ free(digest);
+}
+
+/** Allocate and return a new digest object.
+ */
+ crypto_digest_env_t *
+crypto_new_digest_env(void)
+{
+ crypto_digest_env_t *r;
+ r = (crypto_digest_env_t *)malloc(sizeof(crypto_digest_env_t));
+
+#ifndef USE_OPENSSL
+ gcry_md_open(&r->d,GCRY_MD_SHA1, 0);
+#else
+ SHA1_Init(&r->d);
+#endif
+ return r;
+}
+
+/** Compute the hash of the data that has been passed to the digest
+ * object; write the first out_len bytes of the result to <b>out</b>.
+ * <b>out_len</b> must be \<= DIGEST_LEN.
+ */
+void
+crypto_digest_get_digest(crypto_digest_env_t *digest,
+ char *out, size_t out_len)
+{
+
+#ifdef USE_OPENSSL
+ static unsigned char r[DIGEST_LEN];
+#else
+ unsigned char* r;
+#endif
+ assert(digest);
+ assert(out);
+ assert(out_len <= DIGEST_LEN);
+#ifdef USE_OPENSSL
+ SHA_CTX tmpctx;
+#endif
+
+#ifndef USE_OPENSSL
+ r = gcry_md_read(digest->d, GCRY_MD_SHA1);
+ memcpy(out, r, out_len);
+ gcry_md_close(digest->d);
+#else
+ /* memcpy into a temporary ctx, since SHA1_Final clears the context */
+ memcpy(&tmpctx, &digest->d, sizeof(SHA_CTX));
+ SHA1_Final(r, &tmpctx);
+ memcpy(out, r, out_len);
+#endif
+
+}
+
+/** Add <b>len</b> bytes from <b>data</b> to the digest object.
+ */
+void
+crypto_digest_add_bytes(crypto_digest_env_t *digest, const char *data,
+ size_t len)
+{
+ assert(digest);
+ assert(data);
+ /* Using the SHA1_*() calls directly means we don't support doing
+ * sha1 in hardware. But so far the delay of getting the question
+ * to the hardware, and hearing the answer, is likely higher than
+ * just doing it ourselves. Hashes are fast.
+ */
+#ifndef USE_OPENSSL
+ gcry_md_write(digest->d, data, len);
+#else
+ SHA1_Update(&digest->d, (void*)data, len);
+#endif
+
+}
+
+/** Implement RFC2440-style iterated-salted S2K conversion: convert the
+ * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
+ * <b>key_out</b>. As in RFC2440, the first 8 bytes of s2k_specifier
+ * are a salt; the 9th byte describes how much iteration to do.
+ * Does not support <b>key_out_len</b> &gt; DIGEST_LEN.
+ */
+void
+secret_to_key(char *key_out, size_t key_out_len, const char *secret,
+ size_t secret_len, const char *s2k_specifier)
+{
+ crypto_digest_env_t *d;
+ uint8_t c;
+ size_t count;
+ char *tmp;
+ assert(key_out_len < SIZE_T_CEILING);
+
+#define EXPBIAS 6
+ c = s2k_specifier[8];
+ count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS);
+#undef EXPBIAS
+
+ assert(key_out_len <= DIGEST_LEN);
+
+ d = crypto_new_digest_env();
+ tmp = (char *)malloc(8+secret_len);
+ memcpy(tmp,s2k_specifier,8);
+ memcpy(tmp+8,secret,secret_len);
+ secret_len += 8;
+ while (count) {
+ if (count >= secret_len) {
+ crypto_digest_add_bytes(d, tmp, secret_len);
+ count -= secret_len;
+ } else {
+ crypto_digest_add_bytes(d, tmp, count);
+ count = 0;
+ }
+ }
+ crypto_digest_get_digest(d, key_out, key_out_len);
+ free(tmp);
+ crypto_free_digest_env(d);
+}
+
+
+/** Entry point for password hashing: take the desired password from
+ * the command line, and print its salted hash to stdout. **/
+QString hashPassword(const char* secret)
+{
+
+ char output[256];
+ char key[S2K_SPECIFIER_LEN+DIGEST_LEN];
+
+ crypto_rand(key, S2K_SPECIFIER_LEN-1);
+ key[S2K_SPECIFIER_LEN-1] = (uint8_t)96; /* Hash 64 K of data. */
+ secret_to_key(key+S2K_SPECIFIER_LEN, DIGEST_LEN,
+ secret, strlen(secret),
+ key);
+ base16_encode(output, sizeof(output), key, sizeof(key));
+ kdDebug() << output << endl;
+ return output;
+}
+
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+int base64_decode_value(char value_in)
+{
+ static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ static const char decoding_size = sizeof(decoding);
+ value_in -= 43;
+ if (value_in < 0 || value_in > decoding_size) return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in)
+{
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
+{
+ const char* codechar = code_in;
+ char* plainchar = plaintext_out;
+ char fragment;
+
+ *plainchar = state_in->plainchar;
+
+ switch (state_in->step)
+ {
+ while (1)
+ {
+ case step_a:
+ do {
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_a;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar = (fragment & 0x03f) << 2;
+ case step_b:
+ do {
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_b;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar++ |= (fragment & 0x030) >> 4;
+ *plainchar = (fragment & 0x00f) << 4;
+ case step_c:
+ do {
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_c;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar++ |= (fragment & 0x03c) >> 2;
+ *plainchar = (fragment & 0x003) << 6;
+ case step_d:
+ do {
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_d;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while (fragment < 0);
+ *plainchar++ |= (fragment & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
+
+/*
+cencoder.c - c source to a base64 encoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+
+const int CHARS_PER_LINE = 72;
+
+void base64_init_encodestate(base64_encodestate* state_in)
+{
+ state_in->step = step_A;
+ state_in->result = 0;
+ state_in->stepcount = 0;
+}
+
+char base64_encode_value(char value_in)
+{
+ static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ if (value_in > 63) return '=';
+ return encoding[(int)value_in];
+}
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
+{
+ const char* plainchar = plaintext_in;
+ const char* const plaintextend = plaintext_in + length_in;
+ char* codechar = code_out;
+ char result;
+ char fragment;
+
+ result = state_in->result;
+
+ switch (state_in->step)
+ {
+ while (1)
+ {
+ case step_A:
+ if (plainchar == plaintextend)
+ {
+ state_in->result = result;
+ state_in->step = step_A;
+ return codechar - code_out;
+ }
+ fragment = *plainchar++;
+ result = (fragment & 0x0fc) >> 2;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x003) << 4;
+ case step_B:
+ if (plainchar == plaintextend)
+ {
+ state_in->result = result;
+ state_in->step = step_B;
+ return codechar - code_out;
+ }
+ fragment = *plainchar++;
+ result |= (fragment & 0x0f0) >> 4;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x00f) << 2;
+ case step_C:
+ if (plainchar == plaintextend)
+ {
+ state_in->result = result;
+ state_in->step = step_C;
+ return codechar - code_out;
+ }
+ fragment = *plainchar++;
+ result |= (fragment & 0x0c0) >> 6;
+ *codechar++ = base64_encode_value(result);
+ result = (fragment & 0x03f) >> 0;
+ *codechar++ = base64_encode_value(result);
+
+ ++(state_in->stepcount);
+ if (state_in->stepcount == CHARS_PER_LINE/4)
+ {
+ *codechar++ = '\n';
+ state_in->stepcount = 0;
+ }
+ }
+ }
+ /* control should not reach here */
+ return codechar - code_out;
+}
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
+{
+ char* codechar = code_out;
+
+ switch (state_in->step)
+ {
+ case step_B:
+ *codechar++ = base64_encode_value(state_in->result);
+/* *codechar++ = '=';
+ *codechar++ = '=';*/
+ break;
+ case step_C:
+ *codechar++ = base64_encode_value(state_in->result);
+/* *codechar++ = '=';*/
+ break;
+ case step_A:
+ break;
+ }
+/* *codechar++ = '\n';*/
+
+ return codechar - code_out;
+}