From: Andre Noll <maan@tuebingen.mpg.de>
Date: Thu, 28 Dec 2017 00:41:14 +0000 (+0100)
Subject: crypt: Deduplicate get_public_key().
X-Git-Tag: v0.6.2~5^2~5
X-Git-Url: https://git.tuebingen.mpg.de/?a=commitdiff_plain;h=5f20d9afde364f9ce51aa7841ebe513028a65e81;p=paraslash.git

crypt: Deduplicate get_public_key().

The openssl and grypt implementations of this function share quite
some code. This patch factors out the common code into the new
decode_ssh_key() helper of crypt_common.c. Both implementations are
changed to call the new helper.

As a side effect of this change, all callers of is_ssh_rsa_key()
and check_ssh_key_header() now reside in crypt_common.c, so we can
make these two functions static and remove their declarations from
crypt_backend.h.
---

diff --git a/crypt_backend.h b/crypt_backend.h
index ff956ce3..175a6881 100644
--- a/crypt_backend.h
+++ b/crypt_backend.h
@@ -7,6 +7,6 @@
 /** AES block size in bytes. */
 #define AES_CRT128_BLOCK_SIZE 16
 
-size_t is_ssh_rsa_key(char *data, size_t size);
-int check_ssh_key_header(const unsigned char *blob, int blen);
+int decode_ssh_key(const char *filename, unsigned char **blob,
+		size_t *decoded_size);
 int check_private_key_file(const char *file);
diff --git a/crypt_common.c b/crypt_common.c
index 08361b27..235b8b8d 100644
--- a/crypt_common.c
+++ b/crypt_common.c
@@ -10,20 +10,19 @@
 #include "crypt.h"
 #include "crypt_backend.h"
 #include "portable_io.h"
+#include "fd.h"
+#include "base64.h"
 
 /** If the key begins with this text, we treat it as an ssh key. */
 #define KEY_TYPE_TXT "ssh-rsa"
 
-/**
- * Check if given buffer starts with a ssh rsa key signature.
- *
- * \param data The buffer.
- * \param size Number of data bytes.
+/*
+ * Check if the given buffer starts with an ssh rsa key signature.
  *
- * \return Number of header bytes to be skipped on success, zero if
- * ssh rsa signature was not found.
+ * Returns number of header bytes to be skipped on success, zero if no ssh rsa
+ * signature was found.
  */
-size_t is_ssh_rsa_key(char *data, size_t size)
+static size_t is_ssh_rsa_key(char *data, size_t size)
 {
 	char *cp;
 
@@ -42,20 +41,13 @@ size_t is_ssh_rsa_key(char *data, size_t size)
 	return cp - data;
 }
 
-/**
- * Sanity checks for the header of an ssh key.
- *
- * \param blob The buffer.
- * \param blen The number of bytes of \a blob.
+/*
+ * Perform some sanity checks on the decoded ssh key.
  *
- * This performs some checks to make sure we really have an ssh key. It also
- * computes the offset in bytes of the start of the key values (modulus,
- * exponent..).
- *
- * \return The number of bytes to skip until the start of the first encoded
- * number (usually 11).
+ * This function returns the size of the header. Usually, the header is 11
+ * bytes long: four bytes for the length field, and the string "ssh-rsa".
  */
-int check_ssh_key_header(const unsigned char *blob, int blen)
+static int check_ssh_key_header(const unsigned char *blob, int blen)
 {
 	const unsigned char *p = blob, *end = blob + blen;
 	uint32_t rlen;
@@ -76,6 +68,51 @@ int check_ssh_key_header(const unsigned char *blob, int blen)
 	return 4 + rlen;
 }
 
