import struct
from Crypto.Cipher import AES
 
PASSWORD_DELIMITER = "%$%$"
AES_KEY    = [0xB8, 0x36, 0x3C, 0x9B, 0x77, 0xDA, 0xED, 0x4B, 
              0x9A, 0xBB, 0x9F, 0x2F, 0x6D, 0xF5, 0xF1, 0xD5, 
              0xCB, 0x64, 0x97, 0x5D, 0x5D, 0x3B, 0xCE, 0xE8, 
              0x82, 0x7F, 0x2F, 0x42, 0x23, 0x5F, 0x92, 0x29]
AES_IV     = [0x17, 0x81, 0x6F, 0x96, 0x5B, 0x07, 0x1E, 0xD4,
              0xEC, 0x52, 0x84, 0xFF, 0x94, 0xFF, 0x71, 0x76]
AES_MANGLE = [11, 17, 23, 29]
BLOCK_SIZE = 24
 
def AscUnvisible(s):
    r = ""
    for c in s:
        if c == "~":
            c = "?"
        c = chr(ord(c) - 0x21)
        r += c
    return r
 
def DesEnhSysToLong(d):
    assert len(d) >= 4
 
    x = 1
    r = 0
 
    for i in range(0, 5):
        v0 = ((x << 1) + x) & 0xffffffff
        v1 = (v0 << 5) & 0xffffffff
        r += ord(d[i]) * x
        x = (v1 - v0) & 0xffffffff
 
    return r
 
def PlainToBin_AES(s):
    out = []
    for i in range(0, 16, 4):
        r = DesEnhSysToLong(s[(i/4)*5:])
        
        out.append(r)
 
    r = ""
    for x in out:
        r += struct.pack(">I", x)
 
    return r
 
def GenRandPwdKey_AES(key, salt):
    assert len(key) == 32 and len(salt) == 4
 
    r = list(key[:])
 
    for i in range(len(AES_MANGLE)):
        r[AES_MANGLE[i]] = ord(salt[i])
 
    return "".join([chr(x) for x in r])
 
def decrypt_password(s):
    assert s.startswith(PASSWORD_DELIMITER) and s.endswith(PASSWORD_DELIMITER), \
        "[!] Not a Huawei AES256 password :-("
    t = s[len(PASSWORD_DELIMITER):-len(PASSWORD_DELIMITER)]
 
    t = AscUnvisible(t)
 
    plaintext = ""
    for block_offset in range(0, len(t), BLOCK_SIZE):
        block = t[block_offset:block_offset + BLOCK_SIZE]
 
        # Last 4 chars are the salt
        salt = block[20:]
 
        r = GenRandPwdKey_AES(AES_KEY, salt)
        block = PlainToBin_AES(block)
        
        iv = "".join([chr(x) for x in AES_IV])
        cipher = AES.new(r, AES.MODE_CBC, iv)
        msg = cipher.decrypt(block)
        
        plaintext += msg
 
    plaintext = plaintext.rstrip("\x00")
    return plaintext
 
print (decrypt_password("%$%$P@=yCB4*qVAPCxA=#&+,,kbY`j{A;2|K<4DaC4;0Fxz+$qhc%$%$"))