Exemplo n.º 1
0
static void testVerifyPublicationsFileWithNoConstraints(CuTest *tc) {
	int res;
	KSI_PublicationsFile *pubFile = NULL;
	KSI_CertConstraint arr[] = {
			{NULL, NULL},
			{NULL, NULL}
	};

	KSI_ERR_clearErrors(ctx);


	res = KSI_PublicationsFile_fromFile(ctx, getFullResourcePath(TEST_PUBLICATIONS_FILE), &pubFile);
	CuAssert(tc, "Unable to read publications file", res == KSI_OK && pubFile != NULL);

	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to delete OID 1.2.840.113549.1.9.1", res == KSI_OK);

	/* Verification should not fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file may not verify with no constraints.", res == KSI_PUBFILE_VERIFICATION_NOT_CONFIGURED);

	arr[0].oid = KSI_CERT_EMAIL;
	arr[0].val = "*****@*****.**";
	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to set OID 1.2.840.113549.1.9.1 back to normal", res == KSI_OK);

	/* Verification should not fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file must verify with e-mail.", res == KSI_OK);

	KSI_PublicationsFile_free(pubFile);
}
Exemplo n.º 2
0
static void testVerifyPublicationsFileWithAttributeNotPresent(CuTest *tc) {
	int res;
	KSI_PublicationsFile *pubFile = NULL;
	KSI_CertConstraint arr[] = {
			{NULL, NULL},
			{NULL, NULL}
	};

	KSI_ERR_clearErrors(ctx);


	res = KSI_PublicationsFile_fromFile(ctx, getFullResourcePath(TEST_PUBLICATIONS_FILE), &pubFile);
	CuAssert(tc, "Unable to read publications file", res == KSI_OK && pubFile != NULL);

	arr[0].oid = "2.5.4.9";
	arr[0].val = "Local pub";
	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to delete OID 2.5.4.9", res == KSI_OK);

	/* Verification should fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file must verify with address.", res != KSI_OK);

	arr[0].oid = KSI_CERT_EMAIL;
	arr[0].val = "*****@*****.**";
	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to set OID 2.5.4.9 back to normal", res == KSI_OK);

	/* Verification should not fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file must verify.", res == KSI_OK);

	KSI_PublicationsFile_free(pubFile);
}
Exemplo n.º 3
0
static void testVerifyPublicationsFileWithOrganization(CuTest *tc) {
	int res;
	KSI_PublicationsFile *pubFile = NULL;
	KSI_CertConstraint arr[] = {
			{KSI_CERT_EMAIL, "*****@*****.**"},
			{NULL, NULL},
			{NULL, NULL}
	};

	KSI_ERR_clearErrors(ctx);


	res = KSI_PublicationsFile_fromFile(ctx, getFullResourcePath(TEST_PUBLICATIONS_FILE), &pubFile);
	CuAssert(tc, "Unable to read publications file", res == KSI_OK && pubFile != NULL);

	arr[1].oid = KSI_CERT_ORGANIZATION;
	arr[1].val = "Guardtime AS";
	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to set OID 2.5.4.10", res == KSI_OK);

	/* Verification should fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file must verify with OID='2.5.4.10' value 'Guardtime AS'.", res == KSI_OK);

	arr[1].val = "Guardtime US";
	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to set OID 2.5.4.10", res == KSI_OK);

	/* Verification should fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file may not verify with wrong company'.", res != KSI_OK);
	/* Verification should succeed. */

	arr[1].oid = NULL;
	arr[1].val = NULL;
	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to set OID 2.5.4.10", res == KSI_OK);

	/* Verification should fail. */
	res = KSI_PublicationsFile_verify(pubFile, ctx);
	CuAssert(tc, "Publications file must verify with OID='2.5.4.10' removed from the constraints", res == KSI_OK);

	CuAssert(tc, "Publications file should verify with mock certificate.", res == KSI_OK);

	KSI_PublicationsFile_free(pubFile);
}
Exemplo n.º 4
0
int KSI_CTX_setPublicationCertEmail(KSI_CTX *ctx, const char *email) {
	int res = KSI_UNKNOWN_ERROR;
	char *tmp = NULL;
	KSI_CertConstraint arr[] = {
			{ KSI_CERT_EMAIL, NULL },
			{ NULL, NULL }
	};

	if (ctx == NULL || email == NULL) {
		res = KSI_INVALID_ARGUMENT;
		goto cleanup;
	}

	/* The value is only read - cast is safe. */
	arr[0].val = (char *) email;

	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	if (res != KSI_OK) {
		KSI_pushError(ctx, res, NULL);
		goto cleanup;
	}

	/* Keep the value for compatibility. */
	if (email != NULL && email[0] != '\0') {
		size_t len = strlen(email);
		tmp = KSI_calloc(len + 1, 1);
		if (tmp == NULL) {
			res = KSI_OUT_OF_MEMORY;
			goto cleanup;
		}

		memcpy(tmp, email, len + 1);
	}

	KSI_free(ctx->publicationCertEmail_DEPRECATED);
	ctx->publicationCertEmail_DEPRECATED = tmp;
	tmp = NULL;

	res = KSI_OK;
cleanup:
	KSI_free(tmp);
	return res;
}
Exemplo n.º 5
0
static void testReceivePublicationsFileInvalidPki(CuTest *tc) {
	int res;
	KSI_PublicationsFile *pubFile = NULL;
	KSI_PKITruststore *pki = NULL;
	KSI_CertConstraint arr[] = {
			{KSI_CERT_EMAIL, "*****@*****.**"},
			{NULL, NULL}
	};
	KSI_CTX *ctx = NULL;

	res = KSITest_CTX_clone(&ctx);
	CuAssert(tc, "Unable to create new context.", res == KSI_OK && ctx != NULL);

	KSI_ERR_clearErrors(ctx);

	res = KSI_CTX_setPublicationUrl(ctx, getFullResourcePathUri(TEST_PUBLICATIONS_FILE_INVALID_PKI));
	CuAssert(tc, "Unable to clear pubfile URI.", res == KSI_OK);

	/* Configure expected PIK cert and constraints for pub. file. */
	res = KSI_PKITruststore_new(ctx, 0, &pki);
	CuAssert(tc, "Unable to get PKI truststore from context.", res == KSI_OK && pki != NULL);

	res = KSI_CTX_setPKITruststore(ctx, pki);
	CuAssert(tc, "Unable to set new pki truststrore for ksi context.", res == KSI_OK);

	res = KSI_PKITruststore_addLookupFile(pki, getFullResourcePath("resource/tlv/mock.crt"));
	CuAssert(tc, "Unable to read certificate", res == KSI_OK);

	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, arr);
	CuAssert(tc, "Unable to set OID 2.5.4.10", res == KSI_OK);

	res = KSI_receivePublicationsFile(ctx, &pubFile);
	CuAssert(tc, "Unable to receive publications file.", res == KSI_OK && pubFile != NULL);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Publications file should NOT verify as PKI signature is wrong.", res == KSI_INVALID_PKI_SIGNATURE);

	KSI_PublicationsFile_free(pubFile);
	KSI_CTX_free(ctx);
}
Exemplo n.º 6
0
int main(int argc, char **argv) {
	KSI_CTX *ksi = NULL;
	int res;
	FILE *out = NULL;
	KSI_Signature *sig = NULL;
	KSI_Signature *ext = NULL;
	unsigned char *raw = NULL;
	size_t raw_len;
	unsigned count;
	FILE *logFile = NULL;

	const KSI_CertConstraint pubFileCertConstr[] = {
			{ KSI_CERT_EMAIL, "*****@*****.**"},
			{ NULL, NULL }
	};

	if (argc != 5) {
		printf("Usage:\n"
				"  %s <signature> <extended> <extender uri> <pub-file url>\n", argv[0]);
		res = KSI_INVALID_ARGUMENT;
		goto cleanup;
	}

	/* Init KSI context */
	res = KSI_CTX_new(&ksi);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to init KSI context.\n");
		goto cleanup;
	}

	logFile = fopen("ksi_extend.log", "w");
	if (logFile == NULL) {
		fprintf(stderr, "Unable to open log file.\n");
	}

	res = KSI_CTX_setDefaultPubFileCertConstraints(ksi, pubFileCertConstr);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to configure publications file cert constraints.\n");
		goto cleanup;
	}

	KSI_CTX_setLoggerCallback(ksi, KSI_LOG_StreamLogger, logFile);
	KSI_CTX_setLogLevel(ksi, KSI_LOG_DEBUG);

	KSI_LOG_info(ksi, "Using KSI version: '%s'", KSI_getVersion());

	res = KSI_CTX_setExtender(ksi, argv[3], "anon", "anon");
	if (res != KSI_OK) goto cleanup;

	res = KSI_CTX_setPublicationUrl(ksi, argv[4]);
	if (res != KSI_OK) goto cleanup;

	/* Read the signature. */
	res = KSI_Signature_fromFile(ksi, argv[1], &sig);
	if (res != KSI_OK) {
		KSI_ERR_statusDump(ksi, stdout);
		fprintf(stderr, "Unable to read signature from '%s'\n", argv[1]);
		goto cleanup;
	}

	/* Make sure the signature is ok. */
	res = KSI_verifySignature(ksi, sig);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to verify signature.\n");
		KSI_ERR_statusDump(ksi, stderr);
		goto cleanup;
	}

	/* Extend the signature. */
	res = KSI_extendSignature(ksi, sig, &ext);
	if (res != KSI_OK) {
		if (res == KSI_EXTEND_NO_SUITABLE_PUBLICATION) {
			printf("No suitable publication to extend to.\n");
			goto cleanup;
		}
		fprintf(stderr, "Unable to extend signature.\n");
		KSI_ERR_statusDump(ksi, stderr);
		goto cleanup;
	}

	/* To be extra sure, lets verify the extended signature. */
	res = KSI_verifySignature(ksi, ext);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to verify the extended signature.\n");
		KSI_ERR_statusDump(ksi, stderr);
		goto cleanup;
	}

	/* Serialize the extended signature. */
	res = KSI_Signature_serialize(ext, &raw, &raw_len);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to serialize extended signature.\n");
		goto cleanup;
	}

	/* Open output file. */
	out = fopen(argv[2], "wb");
	if (out == NULL) {
		fprintf(stderr, "Unable to open output file '%s'\n", argv[2]);
		res = KSI_IO_ERROR;
		goto cleanup;
	}

	count = (unsigned) fwrite(raw, 1, raw_len, out);
	if (count != raw_len) {
		fprintf(stderr, "Failed to write output file.\n");
		res = KSI_IO_ERROR;
		goto cleanup;
	}

	printf("Signature extended.");

