Browse code

Add support for Audible AAX (and AAX+) files

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>

Vesselin Bontchev authored on 2015/07/12 03:02:47
Showing 4 changed files
... ...
@@ -226,6 +226,8 @@ library:
226 226
 @item 4xm                       @tab   @tab X
227 227
     @tab 4X Technologies format, used in some games.
228 228
 @item 8088flex TMV              @tab   @tab X
229
+@item AAX                       @tab   @tab X
230
+    @tab Audible Enhanced Audio format, used in audiobooks.
229 231
 @item ACT Voice                 @tab   @tab X
230 232
     @tab contains G.729 audio
231 233
 @item Adobe Filmstrip           @tab X @tab X
... ...
@@ -667,6 +667,13 @@ point on IIS with this muxer. Example:
667 667
 ffmpeg -re @var{<normal input/transcoding options>} -movflags isml+frag_keyframe -f ismv http://server/publishingpoint.isml/Streams(Encoder1)
668 668
 @end example
669 669
 
670
+@subsection Audible AAX
671
+
672
+Audible AAX files are encrypted M4B files, and they can be decrypted by specifying a 4 byte activation secret.
673
+@example
674
+ffmpeg -activation_bytes 1CEB00DA -i test.aax -vn -c:a copy output.mp4
675
+@end example
676
+
670 677
 @section mp3
671 678
 
672 679
 The MP3 muxer writes a raw MP3 stream with the following optional features:
... ...
@@ -198,6 +198,14 @@ typedef struct MOVContext {
198 198
     MOVFragmentIndex** fragment_index_data;
199 199
     unsigned fragment_index_count;
200 200
     int atom_depth;
201
+    unsigned int aax_mode;  ///< 'aax' file has been detected
202
+    uint8_t file_key[20];
203
+    uint8_t file_iv[20];
204
+    void *activation_bytes;
205
+    int activation_bytes_size;
206
+    void *audible_fixed_key;
207
+    int audible_fixed_key_size;
208
+    struct AVAES *aes_decrypt;
201 209
 } MOVContext;
202 210
 
203 211
 int ff_mp4_read_descr_len(AVIOContext *pb);
... ...
@@ -37,6 +37,8 @@
37 37
 #include "libavutil/dict.h"
38 38
 #include "libavutil/display.h"
39 39
 #include "libavutil/opt.h"
40
+#include "libavutil/aes.h"
41
+#include "libavutil/sha.h"
40 42
 #include "libavutil/timecode.h"
41 43
 #include "libavcodec/ac3tab.h"
42 44
 #include "avformat.h"
... ...
@@ -807,6 +809,120 @@ static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom)
807 807
     return 0; /* now go for moov */
808 808
 }
809 809
 
