├── src
├── index.js
├── fuerte-lib.js
├── helpers.php
├── Fuerte.php
├── Generator.php
├── fuerte.js
└── eff_short_wordlist_1.txt
├── CNAME
├── assets
├── og.jpg
└── wordpress-password-reset.png
├── .gitignore
├── .npmignore
├── webpack.mix.js
├── CHANGELOG.md
├── composer.json
├── package.json
├── LICENSE.md
├── fuerte.php
├── index.html
├── README.md
└── dist
├── fuerte.js
└── fuerte-lib.js
/src/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | makepass.dev
--------------------------------------------------------------------------------
/assets/og.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collegeman/fuerte/HEAD/assets/og.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .idea
4 | /vendor
5 | mix-manifest.json
6 | composer.lock
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .idea
4 | /vendor
5 | mix-manifest.json
6 | *.php
7 | composer.*
--------------------------------------------------------------------------------
/assets/wordpress-password-reset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collegeman/fuerte/HEAD/assets/wordpress-password-reset.png
--------------------------------------------------------------------------------
/src/fuerte-lib.js:
--------------------------------------------------------------------------------
1 | import { Fuerte } from './fuerte'
2 | import fuerte from './fuerte'
3 |
4 | window.Fuerte = Fuerte
5 | window.fuerte = fuerte
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | ",
11 | "license": "MIT",
12 | "devDependencies": {
13 | "laravel-mix": "^6.0.3",
14 | "postcss": "^8.1",
15 | "raw-loader": "^4.0.2"
16 | },
17 | "bugs": {
18 | "url": "https://github.com/collegeman/fuerte/issues"
19 | },
20 | "homepage": "https://github.com/collegeman/fuerte#readme",
21 | "scripts": {
22 | "test": "echo \"Error: no test specified\" && exit 1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Fuerte.php:
--------------------------------------------------------------------------------
1 | {$name}(...$args);
22 | }
23 |
24 | protected static function instance()
25 | {
26 | return self::$instance ? self::$instance : (self::$instance = new Generator);
27 | }
28 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2020 Aaron Collegeman
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/fuerte.php:
--------------------------------------------------------------------------------
1 | memorable(), $length, $special_chars, $extra_special_chars);
25 | if ($generator) {
26 | $password = $generator->make();
27 | }
28 | }
29 | return $password;
30 | }, 10, 4);
31 |
32 | add_filter('password_hint', function () {
33 | return __("Hint: Use a long password composed of words that are meaningful to you: that way it's easy for you to remember but hard for a computer to guess.");
34 | });
35 |
36 | $enqueued = true;
37 | }
38 | }
39 | }
40 |
41 | add_action('login_enqueue_scripts', 'enqueue_fuerte_password_generator');
42 |
43 | add_action('login_head', function() {
44 | if (apply_filters('fuerte_style_passwords', true)) {
45 | ?>
46 |
74 |
81 |
82 |
103 |
2 |
3 |
4 |
5 |
6 | Fuerte: a strong password generator with no depenencies
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
146 |
149 |
150 |
--------------------------------------------------------------------------------
/src/Generator.php:
--------------------------------------------------------------------------------
1 | 8,
32 | self::TYPE_MEMORABLE => 4,
33 | self::TYPE_PIN => 3,
34 | ];
35 |
36 | protected static $maxSize = [
37 | self::TYPE_RANDOM => 100,
38 | self::TYPE_MEMORABLE => 15,
39 | self::TYPE_PIN => 12,
40 | ];
41 |
42 | protected $type = self::TYPE_MEMORABLE;
43 | protected $symbols = false;
44 | protected $capitalize = false;
45 | protected $numbers = false;
46 | protected $separator = self::SEPARATOR_SPACES;
47 | protected $size;
48 |
49 | public function __construct()
50 | {
51 | $this->size = self::$minSize[$this->type];
52 | }
53 |
54 | public function word()
55 | {
56 | if (empty(self::$bank)) {
57 | if (empty(self::$words)) {
58 | self::$words = file_get_contents(__DIR__.'/eff_short_wordlist_1.txt');
59 | }
60 | self::$bank = explode("\n", self::$words);
61 | }
62 | $i = self::arrayRand(self::$bank);
63 | $word = self::$bank[$i];
64 | unset(self::$bank[$i]);
65 | return $word;
66 | }
67 |
68 | public function type(string $type)
69 | {
70 | $this->type = $type;
71 | $this->size(0); // uses default min
72 | return $this;
73 | }
74 |
75 | public function random()
76 | {
77 | return $this->type(self::TYPE_RANDOM);
78 | }
79 |
80 | public function pin()
81 | {
82 | return $this->type(self::TYPE_PIN);
83 | }
84 |
85 | public function memorable()
86 | {
87 | return $this->type(self::TYPE_MEMORABLE);
88 | }
89 |
90 | public function symbols($symbols = null)
91 | {
92 | $this->symbols = is_null($symbols) ? true : (bool) $symbols;
93 | return $this;
94 | }
95 |
96 | public function numbers($numbers = null)
97 | {
98 | $this->numbers = is_null($numbers) ? true : (bool) $numbers;
99 | return $this;
100 | }
101 |
102 | public function separator($separator)
103 | {
104 | $this->separator = $separator;
105 | return $this;
106 | }
107 |
108 | public function capitalize($capitalize = null)
109 | {
110 | $this->capitalize = is_null($capitalize) ? true : (bool) $capitalize;
111 | return $this;
112 | }
113 |
114 | public function size(int $size)
115 | {
116 | $this->size = max(self::$minSize[$this->type], min($size, self::$maxSize[$this->type]));
117 | return $this;
118 | }
119 |
120 | /**
121 | * @param array $elements
122 | * @return mixed
123 | * @throws \Exception
124 | */
125 | public static function arrayRand(array $elements)
126 | {
127 | $keys = array_keys($elements);
128 | $min = 0;
129 | $max = count($keys) - 1;
130 | $rand = random_int($min, $max);
131 | return $keys[$rand];
132 | }
133 |
134 | /**
135 | * @return string
136 | */
137 | public function make()
138 | {
139 | $password = null;
140 |
141 | // Make a pin-type password
142 | if ($this->type === self::TYPE_PIN) {
143 | $password = self::randomChars(self::DIGITS, $this->size);
144 |
145 | // Make a memorable-type password
146 | } else if ($this->type === self::TYPE_MEMORABLE) {
147 | $words = [];
148 | for ($i = 0; $i < $this->size; $i++) {
149 | $words[] = $this->word();
150 | }
151 | if ($this->capitalize) {
152 | $which = self::arrayRand($words);
153 | $words[$which] = strtoupper($words[$which]);
154 | }
155 | $separatorBanks = [];
156 | $separator = $this->separator;
157 | if ($this->separator === self::SEPARATOR_DIGITS) {
158 | $separator = self::DIGITS;
159 | $separatorBanks[] = self::DIGITS;
160 | }
161 | if ($this->separator === self::SEPARATOR_DIGITS_AND_SYMBOLS) {
162 | $separator = self::DIGITS . self::SYMBOLS;
163 | $separatorBanks[] = self::DIGITS;
164 | $separatorBanks[] = self::SYMBOLS;
165 | }
166 | do {
167 | $password = '';
168 | foreach ($words as $i => $word) {
169 | $password .= $word;
170 | if ($i < count($words) - 1) {
171 | $password .= self::randomChars($separator, 1);
172 | }
173 | }
174 | } while (!self::containsChars($password, $separatorBanks));
175 |
176 | // Make a random password
177 | } else {
178 | $banks = [];
179 | $banks[] = self::UPPER;
180 | $banks[] = self::LOWER;
181 | if ($this->symbols) {
182 | $banks[] = self::SYMBOLS;
183 | }
184 | if ($this->numbers) {
185 | $banks[] = self::DIGITS;
186 | }
187 | do {
188 | $password = self::randomChars(implode('', $banks), $this->size);
189 | } while (!self::containsChars($password, $banks));
190 | }
191 |
192 | return $password;
193 | }
194 |
195 | public function __toString()
196 | {
197 | return $this->make();
198 | }
199 |
200 | private static function containsChars(string $string, array $banks)
201 | {
202 | if (count($banks) < 1) {
203 | return true;
204 | }
205 |
206 | foreach($banks as $bank) {
207 | $chars = str_split($bank);
208 | $contains = false;
209 | foreach($chars as $char) {
210 | if (stripos($string, $char) !== false) {
211 | $contains = true;
212 | break;
213 | }
214 | }
215 | if (!$contains) {
216 | return false;
217 | }
218 | }
219 |
220 | return true;
221 | }
222 |
223 | private static function randomChars(string $from, int $length)
224 | {
225 | if (strlen($from) < 1) {
226 | return null;
227 | }
228 | if (!$length) {
229 | $length = 1;
230 | }
231 | $bank = [];
232 | $random = [];
233 | while (count($random) < $length) {
234 | if (count($bank) < 1) {
235 | $bank = str_split($from);
236 | }
237 | $i = self::arrayRand($bank);
238 | array_push($random, $bank[$i]);
239 | unset($bank[$i]);
240 | }
241 | return implode('', $random);
242 | }
243 |
244 | }
--------------------------------------------------------------------------------
/src/fuerte.js:
--------------------------------------------------------------------------------
1 | import words from './eff_short_wordlist_1.txt'
2 |
3 | let bank = []
4 |
5 | const Fuerte = function() {
6 | this._listeners = []
7 | this._symbols = false
8 | this._capitalize = false
9 | this._numbers = false
10 | this._separator = Fuerte.SEPARATOR_SPACES
11 | this.type(Fuerte.TYPE_MEMORABLE)
12 | }
13 |
14 | Fuerte.DIGITS = '0123456789'
15 | Fuerte.SYMBOLS = '!@#_-.*%'
16 | Fuerte.LOWER = 'abcdefghijklmnopqrstuvwxyz'
17 | Fuerte.UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
18 |
19 | Fuerte.TYPE_RANDOM = 'random'
20 | Fuerte.TYPE_MEMORABLE = 'memorable'
21 | Fuerte.TYPE_PIN = 'pin'
22 |
23 | Fuerte.SEPARATOR_SPACES = ' '
24 | Fuerte.SEPARATOR_HYPHENS = '-'
25 | Fuerte.SEPARATOR_PERIODS = '.'
26 | Fuerte.SEPARATOR_COMMAS = ','
27 | Fuerte.SEPARATOR_UNDERSCORES = '_'
28 | Fuerte.SEPARATOR_DIGITS = '0'
29 | Fuerte.SEPARATOR_DIGITS_AND_SYMBOLS = '0_'
30 |
31 | const minSize = []
32 | minSize[Fuerte.TYPE_RANDOM] = 8
33 | minSize[Fuerte.TYPE_MEMORABLE] = 4
34 | minSize[Fuerte.TYPE_PIN] = 3
35 |
36 | const maxSize = []
37 | maxSize[Fuerte.TYPE_RANDOM] = 100
38 | maxSize[Fuerte.TYPE_MEMORABLE] = 15
39 | maxSize[Fuerte.TYPE_PIN] = 12
40 |
41 | const rand = (elements) => {
42 | var random = new Uint32Array(1);
43 | window.crypto.getRandomValues(random)
44 | return parseFloat('0.' + random[0].toString())
45 | }
46 |
47 | const containsChars = (string, banks) => {
48 | if (banks.length < 1) {
49 | return true
50 | }
51 |
52 | for(let bank of banks) {
53 | let chars = bank.split('')
54 | let contains = false
55 | for(let char of chars) {
56 | if (string.indexOf(char) > -1) {
57 | contains = true
58 | break
59 | }
60 | }
61 | if (!contains) {
62 | return false
63 | }
64 | }
65 |
66 | return true
67 | }
68 |
69 | const first = (elements) => {
70 | return elements.length ? elements[0] : null
71 | }
72 |
73 | const randomChars = (from, length) => {
74 | if (from.length < 1) {
75 | return null
76 | }
77 | if (!length) {
78 | length = 1
79 | }
80 | let bank = []
81 | let random = []
82 | while (random.length < length) {
83 | if (bank.length < 1) {
84 | bank = from.split('')
85 | }
86 | let i = Math.round(rand() * (bank.length-1))
87 | random.push(bank[i])
88 | bank.splice(i, 1)
89 | }
90 | return random.join('')
91 | }
92 |
93 | const shuffle = (string) => {
94 | var a = string.split(""),
95 | n = a.length;
96 |
97 | for(var i = n - 1; i > 0; i--) {
98 | var j = Math.floor(rand() * (i + 1));
99 | var tmp = a[i];
100 | a[i] = a[j];
101 | a[j] = tmp;
102 | }
103 | return string.join("");
104 | }
105 |
106 | Fuerte.prototype.addEventListener = function(listener) {
107 | this._listeners.push(listener)
108 | return this
109 | }
110 |
111 | Fuerte.prototype.removeEventListener = function(listener) {
112 | for (let i in this._listeners) {
113 | if (this._listeners[i] === listener) {
114 | this._listeners.splice(i, 1)
115 | break;
116 | }
117 | }
118 | return this
119 | }
120 |
121 | Fuerte.prototype.fire = function(event, data) {
122 | for (let listener of this._listeners) {
123 | listener(event, data)
124 | }
125 | return this
126 | }
127 |
128 | Fuerte.prototype.fireChangeEvent = function() {
129 | return this.fire('change', {
130 | type: this._type,
131 | size: this._size,
132 | capitalize: this._capitalize,
133 | numbers: this._numbers,
134 | symbols: this._symbols,
135 | size_min: minSize[this._type],
136 | size_max: maxSize[this._type],
137 | })
138 | }
139 |
140 | Fuerte.prototype.type = function(type) {
141 | this._type = type
142 | this.fireChangeEvent()
143 | return this.size(0) // uses default min
144 | }
145 |
146 | Fuerte.prototype.random = function() {
147 | return this.type(Fuerte.TYPE_RANDOM)
148 | }
149 |
150 | Fuerte.prototype.memorable = function() {
151 | return this.type(Fuerte.TYPE_MEMORABLE)
152 | }
153 |
154 | Fuerte.prototype.pin = function() {
155 | return this.type(Fuerte.TYPE_PIN)
156 | }
157 |
158 | Fuerte.prototype.symbols = function(bool) {
159 | this._symbols = bool === undefined ? true : !!bool
160 | this.fireChangeEvent()
161 | return this
162 | }
163 |
164 | Fuerte.prototype.numbers = function(bool) {
165 | this._numbers = bool === undefined ? true : !!bool
166 | this.fireChangeEvent()
167 | return this
168 | }
169 |
170 | Fuerte.prototype.separator = function(separator) {
171 | this._separator = separator
172 | this.fireChangeEvent()
173 | return this
174 | }
175 |
176 | Fuerte.prototype.capitalize = function(bool) {
177 | this._capitalize = bool === undefined ? true : !!bool
178 | this.fireChangeEvent()
179 | return this
180 | }
181 |
182 | Fuerte.prototype.size = function(number) {
183 | let n = parseInt(number)
184 | this._size = Math.max(minSize[this._type], Math.min(n, maxSize[this._type]))
185 | this.fireChangeEvent()
186 | return this
187 | }
188 |
189 | Fuerte.prototype.word = function() {
190 | if (bank.length < 1) {
191 | bank = words.split("\n")
192 | }
193 | let i = Math.round(rand() * (bank.length-1))
194 | let word = bank[i]
195 | bank.splice(i, 1)
196 | return word
197 | }
198 |
199 | Fuerte.prototype.toString = function() {
200 | return this.make()
201 | }
202 |
203 | Fuerte.prototype.make = function() {
204 | let password = null
205 |
206 | // Make a pin-type password
207 | if (this._type === Fuerte.TYPE_PIN) {
208 | password = randomChars(Fuerte.DIGITS, this._size)
209 |
210 | // Make a memorable-type password
211 | } else if (this._type === Fuerte.TYPE_MEMORABLE) {
212 | let words = []
213 | for (let i = 0; i < this._size; i++) {
214 | words.push(this.word())
215 | }
216 | if (this._capitalize) {
217 | let which = Math.round(rand() * (words.length-1))
218 | words[which] = words[which].toUpperCase()
219 | }
220 | let separatorBanks = []
221 | let separator = this._separator
222 | if (this._separator === Fuerte.SEPARATOR_DIGITS) {
223 | separator = Fuerte.DIGITS
224 | separatorBanks.push(Fuerte.DIGITS)
225 | } else if (this._separator === Fuerte.SEPARATOR_DIGITS_AND_SYMBOLS) {
226 | separator = Fuerte.DIGITS + Fuerte.SYMBOLS
227 | separatorBanks.push(Fuerte.DIGITS)
228 | separatorBanks.push(Fuerte.SYMBOLS)
229 | }
230 | do {
231 | password = ''
232 | for (let i in words) {
233 | password += words[i]
234 | if (i < words.length - 1) {
235 | password += randomChars(separator, 1)
236 | }
237 | }
238 | } while (!containsChars(password, separatorBanks))
239 |
240 | // Make a random password
241 | } else {
242 | let banks = []
243 | banks.push(Fuerte.UPPER)
244 | banks.push(Fuerte.LOWER)
245 | if (this._symbols) {
246 | banks.push(Fuerte.SYMBOLS)
247 | }
248 | if (this._numbers) {
249 | banks.push(Fuerte.DIGITS)
250 | }
251 | do {
252 | password = randomChars(banks.join(''), this._size)
253 | } while (!containsChars(password, banks))
254 | }
255 |
256 | this.fire('make', { password })
257 | return password
258 | }
259 |
260 | Fuerte.prototype.form = function(el) {
261 | let instance = this
262 |
263 | let password = first(el.querySelectorAll(':scope [data-fuerte="password"]'))
264 |
265 | let btnGenerate = first(el.querySelectorAll(':scope [data-fuerte="generate"]'))
266 | if (btnGenerate) {
267 | btnGenerate.addEventListener('click', (e) => {
268 | instance.make()
269 | e.preventDefault()
270 | })
271 | }
272 |
273 | let selectType = first(el.querySelectorAll(':scope [data-fuerte="type"]'))
274 | if (selectType) {
275 | selectType.addEventListener('change', (e) => {
276 | instance.type(selectType.value)
277 | instance.make()
278 | })
279 | }
280 |
281 | let selectSeparator = first(el.querySelectorAll(':scope [data-fuerte="separator"]'))
282 | if (selectSeparator) {
283 | selectSeparator.addEventListener('change', (e) => {
284 | instance.separator(selectSeparator.value)
285 | instance.make()
286 | })
287 | }
288 |
289 | let checkCapitalize = first(el.querySelectorAll(':scope [data-fuerte="capitalize"]'))
290 | if (checkCapitalize) {
291 | checkCapitalize.addEventListener('change', (e) => {
292 | instance.capitalize(checkCapitalize.checked)
293 | instance.make()
294 | })
295 | }
296 |
297 | let checkNumbers = first(el.querySelectorAll(':scope [data-fuerte="numbers"]'))
298 | if (checkNumbers) {
299 | checkNumbers.addEventListener('change', (e) => {
300 | instance.numbers(checkNumbers.checked)
301 | instance.make()
302 | })
303 | }
304 |
305 | let checkSymbols = first(el.querySelectorAll(':scope [data-fuerte="symbols"]'))
306 | if (checkSymbols) {
307 | checkSymbols.addEventListener('change', (e) => {
308 | instance.symbols(checkSymbols.checked)
309 | instance.make()
310 | })
311 | }
312 |
313 | let rangeSize = first(el.querySelectorAll(':scope [data-fuerte="size"]'))
314 | let rangeSizeLabel = null
315 | if (rangeSize) {
316 | rangeSize.addEventListener('change', (e) => {
317 | instance.size(rangeSize.value)
318 | instance.make()
319 | })
320 | if (rangeSize.id) {
321 | rangeSizeLabel = first(document.querySelectorAll('label[for="' + rangeSize.id + '"]'))
322 | }
323 | }
324 |
325 | instance.addEventListener((event, data) => {
326 | if (event === 'make') {
327 | if (password) {
328 | password.value = data.password
329 | }
330 | }
331 |
332 | if (event === 'change') {
333 | if (checkCapitalize) {
334 | checkCapitalize.value = data.capitalize
335 | checkCapitalize.disabled = data.type !== Fuerte.TYPE_MEMORABLE
336 | }
337 | if (checkNumbers) {
338 | checkNumbers.value = data.numbers
339 | checkNumbers.disabled = data.type !== Fuerte.TYPE_RANDOM
340 | }
341 | if (checkSymbols) {
342 | checkSymbols.value = data.symbols
343 | checkSymbols.disabled = data.type !== Fuerte.TYPE_RANDOM
344 | }
345 | if (selectSeparator) {
346 | selectSeparator.disabled = data.type !== Fuerte.TYPE_MEMORABLE
347 | }
348 | if (rangeSize) {
349 | rangeSize.min = data.size_min
350 | rangeSize.max = data.size_max
351 | rangeSize.value = data.size
352 | rangeSize.step = 1
353 | if (rangeSizeLabel) {
354 | let label = data.size
355 | if (data.type === Fuerte.TYPE_MEMORABLE) {
356 | label += ' words'
357 | } else if (data.type === Fuerte.TYPE_PIN) {
358 | label += ' digits'
359 | } else {
360 | label += ' characters'
361 | }
362 | rangeSizeLabel.innerHTML = label
363 | }
364 | }
365 | }
366 | })
367 |
368 | instance.fireChangeEvent()
369 |
370 | instance.make()
371 | }
372 |
373 | export { Fuerte }
374 |
375 | export default function(el) {
376 | let instance = new Fuerte()
377 | if (el) {
378 | instance.form(el)
379 | }
380 | return instance
381 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fuerte is a strong password generator with no dependencies
2 |
3 | "Through 20 years of effort, we've successfully trained everyone
4 | to use passwords that are hard for humans to remember, but easy
5 | for computers to guess." — [XKCD](https://xkcd.com/936/)
6 |
7 | 
8 |
9 | Fuerte is a simple, strong password generator.
10 |
11 | It can create strong
12 | passwords that you should be using, because they're long and
13 | easy to remember, like `correct horse battery staple`,
14 | but it can also create the sorts of strong passwords that your bank
15 | and your insurance company want you to use, because they're
16 | complicated, like `Tr0ub4dor&3`.
17 |
18 | In a pinch, it can even blend the two methods to create something
19 | like `correct!HORSE3battery%staple`, which, while difficult to
20 | memorize, is at least equally difficult for a computer to guess.
21 |
22 | The library is available for JavaScript and PHP projects, and if
23 | you're using PHP, it installs easily into Laravel and WordPress
24 | codebases.
25 |
26 | As of version `1.0.7`, both libraries use cryptographically secure
27 | random number generators.
28 |
29 | ## Need a new password right now?
30 |
31 | You can experience Fuerte in action on [makepass.dev](https://makepass.dev). Enjoy!
32 |
33 | ## Quickstart Guide
34 |
35 | There are two versions of Fuerte: one for JavaScript and one for PHP.
36 | Both are a part of this codebase.
37 |
38 | You can also use the PHP library as a WordPress plugin or as a Laravel
39 | package.
40 |
41 | You only need one of these solutions for your project, but they
42 | can also be used together.
43 |
44 | ### Using the JavaScript library
45 |
46 | The Fuerte package is available via NPM:
47 |
48 | ```bash
49 | npm install @collegeman/fuerte
50 | ```
51 |
52 | You can also just download this repository, place the uncompressed files
53 | among your project's assets, and then load the the library:
54 |
55 | ```html
56 |
57 |
60 | ```
61 |
62 | You can also load the library via jsdeliver:
63 |
64 | ```html
65 |
66 |
69 | ```
70 |
71 | Once added to your project, you can import Fuerte's API like this:
72 |
73 | ```js
74 | import fuerte from '@collegeman/fuerte'
75 | let password = fuerte().make() // a random, memorable password!
76 | ````
77 |
78 | ### Using the PHP library
79 |
80 | The Fuerte package can be installed using Composer:
81 |
82 | ```bash
83 | composer require collegeman/fuerte
84 | ```
85 |
86 | Assuming there are no other functions in your codebase named `fuerte`, you can
87 | use Fuerte like this:
88 |
89 | ```php
90 | make(); // a random, memorable password!
93 | ```
94 |
95 | If a conflict exists preventing the helper function from being created, you
96 | can access Fuerte via its facade:
97 |
98 | ```php
99 | memorable()->separator('.')->capitalize()->size(6)->make();
142 | // e.g., "snout.exit.SCUBA.watch.silly.hash"
143 | ```
144 |
145 | Because the API is consistent across platforms, all remaining examples use the JavaScript version.
146 |
147 | ### Password type
148 |
149 | Fuerte can generate three types of strong passwords: **memorable** (like `correct horse battery staple`),
150 | **random** (like `Tr0ub4dor&3`), and **PIN** (like `1234`).
151 |
152 | You can tell Fuerte what type of password you want just by
153 | invoking the type name:
154 |
155 | ```js
156 | fuerte().memorable().make() // e.g., "film rhyme stunt coat"
157 | fuerte().random().make() // e.g., "TlCxbiKd"
158 | fuerte().pin().make() // e.g., "476"
159 | ```
160 |
161 | ### Length/size
162 |
163 | Password length matters: password length not complexity is what makes a password
164 | hard for a computer to crack (see XKCD comic above).
165 |
166 | Each type of password has a minimum length. When you tell Fuerte what type of
167 | password you want to generate, it will assume you want to use the minimum safe
168 | length for that password. You can then tell Fuerte how long you want the password
169 | to be using the `size()` method:
170 |
171 | ```js
172 | fuerte().memorable().size(5).make() // use 5 words: "polo blush dug cola lance"
173 | fuerte().random().size(32).make() // use 32 characters: "mSyDqEQwZkgdUINljHiJnLsaYcWbrOuK"
174 | fuerte().pin().size(6).make() // use 6 digits: 386419
175 | ```
176 |
177 | ### Separators
178 |
179 | By default, the **memorable** password type uses spaces to separate the words
180 | in the password. You can tell it what you want the separator to be using
181 | the `separator(string)` method:
182 |
183 | ```js
184 | fuerte().memorable().separator('-').make() // "stark-jog-copy-lilac"
185 | fuerte().memorable().separator('.').make() // "avoid.sleet.gas.view"
186 | ```
187 |
188 | See *Symbols and Digits* below for more options.
189 |
190 | ### Symbols and Digits
191 |
192 | Sometimes a system will require passwords to contain symbols or digits.
193 |
194 | The **random** password type has two additional flags you can set: `symbols()`
195 | and `numbers()`. These flags will ensure that the passwords generated
196 | contain at least 1 of each special character:
197 |
198 | ```js
199 | fuerte().random().numbers().make() // e.g., "1A2oPvxR"
200 | fuerte().random().symbols().make() // e.g., "kQm#_xiA"
201 | fuerte().random().numbers().symbols().make() // e.g., "ZCJhGO5%"
202 | ```
203 |
204 | The **memorable** password type supports Symbols and Digits using the
205 | `separator()` method. If you need a memorable password that contains
206 | Digits, do this:
207 |
208 | ```js
209 | fuerte().memorable().separator('0').make() // e.g., "chump9vixen6good9bud"
210 | ```
211 |
212 | If you need a memorable password that contains Symbols *and* Digits, do this:
213 |
214 | ```js
215 | fuerte().memorable().separator('0_').make() // e.g., "cramp2dill#grant1coach"
216 | ```
217 |
218 | ### Capitalization
219 |
220 | If you need a memorable password that contains capital letters, use the
221 | `capitalize()` method:
222 |
223 | ```js
224 | fuerte().memorable().capitalize().make() // e.g., "slept taco START prior"
225 | ```
226 |
227 | You can use `capitalize()` in combination with the `separator(string)` method
228 | to create an almost-memorable password that also satisfies the goofy requirements
229 | for using complex characters:
230 |
231 | ```js
232 | let password = fuerte().memorable().capitalize().separator('0_').make()
233 | // e.g., "tusk9query*cross9DRAB"
234 | ```
235 |
236 | ## WordPress plugin configuration
237 |
238 | By default, the WordPress plugin will have WordPress use Fuerte when it needs
239 | to generate and display a suggestion for a new password, like on the password
240 | reset screen:
241 |
242 | 
243 |
244 | Just click the reload button (either in the UI on the page or on the browser)
245 | to get another suggestion.
246 |
247 | If you don't want Fuerte to add these features to WordPress, just add the
248 | following code to your theme's `functions.php` or to a must-use plugin file:
249 |
250 | ```php
251 | random()->size(12)->symbols()->numbers();
262 | // ...but why would you want to make the passwords suck again?
263 | });
264 | ```
265 |
266 | ## Support
267 |
268 | If you find a problem with Fuerte, please post an [issue](https://github.com/collegeman/fuerte/issues) on GitHub.
269 |
270 | ## Credits
271 |
272 | The inspiration for a cross-platform password generator with a consistent API
273 | comes from the XKCD comic above as well as requirements from several of
274 | the projects I'm working on at the moment. The image has been used without
275 | explicit permission.
276 |
277 | The features of the generator were inspired and constrained by the
278 | minimal design of the password generator built into [1Password](https://1password.com/).
279 | If you don't use a password manager, I highly recommend you give
280 | 1Password a try.
281 |
282 | The word list used by Fuerte for generating memorable passwords belongs
283 | to the [EFF](https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases).
284 | It was used without permission.
285 |
286 | The photo of padlocks is by DynamicWang on Unsplash.
287 |
288 | ## License
289 |
290 | Copyright 2020 Aaron Collegeman
291 |
292 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
293 |
294 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
295 |
296 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/dist/fuerte.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";function n(n,t){var a;if("undefined"==typeof Symbol||null==n[Symbol.iterator]){if(Array.isArray(n)||(a=function(n,t){if(!n)return;if("string"==typeof n)return e(n,t);var a=Object.prototype.toString.call(n).slice(8,-1);"Object"===a&&n.constructor&&(a=n.constructor.name);if("Map"===a||"Set"===a)return Array.from(n);if("Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a))return e(n,t)}(n))||t&&n&&"number"==typeof n.length){a&&(n=a);var r=0,s=function(){};return{s,n:function(){return r>=n.length?{done:!0}:{done:!1,value:n[r++]}},e:function(n){throw n},f:s}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i=!0,l=!1;return{s:function(){a=n[Symbol.iterator]()},n:function(){var n=a.next();return i=n.done,n},e:function(n){l=!0,o=n},f:function(){try{i||null==a.return||a.return()}finally{if(l)throw o}}}}function e(n,e){(null==e||e>n.length)&&(e=n.length);for(var t=0,a=new Array(e);t-1){i=!0;break}}}catch(n){l.e(n)}finally{l.f()}if(!i)return!1}}catch(n){r.e(n)}finally{r.f()}return!0},l=function(n){return n.length?n[0]:null},u=function(n,e){if(n.length<1)return null;e||(e=1);for(var t=[],a=[];a.length{"use strict";var n,e={315:(n,e,t)=>{t.d(e,{v:()=>s,Z:()=>d});function a(n,e){var t;if("undefined"==typeof Symbol||null==n[Symbol.iterator]){if(Array.isArray(n)||(t=function(n,e){if(!n)return;if("string"==typeof n)return r(n,e);var t=Object.prototype.toString.call(n).slice(8,-1);"Object"===t&&n.constructor&&(t=n.constructor.name);if("Map"===t||"Set"===t)return Array.from(n);if("Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t))return r(n,e)}(n))||e&&n&&"number"==typeof n.length){t&&(n=t);var a=0,o=function(){};return{s:o,n:function(){return a>=n.length?{done:!0}:{done:!1,value:n[a++]}},e:function(n){throw n},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,i=!0,l=!1;return{s:function(){t=n[Symbol.iterator]()},n:function(){var n=t.next();return i=n.done,n},e:function(n){l=!0,s=n},f:function(){try{i||null==t.return||t.return()}finally{if(l)throw s}}}}function r(n,e){(null==e||e>n.length)&&(e=n.length);for(var t=0,a=new Array(e);t-1){i=!0;break}}}catch(n){l.e(n)}finally{l.f()}if(!i)return!1}}catch(n){r.e(n)}finally{r.f()}return!0},p=function(n){return n.length?n[0]:null},h=function(n,e){if(n.length<1)return null;e||(e=1);for(var t=[],a=[];a.length{for(var t in e)a.o(e,t)&&!a.o(n,t)&&Object.defineProperty(n,t,{enumerable:!0,get:e[t]})},a.o=(n,e)=>Object.prototype.hasOwnProperty.call(n,e),n=a(315),window.Fuerte=n.v,window.fuerte=n.Z})();
--------------------------------------------------------------------------------
/src/eff_short_wordlist_1.txt:
--------------------------------------------------------------------------------
1 | acid
2 | acorn
3 | acre
4 | acts
5 | afar
6 | affix
7 | aged
8 | agent
9 | agile
10 | aging
11 | agony
12 | ahead
13 | aide
14 | aids
15 | aim
16 | ajar
17 | alarm
18 | alias
19 | alibi
20 | alien
21 | alike
22 | alive
23 | aloe
24 | aloft
25 | aloha
26 | alone
27 | amend
28 | amino
29 | ample
30 | amuse
31 | angel
32 | anger
33 | angle
34 | ankle
35 | apple
36 | april
37 | apron
38 | aqua
39 | area
40 | arena
41 | argue
42 | arise
43 | armed
44 | armor
45 | army
46 | aroma
47 | array
48 | arson
49 | art
50 | ashen
51 | ashes
52 | atlas
53 | atom
54 | attic
55 | audio
56 | avert
57 | avoid
58 | awake
59 | award
60 | awoke
61 | axis
62 | bacon
63 | badge
64 | bagel
65 | baggy
66 | baked
67 | baker
68 | balmy
69 | banjo
70 | barge
71 | barn
72 | bash
73 | basil
74 | bask
75 | batch
76 | bath
77 | baton
78 | bats
79 | blade
80 | blank
81 | blast
82 | blaze
83 | bleak
84 | blend
85 | bless
86 | blimp
87 | blink
88 | bloat
89 | blob
90 | blog
91 | blot
92 | blunt
93 | blurt
94 | blush
95 | boast
96 | boat
97 | body
98 | boil
99 | bok
100 | bolt
101 | boned
102 | boney
103 | bonus
104 | bony
105 | book
106 | booth
107 | boots
108 | boss
109 | botch
110 | both
111 | boxer
112 | breed
113 | bribe
114 | brick
115 | bride
116 | brim
117 | bring
118 | brink
119 | brisk
120 | broad
121 | broil
122 | broke
123 | brook
124 | broom
125 | brush
126 | buck
127 | bud
128 | buggy
129 | bulge
130 | bulk
131 | bully
132 | bunch
133 | bunny
134 | bunt
135 | bush
136 | bust
137 | busy
138 | buzz
139 | cable
140 | cache
141 | cadet
142 | cage
143 | cake
144 | calm
145 | cameo
146 | canal
147 | candy
148 | cane
149 | canon
150 | cape
151 | card
152 | cargo
153 | carol
154 | carry
155 | carve
156 | case
157 | cash
158 | cause
159 | cedar
160 | chain
161 | chair
162 | chant
163 | chaos
164 | charm
165 | chase
166 | cheek
167 | cheer
168 | chef
169 | chess
170 | chest
171 | chew
172 | chief
173 | chili
174 | chill
175 | chip
176 | chomp
177 | chop
178 | chow
179 | chuck
180 | chump
181 | chunk
182 | churn
183 | chute
184 | cider
185 | cinch
186 | city
187 | civic
188 | civil
189 | clad
190 | claim
191 | clamp
192 | clap
193 | clash
194 | clasp
195 | class
196 | claw
197 | clay
198 | clean
199 | clear
200 | cleat
201 | cleft
202 | clerk
203 | click
204 | cling
205 | clink
206 | clip
207 | cloak
208 | clock
209 | clone
210 | cloth
211 | cloud
212 | clump
213 | coach
214 | coast
215 | coat
216 | cod
217 | coil
218 | coke
219 | cola
220 | cold
221 | colt
222 | coma
223 | come
224 | comic
225 | comma
226 | cone
227 | cope
228 | copy
229 | coral
230 | cork
231 | cost
232 | cot
233 | couch
234 | cough
235 | cover
236 | cozy
237 | craft
238 | cramp
239 | crane
240 | crank
241 | crate
242 | crave
243 | crawl
244 | crazy
245 | creme
246 | crepe
247 | crept
248 | crib
249 | cried
250 | crisp
251 | crook
252 | crop
253 | cross
254 | crowd
255 | crown
256 | crumb
257 | crush
258 | crust
259 | cub
260 | cult
261 | cupid
262 | cure
263 | curl
264 | curry
265 | curse
266 | curve
267 | curvy
268 | cushy
269 | cut
270 | cycle
271 | dab
272 | dad
273 | daily
274 | dairy
275 | daisy
276 | dance
277 | dandy
278 | darn
279 | dart
280 | dash
281 | data
282 | date
283 | dawn
284 | deaf
285 | deal
286 | dean
287 | debit
288 | debt
289 | debug
290 | decaf
291 | decal
292 | decay
293 | deck
294 | decor
295 | decoy
296 | deed
297 | delay
298 | denim
299 | dense
300 | dent
301 | depth
302 | derby
303 | desk
304 | dial
305 | diary
306 | dice
307 | dig
308 | dill
309 | dime
310 | dimly
311 | diner
312 | dingy
313 | disco
314 | dish
315 | disk
316 | ditch
317 | ditzy
318 | dizzy
319 | dock
320 | dodge
321 | doing
322 | doll
323 | dome
324 | donor
325 | donut
326 | dose
327 | dot
328 | dove
329 | down
330 | dowry
331 | doze
332 | drab
333 | drama
334 | drank
335 | draw
336 | dress
337 | dried
338 | drift
339 | drill
340 | drive
341 | drone
342 | droop
343 | drove
344 | drown
345 | drum
346 | dry
347 | duck
348 | duct
349 | dude
350 | dug
351 | duke
352 | duo
353 | dusk
354 | dust
355 | duty
356 | dwarf
357 | dwell
358 | eagle
359 | early
360 | earth
361 | easel
362 | east
363 | eaten
364 | eats
365 | ebay
366 | ebony
367 | ebook
368 | echo
369 | edge
370 | eel
371 | eject
372 | elbow
373 | elder
374 | elf
375 | elk
376 | elm
377 | elope
378 | elude
379 | elves
380 | email
381 | emit
382 | empty
383 | emu
384 | enter
385 | entry
386 | envoy
387 | equal
388 | erase
389 | error
390 | erupt
391 | essay
392 | etch
393 | evade
394 | even
395 | evict
396 | evil
397 | evoke
398 | exact
399 | exit
400 | fable
401 | faced
402 | fact
403 | fade
404 | fall
405 | false
406 | fancy
407 | fang
408 | fax
409 | feast
410 | feed
411 | femur
412 | fence
413 | fend
414 | ferry
415 | fetal
416 | fetch
417 | fever
418 | fiber
419 | fifth
420 | fifty
421 | film
422 | filth
423 | final
424 | finch
425 | fit
426 | five
427 | flag
428 | flaky
429 | flame
430 | flap
431 | flask
432 | fled
433 | flick
434 | fling
435 | flint
436 | flip
437 | flirt
438 | float
439 | flock
440 | flop
441 | floss
442 | flyer
443 | foam
444 | foe
445 | fog
446 | foil
447 | folic
448 | folk
449 | food
450 | fool
451 | found
452 | fox
453 | foyer
454 | frail
455 | frame
456 | fray
457 | fresh
458 | fried
459 | frill
460 | frisk
461 | from
462 | front
463 | frost
464 | froth
465 | frown
466 | froze
467 | fruit
468 | gag
469 | gains
470 | gala
471 | game
472 | gap
473 | gas
474 | gave
475 | gear
476 | gecko
477 | geek
478 | gem
479 | genre
480 | gift
481 | gig
482 | gills
483 | given
484 | giver
485 | glad
486 | glass
487 | glide
488 | gloss
489 | glove
490 | glow
491 | glue
492 | goal
493 | going
494 | golf
495 | gong
496 | good
497 | gooey
498 | goofy
499 | gore
500 | gown
501 | grab
502 | grain
503 | grant
504 | grape
505 | graph
506 | grasp
507 | grass
508 | grave
509 | gravy
510 | gray
511 | green
512 | greet
513 | grew
514 | grid
515 | grief
516 | grill
517 | grip
518 | grit
519 | groom
520 | grope
521 | growl
522 | grub
523 | grunt
524 | guide
525 | gulf
526 | gulp
527 | gummy
528 | guru
529 | gush
530 | gut
531 | guy
532 | habit
533 | half
534 | halo
535 | halt
536 | happy
537 | harm
538 | hash
539 | hasty
540 | hatch
541 | hate
542 | haven
543 | hazel
544 | hazy
545 | heap
546 | heat
547 | heave
548 | hedge
549 | hefty
550 | help
551 | herbs
552 | hers
553 | hub
554 | hug
555 | hula
556 | hull
557 | human
558 | humid
559 | hump
560 | hung
561 | hunk
562 | hunt
563 | hurry
564 | hurt
565 | hush
566 | hut
567 | ice
568 | icing
569 | icon
570 | icy
571 | igloo
572 | image
573 | ion
574 | iron
575 | islam
576 | issue
577 | item
578 | ivory
579 | ivy
580 | jab
581 | jam
582 | jaws
583 | jazz
584 | jeep
585 | jelly
586 | jet
587 | jiffy
588 | job
589 | jog
590 | jolly
591 | jolt
592 | jot
593 | joy
594 | judge
595 | juice
596 | juicy
597 | july
598 | jumbo
599 | jump
600 | junky
601 | juror
602 | jury
603 | keep
604 | keg
605 | kept
606 | kick
607 | kilt
608 | king
609 | kite
610 | kitty
611 | kiwi
612 | knee
613 | knelt
614 | koala
615 | kung
616 | ladle
617 | lady
618 | lair
619 | lake
620 | lance
621 | land
622 | lapel
623 | large
624 | lash
625 | lasso
626 | last
627 | latch
628 | late
629 | lazy
630 | left
631 | legal
632 | lemon
633 | lend
634 | lens
635 | lent
636 | level
637 | lever
638 | lid
639 | life
640 | lift
641 | lilac
642 | lily
643 | limb
644 | limes
645 | line
646 | lint
647 | lion
648 | lip
649 | list
650 | lived
651 | liver
652 | lunar
653 | lunch
654 | lung
655 | lurch
656 | lure
657 | lurk
658 | lying
659 | lyric
660 | mace
661 | maker
662 | malt
663 | mama
664 | mango
665 | manor
666 | many
667 | map
668 | march
669 | mardi
670 | marry
671 | mash
672 | match
673 | mate
674 | math
675 | moan
676 | mocha
677 | moist
678 | mold
679 | mom
680 | moody
681 | mop
682 | morse
683 | most
684 | motor
685 | motto
686 | mount
687 | mouse
688 | mousy
689 | mouth
690 | move
691 | movie
692 | mower
693 | mud
694 | mug
695 | mulch
696 | mule
697 | mull
698 | mumbo
699 | mummy
700 | mural
701 | muse
702 | music
703 | musky
704 | mute
705 | nacho
706 | nag
707 | nail
708 | name
709 | nanny
710 | nap
711 | navy
712 | near
713 | neat
714 | neon
715 | nerd
716 | nest
717 | net
718 | next
719 | niece
720 | ninth
721 | nutty
722 | oak
723 | oasis
724 | oat
725 | ocean
726 | oil
727 | old
728 | olive
729 | omen
730 | onion
731 | only
732 | ooze
733 | opal
734 | open
735 | opera
736 | opt
737 | otter
738 | ouch
739 | ounce
740 | outer
741 | oval
742 | oven
743 | owl
744 | ozone
745 | pace
746 | pagan
747 | pager
748 | palm
749 | panda
750 | panic
751 | pants
752 | panty
753 | paper
754 | park
755 | party
756 | pasta
757 | patch
758 | path
759 | patio
760 | payer
761 | pecan
762 | penny
763 | pep
764 | perch
765 | perky
766 | perm
767 | pest
768 | petal
769 | petri
770 | petty
771 | photo
772 | plank
773 | plant
774 | plaza
775 | plead
776 | plot
777 | plow
778 | pluck
779 | plug
780 | plus
781 | poach
782 | pod
783 | poem
784 | poet
785 | pogo
786 | point
787 | poise
788 | poker
789 | polar
790 | polio
791 | polka
792 | polo
793 | pond
794 | pony
795 | poppy
796 | pork
797 | poser
798 | pouch
799 | pound
800 | pout
801 | power
802 | prank
803 | press
804 | print
805 | prior
806 | prism
807 | prize
808 | probe
809 | prong
810 | proof
811 | props
812 | prude
813 | prune
814 | pry
815 | pug
816 | pull
817 | pulp
818 | pulse
819 | puma
820 | punch
821 | punk
822 | pupil
823 | puppy
824 | purr
825 | purse
826 | push
827 | putt
828 | quack
829 | quake
830 | query
831 | quiet
832 | quill
833 | quilt
834 | quit
835 | quota
836 | quote
837 | rabid
838 | race
839 | rack
840 | radar
841 | radio
842 | raft
843 | rage
844 | raid
845 | rail
846 | rake
847 | rally
848 | ramp
849 | ranch
850 | range
851 | rank
852 | rant
853 | rash
854 | raven
855 | reach
856 | react
857 | ream
858 | rebel
859 | recap
860 | relax
861 | relay
862 | relic
863 | remix
864 | repay
865 | repel
866 | reply
867 | rerun
868 | reset
869 | rhyme
870 | rice
871 | rich
872 | ride
873 | rigid
874 | rigor
875 | rinse
876 | riot
877 | ripen
878 | rise
879 | risk
880 | ritzy
881 | rival
882 | river
883 | roast
884 | robe
885 | robin
886 | rock
887 | rogue
888 | roman
889 | romp
890 | rope
891 | rover
892 | royal
893 | ruby
894 | rug
895 | ruin
896 | rule
897 | runny
898 | rush
899 | rust
900 | rut
901 | sadly
902 | sage
903 | said
904 | saint
905 | salad
906 | salon
907 | salsa
908 | salt
909 | same
910 | sandy
911 | santa
912 | satin
913 | sauna
914 | saved
915 | savor
916 | sax
917 | say
918 | scale
919 | scam
920 | scan
921 | scare
922 | scarf
923 | scary
924 | scoff
925 | scold
926 | scoop
927 | scoot
928 | scope
929 | score
930 | scorn
931 | scout
932 | scowl
933 | scrap
934 | scrub
935 | scuba
936 | scuff
937 | sect
938 | sedan
939 | self
940 | send
941 | sepia
942 | serve
943 | set
944 | seven
945 | shack
946 | shade
947 | shady
948 | shaft
949 | shaky
950 | sham
951 | shape
952 | share
953 | sharp
954 | shed
955 | sheep
956 | sheet
957 | shelf
958 | shell
959 | shine
960 | shiny
961 | ship
962 | shirt
963 | shock
964 | shop
965 | shore
966 | shout
967 | shove
968 | shown
969 | showy
970 | shred
971 | shrug
972 | shun
973 | shush
974 | shut
975 | shy
976 | sift
977 | silk
978 | silly
979 | silo
980 | sip
981 | siren
982 | sixth
983 | size
984 | skate
985 | skew
986 | skid
987 | skier
988 | skies
989 | skip
990 | skirt
991 | skit
992 | sky
993 | slab
994 | slack
995 | slain
996 | slam
997 | slang
998 | slash
999 | slate
1000 | slaw
1001 | sled
1002 | sleek
1003 | sleep
1004 | sleet
1005 | slept
1006 | slice
1007 | slick
1008 | slimy
1009 | sling
1010 | slip
1011 | slit
1012 | slob
1013 | slot
1014 | slug
1015 | slum
1016 | slurp
1017 | slush
1018 | small
1019 | smash
1020 | smell
1021 | smile
1022 | smirk
1023 | smog
1024 | snack
1025 | snap
1026 | snare
1027 | snarl
1028 | sneak
1029 | sneer
1030 | sniff
1031 | snore
1032 | snort
1033 | snout
1034 | snowy
1035 | snub
1036 | snuff
1037 | speak
1038 | speed
1039 | spend
1040 | spent
1041 | spew
1042 | spied
1043 | spill
1044 | spiny
1045 | spoil
1046 | spoke
1047 | spoof
1048 | spool
1049 | spoon
1050 | sport
1051 | spot
1052 | spout
1053 | spray
1054 | spree
1055 | spur
1056 | squad
1057 | squat
1058 | squid
1059 | stack
1060 | staff
1061 | stage
1062 | stain
1063 | stall
1064 | stamp
1065 | stand
1066 | stank
1067 | stark
1068 | start
1069 | stash
1070 | state
1071 | stays
1072 | steam
1073 | steep
1074 | stem
1075 | step
1076 | stew
1077 | stick
1078 | sting
1079 | stir
1080 | stock
1081 | stole
1082 | stomp
1083 | stony
1084 | stood
1085 | stool
1086 | stoop
1087 | stop
1088 | storm
1089 | stout
1090 | stove
1091 | straw
1092 | stray
1093 | strut
1094 | stuck
1095 | stud
1096 | stuff
1097 | stump
1098 | stung
1099 | stunt
1100 | suds
1101 | sugar
1102 | sulk
1103 | surf
1104 | sushi
1105 | swab
1106 | swan
1107 | swarm
1108 | sway
1109 | swear
1110 | sweat
1111 | sweep
1112 | swell
1113 | swept
1114 | swim
1115 | swing
1116 | swipe
1117 | swirl
1118 | swoop
1119 | swore
1120 | syrup
1121 | tacky
1122 | taco
1123 | tag
1124 | take
1125 | tall
1126 | talon
1127 | tamer
1128 | tank
1129 | taper
1130 | taps
1131 | tarot
1132 | tart
1133 | task
1134 | taste
1135 | tasty
1136 | taunt
1137 | thank
1138 | thaw
1139 | theft
1140 | theme
1141 | thigh
1142 | thing
1143 | think
1144 | thong
1145 | thorn
1146 | those
1147 | throb
1148 | thud
1149 | thumb
1150 | thump
1151 | thus
1152 | tiara
1153 | tidal
1154 | tidy
1155 | tiger
1156 | tile
1157 | tilt
1158 | tint
1159 | tiny
1160 | trace
1161 | track
1162 | trade
1163 | train
1164 | trait
1165 | trap
1166 | trash
1167 | tray
1168 | treat
1169 | tree
1170 | trek
1171 | trend
1172 | trial
1173 | tribe
1174 | trick
1175 | trio
1176 | trout
1177 | truce
1178 | truck
1179 | trump
1180 | trunk
1181 | try
1182 | tug
1183 | tulip
1184 | tummy
1185 | turf
1186 | tusk
1187 | tutor
1188 | tutu
1189 | tux
1190 | tweak
1191 | tweet
1192 | twice
1193 | twine
1194 | twins
1195 | twirl
1196 | twist
1197 | uncle
1198 | uncut
1199 | undo
1200 | unify
1201 | union
1202 | unit
1203 | untie
1204 | upon
1205 | upper
1206 | urban
1207 | used
1208 | user
1209 | usher
1210 | utter
1211 | value
1212 | vapor
1213 | vegan
1214 | venue
1215 | verse
1216 | vest
1217 | veto
1218 | vice
1219 | video
1220 | view
1221 | viral
1222 | virus
1223 | visa
1224 | visor
1225 | vixen
1226 | vocal
1227 | voice
1228 | void
1229 | volt
1230 | voter
1231 | vowel
1232 | wad
1233 | wafer
1234 | wager
1235 | wages
1236 | wagon
1237 | wake
1238 | walk
1239 | wand
1240 | wasp
1241 | watch
1242 | water
1243 | wavy
1244 | wheat
1245 | whiff
1246 | whole
1247 | whoop
1248 | wick
1249 | widen
1250 | widow
1251 | width
1252 | wife
1253 | wifi
1254 | wilt
1255 | wimp
1256 | wind
1257 | wing
1258 | wink
1259 | wipe
1260 | wired
1261 | wiry
1262 | wise
1263 | wish
1264 | wispy
1265 | wok
1266 | wolf
1267 | womb
1268 | wool
1269 | woozy
1270 | word
1271 | work
1272 | worry
1273 | wound
1274 | woven
1275 | wrath
1276 | wreck
1277 | wrist
1278 | xerox
1279 | yahoo
1280 | yam
1281 | yard
1282 | year
1283 | yeast
1284 | yelp
1285 | yield
1286 | yo-yo
1287 | yodel
1288 | yoga
1289 | yoyo
1290 | yummy
1291 | zebra
1292 | zero
1293 | zesty
1294 | zippy
1295 | zone
1296 | zoom
--------------------------------------------------------------------------------