End-to-End Encryption
This topic includes details on using end-to-end encryption in the Vonage Video Linux client SDK:
- Setting the encryption secret
- Events and errors
- Setting a secret in a session without end-to-end encryption
For an overview of end-to-end encryption, see this topic.
Setting the encryption secret
End-to-end encrypted sessions are created using server APIs (see Enabling encryption using the REST API).
Before the client publishes or subscribes, call the otc_session_set_encryption_secret() function:
otc_session_set_encryption_secret(session, secret);
otc_session_connect(session, TOKEN);
A valid secret is a string between 8 and 256 characters. You can change the secret by calling the otc_session_set_encryption_secret() function again.
Passing an invalid secret will result in an OTC_SESSION_INVALID_ENCRYPTION_SECRET error.
Events and errors
Events and errors are essential to managing the behavior of user-driven encryption behavior. End-to-end encryption uses the shared secret model: everyone in the session is expected to use the same secret to encrypt their media and decrypt everyone else's.
The new error code is OTC_SESSION_INVALID_ENCRYPTION_SECRET and will be returned as the result code of the set_encryption_secret method, not in the on_error callback.
If a client tries to set an invalid encryption secret for a session, the otc_session_callbacks.on_error() returns an error code set to OTC_SESSION_INVALID_ENCRYPTION_SECRET. In the following example, a session is initialized with an empty (and thus invalid) encryption secret:
int result = otc_session_set_encryption_secret(session, "");
if (result == OTC_SESSION_INVALID_ENCRYPTION_SECRET) {
// Report error...
}
If a user tries to publish in an end-to-end encrypted session without having specified an encryption secret, calling the otc_session_publish() function results in the otc_publisher_callbacks.on_error() function being called with an error that has the code set to OTC_SESSION_ENCRYPTION_SECRET_MISSING. For the best user experience, the application should validate a user-supplied secret before calling the otc_session_publish() function:
static void on_publisher_error(otc_publisher *publisher,
void *user_data,
const char* error_string,
enum otc_publisher_error_code error_code) {
if (otc_publisher_error_code == OTC_SESSION_ENCRYPTION_SECRET_MISSING) {
// The application should communicate that the secret was not set.
}
}
struct otc_publisher_callbacks publisher_callbacks = {0};
publisher_callbacks.on_error = on_publisher_error;
session = otc_session_new(API_KEY, SESSION_ID, &session_callbacks);
otc_publisher *publisher = otc_publisher_new("Joe",
nullptr,
&publisher_callbacks);
otc_session_publish(session, publisher);
If a subscriber is unable to decode a stream's media due to an incorrect encryption secret, the subscriber_callbacks.on_error() function is called with an error that has the code set to OTC_SUBSCRIBER_ENCRYPTION_SECRET_MISMATCH. It is important to communicate to the user that media is not being received due to an encryption mismatch and not due to a connection failure or audio/video issue:
static void on_subscriber_error(otc_subscriber *subscriber,
void *user_data,
const char* error_string,
enum otc_subscriber_error_code error_code) {
if (otc_subscriber_error_code == OTC_SUBSCRIBER_ENCRYPTION_SECRET_MISMATCH) {
// Activate a UI element communicating that there's been an encryption secret mismatch.
}
}
struct otc_subscriber_callbacks subscriber_callbacks = {0};
subscriber_callbacks.on_error = on_subscriber_error;
otc_subscriber *subscriber = otc_subscriber_new(stream,
&subscriber_callbacks);
otc_session_subscribe(session, subscriber);
If a subscriber encounters an internal error while decrypting a packet, the subscriber_callbacks.on_error() function is called with an error that has the code set to OTC_SUBSCRIBER_DECRYPTION_INTERNAL_ERROR.
Setting a secret in a session without end-to-end encryption
Note: This behavior is available starting with version 2.36.0 of the SDK.
If a session was not created with end-to-end encryption enabled, setting an encryption secret for it results in an "encryption not enabled" error. Where the error surfaces depends on when the secret is set relative to connecting, publishing, and subscribing.
Setting the secret before connecting
Whether a session has end-to-end encryption enabled is only known once the client connects. If you call otc_session_set_encryption_secret() before connecting, the call succeeds and the session connects normally. The error is reported when the client publishes or subscribes: the otc_session_publish() function returns OTC_PUBLISHER_ENCRYPTION_NOT_ENABLED, and the otc_session_subscribe() function returns OTC_SUBSCRIBER_ENCRYPTION_NOT_ENABLED:
otc_status result = otc_session_publish(session, publisher);
if (result == OTC_PUBLISHER_ENCRYPTION_NOT_ENABLED) {
// The application should communicate that end-to-end encryption
// is not enabled for this session.
}
Setting the secret after connecting
If you call otc_session_set_encryption_secret() after the client has connected to a session without end-to-end encryption, the secret is not set and the function returns OTC_SESSION_ENCRYPTION_NOT_ENABLED:
otc_status result = otc_session_set_encryption_secret(session, secret);
if (result == OTC_SESSION_ENCRYPTION_NOT_ENABLED) {
// The application should communicate that end-to-end encryption
// is not enabled for this session.
}
Setting the secret while publishing or subscribing
If you call otc_session_set_encryption_secret() while the client is already publishing or subscribing, the call fails as described above, and every active publisher and subscriber is also removed from the session. The otc_publisher_callbacks.on_error() function is called for each publisher with an error that has the code set to OTC_PUBLISHER_ENCRYPTION_NOT_ENABLED, and the otc_subscriber_callbacks.on_error() function is called for each subscriber with an error that has the code set to OTC_SUBSCRIBER_ENCRYPTION_NOT_ENABLED.
Recovering from the error
The session remains valid after any of these errors. To publish or subscribe again, clear the secret by calling otc_session_set_encryption_secret() with NULL. The same otc_publisher object can then be passed to otc_session_publish() again:
otc_session_set_encryption_secret(session, NULL);
otc_session_publish(session, publisher);