Audible
Audible copied to clipboard
TEST: New device registration method
Need help from users for another method to register a new Audible dummy device!
Please tell me if the code below let you register a new device or if you get any error message!
import base64
import gzip
import hashlib
import hmac
import json
import secrets
import uuid
from datetime import datetime
from io import BytesIO
from typing import Union
import httpx
from pyaes import AESModeOfOperationCBC, Encrypter, Decrypter
USER_AGENT = "AmazonWebView/GoodreadsForIOS App/4.0.1/iOS/15.4.1/iPhone"
class FrcCookieCipher:
FRC_SIG_SALT: bytes = b"HmacSHA256"
FRC_AES_SALT: bytes = b"AES/CBC/PKCS7Padding"
SIGNATURE_LENGTH: int = 8
def __init__(self, password: Union[str, bytes]) -> None:
if isinstance(password, str):
password = password.encode()
self.aes_key = self._get_key(password, self.FRC_AES_SALT)
self.sig_key = self._get_key(password, self.FRC_SIG_SALT)
def _get_key(self, password, salt: bytes) -> bytes:
return hashlib.pbkdf2_hmac("sha1", password, salt, 1000, 16)
def encrypt(self, plaintext: Union[str, bytes, dict]) -> str:
if isinstance(plaintext, dict):
plaintext = json.dumps(plaintext)
if isinstance(plaintext, str):
plaintext = plaintext.encode()
compressed = gzip.compress(plaintext)
iv = secrets.token_bytes(16)
cipher = Encrypter(AESModeOfOperationCBC(self.aes_key, iv))
ciphertext = cipher.feed(compressed) + cipher.feed()
signature = hmac.new(self.sig_key, iv + ciphertext, hashlib.sha256).digest()
return base64.b64encode(
b"\0" + signature[:self.SIGNATURE_LENGTH] + iv + ciphertext
).decode()
def decrypt(self, frc: str, verify_signature: bool = True) -> Union[dict, bytes]:
pad = (4 - len(frc) % 4) * "="
frc = BytesIO(base64.b64decode(frc + pad))
frc.seek(1) # the first byte is always 0, skip them
signature = frc.read(self.SIGNATURE_LENGTH)
iv = frc.read(16)
ciphertext = frc.read()
if verify_signature:
assert self._verify_signature(signature, iv, ciphertext) is True, "Signature missmatch"
decipher = Decrypter(AESModeOfOperationCBC(self.aes_key, iv))
plaintext = decipher.feed(ciphertext) + decipher.feed()
decompressed = gzip.decompress(plaintext)
try:
return json.loads(decompressed)
except json.JSONDecodeError:
return decompressed
def _verify_signature(self, signature, iv, ciphertext) -> bool:
new_signature = hmac.new(self.sig_key, iv + ciphertext, hashlib.sha256).digest()
return signature == new_signature[:self.SIGNATURE_LENGTH]
def register(username, password, domain):
url = f"https://api.amazon.{domain}/auth/register"
device_serial = secrets.token_hex(16).upper()
frc = {
'ApplicationVersion': '3.56.2',
'DeviceOSVersion': 'iOS/15.5',
'ScreenWidthPixels': '428',
'TimeZone': '+01:00',
'ScreenHeightPixels': '926',
'ApplicationName': 'Audible',
'DeviceJailbroken': False,
'DeviceLanguage': 'de-DE',
'DeviceFingerprintTimestamp': round(datetime.utcnow().timestamp()) * 1000,
'ThirdPartyDeviceId': str(uuid.uuid4()).upper(),
'DeviceName': 'iPhone',
'Carrier': 'Vodafone.de'
}
frc = FrcCookieCipher(device_serial).encrypt(frc)
headers = {
"x-amzn-identity-auth-domain": f"api.amazon.{domain}",
"User-Agent": USER_AGENT,
"Accept-Encoding": "gzip",
"Accept": "application/json",
"Accept-Language": "en-DE",
"Accept-Charset": "utf-8"
}
json_body = {
"requested_extensions": [
"device_info",
"customer_info"
],
"cookies": {
"website_cookies": [],
"domain": ".amazon.de"
},
"registration_data": {
"domain": "Device",
"app_version": "3.56.2",
"device_serial": device_serial,
"device_type": "A2CZJZGLK2JJVM",
"device_name": (
"%FIRST_NAME%%FIRST_NAME_POSSESSIVE_STRING%%DUPE_"
"STRATEGY_1ST%Audible for iPhone"
),
"os_version": "15.5.0",
"software_version": "35602678",
"device_model": "iPhone",
"app_name": "Audible"
},
"auth_data": {
"use_global_authentication": "false",
"user_id_password": {
"user_id": username,
"password": password
}
},
"user_context_map": {
"frc": frc
},
"requested_token_type": [
"bearer",
"mac_dms",
"website_cookies"
]
}
r = httpx.post(url, headers=headers, json=json_body)
return r
if __name__ == "__main__":
username = "" # FILL OUT
password = "" # FILL OUT
domain = "de" # FILL OUT
current_otp = "" # FILL OUT IF 2FA is activated
password += current_otp
r = register(username, password, domain)
print(r)
print(r.headers)
print(r.cookies)
print(r.json())
I've found out myself, how to handle a challenge response from the server (like an OTPCode) when using the code above. But this step needs further user inputs equal to my implemented code. So I think I will don’t switch to this registration method.
This issue has not been updated for a while and will be closed soon.
This issue has automatically been closed due to no activities.