parent
4ea1fbd1f8
commit
c910d1f349
|
@ -10,4 +10,4 @@ from .session import *
|
||||||
from .xml_key import *
|
from .xml_key import *
|
||||||
from .xmrlicense import *
|
from .xmrlicense import *
|
||||||
|
|
||||||
__version__ = "0.4.2"
|
__version__ = "0.4.3"
|
||||||
|
|
|
@ -214,11 +214,6 @@ class Certificate(_BCertStructs):
|
||||||
max_header: int = 15360,
|
max_header: int = 15360,
|
||||||
max_chain_depth: int = 2
|
max_chain_depth: int = 2
|
||||||
) -> Certificate:
|
) -> Certificate:
|
||||||
if not cert_id:
|
|
||||||
raise ValueError("Certificate ID is required")
|
|
||||||
if not client_id:
|
|
||||||
raise ValueError("Client ID is required")
|
|
||||||
|
|
||||||
basic_info = Container(
|
basic_info = Container(
|
||||||
cert_id=cert_id,
|
cert_id=cert_id,
|
||||||
security_level=security_level,
|
security_level=security_level,
|
||||||
|
@ -250,9 +245,20 @@ class Certificate(_BCertStructs):
|
||||||
feature = Container(
|
feature = Container(
|
||||||
feature_count=3,
|
feature_count=3,
|
||||||
features=ListContainer([
|
features=ListContainer([
|
||||||
4, # SECURE_CLOCK
|
# 1, # Transmitter
|
||||||
9, # REVOCATION_LIST_FEATURE
|
# 2, # Receiver
|
||||||
13 # SUPPORTS_PR3_FEATURES
|
# 3, # SharedCertificate
|
||||||
|
4, # SecureClock
|
||||||
|
5, # AntiRollBackClock
|
||||||
|
# 6, # ReservedMetering
|
||||||
|
# 7, # ReservedLicSync
|
||||||
|
# 8, # ReservedSymOpt
|
||||||
|
9, # CRLS (Revocation Lists)
|
||||||
|
# 10, # ServerBasicEdition
|
||||||
|
# 11, # ServerStandardEdition
|
||||||
|
# 12, # ServerPremiumEdition
|
||||||
|
13, # PlayReady3Features
|
||||||
|
# 14, # DeprecatedSecureStop
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
feature_attribute = Container(
|
feature_attribute = Container(
|
||||||
|
@ -385,8 +391,10 @@ class Certificate(_BCertStructs):
|
||||||
return self._BCERT
|
return self._BCERT
|
||||||
|
|
||||||
def verify_signature(self):
|
def verify_signature(self):
|
||||||
sign_payload = self.dumps()[:-144]
|
signature_object = self.get_attribute(8)
|
||||||
signature_attribute = self.get_attribute(8).attribute
|
signature_attribute = signature_object.attribute
|
||||||
|
|
||||||
|
sign_payload = self.dumps()[:-signature_object.length]
|
||||||
|
|
||||||
raw_signature_key = signature_attribute.signature_key
|
raw_signature_key = signature_attribute.signature_key
|
||||||
signature_key = ECC.construct(
|
signature_key = ECC.construct(
|
||||||
|
|
|
@ -56,15 +56,17 @@ class ECCKey:
|
||||||
with Path(path).open(mode="rb") as f:
|
with Path(path).open(mode="rb") as f:
|
||||||
return cls.loads(f.read())
|
return cls.loads(f.read())
|
||||||
|
|
||||||
def dumps(self):
|
def dumps(self, private_only=False):
|
||||||
|
if private_only:
|
||||||
|
return self.private_bytes()
|
||||||
return self.private_bytes() + self.public_bytes()
|
return self.private_bytes() + self.public_bytes()
|
||||||
|
|
||||||
def dump(self, path: Union[Path, str]) -> None:
|
def dump(self, path: Union[Path, str], private_only=False) -> None:
|
||||||
if not isinstance(path, (Path, str)):
|
if not isinstance(path, (Path, str)):
|
||||||
raise ValueError(f"Expecting Path object or path string, got {path!r}")
|
raise ValueError(f"Expecting Path object or path string, got {path!r}")
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
path.write_bytes(self.dumps())
|
path.write_bytes(self.dumps(private_only))
|
||||||
|
|
||||||
def get_point(self, curve: Curve) -> Point:
|
def get_point(self, curve: Curve) -> Point:
|
||||||
return Point(self.key.pointQ.x, self.key.pointQ.y, curve)
|
return Point(self.key.pointQ.x, self.key.pointQ.y, curve)
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Key:
|
||||||
AES_128_ECB = 0x0003
|
AES_128_ECB = 0x0003
|
||||||
COCKTAIL = 0x0004
|
COCKTAIL = 0x0004
|
||||||
AES_128_CBC = 0x0005
|
AES_128_CBC = 0x0005
|
||||||
|
KEYEXCHANGE = 0x0006
|
||||||
UNKNOWN = 0xffff
|
UNKNOWN = 0xffff
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -24,7 +25,8 @@ class Key:
|
||||||
CHAINED_LICENSE = 0x0002
|
CHAINED_LICENSE = 0x0002
|
||||||
ECC_256 = 0x0003
|
ECC_256 = 0x0003
|
||||||
ECC_256_WITH_KZ = 0x0004
|
ECC_256_WITH_KZ = 0x0004
|
||||||
SCALABLE = 0x0005
|
TEE_TRANSIENT = 0x0005
|
||||||
|
ECC_256_VIA_SYMMETRIC = 0x0006
|
||||||
UNKNOWN = 0xffff
|
UNKNOWN = 0xffff
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -279,7 +279,7 @@ def export_device(ctx: click.Context, prd_path: Path, out_dir: Optional[Path] =
|
||||||
|
|
||||||
if device.group_key:
|
if device.group_key:
|
||||||
group_key_path = out_path / "zgpriv.dat"
|
group_key_path = out_path / "zgpriv.dat"
|
||||||
group_key_path.write_bytes(device.group_key.dumps())
|
group_key_path.write_bytes(device.group_key.dumps(private_only=True))
|
||||||
log.info("Exported Group Key as zgpriv.dat")
|
log.info("Exported Group Key as zgpriv.dat")
|
||||||
else:
|
else:
|
||||||
log.warning("Cannot export zgpriv.dat, as v2 devices do not save the group key")
|
log.warning("Cannot export zgpriv.dat, as v2 devices do not save the group key")
|
||||||
|
|
|
@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pyplayready"
|
name = "pyplayready"
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
description = "pyplayready CDM (Content Decryption Module) implementation in Python."
|
description = "pyplayready CDM (Content Decryption Module) implementation in Python."
|
||||||
license = "CC BY-NC-ND 4.0"
|
license = "CC BY-NC-ND 4.0"
|
||||||
authors = ["DevLARLEY, Erevoc", "DevataDev"]
|
authors = ["DevLARLEY, Erevoc", "DevataDev"]
|
||||||
|
|
Loading…
Reference in New Issue