Good day, Habrahabr!

In the process of writing one of my programs I needed to figure out the encryption and de-encryption of the text library. I figured it out and now I want to share my accumulated experience and knowledge with the community.
This article focuses on the libgcrypt library.
Foreword
I am writing a program under Linux. Therefore, the library was also looking for this OS. I did not try to find dozens of libraries, then to choose the best. I chose the one that fit my needs - the time that is used in fairly well-known products - two.
')
About the libgcrypt library itself
The library provides a high-level interface to low-level cryptography mechanisms. Simply put, you choose the encryption mechanism you need, invent a password and encode the text, without delving into how your chosen algorithm works.
The library is written as part of the GnuPG project and is distributed under the LGPL.
Encryption process
In the encryption process, we will use the following functions (listed in order of use):
- gcry_cipher_open - create a context descriptor
- gcry_cipher_setkey - set the password
- gcry_cipher_setiv - set the initialization vector
- gcry_cipher_encrypt - text encryption function
- gcry_cipher_close - closing context handle
Now more about each function.
gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *hd, int algo, int mode, unsigned int flags)
The function creates a context handle, which is required for further encryption functions and returns the handle to 'hd'. In case of an error, a non-zero error code is returned.
hd is a pointer to our future context descriptor.
algo is the algorithm we are going to use to encrypt text. Examples:
GCRY_CIPHER_IDEA - IDEA algorithm. Although you can choose it, it will not work. Since the algorithm is patented, for it there is no implementation in the free library.
GCRY_CIPHER_3DES - (Triple-DES with 3 Keys as EDE) symmetric block cipher.
GCRY_CIPHER_BLOWFISH - Blowfish algorithm. The current implementation allows you to use only a 128-bit key.
There are also RIJNDAEL, TWOFISH, AES, SERPENT and so on.
(The whole list of algorithms to find
here )
mode is one of the following options:
- GCRY_CIPHER_MODE_NONE - do not use any modifier. (You should avoid using this key.)
- GCRY_CIPHER_MODE_ECB - electronic codebook mode. ( Electronic Code Book )
- GCRY_CIPHER_MODE_CFB - ciphertext feedback mode. ( Cipher FeedBack )
- GCRY_CIPHER_MODE_CBC - mode of coupling of ciphertext blocks. ( Cipher Block Chaining )
- GCRY_CIPHER_MODE_STREAM - mode for use only with stream-algorithms. For example, GCRY_CIPHER_MODE_STREAM.
- GCRY_CIPHER_MODE_OFB - output feedback mode. ( Output FeedBack )
- GCRY_CIPHER_MODE_CTR - counter mode. ( Counter )
flags - can be 0 (zero), or a combination of the following flags:
- GCRY_CIPHER_SECURE - all operations are located in protected memory.
- GCRY_CIPHER_ENABLE_SYNC - this flag turns on CFB synchronization mode.
- GCRY_CIPHER_CBC_CTS - enables CTS (cipher text stealing) for CBC mode. (Flan can not be used simultaneously with GCRY_CIPHER_CBC_MAC.) CTS mode allows you to make changes to data of arbitrary size.
- GCRY_CIPHER_CBC_MAC - calculate the checksum CBC MAC keyed. (Flan cannot be used simultaneously with GCRY_CIPHER_CBC_CTS.)
Example
gcryError = gcry_cipher_open( &gcryCipherHd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS);
In order to continue working with the handle, first of all we need to set the key using the gcry_cipher_setkey function.
gcry_error_t gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *k, size_t l)
hd - previously received handle
k - key, it is - the password (character string)
l - key length (strlen (k))
Most encryption modes require an initialization vector, which is usually a non-secret random string that acts as a salt. In the case of CTR mode, you must specify a counter, which is also similar to the value of “salt”. To set these values, use the functions:
- gcry_cipher_setiv (gcry_cipher_hd_t h, const void * k, size_t l)
Set the initialization vector used for encryption or de-encryption. The vector is transmitted as a buffer k long l bytes and copied into the internal data structure. The function also checks if the vector meets the necessary requirements for a given algorithm (algo) and mode (mode). - gcry_cipher_setctr (gcry_cipher_hd_t h, const void * c, size_t l)
Set wind counter used for encryption or de-encryption. The counter is transferred as a buffer k long l bytes and copied to the internal data structure. The function also checks if the vector meets the necessary requirements for a given algorithm (that is, the vector must be as large as the block size).
Here we come to the key point - encryption. The encryption process itself is performed by the gcry_cipher_encrypt function.
gcry_error_t gcry_cipher_encrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
The function can work with one or two buffers. If the in value is transmitted as NULL and inlen is 0 (zero), then encryption is performed with one buffer. In simple terms, the out buffer, in which, before calling a function, contains unencrypted text, upon exiting the function, will be rewritten with new, encrypted text. If the in value is transmitted as non-NULL, then the inlen byte is encrypted and placed in the out buffer (which must be at least inlean size). The outsize must display the size of the allocated piece of memory for the out buffer so that the function can check whether there is enough space for output. (Overlapping buffers is not allowed.)
Depending on the selected algorithm and encryption mode, the buffer length should be a multiple of the block size.
In case of successful encryption, the return code is 0 (zero). Otherwise, an error code is returned.
To free memory and handle use the gcry_cipher_close function.
void gcry_cipher_close (gcry_cipher_hd_t h)
This function will release the context created at runtime gcry_cipher_open. The function also overwrites all sensitive information that was created within the h descriptor with zeros.
De-encryption process
The de-encryption process is similar to the encryption process functions that need to be invoked. Namely (listed in order of use):
- gcry_cipher_open - create a context descriptor
- gcry_cipher_setkey - set the password
- gcry_cipher_setiv - set the initialization vector
- gcry_cipher_decrypt - text de-encryption function
- gcry_cipher_close - closing context handle
Since all functions (except gcry_cipher_decrypt) are similar, we consider only the de-encryption function itself.
gcry_error_t gcry_cipher_decrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
h - context handle
out - the buffer where the resulting (decrypted) text will be placed
outsize - amount of allocated memory for the out buffer
in - cipher text
inlen - the size of the cipher text
Just like the encryption function, the de-encryption function can do with a single buffer. To do this, you must pass zeros instead of in and inlen. If, however, these parameters are not zero, then inlen bytes is decoded and put into the out buffer, which must be at least equal to inlen. The outsize must be set to a value equal to the number of bytes allocated for the out buffer so that the function can make sure that there is enough space for the result. (Overlap by buffer is not allowed.)
Depending on the selected algorithm and encryption mode, the buffer length should be a multiple of the block size.
If successful, the function returns 0. Otherwise, an error code is returned.
Illustrative example
#include <stdio.h> #include <gcrypt.h> #define ENCR 1 #define DECR 0 void myCrypt(int encdec, const char * pass, const char * salt, const char * text) { gcry_error_t gcryError; gcry_cipher_hd_t hd; size_t i; size_t passLength = strlen(pass); size_t saltLength = strlen(salt); size_t textLength = strlen(text)+encdec; char * outBuffer = (char*)malloc(textLength); printf("%scryption...\n", encdec?"En":"De"); printf("passLength = %d\n", passLength); printf("saltLength = %d\n", saltLength); printf("textLength = %d\n", textLength); printf(" pass = %s\n", pass); printf(" salt = %s\n", salt); printf(" text = %s\n", encdec?text:"<null>"); // - GCRY_CIPHER_AES128 // // GCRY_CIPHER_CBC_CTS, gcryError = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS); if (gcryError) { printf("gcry_cipher_open failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; } gcryError = gcry_cipher_setkey(hd, pass, passLength); if (gcryError) { printf("gcry_cipher_setkey failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; } gcryError = gcry_cipher_setiv(hd, salt, saltLength); if (gcryError) { printf("gcry_cipher_setiv failed: %s/%s\n", gcry_strsource(gcryError),gcry_strerror(gcryError)); return; } switch (encdec) { case ENCR: gcryError = gcry_cipher_encrypt(hd, outBuffer, textLength, text, textLength); break; case DECR: gcryError = gcry_cipher_decrypt(hd, outBuffer, textLength, text, textLength); } if (gcryError) { printf("gcry_cipher_encrypt failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; } switch (encdec) { case ENCR: printf("Ecnrypted text = "); for (i = 0; i<textLength; i++) printf("%02X", (unsigned char)outBuffer[i]); printf("\n"); break; case DECR: printf("Original text = %s\n", outBuffer); } gcry_cipher_close(hd); free(outBuffer); } int main(int argc, char **argv) { if ( argc != 4 ) { printf("usage: %s <-e|-d> \"<password>\" \"<salt>\"\n", argv[0]); return 1; } int encdec = ENCR; char line[1024]; printf("Enter text: "); fgets(line, sizeof(line), stdin); if ( !strcmp(argv[1], "-d") ) { // 16 int i = 0; char a[3] = {"00"}; for (; i<strlen(line); i+=2) { sprintf(a, "%c%c", line[i], line[i+1]); line[i/2] = strtol(a, NULL, 16); } line[i/2-1] = '\0'; encdec = DECR; } myCrypt(encdec, argv[2], argv[3], line); return 0; }
Compile for linux
gcc -o crypto main.c -lgcrypt
Run
[serge@magnum enc]$ ./crypto -e "This's my passwd" "It is kinda salt"
Enter text:
Encryption...
passLength = 16
saltLength = 16
textLength = 65
pass = This's my passwd
salt = It is kinda salt
text =
Ecnrypted text = 7DA4C2CB7088BC7432E243B1B1ACAE2A4301CE92D5884404B5AFF181EC4C1B17D3B0565FD82BD88D78916506048BA20E87FA5DDE39288FCC32CA3EF02647F7B140
[serge@magnum enc]$ ./crypto -d "This's my passwd" "It is kinda salt"
Enter text: 7DA4C2CB7088BC7432E243B1B1ACAE2A4301CE92D5884404B5AFF181EC4C1B17D3B0565FD82BD88D78916506048BA20E87FA5DDE39288FCC32CA3EF02647F7B140
Decryption...
passLength = 16
saltLength = 16
textLength = 65
pass = This's my passwd
salt = It is kinda salt
text =
Original text =
A more detailed description of the library and interface functions can be found at:
http://www.gnupg.org/documentation/manuals/gcrypt