-
-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
192 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* eslint-env browser */ | ||
|
||
import * as encoding from './encoding.js' | ||
import * as decoding from './decoding.js' | ||
import * as string from './string.js' | ||
import webcrypto from 'lib0/webcrypto' | ||
|
||
/** | ||
* @param {string | Uint8Array} data | ||
* @return {Uint8Array} | ||
*/ | ||
const toBinary = data => typeof data === 'string' ? string.encodeUtf8(data) : data | ||
|
||
/** | ||
* @experimental The API is not final! | ||
* | ||
* Derive an symmetric key using the Password-Based-Key-Derivation-Function-2. | ||
* | ||
* @param {string | Uint8Array} secret | ||
* @param {string | Uint8Array} salt | ||
* @param {Object} options | ||
* @param {boolean} [options.extractable] | ||
* @return {PromiseLike<CryptoKey>} | ||
*/ | ||
export const deriveSymmetricKey = (secret, salt, { extractable = false } = {}) => { | ||
const binSecret = toBinary(secret) | ||
const binSalt = toBinary(salt) | ||
return webcrypto.subtle.importKey( | ||
'raw', | ||
binSecret, | ||
'PBKDF2', | ||
false, | ||
['deriveKey'] | ||
).then(keyMaterial => | ||
webcrypto.subtle.deriveKey( | ||
{ | ||
name: 'PBKDF2', | ||
salt: binSalt, // NIST recommends at least 64 bits | ||
iterations: 600000, // OWASP recommends 600k iterations | ||
hash: 'SHA-256' | ||
}, | ||
keyMaterial, | ||
{ | ||
name: 'AES-GCM', | ||
length: 256 | ||
}, | ||
extractable, | ||
['encrypt', 'decrypt'] | ||
) | ||
) | ||
} | ||
|
||
/** | ||
* @experimental The API is not final! | ||
* | ||
* Encrypt some data using AES-GCM method. | ||
* | ||
* @param {Uint8Array} data data to be encrypted | ||
* @param {CryptoKey} key | ||
* @return {PromiseLike<Uint8Array>} encrypted, base64 encoded message | ||
*/ | ||
export const encrypt = (data, key) => { | ||
const iv = webcrypto.getRandomValues(new Uint8Array(16)) // 92bit is enough. 128bit is recommended if space is not an issue. | ||
return webcrypto.subtle.encrypt( | ||
{ | ||
name: 'AES-GCM', | ||
iv | ||
}, | ||
key, | ||
data | ||
).then(cipher => { | ||
const encryptedDataEncoder = encoding.createEncoder() | ||
// iv may be sent in the clear to the other peers | ||
encoding.writeUint8Array(encryptedDataEncoder, iv) | ||
encoding.writeVarUint8Array(encryptedDataEncoder, new Uint8Array(cipher)) | ||
return encoding.toUint8Array(encryptedDataEncoder) | ||
}) | ||
} | ||
|
||
/** | ||
* @experimental The API is not final! | ||
* | ||
* Decrypt some data using AES-GCM method. | ||
* | ||
* @param {Uint8Array} data | ||
* @param {CryptoKey} key | ||
* @return {PromiseLike<Uint8Array>} decrypted buffer | ||
*/ | ||
export const decrypt = (data, key) => { | ||
const dataDecoder = decoding.createDecoder(data) | ||
const iv = decoding.readUint8Array(dataDecoder, 16) | ||
const cipher = decoding.readVarUint8Array(dataDecoder) | ||
return webcrypto.subtle.decrypt( | ||
{ | ||
name: 'AES-GCM', | ||
iv | ||
}, | ||
key, | ||
cipher | ||
).then(data => new Uint8Array(data)) | ||
} | ||
|
||
export const exportKey = webcrypto.subtle.exportKey.bind(webcrypto.subtle) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import * as cryptutils from 'lib0/crypto' | ||
import * as t from './testing.js' | ||
import * as prng from './prng.js' | ||
|
||
/** | ||
* @param {t.TestCase} tc | ||
*/ | ||
export const testReapeatEncryption = async tc => { | ||
const secret = prng.word(tc.prng) | ||
const salt = prng.word(tc.prng) | ||
const data = prng.uint8Array(tc.prng, 1000000) | ||
|
||
/** | ||
* @type {any} | ||
*/ | ||
let encrypted | ||
/** | ||
* @type {any} | ||
*/ | ||
let decrypted | ||
/** | ||
* @type {any} | ||
*/ | ||
let key | ||
await t.measureTimeAsync('Key generation', async () => { | ||
key = await cryptutils.deriveSymmetricKey(secret, salt) | ||
}) | ||
await t.measureTimeAsync('Encryption', async () => { | ||
encrypted = await cryptutils.encrypt(data, key) | ||
}) | ||
t.info(`Byte length: ${data.byteLength}b`) | ||
t.info(`Encrypted length: ${encrypted.length}b`) | ||
await t.measureTimeAsync('Decryption', async () => { | ||
decrypted = await cryptutils.decrypt(encrypted, key) | ||
}) | ||
t.compare(data, decrypted) | ||
} | ||
|
||
/** | ||
* @param {t.TestCase} _tc | ||
*/ | ||
export const testConsistentKeyGeneration = async _tc => { | ||
const secret = 'qfycncpxhjktawlqkhc' | ||
const salt = 'my nonce' | ||
const expectedJwk = { | ||
key_ops: ['encrypt', 'decrypt'], | ||
ext: true, | ||
kty: 'oct', | ||
k: 'psAqoMh9apefdr8y1tdbNMVTLxb-tFekEFipYIOX5n8', | ||
alg: 'A256GCM' | ||
} | ||
const key = await cryptutils.deriveSymmetricKey(secret, salt, { extractable: true }) | ||
const jwk = await cryptutils.exportKey('jwk', key) | ||
t.compare(jwk, expectedJwk) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/* eslint-env browser */ | ||
|
||
export default crypto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
import { webcrypto } from 'node:crypto' | ||
|
||
export default webcrypto |