@@ -35,7 +35,7 @@ type PasswordVerifier interface {
35
35
// PasswordHashAlgorithms are named PasswordSaltHashers with a default verifier and hash function
36
36
type PasswordHashAlgorithm struct {
37
37
PasswordSaltHasher
38
- Name string
38
+ Specification string // The specification that is used to create the internal PasswordSaltHasher
39
39
}
40
40
41
41
// Hash the provided password with the salt and return the hash
@@ -61,16 +61,16 @@ func (algorithm *PasswordHashAlgorithm) Hash(password, salt string) (string, err
61
61
62
62
// Verify the provided password matches the hashPassword when hashed with the salt
63
63
func (algorithm * PasswordHashAlgorithm ) VerifyPassword (providedPassword , hashedPassword , salt string ) bool {
64
- // The bcrypt package has its own specialized compare function that takes into
65
- // account the stored password's bcrypt parameters.
64
+ // Some PasswordSaltHashers have their own specialised compare function that takes into
65
+ // account the stored parameters within the hash. e.g. bcrypt
66
66
if verifier , ok := algorithm .PasswordSaltHasher .(PasswordVerifier ); ok {
67
67
return verifier .VerifyPassword (providedPassword , hashedPassword , salt )
68
68
}
69
69
70
70
// Compute the hash of the password.
71
71
providedPasswordHash , err := algorithm .Hash (providedPassword , salt )
72
72
if err != nil {
73
- log .Error ("passwordhash: %v.Hash(): %v" , algorithm .Name , err )
73
+ log .Error ("passwordhash: %v.Hash(): %v" , algorithm .Specification , err )
74
74
return false
75
75
}
76
76
84
84
)
85
85
86
86
// Register registers a PasswordSaltHasher with the availableHasherFactories
87
- // This is not thread safe.
87
+ // Caution: This is not thread safe.
88
88
func Register [T PasswordSaltHasher ](name string , newFn func (config string ) T ) {
89
89
if _ , has := availableHasherFactories [name ]; has {
90
90
panic (fmt .Errorf ("duplicate registration of password salt hasher: %s" , name ))
@@ -96,49 +96,82 @@ func Register[T PasswordSaltHasher](name string, newFn func(config string) T) {
96
96
}
97
97
}
98
98
99
- // In early versions of gitea the password hash algorithm field could be empty
100
- // At that point the default was `pbkdf2` without configuration values
101
- // Please note this is not the same as the DefaultAlgorithm
102
- const defaultEmptyHashAlgorithmName = "pbkdf2"
103
-
104
- func Parse (algorithm string ) * PasswordHashAlgorithm {
105
- if algorithm == "" {
106
- algorithm = defaultEmptyHashAlgorithmName
99
+ // In early versions of gitea the password hash algorithm field of a user could be
100
+ // empty. At that point the default was `pbkdf2` without configuration values
101
+ //
102
+ // Please note this is not the same as the DefaultAlgorithm which is used
103
+ // to determine what an empty PASSWORD_HASH_ALGO setting in the app.ini means.
104
+ // These are not the same even if they have the same apparent value and they mean different things.
105
+ //
106
+ // DO NOT COALESCE THESE VALUES
107
+ const defaultEmptyHashAlgorithmSpecification = "pbkdf2"
108
+
109
+ // Parse will convert the provided algorithm specification in to a PasswordHashAlgorithm
110
+ // If the provided specification matches the DefaultHashAlgorithm Specification it will be
111
+ // used.
112
+ // In addition the last non-default hasher will be cached to help reduce the load from
113
+ // parsing specifications.
114
+ //
115
+ // NOTE: No de-aliasing is done in this function, thus any specification which does not
116
+ // contain a configuration will use the default values for that hasher. These are not
117
+ // necessarily the same values as those obtained by dealiasing. This allows for
118
+ // seamless backwards compatibility with the original configuration.
119
+ //
120
+ // To further labour this point, running `Parse("pbkdf2")` does not obtain the
121
+ // same algorithm as setting `PASSWORD_HASH_ALGO=pbkdf2` in app.ini, nor is it intended to.
122
+ // A user that has `password_hash_algo='pbkdf2'` in the db means get the original, unconfigured algorithm
123
+ // Users will be migrated automatically as they log-in to have the complete specification stored
124
+ // in their `password_hash_algo` fields by other code.
125
+ func Parse (algorithmSpec string ) * PasswordHashAlgorithm {
126
+ if algorithmSpec == "" {
127
+ algorithmSpec = defaultEmptyHashAlgorithmSpecification
107
128
}
108
129
109
- if DefaultHashAlgorithm != nil && algorithm == DefaultHashAlgorithm .Name {
130
+ if DefaultHashAlgorithm != nil && algorithmSpec == DefaultHashAlgorithm .Specification {
110
131
return DefaultHashAlgorithm
111
132
}
112
133
113
134
ptr := lastNonDefaultAlgorithm .Load ()
114
135
if ptr != nil {
115
136
hashAlgorithm , ok := ptr .(* PasswordHashAlgorithm )
116
- if ok && hashAlgorithm .Name == algorithm {
137
+ if ok && hashAlgorithm .Specification == algorithmSpec {
117
138
return hashAlgorithm
118
139
}
119
140
}
120
141
121
- vals := strings .SplitN (algorithm , "$" , 2 )
122
- var name string
142
+ // Now convert the provided specification in to a hasherType +/- some configuration parameters
143
+ vals := strings .SplitN (algorithmSpec , "$" , 2 )
144
+ var hasherType string
123
145
var config string
146
+
124
147
if len (vals ) == 0 {
148
+ // This should not happen as algorithmSpec should not be empty
149
+ // due to it being assigned to defaultEmptyHashAlgorithmSpecification above
150
+ // but we should be absolutely cautious here
125
151
return nil
126
152
}
127
- name = vals [0 ]
153
+
154
+ hasherType = vals [0 ]
128
155
if len (vals ) > 1 {
129
156
config = vals [1 ]
130
157
}
131
- newFn , has := availableHasherFactories [name ]
158
+
159
+ newFn , has := availableHasherFactories [hasherType ]
132
160
if ! has {
161
+ // unknown hasher type
133
162
return nil
134
163
}
164
+
135
165
ph := newFn (config )
136
166
if ph == nil {
167
+ // The provided configuration is likely invalid - it will have been logged already
168
+ // but we cannot hash safely
137
169
return nil
138
170
}
171
+
139
172
hashAlgorithm := & PasswordHashAlgorithm {
140
173
PasswordSaltHasher : ph ,
141
- Name : algorithm ,
174
+ Specification : algorithmSpec ,
142
175
}
143
176
144
177
lastNonDefaultAlgorithm .Store (hashAlgorithm )
0 commit comments