Finally, I managed it to work, I traced every packet on WireShark and got some idea. Here is my implementation if you are all interested. Here is my implementation:
#include "api/crypto/octon_frame_decryptor.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <stddef.h>
#include <stdio.h>
#include <numeric>
#include <vector>
#include "rtc_base/logging.h"
namespace webrtc {
OctonFrameDecryptor::FrameDecryptor(const std::vector<uint8_t>& new_keys)
: key_bytes(new_keys) {
RTC_LOG(LS_VERBOSE) << "XXX OctonFrameDecryptor " << key_bytes.size();
}
int decrypt(unsigned char* key,
unsigned char* ciphertext,
int ciphertext_len,
unsigned char* iv,
unsigned char* plaintext) {
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
RTC_LOG(LS_INFO) << "Error in Decryption 100";
return -1;
}
int len;
int plaintext_len;
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1) {
RTC_LOG(LS_INFO) << "Error in Decryption 200";
EVP_CIPHER_CTX_free(ctx);
return -1;
}
if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) !=
1) {
RTC_LOG(LS_INFO) << "Error in Decryption 300";
EVP_CIPHER_CTX_free(ctx);
return -1;
}
plaintext_len = len;
if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) {
RTC_LOG(LS_INFO) << "Error in Decryption 400";
EVP_CIPHER_CTX_free(ctx);
return -1;
}
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
return plaintext_len;
}
bool matchesPattern(const std::vector<uint8_t>& data,
uint8_t nal_unit_type,
size_t nal_header_size = 5) {
// Check for the start code 0x00000001 and NAL unit type
if (data.size() < nal_header_size)
return false;
return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 &&
data[3] == 0x01 && (data[4] & 0x1F) == nal_unit_type;
}
int find_index_after_pattern(const std::vector<uint8_t>& encrypted_data,
uint8_t nal_unit_type,
size_t nal_header_size) {
size_t encrypted_size = encrypted_data.size();
for (size_t i = 0; i <= encrypted_size - nal_header_size; i++) {
if (matchesPattern(
std::vector<uint8_t>(encrypted_data.begin() + i,
encrypted_data.begin() + i + nal_header_size),
nal_unit_type, nal_header_size)) {
if (i + 5 < encrypted_size) {
return static_cast<int>(i);
} else {
return -1; // Pattern found, but no value follows
}
}
}
return -1; // Pattern not found
}
std::vector<uint8_t> GenerateIVDecryptor(const uint8_t* unencrypted_data,
size_t unencrypted_bytes) {
// implement by yourself
}
OctonFrameDecryptor::Result OctonFrameDecryptor::Decrypt(
cricket::MediaType media_type,
const std::vector<uint32_t>& csrcs,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> encrypted_frame,
rtc::ArrayView<uint8_t> frame) {
RTC_LOG(LS_VERBOSE) << "[Decryptor] Ivan decrypting";
const uint8_t* encrypted_data = encrypted_frame.data();
const size_t encrypted_size = encrypted_frame.size();
const size_t frame_size = frame.size();
uint8_t unencrypted_bytes = 1;
size_t nal_header_size = 0;
enum nal_unit_type {
NAL_UNIT_TYPE_NON_IDR = 1,
NAL_UNIT_TYPE_IDR = 5,
NAL_UNIT_TYPE_SPS = 7,
NAL_UNIT_TYPE_PPS = 8
};
if (media_type == cricket::MEDIA_TYPE_VIDEO) {
unencrypted_bytes = 12;
nal_header_size = 5;
} else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
unencrypted_bytes = 1;
}
if (encrypted_size < unencrypted_bytes) {
RTC_LOG(LS_INFO) << "[Decryptor] Ivan, Memory error";
return Result(Status::kFailedToDecrypt, 0);
}
// Check for SPS and PPS NAL units
if (media_type == cricket::MEDIA_TYPE_VIDEO) {
if ((matchesPattern(std::vector<uint8_t>(encrypted_data,
encrypted_data + nal_header_size),
NAL_UNIT_TYPE_SPS) ||
matchesPattern(std::vector<uint8_t>(encrypted_data,
encrypted_data + nal_header_size),
NAL_UNIT_TYPE_PPS))) {
RTC_LOG(LS_INFO)
<< "[Decryptor] Ivan, Skip Decryption on SPS/PPS with length "
<< encrypted_size;
// Find encrypted IDR Frame
int payload_start = find_index_after_pattern(
std::vector<uint8_t>(encrypted_data, encrypted_data + encrypted_size),
NAL_UNIT_TYPE_IDR, nal_header_size);
// If found, decrypt IDR Frame
if (payload_start != -1) {
// Copy payload before IDR Frame to frame
std::copy(encrypted_data, encrypted_data + payload_start,
frame.begin());
// Copy unencrypted bytes to frame
std::copy(encrypted_data + payload_start,
encrypted_data + payload_start + unencrypted_bytes,
frame.begin() + payload_start);
size_t payload_length = encrypted_size - payload_start;
std::vector<uint8_t> payload(payload_length);
std::copy(encrypted_data + payload_start + unencrypted_bytes,
encrypted_data + encrypted_size, payload.begin());
std::vector<uint8_t> iv = GenerateIVDecryptor(
encrypted_data + payload_start + nal_header_size,
unencrypted_bytes - nal_header_size);
std::vector<unsigned char> decryptedtext(payload_length);
int decryptedtext_len =
decrypt(key_bytes.data(), payload.data(), payload_length, iv.data(),
decryptedtext.data());
if (decryptedtext_len > 0) {
std::copy(decryptedtext.begin(),
decryptedtext.begin() + decryptedtext_len,
frame.begin() + payload_start + unencrypted_bytes);
return Result(Status::kOk, frame_size);
} else {
RTC_LOG(LS_INFO) << "[Decryptor] Ivan, Decryption Failed";
return Result(Status::kFailedToDecrypt, 0);
}
} else {
return Result(Status::kOk, frame_size);
}
}
}
std::copy(encrypted_data, encrypted_data + unencrypted_bytes, frame.begin());
size_t payload_length = encrypted_size - unencrypted_bytes;
std::vector<uint8_t> payload(payload_length);
std::copy(encrypted_data + unencrypted_bytes, encrypted_data + encrypted_size,
payload.begin());
std::vector<uint8_t> iv = GenerateIVDecryptor(
encrypted_data + nal_header_size, unencrypted_bytes - nal_header_size);
std::vector<unsigned char> decryptedtext(payload_length);
int decryptedtext_len =
decrypt(key_bytes.data(), payload.data(), payload_length, iv.data(),
decryptedtext.data());
if (decryptedtext_len > 0) {
std::copy(decryptedtext.begin(), decryptedtext.begin() + decryptedtext_len,
frame.begin() + unencrypted_bytes);
return Result(Status::kOk, frame_size);
} else {
RTC_LOG(LS_INFO) << "[Decryptor] Ivan, Decryption Failed";
return Result(Status::kFailedToDecrypt, 0);
}
}
size_t OctonFrameDecryptor::GetMaxPlaintextByteSize(
cricket::MediaType media_type,
size_t encrypted_frame_size) {
return encrypted_frame_size;
}
} // namespace webrtc