Почему я получаю ошибку segfault при попытке использовать AES-GCM доктора Брайана Глэдмана?

#c #encryption #segmentation-fault #aes

#c #шифрование #ошибка сегментации #aes

Вопрос:

Я пытаюсь использовать знаменитые процедуры шифрования AES доктора Брайана Глэдмана, в данном случае версию режима счетчика Галуа, и я получаю ошибку segfault и не могу найти причину.

Я написал небольшую демонстрационную программу, которая показывает, что я пытаюсь сделать, и на самом деле кажется, что она работает, но в конце она прерывается.

Я немного заржавел в C, поэтому ошибка может быть очевидна для более мудрых голов.

Вот моя программа, которая, я надеюсь, будет максимально короткой и читаемой:

 /* This program tries to be a demo for Dr Brian Gladman's AES-GCM encryption code.
   The plan is to use AES-GCM to encrypt a short message, and provide an authentication key.
   And then to decrypt the ciphertext, verifying the authentication tag on the way.*/

#include<stdio.h>
#include<assert.h>

#include "aes-modes/gcm.h"

/*note that uint_8t is Dr Gladman's type, not to be confused with uint8_t from C99*/

void print_uint_8t_array(uint_8t *a, int len, char* name)
{
  int i;
  printf("%-10s:", name);

  for(i=0; i< len; i  ){
    if(a[i]==0x00) printf("-");
    else if(a[i]>=0x20 amp;amp; a[i]<=0x7e) printf("%c", a[i]); 
    else printf("~");
  }
  printf("n");
}

#define p8(name) (print_uint_8t_array((name), (sizeof(name)), (#name)))

/*Dr Gladman's functions return 0,1 or -1, to be interpreted so:*/
void interpret_retval(AES_RETURN retval){
  switch(retval){
  case      0: 
    printf("RETURN_GOODn");
    break;
  case 1:
    printf("RETURN_WARNn");
    break;
  case -1:
    printf("RETURN_ERRORn");
    break;
  default: printf("Unknown return value");
    assert(0);
    break;
  }
}

int main(void){
  printf("GCM-AES encryption/decryption/authentication examplen");

  aes_init(); 
  /*I think that this is only needed if you've compiled the libraries to make up
    their tables at runtime. If the tables are compiled in, then it's
    unnecessary but probably doesn't do any harm.*/

  uint_8t key[32]="01234567890123456789012345678901"; 
  uint_8t iv[32]="01234567890123456789012345678901";

  uint_8t header[]="Here is a header, which is not to be encrypted, but which is nevertheless to be proved to have been placed in the message by someone who knows the key.";
  uint_8t message[]="Here is some text which is to be protected from the prying eyes of those who do not know the key, whilst at the same time it is to carry along with it an unencrypted header, and a with them both a tag proving beyond reasonable doubt that the two were packaged together and run through the aes-gcm algorithm by someone who knew the key."; 
  uint_8t tag[32]="01234567890123456789012345678901";

  void printtexts(void)
  {
    printf("-----------------------------------n");
    p8(key);
    p8(iv);
    printf("-----------------------------------n");
    p8(header);
    p8(message);
    p8(tag);
    printf("-----------------------------------n");
  }

  printtexts();

  {
    gcm_ctx ecx[1];
    printf("encrypting....n");
    gcm_init_and_key(key, sizeof(key), ecx);
    interpret_retval(gcm_encrypt_message(iv, sizeof(iv), header, sizeof(header), message, sizeof(message), tag, sizeof(tag), ecx));
    gcm_end(ecx);
    printf("donen");
  }

  printtexts();

  {
    gcm_ctx dcx[1];

    printf("decrypting....n");
    gcm_init_and_key(key, sizeof(key), dcx);
    interpret_retval(gcm_decrypt_message(iv, sizeof(iv), header, sizeof(header), message, sizeof(message), tag, sizeof(tag), dcx));
    gcm_end(dcx);
    printf("donen");
  }

  printtexts();

  return 0;

}
  

И вот результат. Кажется, что шифрование / дешифрование и пометка работают, но затем появляется неверный код возврата и ошибка segfault.

 jla@jaspden-desktop$ ./test3
