#include #include #include #include /* Encryption is also supported by uvgRTP. Encryption is facilitated * by Secure RTP (SRTP) protocol. In order to use SRTP, the encryption * context must be exchanged in some way. uvgRTP offers two main methods * for exchanging the encryption contexts. * * This example presents the user implemented encryption key * negotiation. In this scenario the encryption keys are exchanged * by the application and handed over to uvgRTP. */ // network parameters of example constexpr char SENDER_ADDRESS[] = "127.0.0.1"; constexpr uint16_t LOCAL_PORT = 8888; constexpr char RECEIVER_ADDRESS[] = "127.0.0.1"; constexpr uint16_t REMOTE_PORT = 8890; // encryption parameters of example enum Key_length{SRTP_128 = 128, SRTP_196 = 196, SRTP_256 = 256}; constexpr Key_length KEY_SIZE = SRTP_256; constexpr int KEY_SIZE_BYTES = KEY_SIZE/8; constexpr int SALT_SIZE = 112; constexpr int SALT_SIZE_BYTES = SALT_SIZE/8; // demonstration parameters constexpr auto EXAMPLE_DURATION = std::chrono::seconds(5); constexpr int FRAME_RATE = 30; // fps constexpr int SEND_TEST_PACKETS = (EXAMPLE_DURATION.count() - 1)*FRAME_RATE; constexpr int PACKET_INTERVAL_MS = 1000/FRAME_RATE; constexpr int RECEIVER_WAIT_TIME_MS = 100; void process_frame(uvgrtp::frame::rtp_frame *frame); void receive_func(uint8_t key[KEY_SIZE_BYTES], uint8_t salt[SALT_SIZE_BYTES]); void wait_until_next_frame(std::chrono::steady_clock::time_point& start, int frame_index); int main(void) { uvgrtp::context ctx; // first we check if crypto has been included before attempting to use it if (!ctx.crypto_enabled()) { std::cerr << "Cannot run SRTP example if crypto is not included in uvgRTP!" << std::endl; return EXIT_FAILURE; } /* Key and salt for the SRTP session of sender and receiver * * NOTE: uvgRTP supports 128, 196 and 256 bit keys and 112 bit salts */ uint8_t key[KEY_SIZE_BYTES] = { 0 }; uint8_t salt[SALT_SIZE_BYTES] = { 0 }; // initialize SRTP key and salt with dummy values for (int i = 0; i < KEY_SIZE_BYTES; ++i) key[i] = i; for (int i = 0; i < SALT_SIZE_BYTES; ++i) salt[i] = i * 2; std::cout << "Starting uvgRTP SRTP user provided encryption key example. Using key:" << key << " and salt: " << salt << std::endl; // Create separate thread for the receiver std::thread receiver(receive_func, key, salt); // Enable SRTP and let user manage the keys unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER | RCE_SRTP_KEYSIZE_256; uvgrtp::session *sender_session = ctx.create_session(RECEIVER_ADDRESS); uvgrtp::media_stream *send = sender_session->create_stream(LOCAL_PORT, REMOTE_PORT, RTP_FORMAT_GENERIC, flags); if (send) { /* When using user managed keys, before anything else can be done * the add_srtp_ctx() must be called with the user SRTP key and salt. * * All calls to "send" that try to modify and or/use the newly * created media stream before calling add_srtp_ctx() will fail * if SRTP is enabled */ send->add_srtp_ctx(key, salt); // All media is now encrypted/decrypted automatically char* message = (char*)"Hello, world!"; size_t msg_len = strlen(message); auto start = std::chrono::steady_clock::now(); for (unsigned int i = 0; i < SEND_TEST_PACKETS; ++i) { if ((i+1)%10 == 0 || i == 0) // print every 10 frames and first std::cout << "Sending frame # " << i + 1 << '/' << SEND_TEST_PACKETS << std::endl; uint8_t* message_data = new uint8_t[msg_len]; memcpy(message_data, message, msg_len); if (send->push_frame((uint8_t *)message_data, msg_len, RTP_NO_FLAGS) != RTP_OK) { std::cerr << "Failed to send frame" << std::endl; } // wait until it is time to send the next frame. Included only for // demostration purposes since you can use uvgRTP to send packets as fast as desired wait_until_next_frame(start, i); } sender_session->destroy_stream(send); } else { std::cerr << "Failed to create SRTP sender" << std::endl; } if (receiver.joinable()) { receiver.join(); } if (sender_session) ctx.destroy_session(sender_session); return EXIT_SUCCESS; } void receive_func(uint8_t key[KEY_SIZE_BYTES], uint8_t salt[SALT_SIZE_BYTES]) { /* See sending.cc for more details */ uvgrtp::context ctx; uvgrtp::session *receiver_session = ctx.create_session(SENDER_ADDRESS); /* Enable SRTP and let user manage keys */ unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER; /* With user-managed keys, you have the option to use 192- and 256-bit keys. * * If 192- or 256-bit key size is specified in the flags, add_srtp_ctx() expects * the key parameter to be 24 or 32 bytes long, respectively. */ flags |= RCE_SRTP_KEYSIZE_256; /* See sending.cc for more details about create_stream() */ uvgrtp::media_stream *recv = receiver_session->create_stream(REMOTE_PORT, LOCAL_PORT, RTP_FORMAT_GENERIC, flags); // Receive frames by pulling for EXAMPLE_DURATION milliseconds if (recv) { /* Before anything else can be done, * add_srtp_ctx() must be called with the SRTP key and salt. * * All calls to "recv" that try to modify and or/use the newly * created media stream before calling add_srtp_ctx() will fail */ recv->add_srtp_ctx(key, salt); std::cout << "Start receiving frames for " << EXAMPLE_DURATION.count() << " s" << std::endl; auto start = std::chrono::steady_clock::now(); uvgrtp::frame::rtp_frame *frame = nullptr; while (std::chrono::steady_clock::now() - start < EXAMPLE_DURATION) { /* You can specify a timeout for the operation and if the a frame is not received * within that time limit, pull_frame() returns a nullptr * * The parameter tells how long time a frame is waited in milliseconds */ frame = recv->pull_frame(RECEIVER_WAIT_TIME_MS); if (frame) { process_frame(frame); } } receiver_session->destroy_stream(recv); } if (receiver_session) { ctx.destroy_session(receiver_session); } } void process_frame(uvgrtp::frame::rtp_frame *frame) { std::string payload = std::string((char*)frame->payload, frame->payload_len); std::cout << "Received SRTP frame. Payload: " << payload << std::endl; /* When we receive a frame, the ownership of the frame belongs to us and * when we're done with it, we need to deallocate the frame */ (void)uvgrtp::frame::dealloc_frame(frame); } void wait_until_next_frame(std::chrono::steady_clock::time_point &start, int frame_index) { // wait until it is time to send the next frame. Simulates a steady sending pace // and included only for demostration purposes since you can use uvgRTP to send // packets as fast as desired auto time_since_start = std::chrono::steady_clock::now() - start; auto next_frame_time = (frame_index + 1)*std::chrono::milliseconds(PACKET_INTERVAL_MS); if (next_frame_time > time_since_start) { std::this_thread::sleep_for(next_frame_time - time_since_start); } }