Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TEST: New device registration method #106

Closed
mkb79 opened this issue Aug 22, 2022 · 4 comments
Closed

TEST: New device registration method #106

mkb79 opened this issue Aug 22, 2022 · 4 comments
Labels
help wanted Extra attention is needed stale

Comments

@mkb79
Copy link
Owner

mkb79 commented Aug 22, 2022

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!

@mkb79 mkb79 added the help wanted Extra attention is needed label Aug 22, 2022
@mkb79
Copy link
Owner Author

mkb79 commented Aug 22, 2022

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())

@mkb79
Copy link
Owner Author

mkb79 commented Sep 5, 2022

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.

@github-actions
Copy link

This issue has not been updated for a while and will be closed soon.

@github-actions github-actions bot added the stale label Sep 27, 2023
@github-actions
Copy link

github-actions bot commented Oct 8, 2023

This issue has automatically been closed due to no activities.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Oct 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed stale
Projects
None yet
Development

No branches or pull requests

1 participant