From 666718998eaa044f6f25fe99810a78dca8471393 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Sun, 18 May 2014 20:49:35 +0200
Subject: [PATCH] Implement a PEM-like format for Ed25519 keys.

We don't require compatibility with any other software, but we do want Ed25519 keys to work
the same as RSA keys for now.
---
 src/ed25519/ecdsa.c    | 47 ++++++++++++++++++++++++++++++++++++++++--
 src/ed25519/ecdsagen.c | 20 ++++++++++++++++--
 src/tincctl.c          |  2 +-
 3 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/src/ed25519/ecdsa.c b/src/ed25519/ecdsa.c
index 4515131a..bfdabc19 100644
--- a/src/ed25519/ecdsa.c
+++ b/src/ed25519/ecdsa.c
@@ -62,9 +62,52 @@ char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
 
 // Read PEM ECDSA keys
 
+static bool read_pem(FILE *fp, const char *type, void *buf, size_t size) {
+	char line[1024];
+	bool data = false;
+	size_t typelen = strlen(type);
+
+	while(fgets(line, sizeof line, fp)) {
+		if(!data) {
+			if(strncmp(line, "-----BEGIN ", 11))
+				continue;
+			if(strncmp(line + 11, type, typelen))
+				continue;
+			data = true;
+			continue;
+		}
+
+		if(!strncmp(line, "-----END ", 9))
+			break;
+
+		size_t linelen = strcspn(line, "\r\n");
+		size_t len = b64decode(line, line, linelen);
+		if(!len) {
+			logger(DEBUG_ALWAYS, LOG_ERR, "Invalid base64 data in PEM file\n");
+			return false;
+		}
+
+		if(len > size) {
+			logger(DEBUG_ALWAYS, LOG_ERR, "Too much base64 data in PEM file\n");
+			return false;
+		}
+
+		memcpy(buf, line, len);
+		buf += len;
+		size -= len;
+	}
+
+	if(size) {
+		logger(DEBUG_ALWAYS, LOG_ERR, "Too little base64 data in PEM file\n");
+		return false;
+	}
+
+	return true;
+}
+
 ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) {
 	ecdsa_t *ecdsa = xzalloc(sizeof *ecdsa);
-	if(fread(ecdsa->public, sizeof ecdsa->public, 1, fp) == 1)
+	if(read_pem(fp, "ED25519 PUBLIC KEY", ecdsa->public, sizeof ecdsa->public))
 		return ecdsa;
 	free(ecdsa);
 	return 0;
@@ -72,7 +115,7 @@ ecdsa_t *ecdsa_read_pem_public_key(FILE *fp) {
 
 ecdsa_t *ecdsa_read_pem_private_key(FILE *fp) {
 	ecdsa_t *ecdsa = xmalloc(sizeof *ecdsa);
-	if(fread(ecdsa, sizeof *ecdsa, 1, fp) == 1)
+	if(read_pem(fp, "ED25519 PRIVATE KEY", ecdsa->private, sizeof *ecdsa))
 		return ecdsa;
 	free(ecdsa);
 	return 0;
diff --git a/src/ed25519/ecdsagen.c b/src/ed25519/ecdsagen.c
index 41845739..d2a14890 100644
--- a/src/ed25519/ecdsagen.c
+++ b/src/ed25519/ecdsagen.c
@@ -46,10 +46,26 @@ ecdsa_t *ecdsa_generate(void) {
 
 // Write PEM ECDSA keys
 
+static bool write_pem(FILE *fp, const char *type, void *buf, size_t size) {
+	fprintf(fp, "-----BEGIN %s-----\n", type);
+
+	char base64[65];
+	while(size) {
+		size_t todo = size > 48 ? 48 : size;
+		b64encode(buf, base64, todo);
+		fprintf(fp, "%s\n", base64);
+		buf += todo;
+		size -= todo;
+	}
+
+	fprintf(fp, "-----END %s-----\n", type);
+	return !ferror(fp);
+}
+
 bool ecdsa_write_pem_public_key(ecdsa_t *ecdsa, FILE *fp) {
-	return fwrite(ecdsa->public, sizeof ecdsa->public, 1, fp) == 1;
+	return write_pem(fp, "ED25519 PUBLIC KEY", ecdsa->public, sizeof ecdsa->public);
 }
 
 bool ecdsa_write_pem_private_key(ecdsa_t *ecdsa, FILE *fp) {
-	return fwrite(ecdsa, sizeof *ecdsa, 1, fp) == 1;
+	return write_pem(fp, "ED25519 PRIVATE KEY", ecdsa->private, sizeof *ecdsa);
 }
diff --git a/src/tincctl.c b/src/tincctl.c
index 5f2b1351..7a3195e5 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -246,7 +246,7 @@ static void disable_old_keys(const char *filename, const char *what) {
 
 	while(fgets(buf, sizeof buf, r)) {
 		if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
-			if((strstr(buf, " RSA ") && strstr(what, "RSA"))) {
+			if((strstr(buf, " ED25519 ") && strstr(what, "Ed25519")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
 				disabled = true;
 				block = true;
 			}
-- 
2.39.5