Skip to content

Python Example ​

python
import asyncio
import hashlib
import json
import random
import threading
import time

import requests
import websockets


data_json_nested_v1 = {
    "uid": "814910980",
    "dist": "637",
    "keyA": 1,
    "block": "07",
    "cores": [
        {
            "nt1": "3032128150",
            "nt2": "432058316",
            "par": "5"
        },
        {
            "nt1": "808897476",
            "nt2": "904284173",
            "par": "3"
        }
    ]
}

data_json_nested_v2 = {
    "uid": "814910980",
    "keyA": 0,
    "block": "51",
    "cores": [
        {
            "ntp": "1721933979",
            "ks1": "2137132887"
        },
        {
            "ntp": "2755935295",
            "ks1": "2443342898"
        },
        {
            "ntp": "1254421459",
            "ks1": "2132982750"
        },
        {
            "ntp": "4059870459",
            "ks1": "3290103030"
        }
    ]
}

data_json_static_nested = {
    "uid": "961684779",
    "keyA": 0,
    "block": "51",
    "cores": [
        {
            "nt1": "18874693",
            "nt2": "2603586571"
        },
        {
            "nt1": "18874693",
            "nt2": "3619625478"
        }
    ]
}

data_json_darkside = {
    "uid": "2586461451",
    "keyA": 1,
    "block": "07",
    "cores": [
        {
            "ntp": "3617531615",
            "par": "14978751391669411775",
            "ks1": "1128099048457733",
            "nr": "0",
            "ar": "0"
        },
        {
            "ntp": "3617531615",
            "par": "6917397047991074623",
            "ks1": "867234333445718795",
            "nr": "1",
            "ar": "0"
        },
        {
            "ntp": "3617531615",
            "par": "549386136641275711",
            "ks1": "362823457123927557",
            "nr": "2",
            "ar": "0"
        }
    ]
}

data_json_mfkey32 = {
    "uid": "305419896",
    "keyA": 1,
    "block": "0",
    "cores": {
        "core1": {
            "nt": "450420523",
            "nr": "489775140",
            "ar": "1645146184"
        },
        "core2": {
            "nt": "819383047",
            "nr": "3307239394",
            "ar": "2205861402"
        }
    }
}

data_json_mfkey64 = {
    "uid": "341286761",
    "keyA": 1,
    "block": "0",
    "cores": {
        "nt": "3464774241",
        "nr": "4161051851",
        "ar": "86362191",
        "at": "2486291520"
    }
}

data_json_nested_rf08s = {
    "uid": "3779066835",
    "keyA": True,
    "block": "03",
    "cores": {
        "typeA": {
            "nt1": "4041266571",
            "nt2": "3748496197",
            "par": "0"
        },
        "typeB": {
            "nt1": "332352875",
            "nt2": "3968861009",
            "par": "15"
        }
    }
}


# ---------- Change the information here   ----------

appId = "Your appId"
appSecret = "Your appSecret"

# ---------------------------------------------------

baseHost = "decrypt.nikola-lab.cn"
openApiBaseUrl = f"https://{baseHost}/openapi/v1"


def create_sign(time_stamp, nt, name):
    """
        Create a signature based on timestamp, random number, and appSecret, using the algorithm of the passed in name
    :param name: The hash method.
    :param time_stamp: Current unix timestamp
    :param nt: Random number
    :return: The result for the signed.
    """
    hash_obj = hashlib.new(name)
    hash_obj.update(f"{nt}:{time_stamp}:{appSecret}".encode(encoding="ascii"))
    return hash_obj.hexdigest()


def make_decrypt_task_url(url: str):
    return f'{openApiBaseUrl}/decrypt/task/{url}/'


