author | Bryce Van Dyk <bvandyk@mozilla.com> |
Wed, 04 May 2016 11:14:29 +1200 | |
changeset 299138 | 76dd48ec6e04b64621b5d301f90ce0445ee7e6e2 |
parent 299137 | f73ab8de07a3745cbd792619480ec10f0144235e |
child 299139 | 151e42dd75fb1103782c982af98e472dde418b35 |
push id | 30291 |
push user | ryanvm@gmail.com |
push date | Fri, 27 May 2016 01:45:30 +0000 |
treeherder | mozilla-central@4d63dde701b4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kinetik |
bugs | 1257726 |
milestone | 49.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/media/libnestegg/README_MOZILLA +++ b/media/libnestegg/README_MOZILLA @@ -1,8 +1,8 @@ The source from this directory was copied from the nestegg git repository using the update.sh script. The only changes made were those applied by update.sh and the addition of Makefile.in build files for the Mozilla build system. The nestegg git repository is: git://github.com/kinetiknz/nestegg.git -The git commit ID used was d493c8a7abd05c6911cd546fd6b5c82b366f5203. +The git commit ID used was 3bc788d4de8f11a1e6b625047f49b9d35dce824f.
--- a/media/libnestegg/include/nestegg.h +++ b/media/libnestegg/include/nestegg.h @@ -84,16 +84,23 @@ extern "C" { #define NESTEGG_SEEK_END 2 /**< Seek offset relative to end of stream. */ #define NESTEGG_LOG_DEBUG 1 /**< Debug level log message. */ #define NESTEGG_LOG_INFO 10 /**< Informational level log message. */ #define NESTEGG_LOG_WARNING 100 /**< Warning level log message. */ #define NESTEGG_LOG_ERROR 1000 /**< Error level log message. */ #define NESTEGG_LOG_CRITICAL 10000 /**< Critical level log message. */ +#define NESTEGG_ENCODING_COMPRESSION 0 /**< Content encoding type is compression. */ +#define NESTEGG_ENCODING_ENCRYPTION 1 /**< Content encoding type is encryption. */ + +#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE 0 /**< Packet does not have signal byte */ +#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED 1 /**< Packet has signal byte and is unencrypted */ +#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED 2 /**< Packet has signal byte and is encrypted */ + #define NESTEGG_PACKET_HAS_KEYFRAME_FALSE 0 /**< Packet contains only keyframes. */ #define NESTEGG_PACKET_HAS_KEYFRAME_TRUE 1 /**< Packet does not contain any keyframes */ #define NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN 2 /**< Packet may or may not contain keyframes */ typedef struct nestegg nestegg; /**< Opaque handle referencing the stream state. */ typedef struct nestegg_packet nestegg_packet; /**< Opaque handle referencing a packet of data. */ /** User supplied IO context. */ @@ -283,16 +290,38 @@ int nestegg_track_video_params(nestegg * @param context Stream context initialized by #nestegg_init. @param track Zero based track number. @param params Storage for the queried audio parameters. @retval 0 Success. @retval -1 Error. */ int nestegg_track_audio_params(nestegg * context, unsigned int track, nestegg_audio_params * params); +/** Query the encoding status for @a track. If a track has multiple encodings + the first will be returned. + @param context Stream context initialized by #nestegg_init. + @param track Zero based track number. + @retval #NESTEGG_ENCODING_COMPRESSION The track is compressed, but not encrypted. + @retval #NESTEGG_ENCODING_ENCRYPTION The track is encrypted and compressed. + @retval -1 Error. */ +int nestegg_track_encoding(nestegg * context, unsigned int track); + +/** Query the ContentEncKeyId for @a track. Will return an error if the track + in not encrypted, or is not recognized. + @param context Stream context initialized by #nestegg_init. + @param track Zero based track number. + @param content_enc_key_id Storage for queried id. The content encryption key used. + Owned by nestegg and will be freed separately. + @param content_enc_key_id_length Length of the queried ContentEncKeyId in bytes. + @retval 0 Success. + @retval -1 Error. */ +int nestegg_track_content_enc_key_id(nestegg * context, unsigned int track, + unsigned char const ** content_enc_key_id, + size_t * content_enc_key_id_length); + /** Query the default frame duration for @a track. For a video track, this is typically the inverse of the video frame rate. @param context Stream context initialized by #nestegg_init. @param track Zero based track number. @param duration Storage for the default duration in nanoseconds. @retval 0 Success. @retval -1 Error. */ int nestegg_track_default_duration(nestegg * context, unsigned int track, @@ -382,16 +411,39 @@ int nestegg_packet_additional_data(neste /** Returns discard_padding for given packet @param packet Packet initialized by #nestegg_read_packet. @param discard_padding pointer to store discard padding in. @retval 0 Success. @retval -1 Error. */ int nestegg_packet_discard_padding(nestegg_packet * packet, int64_t * discard_padding); +/** Query if a packet is encrypted. + @param packet Packet initialized by #nestegg_read_packet. + @retval #NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE No signal byte, encryption + information not read from packet. + @retval #NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED Encrypted bit not + set, encryption information not read from packet. + @retval #NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED Encrypted bit set, + encryption infomation read from packet. + @retval -1 Error.*/ +int nestegg_packet_encryption(nestegg_packet * packet); + +/** Query the IV for an encrypted packet. Expects a packet from an encrypted + track, and will return error if given a packet that has no signal btye. + @param packet Packet initialized by #nestegg_read_packet. + @param iv Storage for queried iv. + @param length Length of returned iv, may be 0. + The data is owned by the #nestegg_packet packet. + @retval 0 Success. + @retval -1 Error. + */ +int nestegg_packet_iv(nestegg_packet * packet, unsigned char const ** iv, + size_t * length); + /** Returns reference_block given packet @param packet Packet initialized by #nestegg_read_packet. @param reference_block pointer to store reference block in. @retval 0 Success. @retval -1 Error. */ int nestegg_packet_reference_block(nestegg_packet * packet, int64_t * reference_block);
--- a/media/libnestegg/src/nestegg.c +++ b/media/libnestegg/src/nestegg.c @@ -7,148 +7,172 @@ #include <assert.h> #include <stdlib.h> #include <string.h> #include "halloc.h" #include "nestegg/nestegg.h" /* EBML Elements */ -#define ID_EBML 0x1a45dfa3 -#define ID_EBML_VERSION 0x4286 -#define ID_EBML_READ_VERSION 0x42f7 -#define ID_EBML_MAX_ID_LENGTH 0x42f2 -#define ID_EBML_MAX_SIZE_LENGTH 0x42f3 -#define ID_DOCTYPE 0x4282 -#define ID_DOCTYPE_VERSION 0x4287 -#define ID_DOCTYPE_READ_VERSION 0x4285 +#define ID_EBML 0x1a45dfa3 +#define ID_EBML_VERSION 0x4286 +#define ID_EBML_READ_VERSION 0x42f7 +#define ID_EBML_MAX_ID_LENGTH 0x42f2 +#define ID_EBML_MAX_SIZE_LENGTH 0x42f3 +#define ID_DOCTYPE 0x4282 +#define ID_DOCTYPE_VERSION 0x4287 +#define ID_DOCTYPE_READ_VERSION 0x4285 /* Global Elements */ -#define ID_VOID 0xec -#define ID_CRC32 0xbf +#define ID_VOID 0xec +#define ID_CRC32 0xbf /* WebM Elements */ -#define ID_SEGMENT 0x18538067 +#define ID_SEGMENT 0x18538067 /* Seek Head Elements */ -#define ID_SEEK_HEAD 0x114d9b74 -#define ID_SEEK 0x4dbb -#define ID_SEEK_ID 0x53ab -#define ID_SEEK_POSITION 0x53ac +#define ID_SEEK_HEAD 0x114d9b74 +#define ID_SEEK 0x4dbb +#define ID_SEEK_ID 0x53ab +#define ID_SEEK_POSITION 0x53ac /* Info Elements */ -#define ID_INFO 0x1549a966 -#define ID_TIMECODE_SCALE 0x2ad7b1 -#define ID_DURATION 0x4489 +#define ID_INFO 0x1549a966 +#define ID_TIMECODE_SCALE 0x2ad7b1 +#define ID_DURATION 0x4489 /* Cluster Elements */ -#define ID_CLUSTER 0x1f43b675 -#define ID_TIMECODE 0xe7 -#define ID_BLOCK_GROUP 0xa0 -#define ID_SIMPLE_BLOCK 0xa3 +#define ID_CLUSTER 0x1f43b675 +#define ID_TIMECODE 0xe7 +#define ID_BLOCK_GROUP 0xa0 +#define ID_SIMPLE_BLOCK 0xa3 /* BlockGroup Elements */ -#define ID_BLOCK 0xa1 -#define ID_BLOCK_ADDITIONS 0x75a1 -#define ID_BLOCK_DURATION 0x9b -#define ID_REFERENCE_BLOCK 0xfb -#define ID_DISCARD_PADDING 0x75a2 +#define ID_BLOCK 0xa1 +#define ID_BLOCK_ADDITIONS 0x75a1 +#define ID_BLOCK_DURATION 0x9b +#define ID_REFERENCE_BLOCK 0xfb +#define ID_DISCARD_PADDING 0x75a2 /* BlockAdditions Elements */ -#define ID_BLOCK_MORE 0xa6 +#define ID_BLOCK_MORE 0xa6 /* BlockMore Elements */ -#define ID_BLOCK_ADD_ID 0xee -#define ID_BLOCK_ADDITIONAL 0xa5 +#define ID_BLOCK_ADD_ID 0xee +#define ID_BLOCK_ADDITIONAL 0xa5 /* Tracks Elements */ -#define ID_TRACKS 0x1654ae6b -#define ID_TRACK_ENTRY 0xae -#define ID_TRACK_NUMBER 0xd7 -#define ID_TRACK_UID 0x73c5 -#define ID_TRACK_TYPE 0x83 -#define ID_FLAG_ENABLED 0xb9 -#define ID_FLAG_DEFAULT 0x88 -#define ID_FLAG_LACING 0x9c -#define ID_TRACK_TIMECODE_SCALE 0x23314f -#define ID_LANGUAGE 0x22b59c -#define ID_CODEC_ID 0x86 -#define ID_CODEC_PRIVATE 0x63a2 -#define ID_CODEC_DELAY 0x56aa -#define ID_SEEK_PREROLL 0x56bb -#define ID_DEFAULT_DURATION 0x23e383 +#define ID_TRACKS 0x1654ae6b +#define ID_TRACK_ENTRY 0xae +#define ID_TRACK_NUMBER 0xd7 +#define ID_TRACK_UID 0x73c5 +#define ID_TRACK_TYPE 0x83 +#define ID_FLAG_ENABLED 0xb9 +#define ID_FLAG_DEFAULT 0x88 +#define ID_FLAG_LACING 0x9c +#define ID_TRACK_TIMECODE_SCALE 0x23314f +#define ID_LANGUAGE 0x22b59c +#define ID_CODEC_ID 0x86 +#define ID_CODEC_PRIVATE 0x63a2 +#define ID_CODEC_DELAY 0x56aa +#define ID_SEEK_PREROLL 0x56bb +#define ID_DEFAULT_DURATION 0x23e383 /* Video Elements */ -#define ID_VIDEO 0xe0 -#define ID_STEREO_MODE 0x53b8 -#define ID_ALPHA_MODE 0x53c0 -#define ID_PIXEL_WIDTH 0xb0 -#define ID_PIXEL_HEIGHT 0xba -#define ID_PIXEL_CROP_BOTTOM 0x54aa -#define ID_PIXEL_CROP_TOP 0x54bb -#define ID_PIXEL_CROP_LEFT 0x54cc -#define ID_PIXEL_CROP_RIGHT 0x54dd -#define ID_DISPLAY_WIDTH 0x54b0 -#define ID_DISPLAY_HEIGHT 0x54ba +#define ID_VIDEO 0xe0 +#define ID_STEREO_MODE 0x53b8 +#define ID_ALPHA_MODE 0x53c0 +#define ID_PIXEL_WIDTH 0xb0 +#define ID_PIXEL_HEIGHT 0xba +#define ID_PIXEL_CROP_BOTTOM 0x54aa +#define ID_PIXEL_CROP_TOP 0x54bb +#define ID_PIXEL_CROP_LEFT 0x54cc +#define ID_PIXEL_CROP_RIGHT 0x54dd +#define ID_DISPLAY_WIDTH 0x54b0 +#define ID_DISPLAY_HEIGHT 0x54ba /* Audio Elements */ -#define ID_AUDIO 0xe1 -#define ID_SAMPLING_FREQUENCY 0xb5 -#define ID_CHANNELS 0x9f -#define ID_BIT_DEPTH 0x6264 +#define ID_AUDIO 0xe1 +#define ID_SAMPLING_FREQUENCY 0xb5 +#define ID_CHANNELS 0x9f +#define ID_BIT_DEPTH 0x6264 /* Cues Elements */ -#define ID_CUES 0x1c53bb6b -#define ID_CUE_POINT 0xbb -#define ID_CUE_TIME 0xb3 -#define ID_CUE_TRACK_POSITIONS 0xb7 -#define ID_CUE_TRACK 0xf7 -#define ID_CUE_CLUSTER_POSITION 0xf1 -#define ID_CUE_BLOCK_NUMBER 0x5378 +#define ID_CUES 0x1c53bb6b +#define ID_CUE_POINT 0xbb +#define ID_CUE_TIME 0xb3 +#define ID_CUE_TRACK_POSITIONS 0xb7 +#define ID_CUE_TRACK 0xf7 +#define ID_CUE_CLUSTER_POSITION 0xf1 +#define ID_CUE_BLOCK_NUMBER 0x5378 + +/* Encoding Elements */ +#define ID_CONTENT_ENCODINGS 0x6d80 +#define ID_CONTENT_ENCODING 0x6240 +#define ID_CONTENT_ENCODING_TYPE 0x5033 + +/* Encryption Elements */ +#define ID_CONTENT_ENCRYPTION 0x5035 +#define ID_CONTENT_ENC_ALGO 0x47e1 +#define ID_CONTENT_ENC_KEY_ID 0x47e2 +#define ID_CONTENT_ENC_AES_SETTINGS 0x47e7 +#define ID_AES_SETTINGS_CIPHER_MODE 0x47e8 /* EBML Types */ enum ebml_type_enum { TYPE_UNKNOWN, TYPE_MASTER, TYPE_UINT, TYPE_FLOAT, TYPE_STRING, TYPE_BINARY }; -#define LIMIT_STRING (1 << 20) -#define LIMIT_BINARY (1 << 24) -#define LIMIT_BLOCK (1 << 30) -#define LIMIT_FRAME (1 << 28) +#define LIMIT_STRING (1 << 20) +#define LIMIT_BINARY (1 << 24) +#define LIMIT_BLOCK (1 << 30) +#define LIMIT_FRAME (1 << 28) /* Field Flags */ -#define DESC_FLAG_NONE 0 -#define DESC_FLAG_MULTI (1 << 0) -#define DESC_FLAG_SUSPEND (1 << 1) -#define DESC_FLAG_OFFSET (1 << 2) +#define DESC_FLAG_NONE 0 +#define DESC_FLAG_MULTI (1 << 0) +#define DESC_FLAG_SUSPEND (1 << 1) +#define DESC_FLAG_OFFSET (1 << 2) /* Block Header Flags */ #define SIMPLE_BLOCK_FLAGS_KEYFRAME (1 << 7) #define BLOCK_FLAGS_LACING 6 /* Lacing Constants */ -#define LACING_NONE 0 -#define LACING_XIPH 1 -#define LACING_FIXED 2 -#define LACING_EBML 3 +#define LACING_NONE 0 +#define LACING_XIPH 1 +#define LACING_FIXED 2 +#define LACING_EBML 3 /* Track Types */ -#define TRACK_TYPE_VIDEO 1 -#define TRACK_TYPE_AUDIO 2 +#define TRACK_TYPE_VIDEO 1 +#define TRACK_TYPE_AUDIO 2 /* Track IDs */ -#define TRACK_ID_VP8 "V_VP8" -#define TRACK_ID_VP9 "V_VP9" -#define TRACK_ID_VORBIS "A_VORBIS" -#define TRACK_ID_OPUS "A_OPUS" +#define TRACK_ID_VP8 "V_VP8" +#define TRACK_ID_VP9 "V_VP9" +#define TRACK_ID_VORBIS "A_VORBIS" +#define TRACK_ID_OPUS "A_OPUS" + +/* Track Encryption */ +#define CONTENT_ENC_ALGO_AES 5 +#define AES_SETTINGS_CIPHER_CTR 1 + +/* Packet Encryption */ +#define SIGNAL_BYTE_SIZE 1 +#define IV_SIZE 8 + +/* Signal Byte */ +#define PACKET_ENCRYPTED 1 +#define ENCRYPTED_BIT_MASK (1 << 0) enum vint_mask { MASK_NONE, MASK_FIRST_BIT }; struct ebml_binary { unsigned char * data; @@ -218,32 +242,52 @@ struct video { }; struct audio { struct ebml_type sampling_frequency; struct ebml_type channels; struct ebml_type bit_depth; }; +struct content_enc_aes_settings { + struct ebml_type aes_settings_cipher_mode; +}; + +struct content_encryption { + struct ebml_type content_enc_algo; + struct ebml_type content_enc_key_id; + struct ebml_list content_enc_aes_settings; +}; + +struct content_encoding { + struct ebml_type content_encoding_type; + struct ebml_list content_encryption; +}; + +struct content_encodings { + struct ebml_list content_encoding; +}; + struct track_entry { struct ebml_type number; struct ebml_type uid; struct ebml_type type; struct ebml_type flag_enabled; struct ebml_type flag_default; struct ebml_type flag_lacing; struct ebml_type track_timecode_scale; struct ebml_type language; struct ebml_type codec_id; struct ebml_type codec_private; struct ebml_type codec_delay; struct ebml_type seek_preroll; struct ebml_type default_duration; struct video video; struct audio audio; + struct content_encodings content_encodings; }; struct tracks { struct ebml_list track_entry; }; struct cue_track_positions { struct ebml_type track; @@ -280,19 +324,26 @@ struct list_node { struct saved_state { int64_t stream_offset; uint64_t last_id; uint64_t last_size; int last_valid; }; +struct frame_encryption { + unsigned char * iv; + size_t length; + uint8_t signal_byte; +}; + struct frame { unsigned char * data; size_t length; + struct frame_encryption * frame_encryption; struct frame * next; }; struct block_additional { unsigned int id; unsigned char * data; size_t length; struct block_additional * next; @@ -404,32 +455,56 @@ static struct ebml_element_desc ne_video static struct ebml_element_desc ne_audio_elements[] = { E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency), E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels), E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth), E_LAST }; +static struct ebml_element_desc ne_content_enc_aes_settings_elements[] = { + E_FIELD(ID_AES_SETTINGS_CIPHER_MODE, TYPE_UINT, struct content_enc_aes_settings, aes_settings_cipher_mode), + E_LAST +}; + +static struct ebml_element_desc ne_content_encryption_elements[] = { + E_FIELD(ID_CONTENT_ENC_ALGO, TYPE_UINT, struct content_encryption, content_enc_algo), + E_FIELD(ID_CONTENT_ENC_KEY_ID, TYPE_BINARY, struct content_encryption, content_enc_key_id), + E_MASTER(ID_CONTENT_ENC_AES_SETTINGS, TYPE_MASTER, struct content_encryption, content_enc_aes_settings), + E_LAST +}; + +static struct ebml_element_desc ne_content_encoding_elements[] = { + E_FIELD(ID_CONTENT_ENCODING_TYPE, TYPE_UINT, struct content_encoding, content_encoding_type), + E_MASTER(ID_CONTENT_ENCRYPTION, TYPE_MASTER, struct content_encoding, content_encryption), + E_LAST +}; + +static struct ebml_element_desc ne_content_encodings_elements[] = { + E_MASTER(ID_CONTENT_ENCODING, TYPE_MASTER, struct content_encodings, content_encoding), + E_LAST +}; + static struct ebml_element_desc ne_track_entry_elements[] = { E_FIELD(ID_TRACK_NUMBER, TYPE_UINT, struct track_entry, number), E_FIELD(ID_TRACK_UID, TYPE_UINT, struct track_entry, uid), E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type), E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled), E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default), E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing), E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale), E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language), E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id), E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private), E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay), E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll), E_FIELD(ID_DEFAULT_DURATION, TYPE_UINT, struct track_entry, default_duration), E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video), E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio), + E_SINGLE_MASTER(ID_CONTENT_ENCODINGS, TYPE_MASTER, struct track_entry, content_encodings), E_LAST }; static struct ebml_element_desc ne_tracks_elements[] = { E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry), E_LAST }; @@ -1068,16 +1143,65 @@ ne_parse(nestegg * ctx, struct ebml_elem if (r != 1) while (ctx->ancestor) ne_ctx_pop(ctx); return r; } static int +ne_read_block_encryption(nestegg * ctx, struct track_entry const * entry, + uint64_t * encoding_type, uint64_t * encryption_algo, + uint64_t * encryption_mode) +{ + struct content_encoding * encoding; + struct content_encryption * encryption; + struct content_enc_aes_settings * aes_settings; + + *encoding_type = 0; + if (entry->content_encodings.content_encoding.head) { + encoding = entry->content_encodings.content_encoding.head->data; + if (ne_get_uint(encoding->content_encoding_type, encoding_type) != 0) + return -1; + + if (*encoding_type == NESTEGG_ENCODING_ENCRYPTION) { + /* Metadata states content is encrypted */ + if (!encoding->content_encryption.head) + return -1; + + encryption = encoding->content_encryption.head->data; + if (ne_get_uint(encryption->content_enc_algo, encryption_algo) != 0) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAlgo element found"); + return -1; + } + + if (*encryption_algo != CONTENT_ENC_ALGO_AES) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo used"); + return -1; + } + + if (!encryption->content_enc_aes_settings.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAESSettings element found"); + return -1; + } + + aes_settings = encryption->content_enc_aes_settings.head->data; + *encryption_mode = AES_SETTINGS_CIPHER_CTR; + ne_get_uint(aes_settings->aes_settings_cipher_mode, encryption_mode); + + if (*encryption_mode != AES_SETTINGS_CIPHER_CTR) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingsCipherMode used"); + return -1; + } + } + } + return 1; +} + +static int ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed) { int r; uint64_t lace; r = ne_read_uint(io, &lace, 1); if (r != 1) return r; @@ -1219,20 +1343,21 @@ static int ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data) { int r; int64_t timecode, abs_timecode; nestegg_packet * pkt; struct frame * f, * last; struct track_entry * entry; double track_scale; - uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total; + uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total, + encoding_type, encryption_algo, encryption_mode; unsigned int i, lacing, track; - uint8_t keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN; - size_t consumed = 0; + uint8_t signal_byte, keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN; + size_t consumed = 0, data_size, encryption_size; *data = NULL; if (block_size > LIMIT_BLOCK) return -1; r = ne_read_vint(ctx->io, &track_number, &length); if (r != 1) @@ -1319,16 +1444,26 @@ ne_read_block(nestegg * ctx, uint64_t bl if (ne_map_track_number_to_index(ctx, track_number, &track) != 0) return -1; entry = ne_find_track_entry(ctx, track); if (!entry) return -1; + r = ne_read_block_encryption(ctx, entry, &encoding_type, &encryption_algo, &encryption_mode); + if (r != 1) + return r; + + /* Encryption does not support lacing */ + if (lacing != LACING_NONE && encoding_type == NESTEGG_ENCODING_ENCRYPTION) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Encrypted blocks may not also be laced"); + return -1; + } + track_scale = 1.0; tc_scale = ne_get_timecode_scale(ctx); if (!ctx->read_cluster_timecode) return -1; cluster_tc = ctx->cluster_timecode; @@ -1352,25 +1487,74 @@ ne_read_block(nestegg * ctx, uint64_t bl nestegg_free_packet(pkt); return -1; } f = ne_alloc(sizeof(*f)); if (!f) { nestegg_free_packet(pkt); return -1; } - f->data = ne_alloc(frame_sizes[i]); + /* Parse encryption */ + if (encoding_type == NESTEGG_ENCODING_ENCRYPTION) { + r = ne_io_read(ctx->io, &signal_byte, SIGNAL_BYTE_SIZE); + if (r != 1) { + free(f); + nestegg_free_packet(pkt); + return r; + } + f->frame_encryption = ne_alloc(sizeof(*f->frame_encryption)); + if (!f->frame_encryption) { + free(f); + nestegg_free_packet(pkt); + return -1; + } + f->frame_encryption->signal_byte = signal_byte; + if ((signal_byte & ENCRYPTED_BIT_MASK) == PACKET_ENCRYPTED) { + f->frame_encryption->iv = ne_alloc(IV_SIZE); + if (!f->frame_encryption->iv) { + free(f->frame_encryption); + free(f); + nestegg_free_packet(pkt); + return -1; + } + r = ne_io_read(ctx->io, f->frame_encryption->iv, IV_SIZE); + if (r != 1) { + free(f->frame_encryption); + free(f); + nestegg_free_packet(pkt); + return r; + } + f->frame_encryption->length = IV_SIZE; + encryption_size = SIGNAL_BYTE_SIZE + IV_SIZE; + } else { + f->frame_encryption->iv = NULL; + f->frame_encryption->length = 0; + encryption_size = SIGNAL_BYTE_SIZE; + } + } else { + f->frame_encryption = NULL; + encryption_size = 0; + } + data_size = frame_sizes[i] - encryption_size; + /* Encryption parsed */ + f->data = ne_alloc(data_size); if (!f->data) { + if (f->frame_encryption) + free(f->frame_encryption->iv); + free(f->frame_encryption); free(f); nestegg_free_packet(pkt); return -1; } - f->length = frame_sizes[i]; - r = ne_io_read(ctx->io, f->data, frame_sizes[i]); + f->length = data_size; + r = ne_io_read(ctx->io, f->data, data_size); if (r != 1) { + if (f->frame_encryption) + free(f->frame_encryption->iv); + free(f->frame_encryption); free(f->data); free(f); nestegg_free_packet(pkt); return r; } if (!last) pkt->frame = f; @@ -2297,16 +2481,118 @@ nestegg_track_audio_params(nestegg * ctx value = 0; ne_get_uint(entry->seek_preroll, &value); params->seek_preroll = value; return 0; } int +nestegg_track_encoding(nestegg * ctx, unsigned int track) +{ + struct track_entry * entry; + struct content_encoding * encoding; + uint64_t encoding_value; + + entry = ne_find_track_entry(ctx, track); + if (!entry) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found"); + return -1; + } + + if (!entry->content_encodings.content_encoding.head) { + /* Default encoding is compression */ + return NESTEGG_ENCODING_COMPRESSION; + } + + encoding = entry->content_encodings.content_encoding.head->data; + + encoding_value = NESTEGG_ENCODING_COMPRESSION; + ne_get_uint(encoding->content_encoding_type, &encoding_value); + if (encoding_value != NESTEGG_ENCODING_COMPRESSION && encoding_value != NESTEGG_ENCODING_ENCRYPTION) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Invalid ContentEncoding element found"); + return -1; + } + + return encoding_value; +} + +int +nestegg_track_content_enc_key_id(nestegg * ctx, unsigned int track, unsigned char const ** content_enc_key_id, + size_t * content_enc_key_id_length) +{ + struct track_entry * entry; + struct content_encoding * encoding; + struct content_encryption * encryption; + struct content_enc_aes_settings * aes_settings; + struct nestegg_encryption_params; + uint64_t value; + struct ebml_binary enc_key_id; + + entry = ne_find_track_entry(ctx, track); + if (!entry) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found"); + return -1; + } + + if (!entry->content_encodings.content_encoding.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncoding element found"); + return -1; + } + + encoding = entry->content_encodings.content_encoding.head->data; + + value = 0; + ne_get_uint(encoding->content_encoding_type, &value); + if (value != NESTEGG_ENCODING_ENCRYPTION) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncodingType found"); + return -1; + } + + if (!encoding->content_encryption.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncryption element found"); + return -1; + } + + encryption = encoding->content_encryption.head->data; + + value = 0; + ne_get_uint(encryption->content_enc_algo, &value); + + if (value != CONTENT_ENC_ALGO_AES) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo found"); + return -1; + } + + if (!encryption->content_enc_aes_settings.head) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAesSettings element found"); + return -1; + } + + aes_settings = encryption->content_enc_aes_settings.head->data; + value = AES_SETTINGS_CIPHER_CTR; + ne_get_uint(aes_settings->aes_settings_cipher_mode, &value); + + if (value != AES_SETTINGS_CIPHER_CTR) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingCipherMode used"); + return -1; + } + + if (ne_get_binary(encryption->content_enc_key_id, &enc_key_id) != 0) { + ctx->log(ctx, NESTEGG_LOG_ERROR, "Could not retrieve track ContentEncKeyId"); + return -1; + } + + *content_enc_key_id = enc_key_id.data; + *content_enc_key_id_length = enc_key_id.length; + + return 0; +} + +int nestegg_track_default_duration(nestegg * ctx, unsigned int track, uint64_t * duration) { struct track_entry * entry; uint64_t value; entry = ne_find_track_entry(ctx, track); if (!entry) @@ -2471,16 +2757,20 @@ void nestegg_free_packet(nestegg_packet * pkt) { struct frame * frame; struct block_additional * block_additional; while (pkt->frame) { frame = pkt->frame; pkt->frame = frame->next; + if (frame->frame_encryption) { + free(frame->frame_encryption->iv); + } + free(frame->frame_encryption); free(frame->data); free(frame); } while (pkt->block_additional) { block_additional = pkt->block_additional; pkt->block_additional = block_additional->next; free(block_additional->data); @@ -2592,16 +2882,60 @@ nestegg_packet_additional_data(nestegg_p } a = a->next; } return -1; } int +nestegg_packet_encryption(nestegg_packet * pkt) +{ + struct frame * f = pkt->frame; + unsigned char encrypted_bit; + + if (!f->frame_encryption) + return NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE; + + /* Should never have parsed blocks with both encryption and lacing */ + assert(f->next == NULL); + + encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK; + + if (encrypted_bit != PACKET_ENCRYPTED) + return NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED; + + return NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED; +} + +int +nestegg_packet_iv(nestegg_packet * pkt, unsigned char const ** iv, size_t * length) +{ + struct frame * f = pkt->frame; + unsigned char encrypted_bit; + + *iv = NULL; + *length = 0; + if (!f->frame_encryption) + return -1; + + /* Should never have parsed blocks with both encryption and lacing */ + assert(f->next == NULL); + + encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK; + + if (encrypted_bit != PACKET_ENCRYPTED) + return 0; + + *iv = f->frame_encryption->iv; + *length = f->frame_encryption->length; + return 0; +} + +int nestegg_has_cues(nestegg * ctx) { return ctx->segment.cues.cue_point.head || ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES); } int nestegg_sniff(unsigned char const * buffer, size_t length)