810
+#define DRM_BLOB_SIZE 56
811
+
812
+static int mov_read_adrm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
813
+{
814
+    uint8_t intermediate_key[20];
815
+    uint8_t intermediate_iv[20];
816
+    uint8_t input[64];
817
+    uint8_t output[64];
818
+    uint8_t file_checksum[20];
819
+    uint8_t calculated_checksum[20];
820
+    struct AVSHA *sha;
821
+    int i;
822
+    int ret = 0;
823
+    uint8_t *activation_bytes = c->activation_bytes;
824
+    uint8_t *fixed_key = c->audible_fixed_key;
825
+
826
+    c->aax_mode = 1;
827
+
828
+    sha = av_sha_alloc();
829
+    if (!sha)
830
+        return AVERROR(ENOMEM);
831
+    c->aes_decrypt = av_aes_alloc();
832
+    if (!c->aes_decrypt) {
833
+        ret = AVERROR(ENOMEM);
834
+        goto fail;
835
+    }
836
+
837
+    /* drm blob processing */
838
+    avio_read(pb, output, 8); // go to offset 8, absolute postion 0x251
839
+    avio_read(pb, input, DRM_BLOB_SIZE);
840
+    avio_read(pb, output, 4); // go to offset 4, absolute postion 0x28d
841
+    avio_read(pb, file_checksum, 20);
842
+
843
+    av_log(c->fc, AV_LOG_INFO, "[aax] file checksum == "); // required by external tools
844
+    for (i = 0; i < 20; i++)
845
+        av_log(sha, AV_LOG_INFO, "%02x", file_checksum[i]);
846
+    av_log(c->fc, AV_LOG_INFO, "\n");
847
+
848
+    /* verify activation data */
849
+    if (!activation_bytes || c->activation_bytes_size != 4) {
850
+        av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes option is missing!\n");
851
+        ret = AVERROR(EINVAL);
852
+        goto fail;
853
+    }
854
+    if (c->activation_bytes_size != 4) {
855
+        av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes value needs to be 4 bytes!\n");
856
+        ret = AVERROR(EINVAL);
857
+        goto fail;
858
+    }
859
+
860
+    /* verify fixed key */
861
+    if (c->audible_fixed_key_size != 16) {
862
+        av_log(c->fc, AV_LOG_FATAL, "[aax] audible_fixed_key value needs to be 16 bytes!\n");
863
+        ret = AVERROR(EINVAL);
864
+        goto fail;
865
+    }
866
+
867
+    /* AAX (and AAX+) key derivation */
868
+    av_sha_init(sha, 160);
869
+    av_sha_update(sha, fixed_key, 16);
870
+    av_sha_update(sha, activation_bytes, 4);
871
+    av_sha_final(sha, intermediate_key);
872
+    av_sha_init(sha, 160);
873
+    av_sha_update(sha, fixed_key, 16);
874
+    av_sha_update(sha, intermediate_key, 20);
875
+    av_sha_update(sha, activation_bytes, 4);
876
+    av_sha_final(sha, intermediate_iv);
877
+    av_sha_init(sha, 160);
878
+    av_sha_update(sha, intermediate_key, 16);
879
+    av_sha_update(sha, intermediate_iv, 16);
880
+    av_sha_final(sha, calculated_checksum);
881
+    if (memcmp(calculated_checksum, file_checksum, 20)) { // critical error
882
+        av_log(c->fc, AV_LOG_ERROR, "[aax] mismatch in checksums!\n");
883
+        ret = AVERROR_INVALIDDATA;
884
+        goto fail;
885
+    }
886
+    av_aes_init(c->aes_decrypt, intermediate_key, 128, 1);
887
+    av_aes_crypt(c->aes_decrypt, output, input, DRM_BLOB_SIZE >> 4, intermediate_iv, 1);
888
+    for (i = 0; i < 4; i++) {
889
+        // file data (in output) is stored in big-endian mode
890
+        if (activation_bytes[i] != output[3 - i]) { // critical error
891
+            av_log(c->fc, AV_LOG_ERROR, "[aax] error in drm blob decryption!\n");
892
+            ret = AVERROR_INVALIDDATA;
893
+            goto fail;
894
+        }
895
+    }
896
+    memcpy(c->file_key, output + 8, 16);
897
+    memcpy(input, output + 26, 16);
898
+    av_sha_init(sha, 160);
899
+    av_sha_update(sha, input, 16);
900
+    av_sha_update(sha, c->file_key, 16);
901
+    av_sha_update(sha, fixed_key, 16);
902
+    av_sha_final(sha, c->file_iv);
903
+
904
+fail:
905
+    av_free(sha);
906
+
907
+    return ret;
908
+}
909
+
910
+// Audible AAX (and AAX+) bytestream decryption
911
+static int aax_filter(uint8_t *input, int size, MOVContext *c)
912
+{
913
+    int blocks = 0;
914
+    unsigned char iv[16];
915
+
916
+    memcpy(iv, c->file_iv, 16); // iv is overwritten
917
+    blocks = size >> 4; // trailing bytes are not encrypted!
918
+    av_aes_init(c->aes_decrypt, c->file_key, 128, 1);
919
+    av_aes_crypt(c->aes_decrypt, input, input, blocks, iv, 1);
920
+
921
+    return 0;
922
+}
923
+
810 924
 /* read major brand, minor version and compatible brands and store them as metadata */
811 925
 static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
812 926
 {
... ...
@@ -3637,6 +3753,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
3637 3637
 { MKTAG('e','l','s','t'), mov_read_elst },
3638 3638
 { MKTAG('e','n','d','a'), mov_read_enda },
3639 3639
 { MKTAG('f','i','e','l'), mov_read_fiel },
3640
+{ MKTAG('a','d','r','m'), mov_read_adrm },
3640 3641
 { MKTAG('f','t','y','p'), mov_read_ftyp },
3641 3642
 { MKTAG('g','l','b','l'), mov_read_glbl },
3642 3643
 { MKTAG('h','d','l','r'), mov_read_hdlr },
... ...
@@ -4058,6 +4175,8 @@ static int mov_read_close(AVFormatContext *s)
4058 4058
     }
4059 4059
     av_freep(&mov->fragment_index_data);
4060 4060
 
4061
+    av_freep(&mov->aes_decrypt);
4062
+
4061 4063
     return 0;
4062 4064
 }
4063 4065
 
... ...
@@ -4477,6 +4596,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
4477 4477
     pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
4478 4478
     pkt->pos = sample->pos;
4479 4479
 
4480
+    if (mov->aax_mode)
4481
+        aax_filter(pkt->data, pkt->size, mov);
4482
+
4480 4483
     return 0;
4481 4484
 }
4482 4485
 
... ...
@@ -4590,6 +4712,12 @@ static const AVOption mov_options[] = {
4590 4590
         AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
4591 4591
     { "export_xmp", "Export full XMP metadata", OFFSET(export_xmp),
4592 4592
         AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
4593
+    { "activation_bytes", "Secret bytes for Audible AAX files", OFFSET(activation_bytes),
4594
+        AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
4595
+    { "audible_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files!
4596
+        "Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key),
4597
+        AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
4598
+        .flags = AV_OPT_FLAG_DECODING_PARAM },
4593 4599
     { NULL },
4594 4600
 };
4595 4601