crypt: Deduplicate get_public_key().
[paraslash.git] / gcrypt.c
index f4dbeb8e32c7aee89a5d1b6bd6444811616a0a8c..2398a605d29211d8581b5b76e0cb586258fe0898 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -1,8 +1,4 @@
-/*
- * Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 2011 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
 /** \file gcrypt.c Libgrcypt-based encryption/decryption routines. */
 
@@ -56,23 +52,22 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
 }
 
 /*
- * 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 is called at the beginning of every program that uses libgcrypt. The
+ * call to gcry_check_version() initializes the gcrypt library and checks that
+ * we have at least the minimal required version.
  */
 void init_random_seed_or_die(void)
 {
-       const char *ver, *req_ver;
+       const char *req_ver = "1.5.0";
+       int seed;
 
-       ver = gcry_check_version(NULL);
-       req_ver = "1.5.0";
        if (!gcry_check_version(req_ver)) {
                PARA_EMERG_LOG("fatal: need at least libgcrypt-%s, have: %s\n",
-                       req_ver, ver);
+                       req_ver, gcry_check_version(NULL));
                exit(EXIT_FAILURE);
        }
+       get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
+       srandom(seed);
 }
 
 /** S-expression for the public part of an RSA key. */
@@ -191,64 +186,6 @@ 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 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
@@ -350,6 +287,7 @@ static int get_private_key(const char *key_file, struct asymmetric_key **result)
        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)
@@ -428,86 +366,21 @@ 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)
+int get_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;
+       unsigned char *blob, *p, *end;
+       int ret;
        gcry_error_t gret;
-       size_t erroff;
+       size_t nr_scanned, erroff, decoded_size;
+       gcry_mpi_t e, n;
        gcry_sexp_t sexp;
        struct asymmetric_key *key;
 
-       ret = decode_key(key_file, PUBLIC_KEY_HEADER, PUBLIC_KEY_FOOTER,
-               &blob);
+       ret = decode_ssh_key(key_file, &blob, &decoded_size);
        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;
-       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);
-       ret = uudecode((char *)data, size, (char **)&blob, &decoded_size);
-       if (ret < 0)
-               goto free_blob;
-       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;
+       end = blob + decoded_size;
        PARA_DEBUG_LOG("scanning modulus and public exponent\n");
        gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
        if (gret) {
@@ -516,8 +389,6 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
                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;
@@ -528,9 +399,7 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
                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);
+       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)));
@@ -539,6 +408,10 @@ static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result
        }
        ret = nr_scanned / 32 * 32;
        PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8);
+       key = para_malloc(sizeof(*key));
+       key->num_bytes = ret;
+       key->sexp = sexp;
+       *result = key;
 release_n:
        gcry_mpi_release(n);
 release_e:
@@ -548,48 +421,7 @@ free_blob:
        return ret;
 }
 
-int get_asymmetric_key(const char *key_file, int private,
-               struct asymmetric_key **result)
-{
-       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;
-unmap:
-       ret2 = para_munmap(map, map_size);
-       if (ret >= 0 && ret2 < 0)
-               ret = ret2;
-       return ret;
-}
-
-void free_asymmetric_key(struct asymmetric_key *key)
+void free_public_key(struct asymmetric_key *key)
 {
        if (!key)
                return;
@@ -601,10 +433,8 @@ static int decode_rsa(gcry_sexp_t sexp, unsigned char *outbuf, size_t *nbytes)
 {
        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;
-       }
+       if (!p)
+               return -E_RSA_DECODE;
        memcpy(outbuf, p, *nbytes);
        return 1;
 }
@@ -619,7 +449,7 @@ int priv_decrypt(const char *key_file, unsigned char *outbuf,
        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);
@@ -671,7 +501,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;
 }
 
@@ -741,33 +572,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;
 }