mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 04:55:07 +00:00
Verify a vault-sourced key with an FFmpeg decode after decryption. On failure flag the KID:KEY locally with its source, restore the ciphertext, and retry through the remaining vaults and then the CDM.
68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
from abc import ABCMeta, abstractmethod
|
|
from typing import Iterator, Optional, Union
|
|
from uuid import UUID
|
|
|
|
|
|
class Vault(metaclass=ABCMeta):
|
|
# a vault on this machine; Vaults.get_key tries these first, before the network ones
|
|
local: bool = False
|
|
|
|
def __init__(self, name: str, no_push: bool = False):
|
|
self.name = name
|
|
self.no_push = no_push
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.name} {type(self).__name__}"
|
|
|
|
@abstractmethod
|
|
def get_key(self, kid: Union[UUID, str], service: str) -> Optional[str]:
|
|
"""
|
|
Get the content key from the Vault by KID (Key ID) and Service.
|
|
|
|
It does not get the content key by PSSH, as the PSSH can be different depending on its
|
|
implementation, or even on how it was made. Some PSSH values can also be a CENC
|
|
Header rather than a PSSH MP4 Box, which makes the value even more confusingly different.
|
|
|
|
However, the KID never changes unless the video file itself has changed too, meaning the
|
|
content key for the presumed-matching KID would not work, further proving matching by KID
|
|
is superior.
|
|
"""
|
|
|
|
@abstractmethod
|
|
def get_keys(self, service: str) -> Iterator[tuple[str, str]]:
|
|
"""Get All Keys from Vault by Service."""
|
|
|
|
@abstractmethod
|
|
def add_key(self, service: str, kid: Union[UUID, str], key: str) -> bool:
|
|
"""Add KID:KEY to the Vault."""
|
|
|
|
@abstractmethod
|
|
def add_keys(self, service: str, kid_keys: dict[Union[UUID, str], str]) -> int:
|
|
"""
|
|
Add Multiple Content Keys with Key IDs for Service to the Vault.
|
|
The Vault ignores pre-existing Content Keys.
|
|
Raises PermissionError if the user has no permission to make the table.
|
|
"""
|
|
|
|
@abstractmethod
|
|
def get_services(self) -> Iterator[str]:
|
|
"""
|
|
Get a list of Service Tags from Vault.
|
|
|
|
Tags come back exactly as the Vault stores them, so passing one to
|
|
get_key/get_keys/add_key/add_keys reaches the namespace it came from.
|
|
"""
|
|
|
|
def is_bad_key(self, kid: Union[UUID, str], key: str) -> bool:
|
|
"""Return True if this Vault has flagged the KID:KEY as wrong."""
|
|
return False
|
|
|
|
def flag_bad_key(self, service: str, kid: Union[UUID, str], key: str, source: str) -> None:
|
|
"""Remember a KID:KEY that failed decryption and where it came from."""
|
|
|
|
def unflag_bad_key(self, kid: Union[UUID, str], key: str) -> None:
|
|
"""Forget a flagged KID:KEY, for example when a CDM licence confirmed it."""
|
|
|
|
|
|
__all__ = ("Vault",)
|