// beware proto3 won't show missing fields it seems, need to change to "proto2" and add "optional" before every field, and remove all the dummy enum members I added: syntax = "proto3"; // 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 { string Name = 1; string Value = 2; } message ClientCapabilities { enum HdcpVersion { HDCP_NONE = 0; HDCP_V1 = 1; HDCP_V2 = 2; HDCP_V2_1 = 3; HDCP_V2_2 = 4; } uint32 ClientToken = 1; uint32 SessionToken = 2; uint32 VideoResolutionConstraints = 3; HdcpVersion MaxHdcpVersion = 4; uint32 OemCryptoApiVersion = 5; } TokenType Type = 1; //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: SignedDeviceCertificate Token = 2; repeated NameValue ClientInfo = 3; bytes ProviderClientToken = 4; uint32 LicenseCounter = 5; ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later } message DeviceCertificate { enum CertificateType { ROOT = 0; INTERMEDIATE = 1; USER_DEVICE = 2; SERVICE = 3; } //ProvisionedDeviceInfo.WvSecurityLevel Type = 1; // is this how one is supposed to call it? (it's an enum) there might be a bug here, with CertificateType getting confused with WvSecurityLevel, for now renaming it (verify against other binaries) CertificateType Type = 1; bytes SerialNumber = 2; uint32 CreationTimeSeconds = 3; bytes PublicKey = 4; uint32 SystemId = 5; uint32 TestDeviceDeprecated = 6; // is it bool or int? bytes ServiceId = 7; // service URL for service certificates } // missing some references, message DeviceCertificateStatus { enum CertificateStatus { VALID = 0; REVOKED = 1; } bytes SerialNumber = 1; CertificateStatus Status = 2; ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated? } message DeviceCertificateStatusList { uint32 CreationTimeSeconds = 1; repeated DeviceCertificateStatus CertificateStatus = 2; } message EncryptedClientIdentification { string ServiceId = 1; bytes ServiceCertificateSerialNumber = 2; bytes EncryptedClientId = 3; bytes EncryptedClientIdIv = 4; bytes EncryptedPrivacyKey = 5; } // todo: fill (for this top-level type, it might be impossible/difficult) enum LicenseType { ZERO = 0; DEFAULT = 1; // do not know what this is either, but should be 1; on recent versions may go up to 3 (latest x86) } // 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 { DUMMY = 0; CURRENT = 21; // don't have symbols for this } message LicenseIdentification { bytes RequestId = 1; bytes SessionId = 2; bytes PurchaseId = 3; LicenseType Type = 4; uint32 Version = 5; bytes ProviderSessionToken = 6; } message License { message Policy { uint32 CanPlay = 1; uint32 CanPersist = 2; uint32 CanRenew = 3; uint32 RentalDurationSeconds = 4; uint32 PlaybackDurationSeconds = 5; uint32 LicenseDurationSeconds = 6; uint32 RenewalRecoveryDurationSeconds = 7; string RenewalServerUrl = 8; uint32 RenewalDelaySeconds = 9; uint32 RenewalRetryIntervalSeconds = 10; uint32 RenewWithUsage = 11; uint32 UnknownPolicy12 = 12; } message KeyContainer { enum KeyType { _NOKEYTYPE = 0; // dummy, added to satisfy proto3, not present in original SIGNING = 1; CONTENT = 2; KEY_CONTROL = 3; OPERATOR_SESSION = 4; } enum SecurityLevel { _NOSECLEVEL = 0; // dummy, added to satisfy proto3, not present in original 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! } ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away CGMS CgmsFlags = 2; } message KeyControl { bytes KeyControlBlock = 1; // what is this? bytes Iv = 2; } message OperatorSessionKeyPermissions { uint32 AllowEncrypt = 1; uint32 AllowDecrypt = 2; uint32 AllowSign = 3; uint32 AllowSignatureVerify = 4; } message VideoResolutionConstraint { uint32 MinResolutionPixels = 1; uint32 MaxResolutionPixels = 2; OutputProtection RequiredProtection = 3; } bytes Id = 1; bytes Iv = 2; bytes Key = 3; KeyType Type = 4; SecurityLevel Level = 5; OutputProtection RequiredProtection = 6; OutputProtection RequestedProtection = 7; KeyControl _KeyControl = 8; // duped names, etc OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc repeated VideoResolutionConstraint VideoResolutionConstraints = 10; } LicenseIdentification Id = 1; Policy _Policy = 2; // duped names, etc repeated KeyContainer Key = 3; uint32 LicenseStartTime = 4; uint32 RemoteAttestationVerified = 5; // bool? bytes ProviderClientToken = 6; // there might be more, check with newer versions (I see field 7-8 in a lic) // this appeared in latest x86: uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc) bytes UnknownHdcpDataField = 8; } message LicenseError { enum Error { DUMMY_NO_ERROR = 0; // dummy, added to satisfy proto3 INVALID_DEVICE_CERTIFICATE = 1; REVOKED_DEVICE_CERTIFICATE = 2; SERVICE_UNAVAILABLE = 3; } //LicenseRequest.RequestType ErrorCode; // clang mismatch Error ErrorCode = 1; } message LicenseRequest { message ContentIdentification { message CENC { // 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: WidevineCencHeader Pssh = 1; LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 bytes RequestId = 3; } message WebM { bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used LicenseType LicenseType = 2; bytes RequestId = 3; } message ExistingLicense { LicenseIdentification LicenseId = 1; uint32 SecondsSinceStarted = 2; uint32 SecondsSinceLastPlayed = 3; bytes SessionUsageTableEntry = 4; } CENC CencId = 1; WebM WebmId = 2; ExistingLicense License = 3; } enum RequestType { DUMMY_REQ_TYPE = 0; // dummy, added to satisfy proto3 NEW = 1; RENEWAL = 2; RELEASE = 3; } ClientIdentification ClientId = 1; ContentIdentification ContentId = 2; RequestType Type = 3; uint32 RequestTime = 4; bytes KeyControlNonceDeprecated = 5; ProtocolVersion ProtocolVersion = 6; // lacking symbols for this uint32 KeyControlNonce = 7; EncryptedClientIdentification EncryptedClientId = 8; } message ProvisionedDeviceInfo { enum WvSecurityLevel { LEVEL_UNSPECIFIED = 0; LEVEL_1 = 1; LEVEL_2 = 2; LEVEL_3 = 3; } uint32 SystemId = 1; string Soc = 2; string Manufacturer = 3; string Model = 4; string DeviceType = 5; uint32 ModelYear = 6; WvSecurityLevel SecurityLevel = 7; uint32 TestDevice = 8; // bool? } // todo: fill message ProvisioningOptions { } // todo: fill message ProvisioningRequest { } // todo: fill message ProvisioningResponse { } message RemoteAttestation { EncryptedClientIdentification Certificate = 1; string Salt = 2; string Signature = 3; } // todo: fill message SessionInit { } // todo: fill message SessionState { } // todo: fill message SignedCertificateStatusList { } message SignedDeviceCertificate { //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: DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later bytes Signature = 2; SignedDeviceCertificate Signer = 3; } // todo: fill message SignedProvisioningMessage { } // the root of all messages, from either server or client message SignedMessage { enum MessageType { DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 LICENSE_REQUEST = 1; LICENSE = 2; ERROR_RESPONSE = 3; SERVICE_CERTIFICATE_REQUEST = 4; SERVICE_CERTIFICATE = 5; } MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 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 bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) bytes SessionKey = 4; // often RSA wrapped for licenses RemoteAttestation RemoteAttestation = 5; } // This message is copied from google's docs, not reversed: message WidevineCencHeader { enum Algorithm { UNENCRYPTED = 0; AESCTR = 1; }; Algorithm algorithm = 1; repeated bytes key_id = 2; // Content provider name. string provider = 3; // A content identifier, specified by content provider. bytes content_id = 4; // Track type. Acceptable values are SD, HD and AUDIO. Used to // differentiate content keys used by an asset. string track_type_deprecated = 5; // The name of a registered policy to be used for this asset. string policy = 6; // Crypto period index, for media using key rotation. uint32 crypto_period_index = 7; // Optional protected context for group content. The grouped_license is a // serialized SignedMessage. 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). uint32 protection_scheme = 9; // Optional. For media using key rotation, this represents the duration // of each crypto period in seconds. uint32 crypto_period_seconds = 10; } // 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 { DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 LICENSE_REQUEST = 1; LICENSE = 2; ERROR_RESPONSE = 3; SERVICE_CERTIFICATE_REQUEST = 4; SERVICE_CERTIFICATE = 5; } MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 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 bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) bytes SessionKey = 4; // often RSA wrapped for licenses RemoteAttestation RemoteAttestation = 5; } message SignedLicense { enum MessageType { DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 LICENSE_REQUEST = 1; LICENSE = 2; ERROR_RESPONSE = 3; SERVICE_CERTIFICATE_REQUEST = 4; SERVICE_CERTIFICATE = 5; } MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 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 bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) bytes SessionKey = 4; // often RSA wrapped for licenses RemoteAttestation RemoteAttestation = 5; }