# Open API URL and Test Parameter Mapping Table
OPEM_API_MAP = {
    'nested_raw': {
        'url': make_decrypt_task_url("create/nested/raw_response"),
        'data': data_json_nested_v1
    },
    'nested_ntpks1': {
        'url': make_decrypt_task_url("create/nested/ntp_ks1"),
        'data': data_json_nested_v2
    },
    'static_nested': {
        'url': make_decrypt_task_url("create/nested/static_fast"),
        'data': data_json_static_nested
    },
    'darkside': {
        'url': make_decrypt_task_url("create/darkside"),
        'data': data_json_darkside
    },
    'mfkey32': {
        'url': make_decrypt_task_url("create/mfkey32"),
        'data': data_json_mfkey32
    },
    'mfkey64': {
        'url': make_decrypt_task_url("create/mfkey64"),
        'data': data_json_mfkey64
    },
    'hardnested': {
        'url': make_decrypt_task_url("create/nested/hard_nonce"),
        'file': "./nonces.bin",
    },
    "nested_rf08s": {
        'url': make_decrypt_task_url("create/nested/rf08s_2x1nt"),
        'data': data_json_nested_rf08s
    }
}

print(f"The signature algorithms supported by the current operating environment: {hashlib.algorithms_available}")


def create_openapi_key_args():
    """
        Key verification parameters required for creating OpenApi
    :return:
    """
    time_stamp_ms = round(time.time() * 1000.0)
    nonce = random.randint(0, 0xFFFFFFFF)
    # time_stamp_ms = 1700818596082 # Simulate old timestamps to achieve the effect of testing the first stage of replay prevention
    # nonce = 0xFFFFFFFF  # Fix a random number to achieve the effect of testing the second stage of anti replay
    sign_method = "SHA1"
    return {
        'timestamp': str(time_stamp_ms),
        'nonce': str(nonce),
        'method': sign_method,
        'sign': create_sign(time_stamp_ms, nonce, sign_method)
    }


def make_request_header():
    """
        Create a http headers object.
    :return:
    """
    open_api_args = create_openapi_key_args()
    return {
        'X-CA-App-Id': appId,
        'X-CA-Timestamp': open_api_args['timestamp'],
        'X-CA-Nonce': open_api_args['nonce'],
        'X-CA-Signature': open_api_args['sign'],
        'X-CA-Signature-Method': open_api_args['method'],
    }


def request_and_download_body(url, method="GET"):
    """
        Request url to download body stream.
    :param url: The URL to be requested
    :param method: http method
    :return: body bytes
    """
    openapi_header = make_request_header()
    resp = requests.request(method, url, headers=openapi_header)
    if resp.status_code == 200:
        return resp.content
    raise Exception(f"{url} Dwonload failed, status_code = {resp.status_code}")


def request_with_data(url: str, data: str | None, method: str, file_upload=False):
    """
        Carrying data request specified URL
    :param url: The URL to be requested
    :param data: data
    :param method: http method
    :param file_upload: Is it uploading a file
    :return: JSON object for response
    """
    openapi_header = make_request_header()
    # If it is determined that a file needs to be uploaded, we do not need to set the content as a JSON attribute
    if file_upload:
        openapi_header['Content-Type'] = 'application/octet-stream'  # TODO Note that the pure upload interface only accepts one binary file upload
    else:
        openapi_header['Content-Type'] = 'application/json'
    # Call requests.request to start the request
    resp = requests.request(method, url, data=data, headers=openapi_header)
    # print(resp.content.decode())
    json_obj = json.loads(resp.content.decode())
    if resp.status_code >= 400:
        raise RuntimeError(f"Request {json_obj['path']} failed, "
                           f"Status: {json_obj['error']}({json_obj['status']}), "
                           f"Message: {json_obj['message']}")
    return json_obj


def request_create_task():
    """
        Request to create a task and return the UUID of the task after successful creation
    :return: The UUID string of the task
    """
    open_api_item = OPEM_API_MAP['nested_raw']  # TODO Select the OPENAPI you need to test decryption from the mapping table

    # Automatically determine whether data or file upload is required based on the parameters required by the API
    data = None
    is_file_upload = False
    if 'data' in open_api_item:
        data = json.dumps(open_api_item['data'])
    if 'file' in open_api_item:
        data = open(open_api_item['file'], 'rb')
        is_file_upload = True

    # Start request
    content_obj = request_with_data(open_api_item['url'], data, 'POST', is_file_upload)
    print(content_obj)
    return content_obj


