aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuel@nihil.ws>2023-04-08 20:12:35 +0200
committerSamuel Fadel <samuel@nihil.ws>2023-04-08 20:12:35 +0200
commitd314d0cf4b92e1dd9c1f761498b06160dcf5e104 (patch)
tree68dcb1f3d236a7ec174862b0caf6e2e90a29b6cc
Initial commit.
An initial working implementation of the password generating algorithm of lesspass. Lacks any sort of documentation or usage info and simply prints the generated password on stdout. * lpass.c: Initial implementation of lesspass * Makefile: A just works version
-rw-r--r--Makefile5
-rw-r--r--README.md3
-rw-r--r--lpass.c163
3 files changed, 171 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b6fd30e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+CC = gcc
+CFLAGS = -O2 -Wall
+
+lpass: lpass.c
+ $(CC) $(CFLAGS) -lcrypto -o lpass lpass.c
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e385979
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# lpass
+
+A simple implementation of [lesspass](https://github.com/lesspass/lesspass) in C.
diff --git a/lpass.c b/lpass.c
new file mode 100644
index 0000000..2193359
--- /dev/null
+++ b/lpass.c
@@ -0,0 +1,163 @@
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#define MAX_BUF 1024
+#define ENTROPY_ITERATIONS 100000
+#define ENTROPY_KEY_LENGTH 32
+
+#define CHAR_SUBSET_LOWER "abcdefghijklmnopqrstuvwxyz"
+#define CHAR_SUBSET_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define CHAR_SUBSET_DIGITS "0123456789"
+#define CHAR_SUBSET_SYMBOLS "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+
+const char *CHAR_SET = (CHAR_SUBSET_LOWER
+ CHAR_SUBSET_UPPER
+ CHAR_SUBSET_DIGITS
+ CHAR_SUBSET_SYMBOLS);
+
+static BIGNUM *
+calc_entropy(const char *site,
+ const char *login,
+ uint64_t counter,
+ const char *master_pass,
+ int passlen)
+{
+ char salt[MAX_BUF + 1];
+ memset(salt, 0, sizeof(salt));
+ int saltlen = snprintf(salt, MAX_BUF, "%s%s%lx", site, login, counter);
+ if (saltlen > MAX_BUF) {
+ return 0;
+ }
+
+ unsigned char key[ENTROPY_KEY_LENGTH];
+ memset(key, 0, sizeof(key));
+ int status = PKCS5_PBKDF2_HMAC(master_pass, passlen,
+ (const unsigned char *) salt, saltlen,
+ ENTROPY_ITERATIONS,
+ EVP_sha256(),
+ ENTROPY_KEY_LENGTH,
+ key);
+ if (status == 0) {
+ return 0;
+ }
+
+ /* NULL as last arg to allocate a new BIGNUM */
+ return BN_bin2bn(key, ENTROPY_KEY_LENGTH, NULL);
+}
+
+static int
+consume_entropy(char *generated_pass, BIGNUM *entropy, const char *charset, int maxlen)
+{
+ int retval = 1;
+ BN_CTX *ctx = BN_CTX_new();
+ BIGNUM *bn_charsetlen = BN_new();
+ BIGNUM *bn_remainder = BN_new();
+ int charsetlen = strlen(charset);
+ BN_set_word(bn_charsetlen, charsetlen);
+ for (int passlen = strlen(generated_pass);
+ passlen < maxlen;
+ passlen++) {
+ BN_div(entropy, bn_remainder, entropy, bn_charsetlen, ctx);
+ int remainder = BN_get_word(bn_remainder);
+ if (remainder >= charsetlen) {
+ retval = 0;
+ goto consume_entropy_cleanup;
+ }
+ generated_pass[passlen] = charset[remainder];
+ }
+consume_entropy_cleanup:
+ BN_free(bn_remainder);
+ BN_free(bn_charsetlen);
+ BN_CTX_free(ctx);
+ return retval;
+}
+
+static int
+insert_str_pseudo_randomly(char *generated_pass, BIGNUM *entropy, const char *s)
+{
+ int retval = 1;
+ char buf[MAX_BUF + 1];
+ uint64_t passlen = (uint64_t) strlen(generated_pass);
+ BN_CTX *ctx = BN_CTX_new();
+ BIGNUM *bn_passlen = BN_new();
+ BIGNUM *bn_remainder = BN_new();
+ for (char c = *s; *s != '\0'; c = *(++s)) {
+ if (BN_set_word(bn_passlen, passlen) == 0) {
+ retval = 0;
+ goto insert_str_pseudo_randomly_cleanup;
+ }
+ BN_div(entropy, bn_remainder, entropy, bn_passlen, ctx);
+ uint64_t remainder = BN_get_word(bn_remainder);
+ /*
+ * Idea here is to add the char `c` at position `remainder` in `generated_pass`.
+ * 1. Copy the part that would need to be shifted into `buf`.
+ */
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, &generated_pass[remainder], passlen - remainder);
+ /*
+ * 2. Add new character, then copy `buf` back into `generated_pass`.
+ */
+ generated_pass[remainder] = c;
+ strncpy(&generated_pass[remainder + 1], buf, passlen - remainder);
+ passlen++;
+ }
+insert_str_pseudo_randomly_cleanup:
+ BN_free(bn_remainder);
+ BN_free(bn_passlen);
+ BN_CTX_free(ctx);
+ return retval;
+}
+
+static void
+render_pass(BIGNUM *entropy, char *out, int length)
+{
+ /* XXX: FIXME: implement charset restrictions */
+ int num_charsets = 4;
+ consume_entropy(out, entropy, CHAR_SET, length - num_charsets);
+
+ /*
+ * After generating the initial password, add one character of
+ * each charset to ensure at least one is there.
+ */
+ char str_to_add[num_charsets + 1];
+ memset(str_to_add, 0, sizeof(str_to_add));
+ consume_entropy(&str_to_add[0], entropy, CHAR_SUBSET_LOWER, 1);
+ consume_entropy(&str_to_add[1], entropy, CHAR_SUBSET_UPPER, 1);
+ consume_entropy(&str_to_add[2], entropy, CHAR_SUBSET_DIGITS, 1);
+ consume_entropy(&str_to_add[3], entropy, CHAR_SUBSET_SYMBOLS, 1);
+ insert_str_pseudo_randomly(out, entropy, str_to_add);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc != 5) {
+ return 1;
+ }
+
+ const char *master_pass = argv[1];
+ int passlen = strlen(master_pass);
+ const char *site = argv[2];
+ const char *login = argv[3];
+ int length = 0;
+ if (sscanf(argv[4], "%d", &length) == 0) {
+ return 1;
+ }
+
+ uint64_t counter = 1;
+ BIGNUM *entropy = calc_entropy(site, login, counter, master_pass, passlen);
+ if (entropy == NULL) {
+ return 1;
+ }
+
+ char generated_pass[length + 1];
+ memset(generated_pass, 0, sizeof(generated_pass));
+ render_pass(entropy, generated_pass, length);
+ printf("%s\n", generated_pass);
+ BN_free(entropy);
+ return 0;
+}