mirror of
				https://github.com/strongswan/strongswan.git
				synced 2025-11-04 00:00:51 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			658 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			658 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2013 Tobias Brunner
 | 
						|
 * HSR Hochschule fuer Technik Rapperswil
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the
 | 
						|
 * Free Software Foundation; either version 2 of the License, or (at your
 | 
						|
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but
 | 
						|
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 | 
						|
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | 
						|
 * for more details.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <getopt.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
#include <library.h>
 | 
						|
 | 
						|
/** plugins to load */
 | 
						|
#undef PLUGINS
 | 
						|
#define PLUGINS "openssl"
 | 
						|
 | 
						|
/**
 | 
						|
 * Context
 | 
						|
 */
 | 
						|
static struct {
 | 
						|
	/** input file */
 | 
						|
	FILE *in;
 | 
						|
	/** output file */
 | 
						|
	FILE *out;
 | 
						|
	/** whether to use GCM or CBC */
 | 
						|
	bool use_gcm;
 | 
						|
	/** whether to run the Monte Carlo Test */
 | 
						|
	bool use_mct;
 | 
						|
	/** whether to test encryption or decryption */
 | 
						|
	bool decrypt;
 | 
						|
	/** IV length in bits in case of GCM */
 | 
						|
	int ivlen;
 | 
						|
	/** ICV length in bits in case of GCM */
 | 
						|
	int icvlen;
 | 
						|
} ctx;
 | 
						|
 | 
						|
/**
 | 
						|
 * Types of parameters of a test vector
 | 
						|
 */
 | 
						|
typedef enum {
 | 
						|
	PARAM_UNKNOWN,
 | 
						|
	PARAM_COUNT,
 | 
						|
	PARAM_KEY,
 | 
						|
	PARAM_IV,
 | 
						|
	PARAM_PLAINTEXT,
 | 
						|
	PARAM_CIPHERTEXT,
 | 
						|
	PARAM_AAD,
 | 
						|
	PARAM_ICV,
 | 
						|
} param_t;
 | 
						|
 | 
						|
static param_t parse_parameter(char *param)
 | 
						|
{
 | 
						|
	if (strcaseeq(param, "COUNT"))
 | 
						|
	{
 | 
						|
		return PARAM_COUNT;
 | 
						|
	}
 | 
						|
	if (strcaseeq(param, "KEY"))
 | 
						|
	{
 | 
						|
		return PARAM_KEY;
 | 
						|
	}
 | 
						|
	if (strcaseeq(param, "IV"))
 | 
						|
	{
 | 
						|
		return PARAM_IV;
 | 
						|
	}
 | 
						|
	if (strcaseeq(param, "PLAINTEXT") ||
 | 
						|
		strcaseeq(param, "PT"))
 | 
						|
	{
 | 
						|
		return PARAM_PLAINTEXT;
 | 
						|
	}
 | 
						|
	if (strcaseeq(param, "CIPHERTEXT") ||
 | 
						|
		strcaseeq(param, "CT"))
 | 
						|
	{
 | 
						|
		return PARAM_CIPHERTEXT;
 | 
						|
	}
 | 
						|
	if (strcaseeq(param, "AAD"))
 | 
						|
	{
 | 
						|
		return PARAM_AAD;
 | 
						|
	}
 | 
						|
	if (strcaseeq(param, "TAG"))
 | 
						|
	{
 | 
						|
		return PARAM_ICV;
 | 
						|
	}
 | 
						|
	return PARAM_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Test vector
 | 
						|
 */
 | 
						|
typedef struct {
 | 
						|
	/** encryption/decryption key */
 | 
						|
	chunk_t key;
 | 
						|
	/** initialization vector */
 | 
						|
	chunk_t iv;
 | 
						|
	/** plain text */
 | 
						|
	chunk_t plain;
 | 
						|
	/** cipher text */
 | 
						|
	chunk_t cipher;
 | 
						|
	/** associated data */
 | 
						|
	chunk_t aad;
 | 
						|
	/** ICV/tag */
 | 
						|
	chunk_t icv;
 | 
						|
	/** whether the IV was provided */
 | 
						|
	bool external_iv;
 | 
						|
	/** whether the decryption/verification in GCM mode was successful */
 | 
						|
	bool success;
 | 
						|
} test_vector_t;
 | 
						|
 | 
						|
static void test_vector_free(test_vector_t *test)
 | 
						|
{
 | 
						|
	chunk_free(&test->key);
 | 
						|
	chunk_free(&test->iv);
 | 
						|
	chunk_free(&test->plain);
 | 
						|
	chunk_free(&test->cipher);
 | 
						|
	chunk_free(&test->aad);
 | 
						|
	chunk_free(&test->icv);
 | 
						|
}
 | 
						|
 | 
						|
static void print_result(test_vector_t *test)
 | 
						|
{
 | 
						|
	if (ctx.use_gcm)
 | 
						|
	{
 | 
						|
		if (ctx.decrypt)
 | 
						|
		{
 | 
						|
			if (test->success)
 | 
						|
			{
 | 
						|
				fprintf(ctx.out, "PT = %+B\n", &test->plain);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				fprintf(ctx.out, "FAIL\n");
 | 
						|
			}
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		if (!test->external_iv)
 | 
						|
		{
 | 
						|
			fprintf(ctx.out, "IV = %+B\n", &test->iv);
 | 
						|
		}
 | 
						|
		fprintf(ctx.out, "CT = %+B\n", &test->cipher);
 | 
						|
		fprintf(ctx.out, "Tag = %+B\n", &test->icv);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		fprintf(ctx.out, "%s = %+B\n", ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT",
 | 
						|
				ctx.decrypt ? &test->plain : &test->cipher);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static bool get_next_test_vector(test_vector_t *test)
 | 
						|
{
 | 
						|
	param_t param = PARAM_UNKNOWN;
 | 
						|
	char line[512];
 | 
						|
 | 
						|
	memset(test, 0, sizeof(test_vector_t));
 | 
						|
 | 
						|
	while (fgets(line, sizeof(line), ctx.in))
 | 
						|
	{
 | 
						|
		enumerator_t *enumerator;
 | 
						|
		chunk_t value = chunk_empty;
 | 
						|
		char *token;
 | 
						|
		int i;
 | 
						|
 | 
						|
		switch (line[0])
 | 
						|
		{
 | 
						|
			case '\n':
 | 
						|
			case '\r':
 | 
						|
			case '#':
 | 
						|
			case '\0':
 | 
						|
				/* copy comments, empty lines etc. directly to the output */
 | 
						|
				if (param != PARAM_UNKNOWN)
 | 
						|
				{	/* seems we got a complete test vector */
 | 
						|
					return TRUE;
 | 
						|
				}
 | 
						|
				fputs(line, ctx.out);
 | 
						|
				continue;
 | 
						|
			case '[':
 | 
						|
				/* control directives */
 | 
						|
				fputs(line, ctx.out);
 | 
						|
				if (strpfx(line, "[ENCRYPT]"))
 | 
						|
				{
 | 
						|
					ctx.decrypt = FALSE;
 | 
						|
				}
 | 
						|
				else if (strpfx(line, "[DECRYPT]"))
 | 
						|
				{
 | 
						|
					ctx.decrypt = TRUE;
 | 
						|
				}
 | 
						|
				else if (strcasepfx(line, "[IVlen = "))
 | 
						|
				{
 | 
						|
					ctx.ivlen = atoi(line + strlen("[IVlen = "));
 | 
						|
				}
 | 
						|
				else if (strcasepfx(line, "[Taglen = "))
 | 
						|
				{
 | 
						|
					ctx.icvlen = atoi(line + strlen("[Taglen = "));
 | 
						|
				}
 | 
						|
				continue;
 | 
						|
			default:
 | 
						|
				/* we assume the rest of the lines are PARAM = VALUE pairs*/
 | 
						|
				fputs(line, ctx.out);
 | 
						|
				break;
 | 
						|
		}
 | 
						|
 | 
						|
		i = 0;
 | 
						|
		enumerator = enumerator_create_token(line, "=", " \n\r");
 | 
						|
		while (enumerator->enumerate(enumerator, &token))
 | 
						|
		{
 | 
						|
			switch (i++)
 | 
						|
			{
 | 
						|
				case 0: /* PARAM */
 | 
						|
					param = parse_parameter(token);
 | 
						|
					continue;
 | 
						|
				case 1: /* VALUE */
 | 
						|
					if (param != PARAM_UNKNOWN && param != PARAM_COUNT)
 | 
						|
					{
 | 
						|
						value = chunk_from_hex(chunk_from_str(token), NULL);
 | 
						|
					}
 | 
						|
					else
 | 
						|
					{
 | 
						|
						value = chunk_empty;
 | 
						|
					}
 | 
						|
					continue;
 | 
						|
				default:
 | 
						|
					break;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		enumerator->destroy(enumerator);
 | 
						|
		if (i < 2)
 | 
						|
		{
 | 
						|
			value = chunk_empty;
 | 
						|
		}
 | 
						|
		switch (param)
 | 
						|
		{
 | 
						|
			case PARAM_KEY:
 | 
						|
				test->key = value;
 | 
						|
				break;
 | 
						|
			case PARAM_IV:
 | 
						|
				test->iv = value;
 | 
						|
				test->external_iv = TRUE;
 | 
						|
				break;
 | 
						|
			case PARAM_PLAINTEXT:
 | 
						|
				test->plain = value;
 | 
						|
				break;
 | 
						|
			case PARAM_CIPHERTEXT:
 | 
						|
				test->cipher = value;
 | 
						|
				break;
 | 
						|
			case PARAM_AAD:
 | 
						|
				test->aad = value;
 | 
						|
				break;
 | 
						|
			case PARAM_ICV:
 | 
						|
				test->icv = value;
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				chunk_free(&value);
 | 
						|
				break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (param != PARAM_UNKNOWN)
 | 
						|
	{	/* could be that the file ended with a complete test vector */
 | 
						|
		return TRUE;
 | 
						|
	}
 | 
						|
	return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static bool verify_test_vector(test_vector_t *test)
 | 
						|
{
 | 
						|
	if (ctx.use_gcm)
 | 
						|
	{
 | 
						|
		if (ctx.decrypt)
 | 
						|
		{
 | 
						|
			return test->key.ptr && test->iv.ptr && test->cipher.ptr &&
 | 
						|
				   test->icv.ptr;
 | 
						|
		}
 | 
						|
		return test->key.ptr && test->plain.ptr;
 | 
						|
	}
 | 
						|
	if (ctx.decrypt)
 | 
						|
	{
 | 
						|
		return test->key.ptr && test->iv.ptr && test->cipher.ptr;
 | 
						|
	}
 | 
						|
	return test->key.ptr && test->iv.ptr && test->plain.ptr;
 | 
						|
}
 | 
						|
 | 
						|
static bool do_test_gcm(test_vector_t *test)
 | 
						|
{
 | 
						|
	encryption_algorithm_t alg;
 | 
						|
	chunk_t key, iv;
 | 
						|
	aead_t *aead;
 | 
						|
	size_t saltlen, ivlen;
 | 
						|
 | 
						|
	switch (ctx.icvlen / 8)
 | 
						|
	{
 | 
						|
		case 8:
 | 
						|
			alg = ENCR_AES_GCM_ICV8;
 | 
						|
			break;
 | 
						|
		case 12:
 | 
						|
			alg = ENCR_AES_GCM_ICV12;
 | 
						|
			break;
 | 
						|
		case 16:
 | 
						|
			alg = ENCR_AES_GCM_ICV16;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			DBG1(DBG_APP, "unsupported ICV length: %d", ctx.icvlen);
 | 
						|
			return FALSE;
 | 
						|
	}
 | 
						|
 | 
						|
	aead = lib->crypto->create_aead(lib->crypto, alg, test->key.len, 4);
 | 
						|
	if (!aead)
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
 | 
						|
			 encryption_algorithm_names, alg, test->key.len * 8);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	/* our API is quite RFC 4106 specific, that is, part of the IV is provided
 | 
						|
	 * at the end of the key. */
 | 
						|
	saltlen = aead->get_key_size(aead) - test->key.len;
 | 
						|
	ivlen = aead->get_iv_size(aead);
 | 
						|
	if (ctx.ivlen / 8 != saltlen + ivlen)
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "unsupported IV length: %d", ctx.ivlen);
 | 
						|
		aead->destroy(aead);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	if (!test->external_iv)
 | 
						|
	{
 | 
						|
		rng_t *rng;
 | 
						|
 | 
						|
		/* the IV consists of saltlen random bytes (usually additional keymat)
 | 
						|
		 * followed by a counter, zero here */
 | 
						|
		test->iv = chunk_alloc(saltlen + ivlen);
 | 
						|
		memset(test->iv.ptr, 0, test->iv.len);
 | 
						|
		rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
 | 
						|
		if (!rng || !rng->get_bytes(rng, saltlen, test->iv.ptr))
 | 
						|
		{
 | 
						|
			DBG1(DBG_APP, "failed to generate IV");
 | 
						|
			DESTROY_IF(rng);
 | 
						|
			aead->destroy(aead);
 | 
						|
			return FALSE;
 | 
						|
		}
 | 
						|
		rng->destroy(rng);
 | 
						|
	}
 | 
						|
	key = chunk_alloca(test->key.len + saltlen);
 | 
						|
	memcpy(key.ptr, test->key.ptr, test->key.len);
 | 
						|
	memcpy(key.ptr + test->key.len, test->iv.ptr, saltlen);
 | 
						|
	iv = chunk_alloca(ivlen);
 | 
						|
	memcpy(iv.ptr, test->iv.ptr + saltlen, iv.len);
 | 
						|
	if (!aead->set_key(aead, key))
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "failed to set key");
 | 
						|
		aead->destroy(aead);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	if (ctx.decrypt)
 | 
						|
	{
 | 
						|
		/* the ICV is expected to follow the cipher text */
 | 
						|
		chunk_t cipher = chunk_cata("cc", test->cipher, test->icv);
 | 
						|
		/* store if the verification of the ICV verification is successful */
 | 
						|
		test->success = aead->decrypt(aead, cipher, test->aad, iv,
 | 
						|
									  &test->plain);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		if (!aead->encrypt(aead, test->plain, test->aad, iv, &test->cipher))
 | 
						|
		{
 | 
						|
			DBG1(DBG_APP, "encryption failed");
 | 
						|
			aead->destroy(aead);
 | 
						|
			return FALSE;
 | 
						|
		}
 | 
						|
		/* copy ICV from the end of the cipher text */
 | 
						|
		test->icv = chunk_alloc(ctx.icvlen / 8);
 | 
						|
		test->cipher.len -= test->icv.len;
 | 
						|
		memcpy(test->icv.ptr, test->cipher.ptr + test->cipher.len,
 | 
						|
			   test->icv.len);
 | 
						|
	}
 | 
						|
	aead->destroy(aead);
 | 
						|
	return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static bool do_crypt(crypter_t *crypter, test_vector_t *test)
 | 
						|
{
 | 
						|
	if (ctx.decrypt)
 | 
						|
	{
 | 
						|
		if (!crypter->decrypt(crypter, test->cipher, test->iv, &test->plain))
 | 
						|
		{
 | 
						|
			DBG1(DBG_APP, "decryption failed");
 | 
						|
			return FALSE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		if (!crypter->encrypt(crypter, test->plain, test->iv, &test->cipher))
 | 
						|
		{
 | 
						|
			DBG1(DBG_APP, "encryption failed");
 | 
						|
			return FALSE;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static bool do_test_cbc(test_vector_t *test)
 | 
						|
{
 | 
						|
	crypter_t *crypter;
 | 
						|
 | 
						|
	crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
 | 
						|
										  test->key.len);
 | 
						|
	if (!crypter)
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
 | 
						|
			 encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	if (!crypter->set_key(crypter, test->key))
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "failed to set key");
 | 
						|
		crypter->destroy(crypter);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	if (!do_crypt(crypter, test))
 | 
						|
	{
 | 
						|
		crypter->destroy(crypter);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	crypter->destroy(crypter);
 | 
						|
	return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static bool do_test_mct(test_vector_t *test)
 | 
						|
{
 | 
						|
	crypter_t *crypter;
 | 
						|
	chunk_t prev, *input, *output;
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC,
 | 
						|
										  test->key.len);
 | 
						|
	if (!crypter)
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "algorithm %N or key length (%d bits) not supported",
 | 
						|
			 encryption_algorithm_names, ENCR_AES_CBC, test->key.len * 8);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	input = ctx.decrypt ? &test->cipher : &test->plain;
 | 
						|
	output = ctx.decrypt ? &test->plain : &test->cipher;
 | 
						|
	if (crypter->get_block_size(crypter) != input->len)
 | 
						|
	{
 | 
						|
		DBG1(DBG_APP, "MCT only works for input with a length of one block");
 | 
						|
		crypter->destroy(crypter);
 | 
						|
		return FALSE;
 | 
						|
	}
 | 
						|
	prev = chunk_alloca(input->len);
 | 
						|
	/* assume initial IV as previous output */
 | 
						|
	*output = chunk_clone(test->iv);
 | 
						|
	for (i = 0; i < 100; i++)
 | 
						|
	{
 | 
						|
		if (i > 0)
 | 
						|
		{	/* we copied the original lines already */
 | 
						|
			fprintf(ctx.out, "COUNT = %d\n", i);
 | 
						|
			fprintf(ctx.out, "KEY = %+B\n", &test->key);
 | 
						|
			fprintf(ctx.out, "IV = %+B\n", &test->iv);
 | 
						|
			fprintf(ctx.out, "%s = %+B\n",
 | 
						|
					ctx.decrypt ? "CIPHERTEXT" : "PLAINTEXT", input);
 | 
						|
		}
 | 
						|
		if (!crypter->set_key(crypter, test->key))
 | 
						|
		{
 | 
						|
			DBG1(DBG_APP, "failed to set key");
 | 
						|
			return FALSE;
 | 
						|
		}
 | 
						|
		for (j = 0; j < 1000; j++)
 | 
						|
		{
 | 
						|
			/* store previous output as it is used as input after next */
 | 
						|
			memcpy(prev.ptr, output->ptr, prev.len);
 | 
						|
			chunk_free(output);
 | 
						|
			if (!do_crypt(crypter, test))
 | 
						|
			{
 | 
						|
				crypter->destroy(crypter);
 | 
						|
				return FALSE;
 | 
						|
			}
 | 
						|
			/* prepare the next IV (our API does not allow incremental calls) */
 | 
						|
			if (ctx.decrypt)
 | 
						|
			{
 | 
						|
				memcpy(test->iv.ptr, input->ptr, test->iv.len);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				memcpy(test->iv.ptr, output->ptr, test->iv.len);
 | 
						|
			}
 | 
						|
			/* the previous output is the next input */
 | 
						|
			memcpy(input->ptr, prev.ptr, input->len);
 | 
						|
		}
 | 
						|
		fprintf(ctx.out, "%s = %+B\n\n",
 | 
						|
				ctx.decrypt ? "PLAINTEXT" : "CIPHERTEXT", output);
 | 
						|
		/* derive key for next round */
 | 
						|
		switch (test->key.len)
 | 
						|
		{
 | 
						|
			case 16:
 | 
						|
				memxor(test->key.ptr, output->ptr, output->len);
 | 
						|
				break;
 | 
						|
			case 24:
 | 
						|
				memxor(test->key.ptr, prev.ptr + 8, 8);
 | 
						|
				memxor(test->key.ptr + 8, output->ptr, output->len);
 | 
						|
				break;
 | 
						|
			case 32:
 | 
						|
				memxor(test->key.ptr, prev.ptr, prev.len);
 | 
						|
				memxor(test->key.ptr + prev.len, output->ptr, output->len);
 | 
						|
				break;
 | 
						|
		}
 | 
						|
		/* the current output is used as IV for the next round */
 | 
						|
		memcpy(test->iv.ptr, output->ptr, test->iv.len);
 | 
						|
	}
 | 
						|
	crypter->destroy(crypter);
 | 
						|
	/* we return FALSE as we print the output ourselves */
 | 
						|
	return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static bool do_test(test_vector_t *test)
 | 
						|
{
 | 
						|
	if (ctx.use_gcm)
 | 
						|
	{
 | 
						|
		return do_test_gcm(test);
 | 
						|
	}
 | 
						|
	if (ctx.use_mct)
 | 
						|
	{
 | 
						|
		return do_test_mct(test);
 | 
						|
	}
 | 
						|
	return do_test_cbc(test);
 | 
						|
}
 | 
						|
 | 
						|
static void usage(FILE *out, char *name)
 | 
						|
{
 | 
						|
	fprintf(out, "Test AES implementation according to the AES Algorithm Validation Suite (AESAVS)\n");
 | 
						|
	fprintf(out, "and the GCM Validation System (GCMVS)\n\n");
 | 
						|
	fprintf(out, "%s [OPTIONS]\n\n", name);
 | 
						|
	fprintf(out, "Options:\n");
 | 
						|
	fprintf(out, "  -h, --help          print this help.\n");
 | 
						|
	fprintf(out, "  -d, --debug=LEVEL   set debug level (default 1).\n");
 | 
						|
	fprintf(out, "  -m, --mode=MODE     mode to test, either CBC or GCM (default CBC).\n");
 | 
						|
	fprintf(out, "  -t, --mct           run Monte Carlo Test (MCT), only for CBC.\n");
 | 
						|
	fprintf(out, "  -x, --decrypt       test decryption (not needed for CBC as files contain control directives).\n");
 | 
						|
	fprintf(out, "  -i, --in=FILE       request file (default STDIN).\n");
 | 
						|
	fprintf(out, "  -o, --out=FILE      response file (default STDOUT).\n");
 | 
						|
	fprintf(out, "\n");
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	test_vector_t test;
 | 
						|
 | 
						|
	ctx.in = stdin;
 | 
						|
	ctx.out = stdout;
 | 
						|
 | 
						|
	library_init(NULL, "aes-test");
 | 
						|
	atexit(library_deinit);
 | 
						|
 | 
						|
	while (true)
 | 
						|
	{
 | 
						|
		struct option long_opts[] = {
 | 
						|
			{"help",		no_argument,		NULL,	'h' },
 | 
						|
			{"debug",		required_argument,	NULL,	'd' },
 | 
						|
			{"mode",		required_argument,	NULL,	'm' },
 | 
						|
			{"mct",			no_argument,		NULL,	't' },
 | 
						|
			{"decrypt",		no_argument,		NULL,	'x' },
 | 
						|
			{"in",			required_argument,	NULL,	'i' },
 | 
						|
			{"out",			required_argument,	NULL,	'o' },
 | 
						|
			{0,0,0,0 },
 | 
						|
		};
 | 
						|
		switch (getopt_long(argc, argv, "hd:m:txi:o:", long_opts, NULL))
 | 
						|
		{
 | 
						|
			case EOF:
 | 
						|
				break;
 | 
						|
			case 'h':
 | 
						|
				usage(stdout, argv[0]);
 | 
						|
				return 0;
 | 
						|
			case 'd':
 | 
						|
				dbg_default_set_level(atoi(optarg));
 | 
						|
				continue;
 | 
						|
			case 'm':
 | 
						|
				if (strcaseeq(optarg, "GCM"))
 | 
						|
				{
 | 
						|
					ctx.use_gcm = TRUE;
 | 
						|
				}
 | 
						|
				else if (!strcaseeq(optarg, "CBC"))
 | 
						|
				{
 | 
						|
					usage(stderr, argv[0]);
 | 
						|
					return 1;
 | 
						|
				}
 | 
						|
				continue;
 | 
						|
			case 't':
 | 
						|
				ctx.use_mct = TRUE;
 | 
						|
				continue;
 | 
						|
			case 'x':
 | 
						|
				ctx.decrypt = TRUE;
 | 
						|
				continue;
 | 
						|
			case 'i':
 | 
						|
				ctx.in = fopen(optarg, "r");
 | 
						|
				if (!ctx.in)
 | 
						|
				{
 | 
						|
					fprintf(stderr, "failed to open '%s': %s\n", optarg,
 | 
						|
							strerror(errno));
 | 
						|
					usage(stderr, argv[0]);
 | 
						|
					return 1;
 | 
						|
				}
 | 
						|
				continue;
 | 
						|
			case 'o':
 | 
						|
				ctx.out = fopen(optarg, "w");
 | 
						|
				if (!ctx.out)
 | 
						|
				{
 | 
						|
					fprintf(stderr, "failed to open '%s': %s\n", optarg,
 | 
						|
							strerror(errno));
 | 
						|
					usage(stderr, argv[0]);
 | 
						|
					return 1;
 | 
						|
				}
 | 
						|
				continue;
 | 
						|
			default:
 | 
						|
				usage(stderr, argv[0]);
 | 
						|
				return 1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	/* TODO: maybe make plugins configurable */
 | 
						|
	lib->plugins->load(lib->plugins, PLUGINS);
 | 
						|
	lib->plugins->status(lib->plugins, LEVEL_CTRL);
 | 
						|
 | 
						|
	while (get_next_test_vector(&test))
 | 
						|
	{
 | 
						|
		if (verify_test_vector(&test))
 | 
						|
		{
 | 
						|
			if (do_test(&test))
 | 
						|
			{
 | 
						|
				print_result(&test);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			DBG1(DBG_APP, "test vector with missing data encountered");
 | 
						|
		}
 | 
						|
		fprintf(ctx.out, "\n");
 | 
						|
		test_vector_free(&test);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ctx.in != stdin)
 | 
						|
	{
 | 
						|
		fclose(ctx.in);
 | 
						|
	}
 | 
						|
	if (ctx.out != stdout)
 | 
						|
	{
 | 
						|
		fclose(ctx.out);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 |