# [pkpw][]

[![Crates.io](https://img.shields.io/crates/v/pkpw)](https://crates.io/crates/pkpw)
[![npm](https://img.shields.io/npm/v/pkpw)](https://www.npmjs.com/package/pkpw)
[![.github/workflows/ci.yml](https://github.com/jbhannah/pkpw/actions/workflows/ci.yml/badge.svg)](https://github.com/jbhannah/pkpw/actions/workflows/ci.yml)

What if [`correct horse battery staple`][xkcd], but Pokémon.

## Installation

```sh
brew install jbhannah/pkpw/pkpw
```

### CLI/Rust Library

```sh
cargo install pkpw
```

### npm Library

```sh
npm add pkpw
```

## Usage

### CLI

```console
$ pkpw -h
pkpw 1.4.0
Jesse Brooklyn Hannah <jesse@jbhannah.net>
What if correct horse battery staple, but Pokémon.

USAGE:
    pkpw [OPTIONS]

OPTIONS:
    -c, --copy                     Copy the generated value to the clipboard instead of displaying
                                   it
    -h, --help                     Print help information
    -l, --length <LENGTH>          Minimum length of the generated password
    -n, --count <COUNT>            Number of Pokémon names to use in the generated password
                                   [default: 4]
    -s, --separator <SEPARATOR>    Separator between Pokémon names in the generated password; either
                                   a single character, "digit" for random digits, or "special" for
                                   random special characters [default: " "]
    -V, --version                  Print version information
```

### Rust

```rust
use pkpw::generate;
use rand::rng;

let mut rng = rng();
let password = generate(None, 4, " ", None, &mut rng);
```

### Javascript/Typescript

```typescript
import { pkpw } from "pkpw";

const password = pkpw();
```

The Javascript/Typescript package is compiled from Rust into WASM. If embedded
in a [web page][pkpw], for example, all password generation runs in the browser;
only analytics of page visit count, password generation count, and the options
(minimum length, Pokémon name count, and separator character set) are sent over
the network. **Make sure you trust any online password generator that uses this
package before using the passwords you generate from it!**

## But is it secure?

**Disclaimer:** These are just estimates, I have a physics degree but I'm not
a combinatorics or cryptography expert.

[Password entropy][] is calculated using the pool size $R$ and password length
$L$ used to generate a password:

$$
E = log_2(R^L)
$$

where a brute-force attack will need an average of $2^{E-1}$ guesses to
crack a password with $E$ bits of entropy.

By default, `pkpw` chooses **4** Pokémon names from the pool of [**1028** known
Pokémon][names], resulting in an entropy of

$$
E = log_2(1028^4) \approx 40.022
$$

bits. A dictionary attack that knows to use the 1028 known Pokémon names as the
pool of values would take $5.584 \times 10^{11}$ guesses on average to correctly
guess a password, or about **17 years, 8 months, and 12 days** at 1000 guesses
per second.

At an average length of about 7.67 characters per Pokémon name, passwords
generated using the default settings have an average length of about **34**
characters (4 Pokémon names, plus one space separating each name for a total of
3 spaces). A brute-force attack that uses a pool of **95** standard US keyboard
characters (alphanumeric, special characters, and space) would be working
against

$$
E = log_2(95^{34}) \approx 222.375
$$

bits of entropy, taking an average of $1.748 \times 10^{67}$ guesses, or
**$5.540 \times 10^{56}$ years**, to correctly guess a password.

## Copyright

All Pokémon names are ™ and © The Pokémon Company, Inc. Everything else in
this project is © Jesse Brooklyn Hannah and released under the terms of the
[MIT License][].

[pkpw]: https://pkpw.jbhannah.net
[xkcd]: https://xkcd.com/936/
[password entropy]: https://www.omnicalculator.com/other/password-entropy
[names]: https://github.com/jbhannah/pkpw/blob/trunk/src/pokemon/pokemon.txt
[mit license]: https://github.com/jbhannah/pkpw/blob/trunk/LICENSE
