diff options
author | Slávek Banko <[email protected]> | 2013-07-27 16:34:45 +0200 |
---|---|---|
committer | Slávek Banko <[email protected]> | 2013-07-27 16:34:45 +0200 |
commit | d76ff81b7c1beffef0b84e570914c8f2d47834e6 (patch) | |
tree | 284b80ce7c5456fbb041f7979ac2c0baeead8902 /src/crypto.cpp | |
download | tork-d76ff81b7c1beffef0b84e570914c8f2d47834e6.tar.gz tork-d76ff81b7c1beffef0b84e570914c8f2d47834e6.zip |
Initial import of tork 0.33
Diffstat (limited to 'src/crypto.cpp')
-rw-r--r-- | src/crypto.cpp | 816 |
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 * + * [email protected] * + * * + * 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> > 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; +} |