/*************************************************************************** ** $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 #include #include "crypto.h" #include "../config.h" #ifndef USE_OPENSSL #include #include #else #include #include #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include /* 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 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 to. 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 len 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 fd to buf, until we get count bytes * or reach the end of the file. isSocket 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 SIZE_T_CEILING) return -1; end = src+srclen; while (srcd,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 out. * out_len 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 len bytes from data 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 * secret_len-byte secret into a key_out_len byte * key_out. 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 key_out_len > 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; }