Improve use of compiler attributes
[tinc] / src / ed25519 / ecdsa.c
index 1da2797..1ba01cf 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "ed25519.h"
 
-#define __TINC_ECDSA_INTERNAL__
+#define TINC_ECDSA_INTERNAL
 typedef struct {
        uint8_t private[64];
        uint8_t public[32];
@@ -32,21 +32,27 @@ typedef struct {
 #include "../utils.h"
 #include "../xalloc.h"
 
+static ecdsa_t *ecdsa_new(void) ATTR_MALLOC ATTR_DEALLOCATOR(ecdsa_free);
+static ecdsa_t *ecdsa_new(void) {
+       return xzalloc(sizeof(ecdsa_t));
+}
+
 // Get and set ECDSA keys
 //
 ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
-       int len = strlen(p);
+       size_t len = strlen(p);
 
        if(len != 43) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid size %s for public key!", len);
+               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid size %lu for public key!", (unsigned long)len);
                return 0;
        }
 
-       ecdsa_t *ecdsa = xzalloc(sizeof *ecdsa);
-       len = b64decode(p, ecdsa->public, len);
+       ecdsa_t *ecdsa = ecdsa_new();
+       len = b64decode_tinc(p, ecdsa->public, len);
+
        if(len != 32) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid format of public key! len = %d", len);
-               free(ecdsa);
+               logger(DEBUG_ALWAYS, LOG_ERR, "Invalid format of public key! len = %lu", (unsigned long)len);
+               ecdsa_free(ecdsa);
                return 0;
        }
 
@@ -55,30 +61,96 @@ ecdsa_t *ecdsa_set_base64_public_key(const char *p) {
 
 char *ecdsa_get_base64_public_key(ecdsa_t *ecdsa) {
        char *base64 = xmalloc(44);
-       b64encode(ecdsa->public, base64, sizeof ecdsa->public);
+       b64encode_tinc(ecdsa->public, base64, sizeof(ecdsa->public));
 
        return base64;
 }
 
 // Read PEM ECDSA keys
 
+static bool read_pem(FILE *fp, const char *type, void *vbuf, size_t size) {
+       const size_t buflen = size;
+       char line[1024];
+       bool data = false;
+       size_t typelen = strlen(type);
+       char *buf = vbuf;
+
+       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_tinc(line, line, linelen);
+
+               if(!len || len > size) {
+                       logger(DEBUG_ALWAYS, LOG_ERR, "%s base64 data in PEM file\n", len ? "Too much" : "Invalid");
+                       errno = EINVAL;
+                       goto exit;
+               }
+
+               memcpy(buf, line, len);
+               buf += len;
+               size -= len;
+       }
+
+       if(size) {
+               if(data) {
+                       errno = EINVAL;
+                       logger(DEBUG_ALWAYS, LOG_ERR, "Too little base64 data in PEM file\n");
+               } else {
+                       errno = ENOENT;
+               }
+       }
+
+exit:
+       memzero(line, sizeof(line));
+
+       if(size) {
+               memzero(vbuf, buflen);
+               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)
+       ecdsa_t *ecdsa = ecdsa_new();
+
+       if(read_pem(fp, "ED25519 PUBLIC KEY", ecdsa->public, sizeof(ecdsa->public))) {
                return ecdsa;
-       free(ecdsa);
-       return 0;
+       }
+
+       ecdsa_free(ecdsa);
+       return NULL;
 }
 
 ecdsa_t *ecdsa_read_pem_private_key(FILE *fp) {
-       ecdsa_t *ecdsa = xmalloc(sizeof *ecdsa);
-       if(fread(ecdsa, sizeof *ecdsa, 1, fp) == 1)
+       ecdsa_t *ecdsa = ecdsa_new();
+
+       if(read_pem(fp, "ED25519 PRIVATE KEY", ecdsa->private, sizeof(*ecdsa))) {
                return ecdsa;
-       free(ecdsa);
-       return 0;
+       }
+
+       ecdsa_free(ecdsa);
+       return NULL;
 }
 
 size_t ecdsa_size(ecdsa_t *ecdsa) {
+       (void)ecdsa;
        return 64;
 }
 
@@ -98,5 +170,5 @@ bool ecdsa_active(ecdsa_t *ecdsa) {
 }
 
 void ecdsa_free(ecdsa_t *ecdsa) {
-       free(ecdsa);
+       xzfree(ecdsa, sizeof(ecdsa_t));
 }