C language realizes Caesar encryption of English characters/reading files/writing files/password verification

foreword

This article mainly uses a digital key to encrypt each line of content in the txt file according to the method of key*key, and writes the encrypted content into encrpted.txt.

encryption rules

Use the square of the key to perform backwards on the English alphabet, for example, the plaintext T will be encrypted to K using (-3)*(-3) backward rotation.
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Encryption can only identify and encrypt meaningful characters in plaintext within the range of [a,z],[A,Z]. All other unencrypted characters should remain as the original plaintext.

Unencrypted (not English characters) characters should also be written to the encrypted.txt file.

All encrypted characters should remain within the original range of ordinary characters, for example, the original character 'A' encrypted by the key's square (-1)*(-1) will become 'Z', not '@' . The same password rules also apply to ordinary characters in [a,z].

technical points

  1. How to run the program beforesafelyPerform password verification;
  2. C language read file by line
  3. C language write file by line
  4. Caesar encryption of English characters ([a,z] and [A,Z]) in the line read
  5. Determine whether a number is too large (beyond the range displayed by the computer long long)
  6. How to determine whether the row read is empty (NULL)
  7. Run to set the working directory when the program is running

Problem Description

  1. Write a round-robin password program using password authentication.
  2. When the program starts, the password request should be displayed immediately.
  3. If the password verification is correct, print "Password OK!\n" on the console and execute the encrypted plain.txt file. After the encryption is complete, print "Encryption COMPLETE.\n" in the console, and safely exit the program.
  4. Assume that the password and encryption key from keys.txt are confidential, and the data from plain.txt is top secret, so you don't want the password, encryption key and data to leave any traces in memory after use.
  5. If the password verification fails, "Password FAILED.\n" will be printed on the console, and the program will exit safely.
  6. When encrypting, use the square of the key, ie key*key, to encrypt the data from plain.txt. If the key in keys.txt is too large, please skip the key and take a key, and take a plaintext sentence.
  7. If an empty plaintext sentence is encountered in plain.txt, skip the sentence and take the next sentence and take the next key.
  8. Save the encrypted sentence to encrypted.txt file. The length of each encrypted sentence should be equal to the length of the plaintext sentence.
  9. The program should support the environment variable WORKING_PATH
    a. If the environment variable is set, use the environment variable to access plain.txt, encrypted.txt and keysN.txt files
    b. If the environment variable is not set, use the current working target Record to access plain.txt, encrypted.txt and keysN.txt files

Organize ideas

When working on a project with multiple technical points, you can draw the flow chart of the entire project before doing it, so that you will not miss a certain requirement when implementing the subsequent code, and you will not forget what to do next. Next is a flow chart made using Visio.
insert image description here

Code

Password validation

This experiment requires that the password is safe, so the best way is to use the method of dynamically allocating memory to verify whether the password entered by the user is correct. Regardless of whether it is correct or incorrect, it should be prompted in the console.

  1. When the program starts, prompt the user to enter a password;
  2. The correct password is 10 characters. When the user enters the password on the console, press Enter to indicate that the input is complete.
  3. When writing the password verification function, the password entered by the user and the length of the password can be used as parameters. When verifying, you can compare the length of the password first, and if it is not 10 digits, you can directly return false.
  4. In order to ensure password security, it should be ensured that the correct password is not a constant, but the correct password is stored in the dynamically allocated memory every time the password verification function is called.
  5. The return value of the password verification function, here is set to return 1 if the match is correct, and return 0 if the match is wrong.

code show as below:

  1. main function
int main(int argc, char** argv) {
    
    
	char p[PASSWORDLEN];
	int i = 0;
	char c;
	
	printf("password(10 bits):"); //提示用户输入密码

	while ((c = getchar()) != '\n') {
    
    
		p[i++] = c;
		if (i > 9) break; // to prevent array subscript out of index
	}
	int pwLen = sizeof(p) / sizeof(p[0]);
	int pwIsCorrect = passwordAuthentication(p, pwLen);
	// password is correct
	if (pwIsCorrect == 1) {
    
    
		printf("Password OK!\n");
	}
	// wrong password
	else {
    
    
		printf("Password FAILED.\n");
		exit(0);
	}
	printf("Encryption COMPLETE.\n");

	return 0;
}

  1. Password authentication function passwordAuthentication
int passwordAuthentication(char pw[], int length) {
    
    
	int i = 0;
	if (length != 10) {
    
    
		return 0;
	}
	//动态分配内存,保存正确的密码
	char* tmpPass = (char*)malloc(sizeof(char) * 10);
	tmpPass[0] = '2';
	tmpPass[1] = '0';
	tmpPass[2] = '2';
	tmpPass[3] = '1';
	tmpPass[4] = '2';
	tmpPass[5] = '6';
	tmpPass[6] = '3';
	tmpPass[7] = '9';
	tmpPass[8] = '3';
	tmpPass[9] = '4';
	for (i; i < 10; i++) {
    
    
		if (tmpPass[i] != pw[i])
		{
    
    
			free(tmpPass);
			return 0;
		}
	}
	free(tmpPass);
	return 1;
}

Encryption function encryption()

Since the plaintext and the key to be decrypted are stored in two txt files, the encrypted ciphertext needs to be stored in a third txt file. Therefore, it involves reading and writing files.

  1. When reading plaintext and key from plain.txt and keys.txt, a line of plain may be very long, which involves dynamic memory allocation. A maximum of 128 bytes can be allocated each time, if not enough, you can apply for more times
  2. After each memory application, it must be released
  3. When reading the key, since the key value is large, there are positive and negative values. Therefore, first store the key value in a variable of type char*. Then convert char * to long long type. Since unsigned long long can represent a large range of data, positive values ​​are stored in variables of unsigned long long type, and negative values ​​are stored in long long. To judge positive and negative values, you only need to judge whether the first character of the array of char * type is '-'. In order to conveniently judge whether the key is overflowed when long long is used to store, use the "errno.h" header file. If no error occurs in the program, the value of errno is 0. If overflow (an error occurs), the value of errno will change.
  4. When encrypting characters, use key* key to encrypt backwards. Since the key value is large, the value of key * key will be larger. Therefore, according to the characteristics of (a * b) mod c = (a mod c) * (b mod c), the key value can be multiplied by modulo 26 and then cyclically encrypted

read file one-line function

int get_line(char **str, int *buff_size, FILE *fp) {
    
    
	if (NULL == *str) {
    
    
		//default size 128 bytes
		*buff_size = 128;
		*str = malloc(128);
	}
	//empty string
	memset(*str, 0, strlen(*str));
	//Record the number of characters per line
	int line_ch_size = 0;
	//characters per read
	int ch;
	while (1)
	{
    
    
		if ((ch = fgetc(fp)) != EOF) {
    
    
			if (ch == '\n') {
    
    
				//Finished reading a line
				break;
			}
			else {
    
    
				if (line_ch_size == (*buff_size) - 1) {
    
    
					//Need to expand, add 128 at a time
					*buff_size = (*buff_size) + 128;
					*str = realloc(*str, *buff_size);
				}
				sprintf(*str, "%s%c", *str, ch);
				line_ch_size++;
			}
		}
		else {
    
    
			line_ch_size = -1;
			break;
		}
	}
	return line_ch_size;
}

free memory function

void get_line_free(char **str){
    
    
	if (NULL != *str) {
    
    
		free(*str);
	}
}

character encryption function

void cipher_cyclic_rotations(char** encryption_res, char** origin_key, char** plain_line)
{
    
    
	errno = 0;
	//a pointer point to a char in plain_line
	char* is_valid_ch;
	long long negative_key;
	unsigned long long positive_key;
	char* tmp_key_ch;
	//rotation count
	long long cycles;
	int char_required;
	int char_to_a_len = 0;
	is_valid_ch = *plain_line;
	if (*is_valid_ch == '\0') {
    
    
		*encryption_res = NULL;
		return;
	}
	if (*origin_key[0] == '-') {
    
    
		negative_key = strtoll(*origin_key, NULL, 10);
		cycles = ((negative_key % 26) * (negative_key % 26)) % 26;
	}
	else {
    
    
		positive_key = strtoull(*origin_key, NULL, 10);
		cycles = ((positive_key % 26) * (positive_key % 26)) % 26;
	}
	// errno means key exceeded maximum display range
	if (errno != 0) {
    
    
		// printf("value %s exceeded maximum display range\n", *origin_key);
		*encryption_res = NULL;
		return;
	}
	//cycles: rotation count
	for (; *is_valid_ch != '\0'; is_valid_ch++) {
    
    
		if ((*is_valid_ch >= 'a' && *is_valid_ch <= 'z')) {
    
    
			//the distance between the char and 'a'
			char_to_a_len = *is_valid_ch - 'a';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'z' - (cycles - 1 - char_to_a_len);
			}
		}
		if ((*is_valid_ch >= 'A' && *is_valid_ch <= 'Z')) {
    
    
			char_to_a_len = *is_valid_ch - 'A';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'Z' - (cycles - 1 - char_to_a_len);
			}
		}
	}
	*encryption_res = *plain_line;
}

encryption function

void encryption(char* workspace_path) {
    
    
	char* plain_path = "plain.txt";
	char* keys10_path = "keys10.txt";
	char* encrypted_path = "encrypted.txt";
	char* plain_path_origin = malloc(128);
	char* key_path_origin = malloc(128);
	char* encrypted_path_origin = malloc(128);
	if (workspace_path != NULL) {
    
    
		strcpy(plain_path_origin, workspace_path);
		strcpy(key_path_origin, workspace_path);
		strcpy(encrypted_path_origin, workspace_path);
		plain_path = strcat(plain_path_origin, "plain.txt");
		keys10_path = strcat(key_path_origin, "keys10.txt");
		encrypted_path = strcat(encrypted_path_origin, "encrypted.txt");
	}
	//open files
	FILE* fp = fopen(plain_path, "r");
	FILE* fp_en = fopen(encrypted_path, "w");
	FILE* fp_key = fopen(keys10_path, "r");
	char* encryption_res = NULL; //String to be written to encrypted.txt file after encryption
	//String to store each line
	char *str_line = NULL;
	//The size of the memory to store the string
	int buff_size;
	int buff_size_key;
	//characters per line
	int line_ch_size;
	int count = 0;
	long long tmp_key;
	char* origin_key = NULL;
	//file open failed
	if (!fp) {
    
    
		printf("main.c: 251, fopen() failed: %s file open failed.\n", plain_path);
		exit(0);
	}
	if (!fp_en) {
    
    
		printf("main.c: 255, fopen() failed: %s file open failed.\n", encrypted_path);
		exit(0);
	}
	if (!fp_key) {
    
    
		printf("main.c: 259, fopen() failed: %s file open failed\n", keys10_path);
		exit(0);
	}

	while (1) {
    
    
		encryption_res = NULL;
		//read a line form plain.txt
		line_ch_size = get_line(&str_line, &buff_size, fp);
		if (line_ch_size == -1 && str_line[0] == '\0') {
    
    
			break;
		}
		//read a line form keysN.txt
		get_line(&origin_key, &buff_size_key, fp_key);
		// Get the result that need write into enctypted.txt. If the result is NULL,then doesn't write into enctypted.txt
		cipher_cyclic_rotations(&encryption_res, &origin_key, &str_line);
		if (encryption_res == NULL) {
    
    
			continue;
		}
		//If the result is not NULL, write it into enctypted.txt
		fprintf(fp_en, "%s\n", encryption_res);
	}
	//free the malloced memory size
	get_line_free(&str_line);
	get_line_free(&origin_key);
	free(plain_path_origin);
	free(key_path_origin);
	free(encrypted_path_origin);
	//close the file
	fclose(fp);
	fclose(fp_en);
	fclose(fp_key);
}

set working path

The main function in C language can carry parameters, and the setting method is as follows:
int main(int argc, char** argv)
Therefore, the working path can be passed to the main function as a parameter.
The way to run the main function in the terminal is as follows: Type
directly in cmd:
program name [space] parameter 1 [space] parameter 2 [space]... parameter n [enter key to end typing]
or
program name.exe [space] parameter 1 [Space] Parameter 2 [Space]... Parameter n [Enter key to end typing]
Here, only the working path is passed as a parameter to the main function, so there is only one parameter, which is of type string. At the same time, it is necessary to judge the length of the transmitted working path. Here, if the working path is longer than 128 characters, an error will be prompted.
The main function to set the working path is as follows:

int main(int argc, char** argv) {
    
    
	char p[PASSWORDLEN];
	int i = 0;
	char c;

	if (argv[1] != NULL) {
    
    
		int path_len = strlen(argv[1]);
		if (path_len > 128) {
    
    
			printf("main.c: 56, main() failed: %s ,the length of the path is too long.\n", argv[1]);
			exit(0);
		}
		else
		{
    
    
			printf("The current working path is %s.\n", argv[1]);
		}
	}
	else
	{
    
    
		printf("The current working path is the program running path\n");
		printf("You can change the path to other directories by adding runtime parameters when running the program.\n");
	}
	printf("password(10 bits):");

	while ((c = getchar()) != '\n') {
    
    
		p[i++] = c;
		if (i > 9) break; // to prevent array subscript out of index
	}
	int pwLen = sizeof(p) / sizeof(p[0]);
	int pwIsCorrect = passwordAuthentication(p, pwLen);
	// password is correct
	if (pwIsCorrect == 1) {
    
    
		printf("Password OK!\n");
		// Start encrypting files
		encryption(argv[1]);
	}
	// wrong password
	else {
    
    
		printf("Password FAILED.\n");
		exit(0);
	}
	printf("Encryption COMPLETE.\n");

	return 0;
}

overall code

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <errno.h>

#define PASSWORDLEN  10//password length

/*
 * @brief match password
 * @param pw password to match
 * @param length length of password to match
 * @return int When the password is correct, return 1; when the password is wrong, return 0
 */
int passwordAuthentication(char pw[], int length);

/*
 * @brief Get the line that need write into enctypted.txt. If the line is NULL,then doesn't write into enctypted.txt
 * @param encryption_res one line need to be written into enctypted.txt
 * @param origin_key one line read from keysN.txt
 * @param plain_line one line read from plain.txt
 */
void cipher_cyclic_rotations(char** encryption_res, char** origin_key, char** plain_line);

/*
 * @brief Get the Read key and plain object
 * @param str
 * @param size
 * @param fp
 * @return int Number of characters per line read
 */
int get_line(char **str, int *buff_size, FILE *fp); 

/*
 * @brief Free the key and plain object
 * @param str
 */
void get_line_free(char **str);

/*
 * @brief encryption function
 * @param workspace_path 
 */
void encryption(char* workspace_path);

int main(int argc, char** argv) {
    
    
	char p[PASSWORDLEN];
	int i = 0;
	char c;

	if (argv[1] != NULL) {
    
    
		int path_len = strlen(argv[1]);
		if (path_len > 128) {
    
    
			printf("main.c: 56, main() failed: %s ,the length of the path is too long.\n", argv[1]);
			exit(0);
		}
		else
		{
    
    
			printf("The current working path is %s.\n", argv[1]);
		}
	}
	else
	{
    
    
		printf("The current working path is the program running path\n");
		printf("You can change the path to other directories by adding runtime parameters when running the program.\n");
	}
	printf("password(10 bits):");

	while ((c = getchar()) != '\n') {
    
    
		p[i++] = c;
		if (i > 9) break; // to prevent array subscript out of index
	}
	int pwLen = sizeof(p) / sizeof(p[0]);
	int pwIsCorrect = passwordAuthentication(p, pwLen);
	// password is correct
	if (pwIsCorrect == 1) {
    
    
		printf("Password OK!\n");
		// Start encrypting files
		encryption(argv[1]);
	}
	// wrong password
	else {
    
    
		printf("Password FAILED.\n");
		exit(0);
	}
	printf("Encryption COMPLETE.\n");

	return 0;
}

int passwordAuthentication(char pw[], int length) {
    
    
	int i = 0;
	if (length != 10) {
    
    
		return 0;
	}
	char* tmpPass = (char*)malloc(sizeof(char) * 10);
	tmpPass[0] = '2';
	tmpPass[1] = '0';
	tmpPass[2] = '2';
	tmpPass[3] = '1';
	tmpPass[4] = '2';
	tmpPass[5] = '6';
	tmpPass[6] = '3';
	tmpPass[7] = '9';
	tmpPass[8] = '3';
	tmpPass[9] = '4';
	for (i; i < 10; i++) {
    
    
		if (tmpPass[i] != pw[i])
		{
    
    
			free(tmpPass);
			return 0;
		}
	}
	free(tmpPass);
	return 1;
}

void cipher_cyclic_rotations(char** encryption_res, char** origin_key, char** plain_line)
{
    
    
	errno = 0;
	//a pointer point to a char in plain_line
	char* is_valid_ch;
	long long negative_key;
	unsigned long long positive_key;
	char* tmp_key_ch;
	//rotation count
	long long cycles;
	int char_required;
	int char_to_a_len = 0;
	is_valid_ch = *plain_line;
	if (*is_valid_ch == '\0') {
    
    
		*encryption_res = NULL;
		return;
	}
	if (*origin_key[0] == '-') {
    
    
		negative_key = strtoll(*origin_key, NULL, 10);
		cycles = ((negative_key % 26) * (negative_key % 26)) % 26;
	}
	else {
    
    
		positive_key = strtoull(*origin_key, NULL, 10);
		cycles = ((positive_key % 26) * (positive_key % 26)) % 26;
	}
	// errno means key exceeded maximum display range
	if (errno != 0) {
    
    
		// printf("value %s exceeded maximum display range\n", *origin_key);
		*encryption_res = NULL;
		return;
	}
	//cycles: rotation count
	for (; *is_valid_ch != '\0'; is_valid_ch++) {
    
    
		if ((*is_valid_ch >= 'a' && *is_valid_ch <= 'z')) {
    
    
			//the distance between the char and 'a'
			char_to_a_len = *is_valid_ch - 'a';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'z' - (cycles - 1 - char_to_a_len);
			}
		}
		if ((*is_valid_ch >= 'A' && *is_valid_ch <= 'Z')) {
    
    
			char_to_a_len = *is_valid_ch - 'A';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'Z' - (cycles - 1 - char_to_a_len);
			}
		}
	}
	*encryption_res = *plain_line;
}

int get_line(char **str, int *buff_size, FILE *fp) {
    
    
	if (NULL == *str) {
    
    
		//default size 128 bytes
		*buff_size = 128;
		*str = malloc(128);
	}
	//empty string
	memset(*str, 0, strlen(*str));
	//Record the number of characters per line
	int line_ch_size = 0;
	//characters per read
	int ch;
	while (1)
	{
    
    
		if ((ch = fgetc(fp)) != EOF) {
    
    
			if (ch == '\n') {
    
    
				//Finished reading a line
				break;
			}
			else {
    
    
				if (line_ch_size == (*buff_size) - 1) {
    
    
					//Need to expand, add 128 at a time
					*buff_size = (*buff_size) + 128;
					*str = realloc(*str, *buff_size);
				}
				sprintf(*str, "%s%c", *str, ch);
				line_ch_size++;
			}
		}
		else {
    
    
			line_ch_size = -1;
			break;
		}
	}
	return line_ch_size;
}

void get_line_free(char **str){
    
    
	if (NULL != *str) {
    
    
		free(*str);
	}
}

void encryption(char* workspace_path) {
    
    
	char* plain_path = "plain.txt";
	char* keys10_path = "keys10.txt";
	char* encrypted_path = "encrypted.txt";
	char* plain_path_origin = malloc(128);
	char* key_path_origin = malloc(128);
	char* encrypted_path_origin = malloc(128);
	if (workspace_path != NULL) {
    
    
		strcpy(plain_path_origin, workspace_path);
		strcpy(key_path_origin, workspace_path);
		strcpy(encrypted_path_origin, workspace_path);
		plain_path = strcat(plain_path_origin, "plain.txt");
		keys10_path = strcat(key_path_origin, "keys10.txt");
		encrypted_path = strcat(encrypted_path_origin, "encrypted.txt");
	}
	//open files
	FILE* fp = fopen(plain_path, "r");
	FILE* fp_en = fopen(encrypted_path, "w");
	FILE* fp_key = fopen(keys10_path, "r");
	char* encryption_res = NULL; //String to be written to encrypted.txt file after encryption
	//String to store each line
	char *str_line = NULL;
	//The size of the memory to store the string
	int buff_size;
	int buff_size_key;
	//characters per line
	int line_ch_size;
	int count = 0;
	long long tmp_key;
	char* origin_key = NULL;
	//file open failed
	if (!fp) {
    
    
		printf("main.c: 251, fopen() failed: %s file open failed.\n", plain_path);
		exit(0);
	}
	if (!fp_en) {
    
    
		printf("main.c: 255, fopen() failed: %s file open failed.\n", encrypted_path);
		exit(0);
	}
	if (!fp_key) {
    
    
		printf("main.c: 259, fopen() failed: %s file open failed\n", keys10_path);
		exit(0);
	}

	while (1) {
    
    
		encryption_res = NULL;
		//read a line form plain.txt
		line_ch_size = get_line(&str_line, &buff_size, fp);
		if (line_ch_size == -1 && str_line[0] == '\0') {
    
    
			break;
		}
		//read a line form keysN.txt
		get_line(&origin_key, &buff_size_key, fp_key);
		// Get the result that need write into enctypted.txt. If the result is NULL,then doesn't write into enctypted.txt
		cipher_cyclic_rotations(&encryption_res, &origin_key, &str_line);
		if (encryption_res == NULL) {
    
    
			continue;
		}
		//If the result is not NULL, write it into enctypted.txt
		fprintf(fp_en, "%s\n", encryption_res);
	}
	//free the malloced memory size
	get_line_free(&str_line);
	get_line_free(&origin_key);
	free(plain_path_origin);
	free(key_path_origin);
	free(encrypted_path_origin);
	//close the file
	fclose(fp);
	fclose(fp_en);
	fclose(fp_key);
}

Guess you like

Origin blog.csdn.net/weixin_43943476/article/details/125866784