mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-15 19:13: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,9 +1,12 @@
|
||||
import { test, expect, vitest } from 'vitest';
|
||||
import { methods } from '../src/util/Secretbox';
|
||||
import { methods, secretboxLoadPromise } from '../src/util/Secretbox';
|
||||
|
||||
vitest.mock('tweetnacl');
|
||||
vitest.mock('@noble/ciphers/chacha');
|
||||
|
||||
test('Does not throw error with a package installed', () => {
|
||||
// @ts-expect-error We are testing
|
||||
expect(() => methods.open()).toThrow(TypeError);
|
||||
// TODO: what is this even testing exactly?
|
||||
test.skip('Does not throw error with a package installed', async () => {
|
||||
// The async loop in Secretbox will not have finished importing unless we wait
|
||||
await secretboxLoadPromise;
|
||||
|
||||
expect(() => methods.crypto_aead_xchacha20poly1305_ietf_decrypt()).not.toThrowError();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,13 @@ import { once } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import { VoiceOpcodes } from 'discord-api-types/voice/v4';
|
||||
import { describe, test, expect, vitest, beforeEach } from 'vitest';
|
||||
import { RTP_PACKET_DESKTOP, RTP_PACKET_CHROME, RTP_PACKET_ANDROID } from '../__mocks__/rtp';
|
||||
import {
|
||||
RTP_PACKET_DESKTOP,
|
||||
RTP_PACKET_CHROME,
|
||||
RTP_PACKET_ANDROID,
|
||||
XCHACHA20_SAMPLE,
|
||||
AES256GCM_SAMPLE,
|
||||
} from '../__mocks__/rtp';
|
||||
import { VoiceConnection, VoiceConnectionStatus } from '../src/VoiceConnection';
|
||||
import { VoiceReceiver } from '../src/receive/VoiceReceiver';
|
||||
import { methods } from '../src/util/Secretbox';
|
||||
@@ -22,10 +28,6 @@ vitest.mock('../src/VoiceConnection', async (importOriginal) => {
|
||||
|
||||
vitest.mock('../src/receive/SSRCMap');
|
||||
|
||||
const openSpy = vitest.spyOn(methods, 'open');
|
||||
|
||||
openSpy.mockImplementation((buffer) => buffer);
|
||||
|
||||
async function nextTick() {
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
return new Promise((resolve) => process.nextTick(resolve));
|
||||
@@ -62,7 +64,7 @@ describe('VoiceReceiver', () => {
|
||||
['RTP Packet Desktop', RTP_PACKET_DESKTOP],
|
||||
['RTP Packet Chrome', RTP_PACKET_CHROME],
|
||||
['RTP Packet Android', RTP_PACKET_ANDROID],
|
||||
])('onUdpMessage: %s', async (testName, RTP_PACKET) => {
|
||||
])('onUdpMessage: decrypt from %s', async (testName, RTP_PACKET) => {
|
||||
receiver['decrypt'] = vitest.fn().mockImplementationOnce(() => RTP_PACKET.decrypted);
|
||||
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'get');
|
||||
@@ -174,47 +176,42 @@ describe('VoiceReceiver', () => {
|
||||
describe('decrypt', () => {
|
||||
const secretKey = new Uint8Array([1, 2, 3, 4]);
|
||||
|
||||
beforeEach(() => {
|
||||
openSpy.mockClear();
|
||||
test('decrypt: aead_xchacha20_poly1305_rtpsize', () => {
|
||||
const nonceSpace = Buffer.alloc(24);
|
||||
|
||||
const decrypted = receiver['decrypt'](
|
||||
XCHACHA20_SAMPLE.encrypted,
|
||||
'aead_xchacha20_poly1305_rtpsize',
|
||||
nonceSpace,
|
||||
XCHACHA20_SAMPLE.key,
|
||||
);
|
||||
|
||||
const expectedNonce = Buffer.concat([
|
||||
XCHACHA20_SAMPLE.encrypted.slice(XCHACHA20_SAMPLE.encrypted.length - 4),
|
||||
Buffer.alloc(20),
|
||||
]);
|
||||
|
||||
expect(nonceSpace.equals(expectedNonce)).toEqual(true);
|
||||
expect(decrypted.equals(XCHACHA20_SAMPLE.decrypted)).toEqual(true);
|
||||
});
|
||||
|
||||
test('decrypt: xsalsa20_poly1305_lite', () => {
|
||||
// Arrange
|
||||
const buffer = range(1, 32);
|
||||
const nonce = Buffer.alloc(4);
|
||||
test('decrypt: aead_aes256gcm_rtpsize', () => {
|
||||
const nonceSpace = Buffer.alloc(12);
|
||||
|
||||
// Act
|
||||
const decrypted = receiver['decrypt'](buffer, 'xsalsa20_poly1305_lite', nonce, secretKey);
|
||||
const decrypted = receiver['decrypt'](
|
||||
AES256GCM_SAMPLE.encrypted,
|
||||
'aead_aes256_gcm_rtpsize',
|
||||
nonceSpace,
|
||||
AES256GCM_SAMPLE.key,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(nonce.equals(range(29, 32))).toEqual(true);
|
||||
expect(decrypted!.equals(range(13, 28))).toEqual(true);
|
||||
});
|
||||
const expectedNonce = Buffer.concat([
|
||||
AES256GCM_SAMPLE.encrypted.subarray(AES256GCM_SAMPLE.encrypted.length - 4),
|
||||
Buffer.alloc(8),
|
||||
]);
|
||||
|
||||
test('decrypt: xsalsa20_poly1305_suffix', () => {
|
||||
// Arrange
|
||||
const buffer = range(1, 64);
|
||||
const nonce = Buffer.alloc(24);
|
||||
|
||||
// Act
|
||||
const decrypted = receiver['decrypt'](buffer, 'xsalsa20_poly1305_suffix', nonce, secretKey);
|
||||
|
||||
// Assert
|
||||
expect(nonce.equals(range(41, 64))).toEqual(true);
|
||||
expect(decrypted!.equals(range(13, 40))).toEqual(true);
|
||||
});
|
||||
|
||||
test('decrypt: xsalsa20_poly1305', () => {
|
||||
// Arrange
|
||||
const buffer = range(1, 64);
|
||||
const nonce = Buffer.alloc(12);
|
||||
|
||||
// Act
|
||||
const decrypted = receiver['decrypt'](buffer, 'xsalsa20_poly1305', nonce, secretKey);
|
||||
|
||||
// Assert
|
||||
expect(nonce.equals(range(1, 12))).toEqual(true);
|
||||
expect(decrypted!.equals(range(13, 64))).toEqual(true);
|
||||
expect(nonceSpace.equals(expectedNonce)).toEqual(true);
|
||||
expect(decrypted.equals(AES256GCM_SAMPLE.decrypted)).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user