mirror of
https://codeberg.org/scip/valpass.git
synced 2025-12-16 12:11:00 +01:00
204 lines
7.0 KiB
Markdown
204 lines
7.0 KiB
Markdown
[](https://ci.codeberg.org/repos/15685)
|
|
[](https://goreportcard.com/report/codeberg.org/scip/valpass)
|
|
[](https://codeberg.org/scip/valpass/raw/branch/main/LICENSE)
|
|
[](https://godoc.org/codeberg.org/scip/valpass)
|
|
|
|
# valpass - a small golang module to verify passwords
|
|
|
|
## Background
|
|
|
|
A decade ago I designed an encryption algorithm
|
|
just for fun and to learn more about cryptography.
|
|
During development I wrote a little helper tool
|
|
which I could use to verify some quality metrics
|
|
of my algorithm:
|
|
[analyze.c](https://codeberg.org/scip/twenty4/blob/master/analyze/analyze.c).
|
|
|
|
This module is a re-implementation of this code
|
|
with go as a reusable module.
|
|
|
|
## Features
|
|
|
|
- standalone module without external dependencies
|
|
- uses 5 different metrics to measure password quality
|
|
- you can configure which metric to use
|
|
- you can also configure the quality thresholds
|
|
- there's support for dictionary lookup, but you need to provide the dictionary yourself
|
|
- it's reasonably fast
|
|
- the code is small enough to just copy it into your code
|
|
|
|
## Quality metrics
|
|
|
|

|
|
|
|
A good password is easy to remember and hard
|
|
to guess. Don't be fooled by those "use special characters"
|
|
evangelists: diceware passwords as outlined in the
|
|
well known xkcd comic are by far the best ones.
|
|
|
|
However, if it's your job to implement a registration
|
|
user interface, then sooner or later you'll need
|
|
to validate passwords.
|
|
|
|
This module can be used for this job.
|
|
|
|
By default it checks 3 metrics:
|
|
|
|
### Entropy
|
|
|
|
Entropy in this case measures the cryptographic
|
|
strength of the password. In non-technical words:
|
|
it checks how scrambled the password looks or how
|
|
many different bits it uses.
|
|
|
|
We only look for printable US-ASCII characters.
|
|
|
|
### Character diffusion
|
|
|
|
Of course just measuring entropy is insufficient. For
|
|
instance a password `12345678` consists of 8 different
|
|
characters and might pass the entropy check. However, as
|
|
can be easily seen, the characters are sorted and
|
|
therefore this password would be a terrible one.
|
|
|
|
Thus, character diffusion measures how characters are
|
|
distributed.
|
|
|
|
Keep in mind that these two metrics would flag
|
|
the `Tr0ub4dor&3` password of the comic as pretty good,
|
|
while in reality it's not! You might remedy
|
|
this problem with a longer mandatory password
|
|
length. But the harsh reality is that people still
|
|
use such passwords.
|
|
|
|
### Compression
|
|
|
|
We go one step further and also measure how much
|
|
the password can be compressed. For instance, let's
|
|
look at this run length encoding example:
|
|
|
|
The string `aaabggthhhh` can be rle encoded to
|
|
`2ab2gt4h`. The result is shorter than the original, it is compressed.
|
|
The ideal password cannot be compressed
|
|
or not much.
|
|
|
|
Of course we do not use RLE. We measure compression
|
|
using the [Flate algorithm](
|
|
https://en.m.wikipedia.org/wiki/Deflate).
|
|
|
|
### Optional: dictionary check
|
|
|
|
You can supply a dictionary of words of your
|
|
liking and check if the password under test
|
|
matches one of the words. Submatches can also
|
|
be done.
|
|
|
|
### Custom measurements
|
|
|
|
You can also enable or disable certain metrics and
|
|
you can tune the quality thresholds as needed.
|
|
|
|
### Future/ ToDo
|
|
|
|
- checksum test using supplied checksum list, e.g. of leaked passwords
|
|
- fuzzy testing against dictionary to catch variations, using
|
|
Levenshtein or something similar.
|
|
|
|
|
|
## Usage
|
|
|
|
Usage is pretty simple. First, import the module:
|
|
|
|
```default
|
|
go get codeberg.org/scip/valpass/v2
|
|
```
|
|
|
|
```go
|
|
import "codeberg.org/scip/valpass/v2"
|
|
|
|
[..]
|
|
res, err := valpass.Validate("password"); if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if !res.Ok {
|
|
log.Fatal("Password is unsecure!")
|
|
}
|
|
[..]
|
|
```
|
|
|
|
You may also tune which tests you want to execute and with wich
|
|
parameters. To do this, just supply a second argument, which must be a
|
|
`valpas.Options` struct:
|
|
|
|
```go
|
|
type Options struct {
|
|
Compress int // minimum compression rate in percent, default 10%
|
|
CharDistribution float64 // minimum character distribution in percent, default 10%
|
|
Entropy float64 // minimum entropy value in bits/char, default 3 bits/s
|
|
Dictionary *Dictionary // lookup given dictionary, the caller has to provide it
|
|
}
|
|
```
|
|
|
|
To turn off a test, just set the tunable to zero.
|
|
|
|
Please take a look at [the
|
|
example](https://codeberg.org/scip/valpass/raw/branch/main/example/test.go)
|
|
or at [the unit tests](https://codeberg.org/scip/valpass/raw/branch/main/lib_test.go).
|
|
|
|
## Performance
|
|
|
|
Benchmark results of version 0.0.1:
|
|
|
|
```default
|
|
% go test -bench=. -count 5
|
|
goos: linux
|
|
goarch: amd64
|
|
pkg: codeberg.org/scip/valpass
|
|
cpu: Intel(R) Core(TM) i7-10610U CPU @ 1.80GHz
|
|
BenchmarkValidateEntropy-8 98703 12402 ns/op
|
|
BenchmarkValidateEntropy-8 92745 12258 ns/op
|
|
BenchmarkValidateEntropy-8 94020 12495 ns/op
|
|
BenchmarkValidateEntropy-8 96747 12349 ns/op
|
|
BenchmarkValidateEntropy-8 94790 12368 ns/op
|
|
BenchmarkValidateCharDist-8 95610 12184 ns/op
|
|
BenchmarkValidateCharDist-8 96631 12305 ns/op
|
|
BenchmarkValidateCharDist-8 97537 12215 ns/op
|
|
BenchmarkValidateCharDist-8 97544 13703 ns/op
|
|
BenchmarkValidateCharDist-8 95139 15392 ns/op
|
|
BenchmarkValidateCompress-8 2140 636274 ns/op
|
|
BenchmarkValidateCompress-8 5883 204162 ns/op
|
|
BenchmarkValidateCompress-8 5341 229536 ns/op
|
|
BenchmarkValidateCompress-8 4590 221610 ns/op
|
|
BenchmarkValidateCompress-8 5889 186709 ns/op
|
|
BenchmarkValidateDict-8 81 13730450 ns/op
|
|
BenchmarkValidateDict-8 78 16081013 ns/op
|
|
BenchmarkValidateDict-8 74 17545981 ns/op
|
|
BenchmarkValidateDict-8 92 12830625 ns/op
|
|
BenchmarkValidateDict-8 94 12564205 ns/op
|
|
BenchmarkValidateAll-8 5084 200770 ns/op
|
|
BenchmarkValidateAll-8 6054 193329 ns/op
|
|
BenchmarkValidateAll-8 5998 186064 ns/op
|
|
BenchmarkValidateAll-8 5996 191017 ns/op
|
|
BenchmarkValidateAll-8 6268 173846 ns/op
|
|
BenchmarkValidateAllwDict-8 374 3054042 ns/op
|
|
BenchmarkValidateAllwDict-8 390 3109049 ns/op
|
|
BenchmarkValidateAllwDict-8 404 3022698 ns/op
|
|
BenchmarkValidateAllwDict-8 393 3075163 ns/op
|
|
BenchmarkValidateAllwDict-8 381 3112361 ns/op
|
|
PASS
|
|
ok codeberg.org/scip/valpass 54.017s
|
|
```
|
|
|
|
## License
|
|
|
|
This module is licensed under the BSD license.
|
|
|
|
## Prior art
|
|
|
|
|
|
[go-password](https://github.com/wagslane/go-password-validator) provides similar
|
|
functionality and it's stable and battle tested.
|
|
However ir only measures the character entropy.
|
|
|