|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
''' Decode distribute docs. |
|
|
|
|
|
Based on the algorithm described by Changwoo Ryu |
|
|
See https://groups.google.com/forum/#!topic/hwp-foss/d2KL2ypR89Q |
|
|
''' |
|
|
from __future__ import absolute_import |
|
|
from __future__ import print_function |
|
|
from __future__ import unicode_literals |
|
|
from io import BytesIO |
|
|
import logging |
|
|
|
|
|
from .plat import get_aes128ecb_decrypt |
|
|
from .recordstream import read_record |
|
|
from .tagids import HWPTAG_DISTRIBUTE_DOC_DATA |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
def decode(stream): |
|
|
distdoc_data_record = read_record(stream, 0) |
|
|
if distdoc_data_record['tagid'] != HWPTAG_DISTRIBUTE_DOC_DATA: |
|
|
raise IOError('the first record is not an HWPTAG_DISTRIBUTE_DOC_DATA') |
|
|
distdoc_data = distdoc_data_record['payload'] |
|
|
key = decode_head_to_key(distdoc_data) |
|
|
tail = stream.read() |
|
|
decrypted = decrypt_tail(key, tail) |
|
|
return BytesIO(decrypted) |
|
|
|
|
|
|
|
|
class Random: |
|
|
''' MSVC's srand()/rand() like pseudorandom generator. |
|
|
''' |
|
|
|
|
|
def __init__(self, seed): |
|
|
self.seed = seed |
|
|
|
|
|
def rand(self): |
|
|
self.seed = (self.seed * 214013 + 2531011) & 0xffffffff |
|
|
value = (self.seed >> 16) & 0x7fff |
|
|
return value |
|
|
|
|
|
|
|
|
def decode_head_to_sha1(record_payload): |
|
|
''' Decode HWPTAG_DISTRIBUTE_DOC_DATA. |
|
|
|
|
|
It's the sha1 digest of user-supplied password string, i.e., |
|
|
|
|
|
'12345' -> hashlib.sha1('12345').digest() |
|
|
|
|
|
''' |
|
|
if len(record_payload) != 256: |
|
|
raise ValueError('payload size must be 256 bytes') |
|
|
|
|
|
data = bytearray(record_payload) |
|
|
seed = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0] |
|
|
random = Random(seed) |
|
|
|
|
|
n = 0 |
|
|
for i in range(256): |
|
|
if n == 0: |
|
|
key = random.rand() & 0xff |
|
|
n = (random.rand() & 0xf) + 1 |
|
|
if i >= 4: |
|
|
data[i] = data[i] ^ key |
|
|
n -= 1 |
|
|
|
|
|
|
|
|
decoded = data |
|
|
sha1offset = 4 + (seed & 0xf) |
|
|
|
|
|
ucs16le = decoded[sha1offset:sha1offset + 80] |
|
|
return ucs16le |
|
|
|
|
|
|
|
|
def decode_head_to_key(record_payload): |
|
|
sha1ucs16le = decode_head_to_sha1(record_payload) |
|
|
return sha1ucs16le[:16] |
|
|
|
|
|
|
|
|
def decrypt_tail(key, encrypted_tail): |
|
|
decrypt = get_aes128ecb_decrypt() |
|
|
return decrypt(key, encrypted_tail) |
|
|
|