mirror of https://github.com/FFmpeg/FFmpeg.git
* commit 'ab2ad8bd56882c0ea160b154e8b836eb71abc49d': lavf: Add functions for SRTP decryption/encryption lavu: Add an API for calculating HMAC (RFC 2104) Conflicts: doc/APIchanges libavutil/version.h Merged-by: Michael Niedermayer <michaelni@gmx.at>pull/8/head
commit
e7e0186eeb
7 changed files with 584 additions and 1 deletions
@ -0,0 +1,293 @@ |
||||
/*
|
||||
* SRTP encryption/decryption |
||||
* Copyright (c) 2012 Martin Storsjo |
||||
* |
||||
* This file is part of FFmpeg. |
||||
* |
||||
* FFmpeg is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* FFmpeg 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 |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with FFmpeg; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "libavutil/base64.h" |
||||
#include "libavutil/aes.h" |
||||
#include "libavutil/hmac.h" |
||||
#include "libavutil/intreadwrite.h" |
||||
#include "libavutil/log.h" |
||||
#include "rtp.h" |
||||
#include "srtp.h" |
||||
|
||||
void ff_srtp_free(struct SRTPContext *s) |
||||
{ |
||||
if (!s) |
||||
return; |
||||
av_freep(&s->aes); |
||||
if (s->hmac) |
||||
av_hmac_free(s->hmac); |
||||
s->hmac = NULL; |
||||
} |
||||
|
||||
static void encrypt_counter(struct AVAES *aes, uint8_t *iv, uint8_t *outbuf, |
||||
int outlen) |
||||
{ |
||||
int i, j, outpos; |
||||
for (i = 0, outpos = 0; outpos < outlen; i++) { |
||||
uint8_t keystream[16]; |
||||
AV_WB16(&iv[14], i); |
||||
av_aes_crypt(aes, keystream, iv, 1, NULL, 0); |
||||
for (j = 0; j < 16 && outpos < outlen; j++, outpos++) |
||||
outbuf[outpos] ^= keystream[j]; |
||||
} |
||||
} |
||||
|
||||
static void derive_key(struct AVAES *aes, const uint8_t *salt, int label, |
||||
uint8_t *out, int outlen) |
||||
{ |
||||
uint8_t input[16] = { 0 }; |
||||
memcpy(input, salt, 14); |
||||
// Key derivation rate assumed to be zero
|
||||
input[14 - 7] ^= label; |
||||
memset(out, 0, outlen); |
||||
encrypt_counter(aes, input, out, outlen); |
||||
} |
||||
|
||||
int ff_srtp_set_crypto(struct SRTPContext *s, const char *suite, |
||||
const char *params) |
||||
{ |
||||
uint8_t buf[30]; |
||||
|
||||
ff_srtp_free(s); |
||||
|
||||
// RFC 4568
|
||||
if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80")) { |
||||
s->hmac_size = 10; |
||||
} else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) { |
||||
s->hmac_size = 4; |
||||
} else { |
||||
av_log(NULL, AV_LOG_WARNING, "SRTP Crypto suite %s not supported\n", |
||||
suite); |
||||
return AVERROR(EINVAL); |
||||
} |
||||
if (av_base64_decode(buf, params, sizeof(buf)) != sizeof(buf)) { |
||||
av_log(NULL, AV_LOG_WARNING, "Incorrect amount of SRTP params\n"); |
||||
return AVERROR(EINVAL); |
||||
} |
||||
// MKI and lifetime not handled yet
|
||||
s->aes = av_aes_alloc(); |
||||
s->hmac = av_hmac_alloc(AV_HMAC_SHA1); |
||||
if (!s->aes || !s->hmac) |
||||
return AVERROR(ENOMEM); |
||||
memcpy(s->master_key, buf, 16); |
||||
memcpy(s->master_salt, buf + 16, 14); |
||||
|
||||
// RFC 3711
|
||||
av_aes_init(s->aes, s->master_key, 128, 0); |
||||
|
||||
derive_key(s->aes, s->master_salt, 0x00, s->rtp_key, sizeof(s->rtp_key)); |
||||
derive_key(s->aes, s->master_salt, 0x02, s->rtp_salt, sizeof(s->rtp_salt)); |
||||
derive_key(s->aes, s->master_salt, 0x01, s->rtp_auth, sizeof(s->rtp_auth)); |
||||
|
||||
derive_key(s->aes, s->master_salt, 0x03, s->rtcp_key, sizeof(s->rtcp_key)); |
||||
derive_key(s->aes, s->master_salt, 0x05, s->rtcp_salt, sizeof(s->rtcp_salt)); |
||||
derive_key(s->aes, s->master_salt, 0x04, s->rtcp_auth, sizeof(s->rtcp_auth)); |
||||
return 0; |
||||
} |
||||
|
||||
static void create_iv(uint8_t *iv, const uint8_t *salt, uint64_t index, |
||||
uint32_t ssrc) |
||||
{ |
||||
uint8_t indexbuf[8]; |
||||
int i; |
||||
memset(iv, 0, 16); |
||||
AV_WB32(&iv[4], ssrc); |
||||
AV_WB64(indexbuf, index); |
||||
for (i = 0; i < 8; i++) // index << 16
|
||||
iv[6 + i] ^= indexbuf[i]; |
||||
for (i = 0; i < 14; i++) |
||||
iv[i] ^= salt[i]; |
||||
} |
||||
|
||||
int ff_srtp_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr) |
||||
{ |
||||
uint8_t iv[16] = { 0 }, hmac[20]; |
||||
int len = *lenptr; |
||||
int ext, seq_largest; |
||||
uint32_t ssrc, roc; |
||||
uint64_t index; |
||||
int rtcp; |
||||
|
||||
// TODO: Missing replay protection
|
||||
|
||||
if (len < s->hmac_size) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
rtcp = RTP_PT_IS_RTCP(buf[1]); |
||||
|
||||
// Authentication HMAC
|
||||
av_hmac_init(s->hmac, rtcp ? s->rtcp_auth : s->rtp_auth, sizeof(s->rtp_auth)); |
||||
// If MKI is used, this should exclude the MKI as well
|
||||
av_hmac_update(s->hmac, buf, len - s->hmac_size); |
||||
|
||||
if (!rtcp) { |
||||
int seq = AV_RB16(buf + 2); |
||||
uint32_t v; |
||||
uint8_t rocbuf[4]; |
||||
|
||||
// RFC 3711 section 3.3.1, appendix A
|
||||
seq_largest = s->seq_initialized ? s->seq_largest : seq; |
||||
v = roc = s->roc; |
||||
if (seq_largest < 32768) { |
||||
if (seq - seq_largest > 32768) |
||||
v = roc - 1; |
||||
} else { |
||||
if (seq_largest - 32768 > seq) |
||||
v = roc + 1; |
||||
} |
||||
if (v == roc) { |
||||
seq_largest = FFMAX(seq_largest, seq); |
||||
} else if (v == roc + 1) { |
||||
seq_largest = seq; |
||||
roc = v; |
||||
} |
||||
index = seq + (((uint64_t)v) << 16); |
||||
|
||||
AV_WB32(rocbuf, roc); |
||||
av_hmac_update(s->hmac, rocbuf, 4); |
||||
} |
||||
|
||||
av_hmac_final(s->hmac, hmac, sizeof(hmac)); |
||||
if (memcmp(hmac, buf + len - s->hmac_size, s->hmac_size)) { |
||||
av_log(NULL, AV_LOG_WARNING, "HMAC mismatch\n"); |
||||
return AVERROR_INVALIDDATA; |
||||
} |
||||
|
||||
len -= s->hmac_size; |
||||
*lenptr = len; |
||||
|
||||
if (len < 12) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
if (rtcp) { |
||||
uint32_t srtcp_index = AV_RB32(buf + len - 4); |
||||
len -= 4; |
||||
*lenptr = len; |
||||
|
||||
ssrc = AV_RB32(buf + 4); |
||||
index = srtcp_index & 0x7fffffff; |
||||
|
||||
buf += 8; |
||||
len -= 8; |
||||
if (!(srtcp_index & 0x80000000)) |
||||
return 0; |
||||
} else { |
||||
s->seq_initialized = 1; |
||||
s->seq_largest = seq_largest; |
||||
s->roc = roc; |
||||
|
||||
ext = buf[0] & 0x10; |
||||
ssrc = AV_RB32(buf + 8); |
||||
|
||||
buf += 12; |
||||
len -= 12; |
||||
|
||||
if (ext) { |
||||
if (len < 4) |
||||
return AVERROR_INVALIDDATA; |
||||
ext = (AV_RB16(buf + 2) + 1) * 4; |
||||
if (len < ext) |
||||
return AVERROR_INVALIDDATA; |
||||
len -= ext; |
||||
buf += ext; |
||||
} |
||||
} |
||||
|
||||
create_iv(iv, rtcp ? s->rtcp_salt : s->rtp_salt, index, ssrc); |
||||
av_aes_init(s->aes, rtcp ? s->rtcp_key : s->rtp_key, 128, 0); |
||||
encrypt_counter(s->aes, iv, buf, len); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int ff_srtp_encrypt(struct SRTPContext *s, const uint8_t *in, int len, |
||||
uint8_t *out, int outlen) |
||||
{ |
||||
uint8_t iv[16] = { 0 }, hmac[20]; |
||||
uint64_t index; |
||||
uint32_t ssrc; |
||||
int rtcp; |
||||
uint8_t *buf; |
||||
|
||||
if (len + 14 > outlen) |
||||
return 0; |
||||
if (len < 12) |
||||
return 0; |
||||
|
||||
memcpy(out, in, len); |
||||
buf = out; |
||||
|
||||
rtcp = RTP_PT_IS_RTCP(buf[1]); |
||||
|
||||
if (rtcp) { |
||||
ssrc = AV_RB32(buf + 4); |
||||
index = s->rtcp_index++; |
||||
|
||||
buf += 8; |
||||
len -= 8; |
||||
} else { |
||||
int ext; |
||||
int seq = AV_RB16(buf + 2); |
||||
ssrc = AV_RB32(buf + 8); |
||||
|
||||
if (seq < s->seq_largest) |
||||
s->roc++; |
||||
s->seq_largest = seq; |
||||
index = seq + (((uint64_t)s->roc) << 16); |
||||
|
||||
ext = buf[0] & 0x10; |
||||
|
||||
buf += 12; |
||||
len -= 12; |
||||
|
||||
if (ext) { |
||||
if (len < 4) |
||||
return AVERROR_INVALIDDATA; |
||||
ext = (AV_RB16(buf + 2) + 1) * 4; |
||||
if (len < ext) |
||||
return AVERROR_INVALIDDATA; |
||||
len -= ext; |
||||
buf += ext; |
||||
} |
||||
} |
||||
|
||||
create_iv(iv, rtcp ? s->rtcp_salt : s->rtp_salt, index, ssrc); |
||||
av_aes_init(s->aes, rtcp ? s->rtcp_key : s->rtp_key, 128, 0); |
||||
encrypt_counter(s->aes, iv, buf, len); |
||||
|
||||
if (rtcp) { |
||||
AV_WB32(buf + len, 0x80000000 | index); |
||||
len += 4; |
||||
} |
||||
|
||||
av_hmac_init(s->hmac, rtcp ? s->rtcp_auth : s->rtp_auth, sizeof(s->rtp_auth)); |
||||
av_hmac_update(s->hmac, out, buf + len - out); |
||||
if (!rtcp) { |
||||
uint8_t rocbuf[4]; |
||||
AV_WB32(rocbuf, s->roc); |
||||
av_hmac_update(s->hmac, rocbuf, 4); |
||||
} |
||||
av_hmac_final(s->hmac, hmac, sizeof(hmac)); |
||||
|
||||
memcpy(buf + len, hmac, s->hmac_size); |
||||
len += s->hmac_size; |
||||
return buf + len - out; |
||||
} |
@ -0,0 +1,52 @@ |
||||
/*
|
||||
* SRTP encryption/decryption |
||||
* Copyright (c) 2012 Martin Storsjo |
||||
* |
||||
* This file is part of FFmpeg. |
||||
* |
||||
* FFmpeg is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* FFmpeg 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 |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with FFmpeg; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef AVFORMAT_SRTP_H |
||||
#define AVFORMAT_SRTP_H |
||||
|
||||
#include <stdint.h> |
||||
|
||||
struct AVAES; |
||||
struct AVHMAC; |
||||
|
||||
struct SRTPContext { |
||||
struct AVAES *aes; |
||||
struct AVHMAC *hmac; |
||||
int hmac_size; |
||||
uint8_t master_key[16]; |
||||
uint8_t master_salt[14]; |
||||
uint8_t rtp_key[16], rtcp_key[16]; |
||||
uint8_t rtp_salt[14], rtcp_salt[14]; |
||||
uint8_t rtp_auth[20], rtcp_auth[20]; |
||||
int seq_largest, seq_initialized; |
||||
uint32_t roc; |
||||
|
||||
uint32_t rtcp_index; |
||||
}; |
||||
|
||||
int ff_srtp_set_crypto(struct SRTPContext *s, const char *suite, |
||||
const char *params); |
||||
void ff_srtp_free(struct SRTPContext *s); |
||||
int ff_srtp_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr); |
||||
int ff_srtp_encrypt(struct SRTPContext *s, const uint8_t *in, int len, |
||||
uint8_t *out, int outlen); |
||||
|
||||
#endif /* AVFORMAT_SRTP_H */ |
@ -0,0 +1,138 @@ |
||||
/*
|
||||
* Copyright (C) 2012 Martin Storsjo |
||||
* |
||||
* This file is part of FFmpeg. |
||||
* |
||||
* FFmpeg is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* FFmpeg 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 |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with FFmpeg; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include <string.h> |
||||
|
||||
#include "hmac.h" |
||||
#include "md5.h" |
||||
#include "sha.h" |
||||
#include "mem.h" |
||||
|
||||
#define MAX_HASHLEN 20 |
||||
#define MAX_BLOCKLEN 64 |
||||
|
||||
struct AVHMAC { |
||||
void *hash; |
||||
int blocklen, hashlen; |
||||
void (*final)(void*, uint8_t*); |
||||
void (*update)(void*, const uint8_t*, int len); |
||||
void (*init)(void*); |
||||
uint8_t key[MAX_BLOCKLEN]; |
||||
int keylen; |
||||
}; |
||||
|
||||
static void sha1_init(void *ctx) |
||||
{ |
||||
av_sha_init(ctx, 160); |
||||
} |
||||
|
||||
AVHMAC *av_hmac_alloc(enum AVHMACType type) |
||||
{ |
||||
AVHMAC *c = av_mallocz(sizeof(*c)); |
||||
if (!c) |
||||
return NULL; |
||||
switch (type) { |
||||
case AV_HMAC_MD5: |
||||
c->blocklen = 64; |
||||
c->hashlen = 16; |
||||
c->init = av_md5_init; |
||||
c->update = av_md5_update; |
||||
c->final = av_md5_final; |
||||
c->hash = av_md5_alloc(); |
||||
break; |
||||
case AV_HMAC_SHA1: |
||||
c->blocklen = 64; |
||||
c->hashlen = 20; |
||||
c->init = sha1_init; |
||||
c->update = av_sha_update; |
||||
c->final = av_sha_final; |
||||
c->hash = av_sha_alloc(); |
||||
break; |
||||
default: |
||||
av_free(c); |
||||
return NULL; |
||||
} |
||||
if (!c->hash) { |
||||
av_free(c); |
||||
return NULL; |
||||
} |
||||
return c; |
||||
} |
||||
|
||||
void av_hmac_free(AVHMAC *c) |
||||
{ |
||||
if (!c) |
||||
return; |
||||
av_free(c->hash); |
||||
av_free(c); |
||||
} |
||||
|
||||
void av_hmac_init(AVHMAC *c, const uint8_t *key, unsigned int keylen) |
||||
{ |
||||
int i; |
||||
uint8_t block[MAX_BLOCKLEN]; |
||||
if (keylen > c->blocklen) { |
||||
c->init(c->hash); |
||||
c->update(c->hash, key, keylen); |
||||
c->final(c->hash, c->key); |
||||
c->keylen = c->hashlen; |
||||
} else { |
||||
memcpy(c->key, key, keylen); |
||||
c->keylen = keylen; |
||||
} |
||||
c->init(c->hash); |
||||
for (i = 0; i < c->keylen; i++) |
||||
block[i] = c->key[i] ^ 0x36; |
||||
for (i = c->keylen; i < c->blocklen; i++) |
||||
block[i] = 0x36; |
||||
c->update(c->hash, block, c->blocklen); |
||||
} |
||||
|
||||
void av_hmac_update(AVHMAC *c, const uint8_t *data, unsigned int len) |
||||
{ |
||||
c->update(c->hash, data, len); |
||||
} |
||||
|
||||
int av_hmac_final(AVHMAC *c, uint8_t *out, unsigned int outlen) |
||||
{ |
||||
uint8_t block[MAX_BLOCKLEN]; |
||||
int i; |
||||
if (outlen < c->hashlen) |
||||
return AVERROR(EINVAL); |
||||
c->final(c->hash, out); |
||||
c->init(c->hash); |
||||
for (i = 0; i < c->keylen; i++) |
||||
block[i] = c->key[i] ^ 0x5C; |
||||
for (i = c->keylen; i < c->blocklen; i++) |
||||
block[i] = 0x5C; |
||||
c->update(c->hash, block, c->blocklen); |
||||
c->update(c->hash, out, c->hashlen); |
||||
c->final(c->hash, out); |
||||
return c->hashlen; |
||||
} |
||||
|
||||
int av_hmac_calc(AVHMAC *c, const uint8_t *data, unsigned int len, |
||||
const uint8_t *key, unsigned int keylen, |
||||
uint8_t *out, unsigned int outlen) |
||||
{ |
||||
av_hmac_init(c, key, keylen); |
||||
av_hmac_update(c, data, len); |
||||
return av_hmac_final(c, out, outlen); |
||||
} |
@ -0,0 +1,95 @@ |
||||
/*
|
||||
* Copyright (C) 2012 Martin Storsjo |
||||
* |
||||
* This file is part of FFmpeg. |
||||
* |
||||
* FFmpeg is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* FFmpeg 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 |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with FFmpeg; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#ifndef AVUTIL_HMAC_H |
||||
#define AVUTIL_HMAC_H |
||||
|
||||
#include <stdint.h> |
||||
|
||||
/**
|
||||
* @defgroup lavu_hmac HMAC |
||||
* @ingroup lavu_crypto |
||||
* @{ |
||||
*/ |
||||
|
||||
enum AVHMACType { |
||||
AV_HMAC_MD5, |
||||
AV_HMAC_SHA1, |
||||
}; |
||||
|
||||
typedef struct AVHMAC AVHMAC; |
||||
|
||||
/**
|
||||
* Allocate an AVHMAC context. |
||||
* @param type The hash function used for the HMAC. |
||||
*/ |
||||
AVHMAC *av_hmac_alloc(enum AVHMACType type); |
||||
|
||||
/**
|
||||
* Free an AVHMAC context. |
||||
* @param ctx The context to free, may be NULL |
||||
*/ |
||||
void av_hmac_free(AVHMAC *ctx); |
||||
|
||||
/**
|
||||
* Initialize an AVHMAC context with an authentication key. |
||||
* @param ctx The HMAC context |
||||
* @param key The authentication key |
||||
* @param keylen The length of the key, in bytes |
||||
*/ |
||||
void av_hmac_init(AVHMAC *ctx, const uint8_t *key, unsigned int keylen); |
||||
|
||||
/**
|
||||
* Hash data with the HMAC. |
||||
* @param ctx The HMAC context |
||||
* @param data The data to hash |
||||
* @param len The length of the data, in bytes |
||||
*/ |
||||
void av_hmac_update(AVHMAC *ctx, const uint8_t *data, unsigned int len); |
||||
|
||||
/**
|
||||
* Finish hashing and output the HMAC digest. |
||||
* @param ctx The HMAC context |
||||
* @param out The output buffer to write the digest into |
||||
* @param outlen The length of the out buffer, in bytes |
||||
* @return The number of bytes written to out, or a negative error code. |
||||
*/ |
||||
int av_hmac_final(AVHMAC *ctx, uint8_t *out, unsigned int outlen); |
||||
|
||||
/**
|
||||
* Hash an array of data with a key. |
||||
* @param ctx The HMAC context |
||||
* @param data The data to hash |
||||
* @param len The length of the data, in bytes |
||||
* @param key The authentication key |
||||
* @param keylen The length of the key, in bytes |
||||
* @param out The output buffer to write the digest into |
||||
* @param outlen The length of the out buffer, in bytes |
||||
* @return The number of bytes written to out, or a negative error code. |
||||
*/ |
||||
int av_hmac_calc(AVHMAC *ctx, const uint8_t *data, unsigned int len, |
||||
const uint8_t *key, unsigned int keylen, |
||||
uint8_t *out, unsigned int outlen); |
||||
|
||||
/**
|
||||
* @} |
||||
*/ |
||||
|
||||
#endif /* AVUTIL_HMAC_H */ |
Loading…
Reference in new issue