Я создаю приложение React и получаю сообщение об ошибке - Unsupported state or unable to authenticate data
, когда оно пытается расшифровать данные. Я до сих пор не понял, когда это произойдет. Это иногда случается, иногда я могу запустить приложение без ошибки.
Я использую: crypto npm пакет с алгоритмом aes-256-gcm.
Это мой код шифрования / дешифрования:
import crypto from 'crypto'
const getEncryptedPrefix = () => {
return 'enc::'
}
// Convert user's password into cryptographic key
const deriveKeyFromPassword = (password, salt, iterations) => {
return crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha512')
}
export const encrypt = (message, password) => {
const salt = crypto.randomBytes(64)
const iterations = Math.floor(Math.random() * (99999 - 10000 + 1)) + 500
const KEY = deriveKeyFromPassword(password, salt, Math.floor(iterations * 0.47 + 1337))
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv('aes-256-gcm', KEY, iv)
const encryptedData = Buffer.concat([cipher.update(message, 'utf8'), cipher.final()])
const authTag = cipher.getAuthTag()
const output = Buffer.concat([salt, iv, authTag, Buffer.from(iterations.toString()), encryptedData]).toString('hex')
return getEncryptedPrefix() + output
}
export const decrypt = (cipherText, password) => {
const cipherTextParts = cipherText.split('enc::')
if(cipherTextParts.length !== 2) {
console.error("Couldn't determine the beginning of the cipherText. Maybe not encrypted by this method?")
} else {
cipherText = cipherTextParts[1]
}
const inputData = Buffer.from(cipherText, 'hex')
const salt = inputData.slice(0, 64)
const iv = inputData.slice(64, 80)
const authTag = inputData.slice(80, 96)
const iterations = parseInt(inputData.slice(96, 101).toString('utf-8'), 10)
const encryptedData = inputData.slice(101)
const KEY = deriveKeyFromPassword(password, salt, Math.floor(iterations * 0.47 + 1337))
const decipher = crypto.createDecipheriv('aes-256-gcm', KEY, iv)
decipher.setAuthTag(authTag)
const decrypted = decipher.update(encryptedData, 'binary', 'utf-8') + decipher.final('utf-8')
try {
return JSON.parse(decrypted)
} catch (error) {
return decrypted
}
}
Я вызываю это в EncryptForm компонент:
import React, { Component } from 'react'
import { withRouter, Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { encrypt, decrypt } from '../crypto'
class EncryptForm extends Component {
constructor(props) {
super(props)
this.state = {
isSubmitted: false,
decipher: {
website: '',
email: '',
password: ''
},
error: null,
accountRawData: {
website: '',
email: '',
password: ''
},
}
}
handleSubmit = event => {
event.preventDefault()
const secrets = encrypt(JSON.stringify(this.state.accountRawData), this.props.masterPassword)
localStorage.setItem('secrets', secrets)
const decrypted = decrypt(localStorage.getItem('secrets'), this.props.masterPassword)
this.setState({
isSubmitted: true,
decipher: {
website: decrypted.website,
email: decrypted.email,
password: decrypted.password
},
})
if(this.state.isSubmitted) {
this.props.history.push('/')
}
}
handleChange = event => {
this.setState({
accountRawData: {
...this.state.accountRawData,
[event.target.name]: event.target.value
}
})
}
render() {
const { decipher, isSubmitted, error, accountRawData } = this.state
const isInvalid = accountRawData.website === '' || accountRawData.email === '' || accountRawData.password === ''
const hasData = decipher.website !== '' || decipher.email !== '' || decipher.password !== ''
if(isSubmitted) {
return <Redirect to='/' />
}
return (
!error
? <section>
<form onSubmit={this.handleSubmit}>
<label>Website</label>
<input
type='text'
name='website'
onChange={this.handleChange}
placeholder={hasData ? decipher.website : null}
/>
<label>Login Email</label>
<input
type='text'
name='email'
onChange={this.handleChange}
placeholder={hasData ? decipher.email : null}
/>
<label>Password</label>
<input
type='password'
name='password'
onChange={this.handleChange}
placeholder={hasData ? decipher.password : null}
autoComplete='on'
/>
<button disabled={isInvalid} type='submit' className='button'>Update</button>
</form>
{isSubmitted && error
? <p>{error}</p>
: null
}
</section>
: <div>
<h3 className='error-message'>{error}</h3>
<p>Please refresh and enter the right password.</p>
</div>
)
}
}
EncryptForm.propTypes = {
masterPassword: PropTypes.string.isRequired
}
export default withRouter(EncryptForm)
Спасибо за помощь.