1/*
2 *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/p2p/base/transportdescriptionfactory.h"
12
13#include "webrtc/p2p/base/transportdescription.h"
14#include "webrtc/base/helpers.h"
15#include "webrtc/base/logging.h"
16#include "webrtc/base/messagedigest.h"
17#include "webrtc/base/sslfingerprint.h"
18
19namespace cricket {
20
21TransportDescriptionFactory::TransportDescriptionFactory()
22    : secure_(SEC_DISABLED) {
23}
24
25TransportDescription* TransportDescriptionFactory::CreateOffer(
26    const TransportOptions& options,
27    const TransportDescription* current_description) const {
28  rtc::scoped_ptr<TransportDescription> desc(new TransportDescription());
29
30  // Generate the ICE credentials if we don't already have them.
31  if (!current_description || options.ice_restart) {
32    desc->ice_ufrag = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
33    desc->ice_pwd = rtc::CreateRandomString(ICE_PWD_LENGTH);
34  } else {
35    desc->ice_ufrag = current_description->ice_ufrag;
36    desc->ice_pwd = current_description->ice_pwd;
37  }
38
39  // If we are trying to establish a secure transport, add a fingerprint.
40  if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
41    // Fail if we can't create the fingerprint.
42    // If we are the initiator set role to "actpass".
43    if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) {
44      return NULL;
45    }
46  }
47
48  return desc.release();
49}
50
51TransportDescription* TransportDescriptionFactory::CreateAnswer(
52    const TransportDescription* offer,
53    const TransportOptions& options,
54    const TransportDescription* current_description) const {
55  // TODO(juberti): Figure out why we get NULL offers, and fix this upstream.
56  if (!offer) {
57    LOG(LS_WARNING) << "Failed to create TransportDescription answer " <<
58        "because offer is NULL";
59    return NULL;
60  }
61
62  rtc::scoped_ptr<TransportDescription> desc(new TransportDescription());
63  // Generate the ICE credentials if we don't already have them or ice is
64  // being restarted.
65  if (!current_description || options.ice_restart) {
66    desc->ice_ufrag = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
67    desc->ice_pwd = rtc::CreateRandomString(ICE_PWD_LENGTH);
68  } else {
69    desc->ice_ufrag = current_description->ice_ufrag;
70    desc->ice_pwd = current_description->ice_pwd;
71  }
72
73  // Negotiate security params.
74  if (offer && offer->identity_fingerprint.get()) {
75    // The offer supports DTLS, so answer with DTLS, as long as we support it.
76    if (secure_ == SEC_ENABLED || secure_ == SEC_REQUIRED) {
77      // Fail if we can't create the fingerprint.
78      // Setting DTLS role to active.
79      ConnectionRole role = (options.prefer_passive_role) ?
80          CONNECTIONROLE_PASSIVE : CONNECTIONROLE_ACTIVE;
81
82      if (!SetSecurityInfo(desc.get(), role)) {
83        return NULL;
84      }
85    }
86  } else if (secure_ == SEC_REQUIRED) {
87    // We require DTLS, but the other side didn't offer it. Fail.
88    LOG(LS_WARNING) << "Failed to create TransportDescription answer "
89                       "because of incompatible security settings";
90    return NULL;
91  }
92
93  return desc.release();
94}
95
96bool TransportDescriptionFactory::SetSecurityInfo(
97    TransportDescription* desc, ConnectionRole role) const {
98  if (!certificate_) {
99    LOG(LS_ERROR) << "Cannot create identity digest with no certificate";
100    return false;
101  }
102
103  // This digest algorithm is used to produce the a=fingerprint lines in SDP.
104  // RFC 4572 Section 5 requires that those lines use the same hash function as
105  // the certificate's signature.
106  std::string digest_alg;
107  if (!certificate_->ssl_certificate().GetSignatureDigestAlgorithm(
108          &digest_alg)) {
109    LOG(LS_ERROR) << "Failed to retrieve the certificate's digest algorithm";
110    return false;
111  }
112
113  desc->identity_fingerprint.reset(
114      rtc::SSLFingerprint::Create(digest_alg, certificate_->identity()));
115  if (!desc->identity_fingerprint.get()) {
116    LOG(LS_ERROR) << "Failed to create identity fingerprint, alg="
117                  << digest_alg;
118    return false;
119  }
120
121  // Assign security role.
122  desc->connection_role = role;
123  return true;
124}
125
126}  // namespace cricket
127