diff --git a/cryptipass.go b/cryptipass.go index 8637858..7e0211e 100644 --- a/cryptipass.go +++ b/cryptipass.go @@ -41,9 +41,9 @@ import ( // // If the random seed cannot be read from the crypto/rand source, the function // will log a fatal error and terminate the application. -func NewCustomInstance(tokens []string, chain_depth int) *generator { +func NewCustomInstance(tokens []string, chain_depth int) *Generator { rng := new_chacha8_rng() - g := new(generator) + g := new(Generator) g.Rng = rng g.jump_table = distill(tokens, chain_depth) g.depth = chain_depth @@ -72,15 +72,15 @@ func new_chacha8_rng() *rand.Rand { // // NewInstance is ideal for general use cases where you want to generate secure // passphrases with a focus on ease of pronunciation and memorization. -func NewInstance() *generator { - g := new(generator) +func NewInstance() *Generator { + g := new(Generator) g.Rng = new_chacha8_rng() g.jump_table = global_jump_table.jump_table g.depth = global_jump_table.depth return g } -var global_jump_table generator +var global_jump_table Generator func init() { global_jump_table.depth = 3 @@ -112,7 +112,8 @@ func init() { // // The entropy value reflects the randomness of the passphrase. The higher the entropy, // the more secure the passphrase is, making it difficult for attackers to guess. -func (g *generator) GenPassphrase(words uint64) (string, float64) { +func (g *Generator) GenPassphrase(words uint64) (string, float64) { + g.assert_ready() wordvec := []string{} total_entropy := 0.0 for range words { @@ -149,7 +150,8 @@ func (g *generator) GenPassphrase(words uint64) (string, float64) { // // The function returns the generated password or passphrase and the estimated entropy // in bits, which quantifies its strength. -func (g *generator) GenFromPattern(pattern string) (string, float64) { +func (g *Generator) GenFromPattern(pattern string) (string, float64) { + g.assert_ready() passphrase := "" entropy := 0.0 pushnext := false @@ -232,7 +234,8 @@ func (g *generator) GenFromPattern(pattern string) (string, float64) { // This function panics if the transition matrix is not initialized or // if the selection process encounters an unexpected error while choosing // the next token. -func (g *generator) GenNextToken(seed string) (string, float64) { +func (g *Generator) GenNextToken(seed string) (string, float64) { + g.assert_ready() L := min(len(seed), g.depth) tok := strings.ToLower(seed[len(seed)-L:]) for { @@ -268,7 +271,8 @@ func (g *generator) GenNextToken(seed string) (string, float64) { // // int - Word length selected from the transition matrix. // float64 - Entropy of the selected length based on its likelihood. -func (g *generator) GenWordLength() (int, float64) { +func (g *Generator) GenWordLength() (int, float64) { + g.assert_ready() if tr, ok := g.jump_table["LENGTHS"]; ok { N := g.Rng.IntN(tr.total) for i, v := range tr.counts { diff --git a/cryptipass_test.go b/cryptipass_test.go index 0dacc6d..8b64746 100644 --- a/cryptipass_test.go +++ b/cryptipass_test.go @@ -194,3 +194,13 @@ func TestGenFromPattern(t *testing.T) { t.Errorf("Expected entropy to be greater than 0, got %f", entropy) } } + +func TestPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + g := cryptipass.Generator{} //its jump-table is uninitialized + g.GenPassphrase(2) //this must panic +} diff --git a/internal.go b/internal.go index 86d6dc0..741f549 100644 --- a/internal.go +++ b/internal.go @@ -6,16 +6,25 @@ import ( "strings" ) -// generator is a structure that holds the state of the password -// generator instance. It includes a random number generator (Rng) +// Generator is a structure that holds the state of the password +// generator instance. It includes a random number Generator (Rng) // and a jump table (JumpTable) which defines character transition -// probabilities for generating secure, pronounceable passwords. -type generator struct { +// probabilities. +type Generator struct { Rng *rand.Rand // Rng remains public to allow custom RNGS jump_table map[string]distribution depth int } +func (g *Generator) isready() bool { + return g.Rng != nil && len(g.jump_table) != 0 +} +func (g *Generator) assert_ready() { + if !g.isready() { + panic("generator must be initialised using the NewIstance or NewCustomInstance function") + } +} + // distribution represents a character transition model for generating // pronounceable passwords. It encapsulates the statistical data // necessary to understand the frequency and distribution of