? users online
  • Logout
    • Open hangout
    • Open chat for current file
<div class="notebook open-fullscreen">

<div class="nb-cell markdown" name="md1">
# Password Generator

**This is also available for download and offline use at [GitLab](https://gitlab.com/PaulBrownMagic/PPFN/tree/master/PasswordGenerator).**

---
**Note:** This implementation makes use of the builtins `random/0` and `random_member/2`, which are not considered
cryptographically secure. In the [**offline** version](https://gitlab.com/PaulBrownMagic/PPFN/tree/master/PasswordGenerator) `password_generator.pl` file, this is addressed
by using a custom `pwgenrandom` module to generate random numbers, also included. Find out more:
- [SWI-Prolog docs for `random/0`](http://www.swi-prolog.org/pldoc/doc_for?object=f(random/1))
- [SWI-Prolog docs for crypto library](http://www.swi-prolog.org/pldoc/man?section=crypto)
- [Random numbers in password generation on Wikipedia](https://en.wikipedia.org/wiki/Random_password_generator)
---
</div>

<div class="nb-cell markdown" name="md11">
A good password is a mix of uppercase letters, lowercase letters, numbers and punctuation. Let's make a program to automatically generate them, you can then test the results at [howsecureismypassword](https://howsecureismypassword.net/).

First, let's start with our data. In this case we need a list of letters and punctuation that we can include in our password. We'll generate the rest from these.
</div>

<div class="nb-cell program" data-background="true" name="p1">
%! letters(Letters) is det.
% Contains all the letters in the alphabet, lowercase atoms
letters([a, b, c, d, e, f, g, h, i, j, k, l, m,
         n, o, p, q, r, s, t, u, v, w, x, y, z]).

%! punctuation(Punctuation) is det.
% Chosen punctuation symbols for possible inclusion
punctuation(['#', '$', '&amp;', '(', ')', '|', '£', '?', '!']).
</div>

<div class="nb-cell markdown" name="md2">
Now we'll need to get random characters of different kinds.

## Random Letters, Punctuation, and Numbers

Let's start with a random lowercase letter, we'll write a predicate for ease.
</div>

<div class="nb-cell program" data-background="true" name="p2">
%! random_letter(-L) is det. 
% Generates a random character as an atom
random_letter(L) :-
    letters(Letters),
    random_member(L, Letters).
</div>

<div class="nb-cell markdown" name="md3">
This gets the list of letters, and chooses a random member of that list. You can try it out a few times:
</div>

<div class="nb-cell query" name="q1">
random_letter(One), random_letter(Two), random_letter(Three).
</div>

<div class="nb-cell markdown" name="md4">
To get an uppercase letter, we can convert our lowercase one:
</div>

<div class="nb-cell program" data-background="true" name="p3">
%! random_uppercase_letter(-U) is det.
%  Generates a random uppercase letter
random_uppercase_letter(U) :-
    random_letter(L),
    upcase_atom(L, U).
</div>

<div class="nb-cell query" name="q2">
random_uppercase_letter(One), random_uppercase_letter(Two).
</div>

<div class="nb-cell markdown" name="md5">
Getting punctuation works the same as getting a random_letter:
</div>

<div class="nb-cell program" data-background="true" name="p4">
%! random_punctuation(-P) is det.
%  Generates a random member of punctuation
random_punctuation(P) :-
    punctuation(Punctuation),
    random_member(P, Punctuation).
</div>

<div class="nb-cell query" name="q4">
random_punctuation(One), random_punctuation(Two).
</div>

<div class="nb-cell markdown" name="md6">
And for random numbers we can make use of the builtin random/1 to get a single digit number:
</div>

<div class="nb-cell program" data-background="true" name="p5">
%! random_number(-N) is det.
%  Generates a number between 0-9 inclusive
random_number(N) :- N is random(9).
</div>

<div class="nb-cell query" name="q3">
random_number(A), random_number(B), random_number(C).
</div>

<div class="nb-cell markdown" name="md7">
## Random Character

Now we can get a random character of each type, but we really want a way to get a random character of a random type. To do this in SWISH we get one of each type and choose one. In the "offline" version, we can be a bit cleverer, choosing a random type, assembling it using `=..`, and then calling it. That's more efficient, but not allowed in SWISH.

`=..` works like this: `random_number(N) =.. [random_number, N]`
</div>

<div class="nb-cell program" data-background="true" name="p6">
%! random_character(-C) is det.
%  Generates a random letter (either case), 
%  number or punctuation
random_character(Char) :-
    random_letter(A), random_uppercase_letter(B), 
    random_punctuation(C), random_number(D),
    random_member(Char, [A, B, C, D]).
</div>

<div class="nb-cell query" name="q5">
random_character(One), random_character(Two), random_character(Three).
</div>

<div class="nb-cell markdown" name="md8">
## Making A Password

We can get our random characters, now we need a list of them! We also want to be able to choose the size of the list, so we'll make a list of the chosen side, and then fill it in with random characters.

`make_password/1` is recursive, so it sets the first slot in the list to a random character, then calls itself with the rest of the list. Once it reaches an empty list, `[]`, it stops.
</div>

<div class="nb-cell program" data-background="true" name="p7">
%! password_generate(-P, +L) is det.
%  P: A list of random characters of length L
%  L: An integer number to set the length of the password
password_generate(P, L) :-
    length(P, L),
    make_password(P).

%! make_password(-P) is det.
%  Recursively populate the password list with random characters
make_password([]).
make_password([C|T]) :-
    random_character(C),
    make_password(T).
</div>

<div class="nb-cell query" name="q6">
password_generate(Password, 8).
</div>

<div class="nb-cell markdown" name="md9">
That's all the hard work done! But we don't really want our password as a list of characters, so we'll turn it into a string.
</div>

<div class="nb-cell program" data-background="true" name="p8">
%! password(-P, +L) is det.
%  Generate a password of length L as a string.
%  P: password, should not be ground when called!
%  L: An integer number to set the length of the password
password(P, L) :-
    password_generate(Password, L),
    atomics_to_string(Password, P).
</div>

<div class="nb-cell markdown" name="md10">
That's it! Now you can make random passwords, just don't forget any that you choose to use.
</div>

<div class="nb-cell query" name="q7">
password(Password, 10).
</div>

</div>