spg

package module
v0.0.0-...-e3e6898 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2020 License: Apache-2.0 Imports: 9 Imported by: 0

README

spg - A go package for strong password generation

1Password's Strong Password Generator package offers the underlying engine for flexible specification of generated password requirements and ensuring that the generated passwords it returns follow a uniform distribution.

The clients of this package are expected to manage what is presented to users. This engine offers far greater flexibility than should normally be exposed to users.

Get started

Use go get:

go get go.1password.io/spg

Vendored dependencies

Before you can successfully build, you may need to install dependencies. These are currently[^1] managed using govendor. Install it if needed,

go get -u github.com/kardianos/govendor

And then use

govendor sync

to fetch the appropriate dependencies into ./vendor

[^1]: We will probably switch to go modules at some point

License

1Password's spg is copyright 2018, AgileBits Inc and licensed under version 2.0 of the Apache License Agreement.

Contributing

This is on Github: https://github.com/1password/spg create issues, forks, etc there.

Documentation

Overview

Package spg provides 1Password's Strong Password Generator engine for uniform and flexible password generation.

The Strong Password Generator package offers the underlying engine for flexible specification of generated password requirements and ensuring that the generated passwords it returns follow a uniform distribution.

The clients of this package are expected to manage what is presented to users. This engine offers far greater flexibility than should normally be exposed to users.

Usage overview

The very highlevel usage over view for most cases is

1. The user will create either a character recipe, r, with NewCharRecipe() or a word (or syllable) list recipe, r, with NewWLRecipe().

2. The user will call the r.Generate() method of a recipe, r, to generate a password, pwd.

3. The returned password, pwd, has a String() method, which does the obvious thing and Entropy field, which contains the min-Entropy based on the recipe.

Wordlist and pronounceable

The word list generator produces things like "correct horse battery staple", but when the list is of pronounceable syllables, it can also be set up to produce things like

Mirk9vust8jilk3rooy
scuy9lam2lerk9Kais
smoh1fock6mirn7Lic
jaud3Rew4jo6mont

Lengths for these are specified in terms of the number of elements drawn from the list to be included in these passwords (not counting the separators). Although the above examples all have different lengths in terms of number of characters, they were all specified as Length 4.

The passwords that one gets depend on the word list recipe, WLRecipe, and the actual word list provided.

Character passwords

Character-based are your typical notion of generated password, however these can be specified in ways to produce only numeric PINs if desired. The passwords generated are a function of the CharRecipe.

The Generate and Entropy methods

The word list and character recipes (WLRecipe, CharRecipe) implement a Generator interface with two methods, Generate and Entropy.

Generate returns a Password. There is a fair amount of internal structure to a Password object, but the ones you are most after is available through the Password.String() method and the Entropy field.

Entropy returns the entropy of a password that would be generated given the current recipe.

A word about Entropy

Entropy is a highly misleading concept when applied to passwords. In the general case it is either an incoherent concept or the wrong concept to use when talking about the strength of a password. It does, however, make sense when a password is drawn uniformly from a space of possible passwords. When the distribution is uniform, the (Shannon) entropy is the same as the min-entropy (based on probability of getting the most likely result).

This package does ensure that passwords are generated uniformly given the recipe passed to the generator, with the exception of the interaction of capitalizaton for some wordlists. In those cases, min-entropy is reported. That is, where min-entropy is not the same as Shannon Entropy Entropy() returns the min-entropy.

Entropy is a function solely of the recipe.

License

This package is Copyright 2017, 2018 by AgileBits, Inc and is licensed under the Apache 2.0 agreement.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	MaxTrials   = 200              // How many times we will try to generate before giving up
	MaxFailRate = 1.0 / 1000000000 // Maximum acceptable failure rate after MaxTrials
)

Re-generation trials for meeting requirements

