Re: Issues 195 and 218

At the ORTC CG meeting, Peter Thatcher asked if we could produce sample code with and without certificates mandatory in the DtlsTransport constructor to get a sense of what the implications are.

A version of Examples 6 and 7 with the certificate management API and certificates mandatory in the RTCDtlsTransport constructor are given below:

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 transport array
  var iceTransports = [];
  var dtlsTransports = []; 
  for (var i=0; i < 10; i++){
    iceTransports.push(new RTCIceTransport(iceGatherer)); 
  } 
  // Create the certificate array
  var certs = [];
  var dtlsParameters = new RTCDtlsParameters(); 
  var keygenAlgorithm = .... ; 
  RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){ 
    certs.push(certificate);
    // Obtain the fingerprint of the created certificate
    dtlsParameters.fingerprints[0] = certificate.fingerprint; 
  }, function(){
    trace('Certificate could not be created'); 
  }); 
  // Prepare to handle remote ICE candidates
  mySignaller.onRemoteCandidate = function(remote){
    // Retrieve the IceTransport corresponding to the remote usernameFragment
    var transport = iceGatherer.getOrCreateTransport(remote.parameters.usernameFragment);
    // Add the candidate to that IceTransport
    transport.addRemoteCandidate(remote.candidate); 
  }; 
  // ... create RtpSender/RtpReceiver objects as illustrated in Section 6.4 Example 7. 

  mySignaller.mySendInitiate({
    "ice": iceGatherer.getLocalParameters(), 
    "dtls": dtlsParameters, 
    // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.4 Example 7. 
  }, function(remote) {
    // Start the ICE and DTLS transports
    var iceTransport = iceGatherer.getOrCreateTransport(remote.parameters.usernameFragment); 
    var i = dtlsTransports.push(new RTCDtlsTransport(iceTransport, certs)) - 1; 
    iceTransport.start(iceGatherer, remote.ice);
    dtlsTransports[i].start(remote.dtls);
    // ... configure RtpSender/RtpReceiver objects as illustrated in Section 6.4 Example 7.
  });
}
    
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 certs array
  var certs = [];  
  var keygenAlgorithm = ... ;
  RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){
    certs.push(certificate); 
    // Create ICE and DTLS transports
    var ice = new RTCIceTransport(iceGatherer);
    var dtls = new RTCDtlsTransport(ice, certs);
    // Prepare to handle remote candidates
  }, function(){
    trace('Certificate could not be created'); 
  }); 
  mySignaller.onRemoteCandidate = function(remote) {
    ice.addRemoteCandidate(remote.candidate);
  }; 
  // ... create RtpSender/RtpReceiver objects as illustrated in Section 6.4 Example 7.

  mySignaller.mySendAccept({
    "ice": iceGatherer.getLocalParameters(),
    "dtls": dtls.getLocalParameters()
    // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.4 Example 7.
  });
  // 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.4 Example 7.
}

An equivalent example with certain optional in the constructor, getLocalParameters as a promise and RTP/RTCP DtlsTransports with the same certificates/fingerprints by default is given below: 

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, and parallel forking is supported. 
// 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 transport array
  var iceTransports = [];
  var dtlsTransports = []; 
  for (var i=0; i < 10; i++){
    iceTransports.push(new RTCIceTransport(iceGatherer)); 
  } 
  // Create the certificate array
  var certs = [];
  var dtlsParameters = new RTCDtlsParameters(); 
  var keygenAlgorithm = .... ; 
  RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){ 
    certs.push(certificate);
    // Obtain the fingerprint of the created certificate(s)
    dtlsParameters.fingerprints[0] = certificate.fingerprint; 
  }, function(){
    trace('Certificate could not be created'); 
  }); 
  // Prepare to handle remote ICE candidates
  mySignaller.onRemoteCandidate = function(remote){
    // Retrieve the IceTransport corresponding to the remote usernameFragment
    var transport = iceGatherer.getOrCreateTransport(remote.parameters.usernameFragment);
    // Add the candidate to that IceTransport
    transport.addRemoteCandidate(remote.candidate); 
  }; 
  // ... create RtpSender/RtpReceiver objects as illustrated in Section 6.4 Example 7. 

  mySignaller.mySendInitiate({
    "ice": iceGatherer.getLocalParameters(), 
    "dtls": dtlsParameters, 
    // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.4 Example 7. 
  }, function(remote) {
    // Start the ICE and DTLS transports
    var iceTransport = iceGatherer.getOrCreateTransport(remote.parameters.usernameFragment); 
    var i = dtlsTransports.push(new RTCDtlsTransport(iceTransport, certs)) - 1; 
    iceTransport.start(iceGatherer, remote.ice);
    dtlsTransports[i].start(remote.dtls);
    // ... configure RtpSender/RtpReceiver objects as illustrated in Section 6.4 Example 7.
  });
}
    
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 ICE and DTLS transports
  var ice = new RTCIceTransport(iceGatherer);
  var dtls = new RTCDtlsTransport(ice); 
  // Prepare to handle remote candidates
  mySignaller.onRemoteCandidate = function(remote) {
    ice.addRemoteCandidate(remote.candidate);
  }; 
  // ... create RtpSender/RtpReceiver objects as illustrated in Section 6.4 Example 7.
  // obtain DTLS Parameters
  var dtlsParameters; 
  dtls.getLocalParameters.then(function(parameters){
    dtlsParameters = parameters; 
  }, function(){
    trace('DTLS Local Parameters could not be obtained'); 
  }); 
  mySignaller.mySendAccept({
    "ice": iceGatherer.getLocalParameters(),
    "dtls": dtlsParameters,
    // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.4 Example 7.
  });
  // 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.4 Example 7.
}


One thing to notice is that Example 6 is virtually unchanged in both cases - only Example 7 really changes. This is because absent a cloneTransport() method, it is necessary to pass certificates to the DtlsTransport constructor to support DTLS forking. Example 7 can avoid use of the certificate management API since the Responder only needs to construct a single DtlsTransport. 

In reworking not other examples I noticed the same thing. In Examples without DTLS forking, the certificate management API is not required, but if you have forking it is needed.

Received on Sunday, 12 July 2015 23:02:15 UTC