/*

reinhard@finalmedia.de
Mi 8. Jul 12:53:01 CEST 2026
Public Domain

liest von stdin und erzeugt auf stdout
einen crypt hash. default methode ist "yescrypt",
wie es von aktuellen linux distributionen eingesetzt wird.
Es sind aber auch andere methoden an crypt durchreichbar.

 # Compilieren mittels:

 gcc -Q -march=native -O3 -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wl,-z,relro -Wl,-z,now -o crypt crypt.c -lcrypt
 strip crypt

 # Nutzung mittels:

 echo beispielpasswort | ./crypt y

 # Methoden (nur erster Character des ersten Arguments. damit supporten wir nicht alles.)

 1 ... md5
 5 ... sha-256
 6 ... sha-512
 y ... yescrypt

*/

#include <unistd.h>
#include <crypt.h>

static char get_method_char(int argc, char *argv[]) {
    if (argc < 2 || argv == NULL || argv[1] == NULL) {
        return 'y';
    }
    char c = argv[1][0];
    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) {
        return c;
    }
    _exit(1);
}

int main(int argc, char *argv[]) {
    static char password[CRYPT_MAX_PASSPHRASE_SIZE];
    char salt[CRYPT_GENSALT_OUTPUT_SIZE], c;
    int len = 0;

    char prefix[] = {'$', get_method_char(argc, argv), '$', '\0'};

    while (read(0, &c, 1) > 0 && c != '\n' && c != '\r') {
        if (len < (CRYPT_MAX_PASSPHRASE_SIZE - 1)) password[len++] = c;
    }
    password[len] = '\0';

    if (crypt_gensalt_rn(prefix, 0, NULL, 0, salt, sizeof(salt)) == NULL) return 1;
    char *hash = crypt(password, salt);

    if (hash && hash[0] != '*') {
        int hash_len = 0;
        while (hash[hash_len] != '\0') hash_len++;

        if (write(1, hash, hash_len) != hash_len) return 1;
        if (write(1, "\n", 1) != 1) return 1;
        return 0;
    }
    return 1;
}