def request_query_task(uuid: str):
    """
        Request to query task information
    :param uuid: The uuid of the task
    :return: Task status information
    """
    url = make_decrypt_task_url(f"{uuid}/information")
    print(f"The informations of task: {url}")
    result_obj = request_with_data(url, None, 'GET')
    print(result_obj)
    return result_obj


async def client():
    """
        Connect to the decryption result notification service to listen. 
        If decryption is completed, this websocket will call back once to notify a UUID that the task has been completed
    :return:
    """
    open_api_args = create_openapi_key_args()
    url_for_result_notify = (f"wss://{baseHost}/openapi/v1/decrypt/result/notify?"
                             f"appId={appId}&timestamp={open_api_args['timestamp']}&nonce={open_api_args['nonce']}"
                             f"&signature={open_api_args['sign']}&signature_method={open_api_args['method']}")
    async with websockets.connect(url_for_result_notify) as websocket:
        # Process messages received on the connection.
        async for message in websocket:
            print(message)
            json_obj = json.loads(message)
            print(f"The server notification task completed, The UUID of task is {json_obj['uuid']}, result: {json_obj['keysOrLink']}")


def download_keys_from_task(uuid, task_info):
    """
        Download keys from task result
    :param uuid: the uuid of task
    :param task_info: info of the task
    :return:
    """
    url = make_decrypt_task_url(f"{uuid}/keys")
    # {'keysOrLink': 'daf7fabfff41,ffffffffff41,ba6a550955e1', 'finish': True}
    # {'keysOrLink': {'size': 16411, 'link': 'b82a906b4bd708b7f56c65c85930d6818fd8d0c0'}, 'finish': True}
    if task_info['keysOrLink'] is None:
        return  # Empty, maybe decrypt task is failed.
    try:
        # If it can be formatted as JSON, it means that the decryption result is the result of a large number of keys, and we need to download the result
        link_info = task_info['keysOrLink']
        print(f"Keys count = {link_info['size']},Keys link = {link_info['link']}")
        key_bytes = request_and_download_body(url)
        file_name = link_info['link'] + ".bin"
        with open(file_name, 'wb+') as f:
            f.write(key_bytes)
        print(f"Dwonload success, save to {file_name}, the size of file is{len(key_bytes)}bytes")
    except Exception as e:
        # print(e)
        # If it cannot be formatted as JSON, it means it is a normal decryption with a small amount of results, and we can directly display it
        print(f"Decrypt result = {task_info['keysOrLink']}")


def test():
    """
        TEST
    :return:
    """
    test_count = 1
    task_list = []

    # Create task
    for i in range(test_count):
        response = request_create_task()
        task_list.append({
            'uuid': response['uuid'],
            'done': False
        })

    # TODO You can temporarily skip queries
    skip_result_query = False
    if skip_result_query:
        return

    # Query in loop
    while True:
        is_all_done = True  # Flag whether all decryption tasks have been completed currently
        # Query one by one in the loop
        for i in range(test_count):
            if task_list[i]['done']:
                continue
            is_all_done = False  # Found an unfinished task, we need to continue querying
            # Request task informations, to get result of task.
            task_info = request_query_task(task_list[i]['uuid'])
            if task_info is not None:
                if task_info['finish']:
                    task_list[i]['done'] = True

        time.sleep(2)  # TODO Don't request too quickly, it may be blocked by current limiting risk control

        # All tasks have been completed, we can now end the query
        if is_all_done:
            break
        print("")


# Optional testing of multi-threaded requests
# for thread_i in range(50):
#     threading.Thread(target=test, ).start()

# Start ws client processing
# threading.Thread(target=lambda: asyncio.run(client()), ).start()

# Single threaded testing
test()

Power by Nikola-LAB