GCM-AES encryption/decryption/authentication example
-----------------------------------
key       :01234567890123456789012345678901
iv        :01234567890123456789012345678901
-----------------------------------
header    :Here is a header, which is not to be encrypted, but which is nevertheless to be proved to have been placed in the message by someone who knows the key.-
message   :Here is some text which is to be protected from the prying eyes of those who do not know the key, whilst at the same time it is to carry along with it an unencrypted header, and a with them both a tag proving beyond reasonable doubt that the two were packaged together and run through the aes-gcm algorithm by someone who knew the key.-
tag       :01234567890123456789012345678901
-----------------------------------
encrypting....
RETURN_GOOD
done
-----------------------------------
key       :01234567890123456789012345678901
iv        :01234567890123456789012345678901
-----------------------------------
header    :Here is a header, which is not to be encrypted, but which is nevertheless to be proved to have been placed in the message by someone who knows the key.-
message   :~(}Hr~|o~~:~D~~~~3~~T~C~C(l~~~'~~~UC~D~~~~amp;O~ ~L~-~~~Eamp;~~~~!~~~~~K~~~~M<|l%<ho~"~[A~0~~O~3T~%8K~~~L~~~~~~~~~7~~~~~~~~e~~~~;~392~8<~~ ~~v,E~~~~~~~~~~~W~yd~~C$H~~~*r~~~_~~~~O~u~h9s~~`%~~~~~amp;~~~~~~.~~Q~~~Y~Ix~A~~~a5~~~~|~~9J~~~~~~~~ ~~ZOiX~~~~~~f~~`e~~~Ju~Z~~~X~g~OX~C~~~~~~~~~(~~gyT~~x~L~4>~Z~~ge~~~08~;~@~s~3~~WA~~~~Z-~~~~/~~~~~~Tj~bL~~
tag       :|~~,~2~~Ec~~~A~~~~~_j{~~~~`~~~u~
-----------------------------------
decrypting....
RETURN_ERROR
done
-----------------------------------
key       :01234567890123456789012345678901
iv        :01234567890123456789012345678901
-----------------------------------
header    :Here is a header, which is not to be encrypted, but which is nevertheless to be proved to have been placed in the message by someone who knows the key.-
message   :Here is some text which is to be protected from the prying eyes of those who do not know the key, whilst at the same time it is to carry along with it an unencrypted header, and a with them both a tag proving beyond reasonable doubt that the two were packaged together and run through the aes-gcm algorithm by someone who knew the key.-
tag       :|~~,~2~~Ec~~~A~~~~~_j{~~~~`~~~u~
-----------------------------------
Segmentation fault (core dumped)
  

Комментарии:

1. Если я правильно читаю выходные данные, похоже, что ошибка segfault происходит в самом конце, после последнего вызова printtexts() . Возможно, что-то повреждает стек? Обратите внимание, что вы получаете ошибку во время расшифровки — выясните, что является ее причиной, и, возможно, ваша ошибка segfault также будет решена.

2. Спасибо, это отличный совет, хотя на самом деле я в конечном итоге обнаружил, где стек был разбит, и это привело к тому, что «ключ слишком велик для локальной переменной в библиотеке» источник проблемы. Поиск в Google затем привел к тому, что тег AES-GSM ограничен 16 байтами.

Ответ №1:

Автоответчик, на случай, если кто-то еще заинтересован в использовании этих процедур.

По-видимому, максимальный размер тега для AES-GSM составляет 16 байт. Измените длину тега на 16 или меньше (у меня здесь 32), и все работает нормально.

Похоже, что это ограничение жестко запрограммировано в gcm_decrypt_message как BLOCK_SIZE . Изменение этого значения на 32 также, похоже, работает, но один Господь знает, что это делает с криптографическими свойствами алгоритма.

В примечании к отладке у gcc есть опция -fstack-protector-all, которая может перехватывать библиотеки в процессе уничтожения стека возврата, что и происходило здесь.

Насколько я могу судить, ошибка segfault возникает, когда main пытается вернуться к полностью вымышленному адресу, который был записан в стек библиотекой.

Комментарии:

1. Тег GCM может иметь размер не более одного полного блока блочного шифра. Поскольку AES содержит 16-байтовые блоки, больше этого невозможно. (Я не смотрел на реализацию, чтобы увидеть, какой эффект будет иметь больший размер, но, конечно, у вас не будет стандартного AES-GCM.)