@ -3,6 +3,7 @@
*
*
* Copyright ( c ) 2008 Maxim Poliakovski
* Copyright ( c ) 2008 Maxim Poliakovski
* 2008 Benjamin Larsson
* 2008 Benjamin Larsson
* 2011 David Goldwich
*
*
* This file is part of FFmpeg .
* This file is part of FFmpeg .
*
*
@ -36,20 +37,19 @@
* - Sound data organized in packets follow the EA3 header
* - Sound data organized in packets follow the EA3 header
* ( can be encrypted using the Sony DRM ! ) .
* ( can be encrypted using the Sony DRM ! ) .
*
*
* LIMITATIONS : This version supports only plain ( unencrypted ) OMA files .
* If any DRM - protected ( encrypted ) file is encountered you will get the
* corresponding error message . Try to remove the encryption using any
* Sony software ( for example SonicStage ) .
* CODEC SUPPORT : Only ATRAC3 codec is currently supported !
* CODEC SUPPORT : Only ATRAC3 codec is currently supported !
*/
*/
# include "avformat.h"
# include "avformat.h"
# include "libavutil/intreadwrite.h"
# include "libavutil/intreadwrite.h"
# include "libavutil/des.h"
# include "pcm.h"
# include "pcm.h"
# include "riff.h"
# include "riff.h"
# include "id3v2.h"
# include "id3v2.h"
# define EA3_HEADER_SIZE 96
# define EA3_HEADER_SIZE 96
# define ID3v2_EA3_MAGIC "ea3"
# define OMA_ENC_HEADER_SIZE 16
enum {
enum {
OMA_CODECID_ATRAC3 = 0 ,
OMA_CODECID_ATRAC3 = 0 ,
@ -65,7 +65,211 @@ static const AVCodecTag codec_oma_tags[] = {
{ CODEC_ID_MP3 , OMA_CODECID_MP3 } ,
{ CODEC_ID_MP3 , OMA_CODECID_MP3 } ,
} ;
} ;
# define ID3v2_EA3_MAGIC "ea3"
static const uint64_t leaf_table [ ] = {
0xd79e8283acea4620 , 0x7a9762f445afd0d8 ,
0x354d60a60b8c79f1 , 0x584e1cde00b07aee ,
0x1573cd93da7df623 , 0x47f98d79620dd535
} ;
typedef struct OMAContext {
uint64_t content_start ;
int encrypted ;
uint16_t k_size ;
uint16_t e_size ;
uint16_t i_size ;
uint16_t s_size ;
uint32_t rid ;
uint8_t r_val [ 24 ] ;
uint8_t n_val [ 24 ] ;
uint8_t m_val [ 8 ] ;
uint8_t s_val [ 8 ] ;
uint8_t sm_val [ 8 ] ;
uint8_t e_val [ 8 ] ;
uint8_t iv [ 8 ] ;
struct AVDES av_des ;
} OMAContext ;
static void hex_log ( AVFormatContext * s , int level , const char * name , const uint8_t * value , int len )
{
char buf [ 33 ] ;
len = FFMIN ( len , 16 ) ;
if ( av_log_get_level ( ) < level )
return ;
ff_data_to_hex ( buf , value , len , 1 ) ;
buf [ len < < 1 ] = ' \0 ' ;
av_log ( s , level , " %s: %s \n " , name , buf ) ;
}
static int kset ( AVFormatContext * s , const uint8_t * r_val , const uint8_t * n_val , int len )
{
OMAContext * oc = s - > priv_data ;
if ( ! r_val & & ! n_val )
return - 1 ;
len = FFMIN ( len , 16 ) ;
/* use first 64 bits in the third round again */
if ( r_val ) {
if ( r_val ! = oc - > r_val ) {
memset ( oc - > r_val , 0 , 24 ) ;
memcpy ( oc - > r_val , r_val , len ) ;
}
memcpy ( & oc - > r_val [ 16 ] , r_val , 8 ) ;
}
if ( n_val ) {
if ( n_val ! = oc - > n_val ) {
memset ( oc - > n_val , 0 , 24 ) ;
memcpy ( oc - > n_val , n_val , len ) ;
}
memcpy ( & oc - > n_val [ 16 ] , n_val , 8 ) ;
}
return 0 ;
}
static int rprobe ( AVFormatContext * s , uint8_t * enc_header , const uint8_t * r_val )
{
OMAContext * oc = s - > priv_data ;
unsigned int pos ;
struct AVDES av_des ;
if ( ! enc_header | | ! r_val )
return - 1 ;
/* m_val */
av_des_init ( & av_des , r_val , 192 , 1 ) ;
av_des_crypt ( & av_des , oc - > m_val , & enc_header [ 48 ] , 1 , NULL , 1 ) ;
/* s_val */
av_des_init ( & av_des , oc - > m_val , 64 , 0 ) ;
av_des_crypt ( & av_des , oc - > s_val , NULL , 1 , NULL , 0 ) ;
/* sm_val */
pos = OMA_ENC_HEADER_SIZE + oc - > k_size + oc - > e_size ;
av_des_init ( & av_des , oc - > s_val , 64 , 0 ) ;
av_des_mac ( & av_des , oc - > sm_val , & enc_header [ pos ] , ( oc - > i_size > > 3 ) ) ;
pos + = oc - > i_size ;
return memcmp ( & enc_header [ pos ] , oc - > sm_val , 8 ) ? - 1 : 0 ;
}
static int nprobe ( AVFormatContext * s , uint8_t * enc_header , const uint8_t * n_val )
{
OMAContext * oc = s - > priv_data ;
uint32_t pos , taglen , datalen ;
struct AVDES av_des ;
if ( ! enc_header | | ! n_val )
return - 1 ;
pos = OMA_ENC_HEADER_SIZE + oc - > k_size ;
if ( ! memcmp ( & enc_header [ pos ] , " EKB " , 4 ) )
pos + = 32 ;
if ( AV_RB32 ( & enc_header [ pos ] ) ! = oc - > rid )
av_log ( s , AV_LOG_DEBUG , " Mismatching RID \n " ) ;
taglen = AV_RB32 ( & enc_header [ pos + 32 ] ) ;
datalen = AV_RB32 ( & enc_header [ pos + 36 ] ) > > 4 ;
pos + = 44 + taglen ;
av_des_init ( & av_des , n_val , 192 , 1 ) ;
while ( datalen - - > 0 ) {
av_des_crypt ( & av_des , oc - > r_val , & enc_header [ pos ] , 2 , NULL , 1 ) ;
kset ( s , oc - > r_val , NULL , 16 ) ;
if ( ! rprobe ( s , enc_header , oc - > r_val ) )
return 0 ;
pos + = 16 ;
}
return - 1 ;
}
static int decrypt_init ( AVFormatContext * s , ID3v2ExtraMeta * em , uint8_t * header )
{
OMAContext * oc = s - > priv_data ;
ID3v2ExtraMetaGEOB * geob = NULL ;
uint8_t * gdata ;
oc - > encrypted = 1 ;
av_log ( s , AV_LOG_INFO , " File is encrypted \n " ) ;
/* find GEOB metadata */
while ( em ) {
if ( ! strcmp ( em - > tag , " GEOB " ) & &
( geob = em - > data ) & &
! strcmp ( geob - > description , " OMG_LSI " ) | |
! strcmp ( geob - > description , " OMG_BKLSI " ) ) {
break ;
}
em = em - > next ;
}
if ( ! em ) {
av_log ( s , AV_LOG_ERROR , " No encryption header found \n " ) ;
return - 1 ;
}
if ( geob - > datasize < 64 ) {
av_log ( s , AV_LOG_ERROR , " Invalid GEOB data size: %u \n " , geob - > datasize ) ;
return - 1 ;
}
gdata = geob - > data ;
if ( AV_RB16 ( gdata ) ! = 1 )
av_log ( s , AV_LOG_WARNING , " Unknown version in encryption header \n " ) ;
oc - > k_size = AV_RB16 ( & gdata [ 2 ] ) ;
oc - > e_size = AV_RB16 ( & gdata [ 4 ] ) ;
oc - > i_size = AV_RB16 ( & gdata [ 6 ] ) ;
oc - > s_size = AV_RB16 ( & gdata [ 8 ] ) ;
if ( memcmp ( & gdata [ OMA_ENC_HEADER_SIZE ] , " KEYRING " , 12 ) ) {
av_log ( s , AV_LOG_ERROR , " Invalid encryption header \n " ) ;
return - 1 ;
}
oc - > rid = AV_RB32 ( & gdata [ OMA_ENC_HEADER_SIZE + 28 ] ) ;
av_log ( s , AV_LOG_DEBUG , " RID: %.8x \n " , oc - > rid ) ;
memcpy ( oc - > iv , & header [ 0x58 ] , 8 ) ;
hex_log ( s , AV_LOG_DEBUG , " IV " , oc - > iv , 8 ) ;
hex_log ( s , AV_LOG_DEBUG , " CBC-MAC " , & gdata [ OMA_ENC_HEADER_SIZE + oc - > k_size + oc - > e_size + oc - > i_size ] , 8 ) ;
if ( s - > keylen > 0 ) {
kset ( s , s - > key , s - > key , s - > keylen ) ;
}
if ( ! memcmp ( oc - > r_val , ( const uint8_t [ 8 ] ) { 0 } , 8 ) | |
rprobe ( s , gdata , oc - > r_val ) < 0 & &
nprobe ( s , gdata , oc - > n_val ) < 0 ) {
int i ;
for ( i = 0 ; i < sizeof ( leaf_table ) ; i + = 2 ) {
uint8_t buf [ 16 ] ;
AV_WL64 ( buf , leaf_table [ i ] ) ;
AV_WL64 ( & buf [ 8 ] , leaf_table [ i + 1 ] ) ;
kset ( s , buf , buf , 16 ) ;
if ( ! rprobe ( s , gdata , oc - > r_val ) | | ! nprobe ( s , gdata , oc - > n_val ) )
break ;
}
if ( i > = sizeof ( leaf_table ) ) {
av_log ( s , AV_LOG_ERROR , " Invalid key \n " ) ;
return - 1 ;
}
}
/* e_val */
av_des_init ( & oc - > av_des , oc - > m_val , 64 , 0 ) ;
av_des_crypt ( & oc - > av_des , oc - > e_val , & gdata [ OMA_ENC_HEADER_SIZE + 40 ] , 1 , NULL , 0 ) ;
hex_log ( s , AV_LOG_DEBUG , " EK " , oc - > e_val , 8 ) ;
/* init e_val */
av_des_init ( & oc - > av_des , oc - > e_val , 64 , 1 ) ;
return 0 ;
}
static int oma_read_header ( AVFormatContext * s ,
static int oma_read_header ( AVFormatContext * s ,
AVFormatParameters * ap )
AVFormatParameters * ap )
@ -77,8 +281,10 @@ static int oma_read_header(AVFormatContext *s,
uint8_t buf [ EA3_HEADER_SIZE ] ;
uint8_t buf [ EA3_HEADER_SIZE ] ;
uint8_t * edata ;
uint8_t * edata ;
AVStream * st ;
AVStream * st ;
ID3v2ExtraMeta * extra_meta = NULL ;
OMAContext * oc = s - > priv_data ;
ff_id3v2_read ( s , ID3v2_EA3_MAGIC ) ;
ff_id3v2_read_all ( s , ID3v2_EA3_MAGIC , & extra_meta ) ;
ret = avio_read ( s - > pb , buf , EA3_HEADER_SIZE ) ;
ret = avio_read ( s - > pb , buf , EA3_HEADER_SIZE ) ;
if ( ret < EA3_HEADER_SIZE )
if ( ret < EA3_HEADER_SIZE )
return - 1 ;
return - 1 ;
@ -88,12 +294,17 @@ static int oma_read_header(AVFormatContext *s,
return - 1 ;
return - 1 ;
}
}
oc - > content_start = avio_tell ( s - > pb ) ;
/* encrypted file */
eid = AV_RB16 ( & buf [ 6 ] ) ;
eid = AV_RB16 ( & buf [ 6 ] ) ;
if ( eid ! = - 1 & & eid ! = - 128 ) {
if ( eid ! = - 1 & & eid ! = - 128 & & decrypt_init ( s , extra_meta , buf ) < 0 ) {
av_log ( s , AV_LOG_ERROR , " Encrypted file! Eid: %d \n " , eid ) ;
ff_id3v2_free_extra_meta ( & extra_meta ) ;
return - 1 ;
return - 1 ;
}
}
ff_id3v2_free_extra_meta ( & extra_meta ) ;
codec_params = AV_RB24 ( & buf [ 33 ] ) ;
codec_params = AV_RB24 ( & buf [ 33 ] ) ;
st = av_new_stream ( s , 0 ) ;
st = av_new_stream ( s , 0 ) ;
@ -159,12 +370,20 @@ static int oma_read_header(AVFormatContext *s,
static int oma_read_packet ( AVFormatContext * s , AVPacket * pkt )
static int oma_read_packet ( AVFormatContext * s , AVPacket * pkt )
{
{
int ret = av_get_packet ( s - > pb , pkt , s - > streams [ 0 ] - > codec - > block_align ) ;
OMAContext * oc = s - > priv_data ;
int packet_size = s - > streams [ 0 ] - > codec - > block_align ;
int ret = av_get_packet ( s - > pb , pkt , packet_size ) ;
pkt - > stream_index = 0 ;
if ( ret < = 0 )
if ( ret < = 0 )
return AVERROR ( EIO ) ;
return AVERROR ( EIO ) ;
pkt - > stream_index = 0 ;
if ( oc - > encrypted ) {
/* previous unencrypted block saved in IV for the next packet (CBC mode) */
av_des_crypt ( & oc - > av_des , pkt - > data , pkt - > data , ( packet_size > > 3 ) , oc - > iv , 1 ) ;
}
return ret ;
return ret ;
}
}
@ -190,16 +409,38 @@ static int oma_read_probe(AVProbeData *p)
return 0 ;
return 0 ;
}
}
static int oma_read_seek ( struct AVFormatContext * s , int stream_index , int64_t timestamp , int flags )
{
OMAContext * oc = s - > priv_data ;
pcm_read_seek ( s , stream_index , timestamp , flags ) ;
if ( oc - > encrypted ) {
/* readjust IV for CBC */
int64_t pos = avio_tell ( s - > pb ) ;
if ( pos < oc - > content_start )
memset ( oc - > iv , 0 , 8 ) ;
else {
if ( avio_seek ( s - > pb , - 8 , SEEK_CUR ) < 0 | | avio_read ( s - > pb , oc - > iv , 8 ) < 8 ) {
memset ( oc - > iv , 0 , 8 ) ;
return - 1 ;
}
}
}
return 0 ;
}
AVInputFormat ff_oma_demuxer = {
AVInputFormat ff_oma_demuxer = {
. name = " oma " ,
. name = " oma " ,
. long_name = NULL_IF_CONFIG_SMALL ( " Sony OpenMG audio " ) ,
. long_name = NULL_IF_CONFIG_SMALL ( " Sony OpenMG audio " ) ,
. priv_data_size = sizeof ( OMAContext ) ,
. read_probe = oma_read_probe ,
. read_probe = oma_read_probe ,
. read_header = oma_read_header ,
. read_header = oma_read_header ,
. read_packet = oma_read_packet ,
. read_packet = oma_read_packet ,
. read_seek = pcm_read_seek ,
. read_seek = oma _read_seek,
. flags = AVFMT_GENERIC_INDEX ,
. flags = AVFMT_GENERIC_INDEX ,
. extensions = " oma,aa3 " ,
. extensions = " oma,omg ,aa3 " ,
. codec_tag = ( const AVCodecTag * const [ ] ) { codec_oma_tags , 0 } ,
. codec_tag = ( const AVCodecTag * const [ ] ) { codec_oma_tags , 0 } ,
} ;
} ;