You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tork/src/crypto.cpp

817 lines
20 KiB

/***************************************************************************
** $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 TQMap<TQString, TQString> 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();
}
TQString fp_identity(const TQString &server)
{
return serverTofp_identity[server];
}
TQString server(const TQString &fp_identity)
{
return fp_identityToServer[fp_identity];
}
void storeServer(const TQString &server,const TQString &fp_identity)
{
serverTofp_identity[server] = fp_identity;
fp_identityToServer[fp_identity] = server;
}
TQString getFPDigestFromFP(const TQString &fp)
{
char identity64[BASE64_DIGEST_LEN+1];
char digest[DIGEST_LEN];
TQString FP = fp;
FP.replace("$","");
base16_decode(digest, DIGEST_LEN, FP, strlen(FP));
digest_to_base64(identity64, digest);
return identity64;
}
TQString getNickNameFromFPDigest(const TQString &fpdigest)
{
return fp_identityToServer[fpdigest];
}
TQString getNickNameFromFP(const TQString &fp)
{
TQString fpdigest = getFPDigestFromFP(fp);
return fp_identityToServer[fpdigest];
}
TQString getFPFromNickName(const TQString &nickname)
{
char buf[256];
char hexdigest[HEX_DIGEST_LEN+1];
TQString fp = serverTofp_identity[nickname];
if (fp.isEmpty())
return TQString();
if (!digest_from_base64(buf, fp))
base16_encode(hexdigest, HEX_DIGEST_LEN+1, buf, DIGEST_LEN);
return hexdigest;
}
TQString getFPFromFPDigest(const TQString &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). */
TQString
crypto_rand_string(int len)
{
TQString str;
Q_ASSERT(len >= 0);
for (int i = 0; i < len; i++)
str += TQChar('!' + 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. **/
TQString 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;
}