Re: Issue 218: DTLS: Problem with forking

In Example 6 in what is now Section 4.9, the sample code signals IceGatherer and DtlsTransport parameters, and then constructs IceTransport and DtlsTransport objects for each answer.

The problem is that this doesn't work, because each newly constructed DtlsTransport has its own certificate and fingerprint, which doesn't match the fingerprint that was signaled in the original offer. As a result, remote peer attempts to verify the local fingerprint will fail.

The proposed solution is to add support for the Certificate Management API introduced into WebRTC 1.0, and also to add a certificate argument to the DtlsTransport constructor.

The DtlsTransport object would now look like this:

[Constructor(RTCIceTransport transport, RTCCertificate certificate)]

interface RTCDtlsTransport : RTCStatsProvider {

    readonly    attribute RTCIceTransport       transport<http://internaut.com:8080/~baboba/ortc/ortc-9-21-2015.html#widl-RTCDtlsTransport-transport>;

    readonly    attribute RTCDtlsTransportState state<http://internaut.com:8080/~baboba/ortc/ortc-9-21-2015.html#widl-RTCDtlsTransport-state>;

    RTCDtlsParameters     getLocalParameters ();

    RTCDtlsParameters?    getRemoteParameters ();

    sequence<ArrayBuffer> getRemoteCertificates ();

    void                  start<http://internaut.com:8080/~baboba/ortc/ortc-9-21-2015.html#widl-RTCDtlsTransport-start-void-RTCDtlsParameters-remoteParameters> (RTCDtlsParameters remoteParameters);

    void                  stop<http://internaut.com:8080/~baboba/ortc/ortc-9-21-2015.html#widl-RTCDtlsTransport-stop-void> ();

                attribute EventHandler?         ondtlsstatechange;

                attribute EventHandler?         onerror;

};



The Certificate Management API looks like this:

interface RTCCertificate {

    readonly    attribute Date               expires;

    readonly    attribute RTCDtlsFingerprint<http://internaut.com:8080/~baboba/ortc/ortc-9-21-2015.html#idl-def-RTCDtlsFingerprint> fingerprint;

    static Promise<RTCCertificate> generateCertificate (AlgorithmIdentifier keygenAlgorithm);

};

A few oddities should be pointed out:

a.     The certificate argument is mandatory in the DtlsTransport constructor.  Why?  If it is not mandatory, then the certificate would need to be built when the DtlsTransport is constructed or at some other time (such as when getLocalParameters() is called).  There was concern about construction times on low-end mobile devices and also the impact of making getLocalParameters() return a Promise.

b.     getRemoteCertificates() returns an sequence of ArrayBuffer not a sequence of RTCCertificate.  It is not clear if RTCCertificate was intended to be a general interface for certificates, or only used for construction.  The proposal is to extend it by adding a fingerprint attribute since this is useful for the sample code, but we haven't gone so far as to replace ArrayBuffer yet.

With those changes, the sample code in Examples 6 and 7 now appears as follows:
EXAMPLE 6
// This is an example of  how to offer ICE and DTLS parameters and
// ICE candidates and get back ICE and DTLS parameters and ICE candidates,
// and start both ICE and DTLS, when RTP and RTCP are multiplexed.
// Assume that we have a way to signal (mySignaller).
// Include some helper functions
import "helper.js";

