forked from nremond/pbkdf2-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSecureHash.scala
91 lines (81 loc) · 3.05 KB
/
SecureHash.scala
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
/**
* Copyright 2012-2014 Nicolas Rémond (@nremond)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.nremond.legacy
import java.nio.charset.StandardCharsets.UTF_8
import java.security.SecureRandom
import java.util.Arrays
import io.github.nremond._
import scala.annotation.tailrec
/**
* This is the legacy API.
*
* @param iterations the number of encryption iterations. Default to 120000
* @param dkLength derived-key length, default to 32
* @param cryptoAlgo HMAC+SHA512 is the default as HMAC+SHA1 is now considered weak
*/
case class SecureHash(iterations: Int = 120000, dkLength: Int = 32, cryptoAlgo: String = "HmacSHA512") {
/**
* Creates a hashed password using [[PBKDF2]]
*
* this function output a string in the following format:
*
* salt:key
*
* - salt : hex encoded salt
*
* - key : hex encoded derived key
*
* Example :
*
* a9c654289407047fd197516196e14b97bdabfa4bc934d0e9:f2f458b2502ca7595a4c964b14f146bd9c49174fa41b625227602bf4aaffbf5e
*
* @param password the password to hash
*/
@deprecated("Only create password via io.github.nremond.SecureHash", "pbkdf2-scala 0.5")
def createHash(password: String): String = {
val random = new SecureRandom
val salt = new Array[Byte](24) //192 bits
random.nextBytes(salt)
val hash = PBKDF2(password.getBytes(UTF_8), salt, iterations, dkLength, cryptoAlgo)
raw"${toHex(salt)}:${toHex(hash)}"
}
/**
* Validate a password against a password hash
*
* this function will first try to validate with the *new* format as generated
* by [[io.github.nremond.SecureHash.createHash]]. if it fails, it will fall back to the *old* format,
* making this function very useful when transitioning form the *old* to the *new* format.
*
* @param password the password to validate
* @param hashedPassword the password hash.
* @return true is the password is valid
*/
def validatePassword(password: String, hashedPassword: String): Boolean =
//Try new format first and then fall back to legacy
if (io.github.nremond.SecureHash.validatePassword(password, hashedPassword))
true
else
legacyValidatePassword(password, hashedPassword)
private[this] def legacyValidatePassword(password: String, hashedPassword: String): Boolean = {
val params = hashedPassword.split(":")
if (params.size == 2) {
val salt = fromHex(params(0))
val hash = PBKDF2(password.getBytes(UTF_8), salt, iterations, dkLength, cryptoAlgo)
Arrays.equals(fromHex(params(1)), hash)
} else
false
}
}