mirror of
https://codeberg.org/timelimit/timelimit-server.git
synced 2026-08-31 19:03:45 +02:00
127 lines
4.3 KiB
TypeScript
127 lines
4.3 KiB
TypeScript
/*
|
|
* server component for the TimeLimit App
|
|
* Copyright (C) 2019 Jonas Lochmann
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, version 3 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { createDecipheriv, createHash } from 'crypto'
|
|
import { assertIsHexString } from '../util/hexstring'
|
|
import { assertIdWithinFamily } from '../util/token'
|
|
import { ParentAction } from './basetypes'
|
|
|
|
export class ChangeParentPasswordAction extends ParentAction {
|
|
readonly parentUserId: string
|
|
readonly newPasswordFirstHash: string
|
|
readonly newPasswordSecondSalt: string
|
|
readonly newPasswordSecondHashEncrypted: string
|
|
readonly integrity: string
|
|
|
|
constructor ({ parentUserId, newPasswordFirstHash, newPasswordSecondSalt, newPasswordSecondHashEncrypted, integrity }: {
|
|
parentUserId: string
|
|
newPasswordFirstHash: string
|
|
newPasswordSecondSalt: string
|
|
newPasswordSecondHashEncrypted: string
|
|
integrity: string
|
|
}) {
|
|
super()
|
|
|
|
assertIdWithinFamily(parentUserId)
|
|
|
|
if (
|
|
(!parentUserId) ||
|
|
(!newPasswordFirstHash) ||
|
|
(!newPasswordSecondSalt) ||
|
|
(!newPasswordSecondHashEncrypted) ||
|
|
(!integrity)
|
|
) {
|
|
throw new Error('missing required parameter for change parent password')
|
|
}
|
|
|
|
if (integrity.length !== 128) {
|
|
throw new Error('wrong length of integrity data')
|
|
}
|
|
|
|
assertIsHexString(newPasswordSecondHashEncrypted)
|
|
assertIsHexString(integrity)
|
|
|
|
this.parentUserId = parentUserId
|
|
this.newPasswordFirstHash = newPasswordFirstHash
|
|
this.newPasswordSecondSalt = newPasswordSecondSalt
|
|
this.newPasswordSecondHashEncrypted = newPasswordSecondHashEncrypted
|
|
this.integrity = integrity
|
|
}
|
|
|
|
serialize = (): SerializedChangeParentPasswordAction => ({
|
|
type: 'CHANGE_PARENT_PASSWORD',
|
|
userId: this.parentUserId,
|
|
hash: this.newPasswordFirstHash,
|
|
secondSalt: this.newPasswordSecondSalt,
|
|
secondHashEncrypted: this.newPasswordSecondHashEncrypted,
|
|
integrity: this.integrity
|
|
})
|
|
|
|
static parse = ({ userId, hash, secondSalt, secondHashEncrypted, integrity }: SerializedChangeParentPasswordAction) => (
|
|
new ChangeParentPasswordAction({
|
|
parentUserId: userId,
|
|
newPasswordFirstHash: hash,
|
|
newPasswordSecondSalt: secondSalt,
|
|
newPasswordSecondHashEncrypted: secondHashEncrypted,
|
|
integrity
|
|
})
|
|
)
|
|
|
|
assertIntegrityValid ({ oldPasswordSecondHash }: {oldPasswordSecondHash: string}) {
|
|
const integrityData = oldPasswordSecondHash +
|
|
this.parentUserId +
|
|
this.newPasswordFirstHash +
|
|
this.newPasswordSecondSalt +
|
|
this.newPasswordSecondHashEncrypted
|
|
|
|
const expected = createHash('sha512').update(integrityData).digest('hex')
|
|
|
|
if (expected !== this.integrity) {
|
|
throw new Error('invalid integrity for change parent password action')
|
|
}
|
|
}
|
|
|
|
decryptSecondHash ({ oldPasswordSecondHash }: {oldPasswordSecondHash: string}) {
|
|
if (this.newPasswordSecondHashEncrypted.length <= 70) {
|
|
throw new Error('wrong length of the new password')
|
|
}
|
|
|
|
const ivHex = this.newPasswordSecondHashEncrypted.substring(0, 32)
|
|
const salt = this.newPasswordSecondHashEncrypted.substring(32, 64)
|
|
const encryptedData = this.newPasswordSecondHashEncrypted.substring(64)
|
|
|
|
const keyData = oldPasswordSecondHash + salt
|
|
const key = createHash('sha512').update(keyData).digest().slice(0, 16)
|
|
|
|
const decipher = createDecipheriv('aes-128-ctr', key, Buffer.from(ivHex, 'hex'))
|
|
decipher.setAutoPadding(false)
|
|
|
|
const decryptedSecondHash = decipher.update(Buffer.from(encryptedData, 'hex')).toString() + decipher.final().toString()
|
|
|
|
return decryptedSecondHash
|
|
}
|
|
}
|
|
|
|
export interface SerializedChangeParentPasswordAction {
|
|
type: 'CHANGE_PARENT_PASSWORD'
|
|
userId: string
|
|
hash: string
|
|
secondSalt: string
|
|
secondHashEncrypted: string
|
|
integrity: string
|
|
}
|