cleanup:

	if (logFile != NULL) fclose(logFile);
	if (out != NULL) fclose(out);
	KSI_Signature_free(sig);
	KSI_Signature_free(ext);
	KSI_free(raw);
	KSI_CTX_free(ksi);

	return res;
}
Exemplo n.º 7
0
static void testVerifyPublicationsFileWithFileSpecificConstraints(CuTest *tc) {
	int res;
	KSI_PublicationsFile *pubFile = NULL;
	KSI_CertConstraint empty[] = {
			{NULL, NULL},
			{NULL, NULL}
	};
	KSI_CertConstraint email[] = {
			{KSI_CERT_EMAIL, "*****@*****.**"},
			{NULL, NULL}
	};

	KSI_CertConstraint wrong[] = {
			{KSI_CERT_EMAIL, "*****@*****.**"},
			{NULL, NULL}
	};

	KSI_CertConstraint unknown[] = {
			{"3.2.840.113549.1.9.1", "*****@*****.**"},
			{NULL, NULL}
	};

	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, email);
	CuAssert(tc, "Unable to set default certificate constraints", res == KSI_OK);

	res = KSI_PublicationsFile_fromFile(ctx, getFullResourcePath(TEST_PUBLICATIONS_FILE), &pubFile);
	CuAssert(tc, "Unable to read publications file", res == KSI_OK && pubFile != NULL);

	res = KSI_PublicationsFile_setCertConstraints(pubFile, NULL);
	CuAssert(tc, "Unable to set publications file certificate constraints.", res == KSI_OK);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Unable to verify publications file with context based constraints.", res == KSI_OK);

	res = KSI_PublicationsFile_setCertConstraints(pubFile, empty);
	CuAssert(tc, "Unable to set publications file certificate constraints.", res == KSI_OK);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Publications file should not verify with empty certificate constraints.", res != KSI_OK);

	res = KSI_PublicationsFile_setCertConstraints(pubFile, email);
	CuAssert(tc, "Unable to set publications file certificate constraints.", res == KSI_OK);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Unable to verify publications file with email.", res == KSI_OK);

	res = KSI_PublicationsFile_setCertConstraints(pubFile, wrong);
	CuAssert(tc, "Unable to set publications file certificate constraints.", res == KSI_OK);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Publications file should not verify with wrong certificate constraints.", res != KSI_OK);

	res = KSI_PublicationsFile_setCertConstraints(pubFile, unknown);
	CuAssert(tc, "Unable to set publications file certificate constraints.", res == KSI_OK);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Publications file should not verify with unknown certificate constraints.", res == KSI_INVALID_ARGUMENT);

	res = KSI_CTX_setDefaultPubFileCertConstraints(ctx, wrong);
	CuAssert(tc, "Unable to set default certificate constraints", res == KSI_OK);

	res = KSI_PublicationsFile_setCertConstraints(pubFile, email);
	CuAssert(tc, "Unable to set publications file certificate constraints.", res == KSI_OK);

	res = KSI_verifyPublicationsFile(ctx, pubFile);
	CuAssert(tc, "Unable to verify publications file with email.", res == KSI_OK);

	KSI_PublicationsFile_free(pubFile);
}
Exemplo n.º 8
0
int main(int argc, char **argv) {
	KSI_CTX *ksi = NULL;
	int res = KSI_UNKNOWN_ERROR;

	KSI_DataHash *hsh = NULL;
	KSI_RequestHandle *handle[REQUESTS];
	KSI_NetworkClient *http = NULL;

	FILE *logFile = NULL;

	size_t i;

	KSI_DataHasher *hsr = NULL;

	const KSI_CertConstraint pubFileCertConstr[] = {
			{ KSI_CERT_EMAIL, "*****@*****.**"},
			{ NULL, NULL }
	};

	struct {
		size_t ok;
		size_t nok;
	} stat;

	stat.ok = 0;
	stat.nok = 0;




	/* Create new KSI context for this thread. */
	res = KSI_CTX_new(&ksi);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to create context.\n");
		goto cleanup;
	}

	res = KSI_CTX_setDefaultPubFileCertConstraints(ksi, pubFileCertConstr);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to configure publications file cert constraints.\n");
		goto cleanup;
	}

	/* Configure the logger. */
	res = OpenLogging(ksi, "multi_curl.log", &logFile);
	if (res != KSI_OK) goto cleanup;


	KSI_LOG_info(ksi, "Using KSI version: '%s'", KSI_getVersion());

	/* Check publications file url. */
	res = KSI_CTX_setPublicationUrl(ksi, "http://verify.guardtime.com/ksi-publications.bin");
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to set publications file url.\n");
		goto cleanup;
	}

	res = KSI_HttpClient_new(ksi, &http);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to create http client.\n");
		goto cleanup;
	}

	res = KSI_HttpClient_setAggregator(http, "http://ksigw.test.guardtime.com:3332", "anon", "anon");
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to set aggregator url.\n");
		goto cleanup;
	}

	KSI_HttpClient_setReadTimeoutSeconds(http, 10);
	KSI_HttpClient_setConnectTimeoutSeconds(http, 10);

	for (i = 0; i < REQUESTS; i++) {
		char buf[100];
		size_t len;
		KSI_AggregationReq *req = NULL;

		len = KSI_snprintf(buf, sizeof(buf), "Hello %d", i);
		res = KSI_DataHash_create(ksi, buf, len, KSI_getHashAlgorithmByName("default"), &hsh);
		if (res != KSI_OK) {
			fprintf(stderr, "Unable to create hash.");
			goto cleanup;
		}

		res = KSI_AggregationReq_new(ksi, &req);
		if (res != KSI_OK) {
			fprintf(stderr, "Unable to create request.");
			goto cleanup;
		}

		res = KSI_AggregationReq_setRequestHash(req, hsh);
		if (res != KSI_OK) {
			fprintf(stderr, "Unable to set request hash.");
			goto cleanup;
		}

		res = KSI_NetworkClient_sendSignRequest(http, req, &handle[i]);
		if (res != KSI_OK) {
			fprintf(stderr, "Unable to send aggregation request.");
			goto cleanup;
		}

		KSI_AggregationReq_free(req);
	}

	res = KSI_NetworkClient_performAll(http, handle, REQUESTS);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to perform requests.");
		goto cleanup;
	}

	for (i = 0; i < REQUESTS; i++) {
		KSI_AggregationResp *resp = NULL;
		res = KSI_RequestHandle_getAggregationResponse(handle[i], &resp);
		if (res != KSI_OK) {
			const KSI_RequestHandleStatus *st = NULL;
			res = KSI_RequestHandle_getResponseStatus(handle[i], &st);
			if (res == KSI_OK) {
				printf("Status code = %ld: %s\n", st->code, st->errm);
			}

			KSI_ERR_statusDump(ksi, stdout);
			stat.nok++;
		} else {
			stat.ok ++;
		}

		KSI_AggregationResp_free(resp);
	}

	printf("Requests:\n"
			"  Successful: %llu\n"
			"      Failed: %llu\n"
			"       TOTAL: %llu\n", (unsigned long long)stat.ok, (unsigned long long)stat.nok, (unsigned long long)(stat.ok + stat.nok));

	res = KSI_OK;