View Source
var (
	SFNone               SFFunction = func() (string, FloatE) { return "", FloatE(0.0) }                      // Empty separator
	SFDigits1                       = NewSFFunction(CharRecipe{Length: 1, Allow: Digits})                     // Single digit separator
	SFDigits2                       = NewSFFunction(CharRecipe{Length: 2, Allow: Digits})                     // Double digit separator
	SFDigitsNoAmbiguous1            = NewSFFunction(CharRecipe{Length: 1, Allow: Digits, Exclude: Ambiguous}) // Single digit, no ambiguous
	SFDigitsNoAmbiguous2            = NewSFFunction(CharRecipe{Length: 2, Allow: Digits, Exclude: Ambiguous}) // Double digit, no ambiguous
	SFSymbols                       = NewSFFunction(CharRecipe{Length: 1, Allow: Symbols})                    // Symbols
	SFDigitsSymbols                 = NewSFFunction(CharRecipe{Length: 1, Allow: Symbols | Digits})           // Symbols and digits
)

Pre-baked Separator functions

View Source
var AgileSyllables = []string{}/* 10129 elements not displayed */

AgileSyllables is the list of syllables used by the 1Password strong password generator

View Source
var AgileWords = []string{}/* 18325 elements not displayed */

AgileWords is the list of words used by the 1Password strong password generator

Functions

This section is empty.

Types

type CTFlag

type CTFlag uint32

CTFlag is the type for the be

const (
	// Character types useful for Allow and Require
	Uppers CTFlag = 1 << iota
	Lowers
	Digits
	Symbols

	// Character types useful for Exclude
	Ambiguous

	// Named combinations
	None    CTFlag = 0
	Letters        = Uppers | Lowers
	All            = Letters | Digits | Symbols
)

Character type flags

type CapScheme

type CapScheme string

CapScheme is for an enumeration of capitalization schemes

const (
	CSNone   CapScheme = "none"   // No words will be capitalized
	CSFirst  CapScheme = "first"  // First word will be capitalized
	CSAll    CapScheme = "all"    // All words will be capitalized
	CSRandom CapScheme = "random" // Some words (roughly half) will be capitalized
	CSOne    CapScheme = "one"    // One randomly selected word will be capitalized
)

Defined capitalization schemes. (Using strings instead of int enum to make life easier in a debugger and calling from JavaScript)

type CharRecipe

type CharRecipe struct {
	Length int // Length of generated password in characters

	// Character types to Allow, Require, or Exclude in generated password
	Allow   CTFlag // Types which may appear
	Require CTFlag // Types which must appear (at least one from each type)
	Exclude CTFlag // Types must not appear

	// User provided character sets for Allow, Require, and Exclude
	AllowChars   string   // Specific characters that may appear
	RequireSets  []string // At least one character from each string must appear
	ExcludeChars string   // Specific characters that must not appear
	// contains filtered or unexported fields
}

CharRecipe are generator attributes relevent for character list generation

Allow - Any character from any of these sets may be present in generated password.

Exclude - No characters from any of these sets may be present in the generated password. Exclusion overrides Require and Allow.

Require - At least one character from each of these sets must be present in the generated password.

Example (Default)
r := NewCharRecipe(15) // 15 character passwords

// Let's generate 5 passwords to get a small taste of them
for i := 0; i < 5; i++ {
	p, _ := r.Generate() // You'd check for errors in real code
	fmt.Printf("Password: %q\tEntropy: %.3f\n", p, p.Entropy)
}
Output:

Example (Lowerdigits)
r := CharRecipe{
	Length:  17,              // Password will be 17 characters long
	Allow:   Lowers | Digits, // and may contain lowercase letters and digits
	Exclude: Ambiguous,       // but no ambiguous characters
}

// Let's generate five of them for a small sample
for i := 0; i < 5; i++ {
	p, _ := r.Generate() // You would check error in real code
	fmt.Printf("Password: %q\tEntropy: %.3f\n", p, p.Entropy)
}
Output:

Example (Pin)

Examples Because these have random output, we can't use "Output:",

