2019-12-09 23:47:21 +00:00
|
|
|
/* eslint-env browser */
|
|
|
|
|
2021-05-22 13:02:51 +00:00
|
|
|
import * as encoding from 'lib0/encoding'
|
|
|
|
import * as decoding from 'lib0/decoding'
|
|
|
|
import * as promise from 'lib0/promise'
|
|
|
|
import * as error from 'lib0/error'
|
|
|
|
import * as string from 'lib0/string'
|
2019-12-09 23:47:21 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {string} secret
|
|
|
|
* @param {string} roomName
|
|
|
|
* @return {PromiseLike<CryptoKey>}
|
|
|
|
*/
|
|
|
|
export const deriveKey = (secret, roomName) => {
|
|
|
|
const secretBuffer = string.encodeUtf8(secret).buffer
|
|
|
|
const salt = string.encodeUtf8(roomName).buffer
|
|
|
|
return crypto.subtle.importKey(
|
|
|
|
'raw',
|
|
|
|
secretBuffer,
|
|
|
|
'PBKDF2',
|
|
|
|
false,
|
|
|
|
['deriveKey']
|
|
|
|
).then(keyMaterial =>
|
|
|
|
crypto.subtle.deriveKey(
|
|
|
|
{
|
|
|
|
name: 'PBKDF2',
|
|
|
|
salt,
|
|
|
|
iterations: 100000,
|
|
|
|
hash: 'SHA-256'
|
|
|
|
},
|
|
|
|
keyMaterial,
|
|
|
|
{
|
|
|
|
name: 'AES-GCM',
|
|
|
|
length: 256
|
|
|
|
},
|
|
|
|
true,
|
2020-04-07 12:22:21 +00:00
|
|
|
['encrypt', 'decrypt']
|
2019-12-09 23:47:21 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-12-12 20:20:56 +00:00
|
|
|
* @param {Uint8Array} data data to be encrypted
|
|
|
|
* @param {CryptoKey?} key
|
|
|
|
* @return {PromiseLike<Uint8Array>} encrypted, base64 encoded message
|
2019-12-09 23:47:21 +00:00
|
|
|
*/
|
|
|
|
export const encrypt = (data, key) => {
|
2019-12-12 20:20:56 +00:00
|
|
|
if (!key) {
|
|
|
|
return /** @type {PromiseLike<Uint8Array>} */ (promise.resolve(data))
|
|
|
|
}
|
2019-12-09 23:47:21 +00:00
|
|
|
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
|
|
return crypto.subtle.encrypt(
|
|
|
|
{
|
|
|
|
name: 'AES-GCM',
|
|
|
|
iv
|
|
|
|
},
|
|
|
|
key,
|
2019-12-12 20:20:56 +00:00
|
|
|
data
|
2019-12-09 23:47:21 +00:00
|
|
|
).then(cipher => {
|
|
|
|
const encryptedDataEncoder = encoding.createEncoder()
|
|
|
|
encoding.writeVarString(encryptedDataEncoder, 'AES-GCM')
|
|
|
|
encoding.writeVarUint8Array(encryptedDataEncoder, iv)
|
|
|
|
encoding.writeVarUint8Array(encryptedDataEncoder, new Uint8Array(cipher))
|
2019-12-12 20:20:56 +00:00
|
|
|
return encoding.toUint8Array(encryptedDataEncoder)
|
2019-12-09 23:47:21 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-12-12 20:20:56 +00:00
|
|
|
* @param {Object} data data to be encrypted
|
|
|
|
* @param {CryptoKey?} key
|
|
|
|
* @return {PromiseLike<Uint8Array>} encrypted data, if key is provided
|
|
|
|
*/
|
|
|
|
export const encryptJson = (data, key) => {
|
|
|
|
const dataEncoder = encoding.createEncoder()
|
|
|
|
encoding.writeAny(dataEncoder, data)
|
|
|
|
return encrypt(encoding.toUint8Array(dataEncoder), key)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {Uint8Array} data
|
|
|
|
* @param {CryptoKey?} key
|
|
|
|
* @return {PromiseLike<Uint8Array>} decrypted buffer
|
2019-12-09 23:47:21 +00:00
|
|
|
*/
|
|
|
|
export const decrypt = (data, key) => {
|
2019-12-12 20:20:56 +00:00
|
|
|
if (!key) {
|
|
|
|
return /** @type {PromiseLike<Uint8Array>} */ (promise.resolve(data))
|
2019-12-09 23:47:21 +00:00
|
|
|
}
|
2019-12-12 20:20:56 +00:00
|
|
|
const dataDecoder = decoding.createDecoder(data)
|
2019-12-09 23:47:21 +00:00
|
|
|
const algorithm = decoding.readVarString(dataDecoder)
|
|
|
|
if (algorithm !== 'AES-GCM') {
|
|
|
|
promise.reject(error.create('Unknown encryption algorithm'))
|
|
|
|
}
|
|
|
|
const iv = decoding.readVarUint8Array(dataDecoder)
|
|
|
|
const cipher = decoding.readVarUint8Array(dataDecoder)
|
|
|
|
return crypto.subtle.decrypt(
|
|
|
|
{
|
|
|
|
name: 'AES-GCM',
|
|
|
|
iv
|
|
|
|
},
|
|
|
|
key,
|
|
|
|
cipher
|
2019-12-12 20:20:56 +00:00
|
|
|
).then(data => new Uint8Array(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {Uint8Array} data
|
|
|
|
* @param {CryptoKey?} key
|
|
|
|
* @return {PromiseLike<Object>} decrypted object
|
|
|
|
*/
|
|
|
|
export const decryptJson = (data, key) =>
|
|
|
|
decrypt(data, key).then(decryptedValue =>
|
2019-12-09 23:47:21 +00:00
|
|
|
decoding.readAny(decoding.createDecoder(new Uint8Array(decryptedValue)))
|
|
|
|
)
|