Skip to content

Commit

Permalink
Add gi.app/identity/changePassword
Browse files Browse the repository at this point in the history
  • Loading branch information
corrideat committed Dec 6, 2024
1 parent a690227 commit 57eb477
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 5 deletions.
4 changes: 2 additions & 2 deletions backend/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,7 @@ route.GET('/zkpp/{name}/contract_hash', {
return Boom.internal('internal error')
})

route.POST('/zkpp/updatePasswordHash/{name}', {
route.POST('/zkpp/{name}/updatePasswordHash', {
validate: {
payload: Joi.object({
r: Joi.string().required(),
Expand All @@ -841,7 +841,7 @@ route.POST('/zkpp/updatePasswordHash/{name}', {
}
} catch (e) {
e.ip = req.headers['x-real-ip'] || req.info.remoteAddress
console.error(e, 'Error at POST /zkpp/updatePasswordHash/{name}: ' + e.message)
console.error(e, 'Error at POST /zkpp/{name}/updatePasswordHash: ' + e.message)
}

return Boom.internal('internal error')
Expand Down
50 changes: 49 additions & 1 deletion frontend/controller/app/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import sbp from '@sbp/sbp'
import Vue from 'vue'
import { LOGIN, LOGIN_COMPLETE, LOGIN_ERROR } from '~/frontend/utils/events.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { boxKeyPair, buildRegisterSaltRequest, computeCAndHc, decryptContractSalt, hash, hashPassword, randomNonce } from '~/shared/zkpp.js'
import { boxKeyPair, buildRegisterSaltRequest, buildUpdateSaltRequestEa, computeCAndHc, decryptContractSalt, hash, hashPassword, randomNonce } from '~/shared/zkpp.js'
// Using relative path to crypto.js instead of ~-path to workaround some esbuild bug
import { CURVE25519XSALSA20POLY1305, EDWARDS25519SHA512BATCH, deriveKeyFromPassword, serializeKey } from '../../../shared/domains/chelonia/crypto.js'
import { handleFetchResult } from '../utils/misc.js'
Expand Down Expand Up @@ -177,6 +177,37 @@ export default (sbp('sbp/selectors/register', {

return decryptContractSalt(c, contractHash)
},
'gi.app/identity/updateSalt': async (username: string, oldPassword: Secret<string>, newPassword: Secret<string>) => {
const r = randomNonce()
const b = hash(r)
const authHash = await fetch(`${sbp('okTurtles.data/get', 'API_URL')}/zkpp/${encodeURIComponent(username)}/auth_hash?b=${encodeURIComponent(b)}`)
.then(handleFetchResult('json'))

const { authSalt, s, sig } = authHash

const h = await hashPassword(oldPassword.valueOf(), authSalt)

const [c, hc] = computeCAndHc(r, s, h)

const [contractHash, encryptedArgs] = await buildUpdateSaltRequestEa(newPassword.valueOf(), c)

await fetch(`${sbp('okTurtles.data/get', 'API_URL')}/zkpp/${encodeURIComponent(username)}/updatePasswordHash`, {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
body:
`${(new URLSearchParams({
'r': r,
's': s,
'sig': sig,
'hc': Buffer.from(hc).toString('base64').replace(/\//g, '_').replace(/\+/g, '-').replace(/=*$/, ''),
'Ea': encryptedArgs.toString('base64').replace(/\//g, '_').replace(/\+/g, '-').replace(/=*$/, '')
})).toString()}`
}).then(handleFetchResult('text'))

return contractHash
},
'gi.app/identity/create': async function ({
data: { username, email, password, picture },
publishOptions
Expand Down Expand Up @@ -409,5 +440,22 @@ export default (sbp('sbp/selectors/register', {
},
'gi.app/identity/logout': (...params) => {
return sbp('okTurtles.eventQueue/queueEvent', 'APP-LOGIN', ['gi.app/identity/_private/logout', ...params])
},
'gi.app/identity/changePassword': async (woldPassword: Secret<string>, wnewPassword: Secret<string>) => {
const state = sbp('state/vuex/state')
if (!state.loggedIn) return
const getters = sbp('state/vuex/getters')

const { identityContractID } = state.loggedIn
const username = getters.usernameFromID(identityContractID)
// const oldPassword = woldPassword.valueOf()
// const newPassword = wnewPassword.valueOf()

const contractSalt = await sbp('gi.app/identity/updateSalt', username, woldPassword, wnewPassword)

return contractSalt

/* const IPK = await deriveKeyFromPassword(EDWARDS25519SHA512BATCH, newPassword, contractSalt)
const IEK = await deriveKeyFromPassword(CURVE25519XSALSA20POLY1305, newPassword, contractSalt) */
}
}): string[])
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions shared/zkpp.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,29 @@ export const buildRegisterSaltRequest = async (publicKey: string, secretKey: Uin

return [contractSalt, base64ToBase64url(Buffer.concat([nonce, encryptedHashedPasswordBuf]).toString('base64'))]
}

export const buildUpdateSaltRequestEa = async (password: string, c: Uint8Array): Promise<[string, string]> => {
// TODO: Derive S_A and S_C as follows:
// -> q -< random
// -> r -< SHA-512(SHA-512('SU') + SHA-512(q))
// -> b -< SHA-512(r) // as it's now
// Then,
// -> S_T -< BASE64(SHA-512(SHA-512(T) + SHA-512(q))[0..18]) with T being
// `AUTHSALT` or `CONTRACTSALT`
// This way, we ensure both the server and the client contribute to the
// salts' entropy.
// When sending the encrypted data, the encrypted information would be
// `[hashedPassword, q]`, which needs to be verified server-side to verify
// it matches p and would be used to derive S_A and S_C.
const [authSalt, contractSalt] = ['a', 'b']

const encryptionKey = nacl.hash(Buffer.concat([Buffer.from('SU'), c])).slice(0, nacl.secretbox.keyLength)
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength)

const hashedPassword = await hashPassword(password, authSalt)
const encryptedArgsCiphertext = nacl.secretbox(Buffer.from(JSON.stringify([hashedPassword, authSalt, contractSalt])), nonce, encryptionKey)

const encryptedArgs = Buffer.concat([nonce, encryptedArgsCiphertext])

return [contractSalt, base64ToBase64url(encryptedArgs.toString('base64'))]
}

0 comments on commit 57eb477

Please sign in to comment.