static void run_lavu_aes128(uint8_t *output, const uint8_t *input, unsigned size) { static struct AVAES *aes; if (!aes && !(aes = av_aes_alloc())) fatal_error("out of memory"); av_aes_init(aes, hardcoded_key, 128, 0); av_aes_crypt(aes, output, input, size >> 4, NULL, 0); }
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") || !strcmp(suite, "SRTP_AES128_CM_HMAC_SHA1_80")) { s->rtp_hmac_size = s->rtcp_hmac_size = 10; } else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) { s->rtp_hmac_size = s->rtcp_hmac_size = 4; } else if (!strcmp(suite, "SRTP_AES128_CM_HMAC_SHA1_32")) { // RFC 5764 section 4.1.2 s->rtp_hmac_size = 4; s->rtcp_hmac_size = 10; } 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 int crypto_open(URLContext *h, const char *uri, int flags) { const char *nested_url; int ret = 0; CryptoContext *c = h->priv_data; if (!av_strstart(uri, "crypto+", &nested_url) && !av_strstart(uri, "crypto:", &nested_url)) { av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri); ret = AVERROR(EINVAL); goto err; } if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) { av_log(h, AV_LOG_ERROR, "Key or IV not set\n"); ret = AVERROR(EINVAL); goto err; } if (flags & AVIO_FLAG_WRITE) { av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n"); ret = AVERROR(ENOSYS); goto err; } if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ, &h->interrupt_callback, NULL, h->protocols, h)) < 0) { av_log(h, AV_LOG_ERROR, "Unable to open input\n"); goto err; } c->aes = av_aes_alloc(); if (!c->aes) { ret = AVERROR(ENOMEM); goto err; } av_aes_init(c->aes, c->key, 128, 1); h->is_streamed = 1; err: return ret; }
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, hmac_size, padding; uint8_t *buf; if (len < 8) return AVERROR_INVALIDDATA; rtcp = RTP_PT_IS_RTCP(in[1]); hmac_size = rtcp ? s->rtcp_hmac_size : s->rtp_hmac_size; padding = hmac_size; if (rtcp) padding += 4; // For the RTCP index if (len + padding > outlen) return 0; memcpy(out, in, len); buf = out; if (rtcp) { ssrc = AV_RB32(buf + 4); index = s->rtcp_index++; buf += 8; len -= 8; } else { int ext, csrc; int seq = AV_RB16(buf + 2); if (len < 12) return AVERROR_INVALIDDATA; ssrc = AV_RB32(buf + 8); if (seq < s->seq_largest) s->roc++; s->seq_largest = seq; index = seq + (((uint64_t)s->roc) << 16); csrc = buf[0] & 0x0f; ext = buf[0] & 0x10; buf += 12; len -= 12; buf += 4 * csrc; len -= 4 * csrc; if (len < 0) return AVERROR_INVALIDDATA; 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, hmac_size); len += hmac_size; return buf + len - out; }
int ff_srtp_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr) { uint8_t iv[16] = { 0 }, hmac[20]; int len = *lenptr; int av_uninit(seq_largest); uint32_t ssrc, av_uninit(roc); uint64_t index; int rtcp, hmac_size; // TODO: Missing replay protection if (len < 2) return AVERROR_INVALIDDATA; rtcp = RTP_PT_IS_RTCP(buf[1]); hmac_size = rtcp ? s->rtcp_hmac_size : s->rtp_hmac_size; if (len < hmac_size) return AVERROR_INVALIDDATA; // 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 - 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 - hmac_size, hmac_size)) { av_log(NULL, AV_LOG_WARNING, "HMAC mismatch\n"); return AVERROR_INVALIDDATA; } len -= 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 { int ext, csrc; s->seq_initialized = 1; s->seq_largest = seq_largest; s->roc = roc; csrc = buf[0] & 0x0f; ext = buf[0] & 0x10; ssrc = AV_RB32(buf + 8); buf += 12; len -= 12; buf += 4 * csrc; len -= 4 * csrc; if (len < 0) return AVERROR_INVALIDDATA; 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; }
static int list_read(URLContext *h, unsigned char *buf, int size) { struct list_mgt *mgt = h->priv_data; int len=AVERROR(EIO); struct list_item *item=mgt->current_item; int retries = 10; retry: if (url_interrupt_cb()){ av_log(NULL, AV_LOG_ERROR," url_interrupt_cb\n"); return AVERROR(EINTR); } //av_log(NULL, AV_LOG_INFO, "list_read start buf=%x,size=%d\n",buf,size); if(!mgt->cur_uio ) { if(item && item->file) { ByteIOContext *bio; av_log(NULL, AV_LOG_INFO, "list_read switch to new file=%s\n",item->file); len=url_fopen(&bio,item->file,AVIO_FLAG_READ | URL_MINI_BUFFER | URL_NO_LP_BUFFER); if(len!=0) { av_log(NULL, AV_LOG_ERROR, "list url_fopen failed =%d\n",len); return len; } if(url_is_file_list(bio,item->file)) { mgt->have_sub_list = 1; /*have 32 bytes space at the end..*/ memmove(item->file+5,item->file,strlen(item->file)+1); memcpy(item->file,"list:",5); url_fclose(bio); len=url_fopen(&bio,item->file,mgt->flags | URL_MINI_BUFFER | URL_NO_LP_BUFFER); if(len!=0) { av_log(NULL, AV_LOG_INFO, "file list url_fopen failed =%d\n",len); return len; } }else{ if(item->ktype == KEY_AES_128){ if(item->key_ctx&&!item->crypto){ item->crypto = av_mallocz(sizeof(struct AESCryptoContext)); if(!item->crypto){ len = AVERROR(ENOMEM); return len; } item->crypto->aes = av_mallocz(av_aes_size); if(!item->crypto->aes){ len = AVERROR(ENOMEM); return len; } av_aes_init(item->crypto->aes,item->key_ctx->key, 128, 1); item->crypto->have_init = 1; } bio->is_encrypted_media =1; } } mgt->cur_uio=bio; } } if(mgt->cur_uio){ if(size>0&&mgt->cur_uio->is_encrypted_media>0&&mgt->current_item->key_ctx&&mgt->current_item->crypto){//codes from crypto.c int blocks; struct AESCryptoContext* c = mgt->current_item->crypto; readagain: len = 0; if (c->outdata > 0) { size = FFMIN(size, c->outdata); memcpy(buf, c->outptr, size); c->outptr += size; c->outdata -= size; len =size; } // We avoid using the last block until we've found EOF, // since we'll remove PKCS7 padding at the end. So make // sure we've got at least 2 blocks, so we can decrypt // at least one. while (c->indata - c->indata_used < 2*BLOCKSIZE) { int n = get_buffer(mgt->cur_uio,c->inbuffer + c->indata, sizeof(c->inbuffer) - c->indata); if (n <= 0) { c->eof = 1; break; } c->indata += n; } blocks = (c->indata - c->indata_used) / BLOCKSIZE; if (blocks!=0){ if (!c->eof) blocks--; av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks,mgt->current_item->key_ctx->iv, 1); c->outdata = BLOCKSIZE * blocks; c->outptr = c->outbuffer; c->indata_used += BLOCKSIZE * blocks; if (c->indata_used >= sizeof(c->inbuffer)/2) { memmove(c->inbuffer, c->inbuffer + c->indata_used, c->indata - c->indata_used); c->indata -= c->indata_used; c->indata_used = 0; } if (c->eof) { // Remove PKCS7 padding at the end int padding = c->outbuffer[c->outdata - 1]; c->outdata -= padding; } if(len==0){ goto readagain; } }else{ len =0; } }else{ len=get_buffer(mgt->cur_uio,buf,size); } //av_log(NULL, AV_LOG_INFO, "list_read get_buffer=%d\n",len); } if(len==AVERROR(EAGAIN)) return AVERROR(EAGAIN);/*not end,bug need to*/ else if((len<=0)&& mgt->current_item!=NULL) {/*end of the file*/ av_log(NULL, AV_LOG_INFO, "try switchto_next_item buf=%x,size=%d,len=%d\n",buf,size,len); if(item && (item->flags & ENDLIST_FLAG)){ if(mgt->cur_uio) url_fclose(mgt->cur_uio); av_log(NULL, AV_LOG_INFO, "ENDLIST_FLAG, return 0\n"); return 0; } item=switchto_next_item(mgt); if(!item){ if(mgt->flags&REAL_STREAMING_FLAG){ fresh_item_list(mgt); if(retries>0){ retries --; goto retry; } } av_log(NULL, AV_LOG_INFO, "Need more retry by player logic\n"); return AVERROR(EAGAIN);/*not end,but need to refresh the list later*/ } if(mgt->cur_uio) url_fclose(mgt->cur_uio); mgt->cur_uio=NULL; mgt->current_item=item; if(item->flags & ENDLIST_FLAG){ av_log(NULL, AV_LOG_INFO, "reach list end now!,item=%x\n",item); return 0;/*endof file*/ } else if(item->flags & DISCONTINUE_FLAG){ av_log(NULL, AV_LOG_INFO, "Discontiue item \n"); //1 TODO:need to notify uper level stream is changed goto retry; } else{ av_log(NULL, AV_LOG_INFO, "[%s]item->flags=%x \n", __FUNCTION__, item->flags); goto retry; } } #if 0 if(mgt->flags&REAL_STREAMING_FLAG&&mgt->item_num<4){ //force refresh items fresh_item_list(mgt); } #endif //av_log(NULL, AV_LOG_INFO, "list_read end buf=%x,size=%d return len=%x\n",buf,size,len); return len; }
int main(int argc, char **argv) { int i, j; AVAES b; static const uint8_t rkey[2][16] = { { 0 }, { 0x10, 0xa5, 0x88, 0x69, 0xd7, 0x4b, 0xe5, 0xa3, 0x74, 0xcf, 0x86, 0x7c, 0xfb, 0x47, 0x38, 0x59 } }; static const uint8_t rpt[2][16] = { { 0x6a, 0x84, 0x86, 0x7c, 0xd7, 0x7e, 0x12, 0xad, 0x07, 0xea, 0x1b, 0xe8, 0x95, 0xc5, 0x3f, 0xa3 }, { 0 } }; static const uint8_t rct[2][16] = { { 0x73, 0x22, 0x81, 0xc0, 0xa0, 0xaa, 0xb8, 0xf7, 0xa5, 0x4a, 0x0c, 0x67, 0xa0, 0xc4, 0x5e, 0xcf }, { 0x6d, 0x25, 0x1e, 0x69, 0x44, 0xb0, 0x51, 0xe0, 0x4e, 0xaa, 0x6f, 0xb4, 0xdb, 0xf7, 0x84, 0x65 } }; uint8_t pt[16], temp[16]; int err = 0; av_log_set_level(AV_LOG_DEBUG); for (i = 0; i < 2; i++) { av_aes_init(&b, rkey[i], 128, 1); av_aes_crypt(&b, temp, rct[i], 1, NULL, 1); for (j = 0; j < 16; j++) { if (rpt[i][j] != temp[j]) { av_log(NULL, AV_LOG_ERROR, "%d %02X %02X\n", j, rpt[i][j], temp[j]); err = 1; } } } if (argc > 1 && !strcmp(argv[1], "-t")) { AVAES ae, ad; AVLFG prng; av_aes_init(&ae, "PI=3.141592654..", 128, 0); av_aes_init(&ad, "PI=3.141592654..", 128, 1); av_lfg_init(&prng, 1); for (i = 0; i < 10000; i++) { for (j = 0; j < 16; j++) pt[j] = av_lfg_get(&prng); { START_TIMER; av_aes_crypt(&ae, temp, pt, 1, NULL, 0); if (!(i & (i - 1))) av_log(NULL, AV_LOG_ERROR, "%02X %02X %02X %02X\n", temp[0], temp[5], temp[10], temp[15]); av_aes_crypt(&ad, temp, temp, 1, NULL, 1); STOP_TIMER("aes"); } for (j = 0; j < 16; j++) { if (pt[j] != temp[j]) { av_log(NULL, AV_LOG_ERROR, "%d %d %02X %02X\n", i, j, pt[j], temp[j]); } } } } return err; }
static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options) { const char *nested_url; int ret = 0; CryptoContext *c = h->priv_data; c->flags = flags; if (!av_strstart(uri, "crypto+", &nested_url) && !av_strstart(uri, "crypto:", &nested_url)) { av_log(h, AV_LOG_ERROR, "Unsupported url %s\n", uri); ret = AVERROR(EINVAL); goto err; } c->position = 0; if (flags & AVIO_FLAG_READ) { if ((ret = set_aes_arg(c, &c->decrypt_key, &c->decrypt_keylen, c->key, c->keylen, "decryption key")) < 0) goto err; if ((ret = set_aes_arg(c, &c->decrypt_iv, &c->decrypt_ivlen, c->iv, c->ivlen, "decryption IV")) < 0) goto err; } if (flags & AVIO_FLAG_WRITE) { if ((ret = set_aes_arg(c, &c->encrypt_key, &c->encrypt_keylen, c->key, c->keylen, "encryption key")) < 0) if (ret < 0) goto err; if ((ret = set_aes_arg(c, &c->encrypt_iv, &c->encrypt_ivlen, c->iv, c->ivlen, "encryption IV")) < 0) goto err; } if ((ret = ffurl_open_whitelist(&c->hd, nested_url, flags, &h->interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist, h)) < 0) { av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url); goto err; } if (flags & AVIO_FLAG_READ) { c->aes_decrypt = av_aes_alloc(); if (!c->aes_decrypt) { ret = AVERROR(ENOMEM); goto err; } ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE*8, 1); if (ret < 0) goto err; // pass back information about the context we openned if (c->hd->is_streamed) h->is_streamed = c->hd->is_streamed; } if (flags & AVIO_FLAG_WRITE) { c->aes_encrypt = av_aes_alloc(); if (!c->aes_encrypt) { ret = AVERROR(ENOMEM); goto err; } ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE*8, 0); if (ret < 0) goto err; // for write, we must be streamed // - linear write only for crytpo aes-128-cbc h->is_streamed = 1; } c->pad_len = 0; err: return ret; }