FS-10778: Add support for MKI to SRTP

MKI support for SRTP has been tested on calls to/from
Telnyx's Skype for Business from/to local extension registered to FS
and between Skype for Business clients connected to FreeSWITCH.

SfB -> FreeSWITCH -> User 1004
   SRTP           RTP
   with
   MKI

SfB <- FreeSWITCH <- User 1004
   SRTP           RTP

SfB <-> FreeSWITCH <-> SfB
 SRTP/MKI      SRTP/MKI

Channel variable "rtp_secure_media_mki" was added to drive offering
of MKI on outbound SRTP from FS.

How to use rtp_secure_media_mki

Set rtp_secure_media_mki=true to offer MKI for outgoing SRTP (if SRTP is used) in inbound call.
Export rtp_secure_media=true to offer MKI for outgoing SRTP (if SRTP is used) on outbound call.
... or set it in the codec string for bridged calls

<action application="set" data="rtp_secure_media_mki=true"/>

<action application="export" data="rtp_secure_media_mki=true"/>

<action application="bridge" data="[absolute_codec_string=^^:PCMU:PCMA:G729,rtp_secure_media=true,rtp_secure_media_mki=true]sofia/external/+12404373253@169.55.36.24:5060;transport=tls"/>

EXAMPLES

1. Set example

 57     <extension name="telnyx_test_1_2">
 58         <condition field="destination_number" expression="^(0012404373253)$">
 59             <action application="set" data="rtp_secure_media=true"/>
 60             <action application="set" data="rtp_secure_media_mki=true"/>
 61             <action application="answer"/>
 62             <action application="bridge" data="user/1004@${domain_name}"/>
 63       </condition>
 64   </extension>

Description: SRTP will be used on outbound leg in incoming call due to rtp_secure_media=true
set and MKI will be offered in SDP. SRTP will not be used on a bridged call to extension 1004.

2. Export example

 75   <extension name="to_skype_for_business">
 76       <condition field="destination_number" expression="^(840531022)$">
 77           <action application="set" data="rtp_secure_media=optional"/>
 78           <action application="export" data="rtp_secure_media_mki=true"/>
 79           <action application="bridge" data="[absolute_codec_string=^^:PCMU:PCMA:G729,rtp_secure_media=true]sofia/external/+12404373728@169.55.36.24:5060;transport=tls"/>
 80       </condition>
 81   </extension>

Description: SRTP on inbound call has been set to optional therefore MKI will be used
on outbound SRTP in this call if SRTP is used at all. SRTP will be used on a bridged call
due to rtp_secure_media=true set in codec string and MKI will be used in offering SDP.

3. Bridging between Skype for Business clients: set

 97     <extension name="S4B_fs_S4B">
 98         <condition field="destination_number" expression="^(0012404373254)$">
 99             <action application="set" data="rtp_secure_media=true"/>
100             <action application="set" data="rtp_secure_media_mki=true"/>
101             <action application="bridge" data="[absolute_codec_string=^^:PCMU:PCMA:G729,rtp_secure_media=true]sofia/external/+12404373253@169.55.36.$
102       </condition>
103   </extension>

Result:
2017-11-27 19:00:26.977704 [NOTICE] switch_ivr_originate.c:527 Ring Ready sofia/external/+12404373728@telnyxlab.com!
2017-11-27 19:00:32.657687 [NOTICE] switch_core_media.c:1534 Skipping MKI due to empty index
2017-11-27 19:00:32.657687 [INFO] switch_rtp.c:4079 Activating audio Secure RTP SEND
2017-11-27 19:00:32.657687 [INFO] switch_rtp.c:4057 Activating audio Secure RTP RECV
2017-11-27 19:00:32.657687 [NOTICE] sofia.c:8419 Channel [sofia/external/%2B12404373253@169.55.36.24:5060] has been answered
2017-11-27 19:00:35.317702 [INFO] switch_rtp.c:4079 Activating audio Secure RTP SEND (with MKI)
2017-11-27 19:00:35.317702 [INFO] switch_rtp.c:4057 Activating audio Secure RTP RECV (with MKI)
2017-11-27 19:00:35.317702 [NOTICE] sofia_media.c:92 Pre-Answer sofia/external/+12404373728@telnyxlab.com!

Description: SRTP with MKI is used on outbound leg of inbound call (due to use on inbound leg of this call and "set").
Standard SRTP is used in both legs of outbound call, because rtp_secure_media_mki wasn't exported
or set in codec string for the outbound call.

4. Bridging between Skype for Business clients: Set and export

 97     <extension name="S4B_fs_S4B">
 98         <condition field="destination_number" expression="^(0012404373254)$">
 99             <action application="set" data="rtp_secure_media=true"/>
100             <action application="set" data="rtp_secure_media_mki=true"/>
101             <action application="export" data="rtp_secure_media_mki=true"/>
102             <action application="bridge" data="[absolute_codec_string=^^:PCMU:PCMA:G729,rtp_secure_media=true]sofia/external/+12404373253@169.55.36.24:5060;transport=tls"/>
103       </condition>
104   </extension>

Result:
2017-11-27 18:51:29.017689 [NOTICE] switch_ivr_originate.c:527 Ring Ready sofia/external/+12404373728@telnyxlab.com!
2017-11-27 18:51:35.097729 [INFO] switch_rtp.c:4079 Activating audio Secure RTP SEND (with MKI)
2017-11-27 18:51:35.097729 [INFO] switch_rtp.c:4057 Activating audio Secure RTP RECV (with MKI)
2017-11-27 18:51:35.097729 [NOTICE] sofia.c:8419 Channel [sofia/external/%2B12404373253@169.55.36.24:5060] has been answered
2017-11-27 18:51:37.797706 [INFO] switch_rtp.c:4079 Activating audio Secure RTP SEND (with MKI)
2017-11-27 18:51:37.797706 [INFO] switch_rtp.c:4057 Activating audio Secure RTP RECV (with MKI)

Description: Connecting Skype For Business client to Skype for Business client.
Send SRTP with MKI in both outbound streams:
	- for inbound call: MKI was offered in incoming call and enabled for outbound leg with "set"
	- for outbound call: MKI was enabled with "export"

5. Other examples

Setup to use SRTP with MKI only on the inbound SRTP on incoming call from Telnyx SfB
Tested dialing 0012404373253 from SfB to FS, leg SfB <-> FS uses SRTP with MKI
on inbound SRTP only

57     <extension name="telnyx_test_1_2">
58         <condition field="destination_number" expression="^(0012404373253)$">
59              <action application="set" data="rtp_secure_media=true"/>
61              <action application="answer"/>
62              <action application="bridge" data="user/1004@${domain_name}"/>
63       </condition>
64   </extension>

Result:
2017-11-23 20:44:35.406026 [INFO] mod_dialplan_xml.c:637 Processing Test02 <+12404373728>->0012404373253 in context public
2017-11-23 20:44:38.566022 [INFO] switch_rtp.c:4107 Activating audio Secure RTP SEND
2017-11-23 20:44:38.566022 [INFO] switch_rtp.c:4085 Activating audio Secure RTP RECV (with MKI)

Setup to send and receive SRTP with MKI on incoming call from Telnyx SfB
Tested dialing 0012404373253 from SfB to FS, leg SfB <-> FS uses SRTP with MKI
in both directions

57     <extension name="telnyx_test_1_2">
58         <condition field="destination_number" expression="^(0012404373253)$">
59     		<action application="set" data="rtp_secure_media=true"/>
60     		<action application="set" data="rtp_secure_media_mki"/>
61     		<action application="answer"/>
62     		<action application="bridge" data="user/1004@${domain_name}"/>
63       </condition>
64   </extension>

Result:
2017-11-23 20:42:06.026034 [INFO] mod_dialplan_xml.c:637 Processing Test02 <+12404373728>->0012404373253 in context public
2017-11-23 20:42:09.526034 [INFO] switch_rtp.c:4107 Activating audio Secure RTP SEND (with MKI)
2017-11-23 20:42:09.526034 [INFO] switch_rtp.c:4085 Activating audio Secure RTP RECV (with MKI)

Setup to offer MKI on outbound call to extension 1001 (X-Lite -> FS -> linphone)
Tested dialing 0012404373253 from user 1004, leg FS <-> 1001 uses SRTP with MKI

782     <extension name="telnyx_test_1_2">
783         <condition field="destination_number" expression="^(0012404373253)$">
784             <action application="export" data="rtp_secure_media_outbound=true"/>
785             <action application="export" data="rtp_secure_media_mki"/>
786             <action application="answer"/>
797             <action application="bridge" data="user/1001@${domain_name}"/>
798       </condition>
799   </extension>

Result:
2017-11-23 20:23:26.266034 [INFO] mod_dialplan_xml.c:637 Processing 1000 windows <1000>->0012404373253 in context default
2017-11-23 20:23:26.366035 [INFO] switch_rtp.c:4107 Activating audio Secure RTP SEND (with MKI)
2017-11-23 20:23:26.366035 [INFO] switch_rtp.c:4085 Activating audio Secure RTP RECV

SfB sometimes offers crypto with LIFETIME but no MKI index, e.g.:
a=crypto:5 AES_CM_128_HMAC_SHA1_80 inline:9OtFWi17H9E8ywlm0iazemjAqXu2RhJ3DZyo+VLJ|2^31

Defaulting to no-mki SRTP in case key material doesn't contain MKI index.
This commit is contained in:
Piotr Gregor 2017-11-10 21:30:47 +00:00 committed by Muteesa Fred
parent 180d427cd6
commit 1fdd58f533
6 changed files with 626 additions and 140 deletions

View File

@ -44,7 +44,6 @@ SWITCH_BEGIN_EXTERN_C
#define SWITCH_RTP_MAX_BUF_LEN 16384
#define SWITCH_RTCP_MAX_BUF_LEN 16384
#define SWITCH_RTP_MAX_BUF_LEN_WORDS 4094 /* (max / 4) - 2 */
#define SWITCH_RTP_MAX_CRYPTO_LEN 64
//#define SWITCH_RTP_KEY_LEN 30
//#define SWITCH_RTP_CRYPTO_KEY_32 "AES_CM_128_HMAC_SHA1_32"
#define SWITCH_RTP_CRYPTO_KEY_80 "AES_CM_128_HMAC_SHA1_80"
@ -67,14 +66,16 @@ typedef enum {
typedef struct switch_srtp_crypto_suite_s {
char *name;
switch_rtp_crypto_key_type_t type;
int keylen;
int keysalt_len;
int salt_len;
} switch_srtp_crypto_suite_t;
extern switch_srtp_crypto_suite_t SUITES[CRYPTO_INVALID];
struct switch_rtp_crypto_key {
uint32_t index;
switch_rtp_crypto_key_type_t type;
unsigned char key[SWITCH_RTP_MAX_CRYPTO_LEN];
unsigned char keysalt[SWITCH_RTP_MAX_CRYPTO_LEN];
switch_size_t keylen;
struct switch_rtp_crypto_key *next;
};
@ -170,9 +171,7 @@ typedef enum { /* FMT Values for PSFB Payload Types http://www.iana.org/assignme
SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session,
switch_rtp_crypto_direction_t direction,
uint32_t index, switch_rtp_crypto_key_type_t type, unsigned char *key, switch_size_t keylen);
SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session, switch_rtp_crypto_direction_t direction, uint32_t index, switch_secure_settings_t *ssec);
///\defgroup rtp RTP (RealTime Transport Protocol)
///\ingroup core1

View File

@ -779,6 +779,8 @@ typedef enum {
SWITCH_RTP_FLAG_TEXT,
SWITCH_RTP_FLAG_OLD_FIR,
SWITCH_RTP_FLAG_PASSTHRU,
SWITCH_RTP_FLAG_SECURE_SEND_MKI,
SWITCH_RTP_FLAG_SECURE_RECV_MKI,
SWITCH_RTP_FLAG_INVALID
} switch_rtp_flag_t;
@ -2566,6 +2568,12 @@ typedef enum {
CRYPTO_INVALID
} switch_rtp_crypto_key_type_t;
/* Keep in sync with CRYPTO_KEY_PARAM_METHOD table. */
typedef enum {
CRYPTO_KEY_PARAM_METHOD_INLINE, /* identified by "inline" chars in SRTP key parameter */
CRYPTO_KEY_PARAM_METHOD_INVALID
} switch_rtp_crypto_key_param_method_type_t;
typedef struct payload_map_s {
switch_media_type_t type;
switch_sdp_type_t sdp_type;
@ -2709,6 +2717,56 @@ typedef struct switch_mm_s {
char *auth_password;
} switch_mm_t;
#define SWITCH_RTP_MAX_CRYPTO_LEN 64
/* If MKI is used, then one or more key-materials are present in the <key-params> section of the crypto attribute.
* This struct describes the single MKI entry (key-material) within <key-params> section of crypto attribute.
* Key-material follows the format:
* "inline:" <key||salt> ["|" lifetime] ["|" MKI ":" length]
* which translates to
* "inline: KEYSALT|MKI_ID:MKI_SZ" or "inline: KEYSALT|LIFETIME|MKI_ID:MKI_SZ" */
typedef struct switch_crypto_key_material_s {
switch_rtp_crypto_key_param_method_type_t method;
unsigned char raw_key[SWITCH_RTP_MAX_CRYPTO_LEN]; /* Key-salt. Master key appended with salt. Sizes determined by crypto suite. */
char *crypto_key; /* Complete key material string ("method:keysalt[|lifetime]|mki"). */
uint64_t lifetime; /* OPTIONAL. The lifetime value MAY be written as a non-zero, positive decimal integer or as a power of 2. Must be less than max lifetime of RTP and RTCP packets in given crypto suite. */
unsigned int mki_id; /* OPTIONAL. */
unsigned int mki_size; /* OPTIONAL. Byte length of the master key field in the RTP packet. */
struct switch_crypto_key_material_s *next; /* NULL if this is the last master key in crypto attribute set. */
} switch_crypto_key_material_t;
typedef struct secure_settings_s {
int crypto_tag;
unsigned char local_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN];
unsigned char remote_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN];
switch_rtp_crypto_key_type_t crypto_type;
char *local_crypto_key;
char *remote_crypto_key;
/*
* MKI support (as per rfc4568).
* Normally single crypto attribute contains one key-material in a <key-params> section, e.g. "inline: KEYSALT" or "inline: KEYSALT|2^LIFETIME_BITS".
* But if MKI is used, then one or more key-materials are present in the <key-params> section of the crypto attribute. Each key-material follows the format:
*
* "inline:" <key||salt> ["|" lifetime] ["|" MKI ":" length]
*
* "inline: KEYSALT|MKI_ID:MKI_SZ"
* or
* "inline: KEYSALT|2^LIFETIME_BITS|MKI_ID:MKI_SZ"
*
* This points to singly linked list of key-material descriptions if there is more than one key-material present in this crypto attribute (this key is inserted as the head of the list in that case), or to NULL otherwise.
*/
struct switch_crypto_key_material_s *local_key_material_next; /* NULL if MKI not used for crypto set on outbound SRTP. */
unsigned long local_key_material_n; /* number of key_materials in the linked list for outbound SRTP */
struct switch_crypto_key_material_s *remote_key_material_next; /* NULL if MKI not used for crypto set on inbound SRTP. */
unsigned long remote_key_material_n; /* number of key_materials in the linked list for inbound SRTP */
} switch_secure_settings_t;
/* Default MKI index used for packets send from FS. We always use first key if multiple master keys are present in the crypto attribute. */
#define SWITCH_CRYPTO_MKI_INDEX 0
/* max number of MKI in a single crypto line supported */
#define SWITCH_CRYPTO_MKI_MAX 20
SWITCH_END_EXTERN_C
#endif

View File

@ -391,7 +391,7 @@ static inline int switch_string_has_escaped_data(const char *in)
#endif
SWITCH_DECLARE(switch_status_t) switch_b64_encode(unsigned char *in, switch_size_t ilen, unsigned char *out, switch_size_t olen);
SWITCH_DECLARE(switch_size_t) switch_b64_decode(char *in, char *out, switch_size_t olen);
SWITCH_DECLARE(switch_size_t) switch_b64_decode(const char *in, char *out, switch_size_t olen);
SWITCH_DECLARE(char *) switch_amp_encode(char *s, char *buf, switch_size_t len);

View File

@ -64,16 +64,6 @@ typedef enum {
SMF_VB_PAUSED = (1 << 3)
} smh_flag_t;
typedef struct secure_settings_s {
int crypto_tag;
unsigned char local_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN];
unsigned char remote_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN];
switch_rtp_crypto_key_type_t crypto_type;
char *local_crypto_key;
char *remote_crypto_key;
} switch_secure_settings_t;
typedef struct core_video_globals_s {
int cpu_count;
int cur_cpu;
@ -282,16 +272,16 @@ struct switch_media_handle_s {
};
static switch_srtp_crypto_suite_t SUITES[CRYPTO_INVALID] = {
{ "AEAD_AES_256_GCM_8", AEAD_AES_256_GCM_8, 44},
{ "AEAD_AES_128_GCM_8", AEAD_AES_128_GCM_8, 28},
{ "AES_CM_256_HMAC_SHA1_80", AES_CM_256_HMAC_SHA1_80, 46},
{ "AES_CM_192_HMAC_SHA1_80", AES_CM_192_HMAC_SHA1_80, 38},
{ "AES_CM_128_HMAC_SHA1_80", AES_CM_128_HMAC_SHA1_80, 30},
{ "AES_CM_256_HMAC_SHA1_32", AES_CM_256_HMAC_SHA1_32, 46},
{ "AES_CM_192_HMAC_SHA1_32", AES_CM_192_HMAC_SHA1_32, 38},
{ "AES_CM_128_HMAC_SHA1_32", AES_CM_128_HMAC_SHA1_32, 30},
{ "AES_CM_128_NULL_AUTH", AES_CM_128_NULL_AUTH, 30}
switch_srtp_crypto_suite_t SUITES[CRYPTO_INVALID] = {
{ "AEAD_AES_256_GCM_8", AEAD_AES_256_GCM_8, 44, 12},
{ "AEAD_AES_128_GCM_8", AEAD_AES_128_GCM_8, 28, 12},
{ "AES_CM_256_HMAC_SHA1_80", AES_CM_256_HMAC_SHA1_80, 46, 14},
{ "AES_CM_192_HMAC_SHA1_80", AES_CM_192_HMAC_SHA1_80, 38, 14},
{ "AES_CM_128_HMAC_SHA1_80", AES_CM_128_HMAC_SHA1_80, 30, 14},
{ "AES_CM_256_HMAC_SHA1_32", AES_CM_256_HMAC_SHA1_32, 46, 14},
{ "AES_CM_192_HMAC_SHA1_32", AES_CM_192_HMAC_SHA1_32, 38, 14},
{ "AES_CM_128_HMAC_SHA1_32", AES_CM_128_HMAC_SHA1_32, 30, 14},
{ "AES_CM_128_NULL_AUTH", AES_CM_128_NULL_AUTH, 30, 14}
};
SWITCH_DECLARE(switch_rtp_crypto_key_type_t) switch_core_media_crypto_str2type(const char *str)
@ -315,12 +305,16 @@ SWITCH_DECLARE(const char *) switch_core_media_crypto_type2str(switch_rtp_crypto
}
SWITCH_DECLARE(int) switch_core_media_crypto_keylen(switch_rtp_crypto_key_type_t type)
SWITCH_DECLARE(int) switch_core_media_crypto_keysalt_len(switch_rtp_crypto_key_type_t type)
{
switch_assert(type < CRYPTO_INVALID);
return SUITES[type].keylen;
return SUITES[type].keysalt_len;
}
static const char* CRYPTO_KEY_PARAM_METHOD[CRYPTO_KEY_PARAM_METHOD_INVALID] = {
[CRYPTO_KEY_PARAM_METHOD_INLINE] = "inline",
};
static inline switch_media_flow_t sdp_media_flow(unsigned in)
{
switch(in) {
@ -1161,16 +1155,14 @@ static switch_status_t switch_core_media_build_crypto(switch_media_handle_t *smh
return SWITCH_STATUS_SUCCESS;
}
//#define SAME_KEY
#ifdef SAME_KEY
if (switch_channel_test_flag(channel, CF_AVPF) && type == SWITCH_MEDIA_TYPE_VIDEO) {
if (direction == SWITCH_RTP_CRYPTO_SEND) {
memcpy(engine->ssec[ctype].local_raw_key, smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec.local_raw_key, SUITES[ctype].keylen);
memcpy(engine->ssec[ctype].local_raw_key, smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec.local_raw_key, SUITES[ctype].keysalt_len);
key = engine->ssec[ctype].local_raw_key;
} else {
memcpy(engine->ssec[ctype].remote_raw_key, smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec.remote_raw_key, SUITES[ctype].keylen);
memcpy(engine->ssec[ctype].remote_raw_key, smh->engines[SWITCH_MEDIA_TYPE_AUDIO].ssec.remote_raw_key, SUITES[ctype].keysalt_len);
key = engine->ssec[ctype].remote_raw_key;
}
} else {
@ -1181,12 +1173,12 @@ static switch_status_t switch_core_media_build_crypto(switch_media_handle_t *smh
key = engine->ssec[ctype].remote_raw_key;
}
switch_rtp_get_random(key, SUITES[ctype].keylen);
switch_rtp_get_random(key, SUITES[ctype].keysalt_len);
#ifdef SAME_KEY
}
#endif
switch_b64_encode(key, SUITES[ctype].keylen, b64_key, sizeof(b64_key));
switch_b64_encode(key, SUITES[ctype].keysalt_len, b64_key, sizeof(b64_key));
if (!switch_channel_var_true(channel, "rtp_pad_srtp_keys")) {
p = strrchr((char *) b64_key, '=');
@ -1197,7 +1189,12 @@ static switch_status_t switch_core_media_build_crypto(switch_media_handle_t *smh
if (index == SWITCH_NO_CRYPTO_TAG) index = ctype + 1;
engine->ssec[ctype].local_crypto_key = switch_core_session_sprintf(smh->session, "%d %s inline:%s", index, SUITES[ctype].name, b64_key);
if (switch_channel_get_variable(channel, "rtp_secure_media_mki")) {
engine->ssec[ctype].local_crypto_key = switch_core_session_sprintf(smh->session, "%d %s inline:%s|2^31|1:1", index, SUITES[ctype].name, b64_key);
} else {
engine->ssec[ctype].local_crypto_key = switch_core_session_sprintf(smh->session, "%d %s inline:%s", index, SUITES[ctype].name, b64_key);
}
switch_channel_set_variable_name_printf(smh->session->channel, engine->ssec[ctype].local_crypto_key, "rtp_last_%s_local_crypto_key", type2str(type));
switch_channel_set_flag(smh->session->channel, CF_SECURE);
@ -1216,17 +1213,180 @@ static switch_status_t switch_core_media_build_crypto(switch_media_handle_t *smh
}
#define CRYPTO_KEY_MATERIAL_LIFETIME_MKI_ERR 0x0u
#define CRYPTO_KEY_MATERIAL_MKI 0x1u
#define CRYPTO_KEY_MATERIAL_LIFETIME 0x2u
#define RAW_BITS_PER_64_ENCODED_CHAR 6
/* Return uint32_t which contains all the info about the field found:
* XXXXXXXa | YYYYYYYY | YYYYYYYY | ZZZZZZZZ
* where:
* a - CRYPTO_KEY_MATERIAL_LIFETIME if LIFETIME, CRYPTO_KEY_MATERIAL_MKI if MKI
* YYYYYYYY and ZZZZZZZZ - depend on 'a':
* if a is LIFETIME then YYYYYYYY is decimal Base, ZZZZZZZZ is decimal Exponent
* if a is MKI, then YYYYYYYY is decimal MKI_ID, ZZZZZZZZ is decimal MKI_SIZE
*/
static uint32_t parse_lifetime_mki(const char **p, const char *end)
{
const char *field_begin;
const char *field_end;
const char *sep, *space;
uint32_t res = 0;
switch_status_t switch_core_media_add_crypto(switch_secure_settings_t *ssec, const char *key_str, switch_rtp_crypto_direction_t direction)
uint32_t val = 0;
int i;
switch_assert(*p != NULL);
switch_assert(end != NULL);
field_begin = strchr(*p, '|');
if (field_begin && (field_begin + 1 < end)) {
space = strchr(field_begin, ' ');
field_end = strchr(field_begin + 2, '|');
if (!field_end) {
field_end = end;
}
if (space) {
if ((field_end == NULL) || (space < field_end)) {
field_end = space;
}
}
if (field_end) {
/* Closing '|' found. */
sep = strchr(field_begin, ':'); /* The lifetime field never includes a colon, whereas the third field always does. (RFC 4568) */
if (sep && (sep + 1 < field_end) && isdigit(*(sep + 1))) {
res |= (CRYPTO_KEY_MATERIAL_MKI << 24);
for (i = 1, *p = sep - 1; *p != field_begin; --(*p), i *= 10) {
val += ((**p) - '0') * i;
}
res |= ((val << 8) & 0x00ffff00); /* MKI_ID */
val = 0;
for (i = 1, *p = field_end - 1; *p != sep; --(*p), i *= 10) {
val += ((**p) - '0') * i;
}
res |= (val & 0x000000ff); /* MKI_SIZE */
} else if (isdigit(*(field_begin + 1)) && (field_begin + 2) && (*(field_begin + 2) == '^') && (field_begin + 3) && isdigit(*(field_begin + 3))) {
res |= (CRYPTO_KEY_MATERIAL_LIFETIME << 24);
val = ((uint32_t) (*(field_begin + 1) - '0')) << 8;
res |= val; /* LIFETIME base. */
val = 0;
for (i = 1, *p = field_end - 1; *p != (field_begin + 2); --(*p), i *= 10) {
val += ((**p) - '0') * i;
}
res |= (val & 0x000000ff); /* LIFETIME exponent. */
}
}
*p = field_end;
}
return res;
}
static switch_crypto_key_material_t* switch_core_media_crypto_append_key_material(
switch_core_session_t *session,
switch_crypto_key_material_t *tail,
switch_rtp_crypto_key_param_method_type_t method,
unsigned char raw_key[SWITCH_RTP_MAX_CRYPTO_LEN],
int raw_key_len,
const char *key_material,
int key_material_len,
uint64_t lifetime,
unsigned int mki_id,
unsigned int mki_size)
{
struct switch_crypto_key_material_s *new_key_material;
new_key_material = switch_core_session_alloc(session, (sizeof(*new_key_material)));
if (new_key_material == NULL) {
return NULL;
}
new_key_material->method = method;
memcpy(new_key_material->raw_key, raw_key, raw_key_len);
new_key_material->crypto_key = switch_core_session_strdup(session, key_material);
new_key_material->lifetime = lifetime;
new_key_material->mki_id = mki_id;
new_key_material->mki_size = mki_size;
new_key_material->next = tail;
return new_key_material;
}
/*
* Skip all space and return pointer to the '\0' terminator of the char string candidate to be a key-material
* or pointer to first ' ' in the candidate string.
*/
static const char* switch_core_media_crypto_find_key_material_candidate_end(const char *p)
{
const char *end;
switch_assert(p != NULL);
if (p) {
end = strchr(p, ' '); /* find the end of the continuous string of characters in the candidate string */
if (end == NULL)
end = p + strlen(p);
}
return end;
}
switch_status_t switch_core_media_add_crypto(switch_core_session_t *session, switch_secure_settings_t *ssec, switch_rtp_crypto_direction_t direction)
{
unsigned char key[SWITCH_RTP_MAX_CRYPTO_LEN];
switch_rtp_crypto_key_type_t type;
char *p;
const char *p, *delimit;
const char *key_material_begin;
const char *key_material_end; /* begin and end of the current key material candidate */
int method_len;
int keysalt_len;
const char *opts;
uint32_t opt_field; /* LIFETIME or MKI */
switch_rtp_crypto_key_param_method_type_t method = CRYPTO_KEY_PARAM_METHOD_INLINE;
uint64_t lifetime = 0;
uint16_t lifetime_base = 0;
uint16_t lifetime_exp = 0;
uint16_t mki_id = 0;
uint16_t mki_size = 0;
switch_crypto_key_material_t *key_material = NULL;
unsigned long *key_material_n = NULL;
bool multiple_keys = false;
const char *key_param;
p = strchr(key_str, ' ');
if (direction == SWITCH_RTP_CRYPTO_SEND || direction == SWITCH_RTP_CRYPTO_SEND_RTCP) {
key_param = ssec->local_crypto_key;
key_material = ssec->local_key_material_next;
key_material_n = &ssec->local_key_material_n;
} else {
key_param = ssec->remote_crypto_key;
key_material = ssec->remote_key_material_next;
key_material_n = &ssec->remote_key_material_n;
}
if (zstr(key_param)) {
goto no_crypto_found;
}
*key_material_n = 0;
p = strchr(key_param, ' ');
if (p && *p && *(p + 1)) {
p++;
@ -1234,36 +1394,192 @@ switch_status_t switch_core_media_add_crypto(switch_secure_settings_t *ssec, con
type = switch_core_media_crypto_str2type(p);
if (type == CRYPTO_INVALID) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error near [%s]\n", p);
goto bad;
goto bad_crypto;
}
p = strchr(p, ' ');
if (p && *p && *(p + 1)) {
p++;
if (strncasecmp(p, "inline:", 7)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error near [%s]\n", p);
goto bad;
}
p += 7;
switch_b64_decode(p, (char *) key, sizeof(key));
if (direction == SWITCH_RTP_CRYPTO_SEND) {
memcpy(ssec->local_raw_key, key, SUITES[type].keylen);
} else {
memcpy(ssec->remote_raw_key, key, SUITES[type].keylen);
}
return SWITCH_STATUS_SUCCESS;
p = strchr(p, ' '); /* skip the crypto suite description */
if (p == NULL) {
goto bad_crypto;
}
do {
if (*key_material_n == SWITCH_CRYPTO_MKI_MAX) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Skipping excess of MKIs due to max number of suppoerted MKIs %d exceeded\n", SWITCH_CRYPTO_MKI_MAX);
break;
}
p = switch_strip_spaces((char*) p, 0);
if (p) {
key_material_begin = p;
key_material_end = switch_core_media_crypto_find_key_material_candidate_end(p);
/* Parsing the key material candidate within [begin, end). */
if ((delimit = strchr(p, ':')) == NULL) {
goto bad_error_parsing_near;
}
method_len = delimit - p;
if (strncasecmp(p, CRYPTO_KEY_PARAM_METHOD[CRYPTO_KEY_PARAM_METHOD_INLINE], method_len)) {
goto bad_key_param_method;
}
method = CRYPTO_KEY_PARAM_METHOD_INLINE;
/* Valid key-material found. Save as default key in secure_settings_s. */
p = delimit + 1; /* skip ':' */
if (!(p && *p && *(p + 1))) {
goto bad_keysalt;
}
/* Check if '|' is present in currently considered key-material. */
if ((opts = strchr(p, '|')) && (opts < key_material_end)) {
keysalt_len = opts - p;
} else {
keysalt_len = key_material_end - p;
}
if (keysalt_len > sizeof(key)) {
goto bad_keysalt_len;
}
switch_b64_decode(p, (char *) key, keysalt_len);
if (!multiple_keys) { /* First key becomes default (used in case no MKI is found). */
if (direction == SWITCH_RTP_CRYPTO_SEND) {
memcpy(ssec->local_raw_key, key, SUITES[type].keysalt_len);
} else {
memcpy(ssec->remote_raw_key, key, SUITES[type].keysalt_len);
}
multiple_keys = true;
}
p += keysalt_len;
if (p < key_material_end) { /* Parse LIFETIME or MKI. */
if (opts) { /* if opts != NULL then opts points to first '|' in current key-material cadidate */
lifetime = 0;
mki_id = 0;
mki_size = 0;
for (int i = 0; i < 2 && (*opts == '|'); ++i) {
opt_field = parse_lifetime_mki(&opts, key_material_end);
switch ((opt_field >> 24) & 0x3) {
case CRYPTO_KEY_MATERIAL_LIFETIME:
lifetime_base = ((opt_field & 0x00ffff00) >> 8) & 0xffff;
lifetime_exp = (opt_field & 0x000000ff) & 0xffff;
lifetime = pow(lifetime_base, lifetime_exp);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "LIFETIME found in %s, base %u exp %u\n", p, lifetime_base, lifetime_exp);
break;
case CRYPTO_KEY_MATERIAL_MKI:
mki_id = ((opt_field & 0x00ffff00) >> 8) & 0xffff;
mki_size = (opt_field & 0x000000ff) & 0xffff;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MKI found in %s, id %u size %u\n", p, mki_id, mki_size);
break;
default:
goto bad_key_lifetime_or_mki;
}
}
if (mki_id == 0 && lifetime == 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Bad MKI found in %s, (parsed as: id %u size %u lifetime base %u exp %u\n", p, mki_id, mki_size, lifetime_base, lifetime_exp);
return SWITCH_STATUS_FALSE;
} else if (mki_id == 0 || lifetime == 0) {
if (mki_id == 0) {
if (key_material)
goto bad_key_no_mki_index;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Skipping MKI due to empty index\n");
} else {
if (mki_size == 0)
goto bad_key_no_mki_size;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Skipping MKI due to empty lifetime\n");
}
continue;
}
}
if (key_material) {
if (mki_id == 0) {
goto bad_key_no_mki_index;
}
if (mki_size != key_material->mki_size) {
goto bad_key_mki_size;
}
}
key_material = switch_core_media_crypto_append_key_material(session, key_material, method, (unsigned char*) key,
SUITES[type].keysalt_len, (char*) key_material_begin, key_material_end - key_material_begin, lifetime, mki_id, mki_size);
*key_material_n = *key_material_n + 1;
}
}
} while ((p = switch_strip_spaces((char*) key_material_end, 0)) && (*p != '\0'));
if (direction == SWITCH_RTP_CRYPTO_SEND || direction == SWITCH_RTP_CRYPTO_SEND_RTCP) {
ssec->local_key_material_next = key_material;
} else {
ssec->remote_key_material_next = key_material;
}
return SWITCH_STATUS_SUCCESS;
}
bad:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n");
no_crypto_found:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error! No crypto to parse\n");
return SWITCH_STATUS_FALSE;
bad_error_parsing_near:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! Parsing near %s\n", p);
return SWITCH_STATUS_FALSE;
bad_crypto:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! SRTP: Invalid format of crypto attribute %s\n", key_param);
return SWITCH_STATUS_FALSE;
bad_keysalt:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! SRTP: Invalid keysalt in the crypto attribute %s\n", key_param);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! Parsing near %s\n", p);
return SWITCH_STATUS_FALSE;
bad_keysalt_len:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! SRTP: Invalid keysalt length in the crypto attribute %s\n", key_param);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! Parsing near %s\n", p);
return SWITCH_STATUS_FALSE;
bad_key_lifetime_or_mki:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! SRTP: Invalid key param MKI or LIFETIME in the crypto attribute %s\n", key_param);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! Parsing near %s\n", p);
return SWITCH_STATUS_FALSE;
bad_key_no_mki_index:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Crypto invalid: multiple keys in a single crypto MUST all have MKI indices, %s\n", key_param);
return SWITCH_STATUS_FALSE;
bad_key_no_mki_size:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Crypto invalid: MKI index with no MKI size in %s\n", key_param);
return SWITCH_STATUS_FALSE;
bad_key_mki_size:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Crypto invalid: MKI sizes differ in %s\n", key_param);
return SWITCH_STATUS_FALSE;
bad_key_param_method:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! SRTP: Invalid key param method type in %s\n", key_param);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error! Parsing near %s\n", p);
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(void) switch_core_media_set_rtp_session(switch_core_session_t *session, switch_media_type_t type, switch_rtp_t *rtp_session)
@ -1342,19 +1658,16 @@ static void switch_core_session_apply_crypto(switch_core_session_t *session, swi
}
if (engine->ssec[engine->crypto_type].remote_crypto_key && switch_channel_test_flag(session->channel, CF_SECURE)) {
switch_core_media_add_crypto(&engine->ssec[engine->crypto_type], engine->ssec[engine->crypto_type].remote_crypto_key, SWITCH_RTP_CRYPTO_RECV);
if (switch_channel_get_variable(session->channel, "rtp_secure_media_mki"))
switch_core_media_add_crypto(session, &engine->ssec[engine->crypto_type], SWITCH_RTP_CRYPTO_SEND);
switch_core_media_add_crypto(session, &engine->ssec[engine->crypto_type], SWITCH_RTP_CRYPTO_RECV);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_SEND, 1,
engine->ssec[engine->crypto_type].crypto_type,
engine->ssec[engine->crypto_type].local_raw_key,
SUITES[engine->ssec[engine->crypto_type].crypto_type].keylen);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_SEND, 1, &engine->ssec[engine->crypto_type]);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_RECV,
engine->ssec[engine->crypto_type].crypto_tag,
engine->ssec[engine->crypto_type].crypto_type,
engine->ssec[engine->crypto_type].remote_raw_key,
SUITES[engine->ssec[engine->crypto_type].crypto_type].keylen);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_RECV, engine->ssec[engine->crypto_type].crypto_tag, &engine->ssec[engine->crypto_type]);
switch_channel_set_variable(session->channel, varname, "true");
@ -1476,7 +1789,7 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio
for (i = 0; smh->crypto_suite_order[i] != CRYPTO_INVALID; i++) {
switch_rtp_crypto_key_type_t j = SUITES[smh->crypto_suite_order[i]].type;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,"looking for crypto suite [%s] in [%s]\n", SUITES[j].name, crypto);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "looking for crypto suite [%s] in [%s]\n", SUITES[j].name, crypto);
if (switch_stristr(SUITES[j].name, crypto)) {
ctype = SUITES[j].type;
@ -1504,8 +1817,7 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio
switch_channel_set_variable(session->channel, varname, vval);
switch_core_media_build_crypto(session->media_handle, type, crypto_tag, ctype, SWITCH_RTP_CRYPTO_SEND, 1);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_SEND, atoi(crypto), engine->ssec[engine->crypto_type].crypto_type,
engine->ssec[engine->crypto_type].local_raw_key, SUITES[ctype].keylen);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_SEND, atoi(crypto), &engine->ssec[engine->crypto_type]);
}
if (a && b && !strncasecmp(a, b, 23)) {
@ -1532,9 +1844,8 @@ SWITCH_DECLARE(int) switch_core_session_check_incoming_crypto(switch_core_sessio
if (switch_rtp_ready(engine->rtp_session) && switch_channel_test_flag(session->channel, CF_SECURE)) {
switch_core_media_add_crypto(&engine->ssec[engine->crypto_type], engine->ssec[engine->crypto_type].remote_crypto_key, SWITCH_RTP_CRYPTO_RECV);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_RECV, engine->ssec[engine->crypto_type].crypto_tag,
engine->ssec[engine->crypto_type].crypto_type, engine->ssec[engine->crypto_type].remote_raw_key, SUITES[engine->ssec[engine->crypto_type].crypto_type].keylen);
switch_core_media_add_crypto(session, &engine->ssec[engine->crypto_type], SWITCH_RTP_CRYPTO_RECV);
switch_rtp_add_crypto_key(engine->rtp_session, SWITCH_RTP_CRYPTO_RECV, engine->ssec[engine->crypto_type].crypto_tag, &engine->ssec[engine->crypto_type]);
}
got_crypto++;
@ -13415,20 +13726,13 @@ SWITCH_DECLARE (void) switch_core_media_recover_session(switch_core_session_t *s
int idx = atoi(tmp);
a_engine->ssec[a_engine->crypto_type].local_crypto_key = switch_core_session_strdup(session, tmp);
switch_core_media_add_crypto(&a_engine->ssec[a_engine->crypto_type], a_engine->ssec[a_engine->crypto_type].local_crypto_key, SWITCH_RTP_CRYPTO_SEND);
switch_core_media_add_crypto(&a_engine->ssec[a_engine->crypto_type], a_engine->ssec[a_engine->crypto_type].remote_crypto_key, SWITCH_RTP_CRYPTO_RECV);
switch_core_media_add_crypto(session, &a_engine->ssec[a_engine->crypto_type],SWITCH_RTP_CRYPTO_SEND);
switch_core_media_add_crypto(session, &a_engine->ssec[a_engine->crypto_type],SWITCH_RTP_CRYPTO_RECV);
switch_channel_set_flag(smh->session->channel, CF_SECURE);
switch_rtp_add_crypto_key(a_engine->rtp_session, SWITCH_RTP_CRYPTO_SEND, idx,
a_engine->crypto_type,
a_engine->ssec[a_engine->crypto_type].local_raw_key,
SUITES[a_engine->crypto_type].keylen);
switch_rtp_add_crypto_key(a_engine->rtp_session, SWITCH_RTP_CRYPTO_SEND, idx, &a_engine->ssec[a_engine->crypto_type]);
switch_rtp_add_crypto_key(a_engine->rtp_session, SWITCH_RTP_CRYPTO_RECV,
a_engine->ssec[a_engine->crypto_type].crypto_tag,
a_engine->crypto_type,
a_engine->ssec[a_engine->crypto_type].remote_raw_key,
SUITES[a_engine->crypto_type].keylen);
switch_rtp_add_crypto_key(a_engine->rtp_session, SWITCH_RTP_CRYPTO_RECV, a_engine->ssec[a_engine->crypto_type].crypto_tag, &a_engine->ssec[a_engine->crypto_type]);
}

View File

@ -2320,8 +2320,14 @@ static int check_rtcp_and_ice(switch_rtp_t *rtp_session)
#ifdef ENABLE_SRTP
if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND]) {
int stat = 0;
int sbytes = (int) rtcp_bytes;
int stat = srtp_protect_rtcp(rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_send_msg.header, &sbytes);
if (!rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND_MKI]) {
stat = srtp_protect_rtcp(rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_send_msg.header, &sbytes);
} else {
stat = srtp_protect_rtcp_mki(rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_send_msg.header, &sbytes, 1, SWITCH_CRYPTO_MKI_INDEX);
}
if (stat) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error: SRTP RTCP protection failed with code %d\n", stat);
@ -3067,18 +3073,27 @@ static const char *dtls_state_names(dtls_state_t s)
return dtls_state_names_t[s];
}
#define dtls_set_state(_dtls, _state) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Changing %s DTLS state from %s to %s\n", rtp_type(rtp_session), dtls_state_names(_dtls->state), dtls_state_names(_state)); _dtls->new_state = 1; _dtls->last_state = _dtls->state; _dtls->state = _state
#define cr_keylen 16
#define cr_saltlen 14
#define cr_kslen 30
static int dtls_state_setup(switch_rtp_t *rtp_session, switch_dtls_t *dtls)
{
X509 *cert;
switch_secure_settings_t ssec; /* Used just to wrap over params in a call to switch_rtp_add_crypto_key. */
int r = 0;
const switch_size_t cr_kslen = SUITES[AES_CM_128_HMAC_SHA1_80].keysalt_len;
const switch_size_t cr_saltlen = SUITES[AES_CM_128_HMAC_SHA1_80].salt_len;
const switch_size_t cr_keylen = cr_kslen - cr_saltlen;
uint8_t raw_key_data[cr_kslen * 2];
unsigned char local_key_buf[cr_kslen];
unsigned char remote_key_buf[cr_kslen];
memset(&ssec, 0, sizeof(ssec));
memset(&raw_key_data, 0, cr_kslen * 2 * sizeof(uint8_t));
memset(&local_key_buf, 0, cr_kslen * sizeof(unsigned char));
memset(&remote_key_buf, 0, cr_kslen * sizeof(unsigned char));
if ((dtls->type & DTLS_TYPE_SERVER)) {
r = 1;
} else if ((cert = SSL_get_peer_certificate(dtls->ssl))) {
@ -3092,9 +3107,7 @@ static int dtls_state_setup(switch_rtp_t *rtp_session, switch_dtls_t *dtls)
dtls_set_state(dtls, DS_FAIL);
return -1;
} else {
uint8_t raw_key_data[cr_kslen*2] = { 0 };
unsigned char *local_key, *remote_key, *local_salt, *remote_salt;
unsigned char local_key_buf[cr_kslen] = {0}, remote_key_buf[cr_kslen] = {0};
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "%s Fingerprint Verified.\n", rtp_type(rtp_session));
@ -3121,18 +3134,20 @@ static int dtls_state_setup(switch_rtp_t *rtp_session, switch_dtls_t *dtls)
local_salt = remote_salt + cr_saltlen;
}
memcpy(local_key_buf, local_key, cr_keylen);
memcpy(local_key_buf + cr_keylen, local_salt, cr_saltlen);
memcpy(&ssec.local_raw_key, local_key, cr_keylen);
memcpy(&ssec.local_raw_key + cr_keylen, local_salt, cr_saltlen);
memcpy(remote_key_buf, remote_key, cr_keylen);
memcpy(remote_key_buf + cr_keylen, remote_salt, cr_saltlen);
memcpy(&ssec.remote_raw_key, remote_key, cr_keylen);
memcpy(&ssec.remote_raw_key + cr_keylen, remote_salt, cr_saltlen);
ssec.crypto_type = AES_CM_128_HMAC_SHA1_80;
if (dtls == rtp_session->rtcp_dtls && rtp_session->rtcp_dtls != rtp_session->dtls) {
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_SEND_RTCP, 0, AES_CM_128_HMAC_SHA1_80, local_key_buf, cr_kslen);
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_RECV_RTCP, 0, AES_CM_128_HMAC_SHA1_80, remote_key_buf, cr_kslen);
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_SEND_RTCP, 0, &ssec);
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_RECV_RTCP, 0, &ssec);
} else {
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_SEND, 0, AES_CM_128_HMAC_SHA1_80, local_key_buf, cr_kslen);
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_RECV, 0, AES_CM_128_HMAC_SHA1_80, remote_key_buf, cr_kslen);
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_SEND, 0, &ssec);
switch_rtp_add_crypto_key(rtp_session, SWITCH_RTP_CRYPTO_RECV, 0, &ssec);
}
}
@ -3814,15 +3829,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_dtls(switch_rtp_t *rtp_session, d
}
SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session,
switch_rtp_crypto_direction_t direction,
uint32_t index, switch_rtp_crypto_key_type_t type, unsigned char *key, switch_size_t keylen)
SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session, switch_rtp_crypto_direction_t direction, uint32_t index, switch_secure_settings_t *ssec)
{
#ifndef ENABLE_SRTP
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "SRTP NOT SUPPORTED IN THIS BUILD!\n");
return SWITCH_STATUS_FALSE;
#else
switch_rtp_crypto_key_t *crypto_key;
srtp_policy_t *policy;
srtp_err_status_t stat;
@ -3833,12 +3846,43 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
int idx = 0;
const char *var;
unsigned char b64_key[512] = "";
unsigned char *keysalt = NULL;
switch_size_t keysalt_len = 0;
switch_crypto_key_material_t *key_material = NULL;
unsigned long *key_material_n = NULL;
srtp_master_key_t **mkis = NULL;
srtp_master_key_t *mki = NULL;
int mki_idx = 0;
if (direction >= SWITCH_RTP_CRYPTO_MAX || keylen > SWITCH_RTP_MAX_CRYPTO_LEN) {
keysalt_len = SUITES[ssec->crypto_type].keysalt_len;
if (direction >= SWITCH_RTP_CRYPTO_MAX || keysalt_len > SWITCH_RTP_MAX_CRYPTO_LEN) {
return SWITCH_STATUS_FALSE;
}
switch_b64_encode(key, keylen, b64_key, sizeof(b64_key));
if (direction == SWITCH_RTP_CRYPTO_RECV_RTCP) {
direction = SWITCH_RTP_CRYPTO_RECV;
rtp_session->srtp_idx_rtcp = idx = 1;
} else if (direction == SWITCH_RTP_CRYPTO_SEND_RTCP) {
direction = SWITCH_RTP_CRYPTO_SEND;
rtp_session->srtp_idx_rtcp = idx = 1;
}
if (direction == SWITCH_RTP_CRYPTO_RECV) {
policy = &rtp_session->recv_policy[idx];
keysalt = ssec->remote_raw_key;
key_material = ssec->remote_key_material_next;
key_material_n = &ssec->remote_key_material_n;
} else {
policy = &rtp_session->send_policy[idx];
keysalt = ssec->local_raw_key;
key_material = ssec->local_key_material_next;
key_material_n = &ssec->local_key_material_n;
}
switch_b64_encode(keysalt, keysalt_len, b64_key, sizeof(b64_key));
if (switch_true(switch_core_get_variable("rtp_retain_crypto_keys"))) {
switch(direction) {
@ -3861,23 +3905,9 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
crypto_key = switch_core_alloc(rtp_session->pool, sizeof(*crypto_key));
if (direction == SWITCH_RTP_CRYPTO_RECV_RTCP) {
direction = SWITCH_RTP_CRYPTO_RECV;
rtp_session->srtp_idx_rtcp = idx = 1;
} else if (direction == SWITCH_RTP_CRYPTO_SEND_RTCP) {
direction = SWITCH_RTP_CRYPTO_SEND;
rtp_session->srtp_idx_rtcp = idx = 1;
}
if (direction == SWITCH_RTP_CRYPTO_RECV) {
policy = &rtp_session->recv_policy[idx];
} else {
policy = &rtp_session->send_policy[idx];
}
crypto_key->type = type;
crypto_key->type = ssec->crypto_type;
crypto_key->index = index;
memcpy(crypto_key->key, key, keylen);
memcpy(crypto_key->keysalt, keysalt, keysalt_len);
crypto_key->next = rtp_session->crypto_keys[direction];
rtp_session->crypto_keys[direction] = crypto_key;
@ -3947,7 +3977,68 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
break;
}
policy->key = (uint8_t *) crypto_key->key;
/* Setup the policy with MKI if they are used. Number of key materials must be positive to use MKI. */
if (key_material && (*key_material_n > 0)) {
if (direction == SWITCH_RTP_CRYPTO_RECV) {
rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV_MKI] = 1;
} else {
rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND_MKI] = 1;
}
/* key must be NULL for libsrtp to work correctly with MKI. */
policy->key = NULL;
/* Allocate array for MKIs. */
mkis = switch_core_alloc(rtp_session->pool, *key_material_n * sizeof(srtp_master_key_t*));
if (!mkis) {
return SWITCH_STATUS_FALSE;
}
/* Build array of MKIs. */
mki_idx = 0;
while (key_material && (mki_idx < *key_material_n)) {
if (key_material->mki_size < 1) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "MKI bad key size at MKI %d\n", mki_idx);
return SWITCH_STATUS_FALSE;
}
mki = switch_core_alloc(rtp_session->pool, sizeof(srtp_master_key_t));
if (!mki) {
return SWITCH_STATUS_FALSE;
}
/* Setup MKI. */
mki->mki_id = switch_core_alloc(rtp_session->pool, sizeof(key_material->mki_size));
if (!mki->mki_id) {
return SWITCH_STATUS_FALSE;
}
mki->key = switch_core_alloc(rtp_session->pool, keysalt_len);
if (!mki->key) {
return SWITCH_STATUS_FALSE;
}
memcpy(mki->mki_id, &key_material->mki_id, key_material->mki_size);
mki->mki_size = key_material->mki_size;
memcpy(mki->key, key_material->raw_key, keysalt_len);
mkis[mki_idx] = mki;
key_material = key_material->next;
++mki_idx;
}
/* And pass the array of MKIs to libsrtp. */
policy->keys = mkis;
policy->num_master_keys = mki_idx;
} else {
policy->key = (uint8_t *) crypto_key->keysalt;
}
policy->next = NULL;
policy->window_size = 1024;
@ -3968,8 +4059,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
}
if (status == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Activating %s Secure %s RECV\n",
rtp_type(rtp_session), idx ? "RTCP" : "RTP");
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Activating %s Secure %s RECV%s\n",
rtp_type(rtp_session), idx ? "RTCP" : "RTP", rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV_MKI] ? " (with MKI)" : "");
rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV] = 1;
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error allocating srtp [%d]\n", stat);
@ -3990,8 +4081,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_sess
}
if (status == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Activating %s Secure %s SEND\n",
rtp_type(rtp_session), idx ? "RTCP" : "RTP");
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO, "Activating %s Secure %s SEND%s\n",
rtp_type(rtp_session), idx ? "RTCP" : "RTP", rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND_MKI] ? " (with MKI)" : "");
rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND] = 1;
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error allocating SRTP [%d]\n", stat);
@ -5779,8 +5870,13 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
int sbytes = (int) *bytes;
srtp_err_status_t stat = 0;
if (!rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV_MKI]) {
stat = srtp_unprotect_rtcp(rtp_session->recv_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_recv_msg_p->header, &sbytes);
} else {
stat = srtp_unprotect_rtcp_mki(rtp_session->recv_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_recv_msg_p->header, &sbytes, 1);
}
if ((stat = srtp_unprotect_rtcp(rtp_session->recv_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_recv_msg_p->header, &sbytes))) {
if (stat) {
//++rtp_session->srtp_errs[rtp_session->srtp_idx_rtp]++;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "RTCP UNPROTECT ERR\n");
} else {
@ -5974,7 +6070,14 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
}
if (!(*flags & SFF_PLC) && rtp_session->recv_ctx[rtp_session->srtp_idx_rtp]) {
stat = srtp_unprotect(rtp_session->recv_ctx[rtp_session->srtp_idx_rtp], &rtp_session->recv_msg.header, &sbytes);
stat = 0;
if (!rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV_MKI]) {
stat = srtp_unprotect(rtp_session->recv_ctx[rtp_session->srtp_idx_rtp], &rtp_session->recv_msg.header, &sbytes);
} else {
stat = srtp_unprotect_mki(rtp_session->recv_ctx[rtp_session->srtp_idx_rtp], &rtp_session->recv_msg.header, &sbytes, 1);
}
if (rtp_session->flags[SWITCH_RTP_FLAG_NACK] && stat == srtp_err_status_replay_fail) {
/* false alarm nack */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "REPLAY ERR, FALSE NACK\n");
@ -6742,7 +6845,13 @@ static switch_status_t read_rtcp_packet(switch_rtp_t *rtp_session, switch_size_t
srtp_err_status_t stat = 0;
if ((stat = srtp_unprotect_rtcp(rtp_session->recv_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_recv_msg_p->header, &sbytes))) {
if (!rtp_session->flags[SWITCH_RTP_FLAG_SECURE_RECV_MKI]) {
stat = srtp_unprotect_rtcp(rtp_session->recv_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_recv_msg_p->header, &sbytes);
} else {
stat = srtp_unprotect_rtcp_mki(rtp_session->recv_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_recv_msg_p->header, &sbytes, 1);
}
if (stat) {
//++rtp_session->srtp_errs[rtp_session->srtp_idx_rtp]++;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "RTCP UNPROTECT ERR\n");
} else {
@ -7191,8 +7300,15 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
#ifdef ENABLE_SRTP
if (switch_rtp_test_flag(other_rtp_session, SWITCH_RTP_FLAG_SECURE_SEND)) {
int stat = 0;
int sbytes = (int) rtcp_bytes;
int stat = srtp_protect_rtcp(other_rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &other_rtp_session->rtcp_send_msg.header, &sbytes);
if (!other_rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND_MKI]) {
stat = srtp_protect_rtcp(other_rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &other_rtp_session->rtcp_send_msg.header, &sbytes);
} else {
stat = srtp_protect_rtcp_mki(other_rtp_session->send_ctx[other_rtp_session->srtp_idx_rtcp], &other_rtp_session->rtcp_send_msg.header, &sbytes, 1, SWITCH_CRYPTO_MKI_INDEX);
}
if (stat) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error: SRTP RTCP protection failed with code %d\n", stat);
}
@ -8234,8 +8350,11 @@ static int rtp_common_write(switch_rtp_t *rtp_session,
}
}
stat = srtp_protect(rtp_session->send_ctx[rtp_session->srtp_idx_rtp], &send_msg->header, &sbytes);
if (!rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND_MKI]) {
stat = srtp_protect(rtp_session->send_ctx[rtp_session->srtp_idx_rtp], &send_msg->header, &sbytes);
} else {
stat = srtp_protect_mki(rtp_session->send_ctx[rtp_session->srtp_idx_rtp], &send_msg->header, &sbytes, 1, SWITCH_CRYPTO_MKI_INDEX);
}
if (stat) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR,
@ -8820,7 +8939,12 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_write_raw(switch_rtp_t *rtp_session,
}
}
stat = srtp_protect(rtp_session->send_ctx[rtp_session->srtp_idx_rtp], &rtp_session->write_msg.header, &sbytes);
if (!rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND_MKI]) {
stat = srtp_protect(rtp_session->send_ctx[rtp_session->srtp_idx_rtp], &rtp_session->write_msg.header, &sbytes);
} else {
stat = srtp_protect_mki(rtp_session->send_ctx[rtp_session->srtp_idx_rtp], &rtp_session->write_msg.header, &sbytes, 1, SWITCH_CRYPTO_MKI_INDEX);
}
if (stat) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error: SRTP protection failed with code %d\n", stat);
}

View File

@ -955,12 +955,13 @@ SWITCH_DECLARE(switch_status_t) switch_b64_encode(unsigned char *in, switch_size
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_size_t) switch_b64_decode(char *in, char *out, switch_size_t olen)
SWITCH_DECLARE(switch_size_t) switch_b64_decode(const char *in, char *out, switch_size_t olen)
{
char l64[256];
int b = 0, c, l = 0, i;
char *ip, *op = out;
const char *ip;
char *op = out;
size_t ol = 0;
for (i = 0; i < 256; i++) {