# HG changeset patch # User Andre Heinecke # Date 1394734336 0 # Node ID 37fc669675172a9a94a33de983567b93528ea0af # Parent 381558ff6f26f1b5ffbc49b78ddfbcc3c4e46f65 Implement signature verification wiht polarssl diff -r 381558ff6f26 -r 37fc66967517 common/listutil.c --- a/common/listutil.c Thu Mar 13 17:26:33 2014 +0000 +++ b/common/listutil.c Thu Mar 13 18:12:16 2014 +0000 @@ -18,90 +18,77 @@ #pragma GCC diagnostic ignored "-Wconversion" /* Polarssl mh.h contains a conversion which gcc warns about */ #include +#include +#include #pragma GCC diagnostic pop -#define MAX_FILESIZE_KB 1024 +#define MAX_FILESIZE 1048576 /* 1024*1024 */ void handle_errno() { printf("Error: %s \n", strerror(errno)); } -list_status_t read_list (const char *file_name, char **data, size_t *size) +/** + * @brief Read a file into memory. + * + * The caller needs to free data + * + * @param[in] fileName Name of the file. + * @param[out] data the file content + * @param[out] size size in bytes of the file content. + * @param[in] max_size the maximum amount of bytes to read. + * + * @return 0 on success an error code otherwise. + */ +#define READ_FILE_UNREADABLE -1 +#define READ_FILE_TOO_LARGE -2 +#define READ_FILE_NO_MEMORY -3 +#define READ_FILE_READ_FAILED -4 +static int read_file(const char *file_name, char **data, size_t *size, + const size_t max_size) { - int fd = -1; - struct stat file_stat; - int rc = 0; - ssize_t bRead = 0; - - memset(&file_stat, 0, sizeof(file_stat)); - - list_status_t retval = UnknownError; + FILE *f; + long file_size; - fd = open(file_name, O_RDONLY); - if (fd == -1) { - handle_errno(); - retval = StatFailed; - goto cleanup; - } + f = fopen(file_name, "rb"); + if (f == NULL) + return READ_FILE_UNREADABLE; - rc = fstat(fd, &file_stat); - if (rc < 0) { - printf ("Stat failed with rc: %i\n", rc); - retval = StatFailed; - goto cleanup; - } - - // Check the size of the file - if (!file_stat.st_size) { - printf("Size zero\n"); - retval = StatFailed; - goto cleanup; + fseek(f, 0, SEEK_END); + file_size = ftell(f); + if (file_size < 0){ + fclose(f); + return READ_FILE_UNREADABLE; } - if (file_stat.st_size / 1024 > MAX_FILESIZE_KB && - file_stat.st_size > 0) { - printf("File too large\n"); - retval = TooLarge; - goto cleanup; - } + fseek(f, 0, SEEK_SET); - *size = (size_t) file_stat.st_size; - - *data = (char*) malloc(*size); + if (file_size + 1 == 0) { + return READ_FILE_TOO_LARGE; + } + *size = (size_t) file_size; - if (*data == NULL) { - printf("Malloc failed\n"); - retval = UnknownError; - goto cleanup; + if (*size > max_size) + return READ_FILE_TOO_LARGE; + + *data = (char *) malloc( *size + 1 ); + if (data == NULL) { + fclose(f); + return READ_FILE_NO_MEMORY; } - bRead = read(fd, *data, *size); - - if (bRead < 0 || (size_t) bRead != *size) { - printf("Read failed\n"); - if (bRead == -1) { - handle_errno(); - } - retval = UnknownError; - *size = 0; - if (*data) { - free(*data); - printf("Nulling data\n"); - *data = NULL; - } - goto cleanup; + if (fread(*data, 1, *size, f) != *size) { + fclose(f); + free(*data); + return READ_FILE_READ_FAILED; } - retval = UnknownValidity; -cleanup: + fclose(f); - if (fd && fd != -1) { - close(fd); - fd = -1; - } + (*data)[*size] = '\0'; - return retval; + return 0; } /** @brief verify the certificate list @@ -116,57 +103,128 @@ */ int verify_list(char *data, size_t size) { -// char *sigstart = data; int ret = -1; pk_context pub_key_ctx; - size_t lenpem = strlen((const char*)publicKeyPEM); + char *p; + /* Fixed key size of 3072 implies the sizes*/ + const size_t sig_b64_size = 512; + size_t sig_size = 384; + + char signature_b64[sig_b64_size + 1]; + unsigned char signature[sig_size]; + /* Hash algroithm is sha256 */ + unsigned char hash[32]; + + printf ("size: %lu", (unsigned long) size); + + /* Fetch the signature from the first line od data */ + p = strchr(data, '\r'); + if (p == 0 || (unsigned int)(p - (data + 2)) != sig_b64_size) { + printf("Invalid data. Signature might be too long.\n"); + return -1; + } + strncpy(signature_b64, data + 2, sig_b64_size); + signature_b64[sig_b64_size] = '\0'; + + ret = base64_decode(signature, &sig_size, + (unsigned char *)signature_b64, sig_b64_size); + + if (ret != 0 || sig_size != 384) { + printf("failed to decode signature\n"); + return -1; + } + + /* Hash is calculated over the data without the first line. + * linebreaks are \r\n so the first char of the new line is + * p+2 */ + p += 2; + /* Size of the data to hash is the size - signature line + * signature line is sig_b64_size - "S:" and - "\r\n" so -4*/ + sha256((unsigned char *)p, size - sig_b64_size - 4, hash, 0); pk_init(&pub_key_ctx); +#if 0 + { + int i; + FILE *foo = fopen("/tmp/testdump", "w"); + FILE *foo2 = fopen("/tmp/rawdump", "w"); + for (i=0; i< (int)(size - sig_b64_size - 2); i++) + fprintf (foo, "%c", p[i]); + for (i=0; i< (int)(size); i++) + fprintf (foo2, "%c", data[i]); + fclose(foo); + printf ("Hash: \n"); + for (i=0; i<32; i++) { + printf ("%x", hash[i]); + } + printf("\n"); + } +#endif - ret = pk_parse_public_key(&pub_key_ctx, publicKeyPEM, lenpem); - + ret = pk_parse_public_key(&pub_key_ctx, public_key_pem, + public_key_pem_size); if (ret != 0) { printf("pk_parse_public_key failed with -0x%04x\n\n", -ret); - goto done; + pk_free(&pub_key_ctx); + return ret; } -done: + ret = pk_verify(&pub_key_ctx, POLARSSL_MD_SHA256, hash, 0, + signature, sig_size); + + if (ret != 0) { + printf("pk_verify failed with -0x%04x\n\n", -ret); + } pk_free(&pub_key_ctx); + return ret; } list_status_t read_and_verify_list(const char *file_name, char **data, size_t *size) { - char * signature = NULL; - list_status_t retval = UnknownError; *data = NULL; *size = 0; - - retval = read_list(file_name, data, size); + int ret = 0; - if (retval != UnknownValidity) { - printf("Readlist failed\n"); - return retval; - } + ret = read_file(file_name, data, size, MAX_FILESIZE); - if (!data || !*size) { - // should not have happend if read_list works as specified + if (ret != 0) { + if (ret == READ_FILE_TOO_LARGE) { + return TooLarge; + } + if (ret == READ_FILE_UNREADABLE) { + return SeekFailed; + } + if (ret == READ_FILE_READ_FAILED) { + return ReadFailed; + } return UnknownError; } - signature = *data; - - if (*signature != 'S') { - printf("Does not start with S\n"); - retval = InvalidFormat; - goto cleanup; + if (!*data || !*size) { + return UnknownError; } - retval = verify_list (*data, *size); + if (**data != 'S') { + retval = InvalidFormat; + } else { + ret = verify_list (*data, *size); + if (ret == 0) { + /* Hooray */ + return Valid; + } + if (ret == -1) { + /* our error */ + retval = InvalidFormat; + } else if (ret == POLARSSL_ERR_RSA_VERIFY_FAILED) { + retval = InvalidSignature; + } else { + return UnknownError; + } + } -cleanup: if (retval != Valid && *data) { free(*data); *data = NULL; diff -r 381558ff6f26 -r 37fc66967517 common/listutil.h --- a/common/listutil.h Thu Mar 13 17:26:33 2014 +0000 +++ b/common/listutil.h Thu Mar 13 18:12:16 2014 +0000 @@ -16,15 +16,14 @@ * @brief Status of the List Operations */ typedef enum { - UnknownValidity = 0, // Not yet parsed + Valid = 100, // Could be read and signature matched UnknownError = 1, // The expected unexpected TooLarge = 2, // Failed because the file exeeds the limit InvalidFormat = 3, // File does not appear to be in list format InvalidSignature = 4, // Signature was invalid - StatFailed = 5, // Could not stat the file - ReadFailed = 6, // Could not read the file - IncompatibleVersion = 7, // The Format Version does not match - Valid = 8 // List is valid + SeekFailed = 5, // Could not seek in the file + ReadFailed = 6, // File exists but could not read the file + IncompatibleVersion = 7 // The Format Version does not match } list_status_t; /** diff -r 381558ff6f26 -r 37fc66967517 ui/certificatelist.cpp --- a/ui/certificatelist.cpp Thu Mar 13 17:26:33 2014 +0000 +++ b/ui/certificatelist.cpp Thu Mar 13 18:12:16 2014 +0000 @@ -9,7 +9,7 @@ char *data = NULL; size_t size = 0; - mStatus = readAndVerifyList(fileName, &data, &size); + mStatus = read_and_verify_list(fileName, &data, &size); if (!isValid()) { return; @@ -49,7 +49,7 @@ mCertificatesInstall << Certificate( QByteArray::fromBase64(curLine.remove(0,2).toLatin1())); } else if (curLine.startsWith("S:")) { - // Signature is verified in readAndVerifyList + // Signature is verified in read_and_verify_list continue; } else if (!curLine.isEmpty()){ qDebug () << "Don't know how to handle: " << curLine; diff -r 381558ff6f26 -r 37fc66967517 ui/mainwindow.cpp --- a/ui/mainwindow.cpp Thu Mar 13 17:26:33 2014 +0000 +++ b/ui/mainwindow.cpp Thu Mar 13 18:12:16 2014 +0000 @@ -73,7 +73,7 @@ char *data = NULL; size_t size; - if (readAndVerifyList(cFileName, &data, &size) != Valid) { + if (read_and_verify_list(cFileName, &data, &size) != Valid) { // Probably a bug when Qt fileName is encoded and cFileName // fails because of this. This needs a unit test! // Maybe check that the file is in our data directory diff -r 381558ff6f26 -r 37fc66967517 ui/tests/certlistparsertest.cpp --- a/ui/tests/certlistparsertest.cpp Thu Mar 13 17:26:33 2014 +0000 +++ b/ui/tests/certlistparsertest.cpp Thu Mar 13 18:12:16 2014 +0000 @@ -3,6 +3,15 @@ #include +void CertListTest::testValidList() +{ + const char *fname = "list-valid-signed.txt"; + CertificateList *certList = testWithFile(fname); + printf("Status :%i\n ",certList->getStatus()); + QCOMPARE (certList->getStatus(), Valid); + QVERIFY (certList->isValid()); + delete certList; +} void CertListTest::testInvalidSig() { @@ -44,15 +53,6 @@ delete certList; } -void CertListTest::testValidList() -{ - const char *fname = "list-valid-signed.txt"; - CertificateList *certList = testWithFile(fname); - QCOMPARE (certList->getStatus(), Valid); - QVERIFY (certList->isValid()); - delete certList; -} - void CertListTest::benchmarkValid() { const char *fname = "list-valid-signed.txt";