Commit fa353ee8 authored by T. Fischer's avatar T. Fischer

New program to generate passwords

Discerning feature: can generate passwords that use only characters
that are at the same position in German, English, and Swedish keyboard
layouts.
parent 3a2909f8
CFLAGS+=-Wall -ansi -std=c11 -pedantic
LDFLAGS=
OBJECTS=makepasswd.o
HEADERS=
all: makepasswd
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o [email protected] $<
makepasswd: $(OBJECTS)
$(CC) $(LDFLAGS) -o [email protected] $^
style:
LC_ALL=C LANG=C astyle --align-reference=name --align-pointer=name --indent=spaces=4 --indent-labels --pad-oper --unpad-paren --pad-header --keep-one-line-statements --convert-tabs --indent-preprocessor $$(find . -type f -name '*.c')
strip: makepasswd
strip $<
mrproper: clean
rm -f makepasswd
clean:
rm -f *~ *.orig
rm -f $(OBJECTS)
/* Copyright (c) 2018, Thomas Fischer <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Thomas Fischer nor the names of other contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THOMAS FISCHER BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static const char *numbers = "0123456789";
static const unsigned int mod_for_numbers = 10;
static const size_t bits_for_numbers = 4;
static const char *alphanum = "0123456789qaywsxedcrfvtgbzhnujmikolpQAYWSXEDCRFVTGBZHNUJMIKOLP";
static const unsigned int mod_for_alphanum = 62;
static const size_t bits_for_alphanum = 6;
static const char *alphanumspecial = "0123456789qaywsxedcrfvtgbzhnujmikolpQAYWSXEDCRFVTGBZHNUJMIKOLP!\"#%&/()=+?^*'-_.,;<>{}[]";
static const unsigned int mod_for_alphanumspecial = 87;
static const size_t bits_for_alphanumspecial = 7;
static const char *keyboardsafe = "0123456789qawsxedcrfvtgbhnujmikolpQAWSXEDCRFVTGBHNUJMIKOLP!%.,";
static const unsigned int mod_for_keyboardsafe = 62;
static const size_t bits_for_keyboardsafe = 6;
static const unsigned int ERROR_RANDOM_BITS = 0xffff;
/**
* Print help on how to use this program.
* Output is written to a file device as specified by
* the caller, usually 'stdout' or 'stderr'.
*/
void print_help(FILE *output)
{
fprintf(output, "makepasswd v0.1\nCopyright 2018 by Thomas Fischer <[email protected]>\n\n");
fprintf(output, "Usage: makepasswd [--help] [--length N] [--num|--alpha|--special|--keyboard]\n");
fprintf(output, " --help Print this help and exit without computing a password\n");
fprintf(output, " --length N Computed password shall be N characters long\n");
fprintf(output, " --num Computed password shall contain only digits 0 to 9\n");
fprintf(output, " --alpha Computed password shall contain only digits 0 to 9\n");
fprintf(output, " and letters A to Z (both upper and lower case)\n");
fprintf(output, " --special Computed password shall contain only digits 0 to 9,\n");
fprintf(output, " letters A to Z (both upper and lower case),\n");
fprintf(output, " and selected special characters like ! or #\n");
fprintf(output, " --keyboard Computed password shall contain only characters\n");
fprintf(output, " that have the same position on English, German,\n");
fprintf(output, " and Swedish keyboard layouts, such as 1, g, and !\n");
fprintf(output, "\nArguments --num, --alpha, --special, and --keyboard are mutually exclusive.\n");
}
/**
* Read a limited number of random bits from /dev/urandom
* and return them as the LSB of an unsigned int.
* The parameter 'queried_bits's value must be less than
* the number of bits an unsigned int can hold.
*/
unsigned int get_random_bits(const size_t queried_bits)
{
/// Check input arguments for sanity
if (queried_bits >= (sizeof(unsigned int) * 8)) return ERROR_RANDOM_BITS;
/// Declare and initialize internal buffer of random bits
static unsigned int buffered_random_bits = 0;
static size_t remaining_bits = 0;
/// Check if more random bits need to be retrieved from a random device
if (remaining_bits < queried_bits) {
FILE *devrandom = fopen("/dev/urandom", "r");
if (devrandom != NULL) {
while (remaining_bits < queried_bits) {
unsigned short new_random_bytes = 0;
if (fread(&new_random_bytes, sizeof(unsigned short), 1, devrandom) != 1) break;
buffered_random_bits <<= (sizeof(unsigned short)) * 8;
buffered_random_bits |= new_random_bytes;
remaining_bits += (sizeof(unsigned short)) * 8;
}
fclose(devrandom);
}
}
if (remaining_bits >= queried_bits) {
/// Extract result from buffered random bits using a mask
unsigned int resulting_bits = buffered_random_bits & ((1 << queried_bits) - 1);
/// Remove extracted bits from static buffer via shift left
buffered_random_bits >>= queried_bits;
remaining_bits -= queried_bits;
/// Return result
return resulting_bits;
} else
return ERROR_RANDOM_BITS;
}
int main(int argc, char *argv[])
{
int num_characters = 8;
enum CharSet {Numbers, AlphaNum, AlphaNumSpecial, KeyboardSafe};
enum CharSet charset = KeyboardSafe;
/// Process command line options, overwriting default values
for (int i = 1; i < argc; ++i) {
if (i < argc - 1 && strlen(argv[i]) == 7 && strncmp(argv[i], "--length", 8) == 0) {
num_characters = atoi(argv[i + 1]);
++i;
} else if (strlen(argv[i]) > 9 && strncmp(argv[i], "--length=", 9) == 0)
num_characters = atoi(argv[i] + 9);
else if (strncmp(argv[i], "--num", 5) == 0)
charset = Numbers;
else if (strncmp(argv[i], "--alpha", 7) == 0)
charset = AlphaNum;
else if (strncmp(argv[i], "--special", 9) == 0)
charset = AlphaNumSpecial;
else if (strncmp(argv[i], "--keyboard", 10) == 0)
charset = KeyboardSafe;
else if (strncmp(argv[i], "--help", 6) == 0) {
print_help(stdout);
return 1;
} else {
fprintf(stderr, "Unknown or incomplete argument: %s\n", argv[i]);
return 1;
}
}
if (num_characters <= 0 || num_characters > 512) {
fprintf(stderr, "Invalid number of characters given\n");
return 1;
}
const size_t bits = (charset == Numbers) ? bits_for_numbers : (charset == AlphaNum ? bits_for_alphanum : (charset == AlphaNumSpecial ? bits_for_alphanumspecial : bits_for_keyboardsafe));
const unsigned int mod = (charset == Numbers) ? mod_for_numbers : (charset == AlphaNum ? mod_for_alphanum : (charset == AlphaNumSpecial ? mod_for_alphanumspecial : mod_for_keyboardsafe));
const char *chars = (charset == Numbers) ? numbers : (charset == AlphaNum ? alphanum : (charset == AlphaNumSpecial ? alphanumspecial : keyboardsafe));
for (int i = 0; i < num_characters; ++i) {
const unsigned int r = get_random_bits(bits);
if (r == ERROR_RANDOM_BITS) break;
printf("%c", chars[r % mod]);
}
printf("\n");
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment