1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 module hunt.shiro.authc.credential.HashedCredentialsMatcher;
20 
21 import hunt.shiro.authc.credential.SimpleCredentialsMatcher;
22 
23 import hunt.shiro.authc.AuthenticationInfo;
24 import hunt.shiro.authc.AuthenticationToken;
25 import hunt.shiro.authc.SaltedAuthenticationInfo;
26 import hunt.shiro.codec.Base64;
27 import hunt.shiro.codec.Hex;
28 import hunt.shiro.crypto.hash.AbstractHash;
29 import hunt.shiro.crypto.hash.Hash;
30 import hunt.shiro.crypto.hash.SimpleHash;
31 // import hunt.shiro.util.StringUtils;
32 
33 import hunt.Exceptions;
34 import std.array;
35 
36 /**
37  * A {@code HashedCredentialMatcher} provides support for hashing of supplied {@code AuthenticationToken} credentials
38  * before being compared to those in the {@code AuthenticationInfo} from the data store.
39  * <p/>
40  * Credential hashing is one of the most common security techniques when safeguarding a user's private credentials
41  * (passwords, keys, etc).  Most developers never want to store their users' credentials in plain form, viewable by
42  * anyone, so they often hash the users' credentials before they are saved in the data store.
43  * <p/>
44  * This class (and its subclasses) function as follows:
45  * <ol>
46  * <li>Hash the {@code AuthenticationToken} credentials supplied by the user during their login.</li>
47  * <li>Compare this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system
48  * (the stored account credentials are expected to already be in hashed form).</li>
49  * <li>If these two values are {@link #equals(Object, Object) equal}, the submitted credentials match, otherwise
50  * they do not.</li>
51  * </ol>
52  * <h2>Salting and Multiple Hash Iterations</h2>
53  * Because simple hashing is usually not good enough for secure applications, this class also supports 'salting'
54  * and multiple hash iterations.  Please read this excellent
55  * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a> to learn about
56  * salting and multiple iterations and why you might want to use them. (Note of sections 5
57  * &quot;Why add salt?&quot; and 6 "Hardening against the attacker's attack").   We should also note here that all of
58  * Shiro's Hash implementations (for example, {@link hunt.shiro.crypto.hash.Md5Hash Md5Hash},
59  * {@link hunt.shiro.crypto.hash.Sha1Hash Sha1Hash}, etc) support salting and multiple hash iterations via
60  * overloaded constructors.
61  * <h4>Real World Case Study</h4>
62  * In April 2010, some public Atlassian Jira and Confluence
63  * installations (Apache Software Foundation, Codehaus, etc) were the target of account attacks and user accounts
64  * were compromised.  The reason?  Jira and Confluence at the time did not salt user passwords and attackers were
65  * able to use dictionary attacks to compromise user accounts (Atlassian has since
66  * <a href="http://blogs.atlassian.com/news/2010/04/oh_man_what_a_day_an_update_on_our_security_breach.html">
67  * fixed the problem</a> of course).
68  * <p/>
69  * The lesson?
70  * <p/>
71  * <b>ALWAYS, ALWAYS, ALWAYS SALT USER PASSWORDS!</b>
72  * <p/>
73  * <h3>Salting</h3>
74  * Prior to Shiro 1.1, salts could be obtained based on the end-user submitted
75  * {@link AuthenticationToken AuthenticationToken} via the now-deprecated
76  * {@link #getSalt(hunt.shiro.authc.AuthenticationToken) getSalt(AuthenticationToken)} method.  This however
77  * could constitute a security hole since ideally salts should never be obtained based on what a user can submit.
78  * User-submitted salt mechanisms are <em>much</em> more susceptible to dictionary attacks and <b>SHOULD NOT</b> be
79  * used in secure systems.  Instead salts should ideally be a secure randomly-generated number that is generated when
80  * the user account is created.  The secure number should never be disseminated to the user and always kept private
81  * by the application.
82  * <h4>Shiro 1.1</h4>
83  * As of Shiro 1.1, it is expected that any salt used to hash the submitted credentials will be obtained from the
84  * stored account information (represented as an {@link AuthenticationInfo AuthenticationInfo} instance).  This is much
85  * more secure because the salt value remains private to the application (Shiro will never store this value).
86  * <p/>
87  * To enable this, {@code Realm}s should return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances
88  * during authentication.  {@code HashedCredentialsMatcher} implementations will then use the provided
89  * {@link hunt.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt credentialsSalt} for hashing.  To avoid
90  * security risks,
91  * <b>it is highly recommended that any existing {@code Realm} implementations that support hashed credentials are
92  * updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as soon as possible</b>.
93  * <h4>Shiro 1.0 Backwards Compatibility</h4>
94  * Because of the identified security risk, {@code Realm} implementations that support credentials hashing should
95  * be updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as
96  * soon as possible.
97  * <p/>
98  * If this is not possible for some reason, this class will retain 1.0 backwards-compatible behavior of obtaining
99  * the salt via the now-deprecated {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} method.  This
100  * method will only be invoked if a {@code Realm} <em>does not</em> return
101  * {@link SaltedAuthenticationInfo SaltedAutenticationInfo} instances and {@link #isHashSalted() hashSalted} is
102  * {@code true}.
103  * But please note that the {@link #isHashSalted() hashSalted} property and the
104  * {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} methods will be removed before the Shiro 2.0
105  * release.
106  * <h3>Multiple Hash Iterations</h3>
107  * If you hash your users' credentials multiple times before persisting to the data store, you will also need to
108  * set this class's {@link #setHashIterations(int) hashIterations} property.  See the
109  * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a>'s
110  * <a href="http://www.owasp.org/index.php/Hashing_Java#Hardening_against_the_attacker.27s_attack">
111  * &quot;Hardening against the attacker's attack&quot;</a> section to learn more about why you might want to use
112  * multiple hash iterations.
113  * <h2>MD5 &amp; SHA-1 Notice</h2>
114  * <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and
115  * <a href="http://en.wikipedia.org/wiki/SHA_hash_functions">SHA-1</a> algorithms are now known to be vulnerable to
116  * compromise and/or collisions (read the linked pages for more).  While most applications are ok with either of these
117  * two, if your application mandates high security, use the SHA-256 (or higher) hashing algorithms and their
118  * supporting {@code CredentialsMatcher} implementations.
119  *
120  * @see hunt.shiro.crypto.hash.Md5Hash
121  * @see hunt.shiro.crypto.hash.Sha1Hash
122  * @see hunt.shiro.crypto.hash.Sha256Hash
123  * @since 0.9
124  */
125 class HashedCredentialsMatcher : SimpleCredentialsMatcher {
126 
127     /**
128      */
129     private string hashAlgorithm;
130     private int hashIterations;
131     private bool hashSalted;
132     private bool storedCredentialsHexEncoded;
133 
134     /**
135      * JavaBeans-compatible no-arg constructor intended for use in IoC/Dependency Injection environments.  If you
136      * use this constructor, you <em>MUST</em> also additionally set the
137      * {@link #setHashAlgorithmName(string) hashAlgorithmName} property.
138      */
139      this() {
140         this.hashAlgorithm = null;
141         this.hashSalted = false;
142         this.hashIterations = 1;
143         this.storedCredentialsHexEncoded = true; //false means Base64-encoded
144     }
145 
146     /**
147      * Creates an instance using the specified {@link #getHashAlgorithmName() hashAlgorithmName} to hash submitted
148      * credentials.
149      * @param hashAlgorithmName the {@code Hash} {@link hunt.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName}
150      *                          to use when performing hashes for credentials matching.
151      */
152      this(string hashAlgorithmName) {
153         this();
154         if (hashAlgorithmName.empty()) {
155             throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty.");
156         }
157         this.hashAlgorithm = hashAlgorithmName;
158     }
159 
160     /**
161      * Returns the {@code Hash} {@link hunt.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
162      * when performing hashes for credentials matching.
163      *
164      * @return the {@code Hash} {@link hunt.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
165      *         when performing hashes for credentials matching.
166      */
167      string getHashAlgorithmName() {
168         return hashAlgorithm;
169     }
170 
171     /**
172      * Sets the {@code Hash} {@link hunt.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use
173      * when performing hashes for credentials matching.
174      *
175      * @param hashAlgorithmName the {@code Hash} {@link hunt.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName}
176      *                          to use when performing hashes for credentials matching.
177      */
178      void setHashAlgorithmName(string hashAlgorithmName) {
179         this.hashAlgorithm = hashAlgorithmName;
180     }
181 
182     /**
183      * Returns {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
184      * is Base64 encoded.
185      * <p/>
186      * Default value is {@code true} for convenience - all of Shiro's {@link Hash Hash#toString()}
187      * implementations return Hex encoded values by default, making this class's use with those implementations
188      * easier.
189      *
190      * @return {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it
191      *         is Base64 encoded.  Default is {@code true}
192      */
193      bool isStoredCredentialsHexEncoded() {
194         return storedCredentialsHexEncoded;
195     }
196 
197     /**
198      * Sets the indicator if this system's stored credential hash is Hex encoded or not.
199      * <p/>
200      * A value of {@code true} will cause this class to decode the system credential from Hex, a
201      * value of {@code false} will cause this class to decode the system credential from Base64.
202      * <p/>
203      * Unless overridden via this method, the default value is {@code true} for convenience - all of Shiro's
204      * {@link Hash Hash#toString()} implementations return Hex encoded values by default, making this class's use with
205      * those implementations easier.
206      *
207      * @param storedCredentialsHexEncoded the indicator if this system's stored credential hash is Hex
208      *                                    encoded or not ('not' automatically implying it is Base64 encoded).
209      */
210      void setStoredCredentialsHexEncoded(bool storedCredentialsHexEncoded) {
211         this.storedCredentialsHexEncoded = storedCredentialsHexEncoded;
212     }
213 
214     /**
215      * Returns {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
216      * {@code false} if it should not be salted.
217      * <p/>
218      * If enabled, the salt used will be obtained via the {@link #getSalt(AuthenticationToken) getSalt} method.
219      * <p/>
220      * The default value is {@code false}.
221      *
222      * @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
223      *         {@code false} if it should not be salted.
224      * deprecated("") since Shiro 1.1.  Hash salting is now expected to be based on if the {@link AuthenticationInfo}
225      *             returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
226      *             {@link hunt.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
227      *             This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
228      *             {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
229      *             that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
230      *             instances as soon as possible</b>.
231      *             <p/>
232      *             This is because salts should always be obtained from the stored account information and
233      *             never be interpreted based on user/Subject-entered data.  User-entered data is easier to compromise for
234      *             attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
235      *             are almost impossible to break.  This method will be removed in Shiro 2.0.
236      */
237     deprecated("")
238     bool isHashSalted() {
239         return hashSalted;
240     }
241 
242     /**
243      * Sets whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
244      * <p/>
245      * If enabled, the salt used will be obtained via the {@link #getSalt(hunt.shiro.authc.AuthenticationToken) getCredentialsSalt} method.
246      * </p>
247      * The default value is {@code false}.
248      *
249      * @param hashSalted whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
250      * deprecated("") since Shiro 1.1.  Hash salting is now expected to be based on if the {@link AuthenticationInfo}
251      *             returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
252      *             {@link hunt.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
253      *             This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
254      *             {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
255      *             that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
256      *             instances as soon as possible</b>.
257      *             <p/>
258      *             This is because salts should always be obtained from the stored account information and
259      *             never be interpreted based on user/Subject-entered data.  User-entered data is easier to compromise for
260      *             attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
261      *             are almost impossible to break.  This method will be removed in Shiro 2.0.
262      */
263     deprecated("")
264      void setHashSalted(bool hashSalted) {
265         this.hashSalted = hashSalted;
266     }
267 
268     /**
269      * Returns the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
270      * comparing to the credentials stored in the system.
271      * <p/>
272      * Unless overridden, the default value is {@code 1}, meaning a normal hash execution will occur.
273      *
274      * @return the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before
275      *         comparing to the credentials stored in the system.
276      */
277      int getHashIterations() {
278         return hashIterations;
279     }
280 
281     /**
282      * Sets the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before comparing
283      * to the credentials stored in the system.
284      * <p/>
285      * Unless overridden, the default value is {@code 1}, meaning a normal single hash execution will occur.
286      * <p/>
287      * If this argument is less than 1 (i.e. 0 or negative), the default value of 1 is applied.  There must always be
288      * at least 1 hash iteration (otherwise there would be no hash).
289      *
290      * @param hashIterations the number of times to hash a submitted {@code AuthenticationToken}'s credentials.
291      */
292      void setHashIterations(int hashIterations) {
293         if (hashIterations < 1) {
294             this.hashIterations = 1;
295         } else {
296             this.hashIterations = hashIterations;
297         }
298     }
299 
300     /**
301      * Returns a salt value used to hash the token's credentials.
302      * <p/>
303      * This default implementation merely returns {@code token.getPrincipal()}, effectively using the user's
304      * identity (username, user id, etc) as the salt, a most common technique.  If you wish to provide the
305      * authentication token's salt another way, you may override this method.
306      *
307      * @param token the AuthenticationToken submitted during the authentication attempt.
308      * @return a salt value to use to hash the authentication token's credentials.
309      * deprecated("") since Shiro 1.1.  Hash salting is now expected to be based on if the {@link AuthenticationInfo}
310      *             returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
311      *             {@link hunt.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
312      *             This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
313      *             {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
314      *             that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
315      *             instances as soon as possible</b>.<p/>
316      *             This is because salts should always be obtained from the stored account information and
317      *             never be interpreted based on user/Subject-entered data.  User-entered data is easier to compromise for
318      *             attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
319      *             are almost impossible to break.  This method will be removed in Shiro 2.0.
320      */
321     deprecated("")
322     protected string getSalt(AuthenticationToken token) {
323         return token.getPrincipal();
324     }
325 
326     /**
327      * Returns a {@link Hash Hash} instance representing the already-hashed AuthenticationInfo credentials stored in the system.
328      * <p/>
329      * This method reconstructs a {@link Hash Hash} instance based on a {@code info.getCredentials} call,
330      * but it does <em>not</em> hash that value - it is expected that method call will return an already-hashed value.
331      * <p/>
332      * This implementation's reconstruction effort functions as follows:
333      * <ol>
334      * <li>Convert {@code account.getCredentials()} to a byte array via the {@link #toBytes toBytes} method.
335      * <li>If {@code account.getCredentials()} was originally a string or[] char before {@code toBytes} was
336      * called, check for encoding:
337      * <li>If {@link #storedCredentialsHexEncoded storedCredentialsHexEncoded}, Hex decode that byte array, otherwise
338      * Base64 decode the byte array</li>
339      * <li>Set the[] byte array directly on the {@code Hash} implementation and return it.</li>
340      * </ol>
341      *
342      * @param info the AuthenticationInfo from which to retrieve the credentials which assumed to be in already-hashed form.
343      * @return a {@link Hash Hash} instance representing the given AuthenticationInfo's stored credentials.
344      */
345     // protected Object getCredentials(AuthenticationInfo info) {
346     //     Object credentials = info.getCredentials();
347 
348     //     byte[] storedBytes = toBytes(credentials);
349     //     auto credentialsCast = cast(string) credentials;
350     //     if (credentialsCast !is null || credentials instanceof[] char) {
351     //         //account.credentials were a[] char or string, so
352     //         //we need to do text decoding first:
353     //         if (isStoredCredentialsHexEncoded()) {
354     //             storedBytes = Hex.decode(storedBytes);
355     //         } else {
356     //             storedBytes = Base64.decode(storedBytes);
357     //         }
358     //     }
359     //     AbstractHash hash = newHashInstance();
360     //     hash.setBytes(storedBytes);
361     //     return hash;
362     // }
363 
364     /**
365      * This implementation first hashes the {@code token}'s credentials, potentially using a
366      * {@code salt} if the {@code info} argument is a
367      * {@link hunt.shiro.authc.SaltedAuthenticationInfo SaltedAuthenticationInfo}.  It then compares the hash
368      * against the {@code AuthenticationInfo}'s
369      * {@link #getCredentials(hunt.shiro.authc.AuthenticationInfo) already-hashed credentials}.  This method
370      * returns {@code true} if those two values are {@link #equals(Object, Object) equal}, {@code false} otherwise.
371      *
372      * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
373      * @param info  the {@code AuthenticationInfo} stored in the system matching the token principal
374      * @return {@code true} if the provided token credentials hash match to the stored account credentials hash,
375      *         {@code false} otherwise
376      * @since 1.1
377      */
378     override
379      bool doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
380         Object tokenHashedCredentials = hashProvidedCredentials(token, info);
381         Object accountCredentials = getCredentials(info);
382         return equals(tokenHashedCredentials, accountCredentials);
383     }
384 
385     /**
386      * Hash the provided {@code token}'s credentials using the salt stored with the account if the
387      * {@code info} instance is an {@code instanceof} {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} (see
388      * the class-level JavaDoc for why this is the preferred approach).
389      * <p/>
390      * If the {@code info} instance is <em>not</em>
391      * an {@code instanceof} {@code SaltedAuthenticationInfo}, the logic will fall back to Shiro 1.0
392      * backwards-compatible logic:  it will first check to see {@link #isHashSalted() isHashSalted} and if so, will try
393      * to acquire the salt from {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)}.  See the class-level
394      * JavaDoc for why this is not recommended.  This 'fallback' logic exists only for backwards-compatibility.
395      * {@code Realm}s should be updated as soon as possible to return {@code SaltedAuthenticationInfo} instances
396      * if account credentials salting is enabled (highly recommended for password-based systems).
397      *
398      * @param token the submitted authentication token from which its credentials will be hashed
399      * @param info  the stored account data, potentially used to acquire a salt
400      * @return the token credentials hash
401      * @since 1.1
402      */
403     protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
404         // Object salt = null;
405         // SaltedAuthenticationInfo infoCast = cast(SaltedAuthenticationInfo)info;
406         // if (infoCast !is null) {
407         //     salt = cast(Object)infoCast.getCredentialsSalt();
408         // } else {
409         //     //retain 1.0 backwards compatibility:
410         //     if (isHashSalted()) {
411         //         salt = getSalt(token);
412         //     }
413         // }
414         // return cast(Object)hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
415 
416         implementationMissing(false);
417         return null;
418     }
419 
420     /**
421      * Returns the {@link #getHashAlgorithmName() hashAlgorithmName} property, but will throw an
422      * {@link IllegalStateException} if it has not been set.
423      *
424      * @return the required {@link #getHashAlgorithmName() hashAlgorithmName} property
425      * @throws IllegalStateException if the property has not been set prior to calling this method.
426      */
427     private string assertHashAlgorithmName(){
428         string hashAlgorithmName = getHashAlgorithmName();
429         if (hashAlgorithmName  is null) {
430             string msg = "Required 'hashAlgorithmName' property has not been set.  This is required to execute " ~
431                     "the hashing algorithm.";
432             throw new IllegalStateException(msg);
433         }
434         return hashAlgorithmName;
435     }
436 
437     /**
438      * Hashes the provided credentials a total of {@code hashIterations} times, using the given salt.  The hash
439      * implementation/algorithm used is based on the {@link #getHashAlgorithmName() hashAlgorithmName} property.
440      *
441      * @param credentials    the submitted authentication token's credentials to hash
442      * @param salt           the value to salt the hash, or {@code null} if a salt will not be used.
443      * @param hashIterations the number of times to hash the credentials.  At least one hash will always occur though,
444      *                       even if this argument is 0 or negative.
445      * @return the hashed value of the provided credentials, according to the specified salt and hash iterations.
446      */
447     protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
448         string hashAlgorithmName = assertHashAlgorithmName();
449         return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
450     }
451 
452     /**
453      * Returns a new, <em>uninitialized</em> instance, without its byte array set.  Used as a utility method in the
454      * {@link SimpleCredentialsMatcher#getCredentials(hunt.shiro.authc.AuthenticationInfo) getCredentials(AuthenticationInfo)} implementation.
455      *
456      * @return a new, <em>uninitialized</em> instance, without its byte array set.
457      */
458     protected AbstractHash newHashInstance() {
459         string hashAlgorithmName = assertHashAlgorithmName();
460         return new SimpleHash(hashAlgorithmName);
461     }
462 
463 }