2
\$\begingroup\$

I'm coding this program that allows you to encrypt files using vernam cipher in C language. I would be glad if you could inspect my code and tell me if I can make some improvements to it or if there are any error.

The request they gave me is:

The goal is to develop a simple application that allows you to encrypt files using a variant of the Vernam cipher, here called bvernan. The proposed algorithm exploits a simple property of the binary operator XOR: (A XOR B) XOR B = A Given a sequence of k-bytes b0 ... bk-1 (called key), the encoding function of a sequence of bytes d0 ... dN, the encoding / decoding function follows the following simple procedure. The sequence d0 ... dN is first divided into N / k blocks (integer division), D0, ..., D [(N / k) -1] each of which consists of exactly k bytes (apart from the 'last sequence which, obviously, may contain a smaller number of bytes). Subsequently each sequence Dj = dj, 0 ... dj, k-1 is transformed into the sequence D'j = d'j, 0 ... d'j, k-1 such that for each i: d'j, i = b (j + i) mod k XOR dj, i That is, the byte in position i of block j is placed in XOR with the byte (j + i) mod k of the key. The output sequence will then be obtained from the juxtaposition of the sequences D'0, ..., D '[(N / k) -1].

This is the code:

file main.c

#include "encode.h"

int main (int argc ,char** argv){
    if(argc!=4){
        printf("Usage: bvernan keyfile inputfile outputfile \n");
        return 1;
    }
    
    Key_t* key=openKey(argv[1]);
    
    Register_t* file=openFile(argv[2],argv[3],key->lenght);
    encode(file,key);
    closeRegister(file);
    freeKey(key);
    printf("Success!\n");
}

file encode.c

#include "encode.h"

int encode (Register_t* file, Key_t* key ){
    while(readF(file)>0){
        encodeDivision(file->buffer,file->bufferLenght,key);
        writeF(file);
    }
}
void encodeDivision (unsigned char* block,long lenght,Key_t*key){
    for(int i=0;i<lenght;i++){
        block[i]=block[i]^key->buffer[i];
    }
}

file encode.h

#include "key.h"
#include "register.h"

int encode (Register_t* file, Key_t* key );
void encodeDivision (unsigned char* block,long lenght,Key_t*key);

file key.c

#include "key.h"
#include <stdio.h>  
#include <stdlib.h>


long keySize (FILE* file){
    fseek (file,0,SEEK_END);
    long size=ftell(file);
    rewind (file);
    return size;
}
Key_t* openKey(char* path){
    Key_t* kFile= malloc (sizeof(Key_t));
    FILE*file= fopen(path,"rb");
    kFile->lenght=keySize(file);
    kFile->buffer= malloc(kFile->lenght);
    fread(kFile->buffer,1,kFile->lenght,file);
    fclose(file);
    return kFile;
}
void freeKey (Key_t* key ){
free (key->buffer);
free (key);
}

file key.h

typedef struct Key
{
    unsigned char* buffer; 
    long lenght;
} Key_t; 

Key_t* openKey(char* path);
void freeKey (Key_t*);   


file register.c

#include <stdio.h>
#include <stdlib.h>
#include "register.h"

Register_t* openFile (char* inPath,char* outPath,long bufferLenght){
    Register_t* file = malloc (sizeof(Register_t));
    file->buffer= malloc(bufferLenght);
    file->bufferLenght=bufferLenght;
    file->fdIn= fopen(inPath,"rb");
    file->fdOut= fopen(outPath,"wb");
    if(file->fdOut==NULL || file->fdIn==NULL){
        return NULL;
    }
        return file;
}
int readF (Register_t* file){
    int readbyte = fread(file->buffer,1,file->bufferLenght,file->fdIn);
    file->bufferLenght=readbyte;
    return readbyte;
}
int writeF (Register_t* file){
    int writebyte = fwrite(file->buffer,1,file->bufferLenght,file->fdOut);
    return writebyte;
}
void closeRegister (Register_t* file){
    free(file->buffer);
    fclose(file->fdIn);
    fclose(file->fdOut);
    free(file);
}

file register.h

#include <stdio.h>
#include <stdlib.h>

typedef struct Register
{
    unsigned char *buffer;
    FILE *fdIn;
    FILE *fdOut;
    long bufferLenght;
} Register_t;

Register_t *openFile(char *inPath, char *outPath, long bufferLenght);
int readF(Register_t *);
int writeF(Register_t *);
void closeRegister(Register_t *);

file Makefile

all: bvernan    

bvernan: encode.o key.o main.o register.o
    gcc -o bvernan $^

%.o: %.c
    gcc -c $<

clean:
    rm *.o bvernan
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

if I can make some improvements to it or if there are any error.

Lack of error checking

fopen(), malloc(), ftell(), fread(), (all I/O functions), etc. deserve to have their return values checked for errors.

In particular when a file may fail to open or memory fail to allocate, handle such cases to not forget the other. e. g.: do not forget to fclose(), or fclose() twice when memory allocation failed.

Gracefully handling errors and still maintaining nice code flow is challenging.

Lack of comments

some comments would help, especially in .h files. A header files is sometimes all a user sees (or cares to see) .

.h files

Missing code guards.

Reduce namespace scattering. Recommend a common prefix per objects in a .h file

Types

long OK for most files sizes. size_t better for array sizing and indexing.

long lenght ... for(int i=0;i<lenght;i++) --> i and length should be the same type: size_t.

Style

Use an auto formatter to improve code appearance at less time cost.

Tolerant free

Note that free(NULL) is well defined. Do so like-wise for freeKey(NULL);

Spelling

lenght --> length.

Spaces

Style is very dense left=to=right. Some spaces would help.

// block[i]=block[i]^key->buffer[i];
block[i] = block[i] ^ key->buffer[i];

Include order

For xxx.c, I recommend including xxx.h first, to help test its ability to stand alone.

// file register.c

#include "register.h"  // Put first
#include <stdio.h>
#include <stdlib.h>

Unclear why register.h has #include <stdlib.h>. Excessive #include files in a .c file have their place and are not so much a timely concern. (Some coding environments offer a check.)

.h file should use a minimal set.

Naming

Consider using the same case and order for the names key.c, Key_t --> key.c, key_t. keySize(), openKey() --> keySize(), keyOpen().

Allocating

Rather than size by type, size by the referenced object. Easier to code right, review and maintain.

// ptr = malloc(sizeof(hopefully_the_right_type));
ptr = malloc(sizeof *ptr);

Simplistic error handling examples

FILE*file = fopen(path,"rb");
if (file == NULL) {
  fprintf(stderr, "Unable to open file <%s> for reading.\n", path);
  exit EXIT_FAILURE;
}

Key_t* kFile = malloc(sizeof *kFile);
if (kFile == NULL) {
  fprintf(stderr, "Out of memory.\n");
  exit EXIT_FAILURE;
}

long size = ftell(file);
if (size == -1 || size > SIZE_MAX) {
  fprintf(stderr, "File size problem %ld.\n", size);
  exit EXIT_FAILURE;
}

Allow freeKey(NULL)

This allows calls to freeKey(k) without first checking if k is non-null, just like free(NULL) is allowed. Having a check inside freeKey(NULL) allows for simply calls to it, especially if the higher level code itself is in some error recovery block.

void freeKey(Key_t* key) {
  if (key) {  // add
    free (key->buffer);
    free (key);
  }
}
\$\endgroup\$
5
  • \$\begingroup\$ could you add an example for error checking? i dont get it what you mean with code guards in .h files. sorry im not so expert in C language \$\endgroup\$
    – Somakun
    Commented Jul 21, 2021 at 15:47
  • 1
    \$\begingroup\$ @Somakun See include guard. Error checking example later. \$\endgroup\$
    – chux
    Commented Jul 21, 2021 at 15:55
  • \$\begingroup\$ ok i got it. i successfully added code guards. you link was really helpful. could you give me help with error checking? \$\endgroup\$
    – Somakun
    Commented Jul 21, 2021 at 23:45
  • \$\begingroup\$ i also didn't understand what you mean when you talked about free(NULL) well defined \$\endgroup\$
    – Somakun
    Commented Jul 22, 2021 at 0:01
  • 1
    \$\begingroup\$ @Somakun Done.. \$\endgroup\$
    – chux
    Commented Jul 22, 2021 at 13:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.