File: crunchy_test.go

package info (click to toggle)
golang-github-muesli-crunchy 0.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 128 kB
  • sloc: makefile: 2
file content (129 lines) | stat: -rw-r--r-- 3,272 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * crunchy - find common flaws in passwords
 *     Copyright (c) 2017-2018, Christian Muehlhaeuser <muesli@gmail.com>
 *
 *   For license see LICENSE
 */

package crunchy

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"hash"
	"strconv"
	"testing"
)

var (
	pws = []struct {
		pw       string
		expected error
		rating   uint
	}{
		// valid passwords
		{"d1924ce3d0510b2b2b4604c99453e2e1", nil, 100},
		{"aCgIknPv", nil, 40},
		{"1347902586", nil, 37},
		{"aEc!1Edek?", nil, 71},
		{"aEc!1Edek?f", nil, 77},
		{"aEc!1Edek?f_", nil, 91},
		{"aEc!1Edek?f_0", nil, 100},

		// invalid passwords
		{"", ErrEmpty, 0},
		{" ", ErrEmpty, 0},
		{"crunchy", ErrTooShort, 0},
		{"aaaaaaaa", ErrTooFewChars, 0},
		{"aabbccdd", ErrTooFewChars, 0},
		{"aAbBcCdD", ErrTooFewChars, 0},
		{"12345678", ErrTooSystematic, 0},
		{"87654321", ErrTooSystematic, 0},
		{"abcdefgh", ErrTooSystematic, 0},
		{"hgfedcba", ErrTooSystematic, 0},

		// haveibeenpwnd
		{"Qwertyuiop", ErrFoundHIBP, 0},

		{"password", ErrDictionary, 0},
		{"intoxicate", ErrDictionary, 0},
		{"p@ssw0rd", ErrMangledDictionary, 0},    // dictionary with mangling
		{"!pass@word?", ErrMangledDictionary, 0}, // dictionary with mangling
		{"drowssap", ErrMangledDictionary, 0},    // reversed dictionary
		{"?drow@ssap!", ErrMangledDictionary, 0}, // reversed dictionary with mangling

		// md5 dictionary lookup
		{"5f4dcc3b5aa765d61d8327deb882cf99", ErrHashedDictionary, 0},
		// sha1 dictionary lookup
		{"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", ErrHashedDictionary, 0},
		// sha256 dictionary lookup
		{"5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8", ErrHashedDictionary, 0},
		// sha512 dictionary lookup
		{"b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86", ErrHashedDictionary, 0},
	}
)

func TestValidator(t *testing.T) {
	v := NewValidator()

	pw := "crunchy"
	err := v.Check(pw)
	if err == nil {
		t.Errorf("Expected %v for password '%s', got nil", ErrTooShort, pw)
	}
}

func TestRatePassword(t *testing.T) {
	v := NewValidatorWithOpts(Options{
		MinDist:        -1,
		Hashers:        []hash.Hash{md5.New(), sha1.New(), sha256.New(), sha512.New()},
		DictionaryPath: "/usr/share/dict",
	})

	for _, pw := range pws {
		if pw.expected == ErrFoundHIBP {
			continue
		}
		r, err := v.Rate(pw.pw)
		if dicterr, ok := err.(*DictionaryError); ok {
			err = dicterr.Err
		} else if hasherr, ok := err.(*HashedDictionaryError); ok {
			err = hasherr.Err
		}

		if r != pw.rating {
			t.Errorf("Expected rating %d for password '%s', got %d", pw.rating, pw.pw, r)
		}
		if err != pw.expected {
			t.Errorf("Expected %v for password '%s', got %v", pw.expected, pw.pw, err)
		}
	}
}

//func TestCheckHIBP(t *testing.T) {
//	v := NewValidatorWithOpts(Options{
//		CheckHIBP: true,
//	})
//
//	for _, pw := range pws {
//		if pw.expected != ErrFoundHIBP {
//			continue
//		}
//
//		er := v.Check(pw.pw)
//		if er != pw.expected {
//			t.Errorf("Expected %v for password '%s', got %v", pw.expected, pw.pw, er)
//		}
//	}
//}

func BenchmarkValidatePassword(b *testing.B) {
	v := NewValidator()
	s := hashsum(strconv.Itoa(b.N), md5.New())

	for n := 0; n < b.N; n++ {
		_ = v.Check(s)
	}
}