467 lines
18 KiB
Protocol Buffer
467 lines
18 KiB
Protocol Buffer
syntax = "proto2";
|
|
|
|
// from x86 (partial), most of it from the ARM version:
|
|
message ClientIdentification {
|
|
enum TokenType {
|
|
KEYBOX = 0;
|
|
DEVICE_CERTIFICATE = 1;
|
|
REMOTE_ATTESTATION_CERTIFICATE = 2;
|
|
}
|
|
message NameValue {
|
|
required string Name = 1;
|
|
required string Value = 2;
|
|
}
|
|
message ClientCapabilities {
|
|
enum HdcpVersion {
|
|
HDCP_NONE = 0;
|
|
HDCP_V1 = 1;
|
|
HDCP_V2 = 2;
|
|
HDCP_V2_1 = 3;
|
|
HDCP_V2_2 = 4;
|
|
}
|
|
optional uint32 ClientToken = 1;
|
|
optional uint32 SessionToken = 2;
|
|
optional uint32 VideoResolutionConstraints = 3;
|
|
optional HdcpVersion MaxHdcpVersion = 4;
|
|
optional uint32 OemCryptoApiVersion = 5;
|
|
}
|
|
required TokenType Type = 1;
|
|
//optional bytes Token = 2; // by default the client treats this as blob, but it's usually a DeviceCertificate, so for usefulness sake, I'm replacing it with this one:
|
|
optional SignedDeviceCertificate Token = 2; // use this when parsing, "bytes" when building a client id blob
|
|
repeated NameValue ClientInfo = 3;
|
|
optional bytes ProviderClientToken = 4;
|
|
optional uint32 LicenseCounter = 5;
|
|
optional ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later
|
|
optional FileHashes _FileHashes = 7; // vmp blob goes here
|
|
}
|
|
|
|
message DeviceCertificate {
|
|
enum CertificateType {
|
|
ROOT = 0;
|
|
INTERMEDIATE = 1;
|
|
USER_DEVICE = 2;
|
|
SERVICE = 3;
|
|
}
|
|
required CertificateType Type = 1; // the compiled code reused this as ProvisionedDeviceInfo.WvSecurityLevel, however that is incorrect (compiler aliased it as they're both identical as a structure)
|
|
optional bytes SerialNumber = 2;
|
|
optional uint32 CreationTimeSeconds = 3;
|
|
optional bytes PublicKey = 4;
|
|
optional uint32 SystemId = 5;
|
|
optional uint32 TestDeviceDeprecated = 6; // is it bool or int?
|
|
optional bytes ServiceId = 7; // service URL for service certificates
|
|
}
|
|
|
|
// missing some references,
|
|
message DeviceCertificateStatus {
|
|
enum CertificateStatus {
|
|
VALID = 0;
|
|
REVOKED = 1;
|
|
}
|
|
optional bytes SerialNumber = 1;
|
|
optional CertificateStatus Status = 2;
|
|
optional ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated?
|
|
}
|
|
|
|
message DeviceCertificateStatusList {
|
|
optional uint32 CreationTimeSeconds = 1;
|
|
repeated DeviceCertificateStatus CertificateStatus = 2;
|
|
}
|
|
|
|
message EncryptedClientIdentification {
|
|
required string ServiceId = 1;
|
|
optional bytes ServiceCertificateSerialNumber = 2;
|
|
required bytes EncryptedClientId = 3;
|
|
required bytes EncryptedClientIdIv = 4;
|
|
required bytes EncryptedPrivacyKey = 5;
|
|
}
|
|
|
|
// todo: fill (for this top-level type, it might be impossible/difficult)
|
|
enum LicenseType {
|
|
ZERO = 0;
|
|
DEFAULT = 1; // 1 is STREAMING/temporary license; on recent versions may go up to 3 (latest x86); it might be persist/don't persist type, unconfirmed
|
|
OFFLINE = 2;
|
|
}
|
|
|
|
// todo: fill (for this top-level type, it might be impossible/difficult)
|
|
// this is just a guess because these globals got lost, but really, do we need more?
|
|
enum ProtocolVersion {
|
|
CURRENT = 21; // don't have symbols for this
|
|
}
|
|
|
|
|
|
message LicenseIdentification {
|
|
optional bytes RequestId = 1;
|
|
optional bytes SessionId = 2;
|
|
optional bytes PurchaseId = 3;
|
|
optional LicenseType Type = 4;
|
|
optional uint32 Version = 5;
|
|
optional bytes ProviderSessionToken = 6;
|
|
}
|
|
|
|
|
|
message License {
|
|
message Policy {
|
|
optional bool CanPlay = 1; // changed from uint32 to bool
|
|
optional bool CanPersist = 2;
|
|
optional bool CanRenew = 3;
|
|
optional uint32 RentalDurationSeconds = 4;
|
|
optional uint32 PlaybackDurationSeconds = 5;
|
|
optional uint32 LicenseDurationSeconds = 6;
|
|
optional uint32 RenewalRecoveryDurationSeconds = 7;
|
|
optional string RenewalServerUrl = 8;
|
|
optional uint32 RenewalDelaySeconds = 9;
|
|
optional uint32 RenewalRetryIntervalSeconds = 10;
|
|
optional bool RenewWithUsage = 11; // was uint32
|
|
}
|
|
message KeyContainer {
|
|
enum KeyType {
|
|
SIGNING = 1;
|
|
CONTENT = 2;
|
|
KEY_CONTROL = 3;
|
|
OPERATOR_SESSION = 4;
|
|
}
|
|
enum SecurityLevel {
|
|
SW_SECURE_CRYPTO = 1;
|
|
SW_SECURE_DECODE = 2;
|
|
HW_SECURE_CRYPTO = 3;
|
|
HW_SECURE_DECODE = 4;
|
|
HW_SECURE_ALL = 5;
|
|
}
|
|
message OutputProtection {
|
|
enum CGMS {
|
|
COPY_FREE = 0;
|
|
COPY_ONCE = 2;
|
|
COPY_NEVER = 3;
|
|
CGMS_NONE = 0x2A; // PC default!
|
|
}
|
|
optional ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away
|
|
optional CGMS CgmsFlags = 2;
|
|
}
|
|
message KeyControl {
|
|
required bytes KeyControlBlock = 1; // what is this?
|
|
required bytes Iv = 2;
|
|
}
|
|
message OperatorSessionKeyPermissions {
|
|
optional uint32 AllowEncrypt = 1;
|
|
optional uint32 AllowDecrypt = 2;
|
|
optional uint32 AllowSign = 3;
|
|
optional uint32 AllowSignatureVerify = 4;
|
|
}
|
|
message VideoResolutionConstraint {
|
|
optional uint32 MinResolutionPixels = 1;
|
|
optional uint32 MaxResolutionPixels = 2;
|
|
optional OutputProtection RequiredProtection = 3;
|
|
}
|
|
optional bytes Id = 1;
|
|
optional bytes Iv = 2;
|
|
optional bytes Key = 3;
|
|
optional KeyType Type = 4;
|
|
optional SecurityLevel Level = 5;
|
|
optional OutputProtection RequiredProtection = 6;
|
|
optional OutputProtection RequestedProtection = 7;
|
|
optional KeyControl _KeyControl = 8; // duped names, etc
|
|
optional OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc
|
|
repeated VideoResolutionConstraint VideoResolutionConstraints = 10;
|
|
}
|
|
optional LicenseIdentification Id = 1;
|
|
optional Policy _Policy = 2; // duped names, etc
|
|
repeated KeyContainer Key = 3;
|
|
optional uint32 LicenseStartTime = 4;
|
|
optional uint32 RemoteAttestationVerified = 5; // bool?
|
|
optional bytes ProviderClientToken = 6;
|
|
// there might be more, check with newer versions (I see field 7-8 in a lic)
|
|
// this appeared in latest x86:
|
|
optional uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc)
|
|
}
|
|
|
|
message LicenseError {
|
|
enum Error {
|
|
INVALID_DEVICE_CERTIFICATE = 1;
|
|
REVOKED_DEVICE_CERTIFICATE = 2;
|
|
SERVICE_UNAVAILABLE = 3;
|
|
}
|
|
//LicenseRequest.RequestType ErrorCode; // clang mismatch
|
|
optional Error ErrorCode = 1;
|
|
}
|
|
|
|
message LicenseRequest {
|
|
message ContentIdentification {
|
|
message CENC {
|
|
//optional bytes Pssh = 1; // the client's definition is opaque, it doesn't care about the contents, but the PSSH has a clear definition that is understood and requested by the server, thus I'll replace it with:
|
|
optional WidevineCencHeader Pssh = 1;
|
|
optional LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 (is this persist/don't persist? look into it!)
|
|
optional bytes RequestId = 3;
|
|
}
|
|
message WebM {
|
|
optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
|
|
optional LicenseType LicenseType = 2;
|
|
optional bytes RequestId = 3;
|
|
}
|
|
message ExistingLicense {
|
|
optional LicenseIdentification LicenseId = 1;
|
|
optional uint32 SecondsSinceStarted = 2;
|
|
optional uint32 SecondsSinceLastPlayed = 3;
|
|
optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB!
|
|
}
|
|
optional CENC CencId = 1;
|
|
optional WebM WebmId = 2;
|
|
optional ExistingLicense License = 3;
|
|
}
|
|
enum RequestType {
|
|
NEW = 1;
|
|
RENEWAL = 2;
|
|
RELEASE = 3;
|
|
}
|
|
optional ClientIdentification ClientId = 1;
|
|
optional ContentIdentification ContentId = 2;
|
|
optional RequestType Type = 3;
|
|
optional uint32 RequestTime = 4;
|
|
optional bytes KeyControlNonceDeprecated = 5;
|
|
optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
|
|
optional uint32 KeyControlNonce = 7;
|
|
optional EncryptedClientIdentification EncryptedClientId = 8;
|
|
}
|
|
|
|
// raw pssh hack
|
|
message LicenseRequestRaw {
|
|
message ContentIdentification {
|
|
message CENC {
|
|
optional bytes Pssh = 1; // the client's definition is opaque, it doesn't care about the contents, but the PSSH has a clear definition that is understood and requested by the server, thus I'll replace it with:
|
|
//optional WidevineCencHeader Pssh = 1;
|
|
optional LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 (is this persist/don't persist? look into it!)
|
|
optional bytes RequestId = 3;
|
|
}
|
|
message WebM {
|
|
optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
|
|
optional LicenseType LicenseType = 2;
|
|
optional bytes RequestId = 3;
|
|
}
|
|
message ExistingLicense {
|
|
optional LicenseIdentification LicenseId = 1;
|
|
optional uint32 SecondsSinceStarted = 2;
|
|
optional uint32 SecondsSinceLastPlayed = 3;
|
|
optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB!
|
|
}
|
|
optional CENC CencId = 1;
|
|
optional WebM WebmId = 2;
|
|
optional ExistingLicense License = 3;
|
|
}
|
|
enum RequestType {
|
|
NEW = 1;
|
|
RENEWAL = 2;
|
|
RELEASE = 3;
|
|
}
|
|
optional ClientIdentification ClientId = 1;
|
|
optional ContentIdentification ContentId = 2;
|
|
optional RequestType Type = 3;
|
|
optional uint32 RequestTime = 4;
|
|
optional bytes KeyControlNonceDeprecated = 5;
|
|
optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
|
|
optional uint32 KeyControlNonce = 7;
|
|
optional EncryptedClientIdentification EncryptedClientId = 8;
|
|
}
|
|
|
|
|
|
message ProvisionedDeviceInfo {
|
|
enum WvSecurityLevel {
|
|
LEVEL_UNSPECIFIED = 0;
|
|
LEVEL_1 = 1;
|
|
LEVEL_2 = 2;
|
|
LEVEL_3 = 3;
|
|
}
|
|
optional uint32 SystemId = 1;
|
|
optional string Soc = 2;
|
|
optional string Manufacturer = 3;
|
|
optional string Model = 4;
|
|
optional string DeviceType = 5;
|
|
optional uint32 ModelYear = 6;
|
|
optional WvSecurityLevel SecurityLevel = 7;
|
|
optional uint32 TestDevice = 8; // bool?
|
|
}
|
|
|
|
|
|
// todo: fill
|
|
message ProvisioningOptions {
|
|
}
|
|
|
|
// todo: fill
|
|
message ProvisioningRequest {
|
|
}
|
|
|
|
// todo: fill
|
|
message ProvisioningResponse {
|
|
}
|
|
|
|
message RemoteAttestation {
|
|
optional EncryptedClientIdentification Certificate = 1;
|
|
optional string Salt = 2;
|
|
optional string Signature = 3;
|
|
}
|
|
|
|
// todo: fill
|
|
message SessionInit {
|
|
}
|
|
|
|
// todo: fill
|
|
message SessionState {
|
|
}
|
|
|
|
// todo: fill
|
|
message SignedCertificateStatusList {
|
|
}
|
|
|
|
message SignedDeviceCertificate {
|
|
|
|
//optional bytes DeviceCertificate = 1; // again, they use a buffer where it's supposed to be a message, so we'll replace it with what it really is:
|
|
optional DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later
|
|
optional bytes Signature = 2;
|
|
optional SignedDeviceCertificate Signer = 3;
|
|
}
|
|
|
|
|
|
// todo: fill
|
|
message SignedProvisioningMessage {
|
|
}
|
|
|
|
// the root of all messages, from either server or client
|
|
message SignedMessage {
|
|
enum MessageType {
|
|
LICENSE_REQUEST = 1;
|
|
LICENSE = 2;
|
|
ERROR_RESPONSE = 3;
|
|
SERVICE_CERTIFICATE_REQUEST = 4;
|
|
SERVICE_CERTIFICATE = 5;
|
|
}
|
|
optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
|
|
optional bytes Msg = 2; // this has to be casted dynamically, to LicenseRequest, License or LicenseError (? unconfirmed), for Request, no other fields but Type need to be present
|
|
// for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
|
|
optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
|
|
optional bytes SessionKey = 4; // often RSA wrapped for licenses
|
|
optional RemoteAttestation RemoteAttestation = 5;
|
|
}
|
|
|
|
|
|
|
|
// This message is copied from google's docs, not reversed:
|
|
message WidevineCencHeader {
|
|
enum Algorithm {
|
|
UNENCRYPTED = 0;
|
|
AESCTR = 1;
|
|
};
|
|
optional Algorithm algorithm = 1;
|
|
repeated bytes key_id = 2;
|
|
|
|
// Content provider name.
|
|
optional string provider = 3;
|
|
|
|
// A content identifier, specified by content provider.
|
|
optional bytes content_id = 4;
|
|
|
|
// Track type. Acceptable values are SD, HD and AUDIO. Used to
|
|
// differentiate content keys used by an asset.
|
|
optional string track_type_deprecated = 5;
|
|
|
|
// The name of a registered policy to be used for this asset.
|
|
optional string policy = 6;
|
|
|
|
// Crypto period index, for media using key rotation.
|
|
optional uint32 crypto_period_index = 7;
|
|
|
|
// Optional protected context for group content. The grouped_license is a
|
|
// serialized SignedMessage.
|
|
optional bytes grouped_license = 8;
|
|
|
|
// Protection scheme identifying the encryption algorithm.
|
|
// Represented as one of the following 4CC values:
|
|
// 'cenc' (AESCTR), 'cbc1' (AESCBC),
|
|
// 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample).
|
|
optional uint32 protection_scheme = 9;
|
|
|
|
// Optional. For media using key rotation, this represents the duration
|
|
// of each crypto period in seconds.
|
|
optional uint32 crypto_period_seconds = 10;
|
|
}
|
|
|
|
|
|
// remove these when using it outside of protoc:
|
|
|
|
// from here on, it's just for testing, these messages don't exist in the binaries, I'm adding them to avoid detecting type programmatically
|
|
message SignedLicenseRequest {
|
|
enum MessageType {
|
|
LICENSE_REQUEST = 1;
|
|
LICENSE = 2;
|
|
ERROR_RESPONSE = 3;
|
|
SERVICE_CERTIFICATE_REQUEST = 4;
|
|
SERVICE_CERTIFICATE = 5;
|
|
}
|
|
optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
|
|
optional LicenseRequest Msg = 2; // this has to be casted dynamically, to LicenseRequest, License or LicenseError (? unconfirmed), for Request, no other fields but Type need to be present
|
|
// for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
|
|
optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
|
|
optional bytes SessionKey = 4; // often RSA wrapped for licenses
|
|
optional RemoteAttestation RemoteAttestation = 5;
|
|
}
|
|
|
|
// hack
|
|
message SignedLicenseRequestRaw {
|
|
enum MessageType {
|
|
LICENSE_REQUEST = 1;
|
|
LICENSE = 2;
|
|
ERROR_RESPONSE = 3;
|
|
SERVICE_CERTIFICATE_REQUEST = 4;
|
|
SERVICE_CERTIFICATE = 5;
|
|
}
|
|
optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
|
|
optional LicenseRequestRaw Msg = 2; // this has to be casted dynamically, to LicenseRequest, License or LicenseError (? unconfirmed), for Request, no other fields but Type need to be present
|
|
// for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
|
|
optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
|
|
optional bytes SessionKey = 4; // often RSA wrapped for licenses
|
|
optional RemoteAttestation RemoteAttestation = 5;
|
|
}
|
|
|
|
|
|
message SignedLicense {
|
|
enum MessageType {
|
|
LICENSE_REQUEST = 1;
|
|
LICENSE = 2;
|
|
ERROR_RESPONSE = 3;
|
|
SERVICE_CERTIFICATE_REQUEST = 4;
|
|
SERVICE_CERTIFICATE = 5;
|
|
}
|
|
optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
|
|
optional License Msg = 2; // this has to be casted dynamically, to LicenseRequest, License or LicenseError (? unconfirmed), for Request, no other fields but Type need to be present
|
|
// for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
|
|
optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
|
|
optional bytes SessionKey = 4; // often RSA wrapped for licenses
|
|
optional RemoteAttestation RemoteAttestation = 5;
|
|
}
|
|
|
|
message SignedServiceCertificate {
|
|
enum MessageType {
|
|
LICENSE_REQUEST = 1;
|
|
LICENSE = 2;
|
|
ERROR_RESPONSE = 3;
|
|
SERVICE_CERTIFICATE_REQUEST = 4;
|
|
SERVICE_CERTIFICATE = 5;
|
|
}
|
|
optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
|
|
optional SignedDeviceCertificate Msg = 2; // this has to be casted dynamically, to LicenseRequest, License or LicenseError (? unconfirmed), for Request, no other fields but Type need to be present
|
|
// for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
|
|
optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
|
|
optional bytes SessionKey = 4; // often RSA wrapped for licenses
|
|
optional RemoteAttestation RemoteAttestation = 5;
|
|
}
|
|
|
|
//vmp support
|
|
message FileHashes {
|
|
message Signature {
|
|
optional string filename = 1;
|
|
optional bool test_signing = 2; //0 - release, 1 - testing
|
|
optional bytes SHA512Hash = 3;
|
|
optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file
|
|
optional bytes signature = 5;
|
|
}
|
|
optional bytes signer = 1;
|
|
repeated Signature signatures = 2;
|
|
}
|