+/**
+ * Perform sanity checks and base64-decode an ssh-rsa key.
+ *
+ * \param filename The public key file (usually id_rsa.pub).
+ * \param blob Pointer to base64-decoded blob is returned here.
+ * \param decoded_size The size of the decoded blob.
+ *
+ * The memory pointed at by the returned blob pointer has to be freed by the
+ * caller.
+ *
+ * \return On success, the offset in bytes of the start of the key values
+ * (modulus, exponent..). This is the number of bytes to skip from the blob
+ * until the start of the first encoded number. On failure, a negative error
+ * code is returned.
+ *
+ * \sa \ref uudecode().
+ */
+int decode_ssh_key(const char *filename, unsigned char **blob,
+		size_t *decoded_size)
+{
+	int ret, ret2;
+	void *map;
+	size_t map_size;
+
+	ret = mmap_full_file(filename, O_RDONLY, &map, &map_size, NULL);
+	if (ret < 0)
+		return ret;
+	ret = is_ssh_rsa_key(map, map_size);
+	if (ret == 0) {
+		ret = -E_SSH_PARSE;
+		goto unmap;
+	}
+	ret = uudecode(map + ret, map_size - ret, (char **)blob, decoded_size);
+	if (ret < 0)
+		goto unmap;
+	ret = check_ssh_key_header(*blob, *decoded_size);
+	if (ret < 0)
+		goto unmap;
+unmap:
+	ret2 = para_munmap(map, map_size);
+	if (ret >= 0 && ret2 < 0)
+		ret = ret2;
+	return ret;
+}
+
 /**
  * Check existence and permissions of a private key file.
  *
diff --git a/gcrypt.c b/gcrypt.c
index 052546dd..2398a605 100644
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -366,27 +366,21 @@ free_blob:
 	return ret;
 }
 
-static int get_ssh_public_key(unsigned char *data, int size, gcry_sexp_t *result)
+int get_public_key(const char *key_file, struct asymmetric_key **result)
 {
+	unsigned char *blob, *p, *end;
 	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;
+	gcry_mpi_t e, n;
+	gcry_sexp_t sexp;
+	struct asymmetric_key *key;
 
-	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);
+	ret = decode_ssh_key(key_file, &blob, &decoded_size);
 	if (ret < 0)
-		goto free_blob;
+		return ret;
 	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) {
@@ -395,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;
@@ -407,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)));
@@ -418,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:
@@ -427,42 +421,6 @@ free_blob:
 	return ret;
 }
 
-int get_public_key(const char *key_file, 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;
-
-	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) {
-		para_munmap(map, map_size);
-		return -E_SSH_PARSE;
-	}
-	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_public_key(struct asymmetric_key *key)
 {
 	if (!key)
diff --git a/openssl.c b/openssl.c
index f786ce29..99b3f7a6 100644
--- a/openssl.c
+++ b/openssl.c
@@ -16,9 +16,7 @@
 #include "error.h"
 #include "string.h"
 #include "crypt.h"
-#include "fd.h"
 #include "crypt_backend.h"
-#include "base64.h"
 #include "portable_io.h"
 
 struct asymmetric_key {
@@ -45,7 +43,7 @@ void get_random_bytes_or_die(unsigned char *buf, int num)
  * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3),
  * random(3), \ref para_random().
  */
-void init_random_seed_or_die(void)
+void crypt_init(void)
 {
 	int seed, ret = RAND_load_file("/dev/urandom", 64);
 
@@ -141,47 +139,28 @@ fail:
 
 int get_public_key(const char *key_file, struct asymmetric_key **result)
 {
-	struct asymmetric_key *key = NULL;
-	void *map = NULL;
-	unsigned char *blob = NULL;
-	size_t map_size, encoded_size, decoded_size;
-	int ret, ret2;
-	char *cp;
+	unsigned char *blob;
+	size_t decoded_size;
+	int ret;
+	struct asymmetric_key *key = para_malloc(sizeof(*key));
 
-	key = para_malloc(sizeof(*key));
-	ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
+	ret = decode_ssh_key(key_file, &blob, &decoded_size);
 	if (ret < 0)
 		goto out;
-	ret = is_ssh_rsa_key(map, map_size);
-	if (!ret) {
-		ret = -E_SSH_PARSE;
-		goto out_unmap;
-	}
-	cp = map + ret;
-	encoded_size = map_size - ret;
-	PARA_INFO_LOG("decoding public rsa-ssh key %s\n", key_file);
-	ret = uudecode(cp, encoded_size, (char **)&blob, &decoded_size);
-	if (ret < 0)
-		goto out_unmap;
-	ret = check_ssh_key_header(blob, decoded_size);
-	if (ret < 0)
-		goto out_unmap;
 	ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
 	if (ret < 0)
-		goto out_unmap;
+		goto free_blob;
 	ret = RSA_size(key->rsa);
-out_unmap:
-	ret2 = para_munmap(map, map_size);
-	if (ret >= 0 && ret2 < 0)
-		ret = ret2;
+	assert(ret > 0);
+	*result = key;
+free_blob:
+	free(blob);
 out:
 	if (ret < 0) {
 		free(key);
 		*result = NULL;
-		PARA_ERROR_LOG("key %s: %s\n", key_file, para_strerror(-ret));
-	} else
-		*result = key;
-	free(blob);
+		PARA_ERROR_LOG("can not load key %s\n", key_file);
+	}
 	return ret;
 }