DTLS-SRTP: Wait for setup finish and handle setup=passive
This commit is contained in:
parent
8d1c6c29be
commit
fbc10c2023
|
@ -6,8 +6,8 @@ public class Crypto.Srtp {
|
|||
public const string F8_128_HMAC_SHA1_80 = "F8_128_HMAC_SHA1_80";
|
||||
|
||||
public class Session {
|
||||
public bool has_encrypt { get; private set; }
|
||||
public bool has_decrypt { get; private set; }
|
||||
public bool has_encrypt { get; private set; default = false; }
|
||||
public bool has_decrypt { get; private set; default = false; }
|
||||
|
||||
private Context encrypt_context;
|
||||
private Context decrypt_context;
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
using GnuTLS;
|
||||
|
||||
public class DtlsSrtp {
|
||||
namespace Dino.Plugins.Ice.DtlsSrtp {
|
||||
|
||||
public static Handler setup() throws GLib.Error {
|
||||
var obj = new Handler();
|
||||
obj.generate_credentials();
|
||||
return obj;
|
||||
}
|
||||
|
||||
public class Handler {
|
||||
|
||||
public signal void send_data(uint8[] data);
|
||||
|
||||
public bool ready { get {
|
||||
return srtp_session.has_encrypt && srtp_session.has_decrypt;
|
||||
}}
|
||||
|
||||
public Mode mode { get; set; default = Mode.CLIENT; }
|
||||
public uint8[] own_fingerprint { get; private set; }
|
||||
public uint8[] peer_fingerprint { get; set; }
|
||||
public string peer_fp_algo { get; set; }
|
||||
|
||||
private X509.Certificate[] own_cert;
|
||||
private X509.PrivateKey private_key;
|
||||
private Cond buffer_cond = new Cond();
|
||||
|
@ -11,27 +28,12 @@ public class DtlsSrtp {
|
|||
private Gee.LinkedList<Bytes> buffer_queue = new Gee.LinkedList<Bytes>();
|
||||
private uint pull_timeout = uint.MAX;
|
||||
|
||||
private DigestAlgorithm? peer_fp_algo = null;
|
||||
private uint8[] peer_fingerprint = null;
|
||||
private uint8[] own_fingerprint;
|
||||
private bool running = false;
|
||||
private bool stop = false;
|
||||
private bool restart = false;
|
||||
|
||||
private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session();
|
||||
|
||||
public static DtlsSrtp setup() throws GLib.Error {
|
||||
var obj = new DtlsSrtp();
|
||||
obj.generate_credentials();
|
||||
return obj;
|
||||
}
|
||||
|
||||
internal uint8[] get_own_fingerprint(DigestAlgorithm digest_algo) {
|
||||
return own_fingerprint;
|
||||
}
|
||||
|
||||
public void set_peer_fingerprint(uint8[] fingerprint, DigestAlgorithm digest_algo) {
|
||||
this.peer_fingerprint = fingerprint;
|
||||
this.peer_fp_algo = digest_algo;
|
||||
}
|
||||
|
||||
public uint8[] process_incoming_data(uint component_id, uint8[] data) {
|
||||
if (srtp_session.has_decrypt) {
|
||||
try {
|
||||
|
@ -77,7 +79,7 @@ public class DtlsSrtp {
|
|||
buffer_mutex.unlock();
|
||||
}
|
||||
|
||||
private void generate_credentials() throws GLib.Error {
|
||||
internal void generate_credentials() throws GLib.Error {
|
||||
int err = 0;
|
||||
|
||||
private_key = X509.PrivateKey.create();
|
||||
|
@ -102,8 +104,29 @@ public class DtlsSrtp {
|
|||
own_cert = new X509.Certificate[] { (owned)cert };
|
||||
}
|
||||
|
||||
public async Xmpp.Xep.Jingle.ContentEncryption setup_dtls_connection(bool server) {
|
||||
InitFlags server_or_client = server ? InitFlags.SERVER : InitFlags.CLIENT;
|
||||
public void stop_dtls_connection() {
|
||||
buffer_mutex.lock();
|
||||
stop = true;
|
||||
buffer_cond.signal();
|
||||
buffer_mutex.unlock();
|
||||
}
|
||||
|
||||
public async Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection() {
|
||||
buffer_mutex.lock();
|
||||
if (stop) {
|
||||
restart = true;
|
||||
buffer_mutex.unlock();
|
||||
return null;
|
||||
}
|
||||
if (running || ready) {
|
||||
buffer_mutex.unlock();
|
||||
return null;
|
||||
}
|
||||
running = true;
|
||||
restart = false;
|
||||
buffer_mutex.unlock();
|
||||
|
||||
InitFlags server_or_client = mode == Mode.SERVER ? InitFlags.SERVER : InitFlags.CLIENT;
|
||||
debug("Setting up DTLS connection. We're %s", server_or_client.to_string());
|
||||
|
||||
CertificateCredentials cert_cred = CertificateCredentials.create();
|
||||
|
@ -131,7 +154,7 @@ public class DtlsSrtp {
|
|||
DateTime current_time = new DateTime.now_utc();
|
||||
if (maximum_time.compare(current_time) < 0) {
|
||||
warning("DTLS handshake timeouted");
|
||||
return -1;
|
||||
return ErrorCode.APPLICATION_ERROR_MIN + 1;
|
||||
}
|
||||
} while (err < 0 && !((ErrorCode)err).is_fatal());
|
||||
Idle.add(setup_dtls_connection.callback);
|
||||
|
@ -139,6 +162,17 @@ public class DtlsSrtp {
|
|||
});
|
||||
yield;
|
||||
err = thread.join();
|
||||
buffer_mutex.lock();
|
||||
if (stop) {
|
||||
stop = false;
|
||||
running = false;
|
||||
bool restart = restart;
|
||||
buffer_mutex.unlock();
|
||||
if (restart) return yield setup_dtls_connection();
|
||||
return null;
|
||||
}
|
||||
buffer_mutex.unlock();
|
||||
throw_if_error(err);
|
||||
|
||||
uint8[] km = new uint8[150];
|
||||
Datum? client_key, client_salt, server_key, server_salt;
|
||||
|
@ -147,7 +181,8 @@ public class DtlsSrtp {
|
|||
warning("SRTP client/server key/salt null");
|
||||
}
|
||||
|
||||
if (server) {
|
||||
debug("Finished DTLS connection. We're %s", server_or_client.to_string());
|
||||
if (mode == Mode.SERVER) {
|
||||
srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract());
|
||||
srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract());
|
||||
} else {
|
||||
|
@ -158,24 +193,28 @@ public class DtlsSrtp {
|
|||
}
|
||||
|
||||
private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) {
|
||||
DtlsSrtp self = transport_ptr as DtlsSrtp;
|
||||
Handler self = transport_ptr as Handler;
|
||||
|
||||
self.buffer_mutex.lock();
|
||||
while (self.buffer_queue.size == 0) {
|
||||
self.buffer_cond.wait(self.buffer_mutex);
|
||||
if (self.stop) {
|
||||
self.buffer_mutex.unlock();
|
||||
return -1;
|
||||
}
|
||||
owned Bytes data = self.buffer_queue.remove_at(0);
|
||||
}
|
||||
Bytes data = self.buffer_queue.remove_at(0);
|
||||
self.buffer_mutex.unlock();
|
||||
|
||||
uint8[] data_uint8 = Bytes.unref_to_data(data);
|
||||
uint8[] data_uint8 = Bytes.unref_to_data((owned) data);
|
||||
Memory.copy(buffer, data_uint8, data_uint8.length);
|
||||
|
||||
// The callback should return 0 on connection termination, a positive number indicating the number of bytes received, and -1 on error.
|
||||
return (ssize_t)data.length;
|
||||
return (ssize_t)data_uint8.length;
|
||||
}
|
||||
|
||||
private static int pull_timeout_function(void* transport_ptr, uint ms) {
|
||||
DtlsSrtp self = transport_ptr as DtlsSrtp;
|
||||
Handler self = transport_ptr as Handler;
|
||||
|
||||
DateTime current_time = new DateTime.now_utc();
|
||||
current_time.add_seconds(ms/1000);
|
||||
|
@ -184,6 +223,10 @@ public class DtlsSrtp {
|
|||
self.buffer_mutex.lock();
|
||||
while (self.buffer_queue.size == 0) {
|
||||
self.buffer_cond.wait_until(self.buffer_mutex, end_time);
|
||||
if (self.stop) {
|
||||
self.buffer_mutex.unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
DateTime new_current_time = new DateTime.now_utc();
|
||||
if (new_current_time.compare(current_time) > 0) {
|
||||
|
@ -197,7 +240,7 @@ public class DtlsSrtp {
|
|||
}
|
||||
|
||||
private static ssize_t push_function(void* transport_ptr, uint8[] buffer) {
|
||||
DtlsSrtp self = transport_ptr as DtlsSrtp;
|
||||
Handler self = transport_ptr as Handler;
|
||||
self.send_data(buffer);
|
||||
|
||||
// The callback should return a positive number indicating the bytes sent, and -1 on error.
|
||||
|
@ -205,7 +248,7 @@ public class DtlsSrtp {
|
|||
}
|
||||
|
||||
private static int verify_function(Session session) {
|
||||
DtlsSrtp self = session.get_transport_pointer() as DtlsSrtp;
|
||||
Handler self = session.get_transport_pointer() as Handler;
|
||||
try {
|
||||
bool valid = self.verify_peer_cert(session);
|
||||
if (!valid) {
|
||||
|
@ -232,7 +275,17 @@ public class DtlsSrtp {
|
|||
X509.Certificate peer_cert = X509.Certificate.create();
|
||||
peer_cert.import(ref cert_datums[0], CertificateFormat.DER);
|
||||
|
||||
uint8[] real_peer_fp = get_fingerprint(peer_cert, peer_fp_algo);
|
||||
DigestAlgorithm algo;
|
||||
switch (peer_fp_algo) {
|
||||
case "sha-256":
|
||||
algo = DigestAlgorithm.SHA256;
|
||||
break;
|
||||
default:
|
||||
warning("Unkown peer fingerprint algorithm: %s", peer_fp_algo);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8[] real_peer_fp = get_fingerprint(peer_cert, algo);
|
||||
|
||||
if (real_peer_fp.length != this.peer_fingerprint.length) {
|
||||
warning("Fingerprint lengths not equal %i vs %i", real_peer_fp.length, peer_fingerprint.length);
|
||||
|
@ -248,8 +301,9 @@ public class DtlsSrtp {
|
|||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) {
|
||||
private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) {
|
||||
uint8[] buf = new uint8[512];
|
||||
size_t buf_out_size = 512;
|
||||
certificate.get_fingerprint(digest_algo, buf, ref buf_out_size);
|
||||
|
@ -259,9 +313,9 @@ public class DtlsSrtp {
|
|||
ret[i] = buf[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private string format_fingerprint(uint8[] fingerprint) {
|
||||
private string format_fingerprint(uint8[] fingerprint) {
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < fingerprint.length; i++) {
|
||||
sb.append("%02x".printf(fingerprint[i]));
|
||||
|
@ -270,5 +324,11 @@ public class DtlsSrtp {
|
|||
}
|
||||
}
|
||||
return sb.str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum Mode {
|
||||
CLIENT, SERVER
|
||||
}
|
||||
|
||||
}
|
|
@ -9,11 +9,11 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
private bool we_want_connection;
|
||||
private bool remote_credentials_set;
|
||||
private Map<uint8, DatagramConnection> connections = new HashMap<uint8, DatagramConnection>();
|
||||
private DtlsSrtp? dtls_srtp;
|
||||
private DtlsSrtp.Handler? dtls_srtp_handler;
|
||||
|
||||
private class DatagramConnection : Jingle.DatagramConnection {
|
||||
private Nice.Agent agent;
|
||||
private DtlsSrtp? dtls_srtp;
|
||||
private DtlsSrtp.Handler? dtls_srtp_handler;
|
||||
private uint stream_id;
|
||||
private string? error;
|
||||
private ulong sent;
|
||||
|
@ -22,9 +22,9 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
private ulong recv_reported;
|
||||
private ulong datagram_received_id;
|
||||
|
||||
public DatagramConnection(Nice.Agent agent, DtlsSrtp? dtls_srtp, uint stream_id, uint8 component_id) {
|
||||
public DatagramConnection(Nice.Agent agent, DtlsSrtp.Handler? dtls_srtp_handler, uint stream_id, uint8 component_id) {
|
||||
this.agent = agent;
|
||||
this.dtls_srtp = dtls_srtp;
|
||||
this.dtls_srtp_handler = dtls_srtp_handler;
|
||||
this.stream_id = stream_id;
|
||||
this.component_id = component_id;
|
||||
this.datagram_received_id = this.datagram_received.connect((datagram) => {
|
||||
|
@ -45,8 +45,8 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
public override void send_datagram(Bytes datagram) {
|
||||
if (this.agent != null && is_component_ready(agent, stream_id, component_id)) {
|
||||
uint8[] encrypted_data = null;
|
||||
if (dtls_srtp != null) {
|
||||
encrypted_data = dtls_srtp.process_outgoing_data(component_id, datagram.get_data());
|
||||
if (dtls_srtp_handler != null) {
|
||||
encrypted_data = dtls_srtp_handler.process_outgoing_data(component_id, datagram.get_data());
|
||||
if (encrypted_data == null) return;
|
||||
}
|
||||
agent.send(stream_id, component_id, encrypted_data ?? datagram.get_data());
|
||||
|
@ -65,13 +65,18 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
this.agent = agent;
|
||||
|
||||
if (this.peer_fingerprint != null || !incoming) {
|
||||
dtls_srtp = setup_dtls(this);
|
||||
this.own_fingerprint = dtls_srtp.get_own_fingerprint(GnuTLS.DigestAlgorithm.SHA256);
|
||||
dtls_srtp_handler = setup_dtls(this);
|
||||
own_fingerprint = dtls_srtp_handler.own_fingerprint;
|
||||
if (incoming) {
|
||||
dtls_srtp.set_peer_fingerprint(this.peer_fingerprint, this.peer_fp_algo == "sha-256" ? GnuTLS.DigestAlgorithm.SHA256 : GnuTLS.DigestAlgorithm.NULL);
|
||||
own_setup = "active";
|
||||
dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT;
|
||||
dtls_srtp_handler.peer_fingerprint = peer_fingerprint;
|
||||
dtls_srtp_handler.peer_fp_algo = peer_fp_algo;
|
||||
} else {
|
||||
dtls_srtp.setup_dtls_connection.begin(true, (_, res) => {
|
||||
this.content.encryption = dtls_srtp.setup_dtls_connection.end(res);
|
||||
own_setup = "actpass";
|
||||
dtls_srtp_handler.mode = DtlsSrtp.Mode.SERVER;
|
||||
dtls_srtp_handler.setup_dtls_connection.begin((_, res) => {
|
||||
this.content.encryption = dtls_srtp_handler.setup_dtls_connection.end(res) ?? this.content.encryption;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -104,9 +109,9 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
agent.gather_candidates(stream_id);
|
||||
}
|
||||
|
||||
private static DtlsSrtp setup_dtls(TransportParameters tp) {
|
||||
private static DtlsSrtp.Handler setup_dtls(TransportParameters tp) {
|
||||
var weak_self = new WeakRef(tp);
|
||||
DtlsSrtp dtls_srtp = DtlsSrtp.setup();
|
||||
DtlsSrtp.Handler dtls_srtp = DtlsSrtp.setup();
|
||||
dtls_srtp.send_data.connect((data) => {
|
||||
TransportParameters self = (TransportParameters) weak_self.get();
|
||||
if (self != null) self.agent.send(self.stream_id, 1, data);
|
||||
|
@ -144,10 +149,15 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
debug("on_transport_accept from %s", peer_full_jid.to_string());
|
||||
base.handle_transport_accept(transport);
|
||||
|
||||
if (dtls_srtp != null && peer_fingerprint != null) {
|
||||
dtls_srtp.set_peer_fingerprint(this.peer_fingerprint, this.peer_fp_algo == "sha-256" ? GnuTLS.DigestAlgorithm.SHA256 : GnuTLS.DigestAlgorithm.NULL);
|
||||
if (dtls_srtp_handler != null && peer_fingerprint != null) {
|
||||
dtls_srtp_handler.peer_fingerprint = peer_fingerprint;
|
||||
dtls_srtp_handler.peer_fp_algo = peer_fp_algo;
|
||||
if (peer_setup == "passive") {
|
||||
dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT;
|
||||
dtls_srtp_handler.stop_dtls_connection();
|
||||
}
|
||||
} else {
|
||||
dtls_srtp = null;
|
||||
dtls_srtp_handler = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,18 +210,10 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
int new_candidates = agent.set_remote_candidates(stream_id, i, candidates);
|
||||
debug("Initiated component %u with %i remote candidates", i, new_candidates);
|
||||
|
||||
connections[i] = new DatagramConnection(agent, dtls_srtp, stream_id, i);
|
||||
connections[i] = new DatagramConnection(agent, dtls_srtp_handler, stream_id, i);
|
||||
content.set_transport_connection(connections[i], i);
|
||||
}
|
||||
|
||||
if (incoming && dtls_srtp != null) {
|
||||
Jingle.DatagramConnection rtp_datagram = (Jingle.DatagramConnection) content.get_transport_connection(1);
|
||||
rtp_datagram.notify["ready"].connect(() => {
|
||||
dtls_srtp.setup_dtls_connection.begin(false, (_, res) => {
|
||||
this.content.encryption = dtls_srtp.setup_dtls_connection.end(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
base.create_transport_connection(stream, content);
|
||||
}
|
||||
|
||||
|
@ -219,11 +221,16 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
if (stream_id != this.stream_id) return;
|
||||
debug("stream %u component %u state changed to %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string());
|
||||
may_consider_ready(stream_id, component_id);
|
||||
if (incoming && dtls_srtp_handler != null && !dtls_srtp_handler.ready && is_component_ready(agent, stream_id, component_id) && dtls_srtp_handler.mode == DtlsSrtp.Mode.CLIENT) {
|
||||
dtls_srtp_handler.setup_dtls_connection.begin((_, res) => {
|
||||
this.content.encryption = dtls_srtp_handler.setup_dtls_connection.end(res) ?? this.content.encryption;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void may_consider_ready(uint stream_id, uint component_id) {
|
||||
if (stream_id != this.stream_id) return;
|
||||
if (connections.has_key((uint8) component_id) && is_component_ready(agent, stream_id, component_id) && connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready) {
|
||||
if (connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready && is_component_ready(agent, stream_id, component_id) && (dtls_srtp_handler == null || dtls_srtp_handler.ready)) {
|
||||
connections[(uint8)component_id].ready = true;
|
||||
}
|
||||
}
|
||||
|
@ -241,8 +248,8 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport
|
|||
private void on_recv(Nice.Agent agent, uint stream_id, uint component_id, uint8[] data) {
|
||||
if (stream_id != this.stream_id) return;
|
||||
uint8[] decrypt_data = null;
|
||||
if (dtls_srtp != null) {
|
||||
decrypt_data = dtls_srtp.process_incoming_data(component_id, data);
|
||||
if (dtls_srtp_handler != null) {
|
||||
decrypt_data = dtls_srtp_handler.process_incoming_data(component_id, data);
|
||||
if (decrypt_data == null) return;
|
||||
}
|
||||
may_consider_ready(stream_id, component_id);
|
||||
|
|
|
@ -94,6 +94,9 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object {
|
|||
ulong rtp_ready_handler_id = 0;
|
||||
rtp_ready_handler_id = rtp_datagram.notify["ready"].connect(() => {
|
||||
this.stream.on_rtp_ready();
|
||||
if (rtcp_mux) {
|
||||
this.stream.on_rtcp_ready();
|
||||
}
|
||||
connection_ready();
|
||||
|
||||
rtp_datagram.disconnect(rtp_ready_handler_id);
|
||||
|
|
|
@ -14,8 +14,10 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
|
|||
public Gee.List<Candidate> remote_candidates = new ArrayList<Candidate>(Candidate.equals_func);
|
||||
|
||||
public uint8[]? own_fingerprint = null;
|
||||
public string? own_setup = null;
|
||||
public uint8[]? peer_fingerprint = null;
|
||||
public string? peer_fp_algo = null;
|
||||
public string? peer_setup = null;
|
||||
|
||||
public Jid local_full_jid { get; private set; }
|
||||
public Jid peer_full_jid { get; private set; }
|
||||
|
@ -41,8 +43,9 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
|
|||
|
||||
StanzaNode? fingerprint_node = node.get_subnode("fingerprint", DTLS_NS_URI);
|
||||
if (fingerprint_node != null) {
|
||||
peer_fingerprint = fingerprint_to_bytes(fingerprint_node.get_deep_string_content());
|
||||
peer_fingerprint = fingerprint_to_bytes(fingerprint_node.get_string_content());
|
||||
peer_fp_algo = fingerprint_node.get_attribute("hash");
|
||||
peer_setup = fingerprint_node.get_attribute("setup");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,11 +76,7 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
|
|||
.add_self_xmlns()
|
||||
.put_attribute("hash", "sha-256")
|
||||
.put_node(new StanzaNode.text(format_fingerprint(own_fingerprint)));
|
||||
if (incoming) {
|
||||
fingerprint_node.put_attribute("setup", "active");
|
||||
} else {
|
||||
fingerprint_node.put_attribute("setup", "actpass");
|
||||
}
|
||||
fingerprint_node.put_attribute("setup", own_setup);
|
||||
node.put_node(fingerprint_node);
|
||||
}
|
||||
|
||||
|
@ -101,6 +100,7 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
|
|||
if (fingerprint_node != null) {
|
||||
peer_fingerprint = fingerprint_to_bytes(fingerprint_node.get_deep_string_content());
|
||||
peer_fp_algo = fingerprint_node.get_attribute("hash");
|
||||
peer_setup = fingerprint_node.get_attribute("setup");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,8 +142,6 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private string format_fingerprint(uint8[] fingerprint) {
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < fingerprint.length; i++) {
|
||||
|
|
Loading…
Reference in a new issue