Secure Device Associations: Best Practices

Secured Socket connections between Xbox One consoles are established by creating Secure Device Associations. An association represents a binding of two device addresses with an authorization token that allows them to communicate securely. This topic describes a recommended workflow for developers to help them understand of how to create, manage, and maintain Associations between two or more consoles.

Recommended Reading and Terminology

This document expands on the Secure Sockets documentation. The reader should have some familiarity with that subject-matter before reading this topic. This document also briefly refers to the Multiplayer Session Directory (MPSD) in the context of Secure Device Associations but does not require a working knowledge of MPSD.

Prerequisites for Creating a Secure Device Association

Before performing network communication of any kind, titles should check the console’s connectivity to Xbox Live:

    ConnectionProfile^ internetConnectionProfile =
        NetworkInformation::GetInternetConnectionProfile();
    if (internetConnectionProfile != nullptr)
    {
        return internetConnectionProfile->GetNetworkConnectivityLevel() ==
    		    NetworkConnectivityLevel::XboxLiveAccess;
    }  

It is also recommended that the title subscribe to the NetworkInformation.NetworkStatusChanged Event to receive updates when the network connection is lost or otherwise interrupted.

You should also have defined one or more Secure Device Association Templates. Templates, defined in your application’s manifest, describe the exact protocol, port, and traffic information you use to communicate with another device. To get a template, use the static function SecureDeviceAssociationTemplate::GetTemplateByName.

In order to create an association, you combine a template and a Secure Device Address. The secure address uniquely identifies the remote device you intend to communicate with, and as such you will pass one of these to the template’s SecureDeviceAssociationTemplate.CreateAssociationAsync Method or SecureDeviceAssociationTemplate.CreateAssociationForPortsAsync Method.

You can get the local console’s secure address by calling SecureDeviceAddress::GetLocal. It is possible, although unlikely, that your secure device address can change at any time. Your title should be resilient to this. See “Handling Secure Device Address Changes” for more information.

This document assumes both consoles that wish to associate will be actively participating in a multiplayer session, registered either on MPSD or otherwise. The document also assumes that you are able to create, join, and update sessions. It does not discuss implementation details for these steps. If you are using your own session directory solution, you must implement a system to communicate each member’s secure address to new peers. You can use an address GetBuffer method to obtain the raw bytes, or the GetBase64String method if you want to communicate the address over plain text (for example, REST).

Creating Secure Device Associations

A SecureDeviceAssociation^ object represents a mutual agreement to communicate between the two consoles. The first console to call CreateAssociationAsync establishes the association. Subsequent calls to CreateAssociationAsync on either console return the existing association, whether or not it was established by the local device or remotely.

The simplest way to establish an association is for one console to call CreateAssociationAsync while the other listens for an SecureDeviceAssociationTemplate.AssociationIncoming Event. It is valid to call CreateAssociationAsync on both consoles to establish communications, but this approach is vulnerable to race conditions. When one console establishes an association with a remote peer, the AssociationIncoming event is triggered on the remote peer’s console. Race conditions occur if the remote peer is also attempting to establish an association with the local console at the same time. Low-level race conditions are handled automatically and manifest as WSATIMEDOUT (10060, HRESULT 0x8007274C) errors, and you should retry if you encounter this. However, your AssociationIncoming event handler must also be robust in the case when CreateAssociationAsync is executing on another thread.

As an alternative that avoids the race condition entirely, the two consoles can implicitly agree on which one should create associations and which should listen for incoming associations.

An example scenario of two consoles—referred to below as X and Y—attempting to connect to each other securely using a Secure Device Association:

First we need to determine which of the devices is listening for associations and which is creating them. This is easy in a client-host topology:

  1. Console X joins the session. As the first member, it becomes the host and listens for incoming associations.
  2. Subsequent members are clients and create associations with the host.

For a peer-to-peer mesh we recommend the flow outlined below. If we temporarily add the hypothetical console Z to into the scenario:

  1. Console X joins the session and retrieves a list of active members.
  2. The session is empty. X begins listening for incoming associations.
  3. Console Y joins the session and retrieves a list of active members.
  4. Console Y begins listening for incoming associations.
  5. For each member already in the session before Y— only X at present—console Y uses CreateAssociationAsync to establish an association with X.
  6. Console Z joins the session and retrieves a list of active members.
  7. Console Z begins listening for incoming associations.
  8. For each member in the session before Z—now X and Y—console Z uses CreateAssociationAsync to create one association for each of X and Y.
  9. Repeat for subsequent peers.

This flow assumes that your session update process is atomic. See more detail in the Session Directory Best Practices section below.

For the remainder of this document we will discard Z and take a detailed look at the connectivity process between X, the association listener, and Y, the association instigator.

Listening for Incoming Associations

Listening for incoming associations can be accomplished two ways:

  1. X simply subscribes to the template’s AssociationIncoming event. When Y calls CreateAssociationAsync and the association is complete, this event will fire.
  2. X can listen for incoming packets and use the GetAssociationBySocketAddressBytes function with the SOCKADDR to obtain the correlating SecureDeviceAssociation^ object.

