Skip to content

Commit cb9c394

Browse files
author
sevenbitbyte
committed
support argon2 in secure-config and argon2 example
1 parent 528b596 commit cb9c394

4 files changed

Lines changed: 228 additions & 11 deletions

File tree

examples/secure-config-argon2.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
2+
const argon2 = require('argon2')
3+
4+
const Dataparty = require('../src/index')
5+
6+
const prompt = require('prompt')
7+
8+
let secureConfig = null
9+
10+
async function main(){
11+
const memoryConfig = new Dataparty.Config.MemoryConfig({foo: 'bar'})
12+
13+
14+
const jsonConfig = new Dataparty.Config.JsonFileConfig({
15+
basePath: '/tmp'
16+
})
17+
18+
19+
secureConfig = new Dataparty.Config.SecureConfig({
20+
config: jsonConfig,
21+
timeoutMs: 15*1000,
22+
argon: argon2
23+
})
24+
25+
26+
secureConfig.on('locked', ()=>{ console.log('locked') })
27+
28+
secureConfig.on('unlocked', async ()=>{
29+
console.log('unlocked')
30+
31+
await secureConfig.write('timestamp', Date.now())
32+
})
33+
34+
secureConfig.on('timeout', ()=>{ console.log('timeout') })
35+
36+
secureConfig.on('ready', async ()=>{
37+
console.log('ready')
38+
39+
console.log('config', await secureConfig.readAll())
40+
})
41+
42+
let blocked = false
43+
44+
secureConfig.on('blocked', async (reason)=>{
45+
46+
if(await secureConfig.isInitialized() && secureConfig.isLocked()){
47+
48+
if(blocked){
49+
console.log('blocked true')
50+
await secureConfig.waitForUnlocked()
51+
return
52+
}
53+
54+
blocked = true
55+
console.log('blocked -',reason)
56+
57+
58+
const {password} = await prompt.get({
59+
properties: {
60+
password: {
61+
message: 'Enter password',
62+
hidden: true
63+
}
64+
}})
65+
66+
await secureConfig.unlock(password)
67+
68+
blocked = false
69+
}
70+
71+
72+
})
73+
74+
secureConfig.on('setup-required', async ()=>{
75+
76+
console.log('setup-required')
77+
78+
let password = ''
79+
80+
while(1){
81+
let passes = await prompt.get({
82+
properties: {
83+
password1: {
84+
message: 'Set password',
85+
hidden: true
86+
},
87+
password2: {
88+
message: 'Confim password',
89+
hidden: true
90+
}
91+
}
92+
})
93+
94+
if(passes.password1 == passes.password2){
95+
96+
password = passes.password1
97+
break
98+
}
99+
100+
console.log("passwords don't match")
101+
}
102+
103+
await secureConfig.setPassword(password, {
104+
foo: 'bar'
105+
})
106+
107+
console.log('password set')
108+
109+
110+
await secureConfig.unlock(password)
111+
112+
})
113+
114+
console.log('starting')
115+
116+
await secureConfig.start()
117+
118+
console.log('wait for startup')
119+
120+
await secureConfig.waitForUnlocked('startup')
121+
122+
console.log('wait over')
123+
124+
console.log('main config', await secureConfig.readAll())
125+
126+
let timer = setTimeout(async ()=>{
127+
128+
console.log('timer config', await secureConfig.readAll())
129+
130+
}, 1000*30)
131+
132+
}
133+
134+
main().catch((err)=>{
135+
console.error('crashed', err)
136+
}).then(()=>{
137+
console.log('done')
138+
})

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@dataparty/api",
33
"private": false,
4-
"version": "1.2.23",
4+
"version": "1.2.24",
55
"main": "dist/dataparty.js",
66
"frontend": "dist/dataparty-browser.js",
77
"backend": "dist/dataparty.js",
@@ -59,7 +59,7 @@
5959
},
6060
"dependencies": {
6161
"@dataparty/bouncer-db": "1.0.1",
62-
"@dataparty/crypto": "^1.1.1",
62+
"@dataparty/crypto": "^1.1.2",
6363
"@dataparty/tasker": "^0.0.2",
6464
"@diva.exchange/i2p-sam": "^4.1.8",
6565
"@markwylde/liferaft": "^1.3.4",
@@ -116,6 +116,8 @@
116116
"@dataparty/bouncer-model": "1.4.3",
117117
"@hapi/code": "^9.0.1",
118118
"@hapi/lab": "^25.0.1",
119+
"argon2": "^0.30.3",
120+
"argon2-browser": "^1.18.0",
119121
"assert": "^2.0.0",
120122
"browserify-zlib": "^0.2.0",
121123
"clean-jsdoc-theme": "^4.2.6",

src/config/secure-config.js

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ const IConfig = require('./iconfig')
1010
const PASSWORD_HASHING_ROUNDS = 1000000
1111
const DEFAULT_TIMEOUT_MS = 5*60*1000 //! 5min
1212

13+
const ARGON_TIME_COST = 3
14+
const ARGON_MEMORY_COST = 65536
15+
const ARGON_PARALLELLISM = 4
16+
const ARGON_TYPE = 'argon2id'
17+
1318
class SecureConfig extends IConfig {
1419

1520
/**
@@ -20,15 +25,19 @@ class SecureConfig extends IConfig {
2025
* @param {IConfig} config The underlying IConfig to use for storage
2126
* @param {number} timeoutMs Timeout since last unlock, after which the config will be locked. Defaults to 5 minutes.
2227
* @param {boolean} includeActivity When set to `true` the timeout is reset after any read/write activity. Defaults to `true`
28+
* @param {Argon2} argon Instance of argon2 from either `npm:argon2` or `npm:argon2-browser`
2329
*/
2430
constructor({
2531
id = 'secure-config',
26-
config, timeoutMs=DEFAULT_TIMEOUT_MS, includeActivity=true
32+
config, timeoutMs=DEFAULT_TIMEOUT_MS, includeActivity=true,
33+
argon
2734
}){
2835
super()
2936
this.id = id || 'secure-config'
3037
this.config = config
3138

39+
this.argon = argon
40+
3241
this.content = null
3342
this.identity = null
3443
this.timer = null
@@ -84,6 +93,16 @@ class SecureConfig extends IConfig {
8493

8594
return (salt != undefined && salt.length > 16 && rounds > 100000)
8695

96+
} else if(keyType == 'argon2'){
97+
98+
let salt = await this.config.read(this.id+'.settings.salt')
99+
let timeCost = await this.config.read(this.id+'.settings.timeCost')
100+
let memoryCost = await this.config.read(this.id+'.settings.memoryCost')
101+
let parallelism = await this.config.read(this.id+'.settings.parallelism')
102+
let argonType = await this.config.read(this.id+'.settings.argonType')
103+
104+
return (salt != undefined && salt.length > 16 && memoryCost > 1024)
105+
87106
} else if(keyType == 'key'){
88107
return true
89108
} else if(!keyType) {
@@ -111,28 +130,59 @@ class SecureConfig extends IConfig {
111130
* @method module:Config.SecureConfig.setPassword
112131
* @param {string} password
113132
* @param {object} defaults
114-
* @param {('pbkdf2')} type
115133
* @async
116134
*/
117-
async setPassword(password, defaults={}, type='pbkdf2'){
135+
async setPassword(password, defaults={}){
118136
debug('setPassword')
119137
if(await this.isInitialized()){ throw new Error('already initialized') }
120138

121139
let key = null
122140
let settings = null
123141

124-
if(type == 'pbkdf2'){
142+
if(!this.argon){
143+
//! pbkdf2
125144
const salt = await dataparty_crypto.Routines.generateSalt()
126145
const rounds = PASSWORD_HASHING_ROUNDS
127146

128147
settings = {
129-
type: type,
148+
type: 'pbkdf2',
130149
salt: salt.toString('hex'),
131150
rounds
132151
}
133152

134-
key = await dataparty_crypto.Routines.createKeyFromPassword(password, salt, rounds)
153+
key = await dataparty_crypto.Routines.createKeyFromPasswordPbkdf2(password, salt, rounds)
154+
155+
} else if(this.argon){
156+
//! argon2
157+
158+
const salt = await dataparty_crypto.Routines.generateSalt()
159+
let timeCost = ARGON_TIME_COST
160+
let memoryCost = ARGON_MEMORY_COST
161+
let parallelism = ARGON_PARALLELLISM
162+
let argonType = ARGON_TYPE
163+
164+
settings = {
165+
type: 'argon2',
166+
salt: salt.toString('hex'),
167+
timeCost,
168+
memoryCost,
169+
parallelism,
170+
argonType
171+
}
172+
173+
if(!this.argon){
174+
this.argon = argon
175+
}
135176

177+
key = await dataparty_crypto.Routines.createKeyFromPasswordArgon2(
178+
this.argon,
179+
password,
180+
salt,
181+
timeCost,
182+
memoryCost,
183+
parallelism,
184+
argonType
185+
)
136186
} else {
137187
throw new Error('unsupported KDF['+type+']')
138188
}
@@ -263,10 +313,37 @@ class SecureConfig extends IConfig {
263313
this.timer = null
264314
}
265315

266-
let salt = Buffer.from(await this.config.read(this.id+'.settings.salt'),'hex')
267-
let rounds = await this.config.read(this.id+'.settings.rounds')
316+
let key = null
317+
let keyType = await this.config.read(this.id+'.settings.type')
318+
319+
if(keyType == 'pbkdf2'){
320+
321+
let salt = Buffer.from(await this.config.read(this.id+'.settings.salt'),'hex')
322+
let rounds = await this.config.read(this.id+'.settings.rounds')
323+
324+
key = await dataparty_crypto.Routines.createKeyFromPasswordPbkdf2(password, salt, rounds)
325+
326+
} else if(keyType == 'argon2'){
327+
328+
let salt = Buffer.from(await this.config.read(this.id+'.settings.salt'), 'hex')
329+
let timeCost = await this.config.read(this.id+'.settings.timeCost')
330+
let memoryCost = await this.config.read(this.id+'.settings.memoryCost')
331+
let parallelism = await this.config.read(this.id+'.settings.parallelism')
332+
let argonType = await this.config.read(this.id+'.settings.argonType')
333+
334+
335+
key = await dataparty_crypto.Routines.createKeyFromPasswordArgon2(
336+
this.argon,
337+
password,
338+
salt,
339+
timeCost,
340+
memoryCost,
341+
parallelism,
342+
argonType
343+
)
344+
345+
}
268346

269-
let key = await dataparty_crypto.Routines.createKeyFromPassword(password, salt, rounds)
270347

271348
const pwIdentity = new dataparty_crypto.Identity({
272349
key,

0 commit comments

Comments
 (0)