weidner/computer/software/c/

SHA1 Hash mit Libgcrypt berechnen

Für ein Nebenprojekt benötige ich ein paar kryptografische Funktionen, die ich keinesfalls selbst programmieren will.

Die erste Funktion, ist das Berechnen eines SHA1-Hashes, für die ich eine geeignete Bibliothek in C suchte. Recht schnell fand ich bei Stack Overflow einen Verweis auf Libgcrypt mit Link zum Ubuntu-Forum und Beispielcode, der immer noch funktioniert.

So weit so gut, damit habe ich die erste Hürde mit Libgcrypt - mach mal was, das funktioniert - genommen.

Natürlich hätte ich mein Nebenprojekt nun so anpassen können, dass es zum Beispielcode mit Libgcrypt passt. Das hätte den Code aber unübersichtlich und schlecht wartbar gemacht. Darum habe ich mich in die Dokumentation von Libgcrypt eingelesen und versucht, etwas Eigenes zu schreiben, was die in der Dokumentation beschriebenen Funktionen nutzt und sich besser an meine Bedürfnisse anpassen lässt.

SHA1 über STDIN

Der erste Versuch war ein Testprogramm, dass beliebig viele Daten über STDIN entgegennimmt und am Ende den SHA1-Hash darüber ausgibt. Für diesen Zweck benötige ich ein Handle, welches mit gcry_md_open() erzeugt wird. Diesem Handle kann ich dann mit verschiedenen Funktionen die Daten übergeben. Am Ende lese ich den Hash mit gcry_md_read() aus und schließe das Handle.

Soweit die Theorie. In der Praxis war ich schon an der Funktion gcry_md_open() gescheitert, die unverzüglich einen Segmentation Fault auslöste. Leider fand ich keinen Beispielcode dafür und die Angaben in der Dokumentation waren für mich nicht eindeutig, wie die Funktion zu benutzen wäre.

Schließlich hatte ich genug von meinen Versuchen, die Dokumentation zu interpretieren, und den gescheiterten Experimenten. Ich besorgte mir die aktuellen Quellen von Libgcrypt und fand die Lösung für mein Problem in einer der Dateien im Verzeichnis test/, konkret in tests/basic.c in Funktion check_one_md(). Mit dem daraus entnommenen Beispielcode schrieb ich das folgende kleine Programm, welches Daten von STDIN liest und am Ende den SHA1-Hash nach STDOUT schreibt.

// hmac_sha1_stdin.c - create SHA1 Digest from STDIN

#include <gcrypt.h>
#include <stdio.h>

void print_buffer(unsigned char* buf, int buf_len) {
    int i;
    for ( i = 0; i < buf_len; i++) {
        printf ("%02x", buf[i]);
    }
    printf("\n");
}// print_buffer();

int main(int argc, char **argv) {
    gcry_error_t gerr;
    gcry_md_hd_t hd;
    int algo = GCRY_MD_SHA1;

    gerr = gcry_md_open(&hd, algo, 0);
    if (gerr) {
            fprintf(stderr, "algo %d, gcry_md_open failed: %s\n", algo, gpg_strerror (gerr));
        exit(1);
    }

    int c;
    while (EOF !=(c = fgetc(stdin))) {
        gcry_md_putc(hd, c);
    }

    int hash_len = gcry_md_get_algo_dlen(algo);
    unsigned char *hash = gcry_md_read(hd, algo);
    print_buffer(hash, hash_len);

    gcry_md_close(hd);
}// main()

Mit folgendem Befehl kann das Programm übersetzt werden:

gcc -g -o hmac_sha1_stdin hmac_sha1_stdin.c -lgcrypt -lgpg-error

Es liefert die gleiche Hash wie sha1sum:

$ echo 2345 | ./hmac_sha1_stdin 
e0f81408b500f21e030d8292e82cd1eabd5dea2e
$ echo 2345 | sha1sum
e0f81408b500f21e030d8292e82cd1eabd5dea2e  -

Und mit nur einer kleinen Änderung (GCRY_MD_SHA256 statt GCRY_MD_SHA1 in der Zuweisung an algo) berechnet das Programm die Hash für SHA256, was sich mit sha256sum überprüfen lässt.

SHA1 über mehrere Puffer berechnen

In meinem Nebenprojekt brauche ich den SHA1-Hash jedoch über mehrere Puffer:

RFC7296, 2.23. NAT-Traversal

...

...

Zwar könnte ich auch diese drei Positionen jeweils in einer Schleife mit gcry_md_putc() an libgcrypt übergeben, aber es gibt ja die Funktion gcry_md_write(), mit der ich einen ganzen Puffer auf einmal übergeben kann.

Mit dieser Funktion ändert sich die innere Schleife meines Beispielprogramms zu folgendem:

    size_t len, bufsize = 3;
    char buf[bufsize];
    while (len = fread(buf, 1, bufsize, stdin)) {
            gcry_md_write(hd, buf, len);
    }

Ich nutze zwar immer denselben Puffer, doch es geht mir auch nur darum, den Aufruf von gcry_md_write() auszuprobieren. Dieser Funktion übergebe ich bei jedem Aufruf drei Bytes, es sei denn, fread() liefert weniger, zum Beispiel, weil das Ende der Eingabe erreicht ist.

Damit hat sich der Aufwand für das Einarbeiten in diese Bibliothek gelohnt. Ich vermute zwar, dass ich bei anderen Funktionen wieder wie der Ochs vorm Tor stehen werde. Aber jetzt habe ich die Quellen auf dem Rechner und werde gleich in den Testdateien nach Beispielcode suchen.

Updates:

Posted 2020-07-24
Tags: