Skip to content

Commit

Permalink
entirely new algorithm, track entropy
Browse files Browse the repository at this point in the history
  • Loading branch information
Aizen committed Oct 2, 2024
1 parent d0eb45f commit ee464fa
Show file tree
Hide file tree
Showing 6 changed files with 19,361 additions and 108 deletions.
93 changes: 35 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,103 +1,80 @@
# CryptiPass v1.1
# Cryptipass
NOTE: **We also have a [CLI](cmd/genpw) available for non-library uses.**
Cryptipass is a Go package designed to generate secure passphrases composed of human-readable words. The passphrases are generated with a focus on both security (through entropy) and usability by combining cryptographic randomness and customizable word generation strategies.

CryptiPass is a Go library for generating high-entropy, pronounceable passphrases. It offers a secure and simple way to create human-friendly, memorable passphrases that are still cryptographically strong. Each passphrase is composed of a sequence of randomly generated words, ensuring both security and usability.
We also have a [CLI](cmd/genpw) available for non-library uses.
## Features

- **High Entropy**: CryptiPass ensures that generated passphrases exceed 21 bits of entropy per word, providing strong security against brute-force attacks.
- **Pronounceable Words**: Unlike many other random passphrase generators, CryptiPass generates words that are pronounceable, making them easier to remember and use.
- **Customizable Length**: You can specify the number of words in each passphrase, making the length customizable based on your security needs.
- **Cryptographically secure randomization**: Uses `crypto/rand` for generating random data to seed the passphrase generation process, ensuring the highest level of security.
- **Customizable passphrase length**: The number of words in the passphrase can be controlled by the user.
- **Entropy calculation**: Provides an exact evaluation of the total entropy for the generated passphrase, helping users understand the strength of their passphrase.
- **Configurable word lengths**: Words within the passphrase can vary in length, ensuring better randomness and complexity.

## Installation

To install CryptiPass, use the following command:
To use the `cryptipass` package in your project, you need to install it using Go's package management:

```bash
go get github.com/francescoalemanno/cryptipass
```

## Usage

The library exposes two main functions for passphrase generation:

### 1. `NewPassphrase(words uint64) string`

This function generates a passphrase with a specified number of randomly generated, pronounceable words. The words are separated by periods (`.`) for clarity and convenience.

#### Example:
Then import it in your Go files:

```go
package main

import (
"fmt"
"github.com/francescoalemanno/cryptipass"
)

func main() {
passphrase := cryptipass.NewPassphrase(4)
fmt.Println("Your passphrase:", passphrase)
}
import "github.com/francescoalemanno/cryptipass"
```

In this example, a passphrase with 4 words is generated, and the output might look like:

```
Your passphrase: acatorty.britayert.untente.docknost
```
## Usage

### 2. `GenMixWord() string`
### Generate a Passphrase

This function generates a single high-entropy word using a mixture of syllable lengths, ensuring that each word is pronounceable while maintaining strong randomness.
The primary function of the `cryptipass` package is to generate secure passphrases. You can generate a new passphrase using the `NewPassphrase` function. You can specify how many words you want in the passphrase, and the function returns the passphrase and its total entropy.

#### Example:
Example:

```go
package main

import (
"fmt"
"github.com/francescoalemanno/cryptipass"
"fmt"
"cryptipass"
)

func main() {
word := cryptipass.GenMixWord()
fmt.Println("Generated word:", word)
passphrase, entropy := cryptipass.NewPassphrase(5)
fmt.Printf("Passphrase: %s\n", passphrase)
fmt.Printf("Entropy: %.2f bits\n", entropy)
}
```

This will output a randomly generated, pronounceable word such as:
### Example Output:

```
Generated word: skilles
Passphrase: jesside.flyperm.aunsis.dertsy
Entropy: 97.63 bits
```

## How It Works

1. **Passphrase Generation** (`NewPassphrase`):
- The function `NewPassphrase` generates a passphrase consisting of a specified number of words. It calls `GenMixWord` repeatedly to create each word and concatenates them with periods (`.`).

2. **Word Generation** (`GenMixWord`):
- `GenMixWord` uses an entropy-certified word length distribution and creates words by selecting random starting syllables and building on them until a valid word is formed.
- This ensures that each generated word has more than 21 bits of entropy, providing strong security guarantees.
### Word Generation

3. **Pronounceability** (`GenWord`):
- The function `GenWord` ensures that the words are pronounceable by constructing them from a set of predefined syllables, avoiding consonant clusters or vowel sequences that would be difficult to pronounce.
Internally, the package uses a series of functions to generate words of varying lengths. Each word contributes a certain amount of entropy, calculated during the generation process.

## Entropy and Security
- `GenMixWord()`: Generates a random word of mixed length, returning both the word and its entropy.
- `GenWord(n int)`: Generates a word of exactly `n` characters.
- `PickLength()`: Picks a random length for a word.
- `PickNext()`: Generates the next part of a word based on the current string.

Each word generated by CryptiPass contains more than 21 bits of entropy, which means that a 4-word passphrase would have approximately 84 bits of entropy. This level of entropy makes the passphrase highly resistant to brute-force attacks, providing security even against well-resourced attackers.
## Notes

The entropy is certified based on the mixture of word lengths and the randomness of syllable generation, ensuring both unpredictability and usability.
- The package seeds the random number generator with `crypto/rand`, making it cryptographically secure. In scenarios where cryptographic security is not necessary and faster execution is preferred, the package also provides an alternative (commented out) `PCG` random number generator.
- The entropy provided in the output is a measure of how unpredictable the passphrase is. The higher the entropy, the more secure the passphrase is.

## Contributing

If you'd like to contribute to CryptiPass, feel free to fork the repository and submit pull requests. Bug reports and feature requests are also welcome.
Contributions are welcome! If you encounter any issues or have feature suggestions, feel free to open an issue or a pull request on the GitHub repository.

## License

CryptiPass is licensed under the MIT License.
This project is licensed under the MIT License.

## Contact
---

For questions, suggestions, or contributions, feel free to reach out via the repository's [issue tracker](https://github.com/francescoalemanno/cryptipass/issues).
Happy coding and stay secure with Cryptipass!
30 changes: 17 additions & 13 deletions cmd/genpw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ Once installed, you can generate passphrases by running `genpw` with the followi
Usage: genpw [flags]
Flags:
-w uint
Number of words per passphrase (default: 4)
-p uint
Number of passphrases to generate (default: 4)
number of passwords to generate (default 4)
-w uint
number of words per password (default 4)
-c run entropy certification algorithm (for developers)
```

### Example Commands
Expand All @@ -45,10 +47,12 @@ genpw
This will output something like:

```
unrorpied.harabless.unhuthe.plap
awata.ling.sturle.ealsidep
vilhong.mushste.pofroon.trount
animpic.excrushot.avervass.scattor
ENTROPY | PASSPHRASE
+++++++++++++++|++++++++++++++++++++++++++++++++
65.57 | ammud.ruffl.ummi.shing
82.76 | epavoi.hakeyer.bles.monfin
82.91 | everap.gighte.clap.baselec
85.93 | stash.laxor.surevida.dexqy
```

2. **Generate 2 passphrases with 6 words each**:
Expand All @@ -60,23 +64,23 @@ genpw -w 6 -p 2
The output might look like:

```
hemooting.thrept.denting.sagred.sanaker
movity.popic.compoll.exeminess.dretialk
ENTROPY | PASSPHRASE
+++++++++++++++|++++++++++++++++++++++++++++++++
119.97 | tubse.stnew.critedi.priu.bugger.crokess
136.65 | stedin.overjan.gifteto.overepr.comiz.unrupee
```

### Entropy Considerations

Each word generated by CryptiPass has over 21 bits of entropy. For example:
Each word generated by CryptiPass has on average more than 21 bits of entropy. For example:
- A 4-word passphrase has roughly 84 bits of entropy.
- A 6-word passphrase has approximately 126 bits of entropy.

This makes the passphrases highly secure, even against brute-force attacks.

## Certification (Debug/Development Mode)

For debugging or testing purposes, the `certify` function can be used to track the entropy of generated words over large runs. This function prints out the number of unique words and the entropy estimate after every 10,000 words generated.

To run this manually, modify the `main.go` file and call `certify()` in your Go environment.
For debugging or testing purposes, the `certify` function can be used to track the entropy of generated words over large runs, it has to be used if another vocabulary is distilled into random rules for the software.

## Contributing

Expand Down
41 changes: 35 additions & 6 deletions cmd/genpw/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,60 @@ import (
"flag"
"fmt"
"math"
"slices"
"strings"

cp "github.com/francescoalemanno/cryptipass"
)

type Passphrase struct {
F string
H float64
}

func main() {
cert := flag.Bool("c", false, "run entropy certification algorithm (for developers)")
words := flag.Uint64("w", 4, "number of words per password (20.25 bits of entropy per word)")
words := flag.Uint64("w", 4, "number of words per password")
passwords := flag.Uint64("p", 4, "number of passwords to generate")
flag.Parse()
if *cert {
certify()
}

pws := []Passphrase{}
for range *passwords {
fmt.Println(cp.NewPassphrase(*words))
F, H := cp.NewPassphrase(*words)
pws = append(pws, Passphrase{F: F, H: H})
}
slices.SortFunc(pws, func(a, b Passphrase) int {
if a.H > b.H {
return 1
} else if a.H < b.H {
return -1
}
return 0
})

fmt.Printf(" ENTROPY | PASSPHRASE\n")
fmt.Printf("+++++++++++++++|++++++++++++++++++++++++++++++++\n")
for _, p := range pws {
fmt.Printf("% 13.2f | %s\n", p.H, p.F)
}
}

func certify() {
cnt := make(map[string]int)
iN := 0
Q := 128
nominal_H := 0.0
cnt_nom_H := 0.0
for {
Q += Q / 14
for range Q {
cnt[cp.GenMixWord()]++
w, nh := cp.GenMixWord()
nominal_H += nh
cnt_nom_H++
cnt[w]++
iN++
}

Expand All @@ -41,14 +69,15 @@ func certify() {
p := (c / n)
H -= p * math.Log2(p)
}
gap := (m - 1) / (2 * n)
H += gap
H += (m - 1) / (2 * n)
fmt.Print("\r")
CH := int(2 * H)
for i := 0; i <= CH; i++ {
fmt.Print("|")
}
fmt.Print(strings.Repeat(" ", 60-CH), H)
nomH := nominal_H / cnt_nom_H
gap := nomH - H
fmt.Printf("%v | E[H]-E=%.2f E[H] = %.2f ", strings.Repeat(" ", 60-CH), gap, nomH)
if gap < 0.01 {
fmt.Print("\n")
fmt.Println(H)
Expand Down
46 changes: 19 additions & 27 deletions cryptipass.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,30 @@ func init() {
// rng = rand.New(rand.NewPCG(rng.Uint64(), rng.Uint64()))
}

func NewPassphrase(words uint64) string {
tokens := []string{}
func NewPassphrase(words uint64) (string, float64) {
wordvec := []string{}
total_entropy := 0.0
for range words {
tokens = append(tokens, GenMixWord())
tok, h := GenMixWord()
wordvec = append(wordvec, tok)
total_entropy += h
}
return strings.Join(tokens, ".")
return strings.Join(wordvec, "."), total_entropy
}

func GenMixWord() string {
n := rng.IntN(len(D))
return GenWord(D[n])
func GenMixWord() (string, float64) {
l, entropy_l := PickLength()
w, entropy_w := GenWord(l)
return w, entropy_l + entropy_w
}

func GenWord(n int) string {
repeat:
s := w2[rng.IntN(len(w2))]
for {
next := tm[s[len(s)-2:]]
if len(next) == 0 {
goto repeat
}
c := next[rng.IntN(len(next))]
if c == '@' {
break
}
s += string(c)
if len(s) > n {
goto repeat
}
func GenWord(n int) (string, float64) {
s := ""
total_entropy := 0.0
h := 0.0
for len(s) < n {
s, h = PickNext(s)
total_entropy += h
}
if len(s) < n {
goto repeat
}
return s
return s, total_entropy
}
5 changes: 4 additions & 1 deletion cryptipass_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (
)

func TestBasic(t *testing.T) {
pw := cryptipass.NewPassphrase(4)
pw, H := cryptipass.NewPassphrase(4)
if len(pw) < 15 {
t.Fatalf(`Wrong length "%s"`, pw)
}
if H < 60 {
t.Fatalf(`Wrong entropy "%s"`, pw)
}
}
19,254 changes: 19,251 additions & 3 deletions markovchain.go

Large diffs are not rendered by default.

0 comments on commit ee464fa

Please sign in to comment.