Esempio n. 1
0
    def test_add_remove_stream(self):
        # protect RTP
        tx_session = Session(policy=Policy(
            key=KEY,
            ssrc_type=Policy.SSRC_SPECIFIC,
            ssrc_value=12345))
        protected = tx_session.protect(RTP)
        self.assertEqual(len(protected), 182)

        # add stream and unprotect RTP
        rx_session = Session()
        rx_session.add_stream(Policy(
            key=KEY,
            ssrc_type=Policy.SSRC_SPECIFIC,
            ssrc_value=12345))
        unprotected = rx_session.unprotect(protected)
        self.assertEqual(len(unprotected), 172)
        self.assertEqual(unprotected, RTP)

        # remove stream
        rx_session.remove_stream(12345)

        # try removing stream again
        with self.assertRaises(Error) as cm:
            rx_session.remove_stream(12345)
        self.assertEqual(str(cm.exception), 'no appropriate context found')
class srtp_hex2hex_coder(hex_coder):
    '''
    Allow record srtp/rtp stream
    '''
    def __init__(self,
                 override_payload_offset=None,
                 rtp_offset=0,
                 srtpkey=None,
                 resrtpkey=None,
                 verbose=False,
                 payload_type=111,
                 filter_ssrc=None):
        #super(ogg_opus_coder, self).__init__(verbose=verbose)
        hex_coder.__init__(self, verbose=verbose)

        # setup rtp parameters
        self.override_payload_offset = override_payload_offset
        self.rtp_offset = rtp_offset
        self.payload_type = payload_type

        # setup srtp parameters
        self.srtpkey = srtpkey
        self.resrtpkey = resrtpkey
        self.srtp_session = None
        self.ssrc = None
        self.filter_ssrc = filter_ssrc

    def record_rtp_packet(self, packet, is_last=False):
        if not packet:
            print("No packet")
            return False

        if self.verbose:
            print("Dumping packet ", packet)

        rtp_raw_full = None
        try:
            rtp_raw_full = bytes.fromhex(''.join(packet[self.rtp_offset:]))
        except:
            print("Failed to get hex", packet)
            return False

        if len(rtp_raw_full) < RTP_FIXED_HEADER:
            print("udp payload is too small")
            return False

        if self.verbose:
            print(self.rtp_offset, packet[self.rtp_offset - 1],
                  packet[self.rtp_offset], packet[self.rtp_offset + 1])

        # decode RTP Header
        rtp_raw_header = rtp_raw_full[:RTP_FIXED_HEADER]

        if self.verbose:
            print(rtp_raw_header.hex())
        rtp = RTP_HEADER._make(struct.unpack(RTP_HEADER_FMT, rtp_raw_header))
        if self.verbose:
            print(rtp)
        if rtp.PAYLOAD_TYPE & 0b10000000:
            # strip marker bit from payload
            rtp = RTP_HEADER(FIRST=rtp.FIRST,
                             PAYLOAD_TYPE=rtp.PAYLOAD_TYPE & 0b01111111,
                             SEQUENCE_NUMBER=rtp.SEQUENCE_NUMBER,
                             TIMESTAMP=rtp.TIMESTAMP,
                             SSRC=rtp.SSRC)
            print('stripped marker bit', rtp)

        # Filter the RTP with v=2
        if (rtp.FIRST & 0b11000000) != 0b10000000:
            print("Not an RTP")
            return False

        rtp_exten = rtp.FIRST & 0b10000
        rtp_exten_length = 0
        rtp_csrc = rtp.FIRST & 0b1111

        # calculate rtp header length
        calc_rtp_header_len = RTP_FIXED_HEADER + rtp_csrc * 4
        if rtp_exten:
            exten_start = RTP_FIXED_HEADER + rtp_csrc * 4
            exten_raw = rtp_raw_full[exten_start:exten_start + 4]
            if len(exten_raw) != 4:
                print("Skipping malformed RTP")
                return False
            rtp_exten_profile, rtp_exten_length = struct.unpack(
                '>HH', exten_raw)
            calc_rtp_header_len += 4 + rtp_exten_length * 4

        if self.verbose:
            print("calc_rtp_header_len", calc_rtp_header_len)

        if self.override_payload_offset:
            calc_rtp_header_len = self.override_payload_offset - self.rtp_offset

        # Filter opus
        if self.payload_type and rtp.PAYLOAD_TYPE != self.payload_type:
            print(
                "Skipping payload {rtp_payload_type} while {opus_payload_type} expected."
                .format(rtp_payload_type=rtp.PAYLOAD_TYPE,
                        opus_payload_type=self.payload_type))
            return False

        if self.filter_ssrc and self.filter_ssrc != rtp.SSRC:
            print(
                "Skipping ssrc={rtp_ssrc} while {filter_ssrc} expected".format(
                    rtp_ssrc=rtp.SSRC, filter_ssrc=self.filter_ssrc))
            return False

        if len(rtp_raw_full) < (calc_rtp_header_len + 1):
            print("Empty payload")
            return False

        if self.srtpkey:
            if self.ssrc != rtp.SSRC:
                #if not self.srtp_session:
                #    self.srtp_session = Session()
                self.srtp_session = Session()
                print("using key [%s]" % self.srtpkey)
                srtpkey = base64.b64decode(self.srtpkey)
                plc = Policy(key=srtpkey,
                             ssrc_value=rtp.SSRC,
                             ssrc_type=Policy.SSRC_ANY_INBOUND)
                print(plc)
                self.srtp_session.add_stream(plc)
                self.ssrc = rtp.SSRC
            try:
                rtp_raw_full = self.srtp_session.unprotect(rtp_raw_full)
            except:
                print("decrypt fail seq={sequence}, ssrc={ssrc}".format(
                    sequence=rtp.SEQUENCE_NUMBER, ssrc=rtp.SSRC))
                '''
                if self.resrtpkey:
                    srtpkey = base64.b64decode(self.resrtpkey)
                    plc = Policy(key=srtpkey,ssrc_value=rtp.SSRC,ssrc_type=Policy.SSRC_ANY_INBOUND)
                    self.srtp_session.add_stream(plc)
                    print("Using restrpkey here from next packet")
                '''
                return False

        self.write_udp(
            bytes.fromhex(''.join(packet[:self.rtp_offset])) + rtp_raw_full)
        return True

    def record_rtp_file(self, infile, outfile, hexstring=False):
        '''
        Convert an RTP hexdump into a recorded file
        '''
        self.start_file(outfile)

        if hexstring:
            with open(infile, 'r') as rtp_fd:
                packet_counter = 0
                success_counter = 0
                for xline in rtp_fd:
                    xline = xline.strip()
                    packet = [
                        xline[idx:idx + 2] for idx in range(0, len(xline), +2)
                    ]
                    packet_counter += 1
                    if self.record_rtp_packet(packet):
                        success_counter += 1
        else:
            re_valid_hex = re.compile(
                r'^[0-9A-Fa-f]{1,8}\s{1,4}[0-9A-Fa-f]{2}')
            with open(infile, 'r') as rtp_fd:
                packet_counter = 0
                success_counter = 0
                packet = []
                for xline in rtp_fd:
                    if not xline or not re_valid_hex.match(xline):
                        if packet:
                            packet_counter += 1
                            if self.record_rtp_packet(packet):
                                success_counter += 1
                            packet = []
                    else:
                        content = xline.split()
                        #print(len(content))
                        content.pop(0)  # skip the segment column
                        packet.extend(content)

                if packet:
                    packet_counter += 1
                    if self.record_rtp_packet(packet, is_last=True):
                        success_counter += 1

                print("Written %d out of %d packets" %
                      (success_counter, packet_counter))
        self.end_file()