function initiate(mySignaller) {
  // Prepare the ICE gatherer
  var gatherOptions = new RTCIceGatherOptions();
  gatherOptions.gatherPolicy = RTCIceGatherPolicy.all;
  gatherOptions.iceServers = [
    { urls: "stun:stun1.example.net" },
    { urls: "turn:turn.example.org", username: "user", credential: "myPassword" }
  ];
  var iceGatherer = new RTCIceGatherer(gatherOptions);
  iceGatherer.onlocalcandidate = function(event) {
    mySignaller.mySendLocalCandidate(event.candidate);
  };

  // Initialize the ICE and DTLS transport arrays
  var iceTransports = [];
  var dtlsTransports = [];

  // Create the DTLS certificate and parameters
  var dtlsParameters = new RTCDtlsParameters();
  var keygenAlgorithm = { name: "ECDSA", namedCurve: "P-256" };
  RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){
    var cert = certificate;
    // Obtain the fingerprint of the created certificate
    dtlsParameters.fingerprints[0] = cert.fingerprint;
  }, function(){
    trace('Certificate could not be created');
  });
  // Prepare to handle remote ICE candidates
  mySignaller.onRemoteCandidate = function(remote) {
    // Figure out which IceTranport a remote candidate relates to by matching the userNameFragment/password
    var j = 0;
    for (j = 0; j < iceTransport.length; j++) {
      var transport = iceTransports[j];
      if (transport.getRemoteParameters().userNameFragment === remote.parameters.userNameFragment)
        transport.addRemoteCandidate(remote.candidate);
      }
    }  };
  // ... create RtpSender/RtpReceiver objects as illustrated in Section 6.5 Example 9.

  mySignaller.mySendInitiate({
    "ice": iceGatherer.getLocalParameters(),
    "dtls": dtlsParameters,
    // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.5 Example 9.
  }, function(remote) {
    // Create the ICE and DTLS transports
    var iceTransport = new RTCIceTransport(iceGatherer);
    iceTransport.start(iceGatherer, remote.ice, RTCIceRole.controlling);
    iceTransports.push(iceTransport);
    // Construct a RTCDtlsTransport object with the same certificate and fingerprint as in the Offer
    // so that the remote peer can verify it.
    var dtlsTransport = new RTCDtlsTransport(iceTransport, cert);
    dtlsTransport.start(remote.dtls);
    dtlsTransports.push(dtlsTransport);

    // ... configure RtpSender/RtpReceiver objects as illustrated in Section 6.5 Example 9.
  });
}
EXAMPLE 7
// This is an example of how to answer with ICE and DTLS
// and DTLS parameters and ICE candidates and start both ICE and DTLS,
// assuming that RTP and RTCP are multiplexed.
// Include some helper functions
import "helper.js";

// Assume that remote info is signalled to us.
function accept(mySignaller, remote) {
  var gatherOptions = new RTCIceGatherOptions();
  gatherOptions.gatherPolicy = RTCIceGatherPolicy.all;
  gatherOptions.iceServers = [
    { urls: "stun:stun1.example.net" },
    { urls: "turn:turn.example.org", username: "user", credential: "myPassword" }
  ];

  var iceGatherer = new RTCIceGatherer(gatherOptions);
  iceGatherer.onlocalcandidate = function(event) {
    mySignaller.mySendLocalCandidate(event.candidate);
  };

  // Create the DTLS certificate
  var keygenAlgorithm = { name: "ECDSA", namedCurve: "P-256" };
  RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){
    var cert = certificate;
  }, function(){
    trace('Certificate could not be created');
  });

  // Create ICE and DTLS transports
  var ice = new RTCIceTransport(iceGatherer);
  var dtls = new RTCDtlsTransport(ice, cert);

  // Prepare to handle remote candidates
  mySignaller.onRemoteCandidate = function(remote) {
    ice.addRemoteCandidate(remote.candidate);
  };
  // ... create RtpSender/RtpReceiver objects as illustrated in Section 6.5 Example 9.

  mySignaller.mySendAccept({
    "ice": iceGatherer.getLocalParameters(),
    "dtls": dtls.getLocalParameters()
    // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.5 Example 9.
  });

  // Start the ICE transport with an implicit gather policy of "all"
  ice.start(iceGatherer, remote.ice, RTCIceRole.controlled);

 // Start the DTLS transport
  dtls.start(remote.dtls);

  // ... configure RtpSender/RtpReceiver objects as illustrated in Section 6.5 Example 9.
}

Received on Tuesday, 22 September 2015 16:52:34 UTC