]> git.ipfire.org Git - location/libloc.git/commitdiff
database: Add support for two signatures
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 19 May 2020 12:03:05 +0000 (12:03 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 19 May 2020 12:03:05 +0000 (12:03 +0000)
This allows us to sign the database with two different keys
in case the first key gets weakened or compromised in any other
way.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/database.c
src/loc/format.h
src/loc/writer.h
src/python/writer.c
src/test-as.c
src/test-country.c
src/test-database.c
src/test-network.c
src/test-signature.c
src/writer.c

index 5fb411ba6ae3fa1b8e25b83f6758c8d7e32af0cf..659c57b5fce97653da0d7c6cf16df50caf4c57f8 100644 (file)
@@ -58,8 +58,11 @@ struct loc_database {
        off_t description;
        off_t license;
 
-       char* signature;
-       size_t signature_length;
+       // Signatures
+       char* signature1;
+       size_t signature1_length;
+       char* signature2;
+       size_t signature2_length;
 
        // ASes in the database
        struct loc_database_as_v1* as_v1;
@@ -232,8 +235,30 @@ static int loc_database_read_countries_section_v1(struct loc_database* db,
        return 0;
 }
 
+static int loc_database_read_signature(struct loc_database* db,
+               char** dst, char* src, size_t length) {
+       // Check for a plausible signature length
+       if (length > LOC_SIGNATURE_MAX_LENGTH) {
+               ERROR(db->ctx, "Signature too long: %ld\n", length);
+               return -EINVAL;
+       }
+
+       DEBUG(db->ctx, "Reading signature of %ld bytes\n", length);
+
+       // Allocate space
+       *dst = malloc(length);
+       if (!*dst)
+               return -ENOMEM;
+
+       // Copy payload
+       memcpy(*dst, src, length);
+
+       return 0;
+}
+
 static int loc_database_read_header_v1(struct loc_database* db) {
        struct loc_database_header_v1 header;
+       int r;
 
        // Read from file
        size_t size = fread(&header, 1, sizeof(header), db->f);
@@ -249,28 +274,29 @@ static int loc_database_read_header_v1(struct loc_database* db) {
        db->description = be32toh(header.description);
        db->license     = be32toh(header.license);
 
-       // Read signature
-       db->signature_length = be32toh(header.signature_length);
-       if (db->signature_length) {
-               // Check for a plausible signature length
-               if (db->signature_length > LOC_SIGNATURE_MAX_LENGTH) {
-                       ERROR(db->ctx, "Signature too long: %ld\n", db->signature_length);
-                       return -EINVAL;
-               }
+       db->signature1_length = be32toh(header.signature1_length);
+       db->signature2_length = be32toh(header.signature2_length);
 
-               DEBUG(db->ctx, "Reading signature of %ld bytes\n",
-                       db->signature_length);
+       // Read signatures
+       if (db->signature1_length) {
+               r = loc_database_read_signature(db, &db->signature1,
+                       header.signature1, db->signature1_length);
+               if (r)
+                       return r;
+       }
 
-               db->signature = malloc(db->signature_length);
-               for (unsigned int i = 0; i < db->signature_length; i++)
-                       db->signature[i] = header.signature[i];
+       if (db->signature2_length) {
+               r = loc_database_read_signature(db, &db->signature2,
+                       header.signature2, db->signature2_length);
+               if (r)
+                       return r;
        }
 
        // Open pool
        off_t pool_offset  = be32toh(header.pool_offset);
        size_t pool_length = be32toh(header.pool_length);
 
-       int r = loc_stringpool_open(db->ctx, &db->pool,
+       r = loc_stringpool_open(db->ctx, &db->pool,
                db->f, pool_length, pool_offset);
        if (r)
                return r;
@@ -413,8 +439,10 @@ static void loc_database_free(struct loc_database* db) {
                loc_stringpool_unref(db->pool);
 
        // Free signature
-       if (db->signature)
-               free(db->signature);
+       if (db->signature1)
+               free(db->signature1);
+       if (db->signature2)
+               free(db->signature2);
 
        // Close database file
        if (db->f)
@@ -434,7 +462,7 @@ LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) {
 
 LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
        // Cannot do this when no signature is available
-       if (!db->signature) {
+       if (!db->signature1 && !db->signature2) {
                DEBUG(db->ctx, "No signature available to verify\n");
                return 1;
        }
@@ -497,11 +525,11 @@ LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
                                goto CLEANUP;
                        }
 
-                       // Clear signature
-                       for (unsigned int i = 0; i < sizeof(header_v1.signature); i++) {
-                               header_v1.signature[i] = '\0';
-                       }
-                       header_v1.signature_length = 0;
+                       // Clear signatures
+                       memset(header_v1.signature1, '\0', sizeof(header_v1.signature1));
+                       header_v1.signature1_length = 0;
+                       memset(header_v1.signature2, '\0', sizeof(header_v1.signature2));
+                       header_v1.signature2_length = 0;
 
                        hexdump(db->ctx, &header_v1, sizeof(header_v1));
 
@@ -539,24 +567,45 @@ LOC_EXPORT int loc_database_verify(struct loc_database* db, FILE* f) {
                }
        }
 
-       // Finish
-       r = EVP_DigestVerifyFinal(mdctx,
-               (unsigned char*)db->signature, db->signature_length);
+       // Check first signature
+       if (db->signature1) {
+               hexdump(db->ctx, db->signature1, db->signature1_length);
 
-       if (r == 0) {
-               DEBUG(db->ctx, "The signature is invalid\n");
-               r = 1;
-       } else if (r == 1) {
-               DEBUG(db->ctx, "The signature is valid\n");
-               r = 0;
-       } else {
-               ERROR(db->ctx, "Error verifying the signature: %s\n",
-                       ERR_error_string(ERR_get_error(), NULL));
-               r = 1;
+               r = EVP_DigestVerifyFinal(mdctx,
+                       (unsigned char*)db->signature1, db->signature1_length);
+
+               if (r == 0) {
+                       DEBUG(db->ctx, "The first signature is invalid\n");
+                       r = 1;
+               } else if (r == 1) {
+                       DEBUG(db->ctx, "The first signature is valid\n");
+                       r = 0;
+               } else {
+                       ERROR(db->ctx, "Error verifying the first signature: %s\n",
+                               ERR_error_string(ERR_get_error(), NULL));
+                       r = -1;
+               }
        }
 
-       // Dump signature
-       hexdump(db->ctx, db->signature, db->signature_length);
+       // Check second signature only when the first one was invalid
+       if (r && db->signature2) {
+               hexdump(db->ctx, db->signature2, db->signature2_length);
+
+               r = EVP_DigestVerifyFinal(mdctx,
+                       (unsigned char*)db->signature2, db->signature2_length);
+
+               if (r == 0) {
+                       DEBUG(db->ctx, "The second signature is invalid\n");
+                       r = 1;
+               } else if (r == 1) {
+                       DEBUG(db->ctx, "The second signature is valid\n");
+                       r = 0;
+               } else {
+                       ERROR(db->ctx, "Error verifying the second signature: %s\n",
+                               ERR_error_string(ERR_get_error(), NULL));
+                       r = -1;
+               }
+       }
 
        clock_t end = clock();
        DEBUG(db->ctx, "Signature checked in %.4fms\n",
index 2910798c7fcf0cc7b0dc1cd4e9522342cdd26463..ce0d2ff3899badab23ca0b7cb075cda431cd12e2 100644 (file)
@@ -32,9 +32,8 @@ enum loc_database_version {
 
 #define LOC_DATABASE_DOMAIN "_v%u._db.location.ipfire.org"
 
-#define LOC_DATABASE_PAGE_SIZE  4096
-
-#define LOC_SIGNATURE_MAX_LENGTH       4096
+#define LOC_DATABASE_PAGE_SIZE         4096
+#define LOC_SIGNATURE_MAX_LENGTH       (LOC_DATABASE_PAGE_SIZE / 2)
 
 struct loc_database_magic {
        char magic[7];
@@ -76,12 +75,17 @@ struct loc_database_header_v1 {
        uint32_t pool_offset;
        uint32_t pool_length;
 
-       // Signature
-       uint32_t signature_length;
-       char signature[LOC_SIGNATURE_MAX_LENGTH];
+       // Some padding
+       char padding1[2];
+
+       // Signatures
+       uint32_t signature1_length;
+       uint32_t signature2_length;
+       char signature1[LOC_SIGNATURE_MAX_LENGTH];
+       char signature2[LOC_SIGNATURE_MAX_LENGTH];
 
        // Add some padding for future extensions
-       char padding[32];
+       char padding2[32];
 };
 
 struct loc_database_network_node_v1 {
index d08bb185457e2d8162ab19acc7e7029a77eee4fe..f106a948b0c0a16c13464d14f79e8dd47853244a 100644 (file)
@@ -27,7 +27,8 @@
 
 struct loc_writer;
 
-int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer, FILE* fkey);
+int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer,
+    FILE* fkey1, FILE* fkey2);
 
 struct loc_writer* loc_writer_ref(struct loc_writer* writer);
 struct loc_writer* loc_writer_unref(struct loc_writer* writer);
index 00f7f6acfc59ab635eb6e48b9f5d684846c7ce46..557f3e11690dc8755331657c3e907cd5eea73aa2 100644 (file)
@@ -61,7 +61,7 @@ static int Writer_init(WriterObject* self, PyObject* args, PyObject* kwargs) {
        }
 
        // Create the writer object
-       int r = loc_writer_new(loc_ctx, &self->writer, f);
+       int r = loc_writer_new(loc_ctx, &self->writer, f, NULL);
 
        return r;
 }
index 7f0da750580f4ebc7dd7eb4f67b18d9a694ef07c..7a2f34ea5e62f26ced81ca2fe979950d2783114d 100644 (file)
@@ -34,7 +34,7 @@ int main(int argc, char** argv) {
 
        // Create a database
        struct loc_writer* writer;
-       err = loc_writer_new(ctx, &writer, NULL);
+       err = loc_writer_new(ctx, &writer, NULL, NULL);
        if (err < 0)
                exit(EXIT_FAILURE);
 
index a10ec23717e3e245d94cf7816f1d8ea15e5463d3..25baf74e68dd4a7e7eaf413acee5215e6f88742a 100644 (file)
@@ -49,7 +49,7 @@ int main(int argc, char** argv) {
 
        // Create a database
        struct loc_writer* writer;
-       err = loc_writer_new(ctx, &writer, NULL);
+       err = loc_writer_new(ctx, &writer, NULL, NULL);
        if (err < 0)
                exit(EXIT_FAILURE);
 
index dfba4c258c55c9c2409770b5ecdd8662fb47d2cf..6f20a59dfd919057ec8bee71d859d05afddc7fc6 100644 (file)
@@ -83,7 +83,7 @@ int main(int argc, char** argv) {
 
        // Create a database
        struct loc_writer* writer;
-       err = loc_writer_new(ctx, &writer, NULL);
+       err = loc_writer_new(ctx, &writer, NULL, NULL);
        if (err < 0)
                exit(EXIT_FAILURE);
 
index 21e2dbc61a7e052cdf20136d6611d896d1f5f294..eb2a82f3bbbbb8aba654c48ab5807bda18f9959f 100644 (file)
@@ -93,7 +93,7 @@ int main(int argc, char** argv) {
 
        // Create a database
        struct loc_writer* writer;
-       err = loc_writer_new(ctx, &writer, NULL);
+       err = loc_writer_new(ctx, &writer, NULL, NULL);
        if (err < 0)
                exit(EXIT_FAILURE);
 
index ec584a6ee5265b6a5ceedbcd73d23ec862876b71..4c778a0f771a686a457fc52f45edcdc636626096 100644 (file)
@@ -51,7 +51,7 @@ int main(int argc, char** argv) {
 
        // Create an empty database
        struct loc_writer* writer;
-       err = loc_writer_new(ctx, &writer, private_key);
+       err = loc_writer_new(ctx, &writer, private_key, NULL);
        if (err < 0)
                exit(EXIT_FAILURE);
 
index 630e919a25ea75ea83b42996400401fc38719170..db9ec1a1561c23048f8e5e00e74bbf5428f2ddb4 100644 (file)
@@ -50,8 +50,15 @@ struct loc_writer {
        off_t description;
        off_t license;
 
-       // Private key to sign any databases
-       EVP_PKEY* private_key;
+       // Private keys to sign any databases
+       EVP_PKEY* private_key1;
+       EVP_PKEY* private_key2;
+
+       // Signatures
+       char signature1[LOC_SIGNATURE_MAX_LENGTH];
+       size_t signature1_length;
+       char signature2[LOC_SIGNATURE_MAX_LENGTH];
+       size_t signature2_length;
 
        struct loc_as** as;
        size_t as_count;
@@ -62,16 +69,16 @@ struct loc_writer {
        struct loc_network_tree* networks;
 };
 
-static int parse_private_key(struct loc_writer* writer, FILE* f) {
+static int parse_private_key(struct loc_writer* writer, EVP_PKEY** private_key, FILE* f) {
        // Free any previously loaded keys
-       if (writer->private_key)
-               EVP_PKEY_free(writer->private_key);
+       if (*private_key)
+               EVP_PKEY_free(*private_key);
 
        // Read the key
-       writer->private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+       *private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
 
        // Log any errors
-       if (!writer->private_key) {
+       if (!*private_key) {
                char* error = ERR_error_string(ERR_get_error(), NULL);
                ERROR(writer->ctx, "Could not parse private key: %s\n", error);
 
@@ -81,7 +88,8 @@ static int parse_private_key(struct loc_writer* writer, FILE* f) {
        return 0;
 }
 
-LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer, FILE* fkey) {
+LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer,
+               FILE* fkey1, FILE* fkey2) {
        struct loc_writer* w = calloc(1, sizeof(*w));
        if (!w)
                return -ENOMEM;
@@ -102,9 +110,17 @@ LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer, F
                return r;
        }
 
-       // Load the private key to sign databases
-       if (fkey) {
-               r = parse_private_key(w, fkey);
+       // Load the private keys to sign databases
+       if (fkey1) {
+               r = parse_private_key(w, &w->private_key1, fkey1);
+               if (r) {
+                       loc_writer_unref(w);
+                       return r;
+               }
+       }
+
+       if (fkey2) {
+               r = parse_private_key(w, &w->private_key2, fkey2);
                if (r) {
                        loc_writer_unref(w);
                        return r;
@@ -124,9 +140,11 @@ LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
 static void loc_writer_free(struct loc_writer* writer) {
        DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
 
-       // Free private key
-       if (writer->private_key)
-               EVP_PKEY_free(writer->private_key);
+       // Free private keys
+       if (writer->private_key1)
+               EVP_PKEY_free(writer->private_key1);
+       if (writer->private_key2)
+               EVP_PKEY_free(writer->private_key2);
 
        // Unref all AS
        for (unsigned int i = 0; i < writer->as_count; i++) {
@@ -511,7 +529,8 @@ static int loc_database_write_countries(struct loc_writer* writer,
 }
 
 static int loc_writer_create_signature(struct loc_writer* writer,
-               struct loc_database_header_v1* header, FILE* f) {
+               struct loc_database_header_v1* header, FILE* f, EVP_PKEY* private_key,
+               char* signature, size_t* length) {
        DEBUG(writer->ctx, "Signing database...\n");
 
        // Read file from the beginning
@@ -521,7 +540,7 @@ static int loc_writer_create_signature(struct loc_writer* writer,
        EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
 
        // Initialise the context
-       int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, writer->private_key);
+       int r = EVP_DigestSignInit(mdctx, NULL, NULL, NULL, private_key);
        if (r != 1) {
                ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
                goto END;
@@ -568,29 +587,25 @@ static int loc_writer_create_signature(struct loc_writer* writer,
                r = EVP_DigestSignUpdate(mdctx, buffer, bytes_read);
                if (r != 1) {
                        ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+                       r = -1;
                        goto END;
                }
        }
 
        // Compute the signature
-       size_t signature_length = sizeof(header->signature);
-
        r = EVP_DigestSignFinal(mdctx,
-               (unsigned char*)header->signature, &signature_length);
+               (unsigned char*)signature, length);
        if (r != 1) {
                ERROR(writer->ctx, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+               r = -1;
                goto END;
        }
 
-       // Save length of the signature
-       header->signature_length = htobe32(signature_length);
-
-       DEBUG(writer->ctx, "Successfully generated signature of %lu bytes\n",
-               signature_length);
+       DEBUG(writer->ctx, "Successfully generated signature of %lu bytes\n", *length);
        r = 0;
 
        // Dump signature
-       hexdump(writer->ctx, header->signature, signature_length);
+       hexdump(writer->ctx, signature, *length);
 
 END:
        EVP_MD_CTX_free(mdctx);
@@ -627,14 +642,15 @@ LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f, enum loc_dat
        time_t now = time(NULL);
        header.created_at = htobe64(now);
 
-       // Clear the signature
-       header.signature_length = 0;
-       for (unsigned int i = 0; i < sizeof(header.signature); i++)
-               header.signature[i] = '\0';
+       // Clear the signatures
+       memset(header.signature1, '\0', sizeof(header.signature1));
+       header.signature1_length = 0;
+       memset(header.signature2, '\0', sizeof(header.signature2));
+       header.signature2_length = 0;
 
        // Clear the padding
-       for (unsigned int i = 0; i < sizeof(header.padding); i++)
-               header.padding[i] = '\0';
+       memset(header.padding1, '\0', sizeof(header.padding1));
+       memset(header.padding2, '\0', sizeof(header.padding2));
 
        int r;
        off_t offset = 0;
@@ -677,13 +693,40 @@ LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f, enum loc_dat
        if (r)
                return r;
 
-       // Create the signature
-       if (writer->private_key) {
-               r = loc_writer_create_signature(writer, &header, f);
+       // Create the signatures
+       if (writer->private_key1) {
+               DEBUG(writer->ctx, "Creating signature with first private key\n");
+
+               writer->signature1_length = sizeof(writer->signature1);
+
+               r = loc_writer_create_signature(writer, &header, f,
+                       writer->private_key1, writer->signature1, &writer->signature1_length);
                if (r)
                        return r;
        }
 
+       if (writer->private_key2) {
+               DEBUG(writer->ctx, "Creating signature with second private key\n");
+
+               writer->signature2_length = sizeof(writer->signature2);
+
+               r = loc_writer_create_signature(writer, &header, f,
+                       writer->private_key2, writer->signature2, &writer->signature2_length);
+               if (r)
+                       return r;
+       }
+
+       // Copy the signatures into the header
+       if (writer->signature1_length) {
+               memcpy(header.signature1, writer->signature1, writer->signature1_length);
+               header.signature1_length = htobe32(writer->signature1_length);
+       }
+
+       if (writer->signature2_length) {
+               memcpy(header.signature2, writer->signature2, writer->signature2_length);
+               header.signature2_length = htobe32(writer->signature2_length);
+       }
+
        // Write the header
        r = fseek(f, sizeof(magic), SEEK_SET);
        if (r)