passit

package module
v0.0.0-...-8f3e355 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2023 License: BSD-3-Clause Imports: 20 Imported by: 0

README

passit

passit is a password generation toolkit for Go. It features a variety of different password generators from charsets to regular expressions, wordlists and emoji.

All generators implement the following interface:

// Generator is an interface for generating passwords.
type Generator interface {
	// Password returns a randomly generated password using r as the source of
	// randomness.
	//
	// The returned password may or may not be deterministic with respect to r.
	// All generators in this package are deterministic unless otherwise noted.
	//
	// The output of r should be indistinguishable from a random string of the
	// same length. This is a property of a good CSPRNG. Fundamentally the
	// strength of the generated password is only as good as the provided source
	// of randomness.
	//
	// r should implement the io.ByteReader interface for improved performance.
	Password(r io.Reader) (string, error)
}

Generators

The package provides a number of generators that produce output from a fixed set:

Generator Description Examples
Digit [0-9] "0" "7"
LatinLower [a-z] "a" "j"
LatinLowerDigit [a-z0-9] "a" "j" "0" "7"
LatinUpper [A-Z] "A" "J"
LatinUpperDigit [A-Z0-9] "A" "J" "0" "7"
LatinMixed [a-zA-Z] "a" "j" "A" "J"
LatinMixedDigit [a-zA-Z0-9] "a" "j" "A" "J" "0" "7"
OrchardStreetMedium A word from Sam Schlinkert's Orchard Street Medium List "abandon" "forest"
OrchardStreetLong A word from Sam Schlinkert's Orchard Street Long List "abandon" "mobility"
OrchardStreetAlpha A word from Sam Schlinkert's Orchard Street Alpha List "abbot" "points"
OrchardStreetQWERTY A word from Sam Schlinkert's Orchard Street QWERTY List "access" "peg"
STS10Wordlist A word from Sam Schlinkert's '1Password Replacement List' "aback" "loophole"
EFFLargeWordlist A word from the EFF Large Wordlist for Passphrases "abacus" "partition"
EFFShortWordlist1 A word from the EFF Short Wordlist for Passphrases #1 "acid" "match"
EFFShortWordlist2 A word from the EFF Short Wordlist for Passphrases #2 "aardvark" "jaywalker"
Emoji13 A Unicode 13.0 fully-qualified emoji "⌚" "🕸️" "🧎🏾‍♀️"
Emoji15 A Unicode 15.0 fully-qualified emoji "⌚" "🏎️" "🧏🏿‍♂️"
HexLower Lowercase hexadecimal encoding "66e94bd4ef8a2c3b"
HexUpper Uppercase hexadecimal encoding "66E94BD4EF8A2C3B"
Base32 Base32 standard encoding "M3UUXVHPRIWDW"
Base32Hex Base32 hexadecimal encoding "CRKKNL7FH8M3M"
Base64 Base64 standard encoding "ZulL1O+KLDs"
Base64URL Base64 URL encoding "ZulL1O-KLDs"
Ascii85 Ascii85 encoding "B'Dt<mtrYX"
SpectreMaximum The Spectre maximum template "i7,o%yC4&fmQ1r*qfcWq"
SpectreLong The Spectre long template "ZikzXuwuHeve1("
SpectreMedium The Spectre medium template "Zik2~Puh"
SpectreBasic The Spectre basic template "izJ24tHJ"
SpectreShort The Spectre short template "His8"
SpectrePIN The Spectre PIN template "0778"
SpectreName The Spectre name template "hiskixuwu"
SpectrePhrase The Spectre phrase template "zi kixpu hoy vezamcu"
Empty Empty string ""
Hyphen ASCII hyphen-minus "-"
Space ASCII space " "

The package also provides a number of generators that produce output based on user input:

Generator Description
String A fixed string
RegexpParser Password that matches a regular expression pattern
FromCharset A rune from a charset string
FromRangeTable A rune from a unicode.RangeTable
FromSlice A string from a slice of strings

There are also a number of 'helper' generators that interact with the output of other generators:

Generator Description
Alternate Select a generator at random
Join Concatenate the output of multiple generators
Repeat Invoke a generator multiple times and concatenate the output
RandomRepeat Invoke a generator a random number of times and concatenate the output
RejectionSample Continually invoke a generator until the output passes a test
Transform Invoke a generator and convert the output according to a user supplied function
LowerCase Invoke a generator and convert the output to lower case
UpperCase Invoke a generator and convert the output to upper case
TitleCase Invoke a generator and convert the output to language-specific title case

Most generators only generate a single of something, be it a rune, ASCII character or word. For generating longer passwords use Repeat or RandomRepeat, possibly with Join or Alternate. In this way the various generators can be composed to generator arbitrarily long and complex passwords, or short and simple passwords as is needed.

The generators are designed to map from a random string / stream to a text password. This is not designed to be a reversible process and decoding the password to the original random string is not possible.

Commands

Two commands for easy CLI password generation are provided.

passphrase

passphrase is a tool that generates random passphrases using one of the embedded wordlists supported by passit.

$ go install go.tmthrgd.dev/passit/cmd/passphrase@latest
$ passphrase -n 5 -s -
assumes-forth-humanities-exemption-paid
twoproblems

twoproblems is a tool that generates random passwords based on a regular expression template.

$ go install go.tmthrgd.dev/passit/cmd/twoproblems@latest
$ twoproblems '[[:alpha:]]{15}-[[:digit:]]{3}[[:punct:]]{2}'
KsMtvHnSOmqjIll-277&$
$ twoproblems '[[:alnum:]]{5}-(?P<word>count=5&sep=-)-[[:punct:]]{5}'
7Gfyv-degree-currency-revolt-coined-requested-*@`}[

Two special captures ((?P<name>)) are supported:

  1. (?P<word>): A word from any of the supported wordlists. This takes parameters similar to a URL query string (e.g. x=1&y=2&y=3), including supporting percent escaping. The supported parameters are:
    • case transform the word to a given case ('lower' - default, 'upper' or 'title');
    • count generate N multiple words instead of just one;
    • list the name of a supported wordlist to use ('orchard:medium', 'orchard:long' - default, 'orchard:alpha, 'orchard:qwerty, 'eff:large' / 'eff', 'eff:short1' or 'eff:short2');
    • sep a separator to insert between words (defaults to a space).
  2. (?P<emoji>): A Unicode 15.0 emoji returned from Emoji15. This can take a number to generate multiple emoji.

Sources of randomness

Note: Remember that wrapping the io.Reader with bufio.NewReader (if it doesn't already implement io.ByteReader) will greatly improve the performance of the generators.

For generating random passwords, Password should be called with crypto/rand.Reader. Avoid using poor quality sources of randomness like math/rand.

import "crypto/rand"

func ExampleEFFLargeWordlist_WithCryptoRand() {
	pass, _ := passit.Repeat(passit.EFFLargeWordlist, "-", 4).Password(rand.Reader)
	fmt.Println(pass)
}

For generating deterministic passwords, Password should be called with a deterministic stream that should be indistinguishable from a random string of the same length. Good examples of sources for this would be HKDF, ChaCha20 or AES-CTR with proper key generation. Care must be taken when using deterministic password generation as the generated password is only ever as good as the provided source of randomness.

func ExampleEFFLargeWordlist_WithHKDF() {
	secret, salt, info := []byte("secret"), []byte("salt"), []byte("info")

	r := hkdf.New(sha512.New, secret, salt, info)

	pass, _ := passit.Repeat(passit.EFFLargeWordlist, "-", 4).Password(r)
	fmt.Println(pass) // Output: king-unflawed-vagrancy-laxative
}

func ExampleEFFLargeWordlist_WithChaCha20() {
	key := []byte("secret key for password generate")

	c, _ := chacha20.NewUnauthenticatedCipher(key, []byte("IV for PWDGN"))
	r := cipher.StreamReader{S: c, R: zeroReader{}}

	pass, _ := passit.Repeat(passit.EFFLargeWordlist, "-", 4).Password(r)
	fmt.Println(pass) // Output: penny-enclose-preoccupy-sappy
}

func ExampleEFFLargeWordlist_WithAESCTR() {
	key := []byte("secret key for password generate")

	block, _ := aes.NewCipher(key)
	ctr := cipher.NewCTR(block, []byte("IV for PWDGN CTR"))
	r := cipher.StreamReader{S: ctr, R: zeroReader{}}

	pass, _ := passit.Repeat(passit.EFFLargeWordlist, "-", 4).Password(r)
	fmt.Println(pass) // Output: juncture-net-unseen-pegboard
}

type zeroReader struct{}

func (zeroReader) Read(p []byte) (int, error) {
	clear(p)
	return len(p), nil
}

License

This library is Copyright (c) 2022, Tom Thorogood and is licensed under a BSD 3-Clause License.

Embedded wordlists are subject to their own copyright license referenced in the documentation.

Documentation

Overview

Package passit provides a password generation toolkit. It features a variety of different password generators from charsets to regular expressions and emoji.

All generators implement Generator. Once a suitable Generator has been constructed, passwords can be generated by calling the Generator.Password method. All generators in this package are deterministic unless otherwise noted.

Most generators only generate a single of something, be it a rune, ASCII character or word. For generating longer passwords use Repeat or RandomRepeat, possibly with Join or Alternate. In this way the various generators can be composed to generator arbitrarily long and complex passwords, or short and simple passwords as is needed.

For generating random passwords, Generator.Password should be called with crypto/rand.Reader. Avoid using poor quality sources of randomness like math/rand.

For generating deterministic passwords, Generator.Password should be called with a deterministic stream that should be indistinguishable from a random string of the same length. Care must be taken when using deterministic password generation as the generated password is only ever as good as the provided source of randomness.

Note: Wrapping the io.Reader with bufio.NewReader (if it doesn't already implement io.ByteReader) will greatly improve the performance of the generators.

Index

Constants

This section is empty.

Variables

View Source
var EmojiLatest = Emoji15

EmojiLatest is an alias for the latest supported emoji list.

Functions

This section is empty.

Types

type Generator

type Generator interface {
	// Password returns a randomly generated password using r as the source of
	// randomness.
	//
	// The returned password may or may not be deterministic with respect to r.
	// All generators in this package are deterministic unless otherwise noted.
	//
	// The output of r should be indistinguishable from a random string of the
	// same length. This is a property of a good CSPRNG. Fundamentally the
	// strength of the generated password is only as good as the provided source
	// of randomness.
	//
	// r should implement the io.ByteReader interface for improved performance.
	Password(r io.Reader) (string, error)
}

Generator is an interface for generating passwords.

var Digit Generator = &asciiGenerator{"0123456789"}

Digit is a Generator that returns a random numeric digit.

var EFFLargeWordlist Generator = &embeddedGenerator{raw: &wordlist.EFFLargeWordlist}

EFFLargeWordlist is a Generator that returns a random word from the EFF Large Wordlist for Passphrases (eff_large_wordlist.txt).

It contains 7,776 words and has 12.925 bits of entropy per word.

This wordlist is licensed by the Electronic Frontier Foundation under a CC BY 3.0 US license.

var EFFShortWordlist1 Generator = &embeddedGenerator{raw: &wordlist.EFFShortWordlist1}

EFFShortWordlist1 is a Generator that returns a random word from the EFF Short Wordlist for Passphrases #1 (eff_short_wordlist_1.txt).

It contains 1,296 words and has 10.340 bits of entropy per word.

This wordlist is licensed by the Electronic Frontier Foundation under a CC BY 3.0 US license.

var EFFShortWordlist2 Generator = &embeddedGenerator{raw: &wordlist.EFFShortWordlist2}

EFFShortWordlist2 is a Generator that returns a random word from the EFF Short Wordlist for Passphrases #2 (eff_short_wordlist_2_0.txt).

It contains 1,296 words and has 10.340 bits of entropy per word.

This wordlist is licensed by the Electronic Frontier Foundation under a CC BY 3.0 US license.

var Emoji13 Generator = &embeddedGenerator{raw: &emojilist.Unicode13}

Emoji13 is a Generator that returns a random fully-qualified emoji from the Unicode 13.0 emoji list.

var Emoji15 Generator = &embeddedGenerator{raw: &emojilist.Unicode15}

Emoji15 is a Generator that returns a random fully-qualified emoji from the Unicode 15.0 emoji list.

var Empty Generator = fixedString("")

Empty is a Generator that always returns an empty string.

var Hyphen Generator = fixedString("-")

Hyphen is a Generator that always returns a fixed ASCII hyphen-minus.

var LatinLower Generator = &asciiGenerator{"abcdefghijklmnopqrstuvwxyz"}

LatinLower is a Generator that returns a random lowercase character from the latin alphabet.

var LatinLowerDigit Generator = &asciiGenerator{"abcdefghijklmnopqrstuvwxyz0123456789"}

LatinLowerDigit is a Generator that returns a random lowercase character from the latin alphabet or a numeric digit.

var LatinMixed Generator = &asciiGenerator{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"}

LatinMixed is a Generator that returns a random mixed-case characters from the latin alphabet.

var LatinMixedDigit Generator = &asciiGenerator{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}

LatinMixedDigit is a Generator that returns a random mixed-case characters from the latin alphabet or a numeric digit.

var LatinUpper Generator = &asciiGenerator{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"}

LatinUpper is a Generator that returns a random uppercase character from the latin alphabet.

var LatinUpperDigit Generator = &asciiGenerator{"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"}

LatinUpperDigit is a Generator that returns a random uppercase character from the latin alphabet or a numeric digit.

var OrchardStreetAlpha Generator = &embeddedGenerator{raw: &wordlist.OrchardStreetAlpha}

OrchardStreetAlpha is a Generator that returns a random word from Sam Schlinkert's Orchard Street Alpha List.

It contains 1,296 words and has 10.340 bits of entropy per word. This list is uniquely decodable and can be used with or without separators.

This wordlist is licensed by Sam Schlinkert under a CC BY-SA 4.0 license.

var OrchardStreetLong Generator = &embeddedGenerator{raw: &wordlist.OrchardStreetLong}

OrchardStreetLong is a Generator that returns a random word from Sam Schlinkert's Orchard Street Long List.

It contains 17,576 words and has 14.101 bits of entropy per word. This list is uniquely decodable and can be used with or without separators.

This wordlist is licensed by Sam Schlinkert under a CC BY-SA 4.0 license.

var OrchardStreetMedium Generator = &embeddedGenerator{raw: &wordlist.OrchardStreetMedium}

OrchardStreetMedium is a Generator that returns a random word from Sam Schlinkert's Orchard Street Medium List.

It contains 8,192 words and has 13.000 bits of entropy per word. This list is uniquely decodable and can be used with or without separators.

This wordlist is licensed by Sam Schlinkert under a CC BY-SA 4.0 license.

var OrchardStreetQWERTY Generator = &embeddedGenerator{raw: &wordlist.OrchardStreetQWERTY}

OrchardStreetQWERTY is a Generator that returns a random word from Sam Schlinkert's Orchard Street QWERTY List.

It contains 1,296 words and has 10.340 bits of entropy per word. This list is uniquely decodable and can be used with or without separators.

This wordlist is licensed by Sam Schlinkert under a CC BY-SA 4.0 license.

var STS10Wordlist Generator = &embeddedGenerator{raw: &wordlist.STS10Wordlist}

STS10Wordlist is a Generator that returns a random word from Sam Schlinkert's '1Password Replacement List'.

It contains 18,208 words and has 14.152 bits of entropy per word. This list is not uniquely decodable and should only be used with separators.

This wordlist is licensed by Sam Schlinkert under a CC BY 3.0 license.

Deprecated: This list is not safe to use without separators. Use one of the other wordlists instead, like OrchardStreetLong.

var Space Generator = fixedString(" ")

Space is a Generator that always returns a fixed ASCII space.

func Alternate

func Alternate(gens ...Generator) Generator

Alternate returns a Generator that randomly selects one of the provided Generator's to use to generate the password.

func Ascii85

func Ascii85(count int) Generator

Ascii85 returns a Generator that encodes count-bytes with encoding/ascii85.

func Base32

func Base32(count int) Generator

Base32 returns a Generator that encodes count-bytes with encoding/base32.StdEncoding without padding.

func Base32Hex

func Base32Hex(count int) Generator

Base32Hex returns a Generator that encodes count-bytes with encoding/base32.HexEncoding without padding.

func Base64

func Base64(count int) Generator

Base64 returns a Generator that encodes count-bytes with encoding/base64.RawStdEncoding.

func Base64URL

func Base64URL(count int) Generator

Base64URL returns a Generator that encodes count-bytes with encoding/base64.RawURLEncoding.

func FromCharset

func FromCharset(charset string) Generator

FromCharset returns a Generator that returns a random rune from charset.

func FromRangeTable

func FromRangeTable(tab *unicode.RangeTable) Generator

FromRangeTable returns a Generator that returns a random rune from the unicode.RangeTable.

The returned Generator is only deterministic if the same unicode.RangeTable is used. Be aware that the builtin unicode.X tables are subject to change as new versions of Unicode are released and are not suitable for deterministic use.

func FromSlice

func FromSlice(list ...string) Generator

FromSlice returns a Generator that returns a random string from list.

func HexLower

func HexLower(count int) Generator

HexLower returns a Generator that encodes count-bytes in lowercase hexadecimal.

func HexUpper

func HexUpper(count int) Generator

HexUpper returns a Generator that encodes count-bytes in uppercase hexadecimal.

func Join

func Join(sep string, gens ...Generator) Generator

Join returns a Generator that concatenates the outputs of each Generator to create a single string. The separator string sep is placed between the outputs in the resulting string.

func LowerCase

func LowerCase(gen Generator) Generator

LowerCase returns a Generator that uses strings.ToLower to map all Unicode letters in the generated password to their lower case.

func ParseRegexp

func ParseRegexp(pattern string, flags syntax.Flags) (Generator, error)

ParseRegexp is a shortcut for new(RegexpParser).Parse(pattern, flags).

func RandomRepeat

func RandomRepeat(gen Generator, sep string, min, max int) Generator

RandomRepeat returns a Generator that concatenates the output of invoking the Generator a random number of times in [min,max] to create a single string. The separator string sep is placed between the outputs in the resulting string.

func RejectionSample

func RejectionSample(gen Generator, condition func(string) bool) Generator

RejectionSample returns a Generator that continually generates passwords with gen until condition reports true for the generated password or an error occurs.

The behaviour is unspecified if condition never reports true.

func Repeat

func Repeat(gen Generator, sep string, count int) Generator

Repeat returns a Generator that concatenates the output of invoking the Generator count times to create a single string. The separator string sep is placed between the outputs in the resulting string.

func String

func String(s string) Generator

String returns a Generator that always returns the given string.

func TitleCase

func TitleCase(gen Generator, t language.Tag) Generator

TitleCase returns a Generator that uses golang.org/x/text/cases.Title to convert the generated password to language-specific title case.

func Transform

func Transform(gen Generator, fn func(string) string) Generator

Transform returns a Generator that converts the generated password according to the user supplied function fn.

func UpperCase

func UpperCase(gen Generator) Generator

UpperCase returns a Generator that uses strings.ToUpper to map all Unicode letters in the generated password to their upper case.

type GeneratorFunc

type GeneratorFunc func(r io.Reader) (string, error)

The GeneratorFunc type is an adapter to allow the use of ordinary functions as password generators. If f is a function with the appropriate signature, GeneratorFunc(f) is a Generator that calls f.

func (GeneratorFunc) Password

func (f GeneratorFunc) Password(r io.Reader) (string, error)

Password implements Generator, calling f(r).

type RegexpParser

type RegexpParser struct {
	// contains filtered or unexported fields
}

RegexpParser is a regular expressions parser that parses patterns into a Generator that generates passwords matching the parsed regexp. The zero-value is a usable parser.

func (*RegexpParser) Parse

func (p *RegexpParser) Parse(pattern string, flags syntax.Flags) (Generator, error)

Parse parses the regexp pattern according to the flags and returns a Generator. It returns an error if the regexp is invalid. It uses regexp/syntax to parse the pattern.

All regexp features supported by regexp/syntax are supported, though some may have no effect.

It is an error to use named captures (?P<name>) except to refer to special capture factories added with SetSpecialCapture.

func (*RegexpParser) SetAnyRangeTable

func (p *RegexpParser) SetAnyRangeTable(tab *unicode.RangeTable)

SetAnyRangeTable sets the unicode.RangeTable used when generating any (.) characters or when restricting character classes ([a-z]) with a user provided one. By default a subset of ASCII is used. Calling SetAnyRangeTable(nil) will reset the RegexpParser back to the default.

The regexp Generator is only deterministic if the same unicode.RangeTable is used. Be aware that the builtin unicode.X tables are subject to change as new versions of Unicode are released and are not suitable for deterministic use.

func (*RegexpParser) SetSpecialCapture

func (p *RegexpParser) SetSpecialCapture(name string, factory SpecialCaptureFactory)

SetSpecialCapture adds a special capture factory to use for matching named captures. A regexp pattern such as "(?P<name>)" will invoke the factory and use the returned Generator instead of the contents of the capture. If name is "*", the factory will be used as a fallback if a named factory can't be found.

If attempting to parse the inner contents of the capture, be aware that the regexp parser may have mangled them. For instance "(?P<name>1|2)" will become "(?P<name>[1-2])", "(?P<name>z|z)" will become "(?P<name>z)" and "(?i:(?P<name>z))" will become "(?P<name>(?i:Z))".

type SpecialCaptureFactory

type SpecialCaptureFactory func(*syntax.Regexp) (Generator, error)

SpecialCaptureFactory represents a special capture factory to be used with (*RegexpParser).SetSpecialCapture.

func SpecialCaptureBasic

func SpecialCaptureBasic(gen Generator) SpecialCaptureFactory

SpecialCaptureBasic returns a special capture factory that doesn't accept any input and always returns the provided Generator.

func SpecialCaptureWithRepeat

func SpecialCaptureWithRepeat(gen Generator, sep string) SpecialCaptureFactory

SpecialCaptureWithRepeat returns a special capture factory that parses the capture value for a count to be used with Repeat(gen, sep, count). If the capture is empty, the Generator is returned directly.

type SpectreTemplate

type SpectreTemplate string

SpectreTemplate is a Generator that implements a variant of the Spectre / Master Password encoding algorithm by Maarten Billemont.

This algorithm is not compatible with any of the officially published algorithms, but it does produce passwords using the same templates that are indistinguishable from the official algorithm. Unlike that algorithm, this doesn't exhibit a modulo bias.

const (
	SpectreMaximum SpectreTemplate = "anoxxxxxxxxxxxxxxxxx:axxxxxxxxxxxxxxxxxno"
	SpectreLong    SpectreTemplate = "" /* 314-byte string literal not displayed */
	SpectreMedium  SpectreTemplate = "CvcnoCvc:CvcCvcno"
	SpectreBasic   SpectreTemplate = "aaanaaan:aannaaan:aaannaaa"
	SpectreShort   SpectreTemplate = "Cvcn"
	SpectrePIN     SpectreTemplate = "nnnn"
	SpectreName    SpectreTemplate = "cvccvcvcv"
	SpectrePhrase  SpectreTemplate = "cvcc cvc cvccvcv cvc:cvc cvccvcvcv cvcv:cv cvccv cvc cvcvccv"
)

These are the standard templates defined by Spectre / Master Password.

func (SpectreTemplate) Password

func (st SpectreTemplate) Password(r io.Reader) (string, error)

Password implements Generator.

Directories

Path Synopsis
cmd
passphrase
Command passphrase generates random passphrases using either Sam Schlinkert's '1Password Replacement List' (1password-replacement.txt), the EFF Large Wordlist for Passphrases (eff_large_wordlist.txt), the EFF Short Wordlist for Passphrases #1 (eff_short_wordlist_1.txt), or the EFF Short Wordlist for Passphrases #2 (eff_short_wordlist_2_0.txt).
Command passphrase generates random passphrases using either Sam Schlinkert's '1Password Replacement List' (1password-replacement.txt), the EFF Large Wordlist for Passphrases (eff_large_wordlist.txt), the EFF Short Wordlist for Passphrases #1 (eff_short_wordlist_1.txt), or the EFF Short Wordlist for Passphrases #2 (eff_short_wordlist_2_0.txt).
twoproblems
Command twoproblems generates random passwords based on a regular expression template.
Command twoproblems generates random passwords based on a regular expression template.
internal
wordlists
Package wordlists provides utility functions for working with wordlists.
Package wordlists provides utility functions for working with wordlists.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL