55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
|
|
import crypto from 'crypto'
|
||
|
|
|
||
|
|
export async function encrypt(seed: string, password: string) {
|
||
|
|
const salt = crypto.randomBytes(16)
|
||
|
|
const iv = crypto.randomBytes(12)
|
||
|
|
|
||
|
|
// derive 32-byte key từ password
|
||
|
|
const key = await new Promise<Buffer>((resolve, reject) => {
|
||
|
|
crypto.scrypt(password, salt, 32, { N: 16384, r: 8, p: 1 }, (err, derivedKey) => {
|
||
|
|
if (err) reject(err)
|
||
|
|
else resolve(derivedKey as Buffer)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
|
||
|
|
const ciphertext = Buffer.concat([cipher.update(seed, 'utf8'), cipher.final()])
|
||
|
|
const authTag = cipher.getAuthTag()
|
||
|
|
|
||
|
|
const encryptedMnemonic = Buffer.concat([salt, iv, authTag, ciphertext]).toString('hex')
|
||
|
|
|
||
|
|
const keystore = {
|
||
|
|
type: 'neptune-wallet',
|
||
|
|
encryption: 'aes-256-gcm',
|
||
|
|
version: 1,
|
||
|
|
wallet: {
|
||
|
|
mnemonic: encryptedMnemonic,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
return JSON.stringify(keystore, null, 2)
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function fromEncryptedJson(json: string, password: string) {
|
||
|
|
const data = JSON.parse(json)
|
||
|
|
const encrypted = Buffer.from(data.wallet.mnemonic, 'hex')
|
||
|
|
|
||
|
|
const salt = encrypted.subarray(0, 16)
|
||
|
|
const iv = encrypted.subarray(16, 28)
|
||
|
|
const authTag = encrypted.subarray(28, 44)
|
||
|
|
const ciphertext = encrypted.subarray(44)
|
||
|
|
|
||
|
|
const key = await new Promise<Buffer>((resolve, reject) => {
|
||
|
|
crypto.scrypt(password, salt, 32, { N: 16384, r: 8, p: 1 }, (err, derivedKey) => {
|
||
|
|
if (err) reject(err)
|
||
|
|
else resolve(derivedKey as Buffer)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
|
||
|
|
decipher.setAuthTag(authTag)
|
||
|
|
|
||
|
|
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()])
|
||
|
|
return decrypted.toString('utf8')
|
||
|
|
}
|