All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dv8tion.jda.internal.audio.CryptoAdapter Maven / Gradle / Ivy

Go to download

Java wrapper for the popular chat & VOIP service: Discord https://discord.com

The newest version!
/*
 * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.dv8tion.jda.internal.audio;

import com.google.crypto.tink.aead.internal.InsecureNonceAesGcmJce;
import com.google.crypto.tink.aead.internal.InsecureNonceXChaCha20Poly1305;
import net.dv8tion.jda.internal.utils.IOUtil;

import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.EnumSet;

public interface CryptoAdapter
{
    String AES_GCM_NO_PADDING = "AES_256/GCM/NOPADDING";

    AudioEncryption getMode();

    ByteBuffer encrypt(ByteBuffer output, ByteBuffer audio);

    byte[] decrypt(ByteBuffer packet);

    static AudioEncryption negotiate(EnumSet supportedModes)
    {
        for (AudioEncryption mode : AudioEncryption.values())
        {
            if (supportedModes.contains(mode) && isModeSupported(mode))
                return mode;
        }

        return null;
    }

    static boolean isModeSupported(AudioEncryption mode)
    {
        switch (mode)
        {
        case AEAD_AES256_GCM_RTPSIZE:
            return Security.getAlgorithms("Cipher").contains(AES_GCM_NO_PADDING);
        case AEAD_XCHACHA20_POLY1305_RTPSIZE:
            return true;
        default:
            return false;
        }
    }

    static CryptoAdapter getAdapter(AudioEncryption mode, byte[] secretKey)
    {
        switch (mode)
        {
        case AEAD_AES256_GCM_RTPSIZE:
            return new CryptoAdapter.AES_GCM_Adapter(secretKey);
        case AEAD_XCHACHA20_POLY1305_RTPSIZE:
            return new XChaCha20Poly1305Adapter(secretKey);
        default:
            throw new IllegalStateException("Unsupported encryption mode: " + mode);
        }
    }

    abstract class AbstractAaedAdapter implements CryptoAdapter
    {
        protected static final int nonceBytes = 4;
        protected static final SecureRandom random = new SecureRandom();

        protected final byte[] secretKey;
        protected final byte[] nonceBuffer;
        protected final int tagBytes;
        protected final int paddedNonceBytes;
        protected int encryptCounter;

        protected AbstractAaedAdapter(byte[] secretKey, int tagBytes, int paddedNonceBytes)
        {
            this.secretKey = secretKey;
            this.tagBytes = tagBytes;
            this.paddedNonceBytes = paddedNonceBytes;
            this.nonceBuffer = new byte[paddedNonceBytes];
            this.encryptCounter = Math.abs(random.nextInt()) % 513 + 1;
        }

        @Override
        public ByteBuffer encrypt(ByteBuffer output, ByteBuffer audio)
        {
            int minimumOutputSize = audio.remaining() + this.tagBytes + nonceBytes;

            if (output.remaining() < minimumOutputSize)
            {
                ByteBuffer newBuffer = ByteBuffer.allocate(output.capacity() + minimumOutputSize);
                output.flip();
                newBuffer.put(output);
                output = newBuffer;
            }

            IOUtil.setIntBigEndian(nonceBuffer, 0, encryptCounter);

            try
            {
                encryptInternally(output, audio, nonceBuffer);
                output.putInt(encryptCounter++);
                return output;
            }
            catch (Exception e)
            {
                throw new RuntimeException(e);
            }
        }

        @Override
        public byte[] decrypt(ByteBuffer packet)
        {
            try
            {
                int headerLength = packet.position();
                packet.position(0);
                byte[] associatedData = new byte[headerLength];
                packet.get(associatedData);
                byte[] cipherText = new byte[packet.remaining() - nonceBytes];
                packet.get(cipherText);
                byte[] nonce = new byte[paddedNonceBytes];
                packet.get(nonce, 0, nonceBytes);
                return decryptInternally(cipherText, associatedData, nonce);
            }
            catch (Exception e)
            {
                throw new RuntimeException(e);
            }
        }

        protected abstract void encryptInternally(ByteBuffer output, ByteBuffer audio, byte[] nonce) throws Exception;
        protected abstract byte[] decryptInternally(byte[] cipherText, byte[] associatedData, byte[] nonce) throws Exception;

        protected byte[] getAssociatedData(ByteBuffer output)
        {
            return Arrays.copyOfRange(output.array(), output.arrayOffset(), output.arrayOffset() + output.position());
        }

        protected byte[] getPlaintextCopy(ByteBuffer audio)
        {
            return Arrays.copyOfRange(audio.array(), audio.arrayOffset() + audio.position(), audio.arrayOffset() + audio.limit());
        }
    }

    class AES_GCM_Adapter extends AbstractAaedAdapter implements CryptoAdapter
    {
        public AES_GCM_Adapter(byte[] secretKey)
        {
            super(secretKey, 16, 12);
        }

        @Override
        public AudioEncryption getMode()
        {
            return AudioEncryption.AEAD_AES256_GCM_RTPSIZE;
        }

        @Override
        protected void encryptInternally(ByteBuffer output, ByteBuffer audio, byte[] nonce) throws Exception
        {
            InsecureNonceAesGcmJce cipher = getCipher();
            byte[] input = getPlaintextCopy(audio);
            byte[] associatedData = getAssociatedData(output);
            output.put(cipher.encrypt(nonce, input, associatedData));
        }

        @Override
        public byte[] decryptInternally(byte[] cipherText, byte[] associatedData, byte[] nonce) throws Exception
        {
            InsecureNonceAesGcmJce cipher = getCipher();
            return cipher.decrypt(nonce, cipherText, associatedData);
        }

        private InsecureNonceAesGcmJce getCipher() throws GeneralSecurityException
        {
            return new InsecureNonceAesGcmJce(secretKey);
        }
    }

    class XChaCha20Poly1305Adapter extends AbstractAaedAdapter implements CryptoAdapter
    {
        public XChaCha20Poly1305Adapter(byte[] secretKey)
        {
            super(secretKey, 16, 24);
        }

        @Override
        public AudioEncryption getMode()
        {
            return AudioEncryption.AEAD_XCHACHA20_POLY1305_RTPSIZE;
        }

        @Override
        public void encryptInternally(ByteBuffer output, ByteBuffer audio, byte[] nonce) throws Exception
        {
            InsecureNonceXChaCha20Poly1305 cipher = getCipher();
            byte[] input = getPlaintextCopy(audio);
            byte[] associatedData = getAssociatedData(output);
            output.put(cipher.encrypt(nonce, input, associatedData));
        }

        @Override
        public byte[] decryptInternally(byte[] cipherText, byte[] associatedData, byte[] nonce) throws Exception
        {
            InsecureNonceXChaCha20Poly1305 cipher = getCipher();
            return cipher.decrypt(nonce, cipherText, associatedData);
        }

        private InsecureNonceXChaCha20Poly1305 getCipher() throws GeneralSecurityException
        {
            return new InsecureNonceXChaCha20Poly1305(secretKey);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy