End-to-End Encryption

This topic includes details on using end-to-end encryption in the Vonage Video iOS Swift client SDK:

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 Session.setEncryptionSecret() method:

var error: OTError?
session.setEncryptionSecret("encryption-secret", error: &error)
if (error)
{
    // Notify the user.
}

A valid secret is a string between 8 and 256 characters. You can change the secret by calling the Session.setEncryptionSecret() method again.

Setting an invalid secret will result in an InvalidEncryptionSecret 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.

If the client tries to connect to an end-to-end encrypted session and does not set the encryption secret before connecting, an error with the code set to EncryptionSecretMissing:

var error: OTError?
session.connect(withToken: token, error: &error)
if (error && (error.code ==EncryptionSecretMissing))
{
    // Notify the user of the error connecting.
}

If a user tries to publish in an end-to-end encrypted session without having specified an encryption secret, calling the [OTSession publish:error:] method results in an error that has the code set to EncryptionSecretMissing. For the best user experience, the application should validate a user-supplied secret before publishing:

var error: OTError?
session.publish(publisher, error: &error)
if (error && (error.code == EncryptionSecretMissing))
{
    // The application should communicate that the secret was not set.
}

If a subscriber is unable to decode a stream's media due to an incorrect encryption secret, the [OTSubscriberKitDelegate subscriber:didFailWithError:] message is sent with an error that has the code set to ErrorCode.EncryptionSecretMismatch. 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:

// Implementation of [OTSubscriberKitDelegate subscriber:didFailWithError:]:
func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) {
	if (error.code == EncryptionSecretMismatch) {
    // Activate a UI element communicating that there's been an encryption secret mismatch.
	}
}

var error: OTError?
session.subscribe(subscriber!, error: &error)
// ...

If a subscriber encounters an internal error while decrypting a packet, the [OTSubscriberKitDelegate subscriber:didFailWithError:] message is sent with an error that has the code set to OTSubscriberDecryptionInternalError.

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 OTSession.setEncryptionSecret(_:error:) before connecting, the call succeeds and the session connects normally. The error is reported when the client publishes or subscribes: calling OTSession.publish(_:error:) results in an error with the code set to OTPublisherErrorCode.encryptionNotEnabled, and calling OTSession.subscribe(_:error:) results in an error with the code set to OTSubscriberErrorCode.encryptionNotEnabled:

var error: OTError?
session.publish(publisher, error: &error)
if let error = error, error.code == Int(OTPublisherErrorCode.encryptionNotEnabled.rawValue) {
    // The application should communicate that end-to-end encryption
    // is not enabled for this session.
}

Setting the secret after connecting

If you call OTSession.setEncryptionSecret(_:error:) after the client has connected to a session without end-to-end encryption, the secret is not set and the method returns an error with the code set to OTSessionErrorCode.encryptionNotEnabled:

var error: OTError?
session.setEncryptionSecret("encryption-secret", error: &error)
if let error = error, error.code == Int(OTSessionErrorCode.encryptionNotEnabled.rawValue) {
    // 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 OTSession.setEncryptionSecret(_:error:) 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 OTPublisherKitDelegate.publisher(_:didFailWithError:) method is called for each publisher with an error that has the code set to OTPublisherErrorCode.encryptionNotEnabled, and the OTSubscriberKitDelegate.subscriber(_:didFailWithError:) method is called for each subscriber with an error that has the code set to OTSubscriberErrorCode.encryptionNotEnabled.

Recovering from the error

The session remains valid after any of these errors. To publish or subscribe again, clear the secret by calling OTSession.setEncryptionSecret(_:error:) with nil. The same OTPublisherKit object can then be passed to OTSession.publish(_:error:) again:

var error: OTError?
session.setEncryptionSecret(nil, error: &error)
session.publish(publisher, error: &error)