Introduction

To improve data security, developers can encrypt users' media streams during the real-time engagement. Agora supports built-in encryption and customized encryption, and the differences between two encryption schemes are as follows:

  • Built-in encryption: The encryption mode and encryption key exist in the app and the SDK.
  • Customized encryption: The encryption mode and encryption key only exist in the app.

You can choose an encryption schema according to your needs.

  • Both the communication and live interactive streaming scenarios support encryption, but Agora does not support pushing encrypted streams to the CDN in a live-streaming channel.
  • Eure that both the receivers and senders use the same encryption scheme. Otherwise, undefined behaviors such as no voice and black screen may occour.
  • The following diagram describes the encrypted data transmission process:

    Implementation

    Before enabling the encryption, ensure that you have implemented the basic real-time communication functions in your project. For details, see the following documents:

    Additionally, the iOS SDK includes an independent encryption library AgoraRtcCryptoLoader.framework, and you need to integrate and import the encryption library as follows:

    1. To integrate the encryption library, do the following:

      Automatically integrate the encryption library with CocoaPods a. Ensure that you have installed CocoaPods before the following steps. See the installation guide in Getting Started with CocoaPods.

      b. In Terminal, go to the project path and run the pod init command to create a Podfile in the project folder.

      c. Open the Podfile, delete all contents and input the following contents. Remember to change Your App to the target name of your project, and change version to the version of the SDK which you want to integrate.

       # platform :ios, '9.0' use_frameworks!
       target 'Your App' do
         pod 'AgoraRtcEngine_iOS_Crypto', '~> version'
       end

      d. Go back to Terminal, and run the pod update command to update the local libraries.

      e. Run the pod install command to install the Agora SDK. Once you successfully install the SDK, it shows Pod installation complete! in Terminal, and you can see an xcworkspace file in the project folder.

      f. Open the generated xcworkspace file in Xcode.

      Manually integrate the encryption library a. Copy AgoraRtcCryptoLoader.framework from the SDK package to the project folder.

      b. Open Xcode (take the Xcode 11.0 as an example), go to the TARGETS > Project Name > General > Frameworks, Libraries, and Embedded Content menu, click Add Other... after clicking + to add AgoraRtcCryptoLoader.framework. To ensure that the signature of the dynamic library is the same as the signature of the app, you need to set the Embed attribute of the dynamic library to Embed & Sign.

      According to the requirement of Apple, the Extension of app cannot contain the dynamic library. If you need to integrate the SDK with the dynamic library in the Extension, change the file status as Do Not Embed.
    1. To import the AgoraRtcCryptoLoader library, refer to the following sample code:

      // Swift 
      #import <AgoraRtcCryptoLoader/AgoraRtcCryptoLoader.h>
      // Objective-C
      import AgoraRtcCryptoLoader

    Use the built-in encryption

    Before joining a channel, call enableEncryption to enable the built-in encryption, and set the encryption mode and encryption key.

    All users in the same channel must use the same encryption mode and encryption key.

    Agora supports the following encryption modes:

    • AgoraEncryptionModeAES128XTS: 128-bit AES encryption, XTS mode.
    • AgoraEncryptionModeAES128ECB: 128-bit AES encryption, ECB mode.
    • AgoraEncryptionModeAES256XTS: 256-bit AES encryption, XTS mode.

    Sample code

    // Swift
    // Creates an AgoraEncryptionConfig instance.
    let config = AgoraEncryptionConfig()
    // Sets the encryption mode as AgoraEncryptionModeAES128XTS.
    config.encryptionMode = AgoraEncryptionMode.AES128XTS
    // Sets the encryption key.
    config.encryptionKey = "xxxxxxxxxxxxxxxx"
    // Enables the built-in encryption.
    agoraKit.enableEncryption(true, config)
    // Objective-C
    // Creates an AgoraEncryptionConfig instance.
    AgoraEncryptionConfig *config = [[AgoraEncryptionConfig alloc] init];
    // Sets the encryption mode as AgoraEncryptionModeAES128XTS, and sets the encryption key.
    config.encryptionMode = AgoraEncryptionModeAES128XTS;
    config.encryptionKey = @"xxxxxxxxxxxxxxxx";
    // Enables the built-in encryption.
    [agoraKit enableEncryption: YES encryptionConfig:config];

    API reference

    enableEncryption

    Use the customized encryption

    To implement the customized encryption, use IPacketObserver class and registerPacketObserver in C++ as follows:

    1. Before joining a channel, call registerPacketObserver to register the packet observer, so that you can receive events during audio or video packet transmission.

        virtual int registerPacketObserver(IPacketObserver* observer);
    2. Implement an IPacketObserver class.

      class IPacketObserver
      {
      public:
      
      struct Packet
      {
      // Buffer address of the sent or received data.
      const unsigned char* buffer;
      // Buffer size of the sent or received data.
      unsigned int size;
      };
      
      // Occurs when the local user sends an audio packet.
      // The SDK triggers this callback before the audio packet is sent to the remote user.
      // @param packet See Packet.
      // @return
      // - true: The audio packet is sent successfully.
      // - false: The audio packet is discarded.
      virtual bool onSendAudioPacket(Packet& packet) = 0;
      
      // Occurs when the local user sends a video packet.
      // The SDK triggers this callback before the video packet is sent to the remote user.
      // @param packet See Packet.
      // @return
      // - true: The video packet is sent successfully.
      // - false: The video packet is discarded.
      virtual bool onSendVideoPacket(Packet& packet) = 0;
      
      // Occurs when the local user receives an audio packet.
      // The SDK triggers this callback before the audio packet of the remote user is received.
      // @param packet See Packet.
      // @return
      // - true: The audio packet is sent successfully.
      // - false: The audio packet is discarded.
      virtual bool onReceiveAudioPacket(Packet& packet) = 0;
      
      // Occurs when the local user receives a video packet.
      // The SDK triggers this callback before the video packet of the remote user is received.
      // @param packet See Packet.
      // @return
      // - true: The video packet is sent successfully.
      // - false: The video packet is discarded.
      virtual bool onReceiveVideoPacket(Packet& packet) = 0;
      };
    3. Inherit the IPacketObserver class and use your customized encryption algorithm on your app.

      class AgoraPacketObserver : public agora::IPacketObserver
       {
       public:
           AgoraPacketObserver()
           {
               m_txAudioBuffer.resize(2048);
               m_rxAudioBuffer.resize(2048);
               m_txVideoBuffer.resize(2048);
               m_rxVideoBuffer.resize(2048);
           }
           virtual bool onSendAudioPacket(Packet& packet)
           {
               int i;
               // Encrypts the packet.
               const unsigned char* p = packet.buffer;
               const unsigned char* pe = packet.buffer+packet.size;
      
               for (i = 0; p < pe && i < m_txAudioBuffer.size(); ++p, ++i)
               {
                   m_txAudioBuffer[i] = *p ^ 0x55;
               }
               // Sends the buffer and size of the encrypted data to the SDK.
               packet.buffer = &m_txAudioBuffer[0];
               packet.size = i;
               return true;
           }
      
           virtual bool onSendVideoPacket(Packet& packet)
           {
               int i;
               // Encrypts the packet.
               const unsigned char* p = packet.buffer;
               const unsigned char* pe = packet.buffer+packet.size;
               for (i = 0; p < pe && i < m_txVideoBuffer.size(); ++p, ++i)
               {
                   m_txVideoBuffer[i] = *p ^ 0x55;
               }
               // Sends the buffer and size of the encrypted data to the SDK.
               packet.buffer = &m_txVideoBuffer[0];
               packet.size = i;
               return true;
           }
      
           virtual bool onReceiveAudioPacket(Packet& packet)
           {
               int i = 0;
               // Decrypts the packet.
               const unsigned char* p = packet.buffer;
               const unsigned char* pe = packet.buffer+packet.size;
               for (i = 0; p < pe && i < m_rxAudioBuffer.size(); ++p, ++i)
               {
                   m_rxAudioBuffer[i] = *p ^ 0x55;
               }
               // Sends the buffer and size of the decrypted data to the SDK.
               packet.buffer = &m_rxAudioBuffer[0];
               packet.size = i;
               return true;
           }
      
           virtual bool onReceiveVideoPacket(Packet& packet)
           {
               int i = 0;
               // Decrypts the packet.
               const unsigned char* p = packet.buffer;
               const unsigned char* pe = packet.buffer+packet.size;
      
               for (i = 0; p < pe && i < m_rxVideoBuffer.size(); ++p, ++i)
               {
                   m_rxVideoBuffer[i] = *p ^ 0x55;
               }
               // Sends the buffer and size of the decrypted data to the SDK.
               packet.buffer = &m_rxVideoBuffer[0];
               packet.size = i;
               return true;
           }
      
       private:
           std::vector<unsigned char> m_txAudioBuffer; // Buffer for sending the audio data
           std::vector<unsigned char> m_txVideoBuffer; // Buffer for sending the video data
      
           std::vector<unsigned char> m_rxAudioBuffer; // Buffer for receiving the audio data
           std::vector<unsigned char> m_rxVideoBuffer; // Buffer for receiving the video data
       };
    4. Call registerPacketObserver to register the IPacketObserver instance.

    API reference

    registerPacketObserver