mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-13 10:03:31 +01:00
feat(voice)!: add new encryption methods, remove old methods (#10451)
BREAKING CHANGE: This library no longer supports using `tweetnacl` as an encryption library due to Discord deprecating the algorithms that `tweetnacl` helped us support (read more [here](https://discord.com/developers/docs/change-log#voice-encryption-modes)). Please migrate to one of: `sodium-native`, `sodium`, `@stablelib/xchacha20poly1305`, `@noble/ciphers` or `libsodium-wrappers` unless your system supports `aes-256-gcm` (verify by running `require('node:crypto').getCiphers().includes('aes-256-gcm')`). --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
This commit is contained in:
@@ -1,75 +1,135 @@
|
||||
import { Buffer } from 'node:buffer';
|
||||
|
||||
interface Methods {
|
||||
close(opusPacket: Buffer, nonce: Buffer, secretKey: Uint8Array): Buffer;
|
||||
open(buffer: Buffer, nonce: Buffer, secretKey: Uint8Array): Buffer | null;
|
||||
random(bytes: number, nonce: Buffer): Buffer;
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
cipherText: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
): Buffer;
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
plaintext: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
): Buffer;
|
||||
}
|
||||
|
||||
const libs = {
|
||||
'sodium-native': (sodium: any): Methods => ({
|
||||
open: (buffer: Buffer, nonce: Buffer, secretKey: Uint8Array) => {
|
||||
if (buffer) {
|
||||
const output = Buffer.allocUnsafe(buffer.length - sodium.crypto_box_MACBYTES);
|
||||
if (sodium.crypto_secretbox_open_easy(output, buffer, nonce, secretKey)) return output;
|
||||
}
|
||||
|
||||
return null;
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (
|
||||
cipherText: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
) => {
|
||||
const message = Buffer.alloc(cipherText.length - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES);
|
||||
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(message, null, cipherText, additionalData, nonce, key);
|
||||
return message;
|
||||
},
|
||||
close: (opusPacket: Buffer, nonce: Buffer, secretKey: Uint8Array) => {
|
||||
const output = Buffer.allocUnsafe(opusPacket.length + sodium.crypto_box_MACBYTES);
|
||||
sodium.crypto_secretbox_easy(output, opusPacket, nonce, secretKey);
|
||||
return output;
|
||||
},
|
||||
random: (num: number, buffer: Buffer = Buffer.allocUnsafe(num)) => {
|
||||
sodium.randombytes_buf(buffer);
|
||||
return buffer;
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (
|
||||
plaintext: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
) => {
|
||||
const cipherText = Buffer.alloc(plaintext.length + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES);
|
||||
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, plaintext, additionalData, null, nonce, key);
|
||||
return cipherText;
|
||||
},
|
||||
}),
|
||||
sodium: (sodium: any): Methods => ({
|
||||
open: sodium.api.crypto_secretbox_open_easy,
|
||||
close: sodium.api.crypto_secretbox_easy,
|
||||
random: (num: number, buffer: Buffer = Buffer.allocUnsafe(num)) => {
|
||||
sodium.api.randombytes_buf(buffer);
|
||||
return buffer;
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (
|
||||
cipherText: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
) => {
|
||||
return sodium.api.crypto_aead_xchacha20poly1305_ietf_decrypt(cipherText, additionalData, null, nonce, key);
|
||||
},
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (
|
||||
plaintext: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
) => {
|
||||
return sodium.api.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key);
|
||||
},
|
||||
}),
|
||||
'libsodium-wrappers': (sodium: any): Methods => ({
|
||||
open: sodium.crypto_secretbox_open_easy,
|
||||
close: sodium.crypto_secretbox_easy,
|
||||
random: sodium.randombytes_buf,
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (
|
||||
cipherText: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
) => {
|
||||
return sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, cipherText, additionalData, nonce, key);
|
||||
},
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (
|
||||
plaintext: Buffer,
|
||||
additionalData: Buffer,
|
||||
nonce: Buffer,
|
||||
key: ArrayBufferLike,
|
||||
) => {
|
||||
return sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key);
|
||||
},
|
||||
}),
|
||||
tweetnacl: (tweetnacl: any): Methods => ({
|
||||
open: tweetnacl.secretbox.open,
|
||||
close: tweetnacl.secretbox,
|
||||
random: tweetnacl.randomBytes,
|
||||
'@stablelib/xchacha20poly1305': (stablelib: any): Methods => ({
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, nonce, key) {
|
||||
const crypto = new stablelib.XChaCha20Poly1305(key);
|
||||
return crypto.open(nonce, plaintext, additionalData);
|
||||
},
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, additionalData, nonce, key) {
|
||||
const crypto = new stablelib.XChaCha20Poly1305(key);
|
||||
return crypto.seal(nonce, cipherText, additionalData);
|
||||
},
|
||||
}),
|
||||
'@noble/ciphers/chacha': (noble: any): Methods => ({
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt(cipherText, additionalData, nonce, key) {
|
||||
const chacha = noble.xchacha20poly1305(key, nonce, additionalData);
|
||||
return chacha.decrypt(cipherText);
|
||||
},
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, nonce, key) {
|
||||
const chacha = noble.xchacha20poly1305(key, nonce, additionalData);
|
||||
return chacha.encrypt(plaintext);
|
||||
},
|
||||
}),
|
||||
} as const;
|
||||
|
||||
const fallbackError = () => {
|
||||
throw new Error(
|
||||
`Cannot play audio as no valid encryption package is installed.
|
||||
- Install sodium, libsodium-wrappers, or tweetnacl.
|
||||
- Install one of:
|
||||
- sodium
|
||||
- libsodium-wrappers
|
||||
- @stablelib/xchacha20poly1305
|
||||
- @noble/ciphers.
|
||||
- Use the generateDependencyReport() function for more information.\n`,
|
||||
);
|
||||
};
|
||||
|
||||
const methods: Methods = {
|
||||
open: fallbackError,
|
||||
close: fallbackError,
|
||||
random: fallbackError,
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt: fallbackError,
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt: fallbackError,
|
||||
};
|
||||
|
||||
void (async () => {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
export const secretboxLoadPromise = new Promise<void>(async (resolve) => {
|
||||
for (const libName of Object.keys(libs) as (keyof typeof libs)[]) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
||||
const lib = require(libName);
|
||||
if (libName === 'libsodium-wrappers' && lib.ready) await lib.ready;
|
||||
const lib = await import(libName);
|
||||
|
||||
if (libName === 'libsodium-wrappers' && lib.ready) {
|
||||
await lib.ready;
|
||||
}
|
||||
|
||||
Object.assign(methods, libs[libName](lib));
|
||||
|
||||
break;
|
||||
} catch {}
|
||||
}
|
||||
})();
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
export { methods };
|
||||
|
||||
@@ -68,7 +68,8 @@ export function generateDependencyReport() {
|
||||
addVersion('sodium-native');
|
||||
addVersion('sodium');
|
||||
addVersion('libsodium-wrappers');
|
||||
addVersion('tweetnacl');
|
||||
addVersion('@stablelib/xchacha20poly1305');
|
||||
addVersion('@noble/ciphers');
|
||||
report.push('');
|
||||
|
||||
// ffmpeg
|
||||
|
||||
Reference in New Issue
Block a user