Commit 5ce881d4 authored by Michael Tremer's avatar Michael Tremer

database: Add support for two signatures

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: default avatarMichael Tremer <michael.tremer@ipfire.org>
parent 161246f2
......@@ -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",
......
......@@ -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 {
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment