Minor change: Added constant for strings
[wordblah.git] / wordblah.h
index 13874dc..864b238 100644 (file)
@@ -30,11 +30,13 @@ enum COLOR {
        WHITE=7
 };
 
+/* Enum to define terminal attributes */
 enum ATTR {
        NORMAL = 23,
        BOLD=1
 };
 
+/* Enum to describe current movement orientation in puzzle grid */
 enum ORIENTATION {
        ACROSS=1,
        DOWN=2
@@ -50,7 +52,7 @@ typedef char String[MAX_CLUE_LENGTH];
 
 /* The main puzzle struct type */
 typedef struct {
-       char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+       char chars[MAX_PUZZLE_SIZE+1][MAX_PUZZLE_SIZE+1];
        int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
        int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
        String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
@@ -66,7 +68,7 @@ typedef struct {
        Puzzle puzzle;
        char filename[65535];
        bool is_loaded;
-       char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
+       char char_ans[MAX_PUZZLE_SIZE+1][MAX_PUZZLE_SIZE+1];
        int cur_row;
        int cur_col;
        bool solution_revealed;
@@ -103,13 +105,113 @@ err:
        exit (2);
 }
 
-/* encode the binary data to readable text format using OpenSSL */
-void encode_binary (char *encoded, unsigned char *binary_data, unsigned int len)
+/* encode the binary data to readable text format using OpenSSL - also call
+   OPENSSL_free if the binary data was allocated by OpenSSL */
+void encode_binary (char *encoded, unsigned char *binary_data, unsigned int len,
+                                               bool free_openssl_data)
 {
 
        EVP_EncodeBlock ((unsigned char*)encoded, 
                                                        (const unsigned char*)binary_data, len);
-       OPENSSL_free (binary_data);
+       if (free_openssl_data)
+               OPENSSL_free (binary_data);
+}
+
+/* decode the binary data from the textual representation using OpenSSL */
+void decode_binary (char *bin_data, char *encoded)
+{
+       EVP_DecodeBlock ((unsigned char*)bin_data, 
+                                                       (const unsigned char*)encoded, strlen (encoded));
+}
+
+/* encrypt a block of text using password/passphrase with OpenSSL and 
+   also encode it to textual representation */
+void encrypt_data (char *enc_data, const char *data, const char *password)
+{
+       EVP_CIPHER_CTX *ctx;
+       int len, cipher_len; 
+       ctx = EVP_CIPHER_CTX_new ();
+       if (! ctx) goto err;
+       
+       unsigned char encrypted[256] = { '\0' };
+       
+       unsigned char key[EVP_MAX_KEY_LENGTH] = { '\0'};
+       unsigned char iv[EVP_MAX_IV_LENGTH] = { '\0' };
+       
+       if (! EVP_BytesToKey (EVP_aes_256_cbc(), EVP_md5(), NULL, 
+                                                                       (unsigned char*)password, strlen(password),
+                                                                               10, key, iv))
+               goto err;
+       
+       if (1 != EVP_EncryptInit_ex (ctx, EVP_aes_256_cbc(), NULL, key, iv))
+               goto err;
+       
+       if (1 != EVP_EncryptUpdate (ctx, (unsigned char*) encrypted, &len, 
+                                                               (unsigned char*) data, strlen (data) ))
+               goto err;
+       cipher_len = len;
+       
+       if (1 != EVP_EncryptFinal_ex (ctx, encrypted + len, &len))
+               goto err;
+       
+       cipher_len += len;
+       EVP_CIPHER_CTX_free (ctx);
+       
+       EVP_EncodeBlock ((unsigned char*) enc_data, (unsigned char*) encrypted, 
+                                               cipher_len);
+       return;
+ err:
+       ERR_print_errors_fp (stderr);
+       EVP_CIPHER_CTX_free (ctx);
+       exit (2);
+}
+
+/* decrypt a block of text using password/passphrase with OpenSSL */
+void decrypt_data (char *dec_data, const char *data, const char *password)
+{
+       EVP_CIPHER_CTX *ctx;
+       int len, text_len; 
+       ctx = EVP_CIPHER_CTX_new ();
+       if (! ctx) goto err;
+       
+       char enc_data[256] = { '\0' };
+       
+       unsigned char key[EVP_MAX_KEY_LENGTH] = { '\0'};
+       unsigned char iv[EVP_MAX_IV_LENGTH] = { '\0' };
+
+       
+       if (! EVP_BytesToKey (EVP_aes_256_cbc(), EVP_md5(), NULL, 
+                                                                       (unsigned char*)password, strlen(password),
+                                                                               10, key, iv))
+               goto err;
+       
+       int r = EVP_DecodeBlock ((unsigned char*)enc_data, 
+                                                               (const unsigned char*) data, strlen (data));
+       if (-1 == r)
+               goto err;
+       
+       
+       if (1 != EVP_DecryptInit_ex (ctx, EVP_aes_256_cbc(), NULL, key, iv))
+               goto err;
+       
+       if (1 != EVP_DecryptUpdate (ctx, (unsigned char*) dec_data, &len, 
+                                                               (unsigned char*) enc_data, r - (r % 16) ))
+               goto err;
+       text_len = len;
+       
+       if (1 != EVP_DecryptFinal_ex (ctx, (unsigned char *)dec_data + len, &len))
+               goto err;
+       
+       text_len += len;
+       EVP_CIPHER_CTX_free (ctx);
+       
+       dec_data[text_len] = '\0';
+               
+       return;
+ err:
+       ERR_print_errors_fp (stderr);
+       EVP_CIPHER_CTX_free (ctx);
+       exit (2);
 }
 
 /* get a number from the user */
@@ -134,7 +236,7 @@ bool verify_solution_password (Puzzle *p, const char* password)
        digest_message ((const unsigned char *)password, strlen(password),
                                                &hashed_sol_password, &len);
        char hashed_hex_pwd[256] = { '\0' };
-       encode_binary (hashed_hex_pwd, hashed_sol_password, len);
+       encode_binary (hashed_hex_pwd, hashed_sol_password, len, true);
        
        if (strcmp (p->hashed_solution_password, hashed_hex_pwd) == 0)
                return true;
@@ -156,7 +258,7 @@ bool verify_master_password (Puzzle *p, const char* password)
        digest_message ((const unsigned char *)password, strlen(password),
                                                &hashed_mas_password, &len);
        char hashed_hex_pwd[256] = { '\0' };
-       encode_binary (hashed_hex_pwd, hashed_mas_password, len);
+       encode_binary (hashed_hex_pwd, hashed_mas_password, len, true);
        
        if (strcmp (p->hashed_master_password, hashed_hex_pwd) == 0)
                return true;
@@ -180,7 +282,7 @@ void set_solution_password (Puzzle *p, const char *password)
                /* the hashedpwd contains binary data - we will convert it to 
                   hexadecimal data and store in file */
 
-               encode_binary (p->hashed_solution_password, hashedpwd, len);
+               encode_binary (p->hashed_solution_password, hashedpwd, len, true);
        }
 }
 
@@ -200,7 +302,7 @@ void set_master_password (Puzzle *p, const char *password)
                /* the hashedpwd contains binary data - we will convert it to 
                   hexadecimal data and store in file */
 
-               encode_binary (p->hashed_master_password, hashedpwd, len);
+               encode_binary (p->hashed_master_password, hashedpwd, len, true);
        }
 }
 
@@ -479,8 +581,17 @@ void freeze_puzzle (Puzzle *p)
 /* reset the entire grid */
 void init_puzzle (Puzzle *p, int grid_size) 
 {
-       p->grid_size = grid_size;
+       /* check for bounds */
+       if (p->grid_size > MAX_PUZZLE_SIZE)
+               p->grid_size = MAX_PUZZLE_SIZE;
+       else
+               p->grid_size = grid_size;
+               
+       /* grid is always unfrozen for a new puzzle */
        p->grid_frozen = false;
+       
+       /* initialize all the puzzle data - characters, start of words (across/down)
+          and the clues to null */
        for (int i = 0; i < p->grid_size; i ++)
        {
                for (int j = 0; j < p->grid_size; j ++) 
@@ -488,10 +599,11 @@ void init_puzzle (Puzzle *p, int grid_size)
                        p->chars[i][j] = ' ';
                        p->start_across_word[i][j] = -1;
                        p->start_down_word[i][j] = -1;
-                       strcpy (p->clue_across[i][j], "");
-                       strcpy (p->clue_down[i][j], "");                        
+                       strcpy (p->clue_across[i][j], "\0");
+                       strcpy (p->clue_down[i][j], "\0");
                }
        }
+       /* reset the master password and solution password */
        strcpy (p->hashed_master_password, "\0");
        strcpy (p->hashed_solution_password, "\0");
        
@@ -517,11 +629,16 @@ void save_puzzle (Puzzle *puzzle, const char* file)
        /* the hashed_solution_password */
        fprintf (outfile, "%s\n", puzzle->hashed_solution_password);
        
-       /* First output the grid characters columns/rows */
+       /* First output the grid characters columns/rows as encrypted */
        for (int i = 0; i < puzzle->grid_size; i ++)
        {
-               for (int j = 0; j < puzzle->grid_size; j ++)
-                       fprintf (outfile, "%c", puzzle->chars[i][j]);
+               char encrypted[256] = { '\0' };
+               /* encrypt the grid characters at row i with master password to
+                  generate the key and iv */
+               encrypt_data (encrypted, puzzle->chars[i], 
+                                                               puzzle->hashed_master_password);
+
+               fprintf (outfile, "%s", encrypted);
                fprintf (outfile, "\n");
        }
        
@@ -670,22 +787,41 @@ Puzzle load_puzzle (const char* file)
        /* read each character of the grid */
        for (int i = 0; i < p.grid_size; i ++ )
        {
-               fgets (line, MAX_CLUE_LENGTH + 10, infile);
+               char encrypted[256];
+               /* get a line from the file - each line is a grid row */
+               fgets (encrypted, MAX_CLUE_LENGTH + 10, infile);
+               /* decrypt each line from the file and put the decrypted chars 
+                  into the grid array */
+               decrypt_data (line, encrypted, p.hashed_master_password);
+               /* finally read the decrypted data into the array */
                for (int j = 0; j < p.grid_size; j ++)
                        p.chars[i][j] = line[j];
+
        }
        /* read the word numbers */
        for (int i = 0; i < p.grid_size; i ++)
        {
+               /* get a line from the file - each file represents a row */
+               /* the word numbers are started as n1<space>n2 where n1 is 
+                  the across word number and n2 is the down word number. 
+                  Though both across and down word numbers will be the same
+                  in a given cell, we use separate number to determine whether
+                  there is an across or down word or both in a given cell. */
                fgets (line, MAX_CLUE_LENGTH + 10, infile);
+               /* split the line into tokens with space as the separating character */
                char *token = strtok (line, " ");
                for (int j = 0; j < p.grid_size; j ++)
                {
+                       /* so long as token is valid, read the first token as across 
+                          word number */
                        if (token != NULL) 
                                p.start_across_word[i][j] = atoi (token);
+                       /* similarly get the next token as the down word number */
                        token = strtok (NULL, " ");
                        if (token != NULL)
                                p.start_down_word[i][j] = atoi (token);
+                       /* get the next token, it should be the across word number format
+                          the next cell or NULL if we have read all the tokens */
                        token = strtok (NULL, " ");
                }
        }
@@ -700,7 +836,7 @@ Puzzle load_puzzle (const char* file)
        while (1)
        {
                fgets (line, MAX_CLUE_LENGTH + 10, infile);
-               /* if reached the end of across clues */
+               /* the word DOWN indicates that we reached the end of across clues */
                if (strcmp (line, "DOWN\n") == 0)
                        break;
                word_num[c] = atoi (strtok (line, "\t"));
@@ -795,7 +931,7 @@ void print_puzzle (Puzzle *p)
        /* print the clues if set */
        if (p->grid_frozen == true) 
        {
-               printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
+               printf ("\x1B[1m%s\x1B[0m\n", ACROSS_CLUES);
                for (int i = 0; i < p->grid_size; i ++)
                {
                        for (int j = 0; j < p->grid_size; j ++)
@@ -807,7 +943,7 @@ void print_puzzle (Puzzle *p)
                                }
                        }
                }
-               printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
+               printf ("\n\x1B[1m%s\x1B[0m\n", DOWN_CLUES);
                for (int i = 0; i < p->grid_size; i ++)
                {
                        for (int j = 0; j < p->grid_size; j ++)