#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Pascal implementation by lulzkabulz. Python translation by apprenticenaomi. DeDRM integration by anon.
+# Pascal implementation by lulzkabulz. Python translation by apprenticenaomi. DeDRM integration by anon. VoucherEnvelope v2/v3 support by apprenticesakuya.
# BinaryIon.pas + DrmIon.pas + IonSymbols.pas
from __future__ import with_statement
'com.amazon.drm.EnvelopeMetadata@2.0',
'com.amazon.drm.EncryptedPage@2.0',
'com.amazon.drm.PlainText@2.0', 'compression_algorithm',
- 'com.amazon.drm.Compressed@1.0', 'priority', 'refines']
+ 'com.amazon.drm.Compressed@1.0', 'page_index_table',
+ 'com.amazon.drm.VoucherEnvelope@2.0', 'com.amazon.drm.VoucherEnvelope@3.0' ]
def addprottable(ion):
ion.addtocatalog("ProtectedData", 1, SYM_NAMES)
return msg[:-paddinglen]
+# every VoucherEnvelope version has a corresponding "word" and magic number, used in obfuscating the shared secret
+VOUCHER_VERSION_INFOS = {
+ 2: [b'Antidisestablishmentarianism', 5],
+ 3: [b'Floccinaucinihilipilification', 8]
+}
+
+
+# obfuscate shared secret according to the VoucherEnvelope version
+def obfuscate(secret, version):
+ if version == 1: # v1 does not use obfuscation
+ return secret
+
+ params = VOUCHER_VERSION_INFOS[version]
+ word = params[0]
+ magic = params[1]
+
+ # extend secret so that its length is divisible by the magic number
+ if len(secret) % magic != 0:
+ secret = secret + b'\x00' * (magic - len(secret) % magic)
+
+ secret = bytearray(secret)
+
+ obfuscated = bytearray(len(secret))
+ wordhash = bytearray(hashlib.sha256(word).digest())
+
+ # shuffle secret and xor it with the first half of the word hash
+ for i in range(0, len(secret)):
+ index = i // (len(secret) // magic) + magic * (i % (len(secret) // magic))
+ obfuscated[index] = secret[i] ^ wordhash[index % 16]
+
+ return obfuscated
+
+
class DrmIonVoucher(object):
envelope = None
+ version = None
voucher = None
drmkey = None
license_type = "Unknown"
else:
_assert(False, "Unknown lock parameter: %s" % param)
- sharedsecret = shared.encode("UTF-8")
+ sharedsecret = obfuscate(shared.encode('ASCII'), self.version)
- key = hmac.new(sharedsecret, sharedsecret[:5], digestmod=hashlib.sha256).digest()
+ key = hmac.new(sharedsecret, "PIDv3", digestmod=hashlib.sha256).digest()
aes = AES.new(key[:32], AES.MODE_CBC, self.cipheriv[:16])
b = aes.decrypt(self.ciphertext)
b = pkcs7unpad(b, 16)
def parse(self):
self.envelope.reset()
_assert(self.envelope.hasnext(), "Envelope is empty")
- _assert(self.envelope.next() == TID_STRUCT and self.envelope.gettypename() == "com.amazon.drm.VoucherEnvelope@1.0",
+ _assert(self.envelope.next() == TID_STRUCT and str.startswith(self.envelope.gettypename(), "com.amazon.drm.VoucherEnvelope@"),
"Unknown type encountered in envelope, expected VoucherEnvelope")
+ self.version = int(self.envelope.gettypename().split('@')[1][:-2])
self.envelope.stepin()
while self.envelope.hasnext():