X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=gcrypt.c;h=f825f6c6a3de6388959d7b900f31ef9d48e6397b;hp=0de619595f362788111eb24654a2c5749b74b782;hb=c0abcee0da53a6b399c3d16a62830aaa9ae21349;hpb=56e2e6b938e7e3d318964f095ffcabc151411446 diff --git a/gcrypt.c b/gcrypt.c index 0de61959..f825f6c6 100644 --- a/gcrypt.c +++ b/gcrypt.c @@ -7,90 +7,969 @@ /** \file gcrypt.c Libgrcypt-based encryption/decryption routines. */ #include -#include -#include +#include +#include #include "para.h" #include "error.h" #include "string.h" #include "crypt.h" +#include "crypt_backend.h" #include "fd.h" -struct asymmetric_key { - int x; -}; +//#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) +{ + int i; + + fprintf(stderr, "%s (%u bytes): ", msg, len); + for (i = 0; i < len; i++) + fprintf(stderr, "%02x ", buf[i]); + fprintf(stderr, "\n"); +} +#else +/** Empty. Define GCRYPT_DEBUG to dump buffers. */ +#define dump_buffer(a, b, c) +#endif + +void hash_function(const char *data, unsigned long len, unsigned char *hash) +{ + gcry_error_t gret; + gcry_md_hd_t handle; + unsigned char *md; + + gret = gcry_md_open(&handle, GCRY_MD_SHA1, 0); + assert(gret == 0); + gcry_md_write(handle, data, (size_t)len); + gcry_md_final(handle); + md = gcry_md_read(handle, GCRY_MD_SHA1); + assert(md); + memcpy(hash, md, HASH_SIZE); + gcry_md_close(handle); +} void get_random_bytes_or_die(unsigned char *buf, int num) { + gcry_randomize(buf, (size_t)num, GCRY_STRONG_RANDOM); } +/* + * This is called at the beginning of every program that uses libgcrypt. We + * 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. + */ 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)))"; + } +} + +/** 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; +} + +struct asymmetric_key { + gcry_sexp_t sexp; + int num_bytes; +}; + +static const char *gcrypt_strerror(gcry_error_t gret) +{ + return gcry_strerror(gcry_err_code(gret)); +} + +static int decode_key(const char *key_file, const char *header_str, + const char *footer_str, unsigned char **result) +{ + int ret, ret2, i, j; + void *map; + size_t map_size, key_size, blob_size; + unsigned char *blob = NULL; + char *begin, *footer, *key; + + ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL); + if (ret < 0) + return ret; + ret = -E_KEY_MARKER; + if (strncmp(map, header_str, strlen(header_str))) + goto unmap; + footer = strstr(map, footer_str); + ret = -E_KEY_MARKER; + if (!footer) + goto unmap; + begin = map + strlen(header_str); + /* skip whitespace at the beginning */ + for (; begin < footer; begin++) { + if (para_isspace(*begin)) + continue; + break; + } + ret = -E_KEY_MARKER; + if (begin >= footer) + goto unmap; + + key_size = footer - begin; + key = para_malloc(key_size + 1); + for (i = 0, j = 0; begin + i < footer; i++) { + if (para_isspace(begin[i])) + continue; + key[j++] = begin[i]; + } + key[j] = '\0'; + //PARA_CRIT_LOG("key: %s\n", key); + blob_size = key_size * 2; + blob = para_malloc(blob_size); + ret = base64_decode(key, blob, blob_size); + free(key); + if (ret < 0) + goto free_unmap; + goto unmap; +free_unmap: + free(blob); + blob = NULL; +unmap: + ret2 = para_munmap(map, map_size); + if (ret >= 0 && ret2 < 0) + ret = ret2; + if (ret < 0) { + free(blob); + blob = NULL; + } + *result = blob; + return ret; +} + +/** ASN Types and their code. */ +enum asn1_types { + /** The next object is an integer. */ + ASN1_TYPE_INTEGER = 0x2, + /** Bit string object. */ + ASN1_TYPE_BIT_STRING = 0x03, + /** Keys start with one big type sequence. */ + ASN1_TYPE_SEQUENCE = 0x30, +}; + +/* bit 6 has value 0 */ +static inline bool is_primitive(unsigned char c) +{ + return ((c & (1<<6)) == 0); +} + +static inline bool is_primitive_integer(unsigned char c) +{ + if (!is_primitive(c)) + return false; + return ((c & 0x1f) == ASN1_TYPE_INTEGER); +} + +/* Bit 8 is zero (and bits 7-1 give the length) */ +static inline bool is_short_form(unsigned char c) +{ + return (c & 0x80) == 0; +} + +static inline int get_short_form_length(unsigned char c) +{ + return c & 0x7f; +} + +static inline int get_long_form_num_length_bytes(unsigned char c) +{ + 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 istarts 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 + * zero is not considered as an additional byte for bn_bytes. + */ +static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn, + int *bn_bytes) +{ + int i, bn_size; + gcry_error_t gret; + unsigned char *cp = start; + + if (!is_primitive_integer(*cp)) + return -E_BAD_PRIVATE_KEY; + cp++; + if (is_short_form(*cp)) { + bn_size = get_short_form_length(*cp); + cp++; + } else { + int num_bytes = get_long_form_num_length_bytes(*cp); + if (cp + num_bytes > end) + return -E_BAD_PRIVATE_KEY; + if (num_bytes > 4) /* nobody has such a large modulus */ + return -E_BAD_PRIVATE_KEY; + cp++; + bn_size = 0; + for (i = 0; i < num_bytes; i++, cp++) + bn_size = (bn_size << 8) + *cp; + } + PARA_DEBUG_LOG("bn_size %d (0x%x)\n", bn_size, bn_size); + gret = gcry_mpi_scan(bn, GCRYMPI_FMT_STD, cp, bn_size, NULL); + if (gret) { + PARA_ERROR_LOG("%s while scanning n\n", + gcry_strerror(gcry_err_code(gret))); + return-E_MPI_SCAN; + } + /* + * Don't take the first leading zero into account for the size of the + * bignum. + */ + if (*cp == '\0') { + cp++; + bn_size--; + } + if (bn_bytes) + *bn_bytes = bn_size; + cp += bn_size; +// unsigned char *buf; +// gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, *bn); +// PARA_CRIT_LOG("bn: %s\n", buf); + return cp - start; +} + +static int find_privkey_bignum_offset(const unsigned char *data, int len) +{ + const unsigned char *p = data, *end = data + len; + + /* like the public key, the whole thing is contained in a 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; + + /* Skip next integer */ + if (*p != ASN1_TYPE_INTEGER) + return -E_ASN1_PARSE; + p++; + if (p >= end) + return -E_ASN1_PARSE; + if (is_short_form(*p)) + p += 1 + get_short_form_length(*p); + else + p += 1 + get_long_form_num_length_bytes(*p); + if (p >= end) + return -E_ASN1_PARSE; + return p - data; +} + +/** Private keys start with this header. */ +#define PRIVATE_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" +/** Private keys end with this footer. */ +#define PRIVATE_KEY_FOOTER "-----END RSA PRIVATE KEY-----" + +static int get_private_key(const char *key_file, struct asymmetric_key **result) +{ + gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, + u = 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, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER, + &blob); + if (ret < 0) + return ret; + blob_size = ret; + end = blob + blob_size; + ret = find_privkey_bignum_offset(blob, blob_size); + if (ret < 0) + goto free_blob; + PARA_INFO_LOG("reading 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; + cp += ret; + + ret = read_bignum(cp, end, &d, NULL); + if (ret < 0) + goto release_e; + cp += ret; + + ret = read_bignum(cp, end, &p, NULL); + if (ret < 0) + goto release_d; + cp += ret; + + ret = read_bignum(cp, end, &q, NULL); + if (ret < 0) + goto release_p; + cp += ret; + ret = read_bignum(cp, end, &u, NULL); + if (ret < 0) + goto release_q; + cp += ret; + /* + * OpenSSL uses slightly different parameters than gcrypt. To use these + * parameters we need to swap the values of p and q and recompute u. + */ + if (gcry_mpi_cmp(p, q) > 0) { + gcry_mpi_swap(p, q); + gcry_mpi_invm(u, p, q); + } + gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, + n, e, d, p, q, u); + + if (gret) { + PARA_ERROR_LOG("offset %zu: %s\n", erroff, + gcry_strerror(gcry_err_code(gret))); + ret = -E_SEXP_BUILD; + goto release_u; + } + key = para_malloc(sizeof(*key)); + key->sexp = sexp; + *result = key; + ret = n_size * 8; + PARA_INFO_LOG("succesfully read %d bit private key\n", ret); +release_u: + gcry_mpi_release(u); +release_q: + gcry_mpi_release(q); +release_p: + gcry_mpi_release(p); +release_d: + gcry_mpi_release(d); +release_e: + gcry_mpi_release(e); +release_n: + gcry_mpi_release(n); +free_blob: + 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; + cp += ret; + + 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; + *result = key; + ret = n_size; + PARA_INFO_LOG("successfully read %u 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; + gcry_error_t gret; + unsigned char *blob = NULL, *p, *end; + size_t nr_scanned, erroff, decoded_size; + gcry_mpi_t e = NULL, n = NULL; + + PARA_DEBUG_LOG("decoding %d byte public rsa-ssh key\n", size); + if (size > INT_MAX / 4) + return -ERRNO_TO_PARA_ERROR(EOVERFLOW); + blob = para_malloc(2 * size); + ret = uudecode((char *)data, blob, 2 * size); + if (ret < 0) + goto free_blob; + decoded_size = ret; + end = blob + decoded_size; + dump_buffer("decoded key", blob, decoded_size); + ret = check_ssh_key_header(blob, decoded_size); + if (ret < 0) + goto free_blob; + p = blob + ret; + ret = -E_SSH_PARSE; + if (p >= end) + goto free_blob; + PARA_DEBUG_LOG("scanning modulus and public exponent\n"); + gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned); + if (gret) { + ret = -E_MPI_SCAN; + PARA_CRIT_LOG("%s\n", gcry_strerror(gcry_err_code(gret))); + goto free_blob; + } + PARA_DEBUG_LOG("scanned e (%zu bytes)\n", nr_scanned); +// gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_e); +// PARA_CRIT_LOG("e: %s\n", buf); + p += nr_scanned; + if (p >= end) + goto release_e; + gret = gcry_mpi_scan(&n, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned); + if (gret) { + ret = -E_MPI_SCAN; + PARA_ERROR_LOG("%s\n", gcry_strerror(gcry_err_code(gret))); + goto release_e; + } + PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned); +// gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, rsa_n); +// PARA_CRIT_LOG("n: %s\n", buf); + gret = gcry_sexp_build(result, &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_n; + } + ret = nr_scanned / 32 * 32; + PARA_INFO_LOG("successfully read %u bit ssh public key\n", ret * 8); +release_n: + gcry_mpi_release(n); +release_e: + gcry_mpi_release(e); +free_blob: + free(blob); + return ret; } int get_asymmetric_key(const char *key_file, int private, struct asymmetric_key **result) { - return 0; + int ret, ret2; + void *map; + size_t map_size; + unsigned char *start, *end; + 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); + } + start = map + ret; + end = map + map_size; + ret = -E_SSH_PARSE; + if (start >= end) + goto unmap; + ret = get_ssh_public_key(start, end - start, &sexp); + if (ret < 0) + goto unmap; + key = para_malloc(sizeof(*key)); + key->num_bytes = ret; + key->sexp = sexp; + *result = key; + ret = key->num_bytes; +unmap: + ret2 = para_munmap(map, map_size); + if (ret >= 0 && ret2 < 0) + ret = ret2; + return ret; } void free_asymmetric_key(struct asymmetric_key *key) { + if (!key) + return; + gcry_sexp_release(key->sexp); + free(key); +} + +static int decode_rsa(gcry_sexp_t sexp, int key_size, 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; } int priv_decrypt(const char *key_file, unsigned char *outbuf, unsigned char *inbuf, int inlen) { - return 0; + gcry_error_t gret; + int ret, key_size; + struct asymmetric_key *priv; + gcry_mpi_t in_mpi = NULL; + gcry_sexp_t in, out, priv_key; + size_t nbytes; + + PARA_INFO_LOG("decrypting %d byte input\n", inlen); + /* key_file -> asymmetric key priv */ + 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; + priv_key = gcry_sexp_find_token(priv->sexp, "private-key", 0); + if (!priv_key) + goto free_key; + + /* inbuf -> in_mpi */ + gret = gcry_mpi_scan(&in_mpi, GCRYMPI_FMT_USG, inbuf, + inlen, NULL); + if (gret) { + PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); + ret = -E_MPI_SCAN; + goto key_release; + } + /* in_mpi -> in sexp */ + 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; + goto in_mpi_release; + } + + /* rsa decryption: in sexp -> out sexp */ + gret = gcry_pk_decrypt(&out, in, priv_key); + if (gret) { + PARA_ERROR_LOG("decrypt: %s\n", gcrypt_strerror(gret)); + ret = -E_SEXP_DECRYPT; + goto in_release; + } + ret = decode_rsa(out, key_size, outbuf, &nbytes); + if (ret < 0) + goto out_release; + PARA_INFO_LOG("successfully decrypted %zu byte message\n", nbytes); + ret = nbytes; +out_release: + gcry_sexp_release(out); +in_release: + gcry_sexp_release(in); +in_mpi_release: + gcry_mpi_release(in_mpi); +key_release: + gcry_sexp_release(priv_key); +free_key: + free_asymmetric_key(priv); + return ret; } int pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, unsigned len, unsigned char *outbuf) { - return 0; + gcry_error_t gret; + gcry_sexp_t pub_key, in, out, out_a; + gcry_mpi_t out_mpi = NULL; + size_t nbytes; + int ret; + + PARA_INFO_LOG("encrypting %u byte input with %d-byte key\n", len, pub->num_bytes); + + /* get pub 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); + } + if (gret) { + PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); + ret = -E_SEXP_BUILD; + goto key_release; + } + /* rsa sexp encryption: in -> out */ + gret = gcry_pk_encrypt(&out, in, pub_key); + if (gret) { + PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); + ret = -E_SEXP_ENCRYPT; + goto in_release; + } + /* extract a, an MPI with the result of the RSA operation */ + ret = -E_SEXP_FIND; + out_a = gcry_sexp_find_token(out, "a", 0); + if (!out_a) + goto out_release; + /* convert sexp out_a -> out_mpi */ + out_mpi = gcry_sexp_nth_mpi(out_a, 1, GCRYMPI_FMT_USG); + if (!out_mpi) { + ret = -E_SEXP_FIND; + goto out_a_release; + } + gret = gcry_mpi_print(GCRYMPI_FMT_USG, outbuf, 512 /* FIXME */, &nbytes, out_mpi); + if (gret) { + PARA_ERROR_LOG("%s\n", gcrypt_strerror(gret)); + ret = -E_SEXP_ENCRYPT; + goto out_mpi_release; + } + PARA_INFO_LOG("encrypted buffer is %zu bytes\n", nbytes); + dump_buffer("enc buf", outbuf, nbytes); + ret = nbytes; + +out_mpi_release: + gcry_mpi_release(out_mpi); +out_a_release: + gcry_sexp_release(out_a); +out_release: + gcry_sexp_release(out); +in_release: + gcry_sexp_release(in); +key_release: + gcry_sexp_release(pub_key); + return ret; } struct stream_cipher { - int x; + gcry_cipher_hd_t handle; }; struct stream_cipher *sc_new(const unsigned char *data, int len) { - return NULL; + gcry_error_t gret; + + struct stream_cipher *sc = para_malloc(sizeof(*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(gret == 0); + return sc; } void sc_free(struct stream_cipher *sc) { + if (!sc) + return; + gcry_cipher_close(sc->handle); + free(sc); } -int sc_send_bin_buffer(struct stream_cipher_context *scc, const char *buf, - size_t len) +int sc_send_bin_buffer(struct stream_cipher_context *scc, char *buf, + size_t size) { - return 0; -} + gcry_error_t gret; + int ret; + unsigned char *tmp = para_malloc(size); -int sc_send_buffer(struct stream_cipher_context *scc, const char *buf) -{ - return 0; -} - -__printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc, - const char *fmt, ...) -{ - return 0; + assert(size); + gret = gcry_cipher_encrypt(scc->send->handle, tmp, size, + (unsigned char *)buf, size); + assert(gret == 0); + ret = write_all(scc->fd, (char *)tmp, &size); + free(tmp); + return ret; } int sc_recv_bin_buffer(struct stream_cipher_context *scc, char *buf, size_t size) { - return 0; -} + gcry_error_t gret; + ssize_t ret = recv(scc->fd, buf, size, 0); -int sc_recv_buffer(struct stream_cipher_context *scc, char *buf, size_t size) -{ - return 0; -} - -void hash_function(const char *data, unsigned long len, unsigned char *hash) -{ + if (ret < 0) + ret = -ERRNO_TO_PARA_ERROR(errno); + if (ret <= 0) + return ret; + /* perform in-place encryption */ + gret = gcry_cipher_encrypt(scc->recv->handle, (unsigned char *)buf, ret, + NULL, 0); + assert(gret == 0); + return ret; }