Esempio n. 3
0
class srtp_ogg_opus_coder(ogg_opus_coder):
    '''
    Allow record srtp/rtp stream
    '''
    def __init__(self,
                 override_payload_offset=None,
                 rtp_offset=0,
                 srtpkey=None,
                 resrtpkey=None,
                 verbose=False,
                 payload_type=111,
                 filter_ssrc=None):
        #super(ogg_opus_coder, self).__init__(verbose=verbose)
        ogg_opus_coder.__init__(self, verbose=verbose)

        # setup rtp parameters
        self.override_payload_offset = override_payload_offset
        self.rtp_offset = rtp_offset
        self.payload_type = payload_type

        # setup srtp parameters
        self.srtpkey = srtpkey
        self.resrtpkey = resrtpkey
        self.srtp_session = None
        self.ssrc = None
        self.filter_ssrc = filter_ssrc

    def record_rtp_packet(self, packet, is_last=False):
        assert self.ogg is not None

        if not packet:
            print("No packet")
            return False

        if self.verbose:
            print("Dumping packet ", packet)

        rtp_raw_full = None
        try:
            rtp_raw_full = bytes.fromhex(''.join(packet[self.rtp_offset:]))
        except:
            print("Failed to get hex", packet)
            return False

        if len(rtp_raw_full) < RTP_FIXED_HEADER:
            print("udp payload is too small")
            return False

        if self.verbose:
            print(self.rtp_offset, packet[self.rtp_offset - 1],
                  packet[self.rtp_offset], packet[self.rtp_offset + 1])

        # decode RTP Header
        rtp_raw_header = rtp_raw_full[:RTP_FIXED_HEADER]

        if self.verbose:
            print(rtp_raw_header.hex())
        rtp = RTP_HEADER._make(struct.unpack(RTP_HEADER_FMT, rtp_raw_header))
        if self.verbose:
            print(rtp)

        # Filter the RTP with v=2
        if (rtp.FIRST & 0b11000000) != 0b10000000:
            print("Not an RTP")
            return False

        rtp_exten = rtp.FIRST & 0b10000
        rtp_exten_length = 0
        rtp_csrc = rtp.FIRST & 0b1111

        # calculate rtp header length
        calc_rtp_header_len = RTP_FIXED_HEADER + rtp_csrc * 4
        if rtp_exten:
            exten_start = RTP_FIXED_HEADER + rtp_csrc * 4
            exten_raw = rtp_raw_full[exten_start:exten_start + 4]
            if len(exten_raw) != 4:
                print("Skipping malformed RTP")
                return False
            rtp_exten_profile, rtp_exten_length = struct.unpack(
                '>HH', exten_raw)
            calc_rtp_header_len += 4 + rtp_exten_length * 4

        if self.verbose:
            print("calc_rtp_header_len", calc_rtp_header_len)

        if self.override_payload_offset:
            calc_rtp_header_len = self.override_payload_offset - self.rtp_offset

        # Filter opus
        if self.payload_type and rtp.PAYLOAD_TYPE != self.payload_type:
            print(
                "Skipping payload {rtp_payload_type} while {opus_payload_type} expected."
                .format(rtp_payload_type=rtp.PAYLOAD_TYPE,
                        opus_payload_type=self.payload_type))
            return False

        if self.filter_ssrc and self.filter_ssrc != rtp.SSRC:
            print(
                "Skipping ssrc={rtp_ssrc} while {filter_ssrc} expected".format(
                    rtp_ssrc=rtp.SSRC, filter_ssrc=self.filter_ssrc))
            return False

        if len(rtp_raw_full) < (calc_rtp_header_len + 1):
            print("Empty payload")
            return False

        if self.srtpkey:
            if self.ssrc != rtp.SSRC:
                if not self.srtp_session:
                    self.srtp_session = Session()
                print("using key [%s]" % self.srtpkey)
                srtpkey = base64.b64decode(self.srtpkey)
                plc = Policy(key=srtpkey,
                             ssrc_value=rtp.SSRC,
                             ssrc_type=Policy.SSRC_ANY_INBOUND)
                print(plc)
                self.srtp_session.add_stream(plc)
                self.ssrc = rtp.SSRC
            try:
                rtp_raw_full = self.srtp_session.unprotect(rtp_raw_full)
            except:
                print("decrypt fail seq={sequence}, ssrc={ssrc}".format(
                    sequence=rtp.SEQUENCE_NUMBER, ssrc=rtp.SSRC))
                '''
                if self.resrtpkey:
                    srtpkey = base64.b64decode(self.resrtpkey)
                    plc = Policy(key=srtpkey,ssrc_value=rtp.SSRC,ssrc_type=Policy.SSRC_ANY_INBOUND)
                    self.srtp_session.add_stream(plc)
                    print("Using restrpkey here from next packet")
                '''
                return False

        # Add bitstream header
        if self.ogg.get_curr_bitstream() != rtp.SSRC:
            self.write_stream_header(rtp.SSRC)
            self.write_stream_comment('hex_to_opus', [str(rtp)])

        # rtp_payload = rtp_raw_full[RTP_FIXED_HEADER:]
        rtp_payload = rtp_raw_full[calc_rtp_header_len:]
        self.ogg.write_page(
            rtp_payload,
            is_data=True,
            is_last=is_last,
            ptime=20,
            pageno=rtp.SEQUENCE_NUMBER)  # By default the ptime=20
        return True

    def record_rtp_file(self, infile, outfile):
        '''
        Convert an RTP hexdump into a recorded file
        '''
        self.start_file(outfile)
        re_valid_hex = re.compile(r'^[0-9A-Fa-f]{1,8}\s{1,4}[0-9A-Fa-f]{2}')
        with open(infile, 'r') as rtp_fd:
            packet_counter = 0
            success_counter = 0
            packet = []
            for xline in rtp_fd:
                if not xline or not re_valid_hex.match(xline):
                    if packet:
                        packet_counter += 1
                        if self.record_rtp_packet(packet):
                            success_counter += 1
                        packet = []
                else:
                    content = xline.split()
                    #print(len(content))
                    content.pop(0)  # skip the segment column
                    packet.extend(content)

            if packet:
                self.record_rtp_packet(packet, is_last=True)

            print("Written %d out of %d packets" %
                  (success_counter, packet_counter))
        self.end_file()

    def hexdump(self, content):
        counter = 0
        output = []
        for segment in range((len(content) >> 4) + 1):
            segment_out = []

            segment_out.append('%06x' % counter)
            for offset in range(0, 16):
                pos = (segment << 4) + offset
                if pos >= len(content):
                    break  # avoid overflow
                segment_out.append('%02x' % content[pos])
                counter += 1
            output.append(' '.join(segment_out))

        output.append('\n')
        return '\n'.join(output)

    def playback_as_srtp_stream(self, infile, outfile, starting_sequence=0):
        '''
        Read an ogg file and create rtp/srtp in hex form
        '''
        self.ogg.reset(open(infile, 'rb'))

        with open(outfile, 'w') as hex_fd:
            page_counter = 0
            sequence_counter = starting_sequence
            for header, content in self.ogg:
                print(header)
                if 0 == page_counter:
                    opus_identity_head = OPUS_IDENTITY_HEADER._make(
                        struct.unpack(OPUS_IDENTITY_HEADER_FMT, content))
                    print(opus_identity_head)
                #elif 1 == page_counter:
                #    opus_comment_head = OPUS_COMMENT_HEADER._make(struct.unpack(OPUS_COMMENT_HEADER_FMT, content))
                #    print(opus_comment_head)
                else:
                    print(' '.join([hex(x) for x in content]))
                    if page_counter > 1:
                        # show the TOC byte
                        #toc = content[0]
                        #config = (toc >> 3)
                        #s = toc & 0b100
                        #s = s >> 2
                        #num_frames = toc & 0b11
                        #print("config %d, s %d, c/frames %d" % (config, s, num_frames))

                        # make an RTP packet
                        rtp = RTP_HEADER(0x80, self.payload_type,
                                         sequence_counter, header.GRANULE_POS,
                                         header.BITSTREAM)
                        rtp_raw_full = struct.pack(RTP_HEADER_FMT, *
                                                   rtp) + content

                        if self.srtpkey:
                            if self.ssrc != rtp.SSRC:
                                print("using key [%s]" % self.srtpkey)
                                srtpkey = base64.b64decode(self.srtpkey)
                                plc = Policy(
                                    key=srtpkey,
                                    ssrc_value=rtp.SSRC,
                                    ssrc_type=Policy.SSRC_ANY_OUTBOUND)
                                print(plc)
                                self.srtp_session = Session(policy=plc)
                                self.ssrc = rtp.SSRC
                            try:
                                rtp_raw_full = self.srtp_session.protect(
                                    rtp_raw_full)
                            except:
                                print(
                                    "encrypt fail seq={sequence}, ssrc={ssrc}".
                                    format(sequence=rtp.SEQUENCE_NUMBER,
                                           ssrc=rtp.SSRC))

                        hex_fd.write(self.hexdump(rtp_raw_full))
                        if sequence_counter == MAX_RTP_SEQUENCE_NUM:
                            sequence_counter = 0
                        else:
                            sequence_counter += 1

                page_counter += 1
                print("Read %d pages" % page_counter)