PwGenOptions class
A PwGenOptions object is a recipe for PwGen.Generate and PwGen.Entropy: the mode, the characters or words to draw from, the constraints on the result, and the random source.
A recipe is created from a preset. The predeclared object PwGenOptions, written without a variable, is a factory: its five preset properties, Alphanumeric, Complex, Hex, Passphrase and Pin, each return a new recipe with every property set, and Clone copies one.
Dim o As PwGenOptions = PwGenOptions.Complex
o.Length = 20
o.StartsWithLetter = True
Dim pw As String = PwGen.Generate(o)
Debug.Print Len(pw) ' 20
Debug.Print Left$(pw, 1) Like "[A-Za-z]" ' True
A property of a recipe checks nothing when it is set. A value that cannot be used, such as a Length of 0, is found when the recipe is passed to Generate or Entropy, and is reported there as an error code.
Important
Assigning a property on the predeclared object itself raises run-time error 5, Invalid procedure call or argument, with the source PwGenOptions and the message PwGenOptions (default instance) is a factory. Use Alphanumeric, Complex, Passphrase, Pin, or Hex. Create a recipe first and assign the properties of that.
On Error Resume Next
PwGenOptions.Length = 20
Debug.Print Err.Number ' 5
Debug.Print Err.Source ' PwGenOptions
Reading a property of the predeclared object does not raise an error. It returns the empty value of the type, 0, False or an empty string, and not the default of the property.
Debug.Print PwGenOptions.Length ' 0
Debug.Print PwGenOptions.HasUpperCase ' False
Debug.Print "[" & PwGenOptions.WordSeparator & "]" ' []
Note
The class declares a Private constructor and the attribute [COMCreatable(False)], so that only its presets create recipes. In BETA 1005 twinBASIC does not enforce a Private constructor, and a project that references the package can still create an object with New. It is a recipe in which every property has the empty value of its type: Length 0, no class enabled, empty strings, MinPerClass 0, Mode pgmRandom and RandomSource Nothing. Generate returns an empty string for it, with the error InvalidLength, until the properties are set. The presets are the way to get the defaults.
Dim o As PwGenOptions = New PwGenOptions
Debug.Print PwGen.Generate(o) = "" ' True
Debug.Print PwGenError.Code ' InvalidLength
o.Length = 6
o.HasDigits = True
Debug.Print Len(PwGen.Generate(o)) ' 6
Debug.Print o.MinPerClass ' 0
Random mode
In random mode (Mode is pgmRandom, the default) the result is a string of Length characters, each drawn from a pool. The properties of this section apply only in this mode. In wordlist mode they are not read.
The pool. If Alphabet is not empty, the pool is the different characters of the Alphabet, and the settings for classes below have no effect. Otherwise the pool is built from classes:
- A class is enabled by HasUpperCase (
AtoZ), HasLowerCase (atoz), HasDigits (0to9) and HasSpecialChars (the characters of SpecialChars). With no class enabled and an empty Alphabet, the error isNoCharset. - Each enabled class loses the characters of ExcludeChars and, when IsAmbiguousExcluded is True, the characters of AmbiguousChars. A class with no character left is an
EmptyClasserror. - The pool is all the enabled classes together with the characters of IncludeChars, after the same removals, each different character once.
Characters are compared by their character codes, so a and A are different characters. Each character is one UTF-16 code unit, so a character outside the Basic Multilingual Plane, which is written as two code units, counts as two characters of the pool, and a password can contain one half of it alone:
Dim o As PwGenOptions = PwGenOptions.Alphanumeric
o.Alphabet = ChrW(&HD83D) & ChrW(&HDE00) ' U+1F600 as a surrogate pair
o.Length = 1
Debug.Print CLng(2 ^ (PwGen.Entropy(o) / o.Length)) ' 2
Dim pw As String = PwGen.Generate(o)
Debug.Print Len(pw) ' 1
Debug.Print AscW(pw) = AscW(ChrW(&HD83D)) Or AscW(pw) = AscW(ChrW(&HDE00)) ' True
The draw. The result is built in these steps:
- When StartsWithLetter is True, the first character is drawn from the letters
AtoZandatozthat are in the pool. - For each enabled class, MinPerClass characters are drawn from that class and placed at random free positions. The first character, when it was drawn for StartsWithLetter, counts for each class it belongs to. The characters of IncludeChars belong to no class.
- Every position that is still free is filled with a character drawn from the whole pool.
- The characters are shuffled with the Fisher-Yates method. When StartsWithLetter is True, the first character stays where it is.
- When HasNoConsecutiveRepeats is True and two equal characters are next to each other, the characters are shuffled again, up to 48 times.
Each draw is one call to the recipe’s RandomSource. The constraints change how likely the results are: a password that must contain a digit is not a uniform choice among all strings of the pool. The entropy that PwGen.Entropy reports ignores this.
Dim o As PwGenOptions = PwGenOptions.Alphanumeric
Debug.Print CLng(2 ^ (PwGen.Entropy(o) / o.Length)) ' 57
o.IncludeChars = "!" ' adds a character to the pool
Debug.Print CLng(2 ^ (PwGen.Entropy(o) / o.Length)) ' 58
o.ExcludeChars = "!A" ' removes A from its class and ! from the added characters
Debug.Print CLng(2 ^ (PwGen.Entropy(o) / o.Length)) ' 56
o.ExcludeChars = ""
o.IncludeChars = "0" ' 0 is one of the ambiguous characters, so it is removed
Debug.Print CLng(2 ^ (PwGen.Entropy(o) / o.Length)) ' 57
Wordlist mode
In wordlist mode (Mode is pgmWordlist) the result is a passphrase of WordCount words, each drawn from a list and joined with WordSeparator. The properties of random mode are not read.
The list is the array in CustomWords when it has words, otherwise the built-in list that Wordlist names. When HasUniqueWords is False, each word is drawn from the whole list, so a word can appear more than once. When it is True, a word that was drawn is taken out of the list before the next draw. When Capitalize is True, the first character of each word is changed to uppercase.
Each draw is one call to the recipe’s RandomSource. The bounds are 0 and n - 1, where n is the number of words that can still be drawn: the whole list, or, with HasUniqueWords, the words that are left.
Dim o As PwGenOptions = PwGenOptions.Passphrase
Dim words(0 To 0) As String
words(0) = "tango"
o.CustomWords = words
o.WordCount = 3
o.WordSeparator = "+"
o.Capitalize = True
Debug.Print PwGen.Generate(o) ' Tango+Tango+Tango
Debug.Print PwGen.Entropy(o) ' 0
The list has one word, so every draw returns it and the entropy is 0.
Properties
Alphabet
The complete pool of characters for random mode. A String, read/write. Default: an empty string.
Syntax: options.Alphabet [ = value ]
When the value is not empty, it is the whole pool. HasUpperCase, HasLowerCase, HasDigits, HasSpecialChars, SpecialChars, IncludeChars, ExcludeChars, IsAmbiguousExcluded, AmbiguousChars and MinPerClass then have no effect. StartsWithLetter and HasNoConsecutiveRepeats still apply, to this pool. A character that appears more than once counts once, so aabbcc is a pool of three characters. The Hex preset sets this property to PWGEN_ALPHABET_HEX.
Dim o As PwGenOptions = PwGenOptions.Alphanumeric
o.Alphabet = "ABC"
o.Length = 12
Dim pw As String = PwGen.Generate(o)
Debug.Print Len(pw) ' 12
Debug.Print pw Like "*[!ABC]*" ' False
AmbiguousChars
The characters that IsAmbiguousExcluded removes. A String, read/write. Default: 0O1lI, the value of PWGEN_DEFAULT_AMBIGUOUS_CHARS.
Syntax: options.AmbiguousChars [ = value ]
Random mode only. Has no effect when Alphabet is set, or when IsAmbiguousExcluded is False.
Capitalize
Whether the first character of each word of a passphrase is changed to uppercase. A Boolean, read/write. Default: False.
Syntax: options.Capitalize [ = value ]
Wordlist mode only. The change is made with UCase$ on the first character of the word. It is not drawn, so it adds no entropy.
CustomWords
The word list to use instead of a built-in list. A String array, read/write. Default: an empty array.
Syntax: options.CustomWords [ = value( ) ]
Wordlist mode only. When the array has words, it overrides Wordlist. Assigning an empty array, or an array that was never allocated, makes the recipe use the built-in list again. The array can have any lower bound.
Assigning copies the array, and reading returns a new array that starts at index 0 and has one element for each word, so changing an array after assigning it or after reading it does not change the recipe. A list must not contain an empty string (EmptyWord), and no two words may be equal (DuplicateWord). The comparison is by character code, so alpha and Alpha are different words.
Dim o As PwGenOptions = PwGenOptions.Passphrase
Dim words(1 To 2) As String
words(1) = "red"
words(2) = "blue"
o.CustomWords = words
o.WordCount = 3
o.HasUniqueWords = True
Debug.Print PwGen.Generate(o) = "" ' True
Debug.Print PwGenError.Code ' NotEnoughWords
Dim copy() As String = o.CustomWords
Debug.Print LBound(copy) ' 0
Debug.Print UBound(copy) ' 1
ExcludeChars
Characters that are removed from every class and from IncludeChars. A String, read/write. Default: an empty string.
Syntax: options.ExcludeChars [ = value ]
Random mode only. Has no effect when Alphabet is set. Removing every character of an enabled class is an EmptyClass error.
HasDigits
Whether the digits 0 to 9 are a class of the pool. A Boolean, read/write. Default: True.
Syntax: options.HasDigits [ = value ]
Random mode only. Has no effect when Alphabet is set. The Pin preset sets it to True and disables the other classes.
HasLowerCase
Whether the letters a to z are a class of the pool. A Boolean, read/write. Default: True.
Syntax: options.HasLowerCase [ = value ]
Random mode only. Has no effect when Alphabet is set.
HasNoConsecutiveRepeats
Whether two equal characters may not be next to each other. A Boolean, read/write. Default: False.
Syntax: options.HasNoConsecutiveRepeats [ = value ]
Random mode only. The property applies to the pool that Alphabet gives, too. With a Length above 1 and a pool of one character, the error is CannotAvoidRepeats, in Entropy as well as in Generate.
The characters are drawn first, and then shuffled again, up to 48 times, until no two equal ones are neighbours. With the default pool this succeeds every time in practice.
Dim o As PwGenOptions = PwGenOptions.Alphanumeric
o.HasNoConsecutiveRepeats = True
Dim pw As String = PwGen.Generate(o)
Dim i As Long
Dim repeated As Boolean
For i = 2 To Len(pw)
If Mid$(pw, i, 1) = Mid$(pw, i - 1, 1) Then repeated = True
Next
Debug.Print repeated ' False
o.Alphabet = "A"
o.Length = 4
Debug.Print PwGen.Generate(o) = "" ' True
Debug.Print PwGenError.Code ' CannotAvoidRepeats
Note
In version 1.0.8.0 the shuffle only rearranges the characters that were drawn, and the property never draws them again. When the drawn characters cannot be arranged without a repeat, Generate returns an empty string and sets CannotAvoidRepeats, although other passwords of the same pool would be valid. With a small pool this happens often. From the pool AB and a Length of 3, the draws AAA and BBB cannot be arranged, and together they are one draw in four. With a Length of 4 the failures are about three calls in five. With StartsWithLetter the first character is not moved either, which makes the failures more frequent. A call that fails can be repeated, because the next call draws again.
Dim o As PwGenOptions = PwGenOptions.Alphanumeric
o.Alphabet = "AB"
o.Length = 3
o.HasNoConsecutiveRepeats = True
Dim i As Long
Dim failures As Long
For i = 1 To 200
If PwGen.Generate(o) = "" Then failures += 1
Next
Debug.Print failures >= 20 And failures <= 80 ' True
HasSpecialChars
Whether the characters of SpecialChars are a class of the pool. A Boolean, read/write. Default: False. The Complex preset sets it to True.
Syntax: options.HasSpecialChars [ = value ]
Random mode only. Has no effect when Alphabet is set.
HasUniqueWords
Whether the words of one passphrase must be different. A Boolean, read/write. Default: False.
Syntax: options.HasUniqueWords [ = value ]
Wordlist mode only. When True, each word is drawn from the words that are left, and a WordCount larger than the number of words in the list is a NotEnoughWords error. The entropy is then the sum of log2(n - i) over the words, and not WordCount * log2(n).
HasUpperCase
Whether the letters A to Z are a class of the pool. A Boolean, read/write. Default: True.
Syntax: options.HasUpperCase [ = value ]
Random mode only. Has no effect when Alphabet is set.
IncludeChars
Characters added to the pool besides the classes. A String, read/write. Default: an empty string.
Syntax: options.IncludeChars [ = value ]
Random mode only. Has no effect when Alphabet is set. The added characters belong to no class, so MinPerClass does not count them. They are filtered with the pool: ExcludeChars removes them, and so does IsAmbiguousExcluded for the characters of AmbiguousChars. A character that is already in the pool is not added twice.
IsAmbiguousExcluded
Whether the characters of AmbiguousChars are removed from the pool. A Boolean, read/write. Default: True. The Pin preset sets it to False.
Syntax: options.IsAmbiguousExcluded [ = value ]
Random mode only. Has no effect when Alphabet is set. With the default AmbiguousChars the pool of the three default classes has 57 characters, and 62 when this property is False.
Length
The number of characters of the password. A Long, read/write. Default: 16. The valid values run from 1 to PWGEN_MAX_LENGTH, 4096.
Syntax: options.Length [ = value ]
Random mode only. A value outside the valid range is an InvalidLength error when the recipe is used, not when the property is set. With MinPerClass, the Length must also be at least MinPerClass times the number of enabled classes (LengthTooShort).
MinPerClass
The smallest number of characters that the password has from each enabled class. A Long, read/write. Default: 1.
Syntax: options.MinPerClass [ = value ]
Random mode only. Has no effect when Alphabet is set. The value 0 turns the guarantee off. A negative value is an InvalidMinPerClass error. When the guarantee is on, Length must be at least the value times the number of enabled classes, or the error is LengthTooShort. The characters of IncludeChars belong to no class and are not counted.
With the default recipe, the password has at least one uppercase letter, one lowercase letter and one digit.
Dim o As PwGenOptions = PwGenOptions.Alphanumeric
Dim i As Long
Dim ok As Boolean = True
For i = 1 To 200
Dim pw As String = PwGen.Generate(o)
If Not (pw Like "*[A-Z]*" And pw Like "*[a-z]*" And pw Like "*[0-9]*") Then ok = False
Next
Debug.Print ok ' True
o.Length = 2
PwGen.Generate o
Debug.Print PwGenError.Code ' LengthTooShort
Mode
Whether the recipe generates a string of characters or a passphrase of words. A ePwGenMode member, read/write. Default: pgmRandom.
Syntax: options.Mode [ = value ]
The mode decides which of the other properties are read. Random mode and Wordlist mode list them. A value that is not a member of the enumeration is an InvalidMode error when the recipe is used. The Passphrase preset sets it to pgmWordlist.
RandomSource
The object that supplies the random numbers for this recipe. A PwGenRandom, read/write. Default: Nothing.
Syntax: options.RandomSource [ = value ]
The property is assigned with Set. Nothing means the package’s own source, PwGenCryptoSource, which calls the Windows CNG function BCryptGenRandom. Any object that implements PwGenRandom can be assigned. Clone copies the reference, not the object.
PwGen.Entropy does not use the source.
SpecialChars
The characters of the special-character class. A String, read/write. Default: !#$%&()+,-.;=@[]_{}~, the value of PWGEN_DEFAULT_SPECIAL_CHARS.
Syntax: options.SpecialChars [ = value ]
Random mode only. The class is used when HasSpecialChars is True and Alphabet is empty. A character that appears more than once counts once, and the pool and the entropy are smaller by the repeats. With an empty string, an enabled class is an EmptyClass error.
StartsWithLetter
Whether the first character must be an ASCII letter. A Boolean, read/write. Default: False.
Syntax: options.StartsWithLetter [ = value ]
Random mode only. The first character is drawn from the letters A to Z and a to z that are in the final pool. When the pool has no such letter, for example for the Pin preset, the error is CannotStartWithLetter. The property applies to the pool that Alphabet gives, too. The first character is always a letter. The other positions hold a letter only as often as the pool has them, so the results are not evenly spread over the pool.
WordCount
The number of words of a passphrase. A Long, read/write. Default: 5. The valid values run from 1 to PWGEN_MAX_WORD_COUNT, 256.
Syntax: options.WordCount [ = value ]
Wordlist mode only. A value outside the valid range is an InvalidWordCount error when the recipe is used.
Wordlist
The built-in word list to draw from. An ePwGenWordlist member, read/write. Default: pgwEnglish.
Syntax: options.Wordlist [ = value ]
Wordlist mode only. The property is not read when CustomWords has words. A value that is not a member of the enumeration is an InvalidWordlist error when the recipe is used.
WordSeparator
The string placed between the words of a passphrase. A String, read/write. Default: -.
Syntax: options.WordSeparator [ = value ]
Wordlist mode only. The separator can be any string, including an empty one. It adds no entropy.
Presets
Each preset is a read-only property of the predeclared object. Every call returns a new recipe, which the caller can change without affecting any other. All the presets start from Alphanumeric, which sets the properties to the defaults given above, so a property that a preset does not mention has its default.
Alphanumeric
Returns a new recipe for a password of letters and digits.
Syntax: options = PwGenOptions.Alphanumeric
The recipe has a Length of 16, the classes uppercase, lowercase and digits, no special characters, and the five look-alikes 0O1lI removed, which leaves 57 characters. MinPerClass is 1. Its entropy is about 93.3 bits.
Complex
Returns a new recipe like Alphanumeric, with the special characters added.
Syntax: options = PwGenOptions.Complex
HasSpecialChars is True, which adds the 20 characters of SpecialChars and makes 77 characters. The password has at least one of each of the four classes. Its entropy is about 100.3 bits.
Hex
Returns a new recipe for a lowercase hexadecimal token.
Syntax: options = PwGenOptions.Hex
The Alphabet is PWGEN_ALPHABET_HEX, 0123456789abcdef, and the Length is 32. Its entropy is 128 bits.
Passphrase
Returns a new recipe for a passphrase of five English words.
Syntax: options = PwGenOptions.Passphrase
The Mode is pgmWordlist. The other properties of wordlist mode have their defaults: 5 words from the English list, joined by -, drawn with replacement, in lowercase. Its entropy is 65 bits.
Pin
Returns a new recipe for a PIN of six digits.
Syntax: options = PwGenOptions.Pin
The Length is 6, and the only class is the digits. IsAmbiguousExcluded is False, so 0 and 1 are used. Its entropy is about 19.9 bits.
Dim o As PwGenOptions = PwGenOptions.Pin
Dim pw As String = PwGen.Generate(o)
Debug.Print Len(pw) ' 6
Debug.Print pw Like "*[!0-9]*" ' False
Methods
Clone
Returns an independent copy of a recipe.
Syntax: options.Clone ( ) As PwGenOptions
Returns a new PwGenOptions with the same value in each property. Changing the copy does not change the original. The CustomWords array is copied, and the RandomSource is not: the copy refers to the same object.
Dim a As PwGenOptions = PwGenOptions.Alphanumeric
a.Length = 20
Dim b As PwGenOptions = a.Clone()
b.Length = 8
Debug.Print a.Length ' 20
Debug.Print b.Length ' 8
See Also
- PwGen module – generates results from a recipe and reports its entropy
- PwGenError class – reports why a recipe could not be used
- PwGenRandom interface – the type of the random source
- tsPwGen Package – the package