Commit 5d1eada7 authored by Tomáš Stefan's avatar Tomáš Stefan

compare message digest

check data integrity - hash inside the signature equals computed
 hash over provided range
parent 9471c9bc
......@@ -6,6 +6,6 @@
sigil_err_t parse_certs(sigil_t *sgl);
int sigil_sigil_self_test(int verbosity);
int sigil_cert_self_test(int verbosity);
#endif /* PDF_SIGIL_CERT_H */
......@@ -13,43 +13,48 @@
#endif
#define XREF_TYPE_UNSET 0
#define XREF_TYPE_TABLE 1
#define XREF_TYPE_STREAM 2
#define DICT_KEY_UNKNOWN 0
#define DICT_KEY_Size 1
#define DICT_KEY_Prev 2
#define DICT_KEY_Root 3
#define DICT_KEY_AcroForm 4
#define DICT_KEY_Fields 5
#define DICT_KEY_SigFlags 6
#define DICT_KEY_FT 7
#define DICT_KEY_V 8
#define DICT_KEY_SubFilter 9
#define DICT_KEY_Cert 10
#define DICT_KEY_Contents 11
#define DICT_KEY_ByteRange 12
#define XREF_TYPE_UNSET 0
#define XREF_TYPE_TABLE 1
#define XREF_TYPE_STREAM 2
#define DICT_KEY_UNKNOWN 0
#define DICT_KEY_Size 1
#define DICT_KEY_Prev 2
#define DICT_KEY_Root 3
#define DICT_KEY_AcroForm 4
#define DICT_KEY_Fields 5
#define DICT_KEY_SigFlags 6
#define DICT_KEY_FT 7
#define DICT_KEY_V 8
#define DICT_KEY_SubFilter 9
#define DICT_KEY_Cert 10
#define DICT_KEY_Contents 11
#define DICT_KEY_ByteRange 12
#define SUBFILTER_UNKNOWN 0
#define SUBFILTER_adbe_x509_rsa_sha1 1
#define CERT_STATUS_UNKNOWN 0
#define CERT_STATUS_VERIFIED 1
#define CERT_STATUS_FAILED 2
#define DEALLOCATE_FILE 0x01
#define DEALLOCATE_BUFFER 0x02
#define ERR_NO 0
#define ERR_ALLOCATION 1
#define ERR_PARAMETER 2
#define ERR_IO 3
#define ERR_PDF_CONTENT 4
#define ERR_NOT_IMPLEMENTED 5
#define ERR_NO_DATA 6
#define ERR_END_OF_DICT 7
#define ERR_NO_SIGNATURE 8
#define ERR_OPENSSL 9
#define CERT_STATUS_UNKNOWN 0
#define CERT_STATUS_VERIFIED 1
#define CERT_STATUS_FAILED 2
#define HASH_CMP_RESULT_UNKNOWN 0
#define HASH_CMP_RESULT_MATCH 1
#define HASH_CMP_RESULT_DIFFER 2
#define DEALLOCATE_FILE 0x01
#define DEALLOCATE_BUFFER 0x02
#define ERR_NO 0
#define ERR_ALLOCATION 1
#define ERR_PARAMETER 2
#define ERR_IO 3
#define ERR_PDF_CONTENT 4
#define ERR_NOT_IMPLEMENTED 5
#define ERR_NO_DATA 6
#define ERR_END_OF_DICT 7
#define ERR_NO_SIGNATURE 8
#define ERR_OPENSSL 9
#define ERR_DIGEST_TYPE 10
#endif /* PDF_SIGIL_CONSTANTS_H */
......@@ -6,6 +6,6 @@
sigil_err_t parse_contents(sigil_t *sgl);
int sigil_sigil_self_test(int verbosity);
int sigil_contents_self_test(int verbosity);
#endif //PDF_SIGIL_CONTENTS_H
......@@ -4,14 +4,22 @@
#include "types.h"
sigil_err_t hex_to_dec(const char *in, size_t in_len, unsigned char *out, size_t *out_len);
void print_computed_hash(sigil_t *sgl);
sigil_err_t compute_sha1_hash_over_range(sigil_t *sgl);
sigil_err_t compute_digest_pkcs1(sigil_t *sgl);
sigil_err_t load_certificates(sigil_t *sgl);
sigil_err_t load_digest(sigil_t *sgl);
sigil_err_t verify_signing_certificate(sigil_t *sgl);
int sigil_sigil_self_test(int verbosity);
sigil_err_t compare_digest(sigil_t *sgl);
sigil_err_t verify_digest(sigil_t *sgl, int *result);
int sigil_cryptography_self_test(int verbosity);
#endif /* PDF_SIGIL_CRYPTOGRAPHY_H */
......@@ -6,6 +6,6 @@
sigil_err_t process_sig_dict(sigil_t *sgl);
int sigil_sigil_self_test(int verbosity);
int sigil_sig_dict_self_test(int verbosity);
#endif /* PDF_SIGIL_SIG_DICT_H */
......@@ -2,6 +2,7 @@
#define PDF_SIGIL_TYPES_H
#include <openssl/evp.h> // EVP_MAX_MD_SIZE
#include <openssl/x509.h>
#include <stdint.h> // uint32_t
#include <stdio.h>
......@@ -28,9 +29,9 @@ typedef struct {
} contents_t;
typedef struct cert_t {
char *cert_hex;
X509 *x509;
size_t capacity;
char *cert_hex;
X509 *x509;
size_t capacity;
struct cert_t *next;
} cert_t;
......@@ -67,29 +68,31 @@ typedef struct {
} pdf_data_t;
typedef struct {
pdf_data_t pdf_data;
short pdf_x, /* numbers from PDF header */
pdf_y; /* %PDF-<pdf_x>.<pdf_y> */
short xref_type;
xref_t *xref;
reference_t ref_catalog_dict;
reference_t ref_acroform;
size_t offset_acroform;
reference_t ref_sig_field;
reference_t ref_sig_dict;
size_t offset_sig_dict;
ref_array_t fields;
size_t pdf_start_offset; /* offset of %PDF-x.y */
size_t startxref;
size_t sig_flags;
subfilter_t subfilter;
range_t *byte_range;
cert_t *certificates;
contents_t *contents;
unsigned char computed_hash[EVP_MAX_MD_SIZE];
unsigned int computed_hash_len;
int signing_cert_status;
X509_STORE *trusted_store;
pdf_data_t pdf_data;
short pdf_x, /* numbers from PDF header */
pdf_y; /* %PDF-<pdf_x>.<pdf_y> */
short xref_type;
xref_t *xref;
reference_t ref_catalog_dict;
reference_t ref_acroform;
size_t offset_acroform;
reference_t ref_sig_field;
reference_t ref_sig_dict;
size_t offset_sig_dict;
ref_array_t fields;
size_t pdf_start_offset; /* offset of %PDF-x.y */
size_t startxref;
size_t sig_flags;
subfilter_t subfilter;
range_t *byte_range;
cert_t *certificates;
contents_t *contents;
ASN1_OCTET_STRING *computed_digest;
int signing_cert_status;
int hash_cmp_result;
X509_ALGOR *md_algorithm;
ASN1_OCTET_STRING *md_hash;
X509_STORE *trusted_store;
} sigil_t;
#endif /* PDF_SIGIL_TYPES_H */
......@@ -638,6 +638,12 @@ const char *sigil_err_string(sigil_err_t err)
return "ERROR no data available";
case ERR_END_OF_DICT:
return "ERROR end of dictionary occured while processing it's content";
case ERR_NO_SIGNATURE:
return "ERROR this file appears to be missing the signature";
case ERR_OPENSSL:
return "ERROR something bad happened inside of OpenSSL functionality";
case ERR_DIGEST_TYPE:
return "ERROR the signature is using not standard message digest";
default:
return "ERROR unknown";
}
......
#include <openssl/asn1.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <types.h>
......@@ -50,46 +51,70 @@ sigil_err_t hex_to_dec(const char *in, size_t in_len, unsigned char *out, size_t
return ERR_NO;
}
void print_computed_hash(sigil_t *sgl)
{
if (sgl == NULL || sgl->computed_hash_len <= 0)
return;
printf("\nCOMPUTED HASH: ");
for (int i = 0; i < sgl->computed_hash_len; i++) {
printf("%02x ", sgl->computed_hash[i]);
}
printf("\n");
}
sigil_err_t compute_sha1_hash_over_range(sigil_t *sgl)
//void print_computed_hash(sigil_t *sgl)
//{
// if (sgl == NULL || sgl->computed_hash_len <= 0)
// return;
//
// printf("\nCOMPUTED HASH: ");
//
// for (int i = 0; i < sgl->computed_hash_len; i++) {
// printf("%02x ", sgl->computed_hash[i]);
// }
//
// printf("\n");
//}
sigil_err_t compute_digest_pkcs1(sigil_t *sgl)
{
sigil_err_t err;
EVP_MD_CTX *ctx;
char *update_data = NULL;
EVP_MD_CTX *ctx = NULL;
const EVP_MD *evp_md;
const ASN1_OBJECT *md_obj = NULL;
range_t *range;
size_t bytes_left;
char *current_data;
size_t current_length;
size_t read_size;
unsigned char tmp_hash[EVP_MAX_MD_SIZE];
unsigned int tmp_hash_len;
if (sgl == NULL || sgl->byte_range == NULL)
return ERR_PARAMETER;
current_data = malloc(sizeof(*current_data) * (HASH_UPDATE_SIZE + 1));
if (current_data == NULL)
return ERR_ALLOCATION;
update_data = malloc(sizeof(*update_data) * (HASH_UPDATE_SIZE + 1));
if (update_data == NULL) {
err = ERR_ALLOCATION;
goto end;
}
sigil_zeroize(current_data, sizeof(*current_data) * (HASH_UPDATE_SIZE + 1));
sigil_zeroize(update_data, sizeof(*update_data) * (HASH_UPDATE_SIZE + 1));
// initialize context
// initialize digest context
if ((ctx = EVP_MD_CTX_create()) == NULL)
return ERR_ALLOCATION;
if(EVP_DigestInit_ex(ctx, EVP_sha1(), NULL) != 1) {
X509_ALGOR_get0(&md_obj, NULL, NULL, sgl->md_algorithm);
evp_md = EVP_get_digestbyobj(md_obj);
if (evp_md == NULL) {
err = ERR_OPENSSL;
goto failed;
goto end;
}
// only allowed algorithms
if (EVP_MD_type(evp_md) != NID_sha1 &&
EVP_MD_type(evp_md) != NID_sha256 &&
EVP_MD_type(evp_md) != NID_sha384 &&
EVP_MD_type(evp_md) != NID_sha512 &&
EVP_MD_type(evp_md) != NID_ripemd160)
{
err = ERR_DIGEST_TYPE;
goto end;
}
if (EVP_DigestInit_ex(ctx, evp_md, NULL) != 1) {
err = ERR_OPENSSL;
goto end;
}
range = sgl->byte_range;
......@@ -104,15 +129,15 @@ sigil_err_t compute_sha1_hash_over_range(sigil_t *sgl)
while (bytes_left > 0) {
current_length = MIN(HASH_UPDATE_SIZE, bytes_left);
err = pdf_read(sgl, current_length, current_data, &read_size);
err = pdf_read(sgl, current_length, update_data, &read_size);
if (err != ERR_NO)
return err;
if (current_length != read_size)
return ERR_IO;
if (EVP_DigestUpdate(ctx, current_data, current_length) != 1) {
if (EVP_DigestUpdate(ctx, update_data, current_length) != 1) {
err = ERR_OPENSSL;
goto failed;
goto end;
}
bytes_left -= current_length;
......@@ -122,23 +147,25 @@ sigil_err_t compute_sha1_hash_over_range(sigil_t *sgl)
}
// process last pieces of data from context
if (EVP_DigestFinal_ex(ctx, sgl->computed_hash, &(sgl->computed_hash_len)) != 1) {
if (EVP_DigestFinal_ex(ctx, tmp_hash, &tmp_hash_len) != 1) {
err = ERR_OPENSSL;
goto failed;
goto end;
}
free(current_data);
EVP_MD_CTX_destroy(ctx);
sgl->computed_digest = ASN1_OCTET_STRING_new();
if (ASN1_OCTET_STRING_set(sgl->computed_digest, tmp_hash, tmp_hash_len) == 0) {
err = ERR_OPENSSL;
goto end;
}
return ERR_NO;
err = ERR_NO;
failed:
end:
if (update_data != NULL)
free(update_data);
if (ctx != NULL)
EVP_MD_CTX_destroy(ctx);
free(current_data);
return err;
}
......@@ -191,6 +218,114 @@ sigil_err_t load_certificates(sigil_t *sgl)
return ERR_NO;
}
sigil_err_t load_digest(sigil_t *sgl)
{
sigil_err_t err;
char *contents;
size_t contents_len;
unsigned char *tmp_contents = NULL;
size_t tmp_contents_len;
const unsigned char *const_tmp;
ASN1_OCTET_STRING *oc_str = NULL;
EVP_PKEY *pub_key = NULL;
RSA *rsa = NULL;
unsigned char *rsa_out_str = NULL;
int rsa_out_len;
const unsigned char *const_rsa_out;
X509_SIG *sig = NULL;
const X509_SIG *const_sig = NULL;
const X509_ALGOR *tmp_alg = NULL;
const ASN1_OCTET_STRING *tmp_hash = NULL;
contents = sgl->contents->contents_hex;
contents_len = strlen(contents);
tmp_contents = malloc(sizeof(*contents) * ((contents_len + 1) / 2 + 1));
if (tmp_contents == NULL) {
err = ERR_ALLOCATION;
goto end;
}
sigil_zeroize(tmp_contents, sizeof(*contents) * ((contents_len + 1) / 2 + 1));
err = hex_to_dec(contents, contents_len, tmp_contents, &tmp_contents_len);
if (err != ERR_NO)
goto end;
const_tmp = tmp_contents;
oc_str = d2i_ASN1_OCTET_STRING(NULL, &const_tmp, tmp_contents_len);
if (oc_str == NULL) {
err = ERR_OPENSSL;
goto end;
}
pub_key = X509_get_pubkey(sgl->certificates->x509);
if (pub_key == NULL) {
err = ERR_OPENSSL;
goto end;
}
rsa = EVP_PKEY_get1_RSA(pub_key);
if (rsa == NULL) {
err = ERR_OPENSSL;
goto end;
}
rsa_out_len = RSA_size(rsa) - 11;
if (rsa_out_len <= 0) {
err = ERR_OPENSSL;
goto end;
}
rsa_out_str = malloc(sizeof(*rsa_out_str) * rsa_out_len);
if (rsa_out_str == NULL) {
err = ERR_OPENSSL;
goto end;
}
sigil_zeroize(rsa_out_str, sizeof(*rsa_out_str) * rsa_out_len);
rsa_out_len = RSA_public_decrypt(ASN1_STRING_length(oc_str),
oc_str->data,
rsa_out_str,
rsa,
RSA_PKCS1_PADDING);
const_rsa_out = rsa_out_str;
sig = d2i_X509_SIG(NULL, &const_rsa_out, rsa_out_len);
if (sig == NULL) {
err = ERR_OPENSSL;
goto end;
}
const_sig = sig;
X509_SIG_get0(const_sig, &tmp_alg, &tmp_hash);
sgl->md_algorithm = X509_ALGOR_dup((X509_ALGOR *)tmp_alg);
sgl->md_hash = ASN1_OCTET_STRING_dup(tmp_hash);
err = ERR_NO;
end:
if (tmp_contents != NULL)
free(tmp_contents);
if (oc_str != NULL)
ASN1_OCTET_STRING_free(oc_str);
if (pub_key != NULL)
EVP_PKEY_free(pub_key);
if (rsa != NULL)
RSA_free(rsa);
if (rsa_out_str != NULL)
free(rsa_out_str);
if (sig != NULL)
X509_SIG_free(sig);
return err;
}
sigil_err_t verify_signing_certificate(sigil_t *sgl)
{
X509_STORE_CTX *ctx;
......@@ -244,3 +379,32 @@ sigil_err_t verify_signing_certificate(sigil_t *sgl)
return ERR_NO;
}
sigil_err_t compare_digest(sigil_t *sgl)
{
if (sgl == NULL)
return ERR_PARAMETER;
sgl->hash_cmp_result = HASH_CMP_RESULT_DIFFER;
if (ASN1_STRING_cmp(sgl->md_hash, sgl->computed_digest) == 0)
sgl->hash_cmp_result = HASH_CMP_RESULT_MATCH;
return ERR_NO;
}
sigil_err_t verify_digest(sigil_t *sgl, int *result)
{
sigil_err_t err;
*result = 1;
if (sgl == NULL)
return ERR_PARAMETER;
err = compute_digest_pkcs1(sgl);
if (err != ERR_NO)
return err;
return compare_digest(sgl);
}
......@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include <types.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include "acroform.h"
#include "auxiliary.h"
......@@ -17,6 +18,8 @@
#include "types.h"
#include "xref.h"
#include <openssl/err.h>
sigil_err_t sigil_init(sigil_t **sgl)
{
// function parameter checks
......@@ -28,8 +31,6 @@ sigil_err_t sigil_init(sigil_t **sgl)
return ERR_ALLOCATION;
sigil_zeroize(*sgl, sizeof(*sgl));
sigil_zeroize((*sgl)->computed_hash,
sizeof(*(*sgl)->computed_hash) * EVP_MAX_MD_SIZE);
// set default values
(*sgl)->pdf_data.file = NULL;
......@@ -60,8 +61,11 @@ sigil_err_t sigil_init(sigil_t **sgl)
(*sgl)->byte_range = NULL;
(*sgl)->certificates = NULL;
(*sgl)->contents = NULL;
(*sgl)->computed_hash_len = 0;
(*sgl)->computed_digest = NULL;
(*sgl)->signing_cert_status = CERT_STATUS_UNKNOWN;
(*sgl)->hash_cmp_result = HASH_CMP_RESULT_UNKNOWN;
(*sgl)->md_algorithm = NULL;
(*sgl)->md_hash = NULL;
(*sgl)->trusted_store = X509_STORE_new();
......@@ -160,7 +164,7 @@ sigil_err_t sigil_set_pdf_path(sigil_t *sgl, const char *path_to_pdf)
{
free(path_to_pdf_win);
return ERR_IO;
}
}// MultiByteToWideChar TODO
if (_wfopen_s(&pdf_file, path_to_pdf_win, L"rb") != 0) {
free(path_to_pdf_win);
......@@ -235,13 +239,6 @@ static sigil_err_t sigil_verify_adbe_x509_rsa_sha1(sigil_t *sgl)
{
sigil_err_t err;
err = compute_sha1_hash_over_range(sgl);
if (err != ERR_NO)
return err;
// TODO remove
print_computed_hash(sgl);
err = load_certificates(sgl);
if (err != ERR_NO)
return err;
......@@ -250,7 +247,11 @@ static sigil_err_t sigil_verify_adbe_x509_rsa_sha1(sigil_t *sgl)
if (err != ERR_NO)
return err;
err = load_digest(sgl);
if (err != ERR_NO)
return err;
return verify_digest(sgl, &(sgl->hash_cmp_result));
}
sigil_err_t sigil_verify(sigil_t *sgl)
......@@ -411,6 +412,15 @@ void sigil_free(sigil_t **sgl)
if ((*sgl)->contents != NULL)
contents_free(*sgl);
if ((*sgl)->computed_digest != NULL)
ASN1_OCTET_STRING_free((*sgl)->computed_digest);
if ((*sgl)->md_algorithm != NULL)
X509_ALGOR_free((*sgl)->md_algorithm);
if ((*sgl)->md_hash != NULL)
ASN1_OCTET_STRING_free((*sgl)->md_hash);
if ((*sgl)->trusted_store != NULL)
X509_STORE_free((*sgl)->trusted_store);
......@@ -470,7 +480,7 @@ int sigil_sigil_self_test(int verbosity)
if (sigil_set_trusted_default_system(sgl) != ERR_NO)
goto failed;
if (sigil_verify(sgl) != ERR_NO || 1)
if (sigil_verify(sgl) != ERR_NO)
goto failed;
// TODO test verification result
......
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