mirror of
https://git.gay/ready-dl/pyplayready.git
synced 2025-10-27 00:34:50 +00:00
+ Updated RemoteCdm/Serve to support RevLists + Added Storage class for RevList persistence + Added support for ExtData objects in BCerts + signature verification + Added an Xml Builder class (instead of xmltodict) + Added a SOAP Builder/Parser class (instead of xmltodict) + Refactored WRMHeader class to use ET (instead of xmltodict) + Upgraded ServerException detection + Removed dependencies: xmltodict, lxml + Added Util class + Minor Crypto/ElGamal class changes
95 lines
3.5 KiB
Python
95 lines
3.5 KiB
Python
from typing import Union, Tuple
|
|
|
|
from Crypto.Hash import SHA256
|
|
from Crypto.Hash.SHA256 import SHA256Hash
|
|
from Crypto.PublicKey.ECC import EccKey
|
|
from Crypto.Signature import DSS
|
|
from ecpy.curves import Point, Curve
|
|
|
|
from pyplayready.crypto.elgamal import ElGamal
|
|
from pyplayready.crypto.ecc_key import ECCKey
|
|
from pyplayready.system.util import Util
|
|
|
|
|
|
class Crypto:
|
|
curve = Curve.get_curve("secp256r1")
|
|
|
|
@staticmethod
|
|
def ecc256_encrypt(public_key: Union[ECCKey, Point], plaintext: Union[Point, bytes]) -> bytes:
|
|
if isinstance(public_key, ECCKey):
|
|
public_key = public_key.get_point(Crypto.curve)
|
|
if not isinstance(public_key, Point):
|
|
raise ValueError(f"Expecting ECCKey or Point input, got {public_key!r}")
|
|
|
|
if isinstance(plaintext, bytes):
|
|
plaintext = Point(
|
|
x=int.from_bytes(plaintext[:32], 'big'),
|
|
y=int.from_bytes(plaintext[32:64], 'big'),
|
|
curve=Crypto.curve
|
|
)
|
|
if not isinstance(plaintext, Point):
|
|
raise ValueError(f"Expecting Point or Bytes input, got {plaintext!r}")
|
|
|
|
point1, point2 = ElGamal.encrypt(plaintext, public_key)
|
|
return b''.join([
|
|
Util.to_bytes(point1.x),
|
|
Util.to_bytes(point1.y),
|
|
Util.to_bytes(point2.x),
|
|
Util.to_bytes(point2.y)
|
|
])
|
|
|
|
@staticmethod
|
|
def ecc256_decrypt(private_key: ECCKey, ciphertext: Union[Tuple[Point, Point], bytes]) -> bytes:
|
|
if isinstance(ciphertext, bytes):
|
|
ciphertext = (
|
|
Point(
|
|
x=int.from_bytes(ciphertext[:32], 'big'),
|
|
y=int.from_bytes(ciphertext[32:64], 'big'),
|
|
curve=Crypto.curve
|
|
),
|
|
Point(
|
|
x=int.from_bytes(ciphertext[64:96], 'big'),
|
|
y=int.from_bytes(ciphertext[96:128], 'big'),
|
|
curve=Crypto.curve
|
|
)
|
|
)
|
|
if not isinstance(ciphertext, Tuple):
|
|
raise ValueError(f"Expecting Tuple[Point, Point] or Bytes input, got {ciphertext!r}")
|
|
|
|
decrypted = ElGamal.decrypt(ciphertext, int(private_key.key.d))
|
|
return Util.to_bytes(decrypted.x)
|
|
|
|
@staticmethod
|
|
def ecc256_sign(private_key: Union[ECCKey, EccKey], data: Union[SHA256Hash, bytes]) -> bytes:
|
|
if isinstance(private_key, ECCKey):
|
|
private_key = private_key.key
|
|
if not isinstance(private_key, EccKey):
|
|
raise ValueError(f"Expecting ECCKey or EccKey input, got {private_key!r}")
|
|
|
|
if isinstance(data, bytes):
|
|
data = SHA256.new(data)
|
|
if not isinstance(data, SHA256Hash):
|
|
raise ValueError(f"Expecting SHA256Hash or Bytes input, got {data!r}")
|
|
|
|
signer = DSS.new(private_key, 'fips-186-3')
|
|
return signer.sign(data)
|
|
|
|
@staticmethod
|
|
def ecc256_verify(public_key: Union[ECCKey, EccKey], data: Union[SHA256Hash, bytes], signature: bytes) -> bool:
|
|
if isinstance(public_key, ECCKey):
|
|
public_key = public_key.key
|
|
if not isinstance(public_key, EccKey):
|
|
raise ValueError(f"Expecting ECCKey or EccKey input, got {public_key!r}")
|
|
|
|
if isinstance(data, bytes):
|
|
data = SHA256.new(data)
|
|
if not isinstance(data, SHA256Hash):
|
|
raise ValueError(f"Expecting SHA256Hash or Bytes input, got {data!r}")
|
|
|
|
verifier = DSS.new(public_key, 'fips-186-3')
|
|
try:
|
|
verifier.verify(data, signature)
|
|
return True
|
|
except ValueError:
|
|
return False
|