From: Andre Noll Date: Sun, 16 Apr 2017 18:15:35 +0000 (+0200) Subject: Merge branch 'refs/heads/t/rm_rc4' X-Git-Tag: v0.6.0~11 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=a826152ba51fd4813f715e5fa30e7d70407dc846;hp=-c Merge branch 'refs/heads/t/rm_rc4' This patch removes support for RC4, making the AES-based stream cipher mandadory. The aes_ctr128 server feature is made a no-op, breaking support with very old clients (<= 0.5.1). Cooking for three months. * refs/heads/t/rm_rc4: crypt: Remove RC4 support. --- a826152ba51fd4813f715e5fa30e7d70407dc846 diff --combined NEWS.md index fb666529,d9326db7..08acd22c --- a/NEWS.md +++ b/NEWS.md @@@ -1,30 -1,9 +1,33 @@@ NEWS ==== +------------------------------------ +0.6.0 (to be announced) "fuzzy flux" +------------------------------------ +- Support for Mac OS X has been removed. +- On Linux systems, glibc-2.17 or newer is required to build the + source tree. +- Support for RSA public keys in ASN format (as generated by openssl + genrsa) has been removed. These keys have been deprecated since + 2011, so users should have long switched to keys generated with + ssh-keygen(1). +- If libgcrypt is used as the crypto library, we now require version + 1.5.0 (released in 2011) or later. ++- The insecure RC4 stream cipher has been removed. It was superseded ++ by aes_ctr128 three years ago but the RC4 code had been kept for ++ backwards compatibility. + +Downloads: +[tarball](./releases/paraslash-git.tar.bz2), + ------------------------------------- 0.5.7 (2016-12-31) "semantic density" ------------------------------------- + +Mostly a bug fix release, and a bunch of internal improvements. +The only user-visible changes are the sanity checks for the touch +command and the new options to the ls command. + - Speedup of the base64 decoder. - One of the two source browsers has been removed from the web pages. The doxygen API reference still contains an HTML version of each diff --combined crypt.c index 29a1c955,cae47046..f1e42d4a --- a/crypt.c +++ b/crypt.c @@@ -11,7 -11,6 +11,6 @@@ #include #include #include - #include #include #include #include @@@ -61,24 -60,41 +60,24 @@@ void init_random_seed_or_die(void srandom(seed); } -static EVP_PKEY *load_key(const char *file, int private) +static int get_private_key(const char *path, RSA **rsa) { - BIO *key; - EVP_PKEY *pkey = NULL; - int ret = check_key_file(file, private); + EVP_PKEY *pkey; + BIO *bio = BIO_new(BIO_s_file()); - if (ret < 0) { - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - return NULL; - } - key = BIO_new(BIO_s_file()); - if (!key) - return NULL; - if (BIO_read_filename(key, file) > 0) { - if (private == LOAD_PRIVATE_KEY) - pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL); - else - pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL); - } - BIO_free(key); - return pkey; -} - -static int get_openssl_key(const char *key_file, RSA **rsa, int private) -{ - EVP_PKEY *key = load_key(key_file, private); - - if (!key) - return (private == LOAD_PRIVATE_KEY)? -E_PRIVATE_KEY - : -E_PUBLIC_KEY; - *rsa = EVP_PKEY_get1_RSA(key); - EVP_PKEY_free(key); - if (!*rsa) - return -E_RSA; - return RSA_size(*rsa); + *rsa = NULL; + if (!bio) + return -E_PRIVATE_KEY; + if (BIO_read_filename(bio, path) <= 0) + goto bio_free; + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!pkey) + goto bio_free; + *rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); +bio_free: + BIO_free(bio); + return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY; } /* @@@ -143,7 -159,8 +142,7 @@@ fail return ret; } -int get_asymmetric_key(const char *key_file, int private, - struct asymmetric_key **result) +int get_public_key(const char *key_file, struct asymmetric_key **result) { struct asymmetric_key *key = NULL; void *map = NULL; @@@ -153,13 -170,21 +152,13 @@@ char *cp; key = para_malloc(sizeof(*key)); - if (private) { - ret = get_openssl_key(key_file, &key->rsa, LOAD_PRIVATE_KEY); - goto out; - } ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); if (ret < 0) goto out; ret = is_ssh_rsa_key(map, map_size); if (!ret) { - ret = para_munmap(map, map_size); - map = NULL; - if (ret < 0) - goto out; - ret = get_openssl_key(key_file, &key->rsa, LOAD_PUBLIC_KEY); - goto out; + para_munmap(map, map_size); + return -E_SSH_PARSE; } cp = map + ret; encoded_size = map_size - ret; @@@ -189,7 -214,7 +188,7 @@@ out return ret; } -void free_asymmetric_key(struct asymmetric_key *key) +void free_public_key(struct asymmetric_key *key) { if (!key) return; @@@ -203,17 -228,11 +202,17 @@@ int priv_decrypt(const char *key_file, struct asymmetric_key *priv; int ret; + ret = check_private_key_file(key_file); + if (ret < 0) + return ret; if (inlen < 0) return -E_RSA; - ret = get_asymmetric_key(key_file, LOAD_PRIVATE_KEY, &priv); - if (ret < 0) + priv = para_malloc(sizeof(*priv)); + ret = get_private_key(key_file, &priv->rsa); + if (ret < 0) { + free(priv); return ret; + } /* * RSA is vulnerable to timing attacks. Generate a random blinding * factor to protect against this kind of attack. @@@ -227,8 -246,7 +226,8 @@@ if (ret <= 0) ret = -E_DECRYPT; out: - free_asymmetric_key(priv); + RSA_free(priv->rsa); + free(priv); return ret; } @@@ -245,26 -263,16 +244,16 @@@ int pub_encrypt(struct asymmetric_key * } struct stream_cipher { - bool use_aes; - union { - RC4_KEY rc4_key; - EVP_CIPHER_CTX *aes; - } context; + EVP_CIPHER_CTX *aes; }; - struct stream_cipher *sc_new(const unsigned char *data, int len, - bool use_aes) + struct stream_cipher *sc_new(const unsigned char *data, int len) { struct stream_cipher *sc = para_malloc(sizeof(*sc)); - sc->use_aes = use_aes; - if (!use_aes) { - RC4_set_key(&sc->context.rc4_key, len, data); - return sc; - } assert(len >= 2 * AES_CRT128_BLOCK_SIZE); - sc->context.aes = EVP_CIPHER_CTX_new(); - EVP_EncryptInit_ex(sc->context.aes, EVP_aes_128_ctr(), NULL, data, + sc->aes = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data, data + AES_CRT128_BLOCK_SIZE); return sc; } @@@ -273,40 -281,10 +262,10 @@@ void sc_free(struct stream_cipher *sc { if (!sc) return; - EVP_CIPHER_CTX_free(sc->context.aes); + EVP_CIPHER_CTX_free(sc->aes); free(sc); } - /** - * The RC4() implementation of openssl apparently reads and writes data in - * blocks of 8 bytes. So we have to make sure our buffer sizes are a multiple - * of this. - */ - #define RC4_ALIGN 8 - - static void rc4_crypt(RC4_KEY *key, struct iovec *src, struct iovec *dst) - { - size_t len = src->iov_len, l1, l2; - - assert(len > 0); - assert(len < ((typeof(src->iov_len))-1) / 2); - l1 = ROUND_DOWN(len, RC4_ALIGN); - l2 = ROUND_UP(len, RC4_ALIGN); - - *dst = (typeof(*dst)) { - /* Add one for the terminating zero byte. */ - .iov_base = para_malloc(l2 + 1), - .iov_len = len - }; - RC4(key, l1, src->iov_base, dst->iov_base); - if (len > l1) { - unsigned char remainder[RC4_ALIGN] = ""; - memcpy(remainder, src->iov_base + l1, len - l1); - RC4(key, len - l1, remainder, dst->iov_base + l1); - } - ((char *)dst->iov_base)[len] = '\0'; - } - static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src, struct iovec *dst) { @@@ -328,9 -306,7 +287,7 @@@ void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst) { - if (sc->use_aes) - return aes_ctr128_crypt(sc->context.aes, src, dst); - return rc4_crypt(&sc->context.rc4_key, src, dst); + return aes_ctr128_crypt(sc->aes, src, dst); } void hash_function(const char *data, unsigned long len, unsigned char *hash) diff --combined crypt.h index 6f3befdb,f5f8aca6..5e25748c --- a/crypt.h +++ b/crypt.h @@@ -51,21 -51,22 +51,21 @@@ int priv_decrypt(const char *key_file, * Read an asymmetric key from a file. * * \param key_file The file containing the key. - * \param private if non-zero, read the private key, otherwise the public key. * \param result The key structure is returned here. * * \return The size of the key on success, negative on errors. */ -int get_asymmetric_key(const char *key_file, int private, - struct asymmetric_key **result); +int get_public_key(const char *key_file, struct asymmetric_key **result); /** - * Deallocate an asymmetric key structure. + * Deallocate a public key. * * \param key Pointer to the key structure to free. * - * This must be called for any key obtained by get_asymmetric_key(). + * This should be called for keys obtained by get_public_key() if the key is no + * longer needed. */ -void free_asymmetric_key(struct asymmetric_key *key); +void free_public_key(struct asymmetric_key *key); /** @@@ -118,16 -119,14 +118,14 @@@ struct stream_cipher_context }; /** - * Allocate and initialize a stream cipher structure. + * Allocate and initialize an aes_ctr128 stream cipher structure. * * \param data The key. * \param len The size of the key. - * \param use_aes True: Use the aes_ctr128 stream cipher, false: Use RC4. * * \return A new stream cipher structure. */ - struct stream_cipher *sc_new(const unsigned char *data, int len, - bool use_aes); + struct stream_cipher *sc_new(const unsigned char *data, int len); /** * Encrypt or decrypt a buffer using a stream cipher. diff --combined gcrypt.c index 0ba8d526,51b1b1f4..1fde479e --- a/gcrypt.c +++ b/gcrypt.c @@@ -19,6 -19,9 +19,6 @@@ //#define GCRYPT_DEBUG 1 -static bool libgcrypt_has_oaep; -static const char *rsa_decrypt_sexp; - #ifdef GCRYPT_DEBUG static void dump_buffer(const char *msg, unsigned char *buf, int len) { @@@ -60,25 -63,134 +60,25 @@@ void get_random_bytes_or_die(unsigned c * don't have to initialize any random seed here, but we must initialize the * gcrypt library. This task is performed by gcry_check_version() which can * also check that the gcrypt library version is at least the minimal required - * version. This function also tells us whether we have to use our own OAEP - * padding code. + * version. */ void init_random_seed_or_die(void) { - const char *ver, *req_ver; - - ver = gcry_check_version(NULL); - req_ver = "1.4.0"; - if (!gcry_check_version(req_ver)) { - PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n", - req_ver, ver); - exit(EXIT_FAILURE); - } - req_ver = "1.5.0"; - if (gcry_check_version(req_ver)) { - libgcrypt_has_oaep = true; - rsa_decrypt_sexp = "(enc-val(flags oaep)(rsa(a %m)))"; - } else { - libgcrypt_has_oaep = false; - rsa_decrypt_sexp = "(enc-val(rsa(a %m)))"; - } + const char *req_ver = "1.5.0"; + + if (gcry_check_version(req_ver)) + return; + PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n", + req_ver, gcry_check_version(NULL)); + exit(EXIT_FAILURE); } /** S-expression for the public part of an RSA key. */ #define RSA_PUBKEY_SEXP "(public-key (rsa (n %m) (e %m)))" /** S-expression for a private RSA key. */ #define RSA_PRIVKEY_SEXP "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))" - -/* rfc 3447, appendix B.2 */ -static void mgf1(unsigned char *seed, size_t seed_len, unsigned result_len, - unsigned char *result) -{ - gcry_error_t gret; - gcry_md_hd_t handle; - size_t n; - unsigned char *md; - unsigned char octet_string[4], *rp = result, *end = rp + result_len; - - assert(result_len / HASH_SIZE < 1ULL << 31); - gret = gcry_md_open(&handle, GCRY_MD_SHA1, 0); - assert(gret == 0); - for (n = 0; rp < end; n++) { - gcry_md_write(handle, seed, seed_len); - octet_string[0] = (unsigned char)((n >> 24) & 255); - octet_string[1] = (unsigned char)((n >> 16) & 255); - octet_string[2] = (unsigned char)((n >> 8)) & 255; - octet_string[3] = (unsigned char)(n & 255); - gcry_md_write(handle, octet_string, 4); - gcry_md_final(handle); - md = gcry_md_read(handle, GCRY_MD_SHA1); - memcpy(rp, md, PARA_MIN(HASH_SIZE, (int)(end - rp))); - rp += HASH_SIZE; - gcry_md_reset(handle); - } - gcry_md_close(handle); -} - -/** The sha1 hash of an empty file. */ -static const unsigned char empty_hash[HASH_SIZE] = - "\xda" "\x39" "\xa3" "\xee" "\x5e" - "\x6b" "\x4b" "\x0d" "\x32" "\x55" - "\xbf" "\xef" "\x95" "\x60" "\x18" - "\x90" "\xaf" "\xd8" "\x07" "\x09"; - -/* rfc3447, section 7.1.1 */ -static void pad_oaep(unsigned char *in, size_t in_len, unsigned char *out, - size_t out_len) -{ - size_t ps_len = out_len - in_len - 2 * HASH_SIZE - 2; - size_t n, mask_len = out_len - HASH_SIZE - 1; - unsigned char *seed = out + 1, *db = seed + HASH_SIZE, - *ps = db + HASH_SIZE, *one = ps + ps_len; - unsigned char *db_mask, seed_mask[HASH_SIZE]; - - assert(in_len <= out_len - 2 - 2 * HASH_SIZE); - assert(out_len > 2 * HASH_SIZE + 2); - PARA_DEBUG_LOG("padding %zu byte input -> %zu byte output\n", - in_len, out_len); - dump_buffer("unpadded buffer", in, in_len); - - out[0] = '\0'; - get_random_bytes_or_die(seed, HASH_SIZE); - memcpy(db, empty_hash, HASH_SIZE); - memset(ps, 0, ps_len); - *one = 0x01; - memcpy(one + 1, in, in_len); - db_mask = para_malloc(mask_len); - mgf1(seed, HASH_SIZE, mask_len, db_mask); - for (n = 0; n < mask_len; n++) - db[n] ^= db_mask[n]; - mgf1(db, mask_len, HASH_SIZE, seed_mask); - for (n = 0; n < HASH_SIZE; n++) - seed[n] ^= seed_mask[n]; - free(db_mask); - dump_buffer("padded buffer", out, out_len); -} - -/* rfc 3447, section 7.1.2 */ -static int unpad_oaep(unsigned char *in, size_t in_len, unsigned char *out, - size_t *out_len) -{ - unsigned char *masked_seed = in + 1; - unsigned char *db = in + 1 + HASH_SIZE; - unsigned char seed[HASH_SIZE], seed_mask[HASH_SIZE]; - unsigned char *db_mask, *p; - size_t n, mask_len = in_len - HASH_SIZE - 1; - - mgf1(db, mask_len, HASH_SIZE, seed_mask); - for (n = 0; n < HASH_SIZE; n++) - seed[n] = masked_seed[n] ^ seed_mask[n]; - db_mask = para_malloc(mask_len); - mgf1(seed, HASH_SIZE, mask_len, db_mask); - for (n = 0; n < mask_len; n++) - db[n] ^= db_mask[n]; - free(db_mask); - if (memcmp(db, empty_hash, HASH_SIZE)) - return -E_OEAP; - for (p = db + HASH_SIZE; p < in + in_len - 1; p++) - if (*p != '\0') - break; - if (p >= in + in_len - 1) - return -E_OEAP; - p++; - *out_len = in + in_len - p; - memcpy(out, p, *out_len); - return 1; -} +/** S-expression for decryption. */ +#define RSA_DECRYPT_SEXP "(enc-val(flags oaep)(rsa(a %m)))" struct asymmetric_key { gcry_sexp_t sexp; @@@ -189,6 -301,64 +189,6 @@@ static inline int get_long_form_num_len return c & 0x7f; } -static int find_pubkey_bignum_offset(const unsigned char *data, int len) -{ - const unsigned char *p = data, *end = data + len; - - /* the whole thing starts with one sequence */ - if (*p != ASN1_TYPE_SEQUENCE) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (is_short_form(*p)) - p++; - else - p += 1 + get_long_form_num_length_bytes(*p); - if (p >= end) - return -E_ASN1_PARSE; - /* another sequence containing the object id, skip it */ - if (*p != ASN1_TYPE_SEQUENCE) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (!is_short_form(*p)) - return -E_ASN1_PARSE; - p += 1 + get_short_form_length(*p); - if (p >= end) - return -E_ASN1_PARSE; - /* all numbers are wrapped in a bit string object that follows */ - if (*p != ASN1_TYPE_BIT_STRING) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (is_short_form(*p)) - p++; - else - p += 1 + get_long_form_num_length_bytes(*p); - p++; /* skip number of unused bits in the bit string */ - if (p >= end) - return -E_ASN1_PARSE; - - /* next, we have a sequence of two integers (n and e) */ - if (*p != ASN1_TYPE_SEQUENCE) - return -E_ASN1_PARSE; - p++; - if (p >= end) - return -E_ASN1_PARSE; - if (is_short_form(*p)) - p++; - else - p += 1 + get_long_form_num_length_bytes(*p); - if (p >= end) - return -E_ASN1_PARSE; - if (*p != ASN1_TYPE_INTEGER) - return -E_ASN1_PARSE; - return p - data; -} - /* * Returns: Number of bytes scanned. This may differ from the value returned via * bn_bytes because the latter does not include the ASN.1 prefix and a leading @@@ -290,7 -460,6 +290,7 @@@ static int get_private_key(const char * gcry_sexp_t sexp; struct asymmetric_key *key; + *result = NULL; ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER, &blob); if (ret < 0) @@@ -369,6 -538,65 +369,6 @@@ free_blob return ret; } -/** Public keys start with this header. */ -#define PUBLIC_KEY_HEADER "-----BEGIN PUBLIC KEY-----" -/** Public keys end with this footer. */ -#define PUBLIC_KEY_FOOTER "-----END PUBLIC KEY-----" - -static int get_asn_public_key(const char *key_file, struct asymmetric_key **result) -{ - gcry_mpi_t n = NULL, e = NULL; - unsigned char *blob, *cp, *end; - int blob_size, ret, n_size; - gcry_error_t gret; - size_t erroff; - gcry_sexp_t sexp; - struct asymmetric_key *key; - - ret = decode_key(key_file, PUBLIC_KEY_HEADER, PUBLIC_KEY_FOOTER, - &blob); - if (ret < 0) - return ret; - blob_size = ret; - end = blob + blob_size; - ret = find_pubkey_bignum_offset(blob, blob_size); - if (ret < 0) - goto free_blob; - PARA_DEBUG_LOG("decoding public RSA params at offset %d\n", ret); - cp = blob + ret; - - ret = read_bignum(cp, end, &n, &n_size); - if (ret < 0) - goto free_blob; - cp += ret; - - ret = read_bignum(cp, end, &e, NULL); - if (ret < 0) - goto release_n; - - gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e); - if (gret) { - PARA_ERROR_LOG("offset %zu: %s\n", erroff, - gcry_strerror(gcry_err_code(gret))); - ret = -E_SEXP_BUILD; - goto release_e; - } - key = para_malloc(sizeof(*key)); - key->sexp = sexp; - key->num_bytes = n_size; - *result = key; - ret = n_size; - PARA_INFO_LOG("successfully read %d bit asn public key\n", n_size * 8); - -release_e: - gcry_mpi_release(e); -release_n: - gcry_mpi_release(n); -free_blob: - free(blob); - return ret; -} - static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result) { int ret; @@@ -430,7 -658,8 +430,7 @@@ free_blob return ret; } -int get_asymmetric_key(const char *key_file, int private, - struct asymmetric_key **result) +int get_public_key(const char *key_file, struct asymmetric_key **result) { int ret, ret2; void *map; @@@ -439,13 -668,17 +439,13 @@@ gcry_sexp_t sexp; struct asymmetric_key *key; - if (private) - return get_private_key(key_file, result); ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); if (ret < 0) return ret; ret = is_ssh_rsa_key(map, map_size); if (!ret) { - ret = para_munmap(map, map_size); - if (ret < 0) - return ret; - return get_asn_public_key(key_file, result); + para_munmap(map, map_size); + return -E_SSH_PARSE; } start = map + ret; end = map + map_size; @@@ -466,7 -699,7 +466,7 @@@ unmap return ret; } -void free_asymmetric_key(struct asymmetric_key *key) +void free_public_key(struct asymmetric_key *key) { if (!key) return; @@@ -474,27 -707,78 +474,27 @@@ free(key); } -static int decode_rsa(gcry_sexp_t sexp, int key_size, unsigned char *outbuf, - size_t *nbytes) +static int decode_rsa(gcry_sexp_t sexp, unsigned char *outbuf, size_t *nbytes) { - int ret; - gcry_error_t gret; - unsigned char oaep_buf[512]; - gcry_mpi_t out_mpi; - - if (libgcrypt_has_oaep) { - const char *p = gcry_sexp_nth_data(sexp, 1, nbytes); - - if (!p) { - PARA_ERROR_LOG("could not get data from list\n"); - return -E_OEAP; - } - memcpy(outbuf, p, *nbytes); - return 1; - } - out_mpi = gcry_sexp_nth_mpi(sexp, 0, GCRYMPI_FMT_USG); - if (!out_mpi) - return -E_SEXP_FIND; - gret = gcry_mpi_print(GCRYMPI_FMT_USG, oaep_buf, sizeof(oaep_buf), - nbytes, out_mpi); - if (gret) { - PARA_ERROR_LOG("mpi_print: %s\n", gcrypt_strerror(gret)); - ret = -E_MPI_PRINT; - goto out_mpi_release; - } - /* - * An oaep-encoded buffer always starts with at least one zero byte. - * However, leading zeroes in an mpi are omitted in the output of - * gcry_mpi_print() when using the GCRYMPI_FMT_USG format. The - * alternative, GCRYMPI_FMT_STD, does not work either because here the - * leading zero(es) might also be omitted, depending on the value of - * the second byte. - * - * To circumvent this, we shift the oaep buffer to the right. But first - * we check that the buffer actually started with a zero byte, i.e. that - * nbytes < key_size. Otherwise a decoding error occurred. - */ - ret = -E_SEXP_DECRYPT; - if (*nbytes >= key_size) - goto out_mpi_release; - memmove(oaep_buf + key_size - *nbytes, oaep_buf, *nbytes); - memset(oaep_buf, 0, key_size - *nbytes); - - PARA_DEBUG_LOG("decrypted buffer before unpad (%d bytes):\n", - key_size); - dump_buffer("non-unpadded decrypted buffer", oaep_buf, key_size); - ret = unpad_oaep(oaep_buf, key_size, outbuf, nbytes); - if (ret < 0) - goto out_mpi_release; - PARA_DEBUG_LOG("decrypted buffer after unpad (%zu bytes):\n", - *nbytes); - dump_buffer("unpadded decrypted buffer", outbuf, *nbytes); - ret = 1; -out_mpi_release: - gcry_mpi_release(out_mpi); - return ret; + const char *p = gcry_sexp_nth_data(sexp, 1, nbytes); + + if (!p) + return -E_RSA_DECODE; + memcpy(outbuf, p, *nbytes); + return 1; } int priv_decrypt(const char *key_file, unsigned char *outbuf, unsigned char *inbuf, int inlen) { gcry_error_t gret; - int ret, key_size; + int ret; struct asymmetric_key *priv; gcry_mpi_t in_mpi = NULL; gcry_sexp_t in, out, priv_key; size_t nbytes; - ret = check_key_file(key_file, true); + ret = check_private_key_file(key_file); if (ret < 0) return ret; PARA_INFO_LOG("decrypting %d byte input\n", inlen); @@@ -502,6 -786,7 +502,6 @@@ ret = get_private_key(key_file, &priv); if (ret < 0) return ret; - key_size = ret / 8; /* asymmetric key priv -> sexp priv_key */ ret = -E_SEXP_FIND; @@@ -518,7 -803,7 +518,7 @@@ goto key_release; } /* in_mpi -> in sexp */ - gret = gcry_sexp_build(&in, NULL, rsa_decrypt_sexp, in_mpi); + gret = gcry_sexp_build(&in, NULL, RSA_DECRYPT_SEXP, in_mpi); if (gret) { PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); ret = -E_SEXP_BUILD; @@@ -532,7 -817,7 +532,7 @@@ ret = -E_SEXP_DECRYPT; goto in_release; } - ret = decode_rsa(out, key_size, outbuf, &nbytes); + ret = decode_rsa(out, outbuf, &nbytes); if (ret < 0) goto out_release; PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes); @@@ -546,8 -831,7 +546,8 @@@ in_mpi_release key_release: gcry_sexp_release(priv_key); free_key: - free_asymmetric_key(priv); + gcry_sexp_release(priv->sexp); + free(priv); return ret; } @@@ -566,7 -850,18 +566,7 @@@ int pub_encrypt(struct asymmetric_key * pub_key = gcry_sexp_find_token(pub->sexp, "public-key", 0); if (!pub_key) return -E_SEXP_FIND; - if (libgcrypt_has_oaep) { - gret = gcry_sexp_build(&in, NULL, - "(data(flags oaep)(value %b))", len, inbuf); - } else { - unsigned char padded_input[256]; - const size_t pad_size = 256; - /* inbuf -> padded inbuf */ - pad_oaep(inbuf, len, padded_input, pad_size); - /* padded inbuf -> in sexp */ - gret = gcry_sexp_build(&in, NULL, - "(data(flags raw)(value %b))", pad_size, padded_input); - } + gret = gcry_sexp_build(&in, NULL, "(data(flags oaep)(value %b))", len, inbuf); if (gret) { PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); ret = -E_SEXP_BUILD; @@@ -617,33 -912,20 +617,20 @@@ struct stream_cipher gcry_cipher_hd_t handle; }; - struct stream_cipher *sc_new(const unsigned char *data, int len, - bool use_aes) + struct stream_cipher *sc_new(const unsigned char *data, int len) { gcry_error_t gret; struct stream_cipher *sc = para_malloc(sizeof(*sc)); - if (use_aes) { - assert(len >= 2 * AES_CRT128_BLOCK_SIZE); - gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128, - GCRY_CIPHER_MODE_CTR, 0); - assert(gret == 0); - gret = gcry_cipher_setkey(sc->handle, data, - AES_CRT128_BLOCK_SIZE); - assert(gret == 0); - gret = gcry_cipher_setctr(sc->handle, - data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE); - assert(gret == 0); - return sc; - } - gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_ARCFOUR, - GCRY_CIPHER_MODE_STREAM, 0); - if (gret) { - PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); - free(sc); - return NULL; - } - gret = gcry_cipher_setkey(sc->handle, data, (size_t)len); + assert(len >= 2 * AES_CRT128_BLOCK_SIZE); + gret = gcry_cipher_open(&sc->handle, GCRY_CIPHER_AES128, + GCRY_CIPHER_MODE_CTR, 0); + assert(gret == 0); + gret = gcry_cipher_setkey(sc->handle, data, + AES_CRT128_BLOCK_SIZE); + assert(gret == 0); + gret = gcry_cipher_setctr(sc->handle, + data + AES_CRT128_BLOCK_SIZE, AES_CRT128_BLOCK_SIZE); assert(gret == 0); return sc; } diff --combined web/manual.md index e7bb4afc,461886a7..fef8123a --- a/web/manual.md +++ b/web/manual.md @@@ -152,8 -152,9 +152,8 @@@ an array of offsets within the audio fi ### para_write ### A modular audio stream writer. It supports a simple file writer -output plug-in and optional WAV/raw players for ALSA (Linux) and for -coreaudio (Mac OS). para_write can also be used as a stand-alone WAV -or raw audio player. +output plug-in and optional WAV/raw players for ALSA (Linux) and OSS. +para_write can also be used as a stand-alone WAV or raw audio player. ### para_play ### @@@ -447,9 -448,9 +447,9 @@@ User managemen para_server uses a challenge-response mechanism to authenticate requests from incoming connections, similar to ssh's public key authentication method. Authenticated connections are encrypted using - a stream cipher, either RC4 or AES in integer counter mode. + the AES stream cipher in integer counter mode. - In this chapter we briefly describe RSA, RC4 and AES, and sketch the + In this chapter we briefly describe RSA and AES, and sketch the [authentication handshake](#Client-server.authentication) between para_client and para_server. User management is discussed in the section on [the user_list file](#The.user_list.file). @@@ -457,33 -458,33 +457,33 @@@ These sections are all about communicat server. Connecting para_audiod is a different matter and is described in a [separate section](#Connecting.para_audiod). - RSA, RC4, AES - ------------- + RSA and AES + ----------- - RSA is an asymmetric block cipher which is used in many applications, - including ssh and gpg. An RSA key consists in fact of two keys, + A block cipher is a transformation which operates on fixed-length + blocks. For symmetric block ciphers the transformation is determined + by a single key for both encryption and decryption. For asymmetric + block ciphers, on the other hand, the key consists of two parts, called the public key and the private key. A message can be encrypted - with either key and only the counterpart of that key can decrypt - the message. While RSA can be used for both signing and encrypting - a message, paraslash uses RSA only for the latter purpose. The - RSA public key encryption and signatures algorithms are defined in - detail in RFC 2437. - - RC4 is a stream cipher, i.e. the input is XORed with a pseudo-random - key stream to produce the output. Decryption uses the same function - calls as encryption. While RC4 supports variable key lengths, - paraslash uses a fixed length of 256 bits, which is considered a - strong encryption by today's standards. Since the same key must never - be used twice, a different, randomly-generated key is used for every - new connection. + with either key and only the counterpart of that key can decrypt the + message. Asymmetric block ciphers can be used for both signing and + encrypting a message. + + RSA is an asymmetric block cipher which is used in many applications, + including ssh and gpg. The RSA public key encryption and signatures + algorithms are defined in detail in RFC 2437. Paraslash relies on + RSA for authentication. + + Stream ciphers XOR the input with a pseudo-random key stream to produce + the output. Decryption uses the same function calls as encryption. + Any block cipher can be turned into a stream cipher by generating the + pseudo-random key stream by encrypting successive values of a counter + (counter mode). AES, the advanced encryption standard, is a well-known symmetric block - cipher, i.e. a transformation operating on fixed-length blocks which - is determined by a single key for both encryption and decryption. Any - block cipher can be turned into a stream cipher by generating - a pseudo-random key stream by encrypting successive values of a - counter. The AES_CTR128 stream cipher used in paraslash is obtained - in this way from the AES block cipher with a 128 bit block size. + cipher. Paraslash employs AES in counter mode as described above to + encrypt communications. Since a stream cipher key must not be used + twice, a random key is generated for every new connection. Client-server authentication ---------------------------- @@@ -523,8 -524,8 +523,8 @@@ point on the communication is encrypte the session key known to both peers. paraslash relies on the quality of the pseudo-random bytes provided - by the crypto library (openssl or libgcrypt), on the security of the - implementation of the RSA, RC4 and AES crypto routines and on the + by the crypto library (openssl or libgcrypt), on the security of + the implementation of the RSA and AES crypto routines and on the infeasibility to invert the SHA1 function. Neither para_server or para_client create RSA keys on their @@@ -1699,6 -1700,10 +1699,6 @@@ emulation for backwards compatibility. also limited. For example only one application can open the device at any time. The OSS writer is activated by default on BSD Systems. -- *OSX*. Mac OS X has yet another API called CoreAudio. The OSX writer -for this API is only compiled in on such systems and is of course -the default there. - - *FILE*. The file writer allows to capture the audio stream and write the PCM data to a file on the file system rather than playing it through a sound device. It is supported on all platforms and is