r := CharRecipe{
	Length: 4,      // Password will be 4 characters long
	Allow:  Digits, // and comprised of digits
}
pwd, _ := r.Generate() // In real code, you would check error
fmt.Println(pwd)
Output:

func NewCharRecipe

func NewCharRecipe(length int) *CharRecipe

NewCharRecipe creates CharRecipe with reasonable defaults and Length length Defaults are

r.Allow = Letters | Digits | Symbols
r.Exclude = Ambiguous

And these may need to be cleared if you want to tinker with them

func (CharRecipe) Alphabet

func (r CharRecipe) Alphabet() string

Alphabet returns a sorted string of the characters that are drawn from in a given recipe, r

func (CharRecipe) Entropy

func (r CharRecipe) Entropy() float32

Entropy returns the entropy of a character password given the generator attributes

func (CharRecipe) Generate

func (r CharRecipe) Generate() (*Password, error)

Generate a password using the character generator. The attributes contain all of the details needed for generating the password

func (CharRecipe) SuccessProbability

func (r CharRecipe) SuccessProbability() float32

SuccessProbability returns the chances of meeting all of the Require-ments on a single trial.

type FloatE

type FloatE float32

FloatE is the float type bits of a generated password. It is only useful (externally) if you are creating your own Password SeparatorFunc.

type Generator

type Generator interface {
	Generate() (*Password, error)
	Entropy() float32
}

Generator is a fully configured password recipe

type IndexKind

type IndexKind uint8

IndexKind is the kind of tokenization index. Token indices are compact byte arrays that can be used in conjunction to with a password string to reconstruct an array of Tokens

const (
	CharacterIndexKind   IndexKind = iota // Tokens are all Atoms and of length 1
	VarAtomsIndexKind                     // Tokes are all atoms (of potentally varying lengths)
	AlternatingIndexKind                  // Tokens are alternation of A S A S ... A
	FullIndexKind                         // Requires a full token index as sequeunce of token types is not predictable
)

Possible values for first byte of the compact token index index.

type Indices

type Indices []byte

Indices can hold the indices needed to reconstruct tokens, separator from string

type Password

type Password struct {
	Entropy float32 // Entropy in bits of the Recipe from which this password was generated
	// contains filtered or unexported fields
}

Password is what gets generated by the Generators

func Tokenize

func Tokenize(pw string, ti Indices, entropy float32) (Password, error)

Tokenize reconstructs a Password from a password string and Indices produced by MakeIndices()

func (Password) String

func (p Password) String() string

String is the Stringer. It produces the password as string one might expect

func (Password) Tokens

func (p Password) Tokens() Tokens

Tokens returns the tokens

type SFFunction

type SFFunction func() (string, FloatE)

SFFunction is a type for a function that returns a string (to be used within a password) and the entropy it contributes

func NewSFFunction

func NewSFFunction(r CharRecipe) SFFunction

NewSFFunction makes a Separator Function from a CharRecipe

type Token

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

Token is a unit within a generated password. In "correct horse battery" there are five tokens

    []Token{
		{Value: "correct", Type: AtomType},
		{Value: " ", Type: SeparatorType},
		{Value: "horse", Type: AtomType},
		{Value: " ", Type: SeparatorType},
		{Value: "battery", Type: AtomType},
	}

func (Token) Type

func (t Token) Type() TokenType

Type returns the token type

func (Token) Value

func (t Token) Value() string

Value returns the string value

type TokenType

type TokenType uint8

TokenType holds the kinds of tokens within within a generated password Having it be an uint8 makes it easier to compact these into the token indices

const (
	SeparatorType TokenType = iota
	AtomType
)

For labelling tokens within a generated password

type Tokens

type Tokens []Token

Tokens is the array of tokens that comprise a password. Its purpose is to allow some consumer of generated passwords to be able to display a wordlist password with knowledge of where the units and separators are. For example, the separators within a wordlist password might be displayed in a different color.

Tokens is also intended to be a relatively compact entity.

func (Tokens) Atoms

