summaryrefslogtreecommitdiffstats
path: root/samloader/crypt.py
blob: 0b57c5a54b18beebd436623efb553c74dae10390 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# SPDX-License-Identifier: GPL-3.0+
# Copyright (C) 2020 nlscc

""" Calculate keys and decrypt encrypted firmware packages. """

import hashlib
import xml.etree.ElementTree as ET
from Cryptodome.Cipher import AES
from clint.textui import progress

from . import request
from . import fusclient

# PKCS#7 unpad
unpad = lambda d: d[:-d[-1]]

def getv4key(version, model, region):
    """ Retrieve the AES key for V4 encryption. """
    client = fusclient.FUSClient()
    req = request.binaryinform(version, model, region, client.nonce)
    resp = client.makereq("NF_DownloadBinaryInform.do", req)
    root = ET.fromstring(resp)
    fwver = root.find("./FUSBody/Results/LATEST_FW_VERSION/Data").text
    logicval = root.find("./FUSBody/Put/LOGIC_VALUE_FACTORY/Data").text
    deckey = request.getlogiccheck(fwver, logicval)
    return hashlib.md5(deckey.encode()).digest()

def getv2key(version, model, region):
    """ Calculate the AES key for V2 (legacy) encryption. """
    deckey = region + ":" + model + ":" + version
    return hashlib.md5(deckey.encode()).digest()

def decrypt_progress(inf, outf, key, length):
    """ Decrypt a stream of data while showing a progress bar. """
    cipher = AES.new(key, AES.MODE_ECB)
    if length % 16 != 0:
        raise Exception("invalid input block size")
    chunks = length//4096+1
    for i in progress.bar(range(chunks)):
        block = inf.read(4096)
        if not block:
            break
        decblock = cipher.decrypt(block)
        if i == chunks - 1:
            outf.write(unpad(decblock))
        else:
            outf.write(decblock)