UQ Students should read the Disclaimer & Warning
Note: This page dates from 2005, and is kept for historical purposes.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>COMP2301 - Assignment 2 - File I/O in C</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
<!--
.wrong {
background: #FF9999;
}
body {
background: url(_img/DSC04989.jpg) fixed center;
font-family: "Arial Unicode MS", Arial, Helvetica, sans-serif;
}
th, td, textarea {
border: 1px solid #000000;
padding: 0 1ex;
background: transparent;
overflow: hidden;
}
table {
border: none;
}
-->
</style>
</head>
<body>
<h1>COMP2301 – Assignment Two – File <acronym title="Input/Output">I/O</acronym> in
C</h1>
<p> This assignment is a pass/fail assignment. I achieved a pass.</p>
<p>The goal of this assignment is to write C code to perform a simple file encryption
and decryption. </p>
<p>The solution must have the following features / functions:</p>
<h2>Encrypt </h2>
<ul>
<li>The application must encrypt any sample files for use as test input. Valid
files include JPEG, ZIP, EXE, TXT, WORD... </li>
<li>The first 30 bytes of the encrypted files must contain the source student
name as clear text. (Max 30 char) </li>
<li>The last 35 bytes of the encrypted files must contain an encrypted text
version of source student name. </li>
<li>The last five bytes of the encrypted file must contain a clear ASCII representation
of original file size in bytes. </li>
<li>Data must be encrypted by taking a binary representation of the key and
performing a bit wise XOR with the original file binary then repeating the
process with the next key length file segment until the file is encrypted. </li>
<li>A unique five digit hexadecimal key will be supplied with each encrypted
sample file. </li>
</ul>
<h2>Decrypt </h2>
<ul>
<li>The application must decrypt at least 10 unique sample files obtained from
10 different individuals. (+10 unique keys) </li>
<li>The application must decrypt any sample file provided by a tutor during
marking. </li>
<li>The application must generate a text menu showing all input files (max 20)
and allow the user to select one file </li>
<li>This menu must include file selection number, source name, original file
size and encrypted file size (Max 20 files) </li>
<li>After file selection the user must be prompted for a five digit hexadecimal
decryption key. </li>
<li>The key is to be applied to the encrypted file to restore its original content. </li>
<li>In addition to decrypting the file content the original file structure and
size must be restored. The application must display start and end messages
plus include the following strings <source name> = <decoded source
name> is [True| False] Original file size <nnnnn> = decrypted file
size <nnnnn> is [True| False] </li>
<li>Encryption Key is [Valid | Invalid] </li>
</ul>
<h2>General </h2>
<ul>
<li>The application must provide a simple text menu system to navigate its functions. </li>
<li>Code may use C library functions. </li>
<li>Code must be modular and make good use of C functions. </li>
<li>Code comments must correctly reference the work of others. </li>
<li>Code must be readable, well structured and conform to the pre-defined style
guide </li>
</ul>
<p>The most complex part of this assignment, for me, was the creation of the
key. I ended up using the technique below: </p>
<p><a href="#c" title="Skip to the C code">Skip to the code</a>. </p>
<p>Key Creation<br />
<textarea name="k" cols="82" rows="38" readonly="readonly" title="Logic behind the key creation">5 byte ASCII String Input:
DE345
Binary representation of the key. It is only 2 ½ bytes long:
DE 34 5
11011110 00110100 0101
Binary representation of key doubled. It is now 5 bytes long:
DE 34 5D E3 45
11011110 00110100 01011101 11100011 01000101
Binary key converted back to decimal:
11011110 00110100 01011101 11100011 01000101
222 52 93 227 69
5 byte key (as ASCII):
Þ4]ãE
Using sprtinf, the input key is divided into two-byte chunks:
sprintf ( keyChunk, "%c%c", keyChars[0], keyChars[1] );
keyChunk then holds 2 byte ASCII string "DE"
Using the string to long function, the two byte chunk is converted into a single
base 16 long, which is a one byte long hexadecimal representation of the two
byte input:
oKey[0] = (char)strtol (keyChunk, NULL, 16);
oKey[0] then holds 1 byte ASCII character "Þ"
This can then be placed in a while loop for any length N key:
for( byte = 0; byte < N; byte++ )
{
// double keyChars and divide into two-byte keyChunks
sprintf ( keyChunk, "%c%c", keyChars[ii % N], keyChars[(ii + 1) % N] );
ii+=2;
// convert two-byte keyChunks into one-byte hex equivalent
oKey[byte] = (char)strtol (keyChunk, NULL, 16);
}</textarea>
</p>
<p> </p>
<p id="c"> Assignment-2.c<br />
<textarea name="c" cols="82" rows="1124" readonly="readonly" title="C Code - Copyright 2004 Ned Martin">/*
* Author: Ned Martin #40529927
*
* Creation Date: 17-APR-2004
*
* Version: 1.2 18-APR-2004
*
* Copyright: 2004 Ned Martin, Student s40529927, for COMP2301,
* University of Queensland
*
* Description: Assignment 2 for COMP2301
* Encrypts a given file with a given key, or decrypts a
* given file with a key either given or auto-determined.
* Writes the encrypted or decrypted output file to another
* user specified file. A simple XOR function is used to
* perform the encryption. The key is a five digit
* hexadecimal key.
*
* Restrictions: Valid files for decryption, per the specification, are
* files which have an unencrypted header of SNAME_LENGTH
* length in bytes as specified below, where the original
* filesize is less than FSIZE_MAX (see its define below
* for more information) and this size is stored in the
* last FSIZE_LENGTH bytes of the encrypted file as plain
* text ASCII, and where an encrypted form of the
* SNAME_LENGTH header is stored as a footer directly
* preceding the last FSIZE_LENGTH bytes at the end of the
* file. Encryption must be performed by a simple bitwise
* XOR using a hexadecimal key of length KEY_LENGTH. The
* file is encrypted seperately to the footer, and the key
* is not continued between the file and its footer
* although the same key is used. Will not accept empty
* files.
*
* Usage: filename [filename ...]
*
* Tab Stops: Eight spaces
*/
// System includes
#include <stdio.h>
#include <stdlib.h> // defines EXIT_FAILURE, EXIT_SUCCESS
#include <string.h> // strcmp(), strcmp()
#include <ctype.h> // isxdigit()
#include <io.h> // filelength()
/*
* Constant string used as a clear-text header and encrypted as a footer
* in the decrypted file.
*
* Note: This string must be equal in length to SNAME_LENGTH
*/
const char* STUDENT_NAME = "N Martin www.nedmartin.org/uni";
/*
* Length in bytes to store the student name.
*
* Note: This value must be the same as the length in bytes of the string
* STUDENT_NAME
*
* Default: 30
*/
#define SNAME_LENGTH 30
/*
* Length in bytes of the key
*
* This value specifies how int in bytes the key is when doubled, and also
* how many hexadecimal characters int it is.
*
* Default: 5
*/
#define KEY_LENGTH 5
/*
* Maximum number of files to display at a time.
*
* This value determines how many files will be displayed. If more than
* NUM_FILES files are specified as input, only NUM_FILES of them will be
* acknowledged.
*
* Default: 20
*/
#define NUM_FILES 20
/*
* Length in bytes to store file size at end of encrypted file.
*
* This value defines the maximum size that of an input file as the maximum
* size that can be stored in FSIZE_LENGTH ASCII characters.
*
* NOTE: there is a sprintf() in encrypt() that requires manual changing if
* the FSIZE_LENGTH value is changed.
*
* Default: 5
*/
#define FSIZE_LENGTH 5
/*
* Maximum allowable input file size in bytes for encryption.
*
* This value is defined as the maximum size in bytes that can be held in
* an ASCII integer value (where 1 is stored as "1" and so on) of
* FSIZE_LENGTH characters and must not exceed that value.
*
* Default: 99999
*/
#define FSIZE_MAX 99999
/*
* Memory to allocate for file names.
*
* This defines the amount of memory allocated to hold user specified file
* names and paths.
*
* NOTE: File names (including path) exceeding this value may result in
* unpredictable behaviour.
*
* Default: Maximum value required to store a file and path name as string
*/
#define FNAME_LENGTH 30
/*
* Function: xorFile
*
* Purpose: XOR a file with a key and output to another file.
*
* Method: Accepts input and output file pointers, a hexadecimal key, and
* a start and end position. Seeks to start position in input file,
* and XORs each byte of the input file with a byte of the key,
* looping through the key, and outputting the result to the output
* file.
*
* Returns: int 0 on success
* int 1 otherwise
*/
int xorFile
(
char* iKey, // key to encrypt with
FILE* iFileIn, // input file pointer
FILE* iFileOut, // output file pointer
int iStart, // start byte
int iEnd // end byte
)
{
int byte = 0, // counter for looping through iKey
c = 0; // stores current input file character
// seek to required iStart position
if( (fseek (iFileIn, iStart, SEEK_SET)) )
{
// seeking error occured
fprintf (stderr, "XOR reports \"Error seeking to %d in file "
"%s\"\n", iStart, iFileIn);
return 1;
}
// while we haven't reached iEnd position
while ( (ftell (iFileIn)) < iEnd )
{
// get an input byte from input file
c = getc (iFileIn);
// xor input byte with byte of iKey
c ^= iKey[byte++];
// loop through bytes of iKey
byte%=KEY_LENGTH;
// output xored byte to output file
putc (c, iFileOut);
}
return 0;
} // end xorFile
/*
* Function: selectFiles
*
* Purpose: Prints a list of verified input filenames and allows a user to
* select one.
*
* Method: Accepts an array of potential filenames and a decrypting flag
* and verifies that the potential filenames refer to valid files
* for decryption or encryption depending on the value of the
* decryption flag, then prints a listing of valid files and allows
* the user to select a specific file and returns the name of the
* selected valid file.
*
* Returns: char* name of valid user selected file or
* NULL if an error or no file was selected.
*/
char* selectFiles
(
int iNumFiles, // number of iFileNames
char* iFileNames[], // array of filenames
int iDecrypting // flag if mode is decrypting
)
{
int count = 0, // counter for number of input files
bytes = 0, // byte counter for get student name
invalid = 0, // number of invalid files
newCount = 0, // number of valid files
printMax = 0, // holds maximum files to select
fileNum = 0, // holds input
originalSize = 0, // size of original file
fileSize = 0; // size of file
char c = 0, // holds input character
studentName[SNAME_LENGTH]; // holds student name
char* validFileNames[NUM_FILES]; // holds a list of valid files
FILE* f = NULL; // file pointer
// loop through iNumFiles items in iFileNames array upto max NUM_FILES
for( count = 0; (count < iNumFiles) && (count < NUM_FILES); count++ )
{
// attempt to open file in ascii mode
f = fopen (iFileNames[count], "rt");
// file was opened and hence exists
if( f )
{
// get fileSize
fileSize = filelength(fileno (f));
// we are in iDecrypting mode
if( iDecrypting )
{
// seek to last 5 bytes and read ASCII size
fseek (f, -FSIZE_LENGTH, SEEK_END);
// store ASCII size as int in originalSize
fscanf (f, "%d", &originalSize);
}
// we are in decrypting mode
// and fileSize is greater than zero
// and fileSize is less than the allowed size
// and fileSize - originalsize = 65 bytes
// then file is assumed valid for decryption
if( iDecrypting
&& (fileSize > 0)
&& (fileSize <= (FSIZE_MAX + SNAME_LENGTH * 2
+ FSIZE_LENGTH))
&& ((fileSize - originalSize) ==
(SNAME_LENGTH * 2 + FSIZE_LENGTH)) )
{
// put only valid files into array iFileNames
validFileNames[newCount++] =
iFileNames[count];
// get first SNAME_LENGTH bytes student name
rewind (f);
for( bytes = 0; bytes < SNAME_LENGTH; bytes++ )
{
studentName[bytes] = getc (f);
}
// print valid files, the source student name,
// their fileSize, and original unencrypted size
printf ("\t (%d) %s - \"%s\" (%d/%d)\n",
newCount, iFileNames[count],
studentName, originalSize, fileSize);
}
// we are in encrypting mode
// and fileSize is greater than zero
// and fileSize is less than the allowed size
else if( !iDecrypting
&& (fileSize > 0)
&& (fileSize < FSIZE_MAX) )
{
// put only valid files into array iFileNames
validFileNames[newCount++] =
iFileNames[count];
// print valid files and their fileSize
printf ("\t (%d) \"%s\" (%d bytes)\n",
newCount, iFileNames[count],
fileSize);
}
// file is not within bounds for decryption
else
{
// increment the invalid files counter
invalid++;
}
// close file
fclose (f);
}
// file could not be opened so assumed to not exist
else
{
// increment the invalid files counter
invalid++;
}
}
// print a count of invalid files
if( invalid == iNumFiles ) // then no files were valid
{
printf ("\nNo valid files were specified\n");
return NULL;
}
else if( invalid ) // some files were invalid
{
printf ("\n%d invalid files were specified\n", invalid);
}
// loop until 'Q' or a valid file is selected then return that file
for( ; ; )
{
// get the input
scanf (" %s", &c);
// quit if Q is pressed
if( c == 'q' || c == 'Q' )
{
return NULL;
}
// convert input into a number
fileNum = atoi (&c);
// if number is a valid file number
if( fileNum <= NUM_FILES && fileNum <= newCount && fileNum > 0 )
{
// return that file name
return validFileNames[fileNum - 1];
}
// number is not a valid file number
else
{
// there are more than NUM_FILES files
if( newCount > NUM_FILES )
{
// request user enter number below NUM_FILES
printMax = NUM_FILES;
}
// there are NUM_FILES or less files
else
{
// request user enter number >0 and < newCount
printMax = newCount;
}
// file is wrong, print message saying so
printf ("(%d) %s %d\n", fileNum,
"is not a valid file. Please select a file "
"between 1 and", printMax);
}
}
// we should never get here
return NULL;
} // end selectFiles
/*
* Function: requestKey
*
* Purpose: Requests a HEX key of length KEY_LENGTH from the user and
* converts it into a hexadecimal representation.
*
* Method: Requests a string of characters from the user, verifies that
* this string matches the required length and can be converted to
* hexadecimal, doubles this string and converts it into two-byte
* chunks, then converts the two-byte chunks into single-byte
* hexadecimal representation using a string-to-base-16-int
* function.
*
* Returns: int 0 on success
* int 1 otherwse
*
*/
int requestKey
(
char* oKey // holds resulting hex key
)
{
int notHex = 1, // flag for hex verification
ii = 0, // keyChunk counter
byte = 0; // byte counter
char c[KEY_LENGTH], // holds key input
keyChars[KEY_LENGTH], // holds verified key input
keyChunk[2]; // holds intermediate oKey chunks
// verify that input is KEY_LENGTH character hexadecimal
while( notHex )
{
// get input string
scanf (" %5s", c);
// input assumed to be valid hex
notHex = 0;
// exit if Q is input
if( (c[0] == 'Q') || (c[0] == 'q') )
{
// exit this routine and return to caller
return 1;
}
// loop through checking all KEY_LENGTH chars are hex
for( byte = 0; byte < KEY_LENGTH; byte++ )
{
// if char is not valid hex we don't exit while loop
if( !isxdigit (c[byte]) )
{
notHex = 1;
}
}
// if char is not hex then print message
if( notHex )
{
printf ("Invalid Input: \"%s\" is not a %d digit "
"hexadecimal key\n"
"Please enter a valid key or Q to return to "
"the main menu\n", c, KEY_LENGTH);
}
}
// set keyChars to KEY_LENGTH digits of c
for( byte = 0; byte < KEY_LENGTH; byte++ )
{
keyChars[byte] = c[byte];
}
// convert keyChars to hex and store in oKey
for( byte = 0; byte < KEY_LENGTH; byte++ )
{
// double keyChars and divide into two-byte keyChunks
sprintf ( keyChunk, "%c%c", keyChars[(ii % KEY_LENGTH)],
keyChars[((ii + 1) % KEY_LENGTH)] );
ii+=2;
// convert two-byte keyChunks into one-byte hex equivalent
oKey[byte] = (char)strtol (keyChunk, NULL, 16);
}
return 0;
} // end requestKey
/*
* Function: xorString
*
* Purpose: XOR A string with another string using a key.
*
* Method: Accepts a key and string, XOR's the string with the key and
* modifies the string accordingly.
*
* Returns: void but ioString is modified by this function.
*/
void xorString
(
char* iKey, // key to use in XOR
char* ioString // input and output string to XOR
)
{
int c, // holds input byte for XORing
byte, // input byte counter
keyByte = 0; // key byte counter
// for the length of ioString
for( byte = 0; byte < SNAME_LENGTH; byte++ )
{
// get an input byte from ioString
c = ioString[byte];
// xor input byte with byte of key
c ^= iKey[keyByte++];
// loop through bytes of key
keyByte%=KEY_LENGTH;
// put XORed byte to ioString
ioString[byte] = c;
}
} // end xorString
/*
* Function: verify
*
* Purpose: Verifies decryption was successful.
*
* Method: Accepts file pointers to the encrypted and decrypted files and
* the key used to decrypt, checks if the original file size stored
* at the end of the encrypted file is the same as the resulting
* size of the decrypted file and checks if the clear source name
* at the start of the encrypted file is the same as the encrypted
* source name (once decrypted using the key) at the end of the
* encrypted file, and prints a message containing this
* information.
*
* Restriction: The decrypted file must have its file pointer set to the last
* position in the file as this is used to determine the size of
* that file.
*
* Returns: int 1 on successfully verifying decryption or
* int 0 on failure to verify
*/
int verify
(
char* iKey, // iKey used to decrypt
FILE* iEncFile, // file pointer to encrypted file
FILE* iDecFile // file pointer to decrypted file
)
{
int byte = 0, // byte counter
sizesMatch = 0, // if sizes match
namesMatch = 0, // if names match
success = 0, // if verification was success
originalFileSize = 0, // holds original file size
decFileSize = 0; // holds decrypted file size
char sourceName[SNAME_LENGTH], // holds original source name
decSourceName[SNAME_LENGTH + 1];// holds decrypted source name
// rewind input file to start and loop through first SNAME_LENGTH bytes
// to get the source name
rewind (iEncFile);
for( byte = 0; byte < SNAME_LENGTH; byte++ )
{
sourceName[byte] = getc (iEncFile);
}
// seek to the end SNAME_LENGTH + FSIZE_LENGTH bytes of input file
if( (fseek (iEncFile, -(SNAME_LENGTH + FSIZE_LENGTH), SEEK_END)) )
{
// seeking error occured
fprintf (stderr, "Verify reports \"Error seeking to position "
"in input file\"\n");
return 1;
}
// get encrypted source name from input file
for( byte = 0; byte < SNAME_LENGTH; byte++ )
{
decSourceName[byte] = getc (iEncFile);
}
// terminate so can print as a string
decSourceName[SNAME_LENGTH] = '\0';
// XOR the encrypted source name to its clear text original format
xorString(iKey, decSourceName);
// set decrypted file size to current output file pointer position
decFileSize = ftell (iDecFile);
// store the ASCII size as an int in originalSize
fscanf (iEncFile, "%d", &originalFileSize);
// if the original and decrypted sizes match set sizesMatch to 1
if( originalFileSize == decFileSize )
{
sizesMatch = 1;
}
// check if sourceName equals decSourceName by looping through both
// and comparing each byte
byte = 0;
while( sourceName[byte] == decSourceName[byte] && byte < SNAME_LENGTH)
{
byte++;
}
// if we successfully looped through entire sourceName and decSourceName
// then they are equal so set namesMatch to 1
if( byte == SNAME_LENGTH )
{
namesMatch = 1;
}
// if sizes matched and names matched, then verification was a success
if( sizesMatch && namesMatch )
{
success = 1;
}
// print verification message
printf ("\"%s\" = \"%s\" is %s\n", sourceName, decSourceName,
(namesMatch)?"True":"False");
printf ("Original file size %d bytes = "
"decrypted file size %d bytes is %s\n",
originalFileSize, decFileSize, (sizesMatch) ? "True" : "False");
printf ("Encryption Key is %s\n", (success) ? "Valid" : "Invalid");
// return 1 if success, else return 0 for failure to verify
return success;
} // end verify
/*
* Function: decrypt
*
* Purpose: Decrypts a user selected file from an array of possible
* filenames and outputs to another user selected file.
*
* Method: Accepts an array of possible filenames, calls a function to
* verify and return a user selected file for decryption, calls a
* function to request a user input hexadecimal key, calls a
* function to decrypt the given file using the given key and
* writes the decrypted output to a user specified file then calls
* a function to verify that decryption was a success.
*
* Returns: int 0 on success or
* int 1 on failure
*/
int decrypt
(
int iNumFiles, // number of elements in iFileNames array
char* iFileNames[] // array of possible file names
)
{
int decrypting = 1, // decrypting mode
start = 0, // bytes to start xor
end = 0; // bytes to end xor
char* file = NULL; // input file
char key[KEY_LENGTH], // key to xor with
c[FNAME_LENGTH]; // holds output filename
FILE* fi = NULL; // input file pointer
FILE* fo = NULL; // output file pointer
// ask for input filenames
printf ("(2)\tSelect a file to decrypt or Q to return to the main "
"menu\n\n");
if( !(file = selectFiles(iNumFiles, iFileNames, decrypting)) )
{
// no file was selected so return to menu
return 1;
}
printf ("Decrypting \"%s\"\n", file);
// request a key to decrypt the file
printf ("%s \"%s\"\n%s\n", "Enter a 5 digit HEX key to decrypt", file,
"(example: \"A3B5d\") or Q to return to main menu");
if( requestKey(key) )
{
// invalid key or Q entered so return to main menu
return 1;
}
// request output filename
printf ("Enter a filename for the decrypted version of \"%s\"\n", file);
scanf (" %s", c);
while( !(strcmp (c, file)) )
{
printf ("Output filename cannot be the same as input filename\n"
"Enter another filename\n");
scanf (" %s", c);
}
printf ("Decrypted filename is \"%s\"\n", &c);
// open input and output files
fi = fopen (file, "rb");
fo = fopen (c, "wb");
// unable to open output file
if( !fo )
{
// print error and return to main menu
fprintf (stderr, "Decrypt reports \"Unable to open output "
"file %s\"\n", c);
return 1;
}
// start XOR at SNAME_LENGTH + 1 after the SNAME_LENGTH student name
start = SNAME_LENGTH;
// length of input file minus SNAME_LENGTH + FSIZE_LENGTH
// of encrypted student name and ASCII filesize
end = ( filelength(fileno (fi)) - (SNAME_LENGTH + FSIZE_LENGTH) );
// decrypt file from start value from above to end value from above
xorFile(key, fi, fo, start, end);
// verify that decryption was successful
printf ("Verifying output...\n\n");
if( verify(key, fi, fo) )
{
printf ("\nDecryption Successful\n\n");
}
else
{
printf ("\nDecryption Failed\n\n");
return 1;
}
// close input and output files
fclose (fi);
fclose (fo);
return 0;
} // end decrypt
/*
* Function: encrypt
*
* Purpose: Encrypts a user selected file from an array of possible
* filenames and outputs to another user selected file.
*
* Method: Accepts an array of possible filenames, calls a function that
* returns a user selected and verified file from this array, calls
* a function that requests and returns a hexadecimal key, calls a
* function that encrypts the given file with the given key and
* outputs to another user specified file. Encryption is not
* verified to have successfully worked.
*
* Returns: int 0 on success or
* int 1 on failure
*/
int encrypt
(
int iNumFiles, // number of elements in iFileNames array
char* iFileNames[] // array of possible file names
)
{
int byte = 0, // byte counter
decrypting = 0, // not decrypting flag
start = 0, // bytes to start xor
end = 0; // bytes to end xor
char* file = NULL; // holds input file name
char ioString[SNAME_LENGTH + 1], // string used in xorString
fileSize[FSIZE_LENGTH + 1], // string holds input filesize
key[KEY_LENGTH], // key to xor with
c[FNAME_LENGTH]; // output filename
FILE* fi = NULL; // input file pointer
FILE* fo = NULL; // output file pointer
// copy the constant STUDENT_NAME to ioString
strcpy (ioString, STUDENT_NAME);
// request input filename
printf ("(1)\tSelect a file to encrypt or Q to return to the main "
"menu\n\n");
if( !(file = selectFiles(iNumFiles, iFileNames, decrypting)) )
{
// Q entered or no file was selected so return to menu
return 1;
}
printf ("Encrypting \"%s\"\n", file);
// request a key
printf ("%s \"%s\"\n%s\n", "Enter a 5 digit HEX key to encrypt", file,
"(example: \"A3B5d\") or Q to return to main menu");
if( requestKey(key) )
{
// invalid key or Q entered so return to menu
return 1;
}
// request output filename
printf ("Enter a filename for the encrypted version of \"%s\"\n", file);
scanf (" %s", c);
while( !(strcmp (c, file)) )
{
printf ("Output filename cannot be the same as input filename\n"
"Enter another filename\n");
scanf (" %s", c);
}
printf ("Encrypted filename is \"%s\"\n", &c);
// open input (original) and output (encrypted) files
fi = fopen (file, "rb");
fo = fopen (c, "wb");
// unable to open output file
if( !fo )
{
// print error and return to main menu
fprintf (stderr, "Encrypt reports \"Unable to open output "
"file %s\"\n", c);
return 1;
}
// write 30 bytes cleartext STUDENT_NAME
fputs (STUDENT_NAME, fo);
// start XOR at byte 0 which becomes byte 31 of output file
start = 0;
// XOR to the end of the input file
end = filelength(fileno (fi));
// perform the XOR to encrypt the file
xorFile(key, fi, fo, start, end);
// xor the STUDENT_NAME
xorString(key, ioString);
// put XORed STUDENT_NAME to output file
for( byte = 0; byte < SNAME_LENGTH; byte++ )
{
putc (ioString[byte], fo);
}
// put the 5 byte padded ASCII filesize to the end of the output file
// NOTE: 5 here should be equal to FSIZE_LENGTH but cannot use
// defined constant in string modifier.
sprintf (fileSize, "%5u", end);
fputs (fileSize, fo);
// close files
fclose (fi);
fclose (fo);
return 0;
} // end encrypt
/*
* Function: getKey
*
* Purpose: Accepts a pointer to an encrypted file and finds the key that
* was used to encrypt that file.
*
* Method: Finds the cleartext and encrypted student names in an encrypted
* file and XOR's them together to determine the key used to
* encrypt the file. iKey is then set to this key.
*
* Returns: int 0 on success or
* int 1 on failure
* iKey is modified by this function
*/
int getKey
(
FILE* iFile, // pointer to encrypted file
char* oKey // key that is found
)
{
int byte = 0; // byte counter
char studentName[SNAME_LENGTH], // holds student name
ioString[SNAME_LENGTH]; // holds encrypted student name
// gets the student name from the start of the encrypted file
for( byte = 0; byte < SNAME_LENGTH; byte++ )
{
ioString[byte] = getc (iFile);
}
// seeks to position of encrypted student name in encrypted file
if( (fseek (iFile, -(SNAME_LENGTH + FSIZE_LENGTH), SEEK_END)) )
{
// seeking error occured
fprintf (stderr, "GetKey reports \"Error seeking to "
"position in encrypted file\"\n");
return 1;
}
// gets encrypted student name from encrypted file
for( byte = 0; byte < SNAME_LENGTH; byte++ )
{
studentName[byte] = getc (iFile);
}
// XOR's the encrypted and unencrypted student names. ioString returns
// the result of this encryption which is the key used to encrypt
xorString(studentName, ioString);
// sets the bytes of the key to equal the bytes of the returned ioString
for( byte = 0; byte < KEY_LENGTH; byte++ )
{
oKey[byte] = ioString[byte];
}
return 0;
} // end getKey
/*
* Function: autoDecrypt
*
* Purpose: Decrypts a user selected file from an array of possible
* filenames and outputs to another user selected file.
*
* Method: Accepts an array of possible filenames, calls a function to
* verify and return a user selected file for decryption, calls a
* function to determine the hexadecimal key used, calls a
* function to decrypt the given file using the given key and
* writes the decrypted output to a user specified file then calls
* a function to verify that decryption was a success.
*
* Returns: int 0 on success or
* int 1 on failure
*/
int autoDecrypt
(
int iNumFiles, // number of elements in iFileNames array
char* iFileNames[] // array of possible file names
)
{
int decrypting = 1, // decrypting mode
start = 0, // bytes to start xor
end = 0; // bytes to end xor
char* file = NULL; // holds input file name
char key[KEY_LENGTH], // key to xor with
c[FNAME_LENGTH]; // holds output file name
FILE* fi = NULL; // input file pointer
FILE* fo = NULL; // output file pointer
// ask for input filenames
printf ("(3)\tSelect a file to auto-decrypt or Q to return to the main "
"menu\n\n");
if( !(file = selectFiles(iNumFiles, iFileNames, decrypting)) )
{
// no file was selected so return to menu
return 1;
}
printf ("Attempting to auto decrypt \"%s\"\n", file);
// request output filename
printf ("Enter a filename for the decrypted version of \"%s\"\n", file);
scanf (" %s", c);
while( !(strcmp (c, file)) )
{
printf ("Output filename cannot be the same as input filename\n"
"Enter another filename\n");
scanf (" %s", c);
}
printf ("Decrypted filename is \"%s\"\n", &c);
// open input and output files
fi = fopen (file, "rb");
fo = fopen (c, "wb");
// unable to open output file
if( !fo )
{
// print error and return to main menu
fprintf (stderr, "Auto-Decrypt reports \"Unable to open output "
"file %s\"\n", c);
return 1;
}
// get the key used to encrypt the file
getKey(fi, key);
// start XOR at SNAME_LENGTH + 1 after the SNAME_LENGTH student name
start = SNAME_LENGTH;
// length of input file minus SNAME_LENGTH + FSIZE_LENGTH
// of encrypted student name and ASCII filesize
end = ( filelength(fileno (fi)) - (SNAME_LENGTH + FSIZE_LENGTH) );
// decrypt file from start value from above to end value from above
xorFile(key, fi, fo, start, end);
// verify that decryption was successful
printf ("Verifying output...\n\n");
if( verify(key, fi, fo) )
{
printf ("\nDecryption Successful\n\n");
}
else
{
printf ("\nDecryption Failed\n\n");
return 1;
}
// close input and output files
fclose (fi);
fclose (fo);
return 0;
} // end autoDecrypt
/*
* Function: showHelp
*
* Purpose: Prints a screen with information about the application.
*
* Method: Prints a screen with information about the application.
*
* Returns: none
*/
void showHelp()
{
printf ("(3)\tHelp & About\n");
printf ("\tCOMP2301 Assignment 2\tNed Martin www.nedmartin.org/uni\n");
printf ("\tVersion 1.2 18-APR-2004\t(c) Copyright 2004 Ned Martin\n");
printf ("DESCRIPTION:\n");
printf ("\tThis program will encrypt and decrypt files given on the\n");
printf ("\tcommandline provided they have an original filesize less\n");
printf ("\tthan 10,000 bytes. Some simple checking is performed on\n");
printf ("\tfiles to be decrypted. Invalid files are not shown, but\n");
printf ("\ta count of the number of invalid files is shown.\n");
printf ("USAGE:\n");
printf ("\tSelect from the various options in the main menu below.\n");
printf ("\tEntering \"Q\" or \"q\" will return you from a sub-menu\n");
printf ("\tback to the main menu, or exit the program if you are\n");
printf ("\talready at the main menu.\n");
printf ("\tFiles are encrypted using a 5 digit HEX key. This is\n");
printf ("\tentered in the format \"XXXXX\", where X is a single \n");
printf ("\tdigit or character from A to F. You are asked to specify\n");
printf ("\tthe output filename for encrypting and decrypting. You\n");
printf ("\tcan specify any name but you cannot use the same name as\n");
printf ("\tthe current input. The instability is intentional ;-)\n");
} // end showHelp
/*
* Function: splash
*
* Purpose: Shows a screen with options and allows a user to select from
* those options and then calls the appropriate function.
*
* Method: Accepts an array of possible file names, prints a screen with
* options, waits on user input, calls an appropriate function
* based on user input.
*
* Returns: int 0 on success or
* int 1 on failure
*/
int splash
(
int iNumFiles, // number of elements in iFileNames array
char* iFileNames[] // array of possible file names
)
{
char c; // holds user input for menu
// print menu
printf ("Select\t1 to\tEncrypt a file\n");
printf ("\t2\tDecrypt a file\n");
printf ("\t3\tAttempt Auto-Decrypt\n");
printf ("\tH\tHelp & About\n");
printf ("\tQ\tExit");
// capture and parse input
for( ; ; )
{
// capture input
scanf (" %1s", &c);
// clear screen
system ("cls");
// parse input
switch( c )
{
// encrypt a file
case '1':
encrypt(iNumFiles, iFileNames);
return 0;
// decrypt a file
case '2':
decrypt(iNumFiles, iFileNames);
return 0;
// attempt auto-decrypt
case '3':
autoDecrypt(iNumFiles, iFileNames);
return 0;
// show help and about screen
case 'h':
case 'H':
showHelp();
return 0;
// quit
case 'q':
case 'Q':
// this will return and exit the calling loop
return 1;
// invalid user input
default :
printf ("Invalid Selection \"%s\"\n\n", &c);
return 0;
}
}
} // end splash
/*
* Function: main
*
* Purpose: Checks arguments, calls splash function.
*
* Method: Checks for valid number of arguments, prints an introduction
* screen and calls the splash function.
*
* Returns: int EXIT_SUCCESS on success
* int EXIT_FAILURE on invalid number of arguments
*/
int main
(
int argc,
char* argv[]
)
{
// insufficient arguments
if( argc < 2 )
{
fprintf (stderr, "Usage: %s filename [filename ...]\n",
argv[0]);
exit (EXIT_FAILURE);
}
// introduction
system ("cls");
printf ("\n");
printf ("COMP2301 Assignment 2\t\t"
"(c) Copyright 2004 Ned Martin (40529927)\n");
printf ("\n");
// make splash screen
while( !splash(argc - 1, argv + 1) )
{
// run
}
return EXIT_SUCCESS;
} // end main
</textarea>
<br />
Code © Copyright 2004 Ned Martin</p>
<p>20-Apr-2004</p>
</body>
</html>