@sequencemedia/pbkdf2 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.config.cjs +34 -0
- package/config/compare.mjs +19 -0
- package/index.d.ts +5 -0
- package/index.mjs +9 -0
- package/package.json +51 -0
- package/scripts/compare.mjs +48 -0
- package/scripts/hash.mjs +45 -0
- package/scripts/salt.mjs +18 -0
- package/tests/utils/common.spec.mjs +135 -0
- package/tests/utils/compare.spec.mjs +81 -0
- package/tsconfig.json +5 -0
- package/utils/args-map.mjs +9 -0
- package/utils/common.mjs +15 -0
- package/utils/compare.mjs +23 -0
- package/utils/hash.mjs +31 -0
- package/utils/salt.mjs +21 -0
package/babel.config.cjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const {
|
|
2
|
+
env: {
|
|
3
|
+
NODE_ENV = 'development'
|
|
4
|
+
}
|
|
5
|
+
} = process
|
|
6
|
+
|
|
7
|
+
function env () {
|
|
8
|
+
return (
|
|
9
|
+
NODE_ENV === 'production'
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const presets = [
|
|
14
|
+
[
|
|
15
|
+
'@babel/env', {
|
|
16
|
+
targets: {
|
|
17
|
+
node: 'current'
|
|
18
|
+
},
|
|
19
|
+
useBuiltIns: 'usage',
|
|
20
|
+
corejs: 3
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const plugins = []
|
|
26
|
+
|
|
27
|
+
module.exports = (api) => {
|
|
28
|
+
if (api) api.cache.using(env)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
presets,
|
|
32
|
+
plugins
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import argsMap from '#utils/args-map'
|
|
2
|
+
|
|
3
|
+
export const ITERATIONS = (
|
|
4
|
+
argsMap.has('ITERATIONS')
|
|
5
|
+
? argsMap.get('ITERATIONS')
|
|
6
|
+
: 10
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
export const KEYLEN = (
|
|
10
|
+
argsMap.has('KEYLEN')
|
|
11
|
+
? argsMap.get('KEYLEN')
|
|
12
|
+
: 64
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
export const DIGEST = (
|
|
16
|
+
argsMap.has('DIGEST')
|
|
17
|
+
? argsMap.get('DIGEST')
|
|
18
|
+
: 'sha512'
|
|
19
|
+
)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare module '@sequencemedia/pbkdf2' {
|
|
2
|
+
export function compare(password: string, params: { salt: string, hash: Buffer, iteration?: number, keylen?: number, digest?: string }): Promise<boolean>
|
|
3
|
+
export function hash (password: string, params: { salt: string, iteration?: number, keylen?: number, digest?: string }): Promise<Buffer>
|
|
4
|
+
export function salt (size: number): Promise<Buffer>
|
|
5
|
+
}
|
package/index.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sequencemedia/pbkdf2",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"main": "./index.mjs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Jonathan Perry for Sequence Media Limited",
|
|
8
|
+
"email": "sequencemedia@sequencemedia.net",
|
|
9
|
+
"url": "http://sequencemedia.net"
|
|
10
|
+
},
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git@github.com:sequencemedia/pbkdf2.git"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "cross-env NODE_ENV=production eslint . --ext .mjs --ext .cjs",
|
|
18
|
+
"lint:fix": "npm run lint -- --fix",
|
|
19
|
+
"test": "cross-env NODE_ENV=test mocha tests --recursive --verbose --color",
|
|
20
|
+
"compare": "cross-env NODE_ENV=production node scripts/compare.mjs",
|
|
21
|
+
"hash": "cross-env NODE_ENV=production node scripts/hash.mjs",
|
|
22
|
+
"salt": "cross-env NODE_ENV=production node scripts/salt.mjs",
|
|
23
|
+
"prepare": "husky install"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"debug": "^4.3.4",
|
|
27
|
+
"yargs-parser": "^21.1.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@babel/cli": "^7.20.7",
|
|
31
|
+
"@babel/core": "^7.20.12",
|
|
32
|
+
"@babel/eslint-parser": "^7.19.1",
|
|
33
|
+
"@babel/preset-env": "^7.20.2",
|
|
34
|
+
"@sequencemedia/hooks": "^1.0.314",
|
|
35
|
+
"@types/node": "^18.11.18",
|
|
36
|
+
"chai": "^4.3.7",
|
|
37
|
+
"core-js": "^3.27.2",
|
|
38
|
+
"cross-env": "^7.0.3",
|
|
39
|
+
"eslint": "^8.32.0",
|
|
40
|
+
"eslint-config-standard": "^17.0.0",
|
|
41
|
+
"eslint-plugin-import": "^2.27.5",
|
|
42
|
+
"eslint-plugin-node": "^11.1.0",
|
|
43
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
44
|
+
"husky": "^8.0.3",
|
|
45
|
+
"mocha": "^10.2.0"
|
|
46
|
+
},
|
|
47
|
+
"imports": {
|
|
48
|
+
"#config/*": "./config/*.mjs",
|
|
49
|
+
"#utils/*": "./utils/*.mjs"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import argsMap from '#utils/args-map'
|
|
4
|
+
import compare from '#utils/compare'
|
|
5
|
+
import {
|
|
6
|
+
fromBase64ToBuffer,
|
|
7
|
+
fromBase64
|
|
8
|
+
} from '#utils/common'
|
|
9
|
+
import {
|
|
10
|
+
ITERATIONS,
|
|
11
|
+
KEYLEN,
|
|
12
|
+
DIGEST
|
|
13
|
+
} from '#config/compare'
|
|
14
|
+
|
|
15
|
+
if (!argsMap.has('password')) throw new Error('Parameter `password` is required')
|
|
16
|
+
const password = argsMap.get('password')
|
|
17
|
+
|
|
18
|
+
if (!argsMap.has('hash')) throw new Error('Parameter `hash` is required')
|
|
19
|
+
const hash = fromBase64ToBuffer(argsMap.get('hash'))
|
|
20
|
+
|
|
21
|
+
if (!argsMap.has('salt')) throw new Error('Parameter `salt` is required')
|
|
22
|
+
const salt = fromBase64(argsMap.get('salt'))
|
|
23
|
+
|
|
24
|
+
const iterations = (
|
|
25
|
+
argsMap.has('iterations')
|
|
26
|
+
? argsMap.get('iterations')
|
|
27
|
+
: ITERATIONS
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const keylen = (
|
|
31
|
+
argsMap.has('keylen')
|
|
32
|
+
? argsMap.get('keylen')
|
|
33
|
+
: KEYLEN
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const digest = (
|
|
37
|
+
argsMap.has('digest')
|
|
38
|
+
? argsMap.get('digest')
|
|
39
|
+
: DIGEST
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
compare(password, { hash, salt, iterations, keylen, digest })
|
|
43
|
+
.then((isValid) => {
|
|
44
|
+
console.log(isValid ? '👍' : '👎')
|
|
45
|
+
})
|
|
46
|
+
.catch(() => {
|
|
47
|
+
console.error('💩')
|
|
48
|
+
})
|
package/scripts/hash.mjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import argsMap from '#utils/args-map'
|
|
4
|
+
import {
|
|
5
|
+
fromBase64,
|
|
6
|
+
fromBufferToBase64
|
|
7
|
+
} from '#utils/common'
|
|
8
|
+
import hash from '#utils/hash'
|
|
9
|
+
import {
|
|
10
|
+
ITERATIONS,
|
|
11
|
+
KEYLEN,
|
|
12
|
+
DIGEST
|
|
13
|
+
} from '#config/compare'
|
|
14
|
+
|
|
15
|
+
if (!argsMap.has('password')) throw new Error('Parameter `password` is required')
|
|
16
|
+
const password = argsMap.get('password')
|
|
17
|
+
|
|
18
|
+
if (!argsMap.has('salt')) throw new Error('Parameter `salt` is required')
|
|
19
|
+
const salt = fromBase64(argsMap.get('salt'))
|
|
20
|
+
|
|
21
|
+
const iterations = (
|
|
22
|
+
argsMap.has('iterations')
|
|
23
|
+
? argsMap.get('iterations')
|
|
24
|
+
: ITERATIONS
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const keylen = (
|
|
28
|
+
argsMap.has('keylen')
|
|
29
|
+
? argsMap.get('keylen')
|
|
30
|
+
: KEYLEN
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const digest = (
|
|
34
|
+
argsMap.has('digest')
|
|
35
|
+
? argsMap.get('digest')
|
|
36
|
+
: DIGEST
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
hash(password, { salt, iterations, keylen, digest })
|
|
40
|
+
.then((buffer) => {
|
|
41
|
+
console.log(fromBufferToBase64(buffer))
|
|
42
|
+
})
|
|
43
|
+
.catch(() => {
|
|
44
|
+
console.error('💩')
|
|
45
|
+
})
|
package/scripts/salt.mjs
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import argsMap from '#utils/args-map'
|
|
4
|
+
import {
|
|
5
|
+
fromBufferToBase64
|
|
6
|
+
} from '#utils/common'
|
|
7
|
+
import salt from '#utils/salt'
|
|
8
|
+
|
|
9
|
+
if (!argsMap.has('size')) throw new Error('Parameter `size` is required')
|
|
10
|
+
const size = argsMap.get('size')
|
|
11
|
+
|
|
12
|
+
salt(size)
|
|
13
|
+
.then((buffer) => {
|
|
14
|
+
console.log(fromBufferToBase64(buffer))
|
|
15
|
+
})
|
|
16
|
+
.catch(() => {
|
|
17
|
+
console.error('💩')
|
|
18
|
+
})
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
expect
|
|
3
|
+
} from 'chai'
|
|
4
|
+
import {
|
|
5
|
+
fromBase64,
|
|
6
|
+
toBase64,
|
|
7
|
+
fromBase64ToBuffer,
|
|
8
|
+
fromBufferToBase64
|
|
9
|
+
} from '#utils/common'
|
|
10
|
+
|
|
11
|
+
describe('`#utils/common`', () => {
|
|
12
|
+
describe('fromBase64', () => it('is a function', () => expect(fromBase64).to.be.a('function')))
|
|
13
|
+
|
|
14
|
+
describe('toBase64', () => it('is a function', () => expect(toBase64).to.be.a('function')))
|
|
15
|
+
|
|
16
|
+
describe('fromBase64ToBuffer', () => it('is a function', () => expect(fromBase64ToBuffer).to.be.a('function')))
|
|
17
|
+
|
|
18
|
+
describe('fromBufferToBase64', () => it('is a function', () => expect(fromBufferToBase64).to.be.a('function')))
|
|
19
|
+
|
|
20
|
+
describe('fromBase64()', () => {
|
|
21
|
+
describe('User 1', () => {
|
|
22
|
+
it('returns a string', () => {
|
|
23
|
+
const BASE64 = 'YzFjMTY4MmMyMWE4MjFkYjRhNWE5YjJmNDY0OTUzM2Q='
|
|
24
|
+
const UTF8 = 'c1c1682c21a821db4a5a9b2f4649533d'
|
|
25
|
+
|
|
26
|
+
return expect(fromBase64(BASE64)).to.equal(UTF8)
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('User 2', () => {
|
|
31
|
+
it('returns a string', () => {
|
|
32
|
+
const BASE64 = 'MDI2OGM0MDU4NWJiOTgxNWEyYzQwN2RkYjM1YTY5YWU='
|
|
33
|
+
const UTF8 = '0268c40585bb9815a2c407ddb35a69ae'
|
|
34
|
+
|
|
35
|
+
return expect(fromBase64(BASE64)).to.equal(UTF8)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('User 3', () => {
|
|
40
|
+
it('returns a string', () => {
|
|
41
|
+
const BASE64 = 'ZDI3OTZmZWU3M2I3M2Y3MTgxMmYyNGM3MjU4NDE2NDM='
|
|
42
|
+
const UTF8 = 'd2796fee73b73f71812f24c725841643'
|
|
43
|
+
|
|
44
|
+
return expect(fromBase64(BASE64)).to.equal(UTF8)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('toBase64()', () => {
|
|
50
|
+
describe('User 1', () => {
|
|
51
|
+
it('returns a string', () => {
|
|
52
|
+
const UTF8 = 'c1c1682c21a821db4a5a9b2f4649533d'
|
|
53
|
+
const BASE64 = 'YzFjMTY4MmMyMWE4MjFkYjRhNWE5YjJmNDY0OTUzM2Q='
|
|
54
|
+
|
|
55
|
+
return expect(toBase64(UTF8)).to.equal(BASE64)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('User 2', () => {
|
|
60
|
+
it('returns a string', () => {
|
|
61
|
+
const UTF8 = '0268c40585bb9815a2c407ddb35a69ae'
|
|
62
|
+
const BASE64 = 'MDI2OGM0MDU4NWJiOTgxNWEyYzQwN2RkYjM1YTY5YWU='
|
|
63
|
+
|
|
64
|
+
return expect(toBase64(UTF8)).to.equal(BASE64)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('User 3', () => {
|
|
69
|
+
it('returns a string', () => {
|
|
70
|
+
const UTF8 = 'd2796fee73b73f71812f24c725841643'
|
|
71
|
+
const BASE64 = 'ZDI3OTZmZWU3M2I3M2Y3MTgxMmYyNGM3MjU4NDE2NDM='
|
|
72
|
+
|
|
73
|
+
return expect(toBase64(UTF8)).to.equal(BASE64)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('fromBase64ToBuffer()', () => {
|
|
79
|
+
describe('User 1', () => {
|
|
80
|
+
it('returns a buffer', () => {
|
|
81
|
+
const BASE64 = 'YzFjMTY4MmMyMWE4MjFkYjRhNWE5YjJmNDY0OTUzM2Q='
|
|
82
|
+
const BUFFER = Buffer.from('c1c1682c21a821db4a5a9b2f4649533d')
|
|
83
|
+
|
|
84
|
+
return expect(Buffer.compare(fromBase64ToBuffer(BASE64), BUFFER)).to.equal(0)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('User 2', () => {
|
|
89
|
+
it('returns a buffer', () => {
|
|
90
|
+
const BASE64 = 'MDI2OGM0MDU4NWJiOTgxNWEyYzQwN2RkYjM1YTY5YWU='
|
|
91
|
+
const BUFFER = Buffer.from('0268c40585bb9815a2c407ddb35a69ae')
|
|
92
|
+
|
|
93
|
+
return expect(Buffer.compare(fromBase64ToBuffer(BASE64), BUFFER)).to.equal(0)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe('User 3', () => {
|
|
98
|
+
it('returns a buffer', () => {
|
|
99
|
+
const BASE64 = 'ZDI3OTZmZWU3M2I3M2Y3MTgxMmYyNGM3MjU4NDE2NDM='
|
|
100
|
+
const BUFFER = Buffer.from('d2796fee73b73f71812f24c725841643')
|
|
101
|
+
|
|
102
|
+
return expect(Buffer.compare(fromBase64ToBuffer(BASE64), BUFFER)).to.equal(0)
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
describe('fromBufferToBase64()', () => {
|
|
108
|
+
describe('User 1', () => {
|
|
109
|
+
it('returns a string', () => {
|
|
110
|
+
const BUFFER = Buffer.from('c1c1682c21a821db4a5a9b2f4649533d')
|
|
111
|
+
const BASE64 = 'YzFjMTY4MmMyMWE4MjFkYjRhNWE5YjJmNDY0OTUzM2Q='
|
|
112
|
+
|
|
113
|
+
return expect(fromBufferToBase64(BUFFER)).to.equal(BASE64)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe('User 2', () => {
|
|
118
|
+
it('returns a string', () => {
|
|
119
|
+
const BUFFER = Buffer.from('0268c40585bb9815a2c407ddb35a69ae')
|
|
120
|
+
const BASE64 = 'MDI2OGM0MDU4NWJiOTgxNWEyYzQwN2RkYjM1YTY5YWU='
|
|
121
|
+
|
|
122
|
+
return expect(fromBufferToBase64(BUFFER)).to.equal(BASE64)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('User 3', () => {
|
|
127
|
+
it('returns a string', () => {
|
|
128
|
+
const BUFFER = Buffer.from('d2796fee73b73f71812f24c725841643')
|
|
129
|
+
const BASE64 = 'ZDI3OTZmZWU3M2I3M2Y3MTgxMmYyNGM3MjU4NDE2NDM='
|
|
130
|
+
|
|
131
|
+
return expect(fromBufferToBase64(BUFFER)).to.equal(BASE64)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
expect
|
|
3
|
+
} from 'chai'
|
|
4
|
+
import compare from '#utils/compare'
|
|
5
|
+
|
|
6
|
+
describe('`#utils/compare`', () => {
|
|
7
|
+
describe('compare', () => it('is a function', () => expect(compare).to.be.a('function')))
|
|
8
|
+
|
|
9
|
+
describe('compare()', () => {
|
|
10
|
+
const ITERATIONS = 32768
|
|
11
|
+
|
|
12
|
+
describe('User 1', () => {
|
|
13
|
+
const HASH = Buffer.from('kOhpM7//M4I9+WcWLH7vbb/yN2+8qKyxQ0xAfI4JzamcRMdwwl7vAh0ch7UJ0JoBe9AqaAGuUJZF/mVA9EqaLQ==', 'base64')
|
|
14
|
+
const SALT = 'c1c1682c21a821db4a5a9b2f4649533d'
|
|
15
|
+
|
|
16
|
+
describe('`password` is valid', () => {
|
|
17
|
+
it('returns true', async () => {
|
|
18
|
+
const password = '^49pPaRdVSNC'
|
|
19
|
+
const isValid = await compare(password, { hash: HASH, salt: SALT, iterations: ITERATIONS })
|
|
20
|
+
|
|
21
|
+
return expect(isValid).to.be.true
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
describe('`password` is not valid', () => {
|
|
26
|
+
it('returns false', async () => {
|
|
27
|
+
const password = 'not password'
|
|
28
|
+
const isValid = await compare(password, { hash: HASH, salt: SALT, iterations: ITERATIONS })
|
|
29
|
+
|
|
30
|
+
return expect(isValid).to.be.false
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('User 2', () => {
|
|
36
|
+
const HASH = Buffer.from('BrtOel2qy5HrYWn45jBKf5b3HUKdVeQfIj1ljhGxQ/SiWsmMMuOl756H4MXkMOnVJKrgFfHFy7cgFXUaSFA2ow==', 'base64')
|
|
37
|
+
const SALT = '0268c40585bb9815a2c407ddb35a69ae'
|
|
38
|
+
|
|
39
|
+
describe('`password` is valid', () => {
|
|
40
|
+
it('returns true', async () => {
|
|
41
|
+
const password = 'S0uSaC&FzDOO'
|
|
42
|
+
const isValid = await compare(password, { hash: HASH, salt: SALT, iterations: ITERATIONS })
|
|
43
|
+
|
|
44
|
+
return expect(isValid).to.be.true
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('`password` is not valid', () => {
|
|
49
|
+
it('returns false', async () => {
|
|
50
|
+
const password = 'not password'
|
|
51
|
+
const isValid = await compare(password, { hash: HASH, salt: SALT, iterations: ITERATIONS })
|
|
52
|
+
|
|
53
|
+
return expect(isValid).to.be.false
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('User 3', () => {
|
|
59
|
+
const HASH = Buffer.from('bV76sVqsM0doKBFnaHsZQC9OgcVnF/+gzkG7HFXdD7jyQdN1PqCVijyXKMijmYQPWJohi5f7sVxhoLJ5kf8aQA==', 'base64')
|
|
60
|
+
const SALT = 'd2796fee73b73f71812f24c725841643'
|
|
61
|
+
|
|
62
|
+
describe('`password` is valid', () => {
|
|
63
|
+
it('returns true', async () => {
|
|
64
|
+
const password = '@^EJmdlkt5A#'
|
|
65
|
+
const isValid = await compare(password, { hash: HASH, salt: SALT, iterations: ITERATIONS })
|
|
66
|
+
|
|
67
|
+
return expect(isValid).to.be.true
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('`password` is not valid', () => {
|
|
72
|
+
it('returns false', async () => {
|
|
73
|
+
const password = 'not password'
|
|
74
|
+
const isValid = await compare(password, { hash: HASH, salt: SALT, iterations: ITERATIONS })
|
|
75
|
+
|
|
76
|
+
return expect(isValid).to.be.false
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|
package/tsconfig.json
ADDED
package/utils/common.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function fromBase64ToBuffer (string) {
|
|
2
|
+
return Buffer.from(string, 'base64')
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function fromBufferToBase64 (buffer) {
|
|
6
|
+
return buffer.toString('base64')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function fromBase64 (string) {
|
|
10
|
+
return Buffer.from(string, 'base64').toString()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function toBase64 (string) {
|
|
14
|
+
return Buffer.from(string).toString('base64')
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
timingSafeEqual
|
|
3
|
+
} from 'node:crypto'
|
|
4
|
+
import hash from './hash.mjs'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* function compare (password, salt, h, complete = () => {}) {
|
|
8
|
+
* hash(password, salt, (e, v) => {
|
|
9
|
+
* if (e) return complete(e)
|
|
10
|
+
*
|
|
11
|
+
* complete(null, timingSafeEqual(h, v))
|
|
12
|
+
* })
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export default async function compare (password, {
|
|
17
|
+
hash: alpha,
|
|
18
|
+
...params
|
|
19
|
+
} = {}) {
|
|
20
|
+
const omega = await hash(password, params)
|
|
21
|
+
|
|
22
|
+
return timingSafeEqual(alpha, omega)
|
|
23
|
+
}
|
package/utils/hash.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pbkdf2
|
|
3
|
+
} from 'node:crypto'
|
|
4
|
+
import {
|
|
5
|
+
ITERATIONS,
|
|
6
|
+
KEYLEN,
|
|
7
|
+
DIGEST
|
|
8
|
+
} from '#config/compare'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* function hash (password, salt, complete = () => {}) {
|
|
12
|
+
* pbkdf2(password, salt, ITERATIONS, KEYLEN, DIGEST, complete)
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export default function hash (password, {
|
|
17
|
+
salt,
|
|
18
|
+
iterations = ITERATIONS,
|
|
19
|
+
keylen = KEYLEN,
|
|
20
|
+
digest = DIGEST
|
|
21
|
+
} = {}) {
|
|
22
|
+
return (
|
|
23
|
+
new Promise((resolve, reject) => {
|
|
24
|
+
pbkdf2(password, salt, iterations, keylen, digest, (e, buffer) => {
|
|
25
|
+
(!e)
|
|
26
|
+
? resolve(buffer)
|
|
27
|
+
: reject(e)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
}
|
package/utils/salt.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
randomBytes
|
|
3
|
+
} from 'node:crypto'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* function salt (size, complete = () => {}) {
|
|
7
|
+
* randomBytes(size, complete)
|
|
8
|
+
* }
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export default function salt (size) {
|
|
12
|
+
return (
|
|
13
|
+
new Promise((resolve, reject) => {
|
|
14
|
+
randomBytes(size, (e, buffer) => {
|
|
15
|
+
(!e)
|
|
16
|
+
? resolve(buffer)
|
|
17
|
+
: reject(e)
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
}
|