cleanup:

	if (logFile != NULL) fclose(logFile);

	if (res != KSI_OK && ksi != NULL) {
		KSI_ERR_statusDump(ksi, stderr);
	}

	KSI_DataHash_free(hsh);
	KSI_DataHasher_free(hsr);

	KSI_CTX_free(ksi);

	return res;

}
Exemplo n.º 9
0
int main(int argc, char **argv) {
	int res = KSI_UNKNOWN_ERROR;
	/* Signature read from the file. */
	KSI_Signature *sig = NULL;
	
	/* Signature extended to the publication. */
	KSI_Signature *ext = NULL;

	/* Hash of the data file. */
	KSI_DataHash *hsh = NULL;

	/* Hash value extracted from the signature. */
	KSI_DataHash *signHsh = NULL;

	/* Data file hasher. */
	KSI_DataHasher *hsr = NULL;

	/* Input file descriptor. */
	FILE *in = NULL;

	/* Buffer for reading the input. */
	unsigned char buf[1024];

	/* Length of the buffer content. */
	size_t buf_len;

	/* Verification info object. */
	const KSI_VerificationResult *info = NULL;

	/* File descriptor for logging. */
	FILE *logFile = NULL;

	const KSI_CertConstraint pubFileCertConstr[] = {
			{ KSI_CERT_EMAIL, "*****@*****.**"},
			{ NULL, NULL }
	};

	/* Init context. */
	res = KSI_CTX_new(&ksi);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to init KSI context.\n");
		goto cleanup;
	}

	logFile = fopen("ksi_verify.log", "w");
	if (logFile == NULL) {
		fprintf(stderr, "Unable to open log file.\n");
	}

	res = KSI_CTX_setDefaultPubFileCertConstraints(ksi, pubFileCertConstr);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to configure publications file cert constraints.\n");
		goto cleanup;
	}

	/* Configure the logger. */
	KSI_CTX_setLoggerCallback(ksi, KSI_LOG_StreamLogger, logFile);
	KSI_CTX_setLogLevel(ksi, KSI_LOG_DEBUG);

	KSI_LOG_info(ksi, "Using KSI version: '%s'", KSI_getVersion());

	/* Check parameters. */
	if (argc != 6) {
		fprintf(stderr, "Usage\n"
				"  %s <data file | -> <signature> <publication-str> <extender url> <pub-file url>\n", argv[0]);
		goto cleanup;
	}

	/* Configure extender. */
	res = KSI_CTX_setExtender(ksi, argv[4], "anon", "anon");
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to set extender parameters.\n");
		goto cleanup;
	}

	/* Set the publications file url. */
	res = KSI_CTX_setPublicationUrl(ksi, argv[4]);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to set publications file url.\n");
		goto cleanup;
	}

	printf("Reading signature... ");
	/* Read the signature. */
	res = KSI_Signature_fromFile(ksi, argv[2], &sig);
	if (res != KSI_OK) {
		printf("failed (%s)\n", KSI_getErrorString(res));
		goto cleanup;
	}
	printf("ok\n");

	printf("Verifying the signature with the publication... ");
	res = extendToPublication(sig, argv[3], &ext);
	switch (res) {
		case KSI_OK:
			printf("ok\n");
			break;
		case KSI_VERIFICATION_FAILURE:
			printf("failed\n");
			break;
		default:
			printf("failed (%s)\n", KSI_getErrorString(res));
			goto cleanup;
	}

	/* Create hasher. */
	res = KSI_Signature_createDataHasher(ext, &hsr);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to create data hasher.\n");
		goto cleanup;
	}

	if (strcmp(argv[1], "-")) {
		in = fopen(argv[1], "rb");
		if (in == NULL) {
			fprintf(stderr, "Unable to open data file '%s'.\n", argv[1]);
			goto cleanup;
		}
		/* Calculate the hash of the document. */
		while (!feof(in)) {
			buf_len = fread(buf, 1, sizeof(buf), in);
			res = KSI_DataHasher_add(hsr, buf, buf_len);
			if (res != KSI_OK) {
				fprintf(stderr, "Unable hash the document.\n");
				goto cleanup;
			}
		}

		/* Finalize the hash computation. */
		res = KSI_DataHasher_close(hsr, &hsh);
		if (res != KSI_OK) {
			fprintf(stderr, "Failed to close the hashing process.\n");
			goto cleanup;
		}

		res = KSI_Signature_getDocumentHash(sig, &signHsh);
		if (res != KSI_OK) goto cleanup;

		printf("Verifying document hash... ");
		if (!KSI_DataHash_equals(hsh, signHsh)) {
			printf("Wrong document!\n");
			goto cleanup;
		}
		printf("ok\n");
	} 

	res = KSI_Signature_getVerificationResult(ext, &info);
	if (res != KSI_OK) goto cleanup;

	if (info != NULL) {
		size_t i;
		printf("Verification info:\n");
		for (i = 0; i < KSI_VerificationResult_getStepResultCount(info); i++) {
			const KSI_VerificationStepResult *result = NULL;
			const char *desc = NULL;
			res = KSI_VerificationResult_getStepResult(info, i, &result);
			if (res != KSI_OK) goto cleanup;
			printf("\t0x%02x:\t%s", KSI_VerificationStepResult_getStep(result), KSI_VerificationStepResult_isSuccess(result) ? "OK" : "FAIL");
			desc = KSI_VerificationStepResult_getDescription(result);
			if (desc && *desc) {
				printf(" (%s)", desc);
			}
			printf("\n");
		}
	}

	res = KSI_OK;

cleanup:

	if (logFile != NULL) fclose(logFile);
	if (res != KSI_OK && ksi != NULL) {
		KSI_ERR_statusDump(ksi, stderr);
	}

	if (in != NULL) fclose(in);

	KSI_Signature_free(sig);
	KSI_Signature_free(ext);
	KSI_DataHasher_free(hsr);
	KSI_DataHash_free(hsh);
	KSI_CTX_free(ksi);

	return res;
}