func (ts Tokens) Atoms() []string

Atoms returns the tokens (words, syllables, characters) that compromise the bulk of the password

func (Tokens) Kind

func (ts Tokens) Kind() IndexKind

Kind looks at the tokens and works out what the most appropriate kind of token index we should use. It is exported in case callers would like help at guessing what kind of password they have, it does only provide a guess. It is no substitute for the original recipe

func (Tokens) MakeIndices

func (ts Tokens) MakeIndices() (Indices, error)

MakeIndices returns a compact array of indices that indicate where a string is to be separated In the worst case it will need to encode both the length and the type of each token, thus requiring two bytes per token (plus the one leading byte) It does attempt to inspect the tokens to determine whether it can get away with encoding less information. The leading byte of the returned array contains necessary information about the particularly indexing used

token lengths must be in (1, 255)

func (Tokens) Separators

func (ts Tokens) Separators() []string

Separators are the separators between tokens. If this list is shorter than one less then the number of tokens, the last separator listed is used repeatedly to separate subsequent tokens. If this is nil, it is taken as nil no separators between tokens

func (Tokens) Types

func (ts Tokens) Types() map[TokenType]bool

Types returns a set of all of the token types used within a password This is exported to help those who want to do fancy colorful display of passwords.

type WLRecipe

type WLRecipe struct {
	Length        int        // Length of generated password in words
	SeparatorChar string     // What character(s) should separate words
	SeparatorFunc SFFunction // function to generate separators, If nil just use SeperatorChar
	Capitalize    CapScheme  // Which words in generated password should be capitalized
	// contains filtered or unexported fields
}

WLRecipe (Word List password Attributes) are the generator settings for wordlist (syllable list) passwords

Example (Once)
// You would use a much longer wordlist, probably taken from a file
w := []string{"once", "upon", "midnight", "dreary", "while", "pondered", "weak", "and", "weary", "over", "many"}

wl, err := NewWordList(w) // Prepares list for use in recipe creation
if err != nil {
	log.Fatalf("Oops: %v", err)
}

// Four word passwords with digit separator and one of the words capitalized
r := NewWLRecipe(4, wl)
r.SeparatorFunc = SFDigits1
r.Capitalize = CSOne

pwd, err := r.Generate()
if err != nil {
	log.Fatalf("Oops: %v", err)
}

// With random output, we can't list expected Output
// So uncomment the following, but test will fail
_ = pwd // Make go happy for when pwd isn't used
// fmt.Printf("Password: \t%q\n", pwd)

e := r.Entropy()
fmt.Printf("Entropy: \t%.2f\n", e)
Output:

Entropy: 	25.80

func NewWLRecipe

func NewWLRecipe(length int, wl *WordList) *WLRecipe

NewWLRecipe sets up word list password attributes with defaults and Length length

func (WLRecipe) Entropy

func (r WLRecipe) Entropy() float32

Entropy returns the min-entropy from the recipe. It needs to know things about the wordlist used as well as other details of the recipe.

When the generator produces uniform distirbution (the typical case) min-entropy and Shannon entropy are the same. If capitalization is used and the word list contains members whose capitalization does not yield a distinct element, the distribution becomes non-uniform.

func (WLRecipe) Generate

func (r WLRecipe) Generate() (*Password, error)

Generate a password using the wordlist recipe.

func (WLRecipe) Size

func (r WLRecipe) Size() uint32

Size of the wordlist in the recipe

type WordList

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

WordList contains the list of words WLGenerator()

func NewWordList

func NewWordList(list []string) (*WordList, error)

NewWordList does what it says on the tin. Pass it a slice of strings It will remove duplicates from the slice provided, and it will count up how many words on the list can be changed through capitalization This isn't cheap, so it is best to create each word list once and keep it around as long as you need it.

func (WordList) Size

func (wl WordList) Size() uint32

Size returns the number of items in the generator's wordlist or the maxiumum uint32, whichever is smaller (the restriction on size is because of the RNG we are using)

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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