Once you have the association object, your secured communications over TCP and UDP can begin. However, we recommend that in your handshaking phase, Y communicates the session identifier so that X can ensure that Y is referring to the correct session.

Note You should always check that the incoming association is coming from an expected source. Retrieve the session document and confirm that the RemoteSecureDeviceAddress of the incoming association corresponds to one of the members of the session. This ensures that you discard unwanted traffic and is good security practice. However, it will query the session service directly and should only be performed when necessary.

X should also subscribe to the SecureDeviceAssociation.StateChanged Event. In particular, the association will enter a DestroyingRemote state if Y destroys the association for any reason. If this happens, the association is destroyed for both peers; you do not need to destroy the association if the remote has already done so.

It may be possible for X to receive packets from Y before the AssociationIncoming event has completed. It is recommended that you drop any incoming packets from sources with which you do not have a fully established association.

Once X is done with the association it can call DestroyAsync to close it. Y will be automatically notified through the above StateChanged event when this occurs, although you should still use your own networking layer to notify Y that they are about to be disconnected.

Creating an Outgoing Association

As mentioned earlier, a SecureDeviceAddress^ for X is required before Y can establish an association with it. In a traditional workflow, this address comes from the session document, so the steps to obtain it are omitted. If you are not using sessions, you must have some other way to communicate secure device addresses.

Y begins by calling the template’s CreateAssociationAsync and passes the secure address of X. If the association fails to create, Y should retry over short time intervals until a sufficiently long time has passed to indicate a connectivity error—or the user cancels. You should not retry if X actively rejects the association or your handshake.

Once the association is established, you can enter your usual networking flow. As recommended above, you should include the relevant session parameters in your handshake to ensure that Y is referring to the correct session.

As recommended for X, Y should also subscribe to the StateChanged event of the SecureDeviceAssociation. The remote may close the association at any time, and this event will notify you of the state change. If this is the case, Y should not destroy the association. If Y wishes to establish a disconnection, the same steps apply that were recommended for X.

Performance Information

Many of the networking APIs should be considered expensive and should not be called often, especially during gameplay. You should choose carefully when to interact with these components and when to cache state yourself. We recommend the following:

If you encounter problems communicating with peers, correlate your state with the networking API. See Troubleshooting, below, for detailed steps. It is a best practice to reconcile your state periodically where your title’s design allows it—for example, during game state transitions.

Interaction with Process Lifetime Management

If your title process is put into a suspended state due to PLM, you should assume that when your title resumes, your associations have been terminated. This may not really be the case. Even if the association is still active, it is safe to call CreateAssociationAsync again as it will simply return the existing association.

While it is not required that you sever your associations on title suspension, usually there is little value in keeping the association alive when your title is unable to process multiplayer packets. Most titles will want to actively disconnect from the session. We leave this to your discretion.

When you resume the title, make sure to get your local secure device address again, in case it changed while the title was suspended. Make sure to retrieve the up-to-date session document before reestablishing your associations.

Session Directory Best Practices

When you obtain SecureDeviceAddresses from your session directory, whether it is MPSD or otherwise, there are a few additional best practices to be aware of:

If you are using MPSD with the Xbox Live Services API, all of this information is available as properties of the Microsoft.Xbox.Services.Multiplayer.MultiplayerSession class and the Microsoft.Xbox.Services.Multiplayer.MultiplayerSessionMember class.

Troubleshooting

Creating an Association

If you find that you cannot create an association, check that your template does not contain more than one socket definition that share the same port number or have overlapping port ranges. As noted earlier, a WSATIMEDOUT (10060, HRESULT 0x8007274C) failure when calling CreateAssociationAsync indicates a race condition occurred and you should retry or wait for the AssociationIncoming event.

Monitor NetworkInformation::NetworkStatusChanged

There are many possible causes for sudden communication failures between consoles after an association has been established. In addition to subscribing to the NetworkInformation::NetworkStatusChanged event, make sure to check connectivity status periodically, yourself, using a heartbeat at regular intervals.

If you find that you cannot send or receive packets, if the NetworkStatusChanged event fires, or if heartbeat packets are not returned, check the following:

Check Active Associations List

SecureDeviceAssociationTemplate::Associations is a list of associations connected to the local console. It is a best practice to maintain your own list of SecureDeviceAssociations that you are communicating with. Subscribing to the StateChanged event for each association should keep you informed of most changes, but it is worth reconciling your list of associations against the SecureDeviceAssociationTemplate if you are encountering network errors. If you find a discrepancy between the template and your own list, always defer to the template.

Check the Session Document

It may be that your local view of the Multiplayer session document is out of sync. Retrieve a new copy of the document and make sure you are not connected to inactive or even missing players.

Check Your Local Secure Device Address

Very rarely, your local secure device address may change during a multiplayer session. If this happens, all associations with the local console are destroyed and must be recreated. To determine if this happened, you can compare the result of SecureDeviceAddress::GetLocal against a locally cached copy.

Summary

This topic was created to aid understanding of how to manage Secure Device Associations in your title and best-practices surrounding their use. The key principles to remember are: