53 lines
1.2 KiB
JavaScript
53 lines
1.2 KiB
JavaScript
const { Crypto } = require('@peculiar/webcrypto');
|
|
const crypto = new Crypto();
|
|
|
|
const encoder = new TextEncoder();
|
|
const decoder = new TextDecoder();
|
|
|
|
module.exports = class Keychain {
|
|
constructor(secretKeyB64) {
|
|
if (secretKeyB64) {
|
|
this.rawSecret = new Uint8Array(Buffer.from(secretKeyB64, 'base64'));
|
|
} else {
|
|
throw new Error('key is required');
|
|
}
|
|
this.secretKeyPromise = crypto.subtle.importKey(
|
|
'raw',
|
|
this.rawSecret,
|
|
'HKDF',
|
|
false,
|
|
['deriveKey']
|
|
);
|
|
this.metaKeyPromise = this.secretKeyPromise.then(function(secretKey) {
|
|
return crypto.subtle.deriveKey(
|
|
{
|
|
name: 'HKDF',
|
|
salt: new Uint8Array(),
|
|
info: encoder.encode('metadata'),
|
|
hash: 'SHA-256'
|
|
},
|
|
secretKey,
|
|
{
|
|
name: 'AES-GCM',
|
|
length: 128
|
|
},
|
|
false,
|
|
['decrypt']
|
|
);
|
|
});
|
|
}
|
|
|
|
async decryptMetadata(ciphertext) {
|
|
const metaKey = await this.metaKeyPromise;
|
|
const plaintext = await crypto.subtle.decrypt(
|
|
{
|
|
name: 'AES-GCM',
|
|
iv: new Uint8Array(12),
|
|
tagLength: 128
|
|
},
|
|
metaKey,
|
|
ciphertext
|
|
);
|
|
return JSON.parse(decoder.decode(plaintext));
|
|
}
|
|
};
|