gstreamer/subprojects/gst-plugins-base/gst-libs/gst/sdp/gstmikey.c

2537 lines
73 KiB
C

/* GStreamer
* Copyright (C) <2014> Wim Taymans <wim.taymans@gmail.com>
*
* gstmikey.h: various helper functions to manipulate mikey messages
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstmikey
* @title: GstMIKEYMessage
* @short_description: Helper methods for dealing with MIKEY messages
*
* The GstMIKEY helper functions makes it easy to parse and create MIKEY
* messages.
*
* Since: 1.4
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "gstmikey.h"
GST_DEFINE_MINI_OBJECT_TYPE (GstMIKEYPayload, gst_mikey_payload);
GST_DEFINE_MINI_OBJECT_TYPE (GstMIKEYMessage, gst_mikey_message);
static void
payload_destroy (GstMIKEYPayload ** payload)
{
gst_mikey_payload_unref (*payload);
}
#define INIT_ARRAY(field, type, init_func) \
G_STMT_START { \
if (field) \
g_array_set_size ((field), 0); \
else { \
(field) = g_array_new (FALSE, TRUE, sizeof (type)); \
g_array_set_clear_func ((field), (GDestroyNotify)init_func); \
} \
} G_STMT_END
#define FREE_ARRAY(field) \
G_STMT_START { \
if (field) \
g_array_free ((field), TRUE); \
(field) = NULL; \
} G_STMT_END
#define INIT_MEMDUP(field, data, len) \
G_STMT_START { \
g_free ((field)); \
(field) = g_memdup2 (data, len); \
} G_STMT_END
#define FREE_MEMDUP(field) \
G_STMT_START { \
g_free ((field)); \
(field) = NULL; \
} G_STMT_END
/* Key data transport payload (KEMAC) */
static guint
get_mac_len (GstMIKEYMacAlg mac_alg)
{
guint len;
switch (mac_alg) {
case GST_MIKEY_MAC_NULL:
len = 0; /* no MAC key */
break;
case GST_MIKEY_MAC_HMAC_SHA_1_160:
len = 20; /* 160 bits key */
break;
default:
len = -1;
break;
}
return len;
}
/**
* gst_mikey_payload_kemac_set:
* @payload: a #GstMIKEYPayload
* @enc_alg: the #GstMIKEYEncAlg
* @mac_alg: a #GstMIKEYMacAlg
*
* Set the KEMAC parameters. @payload should point to a %GST_MIKEY_PT_KEMAC
* payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_kemac_set (GstMIKEYPayload * payload,
GstMIKEYEncAlg enc_alg, GstMIKEYMacAlg mac_alg)
{
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEMAC, FALSE);
p->enc_alg = enc_alg;
p->mac_alg = mac_alg;
INIT_ARRAY (p->subpayloads, GstMIKEYPayload *, payload_destroy);
return TRUE;
}
static gboolean
gst_mikey_payload_kemac_dispose (GstMIKEYPayloadKEMAC * payload)
{
FREE_ARRAY (payload->subpayloads);
return TRUE;
}
static GstMIKEYPayloadKEMAC *
gst_mikey_payload_kemac_copy (const GstMIKEYPayloadKEMAC * payload)
{
guint i, len;
GstMIKEYPayloadKEMAC *copy = g_slice_dup (GstMIKEYPayloadKEMAC, payload);
gst_mikey_payload_kemac_set (&copy->pt, payload->enc_alg, payload->mac_alg);
len = payload->subpayloads->len;
for (i = 0; i < len; i++) {
GstMIKEYPayload *pay =
g_array_index (payload->subpayloads, GstMIKEYPayload *, i);
gst_mikey_payload_kemac_add_sub (&copy->pt, gst_mikey_payload_copy (pay));
}
return copy;
}
/**
* gst_mikey_payload_kemac_get_n_sub:
* @payload: a #GstMIKEYPayload
*
* Get the number of sub payloads of @payload. @payload should be of type
* %GST_MIKEY_PT_KEMAC.
*
* Returns: the number of sub payloads in @payload
*
* Since: 1.4
*/
guint
gst_mikey_payload_kemac_get_n_sub (const GstMIKEYPayload * payload)
{
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
g_return_val_if_fail (payload != NULL, 0);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEMAC, 0);
return p->subpayloads->len;
}
/**
* gst_mikey_payload_kemac_get_sub:
* @payload: a #GstMIKEYPayload
* @idx: an index
*
* Get the sub payload of @payload at @idx. @payload should be of type
* %GST_MIKEY_PT_KEMAC.
*
* Returns: (transfer none) (nullable): the #GstMIKEYPayload at @idx.
*
* Since: 1.4
*/
const GstMIKEYPayload *
gst_mikey_payload_kemac_get_sub (const GstMIKEYPayload * payload, guint idx)
{
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
g_return_val_if_fail (payload != NULL, 0);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEMAC, 0);
if (p->subpayloads->len <= idx)
return NULL;
return g_array_index (p->subpayloads, GstMIKEYPayload *, idx);
}
/**
* gst_mikey_payload_kemac_remove_sub:
* @payload: a #GstMIKEYPayload
* @idx: the index to remove
*
* Remove the sub payload at @idx in @payload.
*
* Returns: %TRUE on success.
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_kemac_remove_sub (GstMIKEYPayload * payload, guint idx)
{
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
g_return_val_if_fail (payload != NULL, 0);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEMAC, 0);
g_return_val_if_fail (p->subpayloads->len > idx, FALSE);
g_array_remove_index (p->subpayloads, idx);
return TRUE;
}
/**
* gst_mikey_payload_kemac_add_sub:
* @payload: a #GstMIKEYPayload
* @newpay: (transfer full): a #GstMIKEYPayload to add
*
* Add a new sub payload to @payload.
*
* Returns: %TRUE on success.
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_kemac_add_sub (GstMIKEYPayload * payload,
GstMIKEYPayload * newpay)
{
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
g_return_val_if_fail (payload != NULL, 0);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEMAC, 0);
g_array_append_val (p->subpayloads, newpay);
return TRUE;
}
/* Envelope data payload (PKE) */
/**
* gst_mikey_payload_pke_set:
* @payload: a #GstMIKEYPayload
* @C: envelope key cache indicator
* @data_len: the length of @data
* @data: (array length=data_len): the encrypted envelope key
*
* Set the PKE values in @payload. @payload must be of type
* %GST_MIKEY_PT_PKE.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_pke_set (GstMIKEYPayload * payload, GstMIKEYCacheType C,
guint16 data_len, const guint8 * data)
{
GstMIKEYPayloadPKE *p = (GstMIKEYPayloadPKE *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_PKE, FALSE);
p->C = C;
p->data_len = data_len;
INIT_MEMDUP (p->data, data, data_len);
return TRUE;
}
static gboolean
gst_mikey_payload_pke_dispose (GstMIKEYPayloadPKE * payload)
{
FREE_MEMDUP (payload->data);
return TRUE;
}
static GstMIKEYPayloadPKE *
gst_mikey_payload_pke_copy (const GstMIKEYPayloadPKE * payload)
{
GstMIKEYPayloadPKE *copy = g_slice_dup (GstMIKEYPayloadPKE, payload);
gst_mikey_payload_pke_set (&copy->pt, payload->C, payload->data_len,
payload->data);
return copy;
}
/* DH data payload (DH) */
/* Signature payload (SIGN) */
/* Timestamp payload (T) */
static guint
get_ts_len (GstMIKEYTSType type)
{
guint len;
switch (type) {
case GST_MIKEY_TS_TYPE_NTP_UTC:
case GST_MIKEY_TS_TYPE_NTP:
len = 8;
break;
case GST_MIKEY_TS_TYPE_COUNTER:
len = 4;
break;
default:
len = -1;
break;
}
return len;
}
/**
* gst_mikey_payload_t_set:
* @payload: a #GstMIKEYPayload
* @type: the #GstMIKEYTSType
* @ts_value: (array): the timestamp value
*
* Set the timestamp in a %GST_MIKEY_PT_T @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_t_set (GstMIKEYPayload * payload,
GstMIKEYTSType type, const guint8 * ts_value)
{
GstMIKEYPayloadT *p = (GstMIKEYPayloadT *) payload;
guint ts_len;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_T, FALSE);
if ((ts_len = get_ts_len (type)) == -1)
return FALSE;
p->type = type;
INIT_MEMDUP (p->ts_value, ts_value, ts_len);
return TRUE;
}
static gboolean
gst_mikey_payload_t_dispose (GstMIKEYPayloadT * payload)
{
FREE_MEMDUP (payload->ts_value);
return TRUE;
}
static GstMIKEYPayloadT *
gst_mikey_payload_t_copy (const GstMIKEYPayloadT * payload)
{
GstMIKEYPayloadT *copy = g_slice_dup (GstMIKEYPayloadT, payload);
gst_mikey_payload_t_set (&copy->pt, payload->type, payload->ts_value);
return copy;
}
/* ID payload (ID) */
/* Certificate Payload (CERT) */
/* Cert hash payload (CHASH)*/
/* Ver msg payload (V) */
/* Security Policy payload (SP)*/
static void
param_clear (GstMIKEYPayloadSPParam * param)
{
FREE_MEMDUP (param->val);
}
/**
* gst_mikey_payload_sp_set:
* @payload: a #GstMIKEYPayload
* @policy: the policy number
* @proto: a #GstMIKEYSecProto
*
* Set the Security Policy parameters for @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_sp_set (GstMIKEYPayload * payload,
guint policy, GstMIKEYSecProto proto)
{
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_SP, FALSE);
p->policy = policy;
p->proto = proto;
INIT_ARRAY (p->params, GstMIKEYPayloadSPParam, param_clear);
return TRUE;
}
static gboolean
gst_mikey_payload_sp_dispose (GstMIKEYPayloadSP * payload)
{
FREE_ARRAY (payload->params);
return TRUE;
}
static GstMIKEYPayloadSP *
gst_mikey_payload_sp_copy (const GstMIKEYPayloadSP * payload)
{
guint i, len;
GstMIKEYPayloadSP *copy = g_slice_dup (GstMIKEYPayloadSP, payload);
gst_mikey_payload_sp_set (&copy->pt, payload->policy, payload->proto);
len = payload->params->len;
for (i = 0; i < len; i++) {
GstMIKEYPayloadSPParam *param = &g_array_index (payload->params,
GstMIKEYPayloadSPParam, i);
gst_mikey_payload_sp_add_param (&copy->pt, param->type, param->len,
param->val);
}
return copy;
}
/**
* gst_mikey_payload_sp_get_n_params:
* @payload: a #GstMIKEYPayload
*
* Get the number of security policy parameters in a %GST_MIKEY_PT_SP
* @payload.
*
* Returns: the number of parameters in @payload
*
* Since: 1.4
*/
guint
gst_mikey_payload_sp_get_n_params (const GstMIKEYPayload * payload)
{
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
g_return_val_if_fail (payload != NULL, 0);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_SP, 0);
return p->params->len;
}
/**
* gst_mikey_payload_sp_get_param:
* @payload: a #GstMIKEYPayload
* @idx: an index
*
* Get the Security Policy parameter in a %GST_MIKEY_PT_SP @payload
* at @idx.
*
* Returns: (transfer none) (nullable): the #GstMIKEYPayloadSPParam at @idx in @payload
*
* Since: 1.4
*/
const GstMIKEYPayloadSPParam *
gst_mikey_payload_sp_get_param (const GstMIKEYPayload * payload, guint idx)
{
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
g_return_val_if_fail (payload != NULL, NULL);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_SP, NULL);
if (p->params->len <= idx)
return NULL;
return &g_array_index (p->params, GstMIKEYPayloadSPParam, idx);
}
/**
* gst_mikey_payload_sp_remove_param:
* @payload: a #GstMIKEYPayload
* @idx: an index
*
* Remove the Security Policy parameters from a %GST_MIKEY_PT_SP
* @payload at @idx.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_sp_remove_param (GstMIKEYPayload * payload, guint idx)
{
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_SP, FALSE);
g_return_val_if_fail (p->params->len > idx, FALSE);
g_array_remove_index (p->params, idx);
return TRUE;
}
/**
* gst_mikey_payload_sp_add_param:
* @payload: a #GstMIKEYPayload
* @type: a type
* @len: a length
* @val: (array length=len): @len bytes of data
*
* Add a new parameter to the %GST_MIKEY_PT_SP @payload with @type, @len
* and @val.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_sp_add_param (GstMIKEYPayload * payload,
guint8 type, guint8 len, const guint8 * val)
{
GstMIKEYPayloadSPParam param = { 0 };
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_SP, FALSE);
param.type = type;
param.len = len;
INIT_MEMDUP (param.val, val, len);
g_array_append_val (p->params, param);
return TRUE;
}
/* RAND payload (RAND) */
/**
* gst_mikey_payload_rand_set:
* @payload: a #GstMIKEYPayload
* @len: the length of @rand
* @rand: (array length=len): random values
*
* Set the random values in a %GST_MIKEY_PT_RAND @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_rand_set (GstMIKEYPayload * payload, guint8 len,
const guint8 * rand)
{
GstMIKEYPayloadRAND *p = (GstMIKEYPayloadRAND *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_RAND, FALSE);
p->len = len;
INIT_MEMDUP (p->rand, rand, len);
return TRUE;
}
static gboolean
gst_mikey_payload_rand_dispose (GstMIKEYPayloadRAND * payload)
{
FREE_MEMDUP (payload->rand);
return TRUE;
}
static GstMIKEYPayloadRAND *
gst_mikey_payload_rand_copy (const GstMIKEYPayloadRAND * payload)
{
GstMIKEYPayloadRAND *copy = g_slice_dup (GstMIKEYPayloadRAND, payload);
gst_mikey_payload_rand_set (&copy->pt, payload->len, payload->rand);
return copy;
}
/* Error payload (ERR) */
/* Key data sub-payload */
/**
* gst_mikey_payload_key_data_set_key:
* @payload: a #GstMIKEYPayload
* @key_type: a #GstMIKEYKeyDataType
* @key_len: the length of @key_data
* @key_data: (array length=key_len): the key of type @key_type
*
* Set @key_len bytes of @key_data of type @key_type as the key for the
* %GST_MIKEY_PT_KEY_DATA @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_key_data_set_key (GstMIKEYPayload * payload,
GstMIKEYKeyDataType key_type, guint16 key_len, const guint8 * key_data)
{
GstMIKEYPayloadKeyData *p = (GstMIKEYPayloadKeyData *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEY_DATA, FALSE);
g_return_val_if_fail (key_len > 0 && key_data != NULL, FALSE);
p->key_type = key_type;
p->key_len = key_len;
INIT_MEMDUP (p->key_data, key_data, key_len);
return TRUE;
}
/**
* gst_mikey_payload_key_data_set_salt:
* @payload: a #GstMIKEYPayload
* @salt_len: the length of @salt_data
* @salt_data: (array length=salt_len) (allow-none): the salt
*
* Set the salt key data. If @salt_len is 0 and @salt_data is %NULL, the
* salt data will be removed.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_key_data_set_salt (GstMIKEYPayload * payload,
guint16 salt_len, const guint8 * salt_data)
{
GstMIKEYPayloadKeyData *p = (GstMIKEYPayloadKeyData *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEY_DATA, FALSE);
g_return_val_if_fail ((salt_len == 0 && salt_data == NULL) ||
(salt_len > 0 && salt_data != NULL), FALSE);
p->salt_len = salt_len;
INIT_MEMDUP (p->salt_data, salt_data, salt_len);
return TRUE;
}
/* Key validity data */
/**
* gst_mikey_payload_key_data_set_spi:
* @payload: a #GstMIKEYPayload
* @spi_len: the length of @spi_data
* @spi_data: (array length=spi_len): the SPI/MKI data
*
* Set the SPI/MKI validity in the %GST_MIKEY_PT_KEY_DATA @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_key_data_set_spi (GstMIKEYPayload * payload,
guint8 spi_len, const guint8 * spi_data)
{
GstMIKEYPayloadKeyData *p = (GstMIKEYPayloadKeyData *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEY_DATA, FALSE);
g_return_val_if_fail ((spi_len == 0 && spi_data == NULL) ||
(spi_len > 0 && spi_data != NULL), FALSE);
p->kv_type = GST_MIKEY_KV_SPI;
p->kv_len[0] = spi_len;
INIT_MEMDUP (p->kv_data[0], spi_data, spi_len);
p->kv_len[1] = 0;
FREE_MEMDUP (p->kv_data[1]);
return TRUE;
}
/**
* gst_mikey_payload_key_data_set_interval:
* @payload: a #GstMIKEYPayload
* @vf_len: the length of @vf_data
* @vf_data: (array length=vf_data): the Valid From data
* @vt_len: the length of @vt_data
* @vt_data: (array length=vt_len): the Valid To data
*
* Set the key validity period in the %GST_MIKEY_PT_KEY_DATA @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_payload_key_data_set_interval (GstMIKEYPayload * payload,
guint8 vf_len, const guint8 * vf_data, guint8 vt_len,
const guint8 * vt_data)
{
GstMIKEYPayloadKeyData *p = (GstMIKEYPayloadKeyData *) payload;
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (payload->type == GST_MIKEY_PT_KEY_DATA, FALSE);
g_return_val_if_fail ((vf_len == 0 && vf_data == NULL) ||
(vf_len > 0 && vf_data != NULL), FALSE);
g_return_val_if_fail ((vt_len == 0 && vt_data == NULL) ||
(vt_len > 0 && vt_data != NULL), FALSE);
p->kv_type = GST_MIKEY_KV_INTERVAL;
p->kv_len[0] = vf_len;
INIT_MEMDUP (p->kv_data[0], vf_data, vf_len);
p->kv_len[1] = vt_len;
INIT_MEMDUP (p->kv_data[1], vt_data, vt_len);
return TRUE;
}
static gboolean
gst_mikey_payload_key_data_dispose (GstMIKEYPayloadKeyData * payload)
{
FREE_MEMDUP (payload->key_data);
FREE_MEMDUP (payload->salt_data);
FREE_MEMDUP (payload->kv_data[0]);
FREE_MEMDUP (payload->kv_data[1]);
return TRUE;
}
static GstMIKEYPayloadKeyData *
gst_mikey_payload_key_data_copy (const GstMIKEYPayloadKeyData * payload)
{
GstMIKEYPayloadKeyData *copy = g_slice_dup (GstMIKEYPayloadKeyData, payload);
gst_mikey_payload_key_data_set_key (&copy->pt, payload->key_type,
payload->key_len, payload->key_data);
gst_mikey_payload_key_data_set_salt (&copy->pt, payload->salt_len,
payload->salt_data);
if (payload->kv_type == GST_MIKEY_KV_SPI)
gst_mikey_payload_key_data_set_spi (&copy->pt, payload->kv_len[0],
payload->kv_data[0]);
else if (payload->kv_type == GST_MIKEY_KV_INTERVAL)
gst_mikey_payload_key_data_set_interval (&copy->pt, payload->kv_len[0],
payload->kv_data[0], payload->kv_len[1], payload->kv_data[1]);
else {
FREE_MEMDUP (copy->kv_data[0]);
FREE_MEMDUP (copy->kv_data[1]);
}
return copy;
}
/* General Extension Payload */
static void
mikey_payload_free (GstMIKEYPayload * payload)
{
g_slice_free1 (payload->len, payload);
}
/**
* gst_mikey_payload_new:
* @type: a #GstMIKEYPayloadType
*
* Make a new #GstMIKEYPayload with @type.
*
* Returns: (transfer full) (nullable): a new #GstMIKEYPayload or %NULL on failure.
*
* Since: 1.4
*/
GstMIKEYPayload *
gst_mikey_payload_new (GstMIKEYPayloadType type)
{
guint len = 0;
GstMIKEYPayload *result;
GstMiniObjectCopyFunction copy;
GstMiniObjectDisposeFunction clear;
switch (type) {
case GST_MIKEY_PT_KEMAC:
len = sizeof (GstMIKEYPayloadKEMAC);
clear = (GstMiniObjectDisposeFunction) gst_mikey_payload_kemac_dispose;
copy = (GstMiniObjectCopyFunction) gst_mikey_payload_kemac_copy;
break;
case GST_MIKEY_PT_T:
len = sizeof (GstMIKEYPayloadT);
clear = (GstMiniObjectDisposeFunction) gst_mikey_payload_t_dispose;
copy = (GstMiniObjectCopyFunction) gst_mikey_payload_t_copy;
break;
case GST_MIKEY_PT_PKE:
len = sizeof (GstMIKEYPayloadPKE);
clear = (GstMiniObjectDisposeFunction) gst_mikey_payload_pke_dispose;
copy = (GstMiniObjectCopyFunction) gst_mikey_payload_pke_copy;
break;
case GST_MIKEY_PT_DH:
case GST_MIKEY_PT_SIGN:
case GST_MIKEY_PT_ID:
case GST_MIKEY_PT_CERT:
case GST_MIKEY_PT_CHASH:
case GST_MIKEY_PT_V:
case GST_MIKEY_PT_SP:
len = sizeof (GstMIKEYPayloadSP);
clear = (GstMiniObjectDisposeFunction) gst_mikey_payload_sp_dispose;
copy = (GstMiniObjectCopyFunction) gst_mikey_payload_sp_copy;
break;
case GST_MIKEY_PT_RAND:
len = sizeof (GstMIKEYPayloadRAND);
clear = (GstMiniObjectDisposeFunction) gst_mikey_payload_rand_dispose;
copy = (GstMiniObjectCopyFunction) gst_mikey_payload_rand_copy;
break;
case GST_MIKEY_PT_ERR:
break;
case GST_MIKEY_PT_KEY_DATA:
len = sizeof (GstMIKEYPayloadKeyData);
clear = (GstMiniObjectDisposeFunction) gst_mikey_payload_key_data_dispose;
copy = (GstMiniObjectCopyFunction) gst_mikey_payload_key_data_copy;
break;
case GST_MIKEY_PT_GEN_EXT:
case GST_MIKEY_PT_LAST:
break;
}
if (len == 0)
return NULL;
result = g_slice_alloc0 (len);
gst_mini_object_init (GST_MINI_OBJECT_CAST (result),
0, GST_TYPE_MIKEY_PAYLOAD, copy, clear,
(GstMiniObjectFreeFunction) mikey_payload_free);
result->type = type;
result->len = len;
return result;
}
static GstMIKEYMessage *
mikey_message_copy (GstMIKEYMessage * msg)
{
GstMIKEYMessage *copy;
guint i, len;
copy = gst_mikey_message_new ();
gst_mikey_message_set_info (copy, msg->version, msg->type, msg->V,
msg->prf_func, msg->CSB_id, msg->map_type);
len = msg->map_info->len;
for (i = 0; i < len; i++) {
const GstMIKEYMapSRTP *srtp = gst_mikey_message_get_cs_srtp (msg, i);
gst_mikey_message_add_cs_srtp (copy, srtp->policy, srtp->ssrc, srtp->roc);
}
len = msg->payloads->len;
for (i = 0; i < len; i++) {
const GstMIKEYPayload *pay = gst_mikey_message_get_payload (msg, i);
gst_mikey_message_add_payload (copy, gst_mikey_payload_copy (pay));
}
return copy;
}
static void
mikey_message_free (GstMIKEYMessage * msg)
{
FREE_ARRAY (msg->map_info);
FREE_ARRAY (msg->payloads);
g_slice_free (GstMIKEYMessage, msg);
}
/**
* gst_mikey_message_new:
*
* Make a new MIKEY message.
*
* Returns: (transfer full): a new #GstMIKEYMessage on success
*
* Since: 1.4
*/
GstMIKEYMessage *
gst_mikey_message_new (void)
{
GstMIKEYMessage *result;
result = g_slice_new0 (GstMIKEYMessage);
gst_mini_object_init (GST_MINI_OBJECT_CAST (result),
0, GST_TYPE_MIKEY_MESSAGE,
(GstMiniObjectCopyFunction) mikey_message_copy, NULL,
(GstMiniObjectFreeFunction) mikey_message_free);
INIT_ARRAY (result->map_info, GstMIKEYMapSRTP, NULL);
INIT_ARRAY (result->payloads, GstMIKEYPayload *, payload_destroy);
return result;
}
/**
* gst_mikey_message_new_from_bytes:
* @bytes: a #GBytes
* @info: a #GstMIKEYDecryptInfo
* @error: a #GError
*
* Make a new #GstMIKEYMessage from @bytes.
*
* Returns: (transfer full): a new #GstMIKEYMessage
*
* Since: 1.4
*/
GstMIKEYMessage *
gst_mikey_message_new_from_bytes (GBytes * bytes, GstMIKEYDecryptInfo * info,
GError ** error)
{
gconstpointer data;
gsize size;
g_return_val_if_fail (bytes != NULL, NULL);
data = g_bytes_get_data (bytes, &size);
return gst_mikey_message_new_from_data (data, size, info, error);
}
/**
* gst_mikey_message_set_info:
* @msg: a #GstMIKEYMessage
* @version: a version
* @type: a #GstMIKEYType
* @V: verify flag
* @prf_func: the #GstMIKEYPRFFunc function to use
* @CSB_id: the Crypto Session Bundle id
* @map_type: the #GstMIKEYMapType
*
* Set the information in @msg.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_set_info (GstMIKEYMessage * msg, guint8 version,
GstMIKEYType type, gboolean V, GstMIKEYPRFFunc prf_func, guint32 CSB_id,
GstMIKEYMapType map_type)
{
g_return_val_if_fail (msg != NULL, FALSE);
msg->version = version;
msg->type = type;
msg->V = V;
msg->prf_func = prf_func;
msg->CSB_id = CSB_id;
msg->map_type = map_type;
return TRUE;
}
/**
* gst_mikey_message_get_n_cs:
* @msg: a #GstMIKEYMessage
*
* Get the number of crypto sessions in @msg.
*
* Returns: the number of crypto sessions
*
* Since: 1.4
*/
guint
gst_mikey_message_get_n_cs (const GstMIKEYMessage * msg)
{
g_return_val_if_fail (msg != NULL, 0);
return msg->map_info->len;
}
/**
* gst_mikey_message_get_cs_srtp:
* @msg: a #GstMIKEYMessage
* @idx: an index
*
* Get the policy information of @msg at @idx.
*
* Returns: (transfer none) (nullable): a #GstMIKEYMapSRTP
*
* Since: 1.4
*/
const GstMIKEYMapSRTP *
gst_mikey_message_get_cs_srtp (const GstMIKEYMessage * msg, guint idx)
{
g_return_val_if_fail (msg != NULL, NULL);
g_return_val_if_fail (msg->map_type == GST_MIKEY_MAP_TYPE_SRTP, NULL);
if (msg->map_info->len <= idx)
return NULL;
return &g_array_index (msg->map_info, GstMIKEYMapSRTP, idx);
}
/**
* gst_mikey_message_insert_cs_srtp:
* @msg: a #GstMIKEYMessage
* @idx: the index to insert at
* @map: the map info
*
* Insert a Crypto Session map for SRTP in @msg at @idx
*
* When @idx is -1, the policy will be appended.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_insert_cs_srtp (GstMIKEYMessage * msg, gint idx,
const GstMIKEYMapSRTP * map)
{
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (msg->map_type == GST_MIKEY_MAP_TYPE_SRTP, FALSE);
g_return_val_if_fail (map != NULL, FALSE);
g_return_val_if_fail (idx == -1 || msg->map_info->len > idx, FALSE);
if (idx == -1)
g_array_append_val (msg->map_info, *map);
else
g_array_insert_val (msg->map_info, idx, *map);
return TRUE;
}
/**
* gst_mikey_message_replace_cs_srtp:
* @msg: a #GstMIKEYMessage
* @idx: the index to insert at
* @map: the map info
*
* Replace a Crypto Session map for SRTP in @msg at @idx with @map.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_replace_cs_srtp (GstMIKEYMessage * msg, gint idx,
const GstMIKEYMapSRTP * map)
{
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (msg->map_type == GST_MIKEY_MAP_TYPE_SRTP, FALSE);
g_return_val_if_fail (map != NULL, FALSE);
g_return_val_if_fail (msg->map_info->len > idx, FALSE);
g_array_index (msg->map_info, GstMIKEYMapSRTP, idx) = *map;
return TRUE;
}
/**
* gst_mikey_message_remove_cs_srtp:
* @msg: a #GstMIKEYMessage
* @idx: the index to remove
*
* Remove the SRTP policy at @idx.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_remove_cs_srtp (GstMIKEYMessage * msg, gint idx)
{
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (msg->map_type == GST_MIKEY_MAP_TYPE_SRTP, FALSE);
g_return_val_if_fail (msg->map_info->len > idx, FALSE);
g_array_remove_index (msg->map_info, idx);
return TRUE;
}
/**
* gst_mikey_message_add_cs_srtp:
* @msg: a #GstMIKEYMessage
* @policy: The security policy applied for the stream with @ssrc
* @ssrc: the SSRC that must be used for the stream
* @roc: current rollover counter
*
* Add a Crypto policy for SRTP to @msg.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_cs_srtp (GstMIKEYMessage * msg, guint8 policy,
guint32 ssrc, guint32 roc)
{
GstMIKEYMapSRTP val;
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (msg->map_type == GST_MIKEY_MAP_TYPE_SRTP, FALSE);
val.policy = policy;
val.ssrc = ssrc;
val.roc = roc;
return gst_mikey_message_insert_cs_srtp (msg, -1, &val);
}
/* adding/retrieving payloads */
/**
* gst_mikey_message_get_n_payloads:
* @msg: a #GstMIKEYMessage
*
* Get the number of payloads in @msg.
*
* Returns: the number of payloads in @msg
*
* Since: 1.4
*/
guint
gst_mikey_message_get_n_payloads (const GstMIKEYMessage * msg)
{
g_return_val_if_fail (msg != NULL, 0);
return msg->payloads->len;
}
/**
* gst_mikey_message_get_payload:
* @msg: a #GstMIKEYMessage
* @idx: an index
*
* Get the #GstMIKEYPayload at @idx in @msg
*
* Returns: (transfer none) (nullable): the #GstMIKEYPayload at @idx. The payload
* remains valid for as long as it is part of @msg.
*
* Since: 1.4
*/
const GstMIKEYPayload *
gst_mikey_message_get_payload (const GstMIKEYMessage * msg, guint idx)
{
g_return_val_if_fail (msg != NULL, NULL);
if (idx >= msg->payloads->len)
return NULL;
return g_array_index (msg->payloads, GstMIKEYPayload *, idx);
}
/**
* gst_mikey_message_find_payload:
* @msg: a #GstMIKEYMessage
* @type: a #GstMIKEYPayloadType
* @nth: payload to find
*
* Find the @nth occurrence of the payload with @type in @msg.
*
* Returns: (transfer none) (nullable): the @nth #GstMIKEYPayload of @type.
*
* Since: 1.4
*/
const GstMIKEYPayload *
gst_mikey_message_find_payload (const GstMIKEYMessage * msg,
GstMIKEYPayloadType type, guint idx)
{
guint i, len, count;
count = 0;
len = msg->payloads->len;
for (i = 0; i < len; i++) {
GstMIKEYPayload *payload =
g_array_index (msg->payloads, GstMIKEYPayload *, i);
if (payload->type == type) {
if (count == idx)
return payload;
count++;
}
}
return NULL;
}
/**
* gst_mikey_message_remove_payload:
* @msg: a #GstMIKEYMessage
* @idx: an index
*
* Remove the payload in @msg at @idx
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_remove_payload (GstMIKEYMessage * msg, guint idx)
{
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (msg->payloads->len > idx, FALSE);
g_array_remove_index (msg->payloads, idx);
return TRUE;
}
/**
* gst_mikey_message_insert_payload:
* @msg: a #GstMIKEYMessage
* @idx: an index
* @payload: (transfer full): a #GstMIKEYPayload
*
* Insert the @payload at index @idx in @msg. If @idx is -1, the payload
* will be appended to @msg.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_insert_payload (GstMIKEYMessage * msg, guint idx,
GstMIKEYPayload * payload)
{
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (idx == -1 || msg->payloads->len > idx, FALSE);
if (idx == -1)
g_array_append_val (msg->payloads, payload);
else
g_array_insert_val (msg->payloads, idx, payload);
return TRUE;
}
/**
* gst_mikey_message_add_payload:
* @msg: a #GstMIKEYMessage
* @payload: (transfer full): a #GstMIKEYPayload
*
* Add a new payload to @msg.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_payload (GstMIKEYMessage * msg, GstMIKEYPayload * payload)
{
return gst_mikey_message_insert_payload (msg, -1, payload);
}
/**
* gst_mikey_message_replace_payload:
* @msg: a #GstMIKEYMessage
* @idx: an index
* @payload: (transfer full): a #GstMIKEYPayload
*
* Replace the payload at @idx in @msg with @payload.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_replace_payload (GstMIKEYMessage * msg, guint idx,
GstMIKEYPayload * payload)
{
GstMIKEYPayload *p;
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (payload != NULL, FALSE);
g_return_val_if_fail (msg->payloads->len > idx, FALSE);
p = g_array_index (msg->payloads, GstMIKEYPayload *, idx);
gst_mikey_payload_unref (p);
g_array_index (msg->payloads, GstMIKEYPayload *, idx) = payload;
return TRUE;
}
/**
* gst_mikey_message_add_pke:
* @msg: a #GstMIKEYMessage
* @C: envelope key cache indicator
* @data_len: the length of @data
* @data: (array length=data_len): the encrypted envelope key
*
* Add a new PKE payload to @msg with the given parameters.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_pke (GstMIKEYMessage * msg, GstMIKEYCacheType C,
guint16 data_len, const guint8 * data)
{
GstMIKEYPayload *p;
g_return_val_if_fail (msg != NULL, FALSE);
p = gst_mikey_payload_new (GST_MIKEY_PT_PKE);
if (!gst_mikey_payload_pke_set (p, C, data_len, data)) {
gst_mikey_payload_unref (p);
return FALSE;
}
return gst_mikey_message_insert_payload (msg, -1, p);
}
/**
* gst_mikey_message_add_t:
* @msg: a #GstMIKEYMessage
* @type: specifies the timestamp type used
* @ts_value: (array): The timestamp value of the specified @type
*
* Add a new T payload to @msg with the given parameters.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_t (GstMIKEYMessage * msg, GstMIKEYTSType type,
const guint8 * ts_value)
{
GstMIKEYPayload *p;
g_return_val_if_fail (msg != NULL, FALSE);
p = gst_mikey_payload_new (GST_MIKEY_PT_T);
if (!gst_mikey_payload_t_set (p, type, ts_value)) {
gst_mikey_payload_unref (p);
return FALSE;
}
return gst_mikey_message_insert_payload (msg, -1, p);
}
/**
* gst_mikey_message_add_t_now_ntp_utc:
* @msg: a #GstMIKEYMessage
*
* Add a new T payload to @msg that contains the current time
* in NTP-UTC format.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_t_now_ntp_utc (GstMIKEYMessage * msg)
{
gint64 now;
guint64 ntptime;
guint8 bytes[8];
now = g_get_real_time ();
/* convert clock time to NTP time. upper 32 bits should contain the seconds
* and the lower 32 bits, the fractions of a second. */
ntptime = gst_util_uint64_scale (now, (G_GINT64_CONSTANT (1) << 32), 1000000);
/* conversion from UNIX timestamp (seconds since 1970) to NTP (seconds
* since 1900). */
ntptime += (G_GUINT64_CONSTANT (2208988800) << 32);
GST_WRITE_UINT64_BE (bytes, ntptime);
return gst_mikey_message_add_t (msg, GST_MIKEY_TS_TYPE_NTP_UTC, bytes);
}
/**
* gst_mikey_message_add_rand:
* @msg: a #GstMIKEYMessage
* @len: the length of @rand
* @rand: (array length=len): random data
*
* Add a new RAND payload to @msg with the given parameters.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_rand (GstMIKEYMessage * msg, guint8 len,
const guint8 * rand)
{
GstMIKEYPayload *p;
g_return_val_if_fail (msg != NULL, FALSE);
g_return_val_if_fail (len != 0 && rand != NULL, FALSE);
p = gst_mikey_payload_new (GST_MIKEY_PT_RAND);
if (!gst_mikey_payload_rand_set (p, len, rand)) {
gst_mikey_payload_unref (p);
return FALSE;
}
return gst_mikey_message_insert_payload (msg, -1, p);
}
/**
* gst_mikey_message_add_rand_len:
* @msg: a #GstMIKEYMessage
* @len: length
*
* Add a new RAND payload to @msg with @len random bytes.
*
* Returns: %TRUE on success
*
* Since: 1.4
*/
gboolean
gst_mikey_message_add_rand_len (GstMIKEYMessage * msg, guint8 len)
{
GstMIKEYPayloadRAND *p;
guint i;
p = (GstMIKEYPayloadRAND *) gst_mikey_payload_new (GST_MIKEY_PT_RAND);
p->len = len;
p->rand = g_malloc (len);
for (i = 0; i < len; i++)
p->rand[i] = g_random_int_range (0, 256);
return gst_mikey_message_add_payload (msg, &p->pt);
}
#define ENSURE_SIZE(n) \
G_STMT_START { \
guint offset = data - arr->data; \
g_byte_array_set_size (arr, offset + n); \
data = arr->data + offset; \
} G_STMT_END
static guint
payloads_to_bytes (GArray * payloads, GByteArray * arr, guint8 ** ptr,
guint offset, GstMIKEYEncryptInfo * info, GError ** error)
{
guint i, n_payloads, len, start, size;
guint8 *data;
GstMIKEYPayload *next_payload;
len = arr->len;
start = *ptr - arr->data;
data = *ptr + offset;
n_payloads = payloads->len;
for (i = 0; i < n_payloads; i++) {
GstMIKEYPayload *payload = g_array_index (payloads, GstMIKEYPayload *, i);
if (i + 1 < n_payloads)
next_payload = g_array_index (payloads, GstMIKEYPayload *, i + 1);
else
next_payload = NULL;
switch (payload->type) {
case GST_MIKEY_PT_KEMAC:
{
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
guint enc_len;
guint mac_len;
if ((mac_len = get_mac_len (p->mac_alg)) == -1)
break;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next payload ! Encr alg ! Encr data len !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Encr data ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Mac alg ! MAC ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (4);
data[0] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
data[1] = p->enc_alg;
enc_len =
payloads_to_bytes (p->subpayloads, arr, &data, 4, info, error);
/* FIXME, encrypt data here */
GST_WRITE_UINT16_BE (&data[2], enc_len);
data += enc_len;
ENSURE_SIZE (5 + mac_len);
data[4] = p->mac_alg;
/* FIXME, do mac here */
data += 5 + mac_len;
break;
}
case GST_MIKEY_PT_T:
{
GstMIKEYPayloadT *p = (GstMIKEYPayloadT *) payload;
guint ts_len;
if ((ts_len = get_ts_len (p->type)) == -1)
break;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next Payload ! TS type ! TS value ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (2 + ts_len);
data[0] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
data[1] = p->type;
memcpy (&data[2], p->ts_value, ts_len);
data += 2 + ts_len;
break;
}
case GST_MIKEY_PT_PKE:
{
guint16 clen;
GstMIKEYPayloadPKE *p = (GstMIKEYPayloadPKE *) payload;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next Payload ! C ! Data len ! Data ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (3 + p->data_len);
data[0] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
clen = (p->C << 14) || (p->data_len & 0x3fff);
GST_WRITE_UINT16_BE (&data[1], clen);
memcpy (&data[3], p->data, p->data_len);
data += 3 + p->data_len;
break;
}
case GST_MIKEY_PT_DH:
case GST_MIKEY_PT_SIGN:
case GST_MIKEY_PT_ID:
case GST_MIKEY_PT_CERT:
case GST_MIKEY_PT_CHASH:
case GST_MIKEY_PT_V:
break;
case GST_MIKEY_PT_SP:
{
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
guint len, plen, i;
plen = 0;
len = p->params->len;
for (i = 0; i < len; i++) {
GstMIKEYPayloadSPParam *param = &g_array_index (p->params,
GstMIKEYPayloadSPParam, i);
plen += 2 + param->len;
}
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next payload ! Policy no ! Prot type ! Policy param ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ length (cont) ! Policy param ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (5 + plen);
data[0] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
data[1] = p->policy;
data[2] = p->proto;
GST_WRITE_UINT16_BE (&data[3], plen);
data += 5;
for (i = 0; i < len; i++) {
GstMIKEYPayloadSPParam *param = &g_array_index (p->params,
GstMIKEYPayloadSPParam, i);
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Type ! Length ! Value ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
data[0] = param->type;
data[1] = param->len;
memcpy (&data[2], param->val, param->len);
data += 2 + param->len;
}
break;
}
case GST_MIKEY_PT_RAND:
{
GstMIKEYPayloadRAND *p = (GstMIKEYPayloadRAND *) payload;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next payload ! RAND len ! RAND ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (2 + p->len);
data[0] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
data[1] = p->len;
memcpy (&data[2], p->rand, p->len);
data += 2 + p->len;
break;
}
case GST_MIKEY_PT_ERR:
break;
case GST_MIKEY_PT_KEY_DATA:
{
GstMIKEYPayloadKeyData *p = (GstMIKEYPayloadKeyData *) payload;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next Payload ! Type ! KV ! Key data len !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Key data ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Salt len (optional) ! Salt data (optional) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! KV data (optional) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (4 + p->key_len);
data[0] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
data[1] =
((p->key_type | (p->salt_len ? 1 : 0)) << 4) | (p->kv_type & 0xf);
GST_WRITE_UINT16_BE (&data[2], p->key_len);
memcpy (&data[4], p->key_data, p->key_len);
data += 4 + p->key_len;
if (p->salt_len > 0) {
ENSURE_SIZE (2 + p->salt_len);
GST_WRITE_UINT16_BE (&data[0], p->salt_len);
memcpy (&data[2], p->salt_data, p->salt_len);
data += 2 + p->salt_len;
}
if (p->kv_type == GST_MIKEY_KV_SPI) {
/*
* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! SPI Length ! SPI ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (1 + p->kv_len[0]);
data[0] = p->kv_len[0];
memcpy (&data[1], p->kv_data[0], p->kv_len[0]);
data += 1 + p->kv_len[0];
} else if (p->kv_type == GST_MIKEY_KV_INTERVAL) {
/*
* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! VF Length ! Valid From ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! VT Length ! Valid To (expires) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (1 + p->kv_len[0]);
data[0] = p->kv_len[0];
memcpy (&data[1], p->kv_data[0], p->kv_len[0]);
data += 1 + p->kv_len[0];
ENSURE_SIZE (1 + p->kv_len[1]);
data[0] = p->kv_len[1];
memcpy (&data[1], p->kv_data[1], p->kv_len[1]);
data += 1 + p->kv_len[1];
}
break;
}
case GST_MIKEY_PT_GEN_EXT:
case GST_MIKEY_PT_LAST:
break;
}
}
*ptr = arr->data + start;
size = arr->len - len;
return size;
}
/**
* gst_mikey_message_to_bytes:
* @msg: a #GstMIKEYMessage
* @info: a #GstMIKEYEncryptInfo
* @error: a #GError
*
* Convert @msg to a #GBytes.
*
* Returns: (transfer full): a new #GBytes for @msg.
*
* Since: 1.4
*/
GBytes *
gst_mikey_message_to_bytes (GstMIKEYMessage * msg, GstMIKEYEncryptInfo * info,
GError ** error)
{
GByteArray *arr = NULL;
guint8 *data;
GstMIKEYPayload *next_payload;
guint i, n_cs;
arr = g_byte_array_new ();
data = arr->data;
if (msg->payloads->len == 0)
next_payload = NULL;
else
next_payload = g_array_index (msg->payloads, GstMIKEYPayload *, 0);
n_cs = msg->map_info->len;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! version ! data type ! next payload !V! PRF func !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! CSB ID !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! #CS ! CS ID map type! CS ID map info ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ENSURE_SIZE (10 + 9 * n_cs);
data[0] = msg->version;
data[1] = msg->type;
data[2] = next_payload ? next_payload->type : GST_MIKEY_PT_LAST;
data[3] = (msg->V ? 0x80 : 0x00) | (msg->prf_func & 0x7f);
GST_WRITE_UINT32_BE (&data[4], msg->CSB_id);
data[8] = n_cs;
data[9] = msg->map_type;
data += 10;
for (i = 0; i < n_cs; i++) {
GstMIKEYMapSRTP *info = &g_array_index (msg->map_info, GstMIKEYMapSRTP, i);
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Policy_no_1 ! SSRC_1 !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! SSRC_1 (cont) ! ROC_1 !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! ROC_1 (cont) ! Policy_no_2 ! SSRC_2 !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! SSRC_2 (cont) ! ROC_2 !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! ROC_2 (cont) ! :
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ...
*/
data[0] = info->policy;
GST_WRITE_UINT32_BE (&data[1], info->ssrc);
GST_WRITE_UINT32_BE (&data[5], info->roc);
data += 9;
}
payloads_to_bytes (msg->payloads, arr, &data, 0, info, error);
return g_byte_array_free_to_bytes (arr);
}
#undef ENSURE_SIZE
typedef enum
{
STATE_PSK,
STATE_PK,
STATE_KEMAC,
STATE_OTHER
} ParseState;
#define CHECK_SIZE(n) if (size < (n)) goto short_data;
#define ADVANCE(n) (d += (n), size -= (n));
static gboolean
payloads_from_bytes (ParseState state, GArray * payloads, const guint8 * d,
gsize size, guint8 next_payload, GstMIKEYDecryptInfo * info,
GError ** error)
{
GstMIKEYPayload *p;
while (next_payload != GST_MIKEY_PT_LAST) {
p = NULL;
switch (next_payload) {
case GST_MIKEY_PT_KEMAC:
{
guint mac_len;
GstMIKEYEncAlg enc_alg;
guint16 enc_len;
const guint8 *enc_data;
GstMIKEYMacAlg mac_alg;
guint8 np;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next payload ! Encr alg ! Encr data len !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Encr data ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Mac alg ! MAC ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (5);
next_payload = d[0];
enc_alg = d[1];
enc_len = GST_READ_UINT16_BE (&d[2]);
CHECK_SIZE (5 + enc_len);
enc_data = &d[4];
/* FIXME, decrypt data */
ADVANCE (enc_len);
mac_alg = d[4];
if ((mac_len = get_mac_len (mac_alg)) == -1)
goto invalid_data;
CHECK_SIZE (5 + mac_len);
/* FIXME, check MAC */
ADVANCE (5 + mac_len);
p = gst_mikey_payload_new (GST_MIKEY_PT_KEMAC);
gst_mikey_payload_kemac_set (p, enc_alg, mac_alg);
if (state == STATE_PSK)
/* we expect Key data for Preshared key */
np = GST_MIKEY_PT_KEY_DATA;
else if (state == STATE_PK)
/* we expect ID for Public key */
np = GST_MIKEY_PT_ID;
else
goto invalid_data;
payloads_from_bytes (STATE_KEMAC,
((GstMIKEYPayloadKEMAC *) p)->subpayloads, enc_data, enc_len, np,
info, error);
g_array_append_val (payloads, p);
break;
}
case GST_MIKEY_PT_T:
{
GstMIKEYTSType type;
guint ts_len;
const guint8 *ts_value;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next Payload ! TS type ! TS value ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (2);
next_payload = d[0];
type = d[1];
if ((ts_len = get_ts_len (type)) == -1)
goto invalid_data;
CHECK_SIZE (2 + ts_len);
ts_value = &d[2];
ADVANCE (2 + ts_len);
p = gst_mikey_payload_new (GST_MIKEY_PT_T);
gst_mikey_payload_t_set (p, type, ts_value);
g_array_append_val (payloads, p);
break;
}
case GST_MIKEY_PT_PKE:
{
guint8 C;
guint16 clen, data_len;
const guint8 *data;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next Payload ! C ! Data len ! Data ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (3);
next_payload = d[0];
clen = GST_READ_UINT16_BE (&d[1]);
C = clen >> 14;
data_len = clen & 0x3fff;
CHECK_SIZE (3 + data_len);
data = &d[3];
ADVANCE (3 + data_len);
p = gst_mikey_payload_new (GST_MIKEY_PT_PKE);
gst_mikey_payload_pke_set (p, C, data_len, data);
g_array_append_val (payloads, p);
break;
}
case GST_MIKEY_PT_DH:
case GST_MIKEY_PT_SIGN:
case GST_MIKEY_PT_ID:
case GST_MIKEY_PT_CERT:
case GST_MIKEY_PT_CHASH:
case GST_MIKEY_PT_V:
break;
case GST_MIKEY_PT_SP:
{
guint8 policy;
GstMIKEYSecProto proto;
guint16 plen;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next payload ! Policy no ! Prot type ! Policy param ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ~ length (cont) ! Policy param ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (5);
next_payload = d[0];
policy = d[1];
proto = d[2];
plen = GST_READ_UINT16_BE (&d[3]);
ADVANCE (5);
p = gst_mikey_payload_new (GST_MIKEY_PT_SP);
gst_mikey_payload_sp_set (p, policy, proto);
CHECK_SIZE (plen);
while (plen) {
guint8 type, len;
CHECK_SIZE (2);
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Type ! Length ! Value ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
type = d[0];
len = d[1];
CHECK_SIZE (2 + len);
gst_mikey_payload_sp_add_param (p, type, len, &d[2]);
ADVANCE (2 + len);
plen -= 2 + len;
}
g_array_append_val (payloads, p);
break;
}
case GST_MIKEY_PT_RAND:
{
guint8 len;
const guint8 *rand;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next payload ! RAND len ! RAND ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (2);
next_payload = d[0];
len = d[1];
CHECK_SIZE (2 + len);
rand = &d[2];
ADVANCE (2 + len);
p = gst_mikey_payload_new (GST_MIKEY_PT_RAND);
gst_mikey_payload_rand_set (p, len, rand);
g_array_append_val (payloads, p);
break;
}
case GST_MIKEY_PT_ERR:
break;
case GST_MIKEY_PT_KEY_DATA:
{
GstMIKEYKeyDataType key_type;
GstMIKEYKVType kv_type;
guint16 key_len, salt_len = 0;
const guint8 *key_data, *salt_data;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Next Payload ! Type ! KV ! Key data len !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Key data ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Salt len (optional) ! Salt data (optional) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! KV data (optional) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (4);
next_payload = d[0];
key_type = d[1] >> 4;
kv_type = d[1] & 0xf;
key_len = GST_READ_UINT16_BE (&d[2]);
CHECK_SIZE (4 + key_len);
key_data = &d[4];
ADVANCE (4 + key_len);
if (key_type & 1) {
CHECK_SIZE (2);
salt_len = GST_READ_UINT16_BE (&d[0]);
CHECK_SIZE (2 + salt_len);
salt_data = &d[2];
ADVANCE (2 + salt_len);
}
p = gst_mikey_payload_new (GST_MIKEY_PT_KEY_DATA);
gst_mikey_payload_key_data_set_key (p, key_type & 2, key_len, key_data);
if (salt_len > 0)
gst_mikey_payload_key_data_set_salt (p, salt_len, salt_data);
if (kv_type == GST_MIKEY_KV_SPI) {
guint8 spi_len;
const guint8 *spi_data;
/*
* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! SPI Length ! SPI ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (1);
spi_len = d[0];
CHECK_SIZE (1 + spi_len);
spi_data = &d[1];
ADVANCE (1 + spi_len);
gst_mikey_payload_key_data_set_spi (p, spi_len, spi_data);
} else if (kv_type == GST_MIKEY_KV_INTERVAL) {
guint8 vf_len, vt_len;
const guint8 *vf_data, *vt_data;
/*
* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! VF Length ! Valid From ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! VT Length ! Valid To (expires) ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (1);
vf_len = d[0];
CHECK_SIZE (1 + vf_len);
vf_data = &d[1];
ADVANCE (1 + vf_len);
CHECK_SIZE (1);
vt_len = d[0];
CHECK_SIZE (1 + vt_len);
vt_data = &d[1];
ADVANCE (1 + vt_len);
gst_mikey_payload_key_data_set_interval (p, vf_len, vf_data, vt_len,
vt_data);
} else if (kv_type != GST_MIKEY_KV_NULL)
goto invalid_data;
g_array_append_val (payloads, p);
break;
}
case GST_MIKEY_PT_GEN_EXT:
case GST_MIKEY_PT_LAST:
break;
}
}
return TRUE;
/* ERRORS */
short_data:
{
GST_DEBUG ("not enough data");
if (p)
gst_mikey_payload_unref (p);
return FALSE;
}
invalid_data:
{
GST_DEBUG ("invalid data");
if (p)
gst_mikey_payload_unref (p);
return FALSE;
}
}
/**
* gst_mikey_message_new_from_data:
* @data: (array length=size) (element-type guint8): bytes to read
* @size: length of @data
* @info: #GstMIKEYDecryptInfo
* @error: a #GError
*
* Parse @size bytes from @data into a #GstMIKEYMessage. @info contains the
* parameters to decrypt and verify the data.
*
* Returns: (transfer full): a #GstMIKEYMessage on success or %NULL when parsing failed and
* @error will be set.
*
* Since: 1.4
*/
GstMIKEYMessage *
gst_mikey_message_new_from_data (gconstpointer data, gsize size,
GstMIKEYDecryptInfo * info, GError ** error)
{
GstMIKEYMessage *msg;
guint n_cs, i;
const guint8 *d = data;
guint8 next_payload;
ParseState state;
g_return_val_if_fail (data != NULL, NULL);
msg = gst_mikey_message_new ();
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! version ! data type ! next payload !V! PRF func !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! CSB ID !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! #CS ! CS ID map type! CS ID map info ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
CHECK_SIZE (10);
msg->version = d[0];
if (msg->version != GST_MIKEY_VERSION)
goto unknown_version;
msg->type = d[1];
next_payload = d[2];
msg->V = d[3] & 0x80 ? TRUE : FALSE;
msg->prf_func = d[3] & 0x7f;
msg->CSB_id = GST_READ_UINT32_BE (&d[4]);
n_cs = d[8];
msg->map_type = d[9];
ADVANCE (10);
CHECK_SIZE (n_cs * 9);
for (i = 0; i < n_cs; i++) {
GstMIKEYMapSRTP map;
/* 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! Policy_no_1 ! SSRC_1 !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! SSRC_1 (cont) ! ROC_1 !
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ! ROC_1 (cont) ! Policy_no_2 ! SSRC_2 ~
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
map.policy = d[0];
map.ssrc = GST_READ_UINT32_BE (&d[1]);
map.roc = GST_READ_UINT32_BE (&d[5]);
gst_mikey_message_insert_cs_srtp (msg, -1, &map);
ADVANCE (9);
}
if (msg->type == GST_MIKEY_TYPE_PSK_INIT)
state = STATE_PSK;
else if (msg->type == GST_MIKEY_TYPE_PK_INIT)
state = STATE_PK;
else
state = STATE_OTHER;
if (!payloads_from_bytes (state, msg->payloads, d, size, next_payload, info,
error))
goto parse_error;
return msg;
/* ERRORS */
short_data:
{
GST_DEBUG ("not enough data");
gst_mikey_message_unref (msg);
return NULL;
}
unknown_version:
{
GST_DEBUG ("unknown version");
gst_mikey_message_unref (msg);
return NULL;
}
parse_error:
{
GST_DEBUG ("failed to parse");
gst_mikey_message_unref (msg);
return NULL;
}
}
#define AES_128_KEY_LEN 16
#define AES_256_KEY_LEN 32
#define HMAC_32_KEY_LEN 4
#define HMAC_80_KEY_LEN 10
static gboolean
auth_alg_from_cipher_name (const gchar * cipher, guint8 * auth_alg)
{
if (g_strcmp0 (cipher, "aes-128-icm") == 0)
*auth_alg = GST_MIKEY_MAC_HMAC_SHA_1_160;
else if (g_strcmp0 (cipher, "aes-256-icm") == 0)
*auth_alg = GST_MIKEY_MAC_HMAC_SHA_1_160;
else if (g_strcmp0 (cipher, "aes-128-gcm") == 0)
*auth_alg = GST_MIKEY_MAC_NULL;
else if (g_strcmp0 (cipher, "aes-256-gcm") == 0)
*auth_alg = GST_MIKEY_MAC_NULL;
else {
GST_ERROR ("encryption algorithm '%s' not supported", cipher);
return FALSE;
}
return TRUE;
}
static gboolean
enc_alg_from_cipher_name (const gchar * cipher, guint8 * enc_alg)
{
if (g_strcmp0 (cipher, "aes-128-icm") == 0)
*enc_alg = GST_MIKEY_ENC_AES_CM_128;
else if (g_strcmp0 (cipher, "aes-256-icm") == 0)
*enc_alg = GST_MIKEY_ENC_AES_CM_128;
else if (g_strcmp0 (cipher, "aes-128-gcm") == 0)
*enc_alg = GST_MIKEY_ENC_AES_GCM_128;
else if (g_strcmp0 (cipher, "aes-256-gcm") == 0)
*enc_alg = GST_MIKEY_ENC_AES_GCM_128;
else {
GST_ERROR ("encryption algorithm '%s' not supported", cipher);
return FALSE;
}
return TRUE;
}
static gboolean
enc_key_length_from_cipher_name (const gchar * cipher, guint8 * enc_key_length)
{
if (g_strcmp0 (cipher, "aes-128-icm") == 0)
*enc_key_length = AES_128_KEY_LEN;
else if (g_strcmp0 (cipher, "aes-256-icm") == 0)
*enc_key_length = AES_256_KEY_LEN;
else if (g_strcmp0 (cipher, "aes-128-gcm") == 0)
*enc_key_length = AES_128_KEY_LEN;
else if (g_strcmp0 (cipher, "aes-256-gcm") == 0)
*enc_key_length = AES_256_KEY_LEN;
else {
GST_ERROR ("encryption algorithm '%s' not supported", cipher);
return FALSE;
}
return TRUE;
}
static gboolean
auth_key_length_from_auth_cipher_name (const gchar * auth, const gchar * cipher,
guint8 * length)
{
if (g_strcmp0 (cipher, "aes-128-gcm") == 0
|| g_strcmp0 (cipher, "aes-256-gcm") == 0) {
*length = 0;
} else {
if (g_strcmp0 (auth, "hmac-sha1-32") == 0) {
*length = HMAC_32_KEY_LEN;
} else if (g_strcmp0 (auth, "hmac-sha1-80") == 0) {
*length = HMAC_80_KEY_LEN;
} else {
GST_ERROR ("authentication algorithm '%s' not supported", auth);
return FALSE;
}
}
return TRUE;
}
/**
* gst_mikey_message_new_from_caps:
* @caps: a #GstCaps, including SRTP parameters (srtp/srtcp cipher, authorization, key data)
*
* Makes mikey message including:
* - Security Policy Payload
* - Key Data Transport Payload
* - Key Data Sub-Payload
*
* Returns: (transfer full) (nullable): a #GstMIKEYMessage,
* or %NULL if there is no srtp information in the caps.
*
* Since: 1.8
*/
GstMIKEYMessage *
gst_mikey_message_new_from_caps (GstCaps * caps)
{
GstMIKEYMessage *msg;
GstMIKEYPayload *payload, *pkd;
guint8 byte;
guint8 enc_alg;
guint8 auth_alg;
guint8 enc_key_length;
guint8 auth_key_length;
GstStructure *s;
GstMapInfo info;
GstBuffer *srtpkey;
const GValue *val;
const gchar *cipher, *auth;
const gchar *srtpcipher, *srtpauth, *srtcpcipher, *srtcpauth;
g_return_val_if_fail (caps != NULL && GST_IS_CAPS (caps), NULL);
s = gst_caps_get_structure (caps, 0);
g_return_val_if_fail (s != NULL, NULL);
val = gst_structure_get_value (s, "srtp-key");
if (!val)
goto no_key;
srtpkey = gst_value_get_buffer (val);
if (!srtpkey || !GST_IS_BUFFER (srtpkey))
goto no_key;
srtpcipher = gst_structure_get_string (s, "srtp-cipher");
srtpauth = gst_structure_get_string (s, "srtp-auth");
srtcpcipher = gst_structure_get_string (s, "srtcp-cipher");
srtcpauth = gst_structure_get_string (s, "srtcp-auth");
/* we need srtp cipher/auth or srtcp cipher/auth */
if ((srtpcipher == NULL || srtpauth == NULL)
&& (srtcpcipher == NULL || srtcpauth == NULL)) {
GST_WARNING ("could not find the right SRTP parameters in caps");
return NULL;
}
/* prefer srtp cipher over srtcp */
cipher = srtpcipher;
if (cipher == NULL)
cipher = srtcpcipher;
/* prefer srtp auth over srtcp */
auth = srtpauth;
if (auth == NULL)
auth = srtcpauth;
/* get cipher and auth values */
if (!enc_alg_from_cipher_name (cipher, &enc_alg) ||
!auth_alg_from_cipher_name (cipher, &auth_alg) ||
!enc_key_length_from_cipher_name (cipher, &enc_key_length) ||
!auth_key_length_from_auth_cipher_name (auth, cipher, &auth_key_length)) {
return NULL;
}
msg = gst_mikey_message_new ();
/* unencrypted MIKEY message, we send this over TLS so this is allowed */
gst_mikey_message_set_info (msg, GST_MIKEY_VERSION, GST_MIKEY_TYPE_PSK_INIT,
FALSE, GST_MIKEY_PRF_MIKEY_1, g_random_int (), GST_MIKEY_MAP_TYPE_SRTP);
/* timestamp is now */
gst_mikey_message_add_t_now_ntp_utc (msg);
/* add some random data */
gst_mikey_message_add_rand_len (msg, 16);
/* the policy '0' is SRTP */
payload = gst_mikey_payload_new (GST_MIKEY_PT_SP);
gst_mikey_payload_sp_set (payload, 0, GST_MIKEY_SEC_PROTO_SRTP);
/* AES-CM or AES-GCM is supported */
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_ALG, 1,
&enc_alg);
/* encryption key length */
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_ENC_KEY_LEN, 1,
&enc_key_length);
/* HMAC-SHA1 or NULL in case of GCM */
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_ALG, 1,
&auth_alg);
/* authentication key length */
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_AUTH_KEY_LEN, 1,
&auth_key_length);
/* we enable encryption on RTP and RTCP */
byte = 1;
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_ENC, 1,
&byte);
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTCP_ENC, 1,
&byte);
/* we enable authentication on RTP and RTCP */
gst_mikey_payload_sp_add_param (payload, GST_MIKEY_SP_SRTP_SRTP_AUTH, 1,
&byte);
gst_mikey_message_add_payload (msg, payload);
/* make unencrypted KEMAC */
payload = gst_mikey_payload_new (GST_MIKEY_PT_KEMAC);
gst_mikey_payload_kemac_set (payload, GST_MIKEY_ENC_NULL, GST_MIKEY_MAC_NULL);
/* add the key in KEMAC */
pkd = gst_mikey_payload_new (GST_MIKEY_PT_KEY_DATA);
gst_buffer_map (srtpkey, &info, GST_MAP_READ);
gst_mikey_payload_key_data_set_key (pkd, GST_MIKEY_KD_TEK, info.size,
info.data);
gst_buffer_unmap (srtpkey, &info);
gst_mikey_payload_kemac_add_sub (payload, pkd);
gst_mikey_message_add_payload (msg, payload);
return msg;
no_key:
GST_INFO ("No srtp key");
return NULL;
}
#define AES_128_KEY_LEN 16
#define AES_256_KEY_LEN 32
#define HMAC_32_KEY_LEN 4
#define HMAC_80_KEY_LEN 10
/**
* gst_mikey_message_to_caps:
* @msg: a #GstMIKEYMessage
* @caps: a #GstCaps to be filled with SRTP parameters (srtp/srtcp cipher, authorization, key data)
*
* Returns: %TRUE on success
*
* Since: 1.8.1
*/
gboolean
gst_mikey_message_to_caps (const GstMIKEYMessage * msg, GstCaps * caps)
{
gboolean res = FALSE;
const GstMIKEYPayload *payload;
const gchar *srtp_cipher;
const gchar *srtp_auth;
srtp_cipher = "aes-128-icm";
srtp_auth = "hmac-sha1-80";
/* check the Security policy if any */
if ((payload = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_SP, 0))) {
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
guint len, i;
guint enc_alg = GST_MIKEY_ENC_NULL;
if (p->proto != GST_MIKEY_SEC_PROTO_SRTP)
goto done;
len = gst_mikey_payload_sp_get_n_params (payload);
for (i = 0; i < len; i++) {
const GstMIKEYPayloadSPParam *param =
gst_mikey_payload_sp_get_param (payload, i);
switch (param->type) {
case GST_MIKEY_SP_SRTP_ENC_ALG:
enc_alg = param->val[0];
switch (param->val[0]) {
case GST_MIKEY_ENC_NULL:
srtp_cipher = "null";
break;
case GST_MIKEY_ENC_AES_CM_128:
case GST_MIKEY_ENC_AES_KW_128:
srtp_cipher = "aes-128-icm";
break;
case GST_MIKEY_ENC_AES_GCM_128:
srtp_cipher = "aes-128-gcm";
break;
default:
break;
}
break;
case GST_MIKEY_SP_SRTP_ENC_KEY_LEN:
switch (param->val[0]) {
case AES_128_KEY_LEN:
if (enc_alg == GST_MIKEY_ENC_AES_CM_128 ||
enc_alg == GST_MIKEY_ENC_AES_KW_128) {
srtp_cipher = "aes-128-icm";
} else if (enc_alg == GST_MIKEY_ENC_AES_GCM_128) {
srtp_cipher = "aes-128-gcm";
}
break;
case AES_256_KEY_LEN:
if (enc_alg == GST_MIKEY_ENC_AES_CM_128 ||
enc_alg == GST_MIKEY_ENC_AES_KW_128) {
srtp_cipher = "aes-256-icm";
} else if (enc_alg == GST_MIKEY_ENC_AES_GCM_128) {
srtp_cipher = "aes-256-gcm";
}
break;
default:
break;
}
break;
case GST_MIKEY_SP_SRTP_AUTH_ALG:
switch (param->val[0]) {
case GST_MIKEY_MAC_NULL:
srtp_auth = "null";
break;
case GST_MIKEY_MAC_HMAC_SHA_1_160:
srtp_auth = "hmac-sha1-80";
break;
default:
break;
}
break;
case GST_MIKEY_SP_SRTP_AUTH_KEY_LEN:
switch (param->val[0]) {
case HMAC_32_KEY_LEN:
srtp_auth = "hmac-sha1-32";
break;
case HMAC_80_KEY_LEN:
srtp_auth = "hmac-sha1-80";
break;
default:
break;
}
break;
case GST_MIKEY_SP_SRTP_SRTP_ENC:
break;
case GST_MIKEY_SP_SRTP_SRTCP_ENC:
break;
default:
break;
}
}
}
if (!(payload = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_KEMAC, 0)))
goto done;
else {
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
const GstMIKEYPayload *sub;
GstMIKEYPayloadKeyData *pkd;
GstBuffer *buf;
if (p->enc_alg != GST_MIKEY_ENC_NULL || p->mac_alg != GST_MIKEY_MAC_NULL)
goto done;
if (!(sub = gst_mikey_payload_kemac_get_sub (payload, 0)))
goto done;
if (sub->type != GST_MIKEY_PT_KEY_DATA)
goto done;
pkd = (GstMIKEYPayloadKeyData *) sub;
buf = gst_buffer_new_memdup (pkd->key_data, pkd->key_len);
gst_caps_set_simple (caps, "srtp-key", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
}
gst_caps_set_simple (caps,
"srtp-cipher", G_TYPE_STRING, srtp_cipher,
"srtp-auth", G_TYPE_STRING, srtp_auth,
"srtcp-cipher", G_TYPE_STRING, srtp_cipher,
"srtcp-auth", G_TYPE_STRING, srtp_auth, NULL);
res = TRUE;
done:
return res;
}
/**
* gst_mikey_message_base64_encode:
* @msg: a #GstMIKEYMessage
*
* Returns: (transfer full): a #gchar, base64-encoded data
*
* Since: 1.8
*/
gchar *
gst_mikey_message_base64_encode (GstMIKEYMessage * msg)
{
GBytes *bytes;
gchar *base64;
const guint8 *data;
gsize size;
g_return_val_if_fail (msg != NULL, NULL);
/* serialize mikey message to bytes */
bytes = gst_mikey_message_to_bytes (msg, NULL, NULL);
/* and make it into base64 */
data = g_bytes_get_data (bytes, &size);
base64 = g_base64_encode (data, size);
g_bytes_unref (bytes);
return base64;
}