M cmd/moac-pwgen/main.go => cmd/moac-pwgen/main.go +4 -1
@@ 29,6 29,7 @@ OPTIONS:
-P <power> Power available to the computer (W)
-T <temperature> Temperature of the system (K)
-t <time> Time limit for brute-force attack (s)
+ -G <guesses> Guesses per second in a brute-force attack
-l <length> minimum generated password length; can override (increase) -s
-L <length> maximum generated password length; can override (decrease) -s
`
@@ 71,6 72,8 @@ func parseOpts(
givens.Temperature, err = strconv.ParseFloat(opt.Value, 64)
case 't':
givens.Time, err = strconv.ParseFloat(opt.Value, 64)
+ case 'G':
+ givens.GuessesPerSecond, err = strconv.ParseFloat(opt.Value, 64)
case 'l':
minLen64, err = strconv.ParseInt(opt.Value, 10, 32)
case 'L':
@@ 116,7 119,7 @@ func main() {
}
func main1() int {
- opts, optind, err := getopt.Getopts(os.Args, "hvqre:s:m:g:P:T:t:l:L:")
+ opts, optind, err := getopt.Getopts(os.Args, "hvqre:s:m:g:P:T:t:G:l:L:")
if !cli.DisplayErr(err, usage) {
return 1
}
M cmd/moac-pwgen/testdata/scripts/fail.txt => cmd/moac-pwgen/testdata/scripts/fail.txt +4 -0
@@ 37,3 37,7 @@ stderr 'moac: bad GenPW param: lengths and entropies cannot be negative: bad val
! moac-pwgen -l 4 -L 4 -s -128
stderr 'moac: bad GenPW param: lengths and entropies cannot be negative: bad value -128 is below 0'
! stdout '.'
+
+! moac-pwgen -s 128 -G -1e18 -t 3.1536e8
+stderr 'moac: cannot compute MinEntropy: invalid givens: physical values cannot be negative: bad value -1e\+18 is below 0'
+! stdout '.'
M cmd/moac-pwgen/testdata/scripts/success.txt => cmd/moac-pwgen/testdata/scripts/success.txt +6 -0
@@ 72,3 72,9 @@ stdout '^....................$'
moac-pwgen -L 8 ascii latin
! stderr '.'
stdout '^........$'
+
+# actually somewhat realistic scenario: crack in 10 years
+# folding@home managed to hit around 2.5 exaflops once; let's try 10 exaflops
+moac-pwgen -t 3.1536e8 -G 1e18
+! stderr '.'
+stdout '^..............$'
M cmd/moac/main.go => cmd/moac/main.go +4 -1
@@ 30,6 30,7 @@ OPTIONS:
-P <power> Power available to the computer (W)
-T <temperature> Temperature of the system (K)
-t <time> Time limit for brute-force attack (s)
+ -G <guesses> Guesses per second in a brute-force attack
-p <password> Password to analyze; use "-" for stdin
COMMANDS:
@@ 80,6 81,8 @@ func parseOpts(
givens.Temperature, err = strconv.ParseFloat(opt.Value, 64)
case 't':
givens.Time, err = strconv.ParseFloat(opt.Value, 64)
+ case 'G':
+ givens.GuessesPerSecond, err = strconv.ParseFloat(opt.Value, 64)
case 'p':
givens.Password = opt.Value
}
@@ 141,7 144,7 @@ func main1() int {
}
func getOutput() (output float64, exitEarly bool, err error) {
- opts, optind, err := getopt.Getopts(os.Args, "hvqre:s:m:g:P:T:t:p:")
+ opts, optind, err := getopt.Getopts(os.Args, "hvqre:s:m:g:P:T:t:G:p:")
if err != nil {
return output, exitEarly, fmt.Errorf("%w\n%s", err, usage)
}
M cmd/moac/testdata/scripts/fail.txt => cmd/moac/testdata/scripts/fail.txt +8 -0
@@ 22,6 22,14 @@ stderr 'moac: not enough given values: missing password'
stderr 'moac: bad arguments: unknown command ascii'
! stdout '.'
+! moac -q ascii
+stderr 'moac: bad arguments: unknown command ascii'
+! stdout '.'
+
! moac -m -8
stderr 'moac.*BruteForceability.*negative'
! stdout '.'
+
+! moac -s 128 -G -1e18 -t 3.1536e8
+stderr 'moac: cannot compute BruteForceability: invalid givens: physical values cannot be negative: bad value -1e\+18 is below 0'
+! stdout '.'
M cmd/moac/testdata/scripts/success.txt => cmd/moac/testdata/scripts/success.txt +6 -0
@@ 50,3 50,9 @@ stdout '^6\.1e\+29$'
moac -t 1.45e17 -P 3.828e26 -T 1.5e7 -qs 396 strength
stdout '^0\.962$'
+
+# actually somewhat realistic scenario: crack in 10 years
+# folding@home managed to hit around 2.5 exaflops once; let's try 10 exaflops
+moac -t 3.1536e8 -s 128 -G 1e18
+! stderr '.'
+stdout '^9\.27e-13$'
M doc/moac-pwgen.1.scd => doc/moac-pwgen.1.scd +5 -0
@@ 30,6 30,7 @@ moac-pwgen [OPTIONS...] [CHARSETS...]
*-m* <mass>
Mass at attacker's disposal (kg). Used to compute mass-energy. Overrides the
value of *-e* if the computed mass-energy is lower.
+ Mass can also be used to calculate Bremermann's Limit.
*-g* <energy>
Energy used per guess (J).
@@ 46,6 47,10 @@ moac-pwgen [OPTIONS...] [CHARSETS...]
*-t* <time>
Time limit for brute-force attack (s).
+*-G* <guesses>
+ Guesses-per-second in a brute-force attack. Overridden by values computed from
+ Bremermann's Limit and the Landauer limit if those are smaller or if unset.
+
*-l* <length>
Minimum number of characters in generated password. Overrides value of *-s* if
doing so would increase password length.
M doc/moac.1.scd => doc/moac.1.scd +8 -1
@@ 34,6 34,7 @@ moac [OPTIONS...] [COMMAND]
*-m* <mass>
Mass at attacker's disposal (kg). Used to compute mass-energy. Overrides the
value of *-e* if the computed mass-energy is lower.
+ Mass can also be used to calculate Bremermann's Limit.
*-g* <energy>
Energy used per guess (J).
@@ 50,6 51,10 @@ moac [OPTIONS...] [COMMAND]
*-t* <time>
Time limit for brute-force attack (s).
+*-G* <guesses>
+ Guesses-per-second in a brute-force attack. Overridden by values computed from
+ Bremermann's Limit and the Landauer limit if those are smaller or if unset.
+
*-p* <password>
Password to analyze. Use '-' to read the password from stdin.
@@ 68,7 73,9 @@ values, *moac* uses the following default values:
*energy per guess*: Landauer limit.
-*guesses per second*: product of Bremermann's Limit and mass.
+*guesses per second*: product of Bremermann's Limit and mass. If *-P* is
+specified and the quotient of power and the Landauer limit is smaller, set it to
+that instead.
*temperature*: 2.7 K, a low estimate for the temperature of cosmic background
radiation.
M givens.go => givens.go +7 -6
@@ 88,9 88,10 @@ func (givens *Givens) validate() error {
return fmt.Errorf("invalid temperature: %w", err)
}
- if err := bounds.NonNegative(
- givens.Energy, givens.Mass, givens.Power, givens.Time); err != nil {
- return fmt.Errorf("physical values can't be negative: %w", err)
+ err := bounds.NonNegative(
+ givens.Energy, givens.Mass, givens.Power, givens.Time, givens.GuessesPerSecond, givens.EnergyPerGuess)
+ if err != nil {
+ return fmt.Errorf("physical values cannot be negative: %w", err)
}
return nil
@@ 121,7 122,7 @@ func (givens *Givens) Populate() error {
// can be brute-forced with certainty.
func (givens *Givens) BruteForceability() (float64, error) {
if err := givens.Populate(); err != nil {
- return 0, fmt.Errorf("can't compute BruteForceability: %w", err)
+ return 0, fmt.Errorf("cannot compute BruteForceability: %w", err)
}
if givens.Entropy+givens.Time == 0 {
@@ 135,7 136,7 @@ func (givens *Givens) BruteForceability() (float64, error) {
// quantum computers that use Grover's Algorithm.
func (givens *Givens) BruteForceabilityQuantum() (float64, error) {
if err := givens.Populate(); err != nil {
- return 0, fmt.Errorf("can't calculate BruteForceabilityQuantum: %w", err)
+ return 0, fmt.Errorf("cannot calculate BruteForceabilityQuantum: %w", err)
}
givensQuantum := givens
@@ 165,7 166,7 @@ func computeBruteForceability(givens *Givens) float64 {
// Passwords need an entropy greater than this to have a chance of not being guessed.
func (givens *Givens) MinEntropy() (entropyNeeded float64, err error) {
if err := givens.Populate(); err != nil {
- return 0, fmt.Errorf("can't compute MinEntropy: %w", err)
+ return 0, fmt.Errorf("cannot compute MinEntropy: %w", err)
}
energyBound := math.Log2(givens.Energy / givens.EnergyPerGuess)
M givens_test.go => givens_test.go +9 -0
@@ 151,6 151,15 @@ func givensTestCases() []givensTestCase { //nolint:funlen // single statement; l
expectedErrME: bounds.ErrImpossibleNegative,
},
{
+ name: "negativeGPS",
+ given: moac.Givens{
+ Energy: 4.0e52,
+ GuessesPerSecond: -1.0e4,
+ },
+ expectedErrBF: bounds.ErrImpossibleNegative,
+ expectedErrME: bounds.ErrImpossibleNegative,
+ },
+ {
name: "Mising energy, mass",
given: moac.Givens{},
expectedBFQ: 0,
M pwgen/indexing.go => pwgen/indexing.go +1 -1
@@ 13,7 13,7 @@ import (
func randInt(max int) int {
newInt, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
if err != nil {
- log.Panicf("can't generate passwords: crypto/rand unavailable: %v", err)
+ log.Panicf("cannot generate passwords: crypto/rand unavailable: %v", err)
}
return int(newInt.Int64())
M pwgen/nocrand_test.go => pwgen/nocrand_test.go +1 -1
@@ 27,7 27,7 @@ func shouldPanic(t *testing.T) {
defer func() {
rand.Reader = csprng
- if out := recover(); out != "can't generate passwords: crypto/rand unavailable: EOF" {
+ if out := recover(); out != "cannot generate passwords: crypto/rand unavailable: EOF" {
t.Errorf("panic due to CSPRNG unavailability sent unexpected message: %v", out)
}
}()