Beispiel #1
0
void KSI_CTX_free(KSI_CTX *ctx) {
	if (ctx != NULL) {
		/* Call cleanup methods. */
		globalCleanup(ctx);

		KSI_List_free(ctx->cleanupFnList);

		KSI_free(ctx->errors);

		KSI_NetworkClient_free(ctx->netProvider);
		KSI_PKITruststore_free(ctx->pkiTruststore);

		KSI_PublicationsFile_free(ctx->publicationsFile);
		KSI_free(ctx->publicationCertEmail_DEPRECATED);

		freeCertConstraintsArray(ctx->certConstraints);
		KSI_Signature_free(ctx->lastFailedSignature);

		KSI_free(ctx);
	}
}
static void testSingle(CuTest *tc) {
#define TEST_AGGR_RESPONSE_FILE  "resource/tlv/ok-sig-2014-07-01.1-aggr_response.tlv"
	int res = KSI_UNKNOWN_ERROR;
	KSI_BlockSigner *bs = NULL;
	KSI_DataHash *hsh = NULL;
	KSI_BlockSignerHandle *h = NULL;
	KSI_Signature *sig = NULL;
	unsigned char *raw = NULL;
	size_t len = 0;

	res = KSI_CTX_setAggregator(ctx, getFullResourcePathUri(TEST_AGGR_RESPONSE_FILE), TEST_USER, TEST_PASS);
	CuAssert(tc, "Unable to set aggregator file URI.", res == KSI_OK);

	res = KSITest_DataHash_fromStr(ctx, "0111a700b0c8066c47ecba05ed37bc14dcadb238552d86c659342d1d7e87b8772d", &hsh);
	CuAssert(tc, "Unable to create data hash.", res == KSI_OK && hsh != NULL);

	res = KSI_BlockSigner_new(ctx, KSI_HASHALG_SHA1, NULL, NULL, &bs);
	CuAssert(tc, "Unable to create block signer instance.", res == KSI_OK && bs != NULL);

	res = KSI_BlockSigner_addLeaf(bs, hsh, 0, NULL, &h);
	CuAssert(tc, "Unable to add hash to the blocksigner.", res == KSI_OK && h != NULL);

	res = KSI_BlockSigner_close(bs, NULL);
	CuAssert(tc, "Unable to close blocksigner.", res == KSI_OK);

	res = KSI_BlockSignerHandle_getSignature(h, &sig);
	CuAssert(tc, "Unable to extract signature from the blocksigner.", res == KSI_OK && sig != NULL);

	res = KSI_Signature_serialize(sig, &raw, &len);
	CuAssert(tc, "Unable to serialize signature.", res == KSI_OK && raw != NULL && len > 0);

	KSI_LOG_logBlob(ctx, KSI_LOG_DEBUG, "Serialized single signature from block signer.", raw, len);

	KSI_BlockSignerHandle_free(h);
	KSI_Signature_free(sig);
	KSI_BlockSigner_free(bs);
	KSI_DataHash_free(hsh);
	KSI_free(raw);
#undef TEST_AGGR_RESPONSE_FILE
}
Beispiel #3
0
int main(int argc, char **argv) {
	KSI_CTX *ksi = NULL;
	int res;
	FILE *out = NULL;
	KSI_Signature *sig = NULL;
	KSI_Signature *ext = NULL;
	KSI_HttpClient *net = NULL;
	unsigned char *raw = NULL;
	unsigned raw_len;
	unsigned count;
	FILE *logFile = NULL;

	if (argc != 5) {
		printf("Usage:\n"
				"  %s <signature> <extended> <extender uri> <pub-file uri| ->\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");
	}

	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_HttpClient_new(ksi, &net);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to create new network provider.\n");
		goto cleanup;
	}

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

	if (strcmp(argv[4], "-")) {
		res = KSI_HttpClient_setPublicationUrl(net, argv[4]);
		if (res != KSI_OK) goto cleanup;
	}

	res = KSI_CTX_setNetworkProvider(ksi, (KSI_NetworkClient *)net);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to set new network provider.\n");
		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;
}
Beispiel #4
0
int main(int argc, char **argv) {
	KSI_CTX *ksi = NULL;
	int res = KSI_UNKNOWN_ERROR;

	FILE *in = NULL;
	FILE *out = NULL;

	KSI_DataHasher *hsr = NULL;
	KSI_DataHash *hsh = NULL;
	KSI_Signature *sign = NULL;

	unsigned char *raw = NULL;
	unsigned raw_len;

	unsigned char buf[1024];
	unsigned buf_len;

	char *signerIdentity = NULL;

	FILE *logFile = NULL;

	/* Handle command line parameters */
	/* Handle command line parameters */
	if (argc != 7) {
		fprintf(stderr, "Usage:\n"
				"  %s <in-data-file> <out-sign-file> <aggregator-uri> <user> <pass> <pub-file url | -> \n", argv[0]);
		res = KSI_INVALID_ARGUMENT;
		goto cleanup;
	}

	/* Input file */
	in = fopen(argv[1], "rb");
	if (in == NULL) {
		fprintf(stderr, "Unable to open input file '%s'\n", argv[1]);
		res = KSI_IO_ERROR;
		goto cleanup;
	}

	/* 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;
	}

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

	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_setAggregator(ksi, argv[3], argv[4], argv[5]);
	if (res != KSI_OK) goto cleanup;

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

	/* Create a data hasher using default algorithm. */
	res = KSI_DataHasher_open(ksi, KSI_getHashAlgorithmByName("default"), &hsr);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to create hasher.\n");
		goto cleanup;
	}

	/* Read the input file and calculate the hash of its contents. */
	while (!feof(in)) {
		buf_len = (unsigned)fread(buf, 1, sizeof(buf), in);

		/* Add  next block to the calculation. */
		res = KSI_DataHasher_add(hsr, buf, buf_len);
		if (res != KSI_OK) {
			fprintf(stderr, "Unable to add data to hasher.\n");
			goto cleanup;
		}
	}

	/* Close the data hasher and retreive the data hash. */
	res = KSI_DataHasher_close(hsr, &hsh);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to create hash.\n");
		goto cleanup;
	}

	/* Sign the data hash. */
	res = KSI_createSignature(ksi, hsh, &sign);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to sign %d.\n", res);
		goto cleanup;
	}

	res = KSI_Signature_verify(sign, ksi);
	if (res != KSI_OK) {
		fprintf(stderr, "Failed to verify signature.\n");
		goto cleanup;
	}

	/* Output the signer id */
	res = KSI_Signature_getSignerIdentity(sign, &signerIdentity);
	if (res == KSI_OK) {
		printf("Signer id: %s\n", signerIdentity);
	} else {
		fprintf(stderr, "Unable to extract signer identity.\n");
	}
    
	/* Serialize the signature. */
	res = KSI_Signature_serialize(sign, &raw, &raw_len);
	if (res != KSI_OK) {
		fprintf(stderr, "Unable to serialize signature.");
		goto cleanup;
	}

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

	/* Write the signature file. */
	if (!fwrite(raw, 1, raw_len, out)) {
		fprintf(stderr, "Unable to write output file.\n");
		res = KSI_IO_ERROR;
		goto cleanup;
	}

	/* Only print message when signature output is not stdout. */
	if (out != NULL) {
		printf("Signature saved.\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);
	if (out != NULL) fclose(out);

	KSI_free(signerIdentity);

	KSI_Signature_free(sign);
	KSI_DataHash_free(hsh);
	KSI_DataHasher_free(hsr);

	KSI_free(raw);

	KSI_CTX_free(ksi);

	return res;

}
Beispiel #5
0
void KSI_SignatureBuilder_free(KSI_SignatureBuilder *builder) {
	if (builder != NULL) {
		KSI_Signature_free(builder->sig);
		KSI_free(builder);
	}
}
static void testMaskingMultiSig(CuTest *tc) {
#define TEST_AGGR_RESPONSE_FILE  "resource/tlv/ok-sig-2016-05-09.1-lvl4.ksig"
	static const unsigned char diceRolls[] = {0xd5, 0x58, 0xaf, 0xfa, 0x80, 0x67, 0xf4, 0x2c, 0xd9, 0x48, 0x36, 0x21, 0xd1, 0xab,
			0xae, 0x23, 0xed, 0xd6, 0xca, 0x04, 0x72, 0x7e, 0xcf, 0xc7, 0xdb, 0xc7, 0x6b, 0xde, 0x34, 0x77, 0x1e, 0x53};
	int res = KSI_UNKNOWN_ERROR;
	KSI_BlockSigner *bs = NULL;
	KSI_MultiSignature *ms = NULL;
	size_t i;
	KSI_DataHash *hsh = NULL;
	KSI_Signature *sig = NULL;
	KSI_DataHash *zero = NULL;
	KSI_OctetString *iv = NULL;
	KSI_DataHash *prevLeaf = NULL;

	/* Create zero hash. */
	res = KSI_DataHash_createZero(ctx, KSI_HASHALG_SHA2_512, &zero);
	CuAssert(tc, "Unable to create zero hash.", res == KSI_OK && zero != NULL);

	/* Create random initial vector. */
	res = KSI_OctetString_new(ctx, diceRolls, sizeof(diceRolls), &iv);
	CuAssert(tc, "Unable to create initial vector.", res == KSI_OK && iv != NULL);

	res = KSI_BlockSigner_new(ctx, KSI_HASHALG_SHA1, zero, iv, &bs);
	CuAssert(tc, "Unable to create block signer instance with masking.", res == KSI_OK && bs != NULL);

	res = KSI_BlockSigner_getPrevLeaf(bs, &prevLeaf);
	CuAssert(tc, "Unable to retrieve previous leaf.", res == KSI_OK && KSI_DataHash_equals(prevLeaf, zero));

	addInput(tc, bs, 0);

	res = KSI_CTX_setAggregator(ctx, getFullResourcePathUri(TEST_AGGR_RESPONSE_FILE), "anon", "anon");
	CuAssert(tc, "Unable to set aggregator file URI.", res == KSI_OK);

	res = KSI_BlockSigner_close(bs, &ms);
	CuAssert(tc, "Unable to close block signer and extract multi signature.", res == KSI_OK && ms != NULL);
	res = KSITest_setDefaultPubfileAndVerInfo(ctx);
	CuAssert(tc, "Unable to set default pubfile, default cert and default pki constraints.", res == KSI_OK);

	/* Lets loop over all the inputs and try to verify them. */
	for (i = 0; input_data[i] != NULL; i++) {
		res = KSI_DataHash_create(ctx, input_data[i], strlen(input_data[i]), KSI_HASHALG_SHA2_256, &hsh);
		CuAssert(tc, "Unable to create data hash.", res == KSI_OK && hsh != NULL);

		res = KSI_MultiSignature_get(ms, hsh, &sig);
		CuAssert(tc, "Unable to extract signature from the multi signature container.", res == KSI_OK && sig != NULL);

		res = KSI_Signature_verifyDocument(sig, ctx, (void *)input_data[i], strlen(input_data[i]));
		CuAssert(tc, "Unable to verify the input data.", res == KSI_OK);
		KSI_Signature_free(sig);
		sig = NULL;

		KSI_DataHash_free(hsh);
		hsh = NULL;
	}

	KSI_OctetString_free(iv);
	KSI_DataHash_free(zero);
	KSI_DataHash_free(prevLeaf);
	KSI_DataHash_free(hsh);
	KSI_MultiSignature_free(ms);
	KSI_BlockSigner_free(bs);
#undef TEST_AGGR_RESPONSE_FILE
}
static void testMedaData(CuTest *tc) {
#define TEST_AGGR_RESPONSE_FILE  "resource/tlv/test_meta_data_response.tlv"
	int res = KSI_UNKNOWN_ERROR;
	KSI_BlockSigner *bs = NULL;
	KSI_MetaData *md = NULL;
	char data[] = "LAPTOP";
	char *clientId[] = { "Alice", "Bob", "Claire", NULL };
	size_t i;
	KSI_DataHash *hsh = NULL;
	KSI_BlockSignerHandle *hndl[] = {NULL, NULL, NULL};
	KSI_Signature *sig = NULL;
	char *id = NULL;

	res = KSI_DataHash_create(ctx, data, strlen(data), KSI_HASHALG_SHA2_256, &hsh);
	CuAssert(tc, "Unable to create data hash.", res == KSI_OK && hsh != NULL);

	res = KSI_BlockSigner_new(ctx, KSI_HASHALG_SHA2_256, NULL, NULL, &bs);
	CuAssert(tc, "Unable to create block signer instance.", res == KSI_OK && bs != NULL);

	for (i = 0; clientId[i] != NULL; i++) {
		res = createMetaData(clientId[i], &md);
		CuAssert(tc, "Unable to create meta-data.", res == KSI_OK && md != NULL);

		res = KSI_BlockSigner_addLeaf(bs, hsh, 0, md, &hndl[i]);
		CuAssert(tc, "Unable to add leaf to the block signer.", res == KSI_OK && hndl[i] != NULL);

		KSI_MetaData_free(md);
		md = NULL;

	}

	res = KSI_CTX_setAggregator(ctx, getFullResourcePathUri(TEST_AGGR_RESPONSE_FILE), TEST_USER, TEST_PASS);
	CuAssert(tc, "Unable to set aggregator file URI.", res == KSI_OK);

	res = KSI_BlockSigner_close(bs, NULL);
	CuAssert(tc, "Unable to close the blocksigner.", res == KSI_OK);

	/* Loop over all the handles, and extract the signature. */
	for (i = 0; clientId[i] != NULL; i++) {
		char expId[0xff];

		/* Extract the signature. */
		res = KSI_BlockSignerHandle_getSignature(hndl[i], &sig);
		CuAssert(tc, "Unable to extract signature.", res == KSI_OK && sig != NULL);

		/* Verify the signature. */
		res = KSI_verifySignature(ctx, sig);
		CuAssert(tc, "Unable to verify the extracted signature.", res == KSI_OK);

		/* Extract the id attribution. */
		res = KSI_Signature_getSignerIdentity(sig, &id);
		CuAssert(tc, "Unable to extract the signer identity.", res == KSI_OK && id != NULL);

		/* Create the expected id value. */
		KSI_snprintf(expId, sizeof(expId), "%s :: %s", "GT :: GT :: release test :: anon http", clientId[i]);
		CuAssert(tc, "Client id not what expected.", !strcmp(id, expId));

		/* Cleanup. */
		KSI_Signature_free(sig);
		sig = NULL;

		KSI_free(id);
		id = NULL;

		KSI_BlockSignerHandle_free(hndl[i]);
	}

	KSI_DataHash_free(hsh);
	KSI_MetaData_free(md);
	KSI_BlockSigner_free(bs);
#undef TEST_AGGR_RESPONSE_FILE
}
Beispiel #8
0
int KSI_extendSignatureWithPolicy(KSI_CTX *ctx, KSI_Signature *sig, const KSI_Policy *policy, KSI_VerificationContext *context, KSI_Signature **extended) {
	int res = KSI_UNKNOWN_ERROR;
	KSI_PublicationsFile *pubFile = NULL;
	KSI_Integer *signingTime = NULL;
	KSI_PublicationRecord *pubRec = NULL;
	KSI_Signature *extSig = NULL;
	bool verifyPubFile = (ctx->publicationsFile == NULL);

	KSI_ERR_clearErrors(ctx);
	if (ctx == NULL || sig == NULL || extended == NULL) {
		KSI_pushError(ctx, res = KSI_INVALID_ARGUMENT, NULL);
		goto cleanup;
	}

	res = KSI_receivePublicationsFile(ctx, &pubFile);
	if (res != KSI_OK) {
		KSI_pushError(ctx,res, NULL);
		goto cleanup;
	}

	if (verifyPubFile == true) {
		res = KSI_verifyPublicationsFile(ctx, pubFile);
		if (res != KSI_OK) {
			KSI_pushError(ctx,res, NULL);
			goto cleanup;
		}
	}

	res = KSI_Signature_getSigningTime(sig, &signingTime);
	if (res != KSI_OK) {
		KSI_pushError(ctx,res, NULL);
		goto cleanup;
	}


	res = KSI_PublicationsFile_getNearestPublication(pubFile, signingTime, &pubRec);
	if (res != KSI_OK) {
		KSI_pushError(ctx,res, NULL);
		goto cleanup;
	}

	if (pubRec == NULL) {
		KSI_pushError(ctx, res = KSI_EXTEND_NO_SUITABLE_PUBLICATION, NULL);
		goto cleanup;
	}

	res = KSI_Signature_extendWithPolicy(sig, ctx, pubRec, policy, context, &extSig);
	if (res != KSI_OK) {
		KSI_pushError(ctx,res, NULL);
		goto cleanup;
	}

	*extended = extSig;
	extSig = NULL;

cleanup:

	KSI_PublicationRecord_free(pubRec);
	KSI_PublicationsFile_free(pubFile);
	KSI_Signature_free(extSig);
	return res;
}
Beispiel #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;
}