mirror of https://github.com/sipwise/rtpengine.git
Conflicts: daemon/graphite.c daemon/graphite.hpull/101/head
commit
9f421a9d8f
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,208 @@
|
||||
#ifndef __ICE_H__
|
||||
#define __ICE_H__
|
||||
|
||||
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <glib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include "str.h"
|
||||
#include "obj.h"
|
||||
#include "aux.h"
|
||||
#include "call.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#define MAX_COMPONENTS 2
|
||||
#define TIMER_RUN_INTERVAL 20 /* ms */
|
||||
#define STUN_RETRANSMIT_INTERVAL 100 /* ms, with exponential backoff */
|
||||
#define STUN_MAX_RETRANSMITS 7
|
||||
#define MAX_ICE_CANDIDATES 100
|
||||
|
||||
|
||||
|
||||
#define ICE_AGENT_COMPLETED 0x0002
|
||||
#define ICE_AGENT_CONTROLLING 0x0004
|
||||
#define ICE_AGENT_NOMINATING 0x0008
|
||||
|
||||
#define ICE_PAIR_FROZEN 0x0001
|
||||
#define ICE_PAIR_IN_PROGRESS 0x0002
|
||||
#define ICE_PAIR_FAILED 0x0004
|
||||
#define ICE_PAIR_SUCCEEDED 0x0008
|
||||
#define ICE_PAIR_NOMINATED 0x0010
|
||||
#define ICE_PAIR_LEARNED 0x0020
|
||||
#define ICE_PAIR_VALID 0x0040
|
||||
#define ICE_PAIR_TO_USE 0x0080
|
||||
#define ICE_PAIR_TRIGGERED 0x0100
|
||||
|
||||
#define PAIR_ISSET(p, f) bf_isset(&(p)->pair_flags, ICE_PAIR_ ## f)
|
||||
#define PAIR_SET(p, f) bf_set(&(p)->pair_flags, ICE_PAIR_ ## f)
|
||||
#define PAIR_SET2(p, f, g) bf_set(&(p)->pair_flags, ICE_PAIR_ ## f | ICE_PAIR_ ## g)
|
||||
#define PAIR_CLEAR(p, f) bf_clear(&(p)->pair_flags, ICE_PAIR_ ## f)
|
||||
#define PAIR_CLEAR2(p, f, g) bf_clear(&(p)->pair_flags, ICE_PAIR_ ## f | ICE_PAIR_ ## g)
|
||||
|
||||
#define AGENT_ISSET(p, f) bf_isset(&(p)->agent_flags, ICE_AGENT_ ## f)
|
||||
#define AGENT_ISSET2(p, f, g) bf_isset(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g)
|
||||
#define AGENT_SET(p, f) bf_set(&(p)->agent_flags, ICE_AGENT_ ## f)
|
||||
#define AGENT_SET2(p, f, g) bf_set(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g)
|
||||
#define AGENT_CLEAR(p, f) bf_clear(&(p)->agent_flags, ICE_AGENT_ ## f)
|
||||
#define AGENT_CLEAR2(p, f, g) bf_clear(&(p)->agent_flags, ICE_AGENT_ ## f | ICE_AGENT_ ## g)
|
||||
|
||||
|
||||
|
||||
struct local_interface;
|
||||
struct interface_address;
|
||||
struct packet_stream;
|
||||
struct call_media;
|
||||
struct call;
|
||||
struct stream_params;
|
||||
struct stun_attrs;
|
||||
|
||||
|
||||
|
||||
|
||||
enum ice_candidate_type {
|
||||
ICT_UNKNOWN = 0,
|
||||
ICT_HOST,
|
||||
ICT_SRFLX,
|
||||
ICT_PRFLX,
|
||||
ICT_RELAY,
|
||||
__ICT_LAST,
|
||||
};
|
||||
|
||||
enum ice_transport {
|
||||
ITP_UNKNOWN = 0,
|
||||
ITP_UDP,
|
||||
// ITP_TCP,
|
||||
};
|
||||
|
||||
struct ice_candidate {
|
||||
str foundation;
|
||||
unsigned long component_id;
|
||||
enum ice_transport transport;
|
||||
unsigned long priority;
|
||||
struct endpoint endpoint;
|
||||
enum ice_candidate_type type;
|
||||
struct in6_addr related_address;
|
||||
unsigned int related_port;
|
||||
};
|
||||
|
||||
struct ice_candidate_pair {
|
||||
struct ice_candidate *remote_candidate;
|
||||
struct interface_address *local_address;
|
||||
struct packet_stream *packet_stream;
|
||||
volatile unsigned int pair_flags;
|
||||
u_int32_t stun_transaction[3]; /* belongs to transaction_hash, thus agent->lock */
|
||||
unsigned int retransmit_ms;
|
||||
struct timeval retransmit;
|
||||
unsigned int retransmits;
|
||||
struct ice_agent *agent;
|
||||
u_int64_t pair_priority;
|
||||
int was_controlling:1,
|
||||
was_nominated:1;
|
||||
};
|
||||
|
||||
/* these are protected by the call's master_lock */
|
||||
struct ice_agent {
|
||||
struct obj obj;
|
||||
struct call *call; /* main reference */
|
||||
struct call_media *media;
|
||||
struct local_interface *local_interface;
|
||||
int desired_family;
|
||||
|
||||
mutex_t lock; /* for elements below. and call must be locked in R */
|
||||
/* lock order: in_lock first, then agent->lock */
|
||||
GQueue remote_candidates;
|
||||
GQueue candidate_pairs; /* for storage */
|
||||
GQueue triggered;
|
||||
GHashTable *candidate_hash;
|
||||
GHashTable *pair_hash;
|
||||
GHashTable *transaction_hash;
|
||||
GHashTable *foundation_hash;
|
||||
GTree *all_pairs;
|
||||
GQueue all_pairs_list; /* sorted through gtree */
|
||||
GTree *nominated_pairs; /* nominated by peer */
|
||||
GTree *succeeded_pairs; /* checked by us */
|
||||
GTree *valid_pairs; /* succeeded and nominated */
|
||||
unsigned int active_components;
|
||||
struct timeval start_nominating;
|
||||
|
||||
str ufrag[2]; /* 0 = remote, 1 = local */
|
||||
str pwd[2]; /* ditto */
|
||||
volatile unsigned int agent_flags;
|
||||
|
||||
struct timeval next_check; /* protected by ice_agents_timers_lock */
|
||||
struct timeval last_run; /* ditto */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
extern const unsigned int ice_type_preferences[];
|
||||
extern const char * const ice_type_strings[];
|
||||
|
||||
|
||||
|
||||
|
||||
void ice_init(void);
|
||||
|
||||
enum ice_candidate_type ice_candidate_type(const str *s);
|
||||
enum ice_transport ice_transport(const str *s);
|
||||
int ice_has_related(enum ice_candidate_type);
|
||||
void ice_foundation(struct interface_address *ifa);
|
||||
|
||||
void ice_agent_init(struct ice_agent **agp, struct call_media *media);
|
||||
void ice_update(struct ice_agent *, struct stream_params *);
|
||||
void ice_shutdown(struct ice_agent **);
|
||||
void ice_restart(struct ice_agent *);
|
||||
|
||||
void ice_candidates_free(GQueue *);
|
||||
void ice_remote_candidates(GQueue *, struct ice_agent *);
|
||||
|
||||
void ice_thread_run(void *);
|
||||
|
||||
int ice_request(struct packet_stream *, struct sockaddr_in6 *, struct in6_addr *, struct stun_attrs *);
|
||||
int ice_response(struct packet_stream *ps, struct sockaddr_in6 *src, struct in6_addr *dst,
|
||||
struct stun_attrs *attrs, u_int32_t transaction[3]);
|
||||
|
||||
/* returns 0 if ICE still has work to do, 1 otherwise */
|
||||
INLINE int ice_has_finished(struct call_media *media) {
|
||||
if (!media)
|
||||
return 1;
|
||||
if (!MEDIA_ISSET(media, ICE))
|
||||
return 1;
|
||||
if (!media->ice_agent)
|
||||
return 1;
|
||||
if (AGENT_ISSET(media->ice_agent, COMPLETED))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
INLINE unsigned int ice_type_preference(enum ice_candidate_type type) {
|
||||
if (type >= __ICT_LAST)
|
||||
return 0;
|
||||
return ice_type_preferences[type];
|
||||
}
|
||||
/* local_pref starts with 0 */
|
||||
INLINE u_int32_t ice_priority_pref(unsigned int type_pref, unsigned int local_pref, unsigned int component) {
|
||||
return type_pref << 24 | (65535 - local_pref) << 8 | (256 - component);
|
||||
}
|
||||
INLINE u_int32_t ice_priority(enum ice_candidate_type type, unsigned int local_pref, unsigned int component) {
|
||||
return ice_priority_pref(ice_type_preference(type), local_pref, component);
|
||||
}
|
||||
INLINE unsigned int ice_type_pref_from_prio(u_int32_t prio) {
|
||||
return (prio & 0xff000000) >> 24;
|
||||
}
|
||||
INLINE unsigned int ice_local_pref_from_prio(u_int32_t prio) {
|
||||
return 65535 - ((prio & 0xffff00) >> 8);
|
||||
}
|
||||
INLINE const char *ice_candidate_type_str(enum ice_candidate_type type) {
|
||||
if (type >= __ICT_LAST)
|
||||
return 0;
|
||||
return ice_type_strings[type];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Socket;
|
||||
use Socket6;
|
||||
use Digest::SHA qw(hmac_sha1);
|
||||
use Digest::CRC qw(crc32);
|
||||
|
||||
my ($prio, $ip, $port, $username, $pwd) = @ARGV;
|
||||
|
||||
my $fd;
|
||||
my @dests = getaddrinfo($ip, $port, AF_UNSPEC, SOCK_DGRAM);
|
||||
while (@dests >= 5) {
|
||||
my ($fam, $type, $prot, $addr, $canon, @dests) = @dests;
|
||||
socket($fd, $fam, $type, $prot) or undef($fd), next;
|
||||
connect($fd, $addr) or undef($fd), next;
|
||||
last;
|
||||
}
|
||||
$fd or die($!);
|
||||
|
||||
my @rand = ('A' .. 'Z', 'a' .. 'z');
|
||||
my $ufrag = join('', (map {$rand[rand($#rand)]} (1 .. 10)));
|
||||
my $tract = join('', (map {$rand[rand($#rand)]} (1 .. 12)));
|
||||
my $control = rand() < .5;
|
||||
my $tbreak = int(rand(0xffffffff)) * int(rand(0xffffffff));
|
||||
|
||||
print("transaction: $tract\n");
|
||||
print("my username fragment: $ufrag\n");
|
||||
print("controll".($control?"ing":'ed')."\n");
|
||||
print("tie breaker: $tbreak\n");
|
||||
|
||||
|
||||
my $packet = '';
|
||||
$packet .= attr(6, "$username:$ufrag");
|
||||
$packet .= attr($control ? 0x802a : 0x8029, pack('Q', $tbreak));
|
||||
$packet .= attr(0x24, pack('N', $prio));
|
||||
$packet .= integrity();
|
||||
$packet .= fingerprint();
|
||||
$packet = header() . $packet;
|
||||
|
||||
send($fd, $packet, 0) or die $!;
|
||||
my $buf;
|
||||
recv($fd, $buf, 200, 0) or die;
|
||||
|
||||
my ($code, $length, $cookie, $tract2, $attrs) = unpack('nnN a12 a*', $buf);
|
||||
|
||||
if ($cookie == 0x2112A442 || $tract2 ne $tract) {
|
||||
printf("code: \%x\n", $code);
|
||||
while ($attrs ne '') {
|
||||
my ($type, $len, $cont);
|
||||
($type, $len, $attrs) = unpack('nn a*', $attrs);
|
||||
my $pad = 0;
|
||||
while ((($len + $pad) % 4) != 0) {
|
||||
$pad++;
|
||||
}
|
||||
($cont, $pad, $attrs) = unpack("a$len a$pad a*", $attrs);
|
||||
printf(" attr type: \%x\n", $type);
|
||||
print(" content: $cont\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
print("not stun: ".unpack('H*', $buf)."\n");
|
||||
}
|
||||
|
||||
exit;
|
||||
|
||||
|
||||
sub attr {
|
||||
my ($type, $data) = @_;
|
||||
my $len = length($data);
|
||||
while ((length($data) % 4) != 0) {
|
||||
$data .= "\0";
|
||||
}
|
||||
return pack('nn a*', $type, $len, $data);
|
||||
}
|
||||
sub header {
|
||||
my ($add_length) = @_;
|
||||
$add_length ||= 0;
|
||||
return pack('nnN a12', 1, length($packet) + $add_length, 0x2112A442, $tract);
|
||||
}
|
||||
sub integrity {
|
||||
my $h = header(24);
|
||||
my $hmac = hmac_sha1($h.$packet, $pwd);
|
||||
return attr(8, $hmac);
|
||||
}
|
||||
sub fingerprint {
|
||||
my $h = header(8);
|
||||
my $crc = crc32($h.$packet);
|
||||
return attr(0x8028, pack('N', ($crc ^ 0x5354554e)));
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Socket;
|
||||
use Socket6;
|
||||
use Digest::SHA qw(hmac_sha1);
|
||||
use Digest::CRC qw(crc32);
|
||||
|
||||
my ($controlling, $port, $username, $pwd, @addresses) = @ARGV;
|
||||
|
||||
my %attrs = (
|
||||
6 => \&attr_un,
|
||||
8 => \&attr_mi,
|
||||
0x8028 => \&attr_fp,
|
||||
0x25 => \&attr_use,
|
||||
0x8029 => \&attr_controlled,
|
||||
0x802a => \&attr_controlling,
|
||||
);
|
||||
|
||||
my (@sockets, $packet, $tract, $code);
|
||||
|
||||
for my $addr (@addresses) {
|
||||
my ($fam, $pka, $sin, $meth) = addrparse($addr, $port);
|
||||
socket(my $fd, $fam, SOCK_DGRAM, 0) or die $!;
|
||||
bind($fd, $sin) or die $!;
|
||||
push(@sockets, {fd => $fd, fam => $fam, addr => $pka, sin => $sin, xormethod => $meth});
|
||||
}
|
||||
|
||||
while (1) {
|
||||
my $rin = '';
|
||||
for my $s (@sockets) {
|
||||
vec($rin, fileno($$s{fd}), 1) = 1;
|
||||
}
|
||||
|
||||
select($rin, undef, undef, 1);
|
||||
|
||||
for my $s (@sockets) {
|
||||
vec($rin, fileno($$s{fd}), 1) or next;
|
||||
|
||||
my $src = recv($$s{fd}, my $buf, 200, 0) or die $!;
|
||||
print("\npacket from " . addrdeparse($$s{fam}, $src) . " on ".
|
||||
addrdeparse($$s{fam}, $$s{sin}) . "\n");
|
||||
|
||||
my ($cmd, $len, $cookie, $attrs);
|
||||
($cmd, $len, $cookie, $tract, $attrs) = unpack('nnN a12 a*', $buf);
|
||||
|
||||
if ($cookie != 0x2112A442) {
|
||||
if ($buf =~ /^[\x14-\x3f]/s) {
|
||||
print("DTLS\n");
|
||||
}
|
||||
else {
|
||||
print("not stun: " . unpack("H*", $buf)."\n");
|
||||
}
|
||||
next;
|
||||
}
|
||||
$cmd == 1 or print("not stun request\n"), next;
|
||||
length($attrs) == $len or print("length mismatch\n"), next;
|
||||
|
||||
my ($list, $hash) = unpack_attrs($attrs);
|
||||
|
||||
$$list[$#$list]{name} eq 'fingerprint' or print("last attr not fingerprint\n"), next;
|
||||
$$list[$#$list-1]{name} eq 'message-integrity' or print("last but one attr not MI\n"), next;
|
||||
$$hash{username} or print("no username\n"), next;
|
||||
|
||||
$$hash{controlling} and print("is controlling\n");
|
||||
$$hash{controlled} and print("is controlled\n");
|
||||
$$hash{'use-candidate'} and print("nominated\n");
|
||||
|
||||
print("local username is $$hash{username}{split}[0], remote is $$hash{username}{split}[1]\n");
|
||||
|
||||
if ($$hash{controlling} && $controlling || $$hash{controlled} && !$controlling) {
|
||||
print("role conflict, replying with 487");
|
||||
$code = 0x0111; # binding error
|
||||
$packet = attr(0x9, pack('CCCC a16', 0, 0, 4, 87, 'Role conflict'));
|
||||
$packet .= integrity();
|
||||
$packet .= fingerprint();
|
||||
}
|
||||
else {
|
||||
$code = 0x101; # binding success
|
||||
my $xorattr = $$s{xormethod}($src);
|
||||
$packet = attr(0x20, $xorattr);
|
||||
$packet .= integrity();
|
||||
$packet .= fingerprint();
|
||||
}
|
||||
|
||||
$packet = header() . $packet;
|
||||
|
||||
print("sending reply\n");
|
||||
send($$s{fd}, $packet, 0, $src);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
exit;
|
||||
|
||||
sub xor4 {
|
||||
my ($src) = @_;
|
||||
my @a = unpack_sockaddr_in($src);
|
||||
return pack('nna4', 1, $a[0] ^ 0x2112, $a[1] ^ "\x21\x12\xa4\x42");
|
||||
}
|
||||
sub xor6 {
|
||||
my ($src) = @_;
|
||||
my @a = unpack_sockaddr_in6($src);
|
||||
return pack('nna16', 2, $a[0] ^ 0x2112,
|
||||
$a[1] ^ ("\x21\x12\xa4\x42" . $tract));
|
||||
}
|
||||
sub addrparse {
|
||||
my ($addr, $port) = @_;
|
||||
my $r = inet_pton(AF_INET, $addr);
|
||||
$r and return (AF_INET, $r, pack_sockaddr_in($port, $r), \&xor4);
|
||||
$r = inet_pton(AF_INET6, $addr);
|
||||
$r and return (AF_INET6, $r, pack_sockaddr_in6($port, $r), \&xor6);
|
||||
die;
|
||||
}
|
||||
sub addrdeparse {
|
||||
my ($fam, $sin) = @_;
|
||||
if ($fam == AF_INET) {
|
||||
my @up = unpack_sockaddr_in($sin);
|
||||
return inet_ntop(AF_INET, $up[1]) . ":$up[0]";
|
||||
}
|
||||
if ($fam == AF_INET6) {
|
||||
my @up = unpack_sockaddr_in6($sin);
|
||||
return '['.inet_ntop(AF_INET6, $up[1]) . "]:$up[0]";
|
||||
}
|
||||
die;
|
||||
}
|
||||
|
||||
sub attr_un {
|
||||
my ($cont) = @_;
|
||||
return {name => 'username', split => [$cont =~ /(.*):(.*)/]};
|
||||
}
|
||||
sub attr_mi {
|
||||
return {name => 'message-integrity'};
|
||||
}
|
||||
sub attr_fp {
|
||||
my ($cont) = @_;
|
||||
return {name => 'fingerprint', value => unpack('N', $cont)};
|
||||
}
|
||||
sub attr_use {
|
||||
return {name => 'use-candidate'};
|
||||
}
|
||||
sub attr_controlling {
|
||||
my ($cont) = @_;
|
||||
return {name => 'controlling', tiebreaker => unpack('Q', $cont)};
|
||||
}
|
||||
sub attr_controlled {
|
||||
my ($cont) = @_;
|
||||
return {name => 'controlled', tiebreaker => unpack('Q', $cont)};
|
||||
}
|
||||
|
||||
|
||||
sub unpack_attrs {
|
||||
my ($s) = @_;
|
||||
|
||||
my (@out, %out);
|
||||
while ($s ne '') {
|
||||
my ($type, $len, $cont);
|
||||
($type, $len, $s) = unpack('nn a*', $s);
|
||||
my $pad = 0;
|
||||
while ((($len + $pad) % 4) != 0) {
|
||||
$pad++;
|
||||
}
|
||||
($cont, $pad, $s) = unpack("a$len a$pad a*", $s);
|
||||
my $ins = {type => $type, len => $len, content => $cont,
|
||||
raw => pack('nna*a*', $type, $len, $cont, $pad)};
|
||||
push(@out, $ins);
|
||||
$out{$type} = $ins;
|
||||
|
||||
my $pars = $attrs{$type};
|
||||
$pars or next;
|
||||
|
||||
my $ai = $pars->($cont);
|
||||
%$ins = (%$ins, %$ai);
|
||||
$out{$$ins{name}} = $ins;
|
||||
}
|
||||
return (\@out, \%out);
|
||||
}
|
||||
|
||||
|
||||
sub attr {
|
||||
my ($type, $data) = @_;
|
||||
my $len = length($data);
|
||||
while ((length($data) % 4) != 0) {
|
||||
$data .= "\0";
|
||||
}
|
||||
return pack('nn a*', $type, $len, $data);
|
||||
}
|
||||
sub header {
|
||||
my ($add_length) = @_;
|
||||
$add_length ||= 0;
|
||||
return pack('nnN a12', $code, length($packet) + $add_length, 0x2112A442, $tract);
|
||||
}
|
||||
sub integrity {
|
||||
my $h = header(24);
|
||||
my $hmac = hmac_sha1($h.$packet, $pwd);
|
||||
return attr(8, $hmac);
|
||||
}
|
||||
sub fingerprint {
|
||||
my $h = header(8);
|
||||
my $crc = crc32($h.$packet);
|
||||
return attr(0x8028, pack('N', ($crc ^ 0x5354554e)));
|
||||
}
|
||||
@ -0,0 +1,281 @@
|
||||
package SRTP;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Crypt::Rijndael;
|
||||
use Digest::SHA qw(hmac_sha1);
|
||||
use MIME::Base64;
|
||||
|
||||
our $SRTP_DEBUG = 0;
|
||||
|
||||
our @crypto_suites = (
|
||||
{
|
||||
str => 'AES_CM_128_HMAC_SHA1_80',
|
||||
auth_tag => 10,
|
||||
enc_func => \&aes_cm,
|
||||
iv_rtp => \&aes_cm_iv_rtp,
|
||||
iv_rtcp => \&aes_cm_iv_rtcp,
|
||||
},
|
||||
{
|
||||
str => 'AES_CM_128_HMAC_SHA1_32',
|
||||
auth_tag => 4,
|
||||
enc_func => \&aes_cm,
|
||||
iv_rtp => \&aes_cm_iv_rtp,
|
||||
iv_rtcp => \&aes_cm_iv_rtcp,
|
||||
},
|
||||
{
|
||||
str => 'F8_128_HMAC_SHA1_80',
|
||||
auth_tag => 10,
|
||||
enc_func => \&aes_f8,
|
||||
iv_rtp => \&aes_f8_iv_rtp,
|
||||
iv_rtcp => \&aes_f8_iv_rtcp,
|
||||
},
|
||||
);
|
||||
our %crypto_suites = map {$$_{str} => $_} @crypto_suites;
|
||||
|
||||
sub aes_cm {
|
||||
my ($data, $key, $iv) = @_;
|
||||
|
||||
my $c = Crypt::Rijndael->new($key) or die;
|
||||
length($iv) == 16 or die;
|
||||
my @iv = unpack("C16", $iv);
|
||||
my $out = '';
|
||||
|
||||
while ($data ne '') {
|
||||
$iv = pack("C16", @iv);
|
||||
my $key_segment = $c->encrypt($iv);
|
||||
length($key_segment) == 16 or die;
|
||||
my @ks = unpack("C16", $key_segment);
|
||||
my @ds = unpack("C16", $data);
|
||||
|
||||
for my $i (0 .. $#ds) {
|
||||
my $ss = $ds[$i];
|
||||
my $kk = $ks[$i];
|
||||
$out .= chr($ss ^ $kk);
|
||||
}
|
||||
|
||||
substr($data, 0, 16, '');
|
||||
$data eq '' and last;
|
||||
|
||||
for my $i (reverse(0 .. 15)) {
|
||||
$iv[$i]++;
|
||||
if ($iv[$i] == 256) {
|
||||
$iv[$i] = 0;
|
||||
}
|
||||
else {
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub aes_f8 {
|
||||
my ($data, $key, $iv, $salt) = @_;
|
||||
|
||||
my $m = $salt . "\x55\x55";
|
||||
my $c = Crypt::Rijndael->new(xor_128($key, $m)) or die;
|
||||
my $ivx = $c->encrypt($iv);
|
||||
undef($c);
|
||||
|
||||
$c = Crypt::Rijndael->new($key) or die;
|
||||
my $p_s = "\0" x 16;
|
||||
my $j = 0;
|
||||
my $out = '';
|
||||
|
||||
while ($data ne '') {
|
||||
my $jx = ("\0" x 12) . pack("N", $j);
|
||||
my $key_segment = $c->encrypt(xor_128($ivx, $jx, $p_s));
|
||||
length($key_segment) == 16 or die;
|
||||
my @ks = unpack("C16", $key_segment);
|
||||
my @ds = unpack("C16", $data);
|
||||
|
||||
for my $i (0 .. $#ds) {
|
||||
my $ss = $ds[$i];
|
||||
my $kk = $ks[$i];
|
||||
$out .= chr($ss ^ $kk);
|
||||
}
|
||||
|
||||
substr($data, 0, 16, '');
|
||||
$data eq '' and last;
|
||||
|
||||
$p_s = $key_segment;
|
||||
$j++;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub prf_n {
|
||||
my ($n, $key, $x) = @_;
|
||||
my $d = "\0" x ($n / 8);
|
||||
my $ks = aes_cm($d, $key, $x . "\0\0");
|
||||
return substr($ks, 0, $n / 8);
|
||||
}
|
||||
|
||||
sub xor_n {
|
||||
my ($n, @l) = @_;
|
||||
$n /= 8;
|
||||
my @o = (0) x $n;
|
||||
for my $e (@l) {
|
||||
my @e = unpack("C$n", $e);
|
||||
if (@e < $n) {
|
||||
unshift(@e, ((0) x ($n - @e)));
|
||||
}
|
||||
for my $i (0 .. $#o) {
|
||||
$o[$i] ^= $e[$i];
|
||||
}
|
||||
}
|
||||
return pack("C$n", @o);
|
||||
}
|
||||
|
||||
sub xor_112 {
|
||||
return xor_n(112, @_);
|
||||
}
|
||||
sub xor_128 {
|
||||
return xor_n(128, @_);
|
||||
}
|
||||
|
||||
sub gen_rtp_session_keys {
|
||||
my ($master_key, $master_salt) = @_;
|
||||
|
||||
my $session_key = prf_n(128, $master_key, xor_112($master_salt, "\0\0\0\0\0\0\0"));
|
||||
my $auth_key = prf_n(160, $master_key, xor_112($master_salt, "\1\0\0\0\0\0\0"));
|
||||
my $session_salt = prf_n(112, $master_key, xor_112($master_salt, "\2\0\0\0\0\0\0"));
|
||||
if ($SRTP_DEBUG) {
|
||||
print("RTP keys generated for master key " . unpack("H8", $master_key) . "... and salt " .
|
||||
unpack("H8", $master_salt) . "... are: " .
|
||||
unpack("H8", $session_key) . "..., " .
|
||||
unpack("H*", $auth_key) . ", " .
|
||||
unpack("H8", $session_salt) . "...\n");
|
||||
}
|
||||
|
||||
return ($session_key, $auth_key, $session_salt);
|
||||
}
|
||||
|
||||
sub gen_rtcp_session_keys {
|
||||
my ($master_key, $master_salt) = @_;
|
||||
|
||||
my $session_key = prf_n(128, $master_key, xor_112($master_salt, "\3\0\0\0\0\0\0"));
|
||||
my $auth_key = prf_n(160, $master_key, xor_112($master_salt, "\4\0\0\0\0\0\0"));
|
||||
my $session_salt = prf_n(112, $master_key, xor_112($master_salt, "\5\0\0\0\0\0\0"));
|
||||
if ($SRTP_DEBUG) {
|
||||
print("RTCP keys generated for master key " . unpack("H8", $master_key) . "... and salt " .
|
||||
unpack("H8", $master_salt) . "... are: " .
|
||||
unpack("H8", $session_key) . "..., " .
|
||||
unpack("H*", $auth_key) . ", " .
|
||||
unpack("H8", $session_salt) . "...\n");
|
||||
}
|
||||
|
||||
return ($session_key, $auth_key, $session_salt);
|
||||
}
|
||||
|
||||
sub aes_cm_iv_rtp {
|
||||
my ($r, $ssalt, $roc) = @_;
|
||||
|
||||
my ($hdr, $seq, $ts, $ssrc) = unpack('a2na4a4', $r);
|
||||
my $iv = xor_128($ssalt . "\0\0",
|
||||
$ssrc . "\0\0\0\0\0\0\0\0", pack("Nnn", $roc, $seq, 0));
|
||||
return $iv;
|
||||
}
|
||||
|
||||
sub aes_cm_iv_rtcp {
|
||||
my ($ctx, $r) = @_;
|
||||
|
||||
my $idx = $$ctx{rtcp_index} || 0;
|
||||
my ($hdr, $ssrc) = unpack('a4a4', $r);
|
||||
my $iv = xor_128($$ctx{rtcp_session_salt} . "\0\0",
|
||||
$ssrc . "\0\0\0\0\0\0\0\0", pack("Nn", $idx, 0));
|
||||
return $iv;
|
||||
}
|
||||
|
||||
sub aes_f8_iv_rtp {
|
||||
my ($r, $ssalt, $roc) = @_;
|
||||
|
||||
my ($hdr, $fields) = unpack('a1a11', $r);
|
||||
my $iv = pack('Ca*N', 0, $fields, $roc);
|
||||
return $iv;
|
||||
}
|
||||
|
||||
sub aes_f8_iv_rtcp {
|
||||
my ($ctx, $r) = @_;
|
||||
|
||||
my ($fields) = unpack('a8', $r);
|
||||
my $iv = pack('a*Na*', "\0\0\0\0", (($$ctx{rtcp_index} || 0) | 0x80000000), $fields);
|
||||
return $iv;
|
||||
}
|
||||
|
||||
sub decode_inline_base64 {
|
||||
my ($b64) = @_;
|
||||
my $ks = decode_base64($b64);
|
||||
length($ks) == 30 or die;
|
||||
my @ret = unpack('a16a14', $ks);
|
||||
return @ret;
|
||||
}
|
||||
|
||||
sub encrypt_rtp {
|
||||
my ($suite, $skey, $ssalt, $sauth, $roc, $mki, $mki_len, $packet) = @_;
|
||||
|
||||
my ($hdr, $seq, $h2, $to_enc) = unpack('a2na8a*', $packet);
|
||||
$roc = $roc || 0;
|
||||
$seq == 0 and $roc++;
|
||||
|
||||
my $iv = $$suite{iv_rtp}->($packet, $ssalt, $roc);
|
||||
my $enc = $$suite{enc_func}->($to_enc, $skey,
|
||||
$iv, $ssalt);
|
||||
my $pkt = pack('a*na*a*', $hdr, $seq, $h2, $enc);
|
||||
|
||||
my $hmac = hmac_sha1($pkt . pack("N", $roc), $sauth);
|
||||
# print("HMAC for packet " . unpack("H*", $pkt) . " ROC $roc is " . unpack("H*", $hmac) . "\n");
|
||||
|
||||
append_mki(\$pkt, $mki_len, $mki);
|
||||
|
||||
#$pkt .= pack("N", 1); # mki
|
||||
$pkt .= substr($hmac, 0, $$suite{auth_tag});
|
||||
|
||||
return ($pkt, $roc);
|
||||
}
|
||||
|
||||
sub decrypt_rtp {
|
||||
my ($suite, $skey, $ssalt, $sauth, $roc, $packet) = @_;
|
||||
|
||||
# XXX MKI
|
||||
|
||||
my $plen = length($packet);
|
||||
my $auth_tag = substr($packet, $plen - $$suite{auth_tag}, $$suite{auth_tag});
|
||||
$packet = substr($packet, 0, $plen - $$suite{auth_tag});
|
||||
|
||||
my ($hdr, $seq, $h2, $to_enc) = unpack('a2na8a*', $packet);
|
||||
$roc = $roc || 0;
|
||||
$seq == 0 and $roc++;
|
||||
|
||||
my $iv = $$suite{iv_rtp}->($packet, $ssalt, $roc);
|
||||
my $enc = $$suite{enc_func}->($to_enc, $skey,
|
||||
$iv, $ssalt);
|
||||
my $pkt = pack('a*na*a*', $hdr, $seq, $h2, $enc);
|
||||
|
||||
my $hmac = hmac_sha1($packet . pack("N", $roc), $sauth);
|
||||
# print("HMAC for packet " . unpack("H*", $pkt) . " ROC $roc is " . unpack("H*", $hmac) . "\n");
|
||||
|
||||
#$pkt .= pack("N", 1); # mki
|
||||
|
||||
return ($pkt, $roc, $auth_tag, $hmac);
|
||||
}
|
||||
|
||||
sub append_mki {
|
||||
my ($pack_r, $mki_len, $mki) = @_;
|
||||
|
||||
$mki_len or return;
|
||||
|
||||
$mki = pack('N', $mki);
|
||||
while (length($mki) < $mki_len) {
|
||||
$mki = "\x00" . $mki;
|
||||
}
|
||||
if (length($mki) > $mki_len) {
|
||||
$mki = substr($mki, -$mki_len);
|
||||
}
|
||||
$$pack_r .= $mki;
|
||||
}
|
||||
|
||||
1;
|
||||
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use MIME::Base64;
|
||||
use SRTP;
|
||||
|
||||
my $cs = $SRTP::crypto_suites{$ARGV[0]} or die;
|
||||
my $inline_key = $ARGV[1] or die;
|
||||
my ($key, $salt) = SRTP::decode_inline_base64($inline_key);
|
||||
my ($skey, $sauth, $ssalt) = SRTP::gen_rtp_session_keys($key, $salt);
|
||||
print("Master key: " . unpack("H*", $key) . "\n");
|
||||
print("Master salt: " . unpack("H*", $salt) . "\n");
|
||||
print("RTP session key: " . unpack("H*", $skey) . "\n");
|
||||
print("RTP session auth key: " . unpack("H*", $sauth) . "\n");
|
||||
print("RTP session salt: " . unpack("H*", $ssalt) . "\n");
|
||||
|
||||
my $pack = $ARGV[2];
|
||||
my @pack;
|
||||
if ($pack =~ /:/) {
|
||||
my @pack = split(/:/, $pack);
|
||||
$pack = join('', (map {chr(hex($_))} @pack));
|
||||
}
|
||||
else {
|
||||
$pack = pack("H*", $pack);
|
||||
}
|
||||
|
||||
print("Packet length: " . length($pack) . " bytes\n");
|
||||
|
||||
my ($dec, $roc, $tag, $hmac) = SRTP::decrypt_rtp($cs, $skey, $ssalt, $sauth, 0, $pack);
|
||||
|
||||
print("Auth tag from packet: " . unpack("H*", $tag) . "\n");
|
||||
print("Computer auth tag: " . unpack("H*", $hmac) . "\n");
|
||||
|
||||
|
||||
Loading…
Reference in new issue