From patchwork Mon Nov 2 14:12:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vesselin Bontchev X-Patchwork-Id: 23337 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 23D5744A00D for ; Mon, 2 Nov 2020 16:18:57 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id F288868A800; Mon, 2 Nov 2020 16:18:56 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from forward400p.mail.yandex.net (forward400p.mail.yandex.net [77.88.28.105]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E8B1568A543 for ; Mon, 2 Nov 2020 16:18:50 +0200 (EET) Received: from mxback29j.mail.yandex.net (mxback29j.mail.yandex.net [IPv6:2a02:6b8:0:1619::229]) by forward400p.mail.yandex.net (Yandex) with ESMTP id BC71B1BC133F; Mon, 2 Nov 2020 17:12:01 +0300 (MSK) Received: from localhost (localhost [::1]) by mxback29j.mail.yandex.net (mxback/Yandex) with ESMTP id KxHZ2sOspQ-C1aKKqtl; Mon, 02 Nov 2020 17:12:01 +0300 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex.com; s=mail; t=1604326321; bh=HQ2kHeMR9IQ0YE4gWmJdBxy28dX3yBjpL4CL/q5QfIg=; h=Date:Message-Id:Cc:Subject:To:From; b=GoJ3YHAxCwAjsvnRFI1A/sM+hlKCLgAP/FVQ9donrFNSpXf34dFwQWBuKXWeUXkUr WG4245w5XQyrWBkIMoaJV+CIuXUCyP8aradoO1/2cfpc20U10Ig2j9FhXELeyWkV6E pTZ1V20T38G0reDh9RMof96bgXZYEMhdzcV3+e1s= Authentication-Results: mxback29j.mail.yandex.net; dkim=pass header.i=@yandex.com Received: by iva2-957330f6752d.qloud-c.yandex.net with HTTP; Mon, 02 Nov 2020 17:12:00 +0300 From: Vesselin Bontchev Envelope-From: vesselin-bontchev@yandex.com To: FFmpeg development discussions and patches MIME-Version: 1.0 X-Mailer: Yamail [ http://yandex.ru ] 5.0 Date: Mon, 02 Nov 2020 17:12:00 +0300 Message-Id: <1844221604326075@mail.yandex.com> Subject: [FFmpeg-devel] [PATCH] [v4] Add support for playing Audible AAXC (.aaxc) files X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Michael Niedermayer , Carl Eugen Hoyos Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" The AAXC container format is the same as the (already supported) Audible AAX format but it uses a different encryption scheme. Note: audible_key and audible_iv values are variable (per file) and are externally fed. It is possible to extend https://github.com/mkb79/Audible to derive the audible_key and audible_key values. Relevant code: def decrypt_voucher(deviceSerialNumber, customerId, deviceType, asin, voucher): buf = (deviceType + deviceSerialNumber + customerId + asin).encode("ascii") digest = hashlib.sha256(buf).digest() key = digest[0:16] iv = digest[16:] # decrypt "voucher" using AES in CBC mode with no padding cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(voucher).rstrip(b"\x00") # improve this! return json.loads(plaintext) The decrypted "voucher" has the required audible_key and audible_iv values. Update (Nov-2020): This patch has now been tested by multiple folks - details at the following URL: https://github.com/mkb79/Audible/issues/3 Signed-off-by: Vesselin Bontchev ... Thanks Carl for all the reviews. Hopefully we can merge this patch soon :) Vesselin Subject: [PATCH] [v4] Add support for playing Audible AAXC (.aaxc) files The AAXC container format is the same as the (already supported) Audible AAX format but it uses a different encryption scheme. Note: audible_key and audible_iv values are variable (per file) and are externally fed. It is possible to extend https://github.com/mkb79/Audible to derive the audible_key and audible_key values. Relevant code: def decrypt_voucher(deviceSerialNumber, customerId, deviceType, asin, voucher): buf = (deviceType + deviceSerialNumber + customerId + asin).encode("ascii") digest = hashlib.sha256(buf).digest() key = digest[0:16] iv = digest[16:] # decrypt "voucher" using AES in CBC mode with no padding cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(voucher).rstrip(b"\x00") # improve this! return json.loads(plaintext) The decrypted "voucher" has the required audible_key and audible_iv values. Update (Nov-2020): This patch has now been tested by multiple folks - details at the following URL: https://github.com/mkb79/Audible/issues/3 Signed-off-by: Vesselin Bontchev --- libavformat/isom.h | 4 ++++ libavformat/mov.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/libavformat/isom.h b/libavformat/isom.h index 78495fd336..5a6d504090 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -286,6 +286,10 @@ typedef struct MOVContext { int activation_bytes_size; void *audible_fixed_key; int audible_fixed_key_size; + void *audible_key; + int audible_key_size; + void *audible_iv; + int audible_iv_size; struct AVAES *aes_decrypt; uint8_t *decryption_key; int decryption_key_len; diff --git a/libavformat/mov.c b/libavformat/mov.c index dd0db6bca7..2b90e31170 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -1075,6 +1075,30 @@ fail: return ret; } +static int mov_aaxc_crypto(MOVContext *c) +{ + if (c->audible_key_size != 16) { + av_log(c->fc, AV_LOG_FATAL, "[aaxc] audible_key value needs to be 16 bytes!\n"); + return AVERROR(EINVAL); + } + + if (c->audible_iv_size != 16) { + av_log(c->fc, AV_LOG_FATAL, "[aaxc] audible_iv value needs to be 16 bytes!\n"); + return AVERROR(EINVAL); + } + + c->aes_decrypt = av_aes_alloc(); + if (!c->aes_decrypt) { + return AVERROR(ENOMEM); + } + + memcpy(c->file_key, c->audible_key, 16); + memcpy(c->file_iv, c->audible_iv, 16); + c->aax_mode = 1; + + return 0; +} + // Audible AAX (and AAX+) bytestream decryption static int aax_filter(uint8_t *input, int size, MOVContext *c) { @@ -1123,6 +1147,11 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_dict_set(&c->fc->metadata, "compatible_brands", comp_brands_str, AV_DICT_DONT_STRDUP_VAL); + // Logic for handling Audible's .aaxc files + if (!strcmp(type, "aaxc")) { + mov_aaxc_crypto(c); + } + return 0; } @@ -8170,6 +8199,10 @@ static const AVOption mov_options[] = { AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = FLAGS }, { "activation_bytes", "Secret bytes for Audible AAX files", OFFSET(activation_bytes), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM }, + { "audible_key", "AES-128 Key for Audible AAXC files", OFFSET(audible_key), + AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM }, + { "audible_iv", "AES-128 IV for Audible AAXC files", OFFSET(audible_iv), + AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM }, { "audible_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files! "Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key), AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},