generated from denorg/starter
-
Notifications
You must be signed in to change notification settings - Fork 5
/
mod.ts
111 lines (107 loc) · 3.78 KB
/
mod.ts
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
/**
* @module scrypt
* @author oplik0
* @version 4.4.4
*/
import { timingSafeEqual } from "./deps.ts";
import {
decomposeFormat,
detectFormat,
formatPHC,
formatScrypt,
logN,
scryptFormat,
ScryptParameters,
} from "./lib/format.ts";
import { to32bytes } from "./lib/helpers.ts";
import { scrypt } from "./lib/scrypt.ts";
const encoder = new TextEncoder();
/**
* Hash a password with scrypt. Outputs a string in scrypt format by default.
* @author oplik0
* @param {string} password - Password that will be hashed
* @param {ScryptParameters} [parameters] - Scrypt parameters (n, r and p) used for hashing.
* @param {scryptFormat} [format="scrypt"] - format of the result. Defaults to scrypt encrypted data format (https://github.com/Tarsnap/scrypt/blob/master/FORMAT)
* @param {number} [parameters.logN=17] - log2 of the work factor N. Must be an integer between 1 and 63. Defaults to 14 (N=16384)
* @param {number} [parameters.r=8] - Block size. Defaults to 16
* @param {number} [parameters.p=1] - Parralelism factor. Defaults to 1
* @param {string} [parameters.salt] - custom salt (by default it will be randomly generated)
* @param {number} [parameters.dklen=64] - desired key length (in bytes)
* @param {number} [parameters.N=16384] - full number of iterations if you don't like using logN (this overrides that setting). Must be a power of 2.
* @returns {string} - Hash in the chosen format (scrypt by default)
*/
export function hash(
password: string,
parameters?: ScryptParameters,
format?: scryptFormat,
): string {
format = format ? format : "scrypt";
parameters = parameters ?? {};
const N = parameters.N ?? 2 ** (parameters.logN ?? 17);
const r = parameters.r ?? 8;
const p = parameters.p ?? 1;
const salt = parameters.salt
? (format === "scrypt" ? to32bytes(parameters.salt) : parameters.salt)
: genSalt(32, "Uint8Array");
const dklen = parameters.dklen
? parameters.dklen
: format === "phc"
? 32
: 64;
const scryptResult = scrypt(
password,
salt,
N,
r,
p,
dklen,
"base64",
) as string;
switch (format) {
case "raw":
return scryptResult;
case "scrypt":
return formatScrypt(scryptResult, Math.log2(N) as logN, r, p, salt);
case "phc":
return formatPHC(scryptResult, Math.log2(N) as logN, r, p, salt);
default:
throw new Error("invalid output format");
}
}
/**
* Checks provided string against provided hash
* @author oplik0
* @param {string} password - string that will be checked against the hash
* @param {string} testedHash - hash in a compatible format (scrypt and phc formats supported for now)
* @param {scryptFormat} [format] - format of the tested hash. Will attempt to detect it automatically if it's not provided.
* @returns {boolean} result of the check, true if the password matches the hash
*/
export function verify(
password: string,
testedHash: string,
format?: scryptFormat,
): boolean {
format = format ?? detectFormat(testedHash);
const params: ScryptParameters = decomposeFormat(testedHash, format);
const newHash = hash(password, params, format);
return timingSafeEqual(encoder.encode(newHash), encoder.encode(testedHash));
}
/**
* generate random salt using Deno csprng (crypto.getRandomValues)
* @author oplik0
* @param {number} [length=16] - numebr of bytes to generate
* @param {string} [outputType] - decide if the output should be a string or Uint8Array
* @returns {string|Uint8Array} random salt
*/
export function genSalt(
length?: number,
outputType?: "string" | "Uint8Array",
): string | Uint8Array {
const array = new Uint8Array(length || 32);
const decoder = new TextDecoder();
const randomArray = crypto.getRandomValues(array);
const salt = outputType === "Uint8Array"
? randomArray
: decoder.decode(randomArray);
return salt;
}