├── .gitignore
├── proto_compiler
├── bin
│ └── protoc.exe
└── include
│ └── google
│ └── protobuf
│ ├── source_context.proto
│ ├── empty.proto
│ ├── struct.proto
│ ├── wrappers.proto
│ ├── duration.proto
│ ├── any.proto
│ ├── type.proto
│ ├── timestamp.proto
│ ├── api.proto
│ ├── field_mask.proto
│ ├── compiler
│ └── plugin.proto
│ └── descriptor.proto
├── requirements.txt
├── cdm
├── devices
│ └── android_generic
│ │ ├── token.bin
│ │ ├── device_client_id_blob
│ │ ├── config.json
│ │ └── device_private_key
├── key.py
├── session.py
├── formats
│ ├── 1.txt
│ ├── 2.txt
│ ├── sample_request_curl.txt
│ ├── wv_proto3.proto
│ └── wv_proto2.proto
├── deviceconfig.py
├── vmp.py
└── cdm.py
├── .github
└── ISSUE_TEMPLATE
│ └── feature-request--add-site-support.md
├── README.md
├── getPSSH.py
├── wvdecryptcustom.py
├── headers.py
└── l3.py
/.gitignore:
--------------------------------------------------------------------------------
1 | cookies.py
2 | __pycache__
3 |
4 |
--------------------------------------------------------------------------------
/proto_compiler/bin/protoc.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/widevine_keys/HEAD/proto_compiler/bin/protoc.exe
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | xmltodict
2 | requests
3 | google
4 | google-api-python-client
5 | protobuf
6 | pycryptodome
7 | pycryptodomex
--------------------------------------------------------------------------------
/cdm/devices/android_generic/token.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/widevine_keys/HEAD/cdm/devices/android_generic/token.bin
--------------------------------------------------------------------------------
/cdm/devices/android_generic/device_client_id_blob:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nilaoda/widevine_keys/HEAD/cdm/devices/android_generic/device_client_id_blob
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request--add-site-support.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 'Feature request: Add site support'
3 | about: If your site require specific json-formatted data in the license request
4 | title: Add [site_name] support
5 | labels: add site support
6 | assignees: ''
7 |
8 | ---
9 |
10 | **What additional fields does your site require?**
11 | A clear and concise description or screenshot of payload tab of post-request in chrome devtools
12 |
13 | **Describe algorithm for generating additional fields if they are not static**
14 | [text]
15 |
--------------------------------------------------------------------------------
/cdm/key.py:
--------------------------------------------------------------------------------
1 | import binascii
2 |
3 | class Key:
4 | def __init__(self, kid, type, key, permissions=[]):
5 | self.kid = kid
6 | self.type = type
7 | self.key = key
8 | self.permissions = permissions
9 |
10 | def __repr__(self):
11 | if self.type == "OPERATOR_SESSION":
12 | return "key(kid={}, type={}, key={}, permissions={})".format(self.kid, self.type, binascii.hexlify(self.key), self.permissions)
13 | else:
14 | return "key(kid={}, type={}, key={})".format(self.kid, self.type, binascii.hexlify(self.key))
15 |
--------------------------------------------------------------------------------
/cdm/session.py:
--------------------------------------------------------------------------------
1 | class Session:
2 | def __init__(self, session_id, init_data, device_config, offline):
3 | self.session_id = session_id
4 | self.init_data = init_data
5 | self.offline = offline
6 | self.device_config = device_config
7 | self.device_key = None
8 | self.session_key = None
9 | self.derived_keys = {
10 | 'enc': None,
11 | 'auth_1': None,
12 | 'auth_2': None
13 | }
14 | self.license_request = None
15 | self.license = None
16 | self.service_certificate = None
17 | self.privacy_mode = False
18 | self.keys = []
19 |
--------------------------------------------------------------------------------
/cdm/devices/android_generic/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": "token.bin",
3 | "client_info":
4 | {
5 | "company_name": "motorola",
6 | "model_name": "Nexus 6",
7 | "architecture_name": "armeabi-v7a",
8 | "device_name": "shamu",
9 | "product_name": "shamu",
10 | "build_info": "google/shamu/shamu:5.1.1/LMY48M/2167285:user/release-keys",
11 | "device_id": "TU1JX0VGRkYwRkU2NUQ5OAAAAAAAAAAAAAAAAAAAAAA=",
12 | "os_version": "5.1.1"
13 | },
14 | "capabilities":
15 | {
16 | "session_token": 1,
17 | "max_hdcp_version": "HDCP_V2_2",
18 | "oem_crypto_api_version": 9
19 | }
20 | }
--------------------------------------------------------------------------------
/cdm/devices/android_generic/device_private_key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpQIBAAKCAQEA4sUKDpvMG/idF8oCH5AVSwFd5Mk+rEwOBsLZMYdliXWe1hn9
3 | mdE6u9pjsr+bLrZjlKxMFqPPxbIUcC1Ii7BFSje2Fd8kxnaIprQWxDPgK+NSSx7v
4 | Un452TyB1L9lx39ZBt0PlRfwjkCodX+I9y+oBga73NRh7hPbtLzXe/r/ubFBaEu+
5 | aRkDZBwYPqHgH1RoFLuyFNMjfqGcPosGxceDtvPysmBxB93Hk2evml5fjdYGg6tx
6 | z510g+XFPDFv7GSy1KuWqit83MqzPls9qAQMkwUc05ggjDhGCKW4/p97fn23WDFE
7 | 3TzSSsQvyJLKA3s9oJbtJCD/gOHYqDvnWn8zPwIDAQABAoIBAQDCWe1Mp+o+7sx0
8 | XwWC15HoPruiIXg9YtGCqexLrqcvMEd5Z70Z32BfL8TSpbTyTA78lM6BeNPRs9Yg
9 | bi8GyYQZH7ZG+IAkN+LWPPJmJa+y7ZjSGSkzoksiC+GZ3I/2cwZyA3Qfa+0XfgLi
10 | 8PMKJyXyREMt+DgWO57JQC/OakhRdCR19mM6NKd+ynd/IEz/NIbjMLDVKwW8HEPx
11 | N3r5CU9O96nr62DI68KVj3jwUR3cDi/5xfhosYhCQjHJuobNbeFR18dY2nQNLWYd
12 | S0wtskla1fl9eYHwYAzwru4wHT4WJC7+V4pscfCI0YZB6PslxDKrv73l5H1tz4cf
13 | Vy58NRSBAoGBAPSmjoVtQzTvQ6PZIs81SF1ulJI9kUpyFaBoSSgt+2ZkeNtF6Hih
14 | Zm7OVJ9wg9sfjpB3SFBUjuhXz/ts/t6dkA2PgCbrvhBMRKSGbfyhhtM2gRf002I4
15 | bJ7Y0C/ont4WzC/XbXEkAmh+fG2/JRvbdVQaIdyS6MmVHtCtRsHEQZS5AoGBAO1K
16 | IXOKAFA+320+Hkbqskfevmxrv+JHIdetliaREZwQH+VYUUM8u5/Kt3oyMat+mH90
17 | rZOKQK2zM8cz4tKclTUT54nrtICxeo6UHVc56FqXZ6sVvVgm8Cnvt1md4XwG4FwQ
18 | r/OlaM6Hr5HRf8dkzuzqm4ZQYRHGzZ6AMphj8Xu3AoGAdmo7p5dIJVH98kuCDrsi
19 | iJ6iaNpF/buUfiyb5EfFXD0bRj7jE6hDdTSHPxjtqVzv2zrxFHipJwqBz5dlEYlA
20 | FWA0ziHiv+66dsveZp4kLQ0/lMHaorre0E/vDJFSe/qa4DksbsvYIo2+WjxfkMk7
21 | U/bGFwZAiHmWDbkg+16rw3kCgYEAyyodWf9eJVavlakJ404vNrnP8KSQtfyRTUii
22 | toKewTBNHuBvM1JckoPOdCFlxZ+ukfIka56DojU8r+IM4qaOWdOg+sWE1mses9S9
23 | CmHaPzZC3IjQhRlRp5ZHNcOnu7lnf2wKOmH1Sl+CQydMcDwvr0lvv6AyfDXq9zps
24 | F2365CECgYEAmYgs/qwnh9m0aGDw/ZGrASoE0TxlpizPvsVDGx9t9UGC2Z+5QvAE
25 | ZcQeKoLCbktr0BnRLI+W1g+KpXQGcnSF9VX/qwUlf72XA6C6kobQvW+Yd/H/IN5d
26 | jPqoL/m41rRzm+J+9/Tfc8Aiy1kkllUYnVJdC5QLAIswuhI8lkaFTN4=
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
First run:
2 |
3 | [Copy headers ](https://user-images.githubusercontent.com/43696206/145660715-472e4c65-86de-453f-86fc-5bb14028f448.png)(with cookies) of POST license request from browser to headers.py like dictionary.
4 |
5 | ```
6 | pip install -r requirements.txt # if doesn't work try pip3
7 | py l3.py
8 | Input MPD URL: https://site.ru/.../.../filename.mpd
9 | License URL: https://cms.35mm.online/umbraco/api/products/473/drm/widevine?platform=BROWSER&type=MOVIE
10 | ```
11 |
12 | Works only if the site does not require specific json-formatted data in the license request.
13 | And if it requires and you want to add its support, write to issues specifying the required json-formatted fields and the algorithm for their formation (if not static).
14 | Examples:
15 | 1. Normal work:
16 | 
17 | 2. Server did not issue a license, as it requires additional json-formatted data:
18 | 
19 | 3. If "Unable to find PSSH in mpd" - use [this tool](https://tools.axinom.com/generators/PsshBox) to get it manually or write to issues attaching a link to mpd
20 |
21 | Further about kinopoisk (hello to compatriots):
22 | Not working yet.
23 | It is necessary to find out how the POST request is signed (the signature field in the request payload).
24 | It looks like a simple hash of sha1, but from the looks of it, this is not it, but the Amazon's AWS Signature Version 4. Or I am doing something wrong and everything is much easier...
25 | Any ideas are appreciated, write to issues.
26 |
27 | [Parsed payload of license request](https://user-images.githubusercontent.com/43696206/145263764-349dd8be-58ec-4d42-9524-4a098b0fe5e3.png)
28 |
29 |
--------------------------------------------------------------------------------
/getPSSH.py:
--------------------------------------------------------------------------------
1 | import requests, xmltodict, json
2 |
3 | def get_pssh(mpd_url):
4 | pssh = ''
5 | try:
6 | r = requests.get(url=mpd_url)
7 | r.raise_for_status()
8 | xml = xmltodict.parse(r.text)
9 | mpd = json.loads(json.dumps(xml))
10 | periods = mpd['MPD']['Period']
11 | except Exception as e:
12 | pssh = input(f'\nUnable to find PSSH in MPD: {e}. \nEdit getPSSH.py or enter PSSH manually: ')
13 | return pssh
14 | if isinstance(periods, list):
15 | for idx, period in enumerate(periods):
16 | if isinstance(period['AdaptationSet'], list):
17 | for ad_set in period['AdaptationSet']:
18 | if ad_set['@mimeType'] == 'video/mp4':
19 | try:
20 | for t in ad_set['ContentProtection']:
21 | if t['@schemeIdUri'].lower() == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
22 | pssh = t["cenc:pssh"]
23 | except KeyError:
24 | pass
25 | else:
26 | if period['AdaptationSet']['@mimeType'] == 'video/mp4':
27 | try:
28 | for t in period['AdaptationSet']['ContentProtection']:
29 | if t['@schemeIdUri'].lower() == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
30 | pssh = t["cenc:pssh"]
31 | except KeyError:
32 | pass
33 | else:
34 | for ad_set in periods['AdaptationSet']:
35 | if ad_set['@mimeType'] == 'video/mp4':
36 | try:
37 | for t in ad_set['ContentProtection']:
38 | if t['@schemeIdUri'].lower() == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
39 | pssh = t["cenc:pssh"]
40 | except KeyError:
41 | pass
42 | if pssh == '':
43 | pssh = input('Unable to find PSSH in mpd. Edit getPSSH.py or enter PSSH manually: ')
44 | return pssh
--------------------------------------------------------------------------------
/wvdecryptcustom.py:
--------------------------------------------------------------------------------
1 | # uncompyle6 version 3.7.3
2 | # Python bytecode 3.6 (3379)
3 | # Decompiled from: Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)]
4 | # Embedded file name: pywidevine\decrypt\wvdecryptcustom.py
5 | import logging, subprocess, re, base64
6 | from cdm import cdm, deviceconfig
7 |
8 | class WvDecrypt(object):
9 | WV_SYSTEM_ID = [
10 | 237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]
11 |
12 | def __init__(self, init_data_b64, cert_data_b64, device):
13 | self.init_data_b64 = init_data_b64
14 | self.cert_data_b64 = cert_data_b64
15 | self.device = device
16 | self.cdm = cdm.Cdm()
17 |
18 | def check_pssh(pssh_b64):
19 | pssh = base64.b64decode(pssh_b64)
20 | if not pssh[12:28] == bytes(self.WV_SYSTEM_ID):
21 | new_pssh = bytearray([0, 0, 0])
22 | new_pssh.append(32 + len(pssh))
23 | new_pssh[4:] = bytearray(b'pssh')
24 | new_pssh[8:] = [0, 0, 0, 0]
25 | new_pssh[13:] = self.WV_SYSTEM_ID
26 | new_pssh[29:] = [0, 0, 0, 0]
27 | new_pssh[31] = len(pssh)
28 | new_pssh[32:] = pssh
29 | return base64.b64encode(new_pssh)
30 | else:
31 | return pssh_b64
32 |
33 | self.session = self.cdm.open_session(check_pssh(self.init_data_b64), deviceconfig.DeviceConfig(self.device))
34 | if self.cert_data_b64:
35 | self.cdm.set_service_certificate(self.session, self.cert_data_b64)
36 |
37 | def log_message(self, msg):
38 | return '{}'.format(msg)
39 |
40 | def start_process(self):
41 | keyswvdecrypt = []
42 | try:
43 | for key in self.cdm.get_keys(self.session):
44 | if key.type == 'CONTENT':
45 | keyswvdecrypt.append(self.log_message('{}:{}'.format(key.kid.hex(), key.key.hex())))
46 |
47 | except Exception:
48 | return (
49 | False, keyswvdecrypt)
50 | else:
51 | return (
52 | True, keyswvdecrypt)
53 |
54 | def get_challenge(self):
55 | return self.cdm.get_license_request(self.session)
56 |
57 | def update_license(self, license_b64):
58 | self.cdm.provide_license(self.session, license_b64)
59 | return True
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/source_context.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "SourceContextProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
41 |
42 | // `SourceContext` represents information about the source of a
43 | // protobuf element, like the file in which it is defined.
44 | message SourceContext {
45 | // The path-qualified name of the .proto file that contained the associated
46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`.
47 | string file_name = 1;
48 | }
49 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/empty.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/emptypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "EmptyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 | option cc_enable_arenas = true;
42 |
43 | // A generic empty message that you can re-use to avoid defining duplicated
44 | // empty messages in your APIs. A typical example is to use it as the request
45 | // or the response type of an API method. For instance:
46 | //
47 | // service Foo {
48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
49 | // }
50 | //
51 | // The JSON representation for `Empty` is empty JSON object `{}`.
52 | message Empty {}
53 |
--------------------------------------------------------------------------------
/cdm/formats/1.txt:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/cdm/deviceconfig.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | device_android_generic = {
4 | 'name': 'android_generic',
5 | 'description': 'android studio cdm',
6 | 'security_level': 3,
7 | 'session_id_type': 'android',
8 | 'private_key_available': True,
9 | 'vmp': False,
10 | 'send_key_control_nonce': True
11 | }
12 |
13 | devices_available = [device_android_generic]
14 |
15 | FILES_FOLDER = 'devices'
16 |
17 | class DeviceConfig:
18 | def __init__(self, device):
19 | self.device_name = device['name']
20 | self.description = device['description']
21 | self.security_level = device['security_level']
22 | self.session_id_type = device['session_id_type']
23 | self.private_key_available = device['private_key_available']
24 | self.vmp = device['vmp']
25 | self.send_key_control_nonce = device['send_key_control_nonce']
26 |
27 | if 'keybox_filename' in device:
28 | self.keybox_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], device['keybox_filename'])
29 | else:
30 | self.keybox_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 'keybox')
31 |
32 | if 'device_cert_filename' in device:
33 | self.device_cert_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], device['device_cert_filename'])
34 | else:
35 | self.device_cert_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 'device_cert')
36 |
37 | if 'device_private_key_filename' in device:
38 | self.device_private_key_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], device['device_private_key_filename'])
39 | else:
40 | self.device_private_key_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 'device_private_key')
41 |
42 | if 'device_client_id_blob_filename' in device:
43 | self.device_client_id_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], device['device_client_id_blob_filename'])
44 | else:
45 | self.device_client_id_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 'device_client_id_blob')
46 |
47 | if 'device_vmp_blob_filename' in device:
48 | self.device_vmp_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], device['device_vmp_blob_filename'])
49 | else:
50 | self.device_vmp_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 'device_vmp_blob')
51 |
52 | def __repr__(self):
53 | return "DeviceConfig(name={}, description={}, security_level={}, session_id_type={}, private_key_available={}, vmp={})".format(self.device_name, self.description, self.security_level, self.session_id_type, self.private_key_available, self.vmp)
54 |
--------------------------------------------------------------------------------
/cdm/formats/2.txt:
--------------------------------------------------------------------------------
1 | CAES9Q0K8wwIARLtCQqwAggCEhD3lT3lsoIS406iQVTw6mNsGOntrPUFIo4CMIIBCgKCAQEA4sUKDpvMG/idF8oCH5AVSwFd5Mk+rEwOBsLZMYdliXWe1hn9mdE6u9pjsr+bLrZjlKxMFqPPxbIUcC1Ii7BFSje2Fd8kxnaIprQWxDPgK+NSSx7vUn452TyB1L9lx39ZBt0PlRfwjkCodX+I9y+oBga73NRh7hPbtLzXe/r/ubFBaEu+aRkDZBwYPqHgH1RoFLuyFNMjfqGcPosGxceDtvPysmBxB93Hk2evml5fjdYGg6txz510g+XFPDFv7GSy1KuWqit83MqzPls9qAQMkwUc05ggjDhGCKW4/p97fn23WDFE3TzSSsQvyJLKA3s9oJbtJCD/gOHYqDvnWn8zPwIDAQABKPAiSAESgAK1RYcNJEgCArBwmOIYdYDu4cJyCLy0jaIaobfKMZPaAQ7PC33nGH8Kc5MyPWoNJvBnAHtL8eomC+dzymJsoT/6JAKkErDQT4ILMH12fwA8RZJac1NeBkvJUxgNksG5wDNan1xktN0ANO5Xdvh2DAoR1927M2FYgRRl3m0Nj6/ntij0m7hniFPaQkc08Rcz/mdGHCjC/3lQnVIXJ3zXiHzJ4b7OpOIUB91TXto5CXXujG1RDZxNDTClmUizKiY9kunLnxsmKUBY8fCxEVcOSWh1flK4wCxocOqZx5o5NZa7+CwwgtwkscGYiEdWX4P9jAl8JNuJu+RzLhTFZh0GWfIiGrQFCq4CCAESEGnj6Ji7LD+4o7MoHYT4jBQYjtW+kQUijgIwggEKAoIBAQDY9um1ifBRIOmkPtDZTqH+CZUBbb0eK0Cn3NHFf8MFUDzPEz+emK/OTub/hNxCJCao//pP5L8tRNUPFDrrvCBMo7Rn+iUb+mA/2yXiJ6ivqcN9Cu9i5qOU1ygon9SWZRsujFFB8nxVreY5Lzeq0283zn1Cg1stcX4tOHT7utPzFG/ReDFQt0O/GLlzVwB0d1sn3SKMO4XLjhZdncrtF9jljpg7xjMIlnWJUqxDo7TQkTytJmUl0kcM7bndBLerAdJFGaXc6oSY4eNy/IGDluLCQR3KZEQsy/mLeV1ggQ44MFr7XOM+rd+4/314q/deQbjHqjWFuVr8iIaKbq+R63ShAgMBAAEo8CISgAMii2Mw6z+Qs1bvvxGStie9tpcgoO2uAt5Zvv0CDXvrFlwnSbo+qR71Ru2IlZWVSbN5XYSIDwcwBzHjY8rNr3fgsXtSJty425djNQtF5+J2jrAhf3Q2m7EI5aohZGpD2E0cr+dVj9o8x0uJR2NWR8FVoVQSXZpad3M/4QzBLNto/tz+UKyZwa7Sc/eTQc2+ZcDS3ZEO3lGRsH864Kf/cEGvJRBBqcpJXKfG+ItqEW1AAPptjuggzmZEzRq5xTGf6or+bXrKjCpBS9G1SOyvCNF1k5z6lG8KsXhgQxL6ADHMoulxvUIihyPY5MpimdXfUdEQ5HA2EqNiNVNIO4qP007jW51yAeThOry4J22xs8RdkIClOGAauLIl0lLA4flMzW+VfQl5xYxP0E5tuhn0h+844DslU8ZF7U1dU2QprIApffXD9wgAACk26Rggy8e96z8i86/+YYyZQkc9hIdCAERrgEYCEbByzONrdRDs1MrS/ch1moV5pJv63BIKvQHGvLkaFgoMY29tcGFueV9uYW1lEgZHb29nbGUaJwoKbW9kZWxfbmFtZRIZQW5kcm9pZCBTREsgYnVpbHQgZm9yIHg4NhoYChFhcmNoaXRlY3R1cmVfbmFtZRIDeDg2GhoKC2RldmljZV9uYW1lEgtnZW5lcmljX3g4NhokCgxwcm9kdWN0X25hbWUSFHNka19nb29nbGVfcGhvbmVfeDg2GlsKCmJ1aWxkX2luZm8STWdvb2dsZS9zZGtfZ29vZ2xlX3Bob25lX3g4Ni9nZW5lcmljX3g4Njo3LjEuMS9OWUMvNTQ2NDg5Nzp1c2VyZGVidWcvdGVzdC1rZXlzGi0KCWRldmljZV9pZBIgemRmRENQSGFIckJRYWtxS2hFY0ZxWGlMd2JibEp3ZwAaJgoUd2lkZXZpbmVfY2RtX3ZlcnNpb24SDnY0LjEuMC1hbmRyb2lkGiQKH29lbV9jcnlwdG9fc2VjdXJpdHlfcGF0Y2hfbGV2ZWwSATAyCBABIAAoCzAAEm0KawpFCAESEMvGdYzAGUSptcbdLkQ+HkYaDXdpZGV2aW5lX3Rlc3QiIDQzMTUwODI0ODlkODc2NzdiMjFmN2M4MzU5M2ZjYjczEAEaIDQ3QTczM0EwRjBCM0ExNkEwMTAwMDAwMDAwMDAwMDAwGAEg662vjQYwFTjnxouSBBqAArOTGK7ZT7xqzVwVR2JwMkLYCcWtq9mR+3GcXWmpJUtSzCxPaZAYmYDyq5T+nUvlTkGYTY9qKdxwwmSfb3ojbmsYjJSut24bQmFkYf9qQbjGxNF94gXfFaFGc+HmsCCmA6g+FBUEPhgpPeSHTtsxUfC8oRiO6ojuaGqK7KnO6/mWZFnKSHMZL09rOF1VAkwuHypL7+h/9QOFddv8oyXVI+jNQu8cfMWJ+pE3kLpBewPUTvU6W9AcX9PDBguNYpZ11Rc0m38yupccgLcd51++SMFBSYQYs7tFz3qFTDQZEuUO/LPa2iKOugEKQXl6PgInudIxeGEb2tiV8zuqm9EON78=
2 |
3 | # D:\Projects\hd.kinopoisk.ru\kinopoisk_keys_obtaining\proto_compiler\bin>protoc.exe -I=D:\Projects\hd.kinopoisk.ru\kinopoisk_keys_obtaining --python_out=D:\Projects\hd.kinopoisk.ru\kinopoisk_keys_obtaining "D:\Projects\hd.kinopoisk.ru\kinopoisk_keys_obtaining\cdm\formats\wv_proto2.proto"
--------------------------------------------------------------------------------
/headers.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | headers = {
4 | 'authority': 'drmtoday.vieon.vn',
5 | 'sec-ch-ua': '"Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"',
6 | 'sec-ch-ua-mobile': '?0',
7 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36',
8 | 'x-dt-custom-data': 'eyJ1c2VySWQiOiIxMi1hZmRjM2Y5Zjc0OTM4YWE4M2JlMmEzMGE4YzA2MGY3NyIsInNlc3Npb25JZCI6ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUp2Y0dWeVlYUnZjbDlwWkNJNk1USXNJbk5sYzNOcGIyNUpaQ0k2SW1GbVpHTXpaamxtTFRjME9UTXRPR0ZoT0MwelltVXlMV0V6TUdFNFl6QTJNR1kzTnpFMk16a3pNakV3TURraUxDSjBhVzFsYzNSaGJYQWlPakUyTXprek1qRXdNRGtzSW5WelpYSkpaQ0k2SWpFeUxXRm1aR016WmpsbU56UTVNemhoWVRnelltVXlZVE13WVRoak1EWXdaamMzSW4wLmk5dzdYZlZZYWVwTmQyU2t4YWZhRVZRSXJSOXcxbHBxRXJ5WEFldTJjaU0iLCJtZXJjaGFudCI6InFuZXQifQ==',
9 | 'sec-ch-ua-platform': '"Windows"',
10 | 'accept': '/',
11 | 'origin': 'https://vieon.vn',
12 | 'sec-fetch-site': 'same-site',
13 | 'sec-fetch-mode': 'cors',
14 | 'sec-fetch-dest': 'empty',
15 | 'referer': 'https://vieon.vn/',
16 | 'accept-language': 'en-US,en;q=0.9,vi;q=0.8',
17 | }
18 | params = (
19 | ('platform', 'BROWSER'),
20 | ('type', 'MOVIE'),
21 | )
22 | # params inserted below will be passed to data-raw
23 |
24 | token = 'PFYtFSqWMYIROJYywBp0rbKvpD3z6N6E6UezNrZmCeKHkcJ5BiH4J8qwO0kfWOmrSsyFJMg9OqKKEwvuQiXL4qdDTa6ZYplNV9IQt6eEuveglDxjVlYd0vAecS5TZKn5mIs01YxcYNlTC7tu3BIk63JhMykMWEMmj1oEqaLa7N3ZrfU2VTlEPaWXBQV6PxnFeD022Yi298GOX/lDik7sXt5u4duT0aKIYbz0HPmjoh8b9Uy8bvzJUa0/SlgRcsW8RNTNjRJHM6N7BESCBGFEZcWoSXeDLEblEfRoBKro2iDGB1neLfRdY1fj7tJX1Rn9lj+OJwRTrBV4XoTQFuSsFwvYCeQfkAoRHXnze9njI5pY7sYgIGxuM4Zh3Wqaps6kxdO53QMm0jJ/L41X6r5CikSGKQ3Rcps8eQccZb9svEuyXMof2nm+eYL+AVPPrfmAf/BrmUHfiuF9CmNsF9pI7PjJfLeASmh3KDI/5gUuC4uJB7rjTzHVWNRz8MSvYjJ37/5V9+FTihpu04+0jFZ1AlGRks2dzk2IGvm5WcwUhsW2l/oQXlh7AgsksIl2DT0wH11WeG+h1/ijymMcG9AZzsxtkC53eLJ8bl55/Aag0TJfX4N6KvV7UhglXxkLifb06bLK/wlcGCfb01b4cnngcjPJEMxh2+Q7sJSnbyrtsfodzNah8JXITmx5kTkFFZkdtga1ha9jAb+Z40VMQfbrCLgh0GEe2G3VczcIt8QHAWAeS91TkzLbMRAugbgLvdbI8yQXtM15uo+TXrV4PNFprug33AnvnQu7IpNxlmgPMZhEn4Qqc/V++OFIpz/JYEMnLFvjYcXE2+sPc4uKnoE0xk33+IgZKtWtDhxRg2SJKyKOYxXucXybUuqPP/d+Nq1VNhD84Of+EfVvBjs1u75pNz/kmu5ms9ppRMkafq0/2311hbYi6VXmp6AeJweGJWkGKEFxw7xADuC8iM7xWnXW5aiNvhbsXBOwuTK8b2ioxjrg6OFhiMck+z9M5FHg+ktY7YB8iY+0V6B/Op2cQGtLaZ2E9lsomlQpxL+LiPI7UWEqmALEGH/Pk5tdLnlFRjEk2PPc8YURw+/nkP9tqJ/AXQwaXc9iwC5yxlYPCsp/Tc4='
25 | provider = 'kakaotv'
26 |
27 |
28 | releasePid = "_qVpiY31v_oU"
29 | # response = requests.post('https://widevine-proxy.ott.yandex.ru/proxy', headers=headers, cookies=cookies, data=data)
30 |
31 | # print(f'{chr(10)}widevine_license: {response.content}')
32 | #NB. Original query string below. It seems impossible to parse and
33 | #reproduce query strings 100% accurately so the one below is given
34 | #in case the reproduced version is not "correct".
35 | # response = requests.options('https://api.ott.kinopoisk.ru/v12/license-affected-content-metadata?contentIds=4b63db58ab27e92b90a457e533b00007&serviceId=25', headers=headers)
36 | # dash-cenc/hdr10_uhd_hevc_ec3.mpd
37 | # https://strm.yandex.ru/vh-ottenc-converted/vod-content/4315082489d87677b21f7c83593fcb73/8614535x1631802676x41611665-4e76-41ac-93a7-5070b77b5f3c/dash-cenc/sdr_uhd_hevc_ec3.mpd
38 |
39 | # 'GET /certificate HTTP/1.1'
40 |
--------------------------------------------------------------------------------
/cdm/vmp.py:
--------------------------------------------------------------------------------
1 | try:
2 | from google.protobuf.internal.decoder import _DecodeVarint as _di # this was tested to work with protobuf 3, but it's an internal API (any varint decoder might work)
3 | except ImportError:
4 | # this is generic and does not depend on pb internals, however it will decode "larger" possible numbers than pb decoder which has them fixed
5 | def LEB128_decode(buffer, pos, limit = 64):
6 | result = 0
7 | shift = 0
8 | while True:
9 | b = buffer[pos]
10 | pos += 1
11 | result |= ((b & 0x7F) << shift)
12 | if not (b & 0x80):
13 | return (result, pos)
14 | shift += 7
15 | if shift > limit:
16 | raise Exception("integer too large, shift: {}".format(shift))
17 | _di = LEB128_decode
18 |
19 |
20 | class FromFileMixin:
21 | @classmethod
22 | def from_file(cls, filename):
23 | """Load given a filename"""
24 | with open(filename,"rb") as f:
25 | return cls(f.read())
26 |
27 | # the signatures use a format internally similar to
28 | # protobuf's encoding, but without wire types
29 | class VariableReader(FromFileMixin):
30 | """Protobuf-like encoding reader"""
31 |
32 | def __init__(self, buf):
33 | self.buf = buf
34 | self.pos = 0
35 | self.size = len(buf)
36 |
37 | def read_int(self):
38 | """Read a variable length integer"""
39 | # _DecodeVarint will take care of out of range errors
40 | (val, nextpos) = _di(self.buf, self.pos)
41 | self.pos = nextpos
42 | return val
43 |
44 | def read_bytes_raw(self, size):
45 | """Read size bytes"""
46 | b = self.buf[self.pos:self.pos+size]
47 | self.pos += size
48 | return b
49 |
50 | def read_bytes(self):
51 | """Read a bytes object"""
52 | size = self.read_int()
53 | return self.read_bytes_raw(size)
54 |
55 | def is_end(self):
56 | return (self.size == self.pos)
57 |
58 |
59 | class TaggedReader(VariableReader):
60 | """Tagged reader, needed for implementing a WideVine signature reader"""
61 |
62 | def read_tag(self):
63 | """Read a tagged buffer"""
64 | return (self.read_int(), self.read_bytes())
65 |
66 | def read_all_tags(self, max_tag=3):
67 | tags = {}
68 | while (not self.is_end()):
69 | (tag, bytes) = self.read_tag()
70 | if (tag > max_tag):
71 | raise IndexError("tag out of bound: got {}, max {}".format(tag, max_tag))
72 |
73 | tags[tag] = bytes
74 | return tags
75 |
76 | class WideVineSignatureReader(FromFileMixin):
77 | """Parses a widevine .sig signature file."""
78 |
79 | SIGNER_TAG = 1
80 | SIGNATURE_TAG = 2
81 | ISMAINEXE_TAG = 3
82 |
83 | def __init__(self, buf):
84 | reader = TaggedReader(buf)
85 | self.version = reader.read_int()
86 | if (self.version != 0):
87 | raise Exception("Unsupported signature format version {}".format(self.version))
88 | self.tags = reader.read_all_tags()
89 |
90 | self.signer = self.tags[self.SIGNER_TAG]
91 | self.signature = self.tags[self.SIGNATURE_TAG]
92 |
93 | extra = self.tags[self.ISMAINEXE_TAG]
94 | if (len(extra) != 1 or (extra[0] > 1)):
95 | raise Exception("Unexpected 'ismainexe' field value (not '\\x00' or '\\x01'), please check: {0}".format(extra))
96 |
97 | self.mainexe = bool(extra[0])
98 |
99 | @classmethod
100 | def get_tags(cls, filename):
101 | """Return a dictionary of each tag in the signature file"""
102 | return cls.from_file(filename).tags
103 |
--------------------------------------------------------------------------------
/cdm/formats/sample_request_curl.txt:
--------------------------------------------------------------------------------
1 | curl 'https://widevine-proxy.ott.yandex.ru/proxy' \
2 | -H 'Connection: keep-alive' \
3 | -H 'Pragma: no-cache' \
4 | -H 'Cache-Control: no-cache' \
5 | -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"' \
6 | -H 'DNT: 1' \
7 | -H 'sec-ch-ua-mobile: ?0' \
8 | -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36' \
9 | -H 'sec-ch-ua-platform: "Windows"' \
10 | -H 'content-type: application/json' \
11 | -H 'Accept: */*' \
12 | -H 'Origin: https://hd.kinopoisk.ru' \
13 | -H 'Sec-Fetch-Site: cross-site' \
14 | -H 'Sec-Fetch-Mode: cors' \
15 | -H 'Sec-Fetch-Dest: empty' \
16 | -H 'Referer: https://hd.kinopoisk.ru/' \
17 | -H 'Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7,id;q=0.6,de;q=0.5,zh-TW;q=0.4,zh-CN;q=0.3,zh;q=0.2,uk;q=0.1' \
18 | -H 'Cookies were here...' \
19 | --data-raw '{"rawLicenseRequestBase64":"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","contentId":"4315082489d87677b21f7c83593fcb73","contentTypeId":21,"serviceName":"ott-kp","productId":2,"monetizationModel":"SVOD","expirationTimestamp":1638667827,"verificationRequired":true,"signature":"83e0e97ddfe852b73a1093a68ec43a349ce05c87","version":"V4"}' \
20 | --compressed --output -
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/struct.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option cc_enable_arenas = true;
37 | option go_package = "google.golang.org/protobuf/types/known/structpb";
38 | option java_package = "com.google.protobuf";
39 | option java_outer_classname = "StructProto";
40 | option java_multiple_files = true;
41 | option objc_class_prefix = "GPB";
42 |
43 | // `Struct` represents a structured data value, consisting of fields
44 | // which map to dynamically typed values. In some languages, `Struct`
45 | // might be supported by a native representation. For example, in
46 | // scripting languages like JS a struct is represented as an
47 | // object. The details of that representation are described together
48 | // with the proto support for the language.
49 | //
50 | // The JSON representation for `Struct` is JSON object.
51 | message Struct {
52 | // Unordered map of dynamically typed values.
53 | map fields = 1;
54 | }
55 |
56 | // `Value` represents a dynamically typed value which can be either
57 | // null, a number, a string, a boolean, a recursive struct value, or a
58 | // list of values. A producer of value is expected to set one of these
59 | // variants. Absence of any variant indicates an error.
60 | //
61 | // The JSON representation for `Value` is JSON value.
62 | message Value {
63 | // The kind of value.
64 | oneof kind {
65 | // Represents a null value.
66 | NullValue null_value = 1;
67 | // Represents a double value.
68 | double number_value = 2;
69 | // Represents a string value.
70 | string string_value = 3;
71 | // Represents a boolean value.
72 | bool bool_value = 4;
73 | // Represents a structured value.
74 | Struct struct_value = 5;
75 | // Represents a repeated `Value`.
76 | ListValue list_value = 6;
77 | }
78 | }
79 |
80 | // `NullValue` is a singleton enumeration to represent the null value for the
81 | // `Value` type union.
82 | //
83 | // The JSON representation for `NullValue` is JSON `null`.
84 | enum NullValue {
85 | // Null value.
86 | NULL_VALUE = 0;
87 | }
88 |
89 | // `ListValue` is a wrapper around a repeated field of values.
90 | //
91 | // The JSON representation for `ListValue` is JSON array.
92 | message ListValue {
93 | // Repeated field of dynamically typed values.
94 | repeated Value values = 1;
95 | }
96 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/wrappers.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | // Wrappers for primitive (non-message) types. These types are useful
32 | // for embedding primitives in the `google.protobuf.Any` type and for places
33 | // where we need to distinguish between the absence of a primitive
34 | // typed field and its default value.
35 | //
36 | // These wrappers have no meaningful use within repeated fields as they lack
37 | // the ability to detect presence on individual elements.
38 | // These wrappers have no meaningful use within a map or a oneof since
39 | // individual entries of a map or fields of a oneof can already detect presence.
40 |
41 | syntax = "proto3";
42 |
43 | package google.protobuf;
44 |
45 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
46 | option cc_enable_arenas = true;
47 | option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
48 | option java_package = "com.google.protobuf";
49 | option java_outer_classname = "WrappersProto";
50 | option java_multiple_files = true;
51 | option objc_class_prefix = "GPB";
52 |
53 | // Wrapper message for `double`.
54 | //
55 | // The JSON representation for `DoubleValue` is JSON number.
56 | message DoubleValue {
57 | // The double value.
58 | double value = 1;
59 | }
60 |
61 | // Wrapper message for `float`.
62 | //
63 | // The JSON representation for `FloatValue` is JSON number.
64 | message FloatValue {
65 | // The float value.
66 | float value = 1;
67 | }
68 |
69 | // Wrapper message for `int64`.
70 | //
71 | // The JSON representation for `Int64Value` is JSON string.
72 | message Int64Value {
73 | // The int64 value.
74 | int64 value = 1;
75 | }
76 |
77 | // Wrapper message for `uint64`.
78 | //
79 | // The JSON representation for `UInt64Value` is JSON string.
80 | message UInt64Value {
81 | // The uint64 value.
82 | uint64 value = 1;
83 | }
84 |
85 | // Wrapper message for `int32`.
86 | //
87 | // The JSON representation for `Int32Value` is JSON number.
88 | message Int32Value {
89 | // The int32 value.
90 | int32 value = 1;
91 | }
92 |
93 | // Wrapper message for `uint32`.
94 | //
95 | // The JSON representation for `UInt32Value` is JSON number.
96 | message UInt32Value {
97 | // The uint32 value.
98 | uint32 value = 1;
99 | }
100 |
101 | // Wrapper message for `bool`.
102 | //
103 | // The JSON representation for `BoolValue` is JSON `true` and `false`.
104 | message BoolValue {
105 | // The bool value.
106 | bool value = 1;
107 | }
108 |
109 | // Wrapper message for `string`.
110 | //
111 | // The JSON representation for `StringValue` is JSON string.
112 | message StringValue {
113 | // The string value.
114 | string value = 1;
115 | }
116 |
117 | // Wrapper message for `bytes`.
118 | //
119 | // The JSON representation for `BytesValue` is JSON string.
120 | message BytesValue {
121 | // The bytes value.
122 | bytes value = 1;
123 | }
124 |
--------------------------------------------------------------------------------
/l3.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Module: widevine_keys
3 | # Created on: 10.12.2021
4 | # Authors: medvm
5 | # Version: 2.1.0
6 |
7 | import base64, requests, sys, xmltodict
8 | import headers
9 | # import cookies
10 | import json
11 | from cdm import cdm, deviceconfig
12 | from base64 import b64encode
13 | from getPSSH import get_pssh
14 | from wvdecryptcustom import WvDecrypt
15 | from cdm.formats import wv_proto2_pb2 as wv_proto2
16 | from urllib.parse import urlparse
17 | import logging
18 | # logging.basicConfig(level=logging.DEBUG)
19 | MDP_URL = input('\nInput MPD URL: ')
20 | lic_url = input('License URL: ')
21 | # hardcoded for kinopoisk.ru
22 | # lic_url = 'https://widevine-proxy.ott.yandex.ru/proxy'
23 | responses = []
24 | license_b64 = ''
25 | pssh = get_pssh(MDP_URL)
26 | params = None
27 | params = urlparse(lic_url).query
28 | # pssh = 'AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQ7iYSc3cNGm7XKPe3hSn3MhoIdXNwLWNlbmMiGDdpWVNjM2NOR203WEtQZTNoU24zTWc9PSoAMgA='
29 | # params from mdp_url:
30 | # ottsession=5945048d6f844d1699054cc5d44548f1&
31 | # puid=339572866&
32 | # video_content_id=4315082489d87677b21f7c83593fcb73&
33 |
34 | print(f'{chr(10)}PSSH obtained.\n{pssh}')
35 |
36 | def WV_Function(pssh, lic_url, cert_b64=None):
37 | """main func, emulates license request and then decrypt obtained license
38 | fileds that changes every new request is signature, expirationTimestamp, watchSessionId, puid, and rawLicenseRequestBase64 """
39 | wvdecrypt = WvDecrypt(init_data_b64=pssh, cert_data_b64=cert_b64, device=deviceconfig.device_android_generic)
40 | raw_request = wvdecrypt.get_challenge()
41 | request = b64encode(raw_request)
42 | signature = cdm.hash_object
43 | # basic, mostly sites works
44 | responses.append(requests.post(url=lic_url, headers=headers.headers, data=raw_request, params=params))
45 | # some another sites support
46 | responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
47 | json={
48 | "rawLicenseRequestBase64": str(request, "utf-8" ),
49 | }))
50 | # kakaotv support
51 | responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
52 | data=f'token={headers.token}&provider={headers.provider}&payload={str(request, "utf-8" )}'
53 | ))
54 | # xfinity.com support
55 | headers.headers['licenseRequest'] = str(request, "utf-8" )
56 | responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
57 | ))
58 | del headers.headers['licenseRequest']
59 | # rte.ie support
60 | responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
61 | json={
62 | "getWidevineLicense":
63 | {
64 | 'releasePid': headers.releasePid,
65 | 'widevineChallenge': str(request, "utf-8" )
66 | },
67 | }))
68 | # kinopoisk support
69 | responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
70 | json={
71 | "rawLicenseRequestBase64": str(request, "utf-8" ),
72 | "puid": '339572866',
73 | "watchSessionId": 'ed0e355063ac48b783130a390dc27ba6',
74 | "contentId": '4315082489d87677b21f7c83593fcb73',
75 | "contentTypeId": '21',
76 | "serviceName": 'ott-kp',
77 | "productId": '2',
78 | "monetizationModel": 'SVOD',
79 | "expirationTimestamp": '1639009453',
80 | "verificationRequired": 'false',
81 | "signature": str(signature),
82 | "version": 'V4'
83 | }))
84 | for idx, response in enumerate(responses):
85 | try:
86 | str(response.content, "utf-8")
87 | except UnicodeDecodeError:
88 | widevine_license = response
89 | print(f'{chr(10)}license response status: {widevine_license}{chr(10)}')
90 | break
91 | else:
92 | if len(str(response.content, "utf-8")) > 500:
93 | widevine_license = response
94 | print(f'{chr(10)}license response status: {widevine_license}{chr(10)}')
95 | break
96 | if idx == len(responses) - 1:
97 | print(f'{chr(10)}license response status: {response}')
98 | print(f'server reports: {str(response.content, "utf-8")}')
99 | print(f'server did not issue license, make sure you have correctly pasted all the required headers in the headers.py. Also check json/raw params of POST request.{chr(10)}')
100 | exit()
101 |
102 | lic_field_names = ['license', 'payload', 'getWidevineLicenseResponse']
103 | lic_field_names2 = ['license']
104 |
105 | open('license_content.bin', 'wb').write(widevine_license.content)
106 |
107 | try:
108 | if str(widevine_license.content, 'utf-8').find(':'):
109 | for key in lic_field_names:
110 | try:
111 | license_b64 = json.loads(widevine_license.content.decode())[key]
112 | except:
113 | pass
114 | else:
115 | for key2 in lic_field_names2:
116 | try:
117 | license_b64 = json.loads(widevine_license.content.decode())[key][key2]
118 | except:
119 | pass
120 | else:
121 | license_b64 = widevine_license.content
122 | except:
123 | license_b64 = b64encode(widevine_license.content)
124 |
125 | wvdecrypt.update_license(license_b64)
126 | Correct, keyswvdecrypt = wvdecrypt.start_process()
127 | if Correct:
128 | return Correct, keyswvdecrypt
129 |
130 | correct, keys = WV_Function(pssh, lic_url)
131 |
132 | for key in keys:
133 | print('KID:KEY -> ' + key)
134 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/duration.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option cc_enable_arenas = true;
37 | option go_package = "google.golang.org/protobuf/types/known/durationpb";
38 | option java_package = "com.google.protobuf";
39 | option java_outer_classname = "DurationProto";
40 | option java_multiple_files = true;
41 | option objc_class_prefix = "GPB";
42 |
43 | // A Duration represents a signed, fixed-length span of time represented
44 | // as a count of seconds and fractions of seconds at nanosecond
45 | // resolution. It is independent of any calendar and concepts like "day"
46 | // or "month". It is related to Timestamp in that the difference between
47 | // two Timestamp values is a Duration and it can be added or subtracted
48 | // from a Timestamp. Range is approximately +-10,000 years.
49 | //
50 | // # Examples
51 | //
52 | // Example 1: Compute Duration from two Timestamps in pseudo code.
53 | //
54 | // Timestamp start = ...;
55 | // Timestamp end = ...;
56 | // Duration duration = ...;
57 | //
58 | // duration.seconds = end.seconds - start.seconds;
59 | // duration.nanos = end.nanos - start.nanos;
60 | //
61 | // if (duration.seconds < 0 && duration.nanos > 0) {
62 | // duration.seconds += 1;
63 | // duration.nanos -= 1000000000;
64 | // } else if (duration.seconds > 0 && duration.nanos < 0) {
65 | // duration.seconds -= 1;
66 | // duration.nanos += 1000000000;
67 | // }
68 | //
69 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
70 | //
71 | // Timestamp start = ...;
72 | // Duration duration = ...;
73 | // Timestamp end = ...;
74 | //
75 | // end.seconds = start.seconds + duration.seconds;
76 | // end.nanos = start.nanos + duration.nanos;
77 | //
78 | // if (end.nanos < 0) {
79 | // end.seconds -= 1;
80 | // end.nanos += 1000000000;
81 | // } else if (end.nanos >= 1000000000) {
82 | // end.seconds += 1;
83 | // end.nanos -= 1000000000;
84 | // }
85 | //
86 | // Example 3: Compute Duration from datetime.timedelta in Python.
87 | //
88 | // td = datetime.timedelta(days=3, minutes=10)
89 | // duration = Duration()
90 | // duration.FromTimedelta(td)
91 | //
92 | // # JSON Mapping
93 | //
94 | // In JSON format, the Duration type is encoded as a string rather than an
95 | // object, where the string ends in the suffix "s" (indicating seconds) and
96 | // is preceded by the number of seconds, with nanoseconds expressed as
97 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be
98 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
99 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1
100 | // microsecond should be expressed in JSON format as "3.000001s".
101 | //
102 | //
103 | message Duration {
104 | // Signed seconds of the span of time. Must be from -315,576,000,000
105 | // to +315,576,000,000 inclusive. Note: these bounds are computed from:
106 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
107 | int64 seconds = 1;
108 |
109 | // Signed fractions of a second at nanosecond resolution of the span
110 | // of time. Durations less than one second are represented with a 0
111 | // `seconds` field and a positive or negative `nanos` field. For durations
112 | // of one second or more, a non-zero value for the `nanos` field must be
113 | // of the same sign as the `seconds` field. Must be from -999,999,999
114 | // to +999,999,999 inclusive.
115 | int32 nanos = 2;
116 | }
117 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/any.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/anypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "AnyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 |
42 | // `Any` contains an arbitrary serialized protocol buffer message along with a
43 | // URL that describes the type of the serialized message.
44 | //
45 | // Protobuf library provides support to pack/unpack Any values in the form
46 | // of utility functions or additional generated methods of the Any type.
47 | //
48 | // Example 1: Pack and unpack a message in C++.
49 | //
50 | // Foo foo = ...;
51 | // Any any;
52 | // any.PackFrom(foo);
53 | // ...
54 | // if (any.UnpackTo(&foo)) {
55 | // ...
56 | // }
57 | //
58 | // Example 2: Pack and unpack a message in Java.
59 | //
60 | // Foo foo = ...;
61 | // Any any = Any.pack(foo);
62 | // ...
63 | // if (any.is(Foo.class)) {
64 | // foo = any.unpack(Foo.class);
65 | // }
66 | //
67 | // Example 3: Pack and unpack a message in Python.
68 | //
69 | // foo = Foo(...)
70 | // any = Any()
71 | // any.Pack(foo)
72 | // ...
73 | // if any.Is(Foo.DESCRIPTOR):
74 | // any.Unpack(foo)
75 | // ...
76 | //
77 | // Example 4: Pack and unpack a message in Go
78 | //
79 | // foo := &pb.Foo{...}
80 | // any, err := anypb.New(foo)
81 | // if err != nil {
82 | // ...
83 | // }
84 | // ...
85 | // foo := &pb.Foo{}
86 | // if err := any.UnmarshalTo(foo); err != nil {
87 | // ...
88 | // }
89 | //
90 | // The pack methods provided by protobuf library will by default use
91 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack
92 | // methods only use the fully qualified type name after the last '/'
93 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type
94 | // name "y.z".
95 | //
96 | //
97 | // JSON
98 | // ====
99 | // The JSON representation of an `Any` value uses the regular
100 | // representation of the deserialized, embedded message, with an
101 | // additional field `@type` which contains the type URL. Example:
102 | //
103 | // package google.profile;
104 | // message Person {
105 | // string first_name = 1;
106 | // string last_name = 2;
107 | // }
108 | //
109 | // {
110 | // "@type": "type.googleapis.com/google.profile.Person",
111 | // "firstName": ,
112 | // "lastName":
113 | // }
114 | //
115 | // If the embedded message type is well-known and has a custom JSON
116 | // representation, that representation will be embedded adding a field
117 | // `value` which holds the custom JSON in addition to the `@type`
118 | // field. Example (for message [google.protobuf.Duration][]):
119 | //
120 | // {
121 | // "@type": "type.googleapis.com/google.protobuf.Duration",
122 | // "value": "1.212s"
123 | // }
124 | //
125 | message Any {
126 | // A URL/resource name that uniquely identifies the type of the serialized
127 | // protocol buffer message. This string must contain at least
128 | // one "/" character. The last segment of the URL's path must represent
129 | // the fully qualified name of the type (as in
130 | // `path/google.protobuf.Duration`). The name should be in a canonical form
131 | // (e.g., leading "." is not accepted).
132 | //
133 | // In practice, teams usually precompile into the binary all types that they
134 | // expect it to use in the context of Any. However, for URLs which use the
135 | // scheme `http`, `https`, or no scheme, one can optionally set up a type
136 | // server that maps type URLs to message definitions as follows:
137 | //
138 | // * If no scheme is provided, `https` is assumed.
139 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
140 | // value in binary format, or produce an error.
141 | // * Applications are allowed to cache lookup results based on the
142 | // URL, or have them precompiled into a binary to avoid any
143 | // lookup. Therefore, binary compatibility needs to be preserved
144 | // on changes to types. (Use versioned type names to manage
145 | // breaking changes.)
146 | //
147 | // Note: this functionality is not currently available in the official
148 | // protobuf release, and it is not used for type URLs beginning with
149 | // type.googleapis.com.
150 | //
151 | // Schemes other than `http`, `https` (or the empty scheme) might be
152 | // used with implementation specific semantics.
153 | //
154 | string type_url = 1;
155 |
156 | // Must be a valid serialized protocol buffer of the above specified type.
157 | bytes value = 2;
158 | }
159 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/type.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | import "google/protobuf/any.proto";
36 | import "google/protobuf/source_context.proto";
37 |
38 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
39 | option cc_enable_arenas = true;
40 | option java_package = "com.google.protobuf";
41 | option java_outer_classname = "TypeProto";
42 | option java_multiple_files = true;
43 | option objc_class_prefix = "GPB";
44 | option go_package = "google.golang.org/protobuf/types/known/typepb";
45 |
46 | // A protocol buffer message type.
47 | message Type {
48 | // The fully qualified message name.
49 | string name = 1;
50 | // The list of fields.
51 | repeated Field fields = 2;
52 | // The list of types appearing in `oneof` definitions in this type.
53 | repeated string oneofs = 3;
54 | // The protocol buffer options.
55 | repeated Option options = 4;
56 | // The source context.
57 | SourceContext source_context = 5;
58 | // The source syntax.
59 | Syntax syntax = 6;
60 | }
61 |
62 | // A single field of a message type.
63 | message Field {
64 | // Basic field types.
65 | enum Kind {
66 | // Field type unknown.
67 | TYPE_UNKNOWN = 0;
68 | // Field type double.
69 | TYPE_DOUBLE = 1;
70 | // Field type float.
71 | TYPE_FLOAT = 2;
72 | // Field type int64.
73 | TYPE_INT64 = 3;
74 | // Field type uint64.
75 | TYPE_UINT64 = 4;
76 | // Field type int32.
77 | TYPE_INT32 = 5;
78 | // Field type fixed64.
79 | TYPE_FIXED64 = 6;
80 | // Field type fixed32.
81 | TYPE_FIXED32 = 7;
82 | // Field type bool.
83 | TYPE_BOOL = 8;
84 | // Field type string.
85 | TYPE_STRING = 9;
86 | // Field type group. Proto2 syntax only, and deprecated.
87 | TYPE_GROUP = 10;
88 | // Field type message.
89 | TYPE_MESSAGE = 11;
90 | // Field type bytes.
91 | TYPE_BYTES = 12;
92 | // Field type uint32.
93 | TYPE_UINT32 = 13;
94 | // Field type enum.
95 | TYPE_ENUM = 14;
96 | // Field type sfixed32.
97 | TYPE_SFIXED32 = 15;
98 | // Field type sfixed64.
99 | TYPE_SFIXED64 = 16;
100 | // Field type sint32.
101 | TYPE_SINT32 = 17;
102 | // Field type sint64.
103 | TYPE_SINT64 = 18;
104 | }
105 |
106 | // Whether a field is optional, required, or repeated.
107 | enum Cardinality {
108 | // For fields with unknown cardinality.
109 | CARDINALITY_UNKNOWN = 0;
110 | // For optional fields.
111 | CARDINALITY_OPTIONAL = 1;
112 | // For required fields. Proto2 syntax only.
113 | CARDINALITY_REQUIRED = 2;
114 | // For repeated fields.
115 | CARDINALITY_REPEATED = 3;
116 | }
117 |
118 | // The field type.
119 | Kind kind = 1;
120 | // The field cardinality.
121 | Cardinality cardinality = 2;
122 | // The field number.
123 | int32 number = 3;
124 | // The field name.
125 | string name = 4;
126 | // The field type URL, without the scheme, for message or enumeration
127 | // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
128 | string type_url = 6;
129 | // The index of the field type in `Type.oneofs`, for message or enumeration
130 | // types. The first type has index 1; zero means the type is not in the list.
131 | int32 oneof_index = 7;
132 | // Whether to use alternative packed wire representation.
133 | bool packed = 8;
134 | // The protocol buffer options.
135 | repeated Option options = 9;
136 | // The field JSON name.
137 | string json_name = 10;
138 | // The string value of the default value of this field. Proto2 syntax only.
139 | string default_value = 11;
140 | }
141 |
142 | // Enum type definition.
143 | message Enum {
144 | // Enum type name.
145 | string name = 1;
146 | // Enum value definitions.
147 | repeated EnumValue enumvalue = 2;
148 | // Protocol buffer options.
149 | repeated Option options = 3;
150 | // The source context.
151 | SourceContext source_context = 4;
152 | // The source syntax.
153 | Syntax syntax = 5;
154 | }
155 |
156 | // Enum value definition.
157 | message EnumValue {
158 | // Enum value name.
159 | string name = 1;
160 | // Enum value number.
161 | int32 number = 2;
162 | // Protocol buffer options.
163 | repeated Option options = 3;
164 | }
165 |
166 | // A protocol buffer option, which can be attached to a message, field,
167 | // enumeration, etc.
168 | message Option {
169 | // The option's name. For protobuf built-in options (options defined in
170 | // descriptor.proto), this is the short name. For example, `"map_entry"`.
171 | // For custom options, it should be the fully-qualified name. For example,
172 | // `"google.api.http"`.
173 | string name = 1;
174 | // The option's value packed in an Any message. If the value is a primitive,
175 | // the corresponding wrapper type defined in google/protobuf/wrappers.proto
176 | // should be used. If the value is an enum, it should be stored as an int32
177 | // value using the google.protobuf.Int32Value type.
178 | Any value = 2;
179 | }
180 |
181 | // The syntax in which a protocol buffer element is defined.
182 | enum Syntax {
183 | // Syntax `proto2`.
184 | SYNTAX_PROTO2 = 0;
185 | // Syntax `proto3`.
186 | SYNTAX_PROTO3 = 1;
187 | }
188 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/timestamp.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option cc_enable_arenas = true;
37 | option go_package = "google.golang.org/protobuf/types/known/timestamppb";
38 | option java_package = "com.google.protobuf";
39 | option java_outer_classname = "TimestampProto";
40 | option java_multiple_files = true;
41 | option objc_class_prefix = "GPB";
42 |
43 | // A Timestamp represents a point in time independent of any time zone or local
44 | // calendar, encoded as a count of seconds and fractions of seconds at
45 | // nanosecond resolution. The count is relative to an epoch at UTC midnight on
46 | // January 1, 1970, in the proleptic Gregorian calendar which extends the
47 | // Gregorian calendar backwards to year one.
48 | //
49 | // All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
50 | // second table is needed for interpretation, using a [24-hour linear
51 | // smear](https://developers.google.com/time/smear).
52 | //
53 | // The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
54 | // restricting to that range, we ensure that we can convert to and from [RFC
55 | // 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
56 | //
57 | // # Examples
58 | //
59 | // Example 1: Compute Timestamp from POSIX `time()`.
60 | //
61 | // Timestamp timestamp;
62 | // timestamp.set_seconds(time(NULL));
63 | // timestamp.set_nanos(0);
64 | //
65 | // Example 2: Compute Timestamp from POSIX `gettimeofday()`.
66 | //
67 | // struct timeval tv;
68 | // gettimeofday(&tv, NULL);
69 | //
70 | // Timestamp timestamp;
71 | // timestamp.set_seconds(tv.tv_sec);
72 | // timestamp.set_nanos(tv.tv_usec * 1000);
73 | //
74 | // Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
75 | //
76 | // FILETIME ft;
77 | // GetSystemTimeAsFileTime(&ft);
78 | // UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
79 | //
80 | // // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
81 | // // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
82 | // Timestamp timestamp;
83 | // timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
84 | // timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
85 | //
86 | // Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
87 | //
88 | // long millis = System.currentTimeMillis();
89 | //
90 | // Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
91 | // .setNanos((int) ((millis % 1000) * 1000000)).build();
92 | //
93 | //
94 | // Example 5: Compute Timestamp from Java `Instant.now()`.
95 | //
96 | // Instant now = Instant.now();
97 | //
98 | // Timestamp timestamp =
99 | // Timestamp.newBuilder().setSeconds(now.getEpochSecond())
100 | // .setNanos(now.getNano()).build();
101 | //
102 | //
103 | // Example 6: Compute Timestamp from current time in Python.
104 | //
105 | // timestamp = Timestamp()
106 | // timestamp.GetCurrentTime()
107 | //
108 | // # JSON Mapping
109 | //
110 | // In JSON format, the Timestamp type is encoded as a string in the
111 | // [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
112 | // format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
113 | // where {year} is always expressed using four digits while {month}, {day},
114 | // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
115 | // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
116 | // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
117 | // is required. A proto3 JSON serializer should always use UTC (as indicated by
118 | // "Z") when printing the Timestamp type and a proto3 JSON parser should be
119 | // able to accept both UTC and other timezones (as indicated by an offset).
120 | //
121 | // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
122 | // 01:30 UTC on January 15, 2017.
123 | //
124 | // In JavaScript, one can convert a Date object to this format using the
125 | // standard
126 | // [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
127 | // method. In Python, a standard `datetime.datetime` object can be converted
128 | // to this format using
129 | // [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
130 | // the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
131 | // the Joda Time's [`ISODateTimeFormat.dateTime()`](
132 | // http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
133 | // ) to obtain a formatter capable of generating timestamps in this format.
134 | //
135 | //
136 | message Timestamp {
137 | // Represents seconds of UTC time since Unix epoch
138 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
139 | // 9999-12-31T23:59:59Z inclusive.
140 | int64 seconds = 1;
141 |
142 | // Non-negative fractions of a second at nanosecond resolution. Negative
143 | // second values with fractions must still have non-negative nanos values
144 | // that count forward in time. Must be from 0 to 999,999,999
145 | // inclusive.
146 | int32 nanos = 2;
147 | }
148 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/api.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | import "google/protobuf/source_context.proto";
36 | import "google/protobuf/type.proto";
37 |
38 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
39 | option java_package = "com.google.protobuf";
40 | option java_outer_classname = "ApiProto";
41 | option java_multiple_files = true;
42 | option objc_class_prefix = "GPB";
43 | option go_package = "google.golang.org/protobuf/types/known/apipb";
44 |
45 | // Api is a light-weight descriptor for an API Interface.
46 | //
47 | // Interfaces are also described as "protocol buffer services" in some contexts,
48 | // such as by the "service" keyword in a .proto file, but they are different
49 | // from API Services, which represent a concrete implementation of an interface
50 | // as opposed to simply a description of methods and bindings. They are also
51 | // sometimes simply referred to as "APIs" in other contexts, such as the name of
52 | // this message itself. See https://cloud.google.com/apis/design/glossary for
53 | // detailed terminology.
54 | message Api {
55 | // The fully qualified name of this interface, including package name
56 | // followed by the interface's simple name.
57 | string name = 1;
58 |
59 | // The methods of this interface, in unspecified order.
60 | repeated Method methods = 2;
61 |
62 | // Any metadata attached to the interface.
63 | repeated Option options = 3;
64 |
65 | // A version string for this interface. If specified, must have the form
66 | // `major-version.minor-version`, as in `1.10`. If the minor version is
67 | // omitted, it defaults to zero. If the entire version field is empty, the
68 | // major version is derived from the package name, as outlined below. If the
69 | // field is not empty, the version in the package name will be verified to be
70 | // consistent with what is provided here.
71 | //
72 | // The versioning schema uses [semantic
73 | // versioning](http://semver.org) where the major version number
74 | // indicates a breaking change and the minor version an additive,
75 | // non-breaking change. Both version numbers are signals to users
76 | // what to expect from different versions, and should be carefully
77 | // chosen based on the product plan.
78 | //
79 | // The major version is also reflected in the package name of the
80 | // interface, which must end in `v`, as in
81 | // `google.feature.v1`. For major versions 0 and 1, the suffix can
82 | // be omitted. Zero major versions must only be used for
83 | // experimental, non-GA interfaces.
84 | //
85 | //
86 | string version = 4;
87 |
88 | // Source context for the protocol buffer service represented by this
89 | // message.
90 | SourceContext source_context = 5;
91 |
92 | // Included interfaces. See [Mixin][].
93 | repeated Mixin mixins = 6;
94 |
95 | // The source syntax of the service.
96 | Syntax syntax = 7;
97 | }
98 |
99 | // Method represents a method of an API interface.
100 | message Method {
101 | // The simple name of this method.
102 | string name = 1;
103 |
104 | // A URL of the input message type.
105 | string request_type_url = 2;
106 |
107 | // If true, the request is streamed.
108 | bool request_streaming = 3;
109 |
110 | // The URL of the output message type.
111 | string response_type_url = 4;
112 |
113 | // If true, the response is streamed.
114 | bool response_streaming = 5;
115 |
116 | // Any metadata attached to the method.
117 | repeated Option options = 6;
118 |
119 | // The source syntax of this method.
120 | Syntax syntax = 7;
121 | }
122 |
123 | // Declares an API Interface to be included in this interface. The including
124 | // interface must redeclare all the methods from the included interface, but
125 | // documentation and options are inherited as follows:
126 | //
127 | // - If after comment and whitespace stripping, the documentation
128 | // string of the redeclared method is empty, it will be inherited
129 | // from the original method.
130 | //
131 | // - Each annotation belonging to the service config (http,
132 | // visibility) which is not set in the redeclared method will be
133 | // inherited.
134 | //
135 | // - If an http annotation is inherited, the path pattern will be
136 | // modified as follows. Any version prefix will be replaced by the
137 | // version of the including interface plus the [root][] path if
138 | // specified.
139 | //
140 | // Example of a simple mixin:
141 | //
142 | // package google.acl.v1;
143 | // service AccessControl {
144 | // // Get the underlying ACL object.
145 | // rpc GetAcl(GetAclRequest) returns (Acl) {
146 | // option (google.api.http).get = "/v1/{resource=**}:getAcl";
147 | // }
148 | // }
149 | //
150 | // package google.storage.v2;
151 | // service Storage {
152 | // rpc GetAcl(GetAclRequest) returns (Acl);
153 | //
154 | // // Get a data record.
155 | // rpc GetData(GetDataRequest) returns (Data) {
156 | // option (google.api.http).get = "/v2/{resource=**}";
157 | // }
158 | // }
159 | //
160 | // Example of a mixin configuration:
161 | //
162 | // apis:
163 | // - name: google.storage.v2.Storage
164 | // mixins:
165 | // - name: google.acl.v1.AccessControl
166 | //
167 | // The mixin construct implies that all methods in `AccessControl` are
168 | // also declared with same name and request/response types in
169 | // `Storage`. A documentation generator or annotation processor will
170 | // see the effective `Storage.GetAcl` method after inheriting
171 | // documentation and annotations as follows:
172 | //
173 | // service Storage {
174 | // // Get the underlying ACL object.
175 | // rpc GetAcl(GetAclRequest) returns (Acl) {
176 | // option (google.api.http).get = "/v2/{resource=**}:getAcl";
177 | // }
178 | // ...
179 | // }
180 | //
181 | // Note how the version in the path pattern changed from `v1` to `v2`.
182 | //
183 | // If the `root` field in the mixin is specified, it should be a
184 | // relative path under which inherited HTTP paths are placed. Example:
185 | //
186 | // apis:
187 | // - name: google.storage.v2.Storage
188 | // mixins:
189 | // - name: google.acl.v1.AccessControl
190 | // root: acls
191 | //
192 | // This implies the following inherited HTTP annotation:
193 | //
194 | // service Storage {
195 | // // Get the underlying ACL object.
196 | // rpc GetAcl(GetAclRequest) returns (Acl) {
197 | // option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
198 | // }
199 | // ...
200 | // }
201 | message Mixin {
202 | // The fully qualified name of the interface which is included.
203 | string name = 1;
204 |
205 | // If non-empty specifies a path under which inherited HTTP paths
206 | // are rooted.
207 | string root = 2;
208 | }
209 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/field_mask.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "FieldMaskProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb";
41 | option cc_enable_arenas = true;
42 |
43 | // `FieldMask` represents a set of symbolic field paths, for example:
44 | //
45 | // paths: "f.a"
46 | // paths: "f.b.d"
47 | //
48 | // Here `f` represents a field in some root message, `a` and `b`
49 | // fields in the message found in `f`, and `d` a field found in the
50 | // message in `f.b`.
51 | //
52 | // Field masks are used to specify a subset of fields that should be
53 | // returned by a get operation or modified by an update operation.
54 | // Field masks also have a custom JSON encoding (see below).
55 | //
56 | // # Field Masks in Projections
57 | //
58 | // When used in the context of a projection, a response message or
59 | // sub-message is filtered by the API to only contain those fields as
60 | // specified in the mask. For example, if the mask in the previous
61 | // example is applied to a response message as follows:
62 | //
63 | // f {
64 | // a : 22
65 | // b {
66 | // d : 1
67 | // x : 2
68 | // }
69 | // y : 13
70 | // }
71 | // z: 8
72 | //
73 | // The result will not contain specific values for fields x,y and z
74 | // (their value will be set to the default, and omitted in proto text
75 | // output):
76 | //
77 | //
78 | // f {
79 | // a : 22
80 | // b {
81 | // d : 1
82 | // }
83 | // }
84 | //
85 | // A repeated field is not allowed except at the last position of a
86 | // paths string.
87 | //
88 | // If a FieldMask object is not present in a get operation, the
89 | // operation applies to all fields (as if a FieldMask of all fields
90 | // had been specified).
91 | //
92 | // Note that a field mask does not necessarily apply to the
93 | // top-level response message. In case of a REST get operation, the
94 | // field mask applies directly to the response, but in case of a REST
95 | // list operation, the mask instead applies to each individual message
96 | // in the returned resource list. In case of a REST custom method,
97 | // other definitions may be used. Where the mask applies will be
98 | // clearly documented together with its declaration in the API. In
99 | // any case, the effect on the returned resource/resources is required
100 | // behavior for APIs.
101 | //
102 | // # Field Masks in Update Operations
103 | //
104 | // A field mask in update operations specifies which fields of the
105 | // targeted resource are going to be updated. The API is required
106 | // to only change the values of the fields as specified in the mask
107 | // and leave the others untouched. If a resource is passed in to
108 | // describe the updated values, the API ignores the values of all
109 | // fields not covered by the mask.
110 | //
111 | // If a repeated field is specified for an update operation, new values will
112 | // be appended to the existing repeated field in the target resource. Note that
113 | // a repeated field is only allowed in the last position of a `paths` string.
114 | //
115 | // If a sub-message is specified in the last position of the field mask for an
116 | // update operation, then new value will be merged into the existing sub-message
117 | // in the target resource.
118 | //
119 | // For example, given the target message:
120 | //
121 | // f {
122 | // b {
123 | // d: 1
124 | // x: 2
125 | // }
126 | // c: [1]
127 | // }
128 | //
129 | // And an update message:
130 | //
131 | // f {
132 | // b {
133 | // d: 10
134 | // }
135 | // c: [2]
136 | // }
137 | //
138 | // then if the field mask is:
139 | //
140 | // paths: ["f.b", "f.c"]
141 | //
142 | // then the result will be:
143 | //
144 | // f {
145 | // b {
146 | // d: 10
147 | // x: 2
148 | // }
149 | // c: [1, 2]
150 | // }
151 | //
152 | // An implementation may provide options to override this default behavior for
153 | // repeated and message fields.
154 | //
155 | // In order to reset a field's value to the default, the field must
156 | // be in the mask and set to the default value in the provided resource.
157 | // Hence, in order to reset all fields of a resource, provide a default
158 | // instance of the resource and set all fields in the mask, or do
159 | // not provide a mask as described below.
160 | //
161 | // If a field mask is not present on update, the operation applies to
162 | // all fields (as if a field mask of all fields has been specified).
163 | // Note that in the presence of schema evolution, this may mean that
164 | // fields the client does not know and has therefore not filled into
165 | // the request will be reset to their default. If this is unwanted
166 | // behavior, a specific service may require a client to always specify
167 | // a field mask, producing an error if not.
168 | //
169 | // As with get operations, the location of the resource which
170 | // describes the updated values in the request message depends on the
171 | // operation kind. In any case, the effect of the field mask is
172 | // required to be honored by the API.
173 | //
174 | // ## Considerations for HTTP REST
175 | //
176 | // The HTTP kind of an update operation which uses a field mask must
177 | // be set to PATCH instead of PUT in order to satisfy HTTP semantics
178 | // (PUT must only be used for full updates).
179 | //
180 | // # JSON Encoding of Field Masks
181 | //
182 | // In JSON, a field mask is encoded as a single string where paths are
183 | // separated by a comma. Fields name in each path are converted
184 | // to/from lower-camel naming conventions.
185 | //
186 | // As an example, consider the following message declarations:
187 | //
188 | // message Profile {
189 | // User user = 1;
190 | // Photo photo = 2;
191 | // }
192 | // message User {
193 | // string display_name = 1;
194 | // string address = 2;
195 | // }
196 | //
197 | // In proto a field mask for `Profile` may look as such:
198 | //
199 | // mask {
200 | // paths: "user.display_name"
201 | // paths: "photo"
202 | // }
203 | //
204 | // In JSON, the same mask is represented as below:
205 | //
206 | // {
207 | // mask: "user.displayName,photo"
208 | // }
209 | //
210 | // # Field Masks and Oneof Fields
211 | //
212 | // Field masks treat fields in oneofs just as regular fields. Consider the
213 | // following message:
214 | //
215 | // message SampleMessage {
216 | // oneof test_oneof {
217 | // string name = 4;
218 | // SubMessage sub_message = 9;
219 | // }
220 | // }
221 | //
222 | // The field mask can be:
223 | //
224 | // mask {
225 | // paths: "name"
226 | // }
227 | //
228 | // Or:
229 | //
230 | // mask {
231 | // paths: "sub_message"
232 | // }
233 | //
234 | // Note that oneof type names ("test_oneof" in this case) cannot be used in
235 | // paths.
236 | //
237 | // ## Field Mask Verification
238 | //
239 | // The implementation of any API method which has a FieldMask type field in the
240 | // request should verify the included field paths, and return an
241 | // `INVALID_ARGUMENT` error if any path is unmappable.
242 | message FieldMask {
243 | // The set of field mask paths.
244 | repeated string paths = 1;
245 | }
246 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/compiler/plugin.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | // Author: kenton@google.com (Kenton Varda)
32 | //
33 | // WARNING: The plugin interface is currently EXPERIMENTAL and is subject to
34 | // change.
35 | //
36 | // protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is
37 | // just a program that reads a CodeGeneratorRequest from stdin and writes a
38 | // CodeGeneratorResponse to stdout.
39 | //
40 | // Plugins written using C++ can use google/protobuf/compiler/plugin.h instead
41 | // of dealing with the raw protocol defined here.
42 | //
43 | // A plugin executable needs only to be placed somewhere in the path. The
44 | // plugin should be named "protoc-gen-$NAME", and will then be used when the
45 | // flag "--${NAME}_out" is passed to protoc.
46 |
47 | syntax = "proto2";
48 |
49 | package google.protobuf.compiler;
50 | option java_package = "com.google.protobuf.compiler";
51 | option java_outer_classname = "PluginProtos";
52 |
53 | option go_package = "google.golang.org/protobuf/types/pluginpb";
54 |
55 | import "google/protobuf/descriptor.proto";
56 |
57 | // The version number of protocol compiler.
58 | message Version {
59 | optional int32 major = 1;
60 | optional int32 minor = 2;
61 | optional int32 patch = 3;
62 | // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
63 | // be empty for mainline stable releases.
64 | optional string suffix = 4;
65 | }
66 |
67 | // An encoded CodeGeneratorRequest is written to the plugin's stdin.
68 | message CodeGeneratorRequest {
69 | // The .proto files that were explicitly listed on the command-line. The
70 | // code generator should generate code only for these files. Each file's
71 | // descriptor will be included in proto_file, below.
72 | repeated string file_to_generate = 1;
73 |
74 | // The generator parameter passed on the command-line.
75 | optional string parameter = 2;
76 |
77 | // FileDescriptorProtos for all files in files_to_generate and everything
78 | // they import. The files will appear in topological order, so each file
79 | // appears before any file that imports it.
80 | //
81 | // protoc guarantees that all proto_files will be written after
82 | // the fields above, even though this is not technically guaranteed by the
83 | // protobuf wire format. This theoretically could allow a plugin to stream
84 | // in the FileDescriptorProtos and handle them one by one rather than read
85 | // the entire set into memory at once. However, as of this writing, this
86 | // is not similarly optimized on protoc's end -- it will store all fields in
87 | // memory at once before sending them to the plugin.
88 | //
89 | // Type names of fields and extensions in the FileDescriptorProto are always
90 | // fully qualified.
91 | repeated FileDescriptorProto proto_file = 15;
92 |
93 | // The version number of protocol compiler.
94 | optional Version compiler_version = 3;
95 |
96 | }
97 |
98 | // The plugin writes an encoded CodeGeneratorResponse to stdout.
99 | message CodeGeneratorResponse {
100 | // Error message. If non-empty, code generation failed. The plugin process
101 | // should exit with status code zero even if it reports an error in this way.
102 | //
103 | // This should be used to indicate errors in .proto files which prevent the
104 | // code generator from generating correct code. Errors which indicate a
105 | // problem in protoc itself -- such as the input CodeGeneratorRequest being
106 | // unparseable -- should be reported by writing a message to stderr and
107 | // exiting with a non-zero status code.
108 | optional string error = 1;
109 |
110 | // A bitmask of supported features that the code generator supports.
111 | // This is a bitwise "or" of values from the Feature enum.
112 | optional uint64 supported_features = 2;
113 |
114 | // Sync with code_generator.h.
115 | enum Feature {
116 | FEATURE_NONE = 0;
117 | FEATURE_PROTO3_OPTIONAL = 1;
118 | }
119 |
120 | // Represents a single generated file.
121 | message File {
122 | // The file name, relative to the output directory. The name must not
123 | // contain "." or ".." components and must be relative, not be absolute (so,
124 | // the file cannot lie outside the output directory). "/" must be used as
125 | // the path separator, not "\".
126 | //
127 | // If the name is omitted, the content will be appended to the previous
128 | // file. This allows the generator to break large files into small chunks,
129 | // and allows the generated text to be streamed back to protoc so that large
130 | // files need not reside completely in memory at one time. Note that as of
131 | // this writing protoc does not optimize for this -- it will read the entire
132 | // CodeGeneratorResponse before writing files to disk.
133 | optional string name = 1;
134 |
135 | // If non-empty, indicates that the named file should already exist, and the
136 | // content here is to be inserted into that file at a defined insertion
137 | // point. This feature allows a code generator to extend the output
138 | // produced by another code generator. The original generator may provide
139 | // insertion points by placing special annotations in the file that look
140 | // like:
141 | // @@protoc_insertion_point(NAME)
142 | // The annotation can have arbitrary text before and after it on the line,
143 | // which allows it to be placed in a comment. NAME should be replaced with
144 | // an identifier naming the point -- this is what other generators will use
145 | // as the insertion_point. Code inserted at this point will be placed
146 | // immediately above the line containing the insertion point (thus multiple
147 | // insertions to the same point will come out in the order they were added).
148 | // The double-@ is intended to make it unlikely that the generated code
149 | // could contain things that look like insertion points by accident.
150 | //
151 | // For example, the C++ code generator places the following line in the
152 | // .pb.h files that it generates:
153 | // // @@protoc_insertion_point(namespace_scope)
154 | // This line appears within the scope of the file's package namespace, but
155 | // outside of any particular class. Another plugin can then specify the
156 | // insertion_point "namespace_scope" to generate additional classes or
157 | // other declarations that should be placed in this scope.
158 | //
159 | // Note that if the line containing the insertion point begins with
160 | // whitespace, the same whitespace will be added to every line of the
161 | // inserted text. This is useful for languages like Python, where
162 | // indentation matters. In these languages, the insertion point comment
163 | // should be indented the same amount as any inserted code will need to be
164 | // in order to work correctly in that context.
165 | //
166 | // The code generator that generates the initial file and the one which
167 | // inserts into it must both run as part of a single invocation of protoc.
168 | // Code generators are executed in the order in which they appear on the
169 | // command line.
170 | //
171 | // If |insertion_point| is present, |name| must also be present.
172 | optional string insertion_point = 2;
173 |
174 | // The file contents.
175 | optional string content = 15;
176 |
177 | // Information describing the file content being inserted. If an insertion
178 | // point is used, this information will be appropriately offset and inserted
179 | // into the code generation metadata for the generated files.
180 | optional GeneratedCodeInfo generated_code_info = 16;
181 | }
182 | repeated File file = 15;
183 | }
184 |
--------------------------------------------------------------------------------
/cdm/formats/wv_proto3.proto:
--------------------------------------------------------------------------------
1 | // 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:
2 | syntax = "proto3";
3 |
4 | // from x86 (partial), most of it from the ARM version:
5 | message ClientIdentification {
6 | enum TokenType {
7 | KEYBOX = 0;
8 | DEVICE_CERTIFICATE = 1;
9 | REMOTE_ATTESTATION_CERTIFICATE = 2;
10 | }
11 | message NameValue {
12 | string Name = 1;
13 | string Value = 2;
14 | }
15 | message ClientCapabilities {
16 | enum HdcpVersion {
17 | HDCP_NONE = 0;
18 | HDCP_V1 = 1;
19 | HDCP_V2 = 2;
20 | HDCP_V2_1 = 3;
21 | HDCP_V2_2 = 4;
22 | }
23 | uint32 ClientToken = 1;
24 | uint32 SessionToken = 2;
25 | uint32 VideoResolutionConstraints = 3;
26 | HdcpVersion MaxHdcpVersion = 4;
27 | uint32 OemCryptoApiVersion = 5;
28 | }
29 | TokenType Type = 1;
30 | //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:
31 | SignedDeviceCertificate Token = 2;
32 | repeated NameValue ClientInfo = 3;
33 | bytes ProviderClientToken = 4;
34 | uint32 LicenseCounter = 5;
35 | ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later
36 | }
37 |
38 | message DeviceCertificate {
39 | enum CertificateType {
40 | ROOT = 0;
41 | INTERMEDIATE = 1;
42 | USER_DEVICE = 2;
43 | SERVICE = 3;
44 | }
45 | //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)
46 | CertificateType Type = 1;
47 | bytes SerialNumber = 2;
48 | uint32 CreationTimeSeconds = 3;
49 | bytes PublicKey = 4;
50 | uint32 SystemId = 5;
51 | uint32 TestDeviceDeprecated = 6; // is it bool or int?
52 | bytes ServiceId = 7; // service URL for service certificates
53 | }
54 |
55 | // missing some references,
56 | message DeviceCertificateStatus {
57 | enum CertificateStatus {
58 | VALID = 0;
59 | REVOKED = 1;
60 | }
61 | bytes SerialNumber = 1;
62 | CertificateStatus Status = 2;
63 | ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated?
64 | }
65 |
66 | message DeviceCertificateStatusList {
67 | uint32 CreationTimeSeconds = 1;
68 | repeated DeviceCertificateStatus CertificateStatus = 2;
69 | }
70 |
71 | message EncryptedClientIdentification {
72 | string ServiceId = 1;
73 | bytes ServiceCertificateSerialNumber = 2;
74 | bytes EncryptedClientId = 3;
75 | bytes EncryptedClientIdIv = 4;
76 | bytes EncryptedPrivacyKey = 5;
77 | }
78 |
79 | // todo: fill (for this top-level type, it might be impossible/difficult)
80 | enum LicenseType {
81 | ZERO = 0;
82 | DEFAULT = 1; // do not know what this is either, but should be 1; on recent versions may go up to 3 (latest x86)
83 | }
84 |
85 | // todo: fill (for this top-level type, it might be impossible/difficult)
86 | // this is just a guess because these globals got lost, but really, do we need more?
87 | enum ProtocolVersion {
88 | DUMMY = 0;
89 | CURRENT = 21; // don't have symbols for this
90 | }
91 |
92 |
93 | message LicenseIdentification {
94 | bytes RequestId = 1;
95 | bytes SessionId = 2;
96 | bytes PurchaseId = 3;
97 | LicenseType Type = 4;
98 | uint32 Version = 5;
99 | bytes ProviderSessionToken = 6;
100 | }
101 |
102 |
103 | message License {
104 | message Policy {
105 | uint32 CanPlay = 1;
106 | uint32 CanPersist = 2;
107 | uint32 CanRenew = 3;
108 | uint32 RentalDurationSeconds = 4;
109 | uint32 PlaybackDurationSeconds = 5;
110 | uint32 LicenseDurationSeconds = 6;
111 | uint32 RenewalRecoveryDurationSeconds = 7;
112 | string RenewalServerUrl = 8;
113 | uint32 RenewalDelaySeconds = 9;
114 | uint32 RenewalRetryIntervalSeconds = 10;
115 | uint32 RenewWithUsage = 11;
116 | uint32 UnknownPolicy12 = 12;
117 | }
118 | message KeyContainer {
119 | enum KeyType {
120 | _NOKEYTYPE = 0; // dummy, added to satisfy proto3, not present in original
121 | SIGNING = 1;
122 | CONTENT = 2;
123 | KEY_CONTROL = 3;
124 | OPERATOR_SESSION = 4;
125 | }
126 | enum SecurityLevel {
127 | _NOSECLEVEL = 0; // dummy, added to satisfy proto3, not present in original
128 | SW_SECURE_CRYPTO = 1;
129 | SW_SECURE_DECODE = 2;
130 | HW_SECURE_CRYPTO = 3;
131 | HW_SECURE_DECODE = 4;
132 | HW_SECURE_ALL = 5;
133 | }
134 | message OutputProtection {
135 | enum CGMS {
136 | COPY_FREE = 0;
137 | COPY_ONCE = 2;
138 | COPY_NEVER = 3;
139 | CGMS_NONE = 0x2A; // PC default!
140 | }
141 | ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away
142 | CGMS CgmsFlags = 2;
143 | }
144 | message KeyControl {
145 | bytes KeyControlBlock = 1; // what is this?
146 | bytes Iv = 2;
147 | }
148 | message OperatorSessionKeyPermissions {
149 | uint32 AllowEncrypt = 1;
150 | uint32 AllowDecrypt = 2;
151 | uint32 AllowSign = 3;
152 | uint32 AllowSignatureVerify = 4;
153 | }
154 | message VideoResolutionConstraint {
155 | uint32 MinResolutionPixels = 1;
156 | uint32 MaxResolutionPixels = 2;
157 | OutputProtection RequiredProtection = 3;
158 | }
159 | bytes Id = 1;
160 | bytes Iv = 2;
161 | bytes Key = 3;
162 | KeyType Type = 4;
163 | SecurityLevel Level = 5;
164 | OutputProtection RequiredProtection = 6;
165 | OutputProtection RequestedProtection = 7;
166 | KeyControl _KeyControl = 8; // duped names, etc
167 | OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc
168 | repeated VideoResolutionConstraint VideoResolutionConstraints = 10;
169 | }
170 | LicenseIdentification Id = 1;
171 | Policy _Policy = 2; // duped names, etc
172 | repeated KeyContainer Key = 3;
173 | uint32 LicenseStartTime = 4;
174 | uint32 RemoteAttestationVerified = 5; // bool?
175 | bytes ProviderClientToken = 6;
176 | // there might be more, check with newer versions (I see field 7-8 in a lic)
177 | // this appeared in latest x86:
178 | uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc)
179 | bytes UnknownHdcpDataField = 8;
180 | }
181 |
182 | message LicenseError {
183 | enum Error {
184 | DUMMY_NO_ERROR = 0; // dummy, added to satisfy proto3
185 | INVALID_DEVICE_CERTIFICATE = 1;
186 | REVOKED_DEVICE_CERTIFICATE = 2;
187 | SERVICE_UNAVAILABLE = 3;
188 | }
189 | //LicenseRequest.RequestType ErrorCode; // clang mismatch
190 | Error ErrorCode = 1;
191 | }
192 |
193 | message LicenseRequest {
194 | message ContentIdentification {
195 | message CENC {
196 | // 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:
197 | WidevineCencHeader Pssh = 1;
198 | LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1
199 | bytes RequestId = 3;
200 | }
201 | message WebM {
202 | bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
203 | LicenseType LicenseType = 2;
204 | bytes RequestId = 3;
205 | }
206 | message ExistingLicense {
207 | LicenseIdentification LicenseId = 1;
208 | uint32 SecondsSinceStarted = 2;
209 | uint32 SecondsSinceLastPlayed = 3;
210 | bytes SessionUsageTableEntry = 4;
211 | }
212 | CENC CencId = 1;
213 | WebM WebmId = 2;
214 | ExistingLicense License = 3;
215 | }
216 | enum RequestType {
217 | DUMMY_REQ_TYPE = 0; // dummy, added to satisfy proto3
218 | NEW = 1;
219 | RENEWAL = 2;
220 | RELEASE = 3;
221 | }
222 | ClientIdentification ClientId = 1;
223 | ContentIdentification ContentId = 2;
224 | RequestType Type = 3;
225 | uint32 RequestTime = 4;
226 | bytes KeyControlNonceDeprecated = 5;
227 | ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
228 | uint32 KeyControlNonce = 7;
229 | EncryptedClientIdentification EncryptedClientId = 8;
230 | }
231 |
232 | message ProvisionedDeviceInfo {
233 | enum WvSecurityLevel {
234 | LEVEL_UNSPECIFIED = 0;
235 | LEVEL_1 = 1;
236 | LEVEL_2 = 2;
237 | LEVEL_3 = 3;
238 | }
239 | uint32 SystemId = 1;
240 | string Soc = 2;
241 | string Manufacturer = 3;
242 | string Model = 4;
243 | string DeviceType = 5;
244 | uint32 ModelYear = 6;
245 | WvSecurityLevel SecurityLevel = 7;
246 | uint32 TestDevice = 8; // bool?
247 | }
248 |
249 |
250 | // todo: fill
251 | message ProvisioningOptions {
252 | }
253 |
254 | // todo: fill
255 | message ProvisioningRequest {
256 | }
257 |
258 | // todo: fill
259 | message ProvisioningResponse {
260 | }
261 |
262 | message RemoteAttestation {
263 | EncryptedClientIdentification Certificate = 1;
264 | string Salt = 2;
265 | string Signature = 3;
266 | }
267 |
268 | // todo: fill
269 | message SessionInit {
270 | }
271 |
272 | // todo: fill
273 | message SessionState {
274 | }
275 |
276 | // todo: fill
277 | message SignedCertificateStatusList {
278 | }
279 |
280 | message SignedDeviceCertificate {
281 |
282 | //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:
283 | DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later
284 | bytes Signature = 2;
285 | SignedDeviceCertificate Signer = 3;
286 | }
287 |
288 |
289 | // todo: fill
290 | message SignedProvisioningMessage {
291 | }
292 |
293 | // the root of all messages, from either server or client
294 | message SignedMessage {
295 | enum MessageType {
296 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3
297 | LICENSE_REQUEST = 1;
298 | LICENSE = 2;
299 | ERROR_RESPONSE = 3;
300 | SERVICE_CERTIFICATE_REQUEST = 4;
301 | SERVICE_CERTIFICATE = 5;
302 | }
303 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
304 | 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
305 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
306 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
307 | bytes SessionKey = 4; // often RSA wrapped for licenses
308 | RemoteAttestation RemoteAttestation = 5;
309 | }
310 |
311 |
312 |
313 | // This message is copied from google's docs, not reversed:
314 | message WidevineCencHeader {
315 | enum Algorithm {
316 | UNENCRYPTED = 0;
317 | AESCTR = 1;
318 | };
319 | Algorithm algorithm = 1;
320 | repeated bytes key_id = 2;
321 |
322 | // Content provider name.
323 | string provider = 3;
324 |
325 | // A content identifier, specified by content provider.
326 | bytes content_id = 4;
327 |
328 | // Track type. Acceptable values are SD, HD and AUDIO. Used to
329 | // differentiate content keys used by an asset.
330 | string track_type_deprecated = 5;
331 |
332 | // The name of a registered policy to be used for this asset.
333 | string policy = 6;
334 |
335 | // Crypto period index, for media using key rotation.
336 | uint32 crypto_period_index = 7;
337 |
338 | // Optional protected context for group content. The grouped_license is a
339 | // serialized SignedMessage.
340 | bytes grouped_license = 8;
341 |
342 | // Protection scheme identifying the encryption algorithm.
343 | // Represented as one of the following 4CC values:
344 | // 'cenc' (AESCTR), 'cbc1' (AESCBC),
345 | // 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample).
346 | uint32 protection_scheme = 9;
347 |
348 | // Optional. For media using key rotation, this represents the duration
349 | // of each crypto period in seconds.
350 | uint32 crypto_period_seconds = 10;
351 | }
352 |
353 |
354 |
355 |
356 | // 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
357 | message SignedLicenseRequest {
358 | enum MessageType {
359 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3
360 | LICENSE_REQUEST = 1;
361 | LICENSE = 2;
362 | ERROR_RESPONSE = 3;
363 | SERVICE_CERTIFICATE_REQUEST = 4;
364 | SERVICE_CERTIFICATE = 5;
365 | }
366 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
367 | 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
368 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
369 | string Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
370 | bytes SessionKey = 4; // often RSA wrapped for licenses
371 | RemoteAttestation RemoteAttestation = 5;
372 | }
373 |
374 | message SignedLicense {
375 | enum MessageType {
376 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3
377 | LICENSE_REQUEST = 1;
378 | LICENSE = 2;
379 | ERROR_RESPONSE = 3;
380 | SERVICE_CERTIFICATE_REQUEST = 4;
381 | SERVICE_CERTIFICATE = 5;
382 | }
383 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
384 | 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
385 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
386 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
387 | bytes SessionKey = 4; // often RSA wrapped for licenses
388 | RemoteAttestation RemoteAttestation = 5;
389 | }
--------------------------------------------------------------------------------
/cdm/cdm.py:
--------------------------------------------------------------------------------
1 | import base64
2 |
3 | import os
4 | import time
5 | import binascii
6 | import base64
7 | from google.protobuf.message import DecodeError
8 | from google.protobuf import text_format
9 | import hashlib
10 | from cdm.formats import wv_proto2_pb2 as wv_proto2
11 | from cdm.session import Session
12 | from cdm.key import Key
13 | from Cryptodome.Random import get_random_bytes
14 | from Cryptodome.Random import random
15 | from Cryptodome.Cipher import PKCS1_OAEP, AES
16 | from Cryptodome.Hash import CMAC, SHA256, HMAC, SHA1
17 | from Cryptodome.PublicKey import RSA
18 | from Cryptodome.Signature import pss
19 | from Cryptodome.Util import Padding
20 | import logging
21 |
22 | class Cdm:
23 | def __init__(self):
24 | self.logger = logging.getLogger(__name__)
25 | self.sessions = {}
26 |
27 | def open_session(self, init_data_b64, device, raw_init_data = None, offline=False):
28 | self.logger.debug("open_session(init_data_b64={}, device={}".format(init_data_b64, device))
29 | self.logger.info("opening new cdm session")
30 | if device.session_id_type == 'android':
31 | # format: 16 random hexdigits, 2 digit counter, 14 0s
32 | rand_ascii = ''.join(random.choice('ABCDEF0123456789') for _ in range(16))
33 | counter = '01' # this resets regularly so its fine to use 01
34 | rest = '00000000000000'
35 | session_id = rand_ascii + counter + rest
36 | session_id = session_id.encode('ascii')
37 | elif device.session_id_type == 'chrome':
38 | rand_bytes = get_random_bytes(16)
39 | session_id = rand_bytes
40 | else:
41 | # other formats NYI
42 | self.logger.error("device type is unusable")
43 | return 1
44 | if raw_init_data and isinstance(raw_init_data, (bytes, bytearray)):
45 | # used for NF key exchange, where they don't provide a valid PSSH
46 | init_data = raw_init_data
47 | self.raw_pssh = True
48 | else:
49 | init_data = self._parse_init_data(init_data_b64)
50 | self.raw_pssh = False
51 |
52 | if init_data:
53 | new_session = Session(session_id, init_data, device, offline)
54 | else:
55 | self.logger.error("unable to parse init data")
56 | return 1
57 | self.sessions[session_id] = new_session
58 | self.logger.info("session opened and init data parsed successfully")
59 | return session_id
60 |
61 | def _parse_init_data(self, init_data_b64):
62 | parsed_init_data = wv_proto2.WidevineCencHeader()
63 | try:
64 | self.logger.debug("trying to parse init_data directly")
65 | parsed_init_data.ParseFromString(base64.b64decode(init_data_b64)[32:])
66 | except DecodeError:
67 | self.logger.debug("unable to parse as-is, trying with removed pssh box header")
68 | try:
69 | id_bytes = parsed_init_data.ParseFromString(base64.b64decode(init_data_b64)[32:])
70 | except DecodeError:
71 | self.logger.error("unable to parse, unsupported init data format")
72 | return None
73 | self.logger.debug("init_data:")
74 | for line in text_format.MessageToString(parsed_init_data).splitlines():
75 | self.logger.debug(line)
76 | return parsed_init_data
77 |
78 | def close_session(self, session_id):
79 | self.logger.debug("close_session(session_id={})".format(session_id))
80 | self.logger.info("closing cdm session")
81 | if session_id in self.sessions:
82 | self.sessions.pop(session_id)
83 | self.logger.info("cdm session closed")
84 | return 0
85 | else:
86 | self.logger.info("session {} not found".format(session_id))
87 | return 1
88 |
89 | def set_service_certificate(self, session_id, cert_b64):
90 | self.logger.debug("set_service_certificate(session_id={}, cert={})".format(session_id, cert_b64))
91 | self.logger.info("setting service certificate")
92 |
93 | if session_id not in self.sessions:
94 | self.logger.error("session id doesn't exist")
95 | return 1
96 |
97 | session = self.sessions[session_id]
98 |
99 | message = wv_proto2.SignedMessage()
100 |
101 | try:
102 | message.ParseFromString(base64.b64decode(cert_b64))
103 | except DecodeError:
104 | self.logger.error("failed to parse cert as SignedMessage")
105 |
106 | service_certificate = wv_proto2.SignedDeviceCertificate()
107 |
108 | if message.Type:
109 | self.logger.debug("service cert provided as signedmessage")
110 | try:
111 | service_certificate.ParseFromString(message.Msg)
112 | except DecodeError:
113 | self.logger.error("failed to parse service certificate")
114 | return 1
115 | else:
116 | self.logger.debug("service cert provided as signeddevicecertificate")
117 | try:
118 | service_certificate.ParseFromString(base64.b64decode(cert_b64))
119 | except DecodeError:
120 | self.logger.error("failed to parse service certificate")
121 | return 1
122 |
123 | self.logger.debug("service certificate:")
124 | for line in text_format.MessageToString(service_certificate).splitlines():
125 | self.logger.debug(line)
126 |
127 | session.service_certificate = service_certificate
128 | session.privacy_mode = True
129 |
130 | return 0
131 |
132 | def get_license_request(self, session_id):
133 | self.logger.debug("get_license_request(session_id={})".format(session_id))
134 | self.logger.info("getting license request")
135 |
136 | if session_id not in self.sessions:
137 | self.logger.error("session ID does not exist")
138 | return 1
139 |
140 | session = self.sessions[session_id]
141 |
142 | # raw pssh will be treated as bytes and not parsed
143 | if self.raw_pssh:
144 | license_request = wv_proto2.SignedLicenseRequestRaw()
145 | else:
146 | license_request = wv_proto2.SignedLicenseRequest()
147 | client_id = wv_proto2.ClientIdentification()
148 |
149 | if not os.path.exists(session.device_config.device_client_id_blob_filename):
150 | self.logger.error("no client ID blob available for this device")
151 | return 1
152 |
153 | with open(session.device_config.device_client_id_blob_filename, "rb") as f:
154 | try:
155 | cid_bytes = client_id.ParseFromString(f.read())
156 | except DecodeError:
157 | self.logger.error("client id failed to parse as protobuf")
158 | return 1
159 |
160 | self.logger.debug("building license request")
161 | if not self.raw_pssh:
162 | license_request.Type = wv_proto2.SignedLicenseRequest.MessageType.Value('LICENSE_REQUEST')
163 | license_request.Msg.ContentId.CencId.Pssh.CopyFrom(session.init_data)
164 | else:
165 | license_request.Type = wv_proto2.SignedLicenseRequestRaw.MessageType.Value('LICENSE_REQUEST')
166 | license_request.Msg.ContentId.CencId.Pssh = session.init_data # bytes
167 |
168 | if session.offline:
169 | license_type = wv_proto2.LicenseType.Value('OFFLINE')
170 | else:
171 | license_type = wv_proto2.LicenseType.Value('DEFAULT')
172 | license_request.Msg.ContentId.CencId.LicenseType = license_type
173 | license_request.Msg.ContentId.CencId.RequestId = session_id
174 | license_request.Msg.Type = wv_proto2.LicenseRequest.RequestType.Value('NEW')
175 | license_request.Msg.RequestTime = int(time.time())
176 | license_request.Msg.ProtocolVersion = wv_proto2.ProtocolVersion.Value('CURRENT')
177 | if session.device_config.send_key_control_nonce:
178 | license_request.Msg.KeyControlNonce = random.randrange(1, 2**31)
179 |
180 | # wv_proto2.WidevineCencHeader().provider = 'conax'
181 |
182 | if session.privacy_mode:
183 | if session.device_config.vmp:
184 | self.logger.debug("vmp required, adding to client_id")
185 | self.logger.debug("reading vmp hashes")
186 | vmp_hashes = wv_proto2.FileHashes()
187 | with open(session.device_config.device_vmp_blob_filename, "rb") as f:
188 | try:
189 | vmp_bytes = vmp_hashes.ParseFromString(f.read())
190 | except DecodeError:
191 | self.logger.error("vmp hashes failed to parse as protobuf")
192 | return 1
193 | client_id._FileHashes.CopyFrom(vmp_hashes)
194 | self.logger.debug("privacy mode & service certificate loaded, encrypting client id")
195 | self.logger.debug("unencrypted client id:")
196 | for line in text_format.MessageToString(client_id).splitlines():
197 | self.logger.debug(line)
198 | cid_aes_key = get_random_bytes(16)
199 | cid_iv = get_random_bytes(16)
200 |
201 | cid_cipher = AES.new(cid_aes_key, AES.MODE_CBC, cid_iv)
202 |
203 | encrypted_client_id = cid_cipher.encrypt(Padding.pad(client_id.SerializeToString(), 16))
204 |
205 | service_public_key = RSA.importKey(session.service_certificate._DeviceCertificate.PublicKey)
206 |
207 | service_cipher = PKCS1_OAEP.new(service_public_key)
208 |
209 | encrypted_cid_key = service_cipher.encrypt(cid_aes_key)
210 |
211 | encrypted_client_id_proto = wv_proto2.EncryptedClientIdentification()
212 |
213 | encrypted_client_id_proto.ServiceId = session.service_certificate._DeviceCertificate.ServiceId
214 | encrypted_client_id_proto.ServiceCertificateSerialNumber = session.service_certificate._DeviceCertificate.SerialNumber
215 | encrypted_client_id_proto.EncryptedClientId = encrypted_client_id
216 | encrypted_client_id_proto.EncryptedClientIdIv = cid_iv
217 | encrypted_client_id_proto.EncryptedPrivacyKey = encrypted_cid_key
218 |
219 | license_request.Msg.EncryptedClientId.CopyFrom(encrypted_client_id_proto)
220 | else:
221 | license_request.Msg.ClientId.CopyFrom(client_id)
222 |
223 | if session.device_config.private_key_available:
224 | key = RSA.importKey(open(session.device_config.device_private_key_filename).read())
225 | session.device_key = key
226 | else:
227 | self.logger.error("need device private key, other methods unimplemented")
228 | return 1
229 |
230 | self.logger.debug("signing license request")
231 |
232 | hash = SHA1.new(license_request.Msg.SerializeToString())
233 |
234 | signature = pss.new(key).sign(hash)
235 |
236 | global hash_object
237 | global hash2test
238 | m = hashlib.sha1()
239 | m.update(signature)
240 | # hash2test = m.digest()
241 | hash_object = m.hexdigest()
242 |
243 | license_request.Signature = signature
244 |
245 | session.license_request = license_request
246 |
247 | self.logger.debug("license request:")
248 | for line in text_format.MessageToString(session.license_request).splitlines():
249 | self.logger.debug(line)
250 | self.logger.info("license request created")
251 | self.logger.debug("license request b64: {}".format(base64.b64encode(license_request.SerializeToString())))
252 | return license_request.SerializeToString()
253 |
254 | def provide_license(self, session_id, license_b64):
255 | self.logger.debug("provide_license(session_id={}, license_b64={})".format(session_id, license_b64))
256 | self.logger.info("decrypting provided license")
257 |
258 | if session_id not in self.sessions:
259 | self.logger.error("session does not exist")
260 | return 1
261 |
262 | session = self.sessions[session_id]
263 |
264 | if not session.license_request:
265 | self.logger.error("generate a license request first!")
266 | return 1
267 |
268 | license = wv_proto2.SignedLicense()
269 | try:
270 | license.ParseFromString(base64.b64decode(license_b64))
271 | except DecodeError:
272 | self.logger.error("unable to parse license - check protobufs")
273 | return 1
274 |
275 | session.license = license
276 |
277 | self.logger.debug("license:")
278 | for line in text_format.MessageToString(license).splitlines():
279 | self.logger.debug(line)
280 |
281 | self.logger.debug("deriving keys from session key")
282 |
283 | oaep_cipher = PKCS1_OAEP.new(session.device_key)
284 |
285 | session.session_key = oaep_cipher.decrypt(license.SessionKey)
286 |
287 | lic_req_msg = session.license_request.Msg.SerializeToString()
288 |
289 | enc_key_base = b"ENCRYPTION\000" + lic_req_msg + b"\0\0\0\x80"
290 | auth_key_base = b"AUTHENTICATION\0" + lic_req_msg + b"\0\0\2\0"
291 |
292 | enc_key = b"\x01" + enc_key_base
293 | auth_key_1 = b"\x01" + auth_key_base
294 | auth_key_2 = b"\x02" + auth_key_base
295 | auth_key_3 = b"\x03" + auth_key_base
296 | auth_key_4 = b"\x04" + auth_key_base
297 |
298 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
299 | cmac_obj.update(enc_key)
300 |
301 | enc_cmac_key = cmac_obj.digest()
302 |
303 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
304 | cmac_obj.update(auth_key_1)
305 | auth_cmac_key_1 = cmac_obj.digest()
306 |
307 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
308 | cmac_obj.update(auth_key_2)
309 | auth_cmac_key_2 = cmac_obj.digest()
310 |
311 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
312 | cmac_obj.update(auth_key_3)
313 | auth_cmac_key_3 = cmac_obj.digest()
314 |
315 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
316 | cmac_obj.update(auth_key_4)
317 | auth_cmac_key_4 = cmac_obj.digest()
318 |
319 | auth_cmac_combined_1 = auth_cmac_key_1 + auth_cmac_key_2
320 | auth_cmac_combined_2 = auth_cmac_key_3 + auth_cmac_key_4
321 |
322 | session.derived_keys['enc'] = enc_cmac_key
323 | session.derived_keys['auth_1'] = auth_cmac_combined_1
324 | session.derived_keys['auth_2'] = auth_cmac_combined_2
325 |
326 | self.logger.debug('verifying license signature')
327 |
328 | lic_hmac = HMAC.new(session.derived_keys['auth_1'], digestmod=SHA256)
329 | lic_hmac.update(license.Msg.SerializeToString())
330 |
331 | self.logger.debug("calculated sig: {} actual sig: {}".format(lic_hmac.hexdigest(), binascii.hexlify(license.Signature)))
332 |
333 | if lic_hmac.digest() != license.Signature:
334 | self.logger.info("license signature doesn't match - writing bin so they can be debugged")
335 | with open("original_lic.bin", "wb") as f:
336 | f.write(base64.b64decode(license_b64))
337 | with open("parsed_lic.bin", "wb") as f:
338 | f.write(license.SerializeToString())
339 | self.logger.info("continuing anyway")
340 |
341 | self.logger.debug("key count: {}".format(len(license.Msg.Key)))
342 | for key in license.Msg.Key:
343 | if key.Id:
344 | key_id = key.Id
345 | else:
346 | key_id = wv_proto2.License.KeyContainer.KeyType.Name(key.Type).encode('utf-8')
347 | encrypted_key = key.Key
348 | iv = key.Iv
349 | type = wv_proto2.License.KeyContainer.KeyType.Name(key.Type)
350 |
351 | cipher = AES.new(session.derived_keys['enc'], AES.MODE_CBC, iv=iv)
352 | decrypted_key = cipher.decrypt(encrypted_key)
353 | if type == "OPERATOR_SESSION":
354 | permissions = []
355 | perms = key._OperatorSessionKeyPermissions
356 | for (descriptor, value) in perms.ListFields():
357 | if value == 1:
358 | permissions.append(descriptor.name)
359 | print(permissions)
360 | else:
361 | permissions = []
362 | session.keys.append(Key(key_id, type, Padding.unpad(decrypted_key, 16), permissions))
363 |
364 | self.logger.info("decrypted all keys")
365 | return 0
366 |
367 | def get_keys(self, session_id):
368 | if session_id in self.sessions:
369 | return self.sessions[session_id].keys
370 | else:
371 | self.logger.error("session not found")
372 | return 1
373 |
--------------------------------------------------------------------------------
/cdm/formats/wv_proto2.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto2";
2 |
3 |
4 | // This message is copied from google's docs, not reversed:
5 | message WidevineCencHeader {
6 | enum Algorithm {
7 | UNENCRYPTED = 0;
8 | AESCTR = 1;
9 | };
10 | optional Algorithm algorithm = 1;
11 | repeated bytes key_id = 2;
12 |
13 | // Content provider name.
14 | optional string provider = 3;
15 |
16 | // A content identifier, specified by content provider.
17 | optional bytes content_id = 4;
18 |
19 | // Track type. Acceptable values are SD, HD and AUDIO. Used to
20 | // differentiate content keys used by an asset.
21 | optional string track_type_deprecated = 5;
22 |
23 | // The name of a registered policy to be used for this asset.
24 | optional string policy = 6;
25 |
26 | // Crypto period index, for media using key rotation.
27 | optional uint32 crypto_period_index = 7;
28 |
29 | // Optional protected context for group content. The grouped_license is a
30 | // serialized SignedMessage.
31 | optional bytes grouped_license = 8;
32 |
33 | // Protection scheme identifying the encryption algorithm.
34 | // Represented as one of the following 4CC values:
35 | // 'cenc' (AESCTR), 'cbc1' (AESCBC),
36 | // 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample).
37 | optional uint32 protection_scheme = 9;
38 |
39 | // Optional. For media using key rotation, this represents the duration
40 | // of each crypto period in seconds.
41 | optional uint32 crypto_period_seconds = 10;
42 | }
43 |
44 |
45 |
46 | // from x86 (partial), most of it from the ARM version:
47 | message ClientIdentification {
48 | enum TokenType {
49 | KEYBOX = 0;
50 | DEVICE_CERTIFICATE = 1;
51 | REMOTE_ATTESTATION_CERTIFICATE = 2;
52 | }
53 | message NameValue {
54 | required string Name = 1;
55 | required string Value = 2;
56 | }
57 | message ClientCapabilities {
58 | enum HdcpVersion {
59 | HDCP_NONE = 0;
60 | HDCP_V1 = 1;
61 | HDCP_V2 = 2;
62 | HDCP_V2_1 = 3;
63 | HDCP_V2_2 = 4;
64 | }
65 | optional uint32 ClientToken = 1;
66 | optional uint32 SessionToken = 2;
67 | optional uint32 VideoResolutionConstraints = 3;
68 | optional HdcpVersion MaxHdcpVersion = 4;
69 | optional uint32 OemCryptoApiVersion = 5;
70 | }
71 | required TokenType Type = 1;
72 | //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:
73 | optional SignedDeviceCertificate Token = 2; // use this when parsing, "bytes" when building a client id blob
74 | repeated NameValue ClientInfo = 3;
75 | optional bytes ProviderClientToken = 4;
76 | optional uint32 LicenseCounter = 5;
77 | optional ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later
78 | optional FileHashes _FileHashes = 7; // vmp blob goes here
79 | }
80 |
81 | message DeviceCertificate {
82 | enum CertificateType {
83 | ROOT = 0;
84 | INTERMEDIATE = 1;
85 | USER_DEVICE = 2;
86 | SERVICE = 3;
87 | }
88 | 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)
89 | optional bytes SerialNumber = 2;
90 | optional uint32 CreationTimeSeconds = 3;
91 | optional bytes PublicKey = 4;
92 | optional uint32 SystemId = 5;
93 | optional uint32 TestDeviceDeprecated = 6; // is it bool or int?
94 | optional bytes ServiceId = 7; // service URL for service certificates
95 | }
96 |
97 | // missing some references,
98 | message DeviceCertificateStatus {
99 | enum CertificateStatus {
100 | VALID = 0;
101 | REVOKED = 1;
102 | }
103 | optional bytes SerialNumber = 1;
104 | optional CertificateStatus Status = 2;
105 | optional ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated?
106 | }
107 |
108 | message DeviceCertificateStatusList {
109 | optional uint32 CreationTimeSeconds = 1;
110 | repeated DeviceCertificateStatus CertificateStatus = 2;
111 | }
112 |
113 | message EncryptedClientIdentification {
114 | required string ServiceId = 1;
115 | optional bytes ServiceCertificateSerialNumber = 2;
116 | required bytes EncryptedClientId = 3;
117 | required bytes EncryptedClientIdIv = 4;
118 | required bytes EncryptedPrivacyKey = 5;
119 | }
120 |
121 | // todo: fill (for this top-level type, it might be impossible/difficult)
122 | enum LicenseType {
123 | ZERO = 0;
124 | 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
125 | OFFLINE = 2;
126 | }
127 |
128 | // todo: fill (for this top-level type, it might be impossible/difficult)
129 | // this is just a guess because these globals got lost, but really, do we need more?
130 | enum ProtocolVersion {
131 | CURRENT = 21; // don't have symbols for this
132 | }
133 |
134 |
135 | message LicenseIdentification {
136 | optional bytes RequestId = 1;
137 | optional bytes SessionId = 2;
138 | optional bytes PurchaseId = 3;
139 | optional LicenseType Type = 4;
140 | optional uint32 Version = 5;
141 | optional bytes ProviderSessionToken = 6;
142 | }
143 |
144 |
145 | message License {
146 | message Policy {
147 | optional bool CanPlay = 1; // changed from uint32 to bool
148 | optional bool CanPersist = 2;
149 | optional bool CanRenew = 3;
150 | optional uint32 RentalDurationSeconds = 4;
151 | optional uint32 PlaybackDurationSeconds = 5;
152 | optional uint32 LicenseDurationSeconds = 6;
153 | optional uint32 RenewalRecoveryDurationSeconds = 7;
154 | optional string RenewalServerUrl = 8;
155 | optional uint32 RenewalDelaySeconds = 9;
156 | optional uint32 RenewalRetryIntervalSeconds = 10;
157 | optional bool RenewWithUsage = 11; // was uint32
158 | }
159 | message KeyContainer {
160 | enum KeyType {
161 | SIGNING = 1;
162 | CONTENT = 2;
163 | KEY_CONTROL = 3;
164 | OPERATOR_SESSION = 4;
165 | }
166 | enum SecurityLevel {
167 | SW_SECURE_CRYPTO = 1;
168 | SW_SECURE_DECODE = 2;
169 | HW_SECURE_CRYPTO = 3;
170 | HW_SECURE_DECODE = 4;
171 | HW_SECURE_ALL = 5;
172 | }
173 | message OutputProtection {
174 | enum CGMS {
175 | COPY_FREE = 0;
176 | COPY_ONCE = 2;
177 | COPY_NEVER = 3;
178 | CGMS_NONE = 0x2A; // PC default!
179 | }
180 | optional ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away
181 | optional CGMS CgmsFlags = 2;
182 | }
183 | message KeyControl {
184 | required bytes KeyControlBlock = 1; // what is this?
185 | required bytes Iv = 2;
186 | }
187 | message OperatorSessionKeyPermissions {
188 | optional uint32 AllowEncrypt = 1;
189 | optional uint32 AllowDecrypt = 2;
190 | optional uint32 AllowSign = 3;
191 | optional uint32 AllowSignatureVerify = 4;
192 | }
193 | message VideoResolutionConstraint {
194 | optional uint32 MinResolutionPixels = 1;
195 | optional uint32 MaxResolutionPixels = 2;
196 | optional OutputProtection RequiredProtection = 3;
197 | }
198 | optional bytes Id = 1;
199 | optional bytes Iv = 2;
200 | optional bytes Key = 3;
201 | optional KeyType Type = 4;
202 | optional SecurityLevel Level = 5;
203 | optional OutputProtection RequiredProtection = 6;
204 | optional OutputProtection RequestedProtection = 7;
205 | optional KeyControl _KeyControl = 8; // duped names, etc
206 | optional OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc
207 | repeated VideoResolutionConstraint VideoResolutionConstraints = 10;
208 | }
209 | optional LicenseIdentification Id = 1;
210 | optional Policy _Policy = 2; // duped names, etc
211 | repeated KeyContainer Key = 3;
212 | optional uint32 LicenseStartTime = 4;
213 | optional uint32 RemoteAttestationVerified = 5; // bool?
214 | optional bytes ProviderClientToken = 6;
215 | // there might be more, check with newer versions (I see field 7-8 in a lic)
216 | // this appeared in latest x86:
217 | optional uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc)
218 | }
219 |
220 | message LicenseError {
221 | enum Error {
222 | INVALID_DEVICE_CERTIFICATE = 1;
223 | REVOKED_DEVICE_CERTIFICATE = 2;
224 | SERVICE_UNAVAILABLE = 3;
225 | }
226 | //LicenseRequest.RequestType ErrorCode; // clang mismatch
227 | optional Error ErrorCode = 1;
228 | }
229 |
230 | message LicenseRequest {
231 | message ContentIdentification {
232 | message CENC {
233 | //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:
234 | optional WidevineCencHeader Pssh = 1;
235 | 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!)
236 | optional bytes RequestId = 3;
237 | }
238 | message WebM {
239 | optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
240 | optional LicenseType LicenseType = 2;
241 | optional bytes RequestId = 3;
242 | }
243 | message ExistingLicense {
244 | optional LicenseIdentification LicenseId = 1;
245 | optional uint32 SecondsSinceStarted = 2;
246 | optional uint32 SecondsSinceLastPlayed = 3;
247 | optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB!
248 | }
249 | optional CENC CencId = 1;
250 | optional WebM WebmId = 2;
251 | optional ExistingLicense License = 3;
252 | }
253 | enum RequestType {
254 | NEW = 1;
255 | RENEWAL = 2;
256 | RELEASE = 3;
257 | }
258 | optional ClientIdentification ClientId = 1;
259 | optional ContentIdentification ContentId = 2;
260 | optional RequestType Type = 3;
261 | optional uint32 RequestTime = 4;
262 | optional bytes KeyControlNonceDeprecated = 5;
263 | optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
264 | optional uint32 KeyControlNonce = 7;
265 | optional EncryptedClientIdentification EncryptedClientId = 8;
266 | optional string version = 9;
267 | optional string puid = 10;
268 | optional string watchSessionId = 11;
269 | optional string contentId = 12;
270 | optional string contentTypeId = 13;
271 | optional string serviceName = 14;
272 | optional string productId = 15;
273 | optional string monetizationModel = 16;
274 | optional string expirationTimestamp = 17;
275 | optional string verificationRequired = 18;
276 | optional string signature = 19;
277 | }
278 |
279 | // raw pssh hack
280 | message LicenseRequestRaw {
281 | message ContentIdentification {
282 | message CENC {
283 | 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:
284 | //optional WidevineCencHeader Pssh = 1;
285 | 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!)
286 | optional bytes RequestId = 3;
287 | }
288 | message WebM {
289 | optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
290 | optional LicenseType LicenseType = 2;
291 | optional bytes RequestId = 3;
292 | }
293 | message ExistingLicense {
294 | optional LicenseIdentification LicenseId = 1;
295 | optional uint32 SecondsSinceStarted = 2;
296 | optional uint32 SecondsSinceLastPlayed = 3;
297 | optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB!
298 | }
299 | optional CENC CencId = 1;
300 | optional WebM WebmId = 2;
301 | optional ExistingLicense License = 3;
302 | }
303 | enum RequestType {
304 | NEW = 1;
305 | RENEWAL = 2;
306 | RELEASE = 3;
307 | }
308 | optional ClientIdentification ClientId = 1;
309 | optional ContentIdentification ContentId = 2;
310 | optional RequestType Type = 3;
311 | optional uint32 RequestTime = 4;
312 | optional bytes KeyControlNonceDeprecated = 5;
313 | optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
314 | optional uint32 KeyControlNonce = 7;
315 | optional EncryptedClientIdentification EncryptedClientId = 8;
316 | }
317 |
318 |
319 | message ProvisionedDeviceInfo {
320 | enum WvSecurityLevel {
321 | LEVEL_UNSPECIFIED = 0;
322 | LEVEL_1 = 1;
323 | LEVEL_2 = 2;
324 | LEVEL_3 = 3;
325 | }
326 | optional uint32 SystemId = 1;
327 | optional string Soc = 2;
328 | optional string Manufacturer = 3;
329 | optional string Model = 4;
330 | optional string DeviceType = 5;
331 | optional uint32 ModelYear = 6;
332 | optional WvSecurityLevel SecurityLevel = 7;
333 | optional uint32 TestDevice = 8; // bool?
334 | }
335 |
336 |
337 | // todo: fill
338 | message ProvisioningOptions {
339 | }
340 |
341 | // todo: fill
342 | message ProvisioningRequest {
343 | }
344 |
345 | // todo: fill
346 | message ProvisioningResponse {
347 | }
348 |
349 | message RemoteAttestation {
350 | optional EncryptedClientIdentification Certificate = 1;
351 | optional string Salt = 2;
352 | optional string Signature = 3;
353 | }
354 |
355 | // todo: fill
356 | message SessionInit {
357 | }
358 |
359 | // todo: fill
360 | message SessionState {
361 | }
362 |
363 | // todo: fill
364 | message SignedCertificateStatusList {
365 | }
366 |
367 | message SignedDeviceCertificate {
368 |
369 | //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:
370 | optional DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later
371 | optional bytes Signature = 2;
372 | optional SignedDeviceCertificate Signer = 3;
373 | }
374 |
375 |
376 | // todo: fill
377 | message SignedProvisioningMessage {
378 | }
379 |
380 | // the root of all messages, from either server or client
381 | message SignedMessage {
382 | enum MessageType {
383 | LICENSE_REQUEST = 1;
384 | LICENSE = 2;
385 | ERROR_RESPONSE = 3;
386 | SERVICE_CERTIFICATE_REQUEST = 4;
387 | SERVICE_CERTIFICATE = 5;
388 | }
389 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
390 | 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
391 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
392 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
393 | optional bytes SessionKey = 4; // often RSA wrapped for licenses
394 | optional RemoteAttestation RemoteAttestation = 5;
395 | }
396 |
397 |
398 |
399 | // remove these when using it outside of protoc:
400 |
401 | // 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
402 | message SignedLicenseRequest {
403 | enum MessageType {
404 | LICENSE_REQUEST = 1;
405 | LICENSE = 2;
406 | ERROR_RESPONSE = 3;
407 | SERVICE_CERTIFICATE_REQUEST = 4;
408 | SERVICE_CERTIFICATE = 5;
409 | }
410 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
411 | 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
412 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
413 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
414 | optional bytes SessionKey = 4; // often RSA wrapped for licenses
415 | optional RemoteAttestation RemoteAttestation = 5;
416 | }
417 |
418 | // hack
419 | message SignedLicenseRequestRaw {
420 | enum MessageType {
421 | LICENSE_REQUEST = 1;
422 | LICENSE = 2;
423 | ERROR_RESPONSE = 3;
424 | SERVICE_CERTIFICATE_REQUEST = 4;
425 | SERVICE_CERTIFICATE = 5;
426 | }
427 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
428 | 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
429 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
430 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
431 | optional bytes SessionKey = 4; // often RSA wrapped for licenses
432 | optional RemoteAttestation RemoteAttestation = 5;
433 | }
434 |
435 |
436 | message SignedLicense {
437 | enum MessageType {
438 | LICENSE_REQUEST = 1;
439 | LICENSE = 2;
440 | ERROR_RESPONSE = 3;
441 | SERVICE_CERTIFICATE_REQUEST = 4;
442 | SERVICE_CERTIFICATE = 5;
443 | }
444 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
445 | 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
446 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
447 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
448 | optional bytes SessionKey = 4; // often RSA wrapped for licenses
449 | optional RemoteAttestation RemoteAttestation = 5;
450 | }
451 |
452 | message SignedServiceCertificate {
453 | enum MessageType {
454 | LICENSE_REQUEST = 1;
455 | LICENSE = 2;
456 | ERROR_RESPONSE = 3;
457 | SERVICE_CERTIFICATE_REQUEST = 4;
458 | SERVICE_CERTIFICATE = 5;
459 | }
460 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
461 | 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
462 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
463 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
464 | optional bytes SessionKey = 4; // often RSA wrapped for licenses
465 | optional RemoteAttestation RemoteAttestation = 5;
466 | }
467 |
468 | //vmp support
469 | message FileHashes {
470 | message Signature {
471 | optional string filename = 1;
472 | optional bool test_signing = 2; //0 - release, 1 - testing
473 | optional bytes SHA512Hash = 3;
474 | optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file
475 | optional bytes signature = 5;
476 | }
477 | optional bytes signer = 1;
478 | repeated Signature signatures = 2;
479 | }
480 |
481 |
--------------------------------------------------------------------------------
/proto_compiler/include/google/protobuf/descriptor.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | // Author: kenton@google.com (Kenton Varda)
32 | // Based on original Protocol Buffers design by
33 | // Sanjay Ghemawat, Jeff Dean, and others.
34 | //
35 | // The messages in this file describe the definitions found in .proto files.
36 | // A valid .proto file can be translated directly to a FileDescriptorProto
37 | // without any other information (e.g. without reading its imports).
38 |
39 |
40 | syntax = "proto2";
41 |
42 | package google.protobuf;
43 |
44 | option go_package = "google.golang.org/protobuf/types/descriptorpb";
45 | option java_package = "com.google.protobuf";
46 | option java_outer_classname = "DescriptorProtos";
47 | option csharp_namespace = "Google.Protobuf.Reflection";
48 | option objc_class_prefix = "GPB";
49 | option cc_enable_arenas = true;
50 |
51 | // descriptor.proto must be optimized for speed because reflection-based
52 | // algorithms don't work during bootstrapping.
53 | option optimize_for = SPEED;
54 |
55 | // The protocol compiler can output a FileDescriptorSet containing the .proto
56 | // files it parses.
57 | message FileDescriptorSet {
58 | repeated FileDescriptorProto file = 1;
59 | }
60 |
61 | // Describes a complete .proto file.
62 | message FileDescriptorProto {
63 | optional string name = 1; // file name, relative to root of source tree
64 | optional string package = 2; // e.g. "foo", "foo.bar", etc.
65 |
66 | // Names of files imported by this file.
67 | repeated string dependency = 3;
68 | // Indexes of the public imported files in the dependency list above.
69 | repeated int32 public_dependency = 10;
70 | // Indexes of the weak imported files in the dependency list.
71 | // For Google-internal migration only. Do not use.
72 | repeated int32 weak_dependency = 11;
73 |
74 | // All top-level definitions in this file.
75 | repeated DescriptorProto message_type = 4;
76 | repeated EnumDescriptorProto enum_type = 5;
77 | repeated ServiceDescriptorProto service = 6;
78 | repeated FieldDescriptorProto extension = 7;
79 |
80 | optional FileOptions options = 8;
81 |
82 | // This field contains optional information about the original source code.
83 | // You may safely remove this entire field without harming runtime
84 | // functionality of the descriptors -- the information is needed only by
85 | // development tools.
86 | optional SourceCodeInfo source_code_info = 9;
87 |
88 | // The syntax of the proto file.
89 | // The supported values are "proto2" and "proto3".
90 | optional string syntax = 12;
91 | }
92 |
93 | // Describes a message type.
94 | message DescriptorProto {
95 | optional string name = 1;
96 |
97 | repeated FieldDescriptorProto field = 2;
98 | repeated FieldDescriptorProto extension = 6;
99 |
100 | repeated DescriptorProto nested_type = 3;
101 | repeated EnumDescriptorProto enum_type = 4;
102 |
103 | message ExtensionRange {
104 | optional int32 start = 1; // Inclusive.
105 | optional int32 end = 2; // Exclusive.
106 |
107 | optional ExtensionRangeOptions options = 3;
108 | }
109 | repeated ExtensionRange extension_range = 5;
110 |
111 | repeated OneofDescriptorProto oneof_decl = 8;
112 |
113 | optional MessageOptions options = 7;
114 |
115 | // Range of reserved tag numbers. Reserved tag numbers may not be used by
116 | // fields or extension ranges in the same message. Reserved ranges may
117 | // not overlap.
118 | message ReservedRange {
119 | optional int32 start = 1; // Inclusive.
120 | optional int32 end = 2; // Exclusive.
121 | }
122 | repeated ReservedRange reserved_range = 9;
123 | // Reserved field names, which may not be used by fields in the same message.
124 | // A given name may only be reserved once.
125 | repeated string reserved_name = 10;
126 | }
127 |
128 | message ExtensionRangeOptions {
129 | // The parser stores options it doesn't recognize here. See above.
130 | repeated UninterpretedOption uninterpreted_option = 999;
131 |
132 |
133 | // Clients can define custom options in extensions of this message. See above.
134 | extensions 1000 to max;
135 | }
136 |
137 | // Describes a field within a message.
138 | message FieldDescriptorProto {
139 | enum Type {
140 | // 0 is reserved for errors.
141 | // Order is weird for historical reasons.
142 | TYPE_DOUBLE = 1;
143 | TYPE_FLOAT = 2;
144 | // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
145 | // negative values are likely.
146 | TYPE_INT64 = 3;
147 | TYPE_UINT64 = 4;
148 | // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
149 | // negative values are likely.
150 | TYPE_INT32 = 5;
151 | TYPE_FIXED64 = 6;
152 | TYPE_FIXED32 = 7;
153 | TYPE_BOOL = 8;
154 | TYPE_STRING = 9;
155 | // Tag-delimited aggregate.
156 | // Group type is deprecated and not supported in proto3. However, Proto3
157 | // implementations should still be able to parse the group wire format and
158 | // treat group fields as unknown fields.
159 | TYPE_GROUP = 10;
160 | TYPE_MESSAGE = 11; // Length-delimited aggregate.
161 |
162 | // New in version 2.
163 | TYPE_BYTES = 12;
164 | TYPE_UINT32 = 13;
165 | TYPE_ENUM = 14;
166 | TYPE_SFIXED32 = 15;
167 | TYPE_SFIXED64 = 16;
168 | TYPE_SINT32 = 17; // Uses ZigZag encoding.
169 | TYPE_SINT64 = 18; // Uses ZigZag encoding.
170 | }
171 |
172 | enum Label {
173 | // 0 is reserved for errors
174 | LABEL_OPTIONAL = 1;
175 | LABEL_REQUIRED = 2;
176 | LABEL_REPEATED = 3;
177 | }
178 |
179 | optional string name = 1;
180 | optional int32 number = 3;
181 | optional Label label = 4;
182 |
183 | // If type_name is set, this need not be set. If both this and type_name
184 | // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
185 | optional Type type = 5;
186 |
187 | // For message and enum types, this is the name of the type. If the name
188 | // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
189 | // rules are used to find the type (i.e. first the nested types within this
190 | // message are searched, then within the parent, on up to the root
191 | // namespace).
192 | optional string type_name = 6;
193 |
194 | // For extensions, this is the name of the type being extended. It is
195 | // resolved in the same manner as type_name.
196 | optional string extendee = 2;
197 |
198 | // For numeric types, contains the original text representation of the value.
199 | // For booleans, "true" or "false".
200 | // For strings, contains the default text contents (not escaped in any way).
201 | // For bytes, contains the C escaped value. All bytes >= 128 are escaped.
202 | // TODO(kenton): Base-64 encode?
203 | optional string default_value = 7;
204 |
205 | // If set, gives the index of a oneof in the containing type's oneof_decl
206 | // list. This field is a member of that oneof.
207 | optional int32 oneof_index = 9;
208 |
209 | // JSON name of this field. The value is set by protocol compiler. If the
210 | // user has set a "json_name" option on this field, that option's value
211 | // will be used. Otherwise, it's deduced from the field's name by converting
212 | // it to camelCase.
213 | optional string json_name = 10;
214 |
215 | optional FieldOptions options = 8;
216 |
217 | // If true, this is a proto3 "optional". When a proto3 field is optional, it
218 | // tracks presence regardless of field type.
219 | //
220 | // When proto3_optional is true, this field must be belong to a oneof to
221 | // signal to old proto3 clients that presence is tracked for this field. This
222 | // oneof is known as a "synthetic" oneof, and this field must be its sole
223 | // member (each proto3 optional field gets its own synthetic oneof). Synthetic
224 | // oneofs exist in the descriptor only, and do not generate any API. Synthetic
225 | // oneofs must be ordered after all "real" oneofs.
226 | //
227 | // For message fields, proto3_optional doesn't create any semantic change,
228 | // since non-repeated message fields always track presence. However it still
229 | // indicates the semantic detail of whether the user wrote "optional" or not.
230 | // This can be useful for round-tripping the .proto file. For consistency we
231 | // give message fields a synthetic oneof also, even though it is not required
232 | // to track presence. This is especially important because the parser can't
233 | // tell if a field is a message or an enum, so it must always create a
234 | // synthetic oneof.
235 | //
236 | // Proto2 optional fields do not set this flag, because they already indicate
237 | // optional with `LABEL_OPTIONAL`.
238 | optional bool proto3_optional = 17;
239 | }
240 |
241 | // Describes a oneof.
242 | message OneofDescriptorProto {
243 | optional string name = 1;
244 | optional OneofOptions options = 2;
245 | }
246 |
247 | // Describes an enum type.
248 | message EnumDescriptorProto {
249 | optional string name = 1;
250 |
251 | repeated EnumValueDescriptorProto value = 2;
252 |
253 | optional EnumOptions options = 3;
254 |
255 | // Range of reserved numeric values. Reserved values may not be used by
256 | // entries in the same enum. Reserved ranges may not overlap.
257 | //
258 | // Note that this is distinct from DescriptorProto.ReservedRange in that it
259 | // is inclusive such that it can appropriately represent the entire int32
260 | // domain.
261 | message EnumReservedRange {
262 | optional int32 start = 1; // Inclusive.
263 | optional int32 end = 2; // Inclusive.
264 | }
265 |
266 | // Range of reserved numeric values. Reserved numeric values may not be used
267 | // by enum values in the same enum declaration. Reserved ranges may not
268 | // overlap.
269 | repeated EnumReservedRange reserved_range = 4;
270 |
271 | // Reserved enum value names, which may not be reused. A given name may only
272 | // be reserved once.
273 | repeated string reserved_name = 5;
274 | }
275 |
276 | // Describes a value within an enum.
277 | message EnumValueDescriptorProto {
278 | optional string name = 1;
279 | optional int32 number = 2;
280 |
281 | optional EnumValueOptions options = 3;
282 | }
283 |
284 | // Describes a service.
285 | message ServiceDescriptorProto {
286 | optional string name = 1;
287 | repeated MethodDescriptorProto method = 2;
288 |
289 | optional ServiceOptions options = 3;
290 | }
291 |
292 | // Describes a method of a service.
293 | message MethodDescriptorProto {
294 | optional string name = 1;
295 |
296 | // Input and output type names. These are resolved in the same way as
297 | // FieldDescriptorProto.type_name, but must refer to a message type.
298 | optional string input_type = 2;
299 | optional string output_type = 3;
300 |
301 | optional MethodOptions options = 4;
302 |
303 | // Identifies if client streams multiple client messages
304 | optional bool client_streaming = 5 [default = false];
305 | // Identifies if server streams multiple server messages
306 | optional bool server_streaming = 6 [default = false];
307 | }
308 |
309 |
310 | // ===================================================================
311 | // Options
312 |
313 | // Each of the definitions above may have "options" attached. These are
314 | // just annotations which may cause code to be generated slightly differently
315 | // or may contain hints for code that manipulates protocol messages.
316 | //
317 | // Clients may define custom options as extensions of the *Options messages.
318 | // These extensions may not yet be known at parsing time, so the parser cannot
319 | // store the values in them. Instead it stores them in a field in the *Options
320 | // message called uninterpreted_option. This field must have the same name
321 | // across all *Options messages. We then use this field to populate the
322 | // extensions when we build a descriptor, at which point all protos have been
323 | // parsed and so all extensions are known.
324 | //
325 | // Extension numbers for custom options may be chosen as follows:
326 | // * For options which will only be used within a single application or
327 | // organization, or for experimental options, use field numbers 50000
328 | // through 99999. It is up to you to ensure that you do not use the
329 | // same number for multiple options.
330 | // * For options which will be published and used publicly by multiple
331 | // independent entities, e-mail protobuf-global-extension-registry@google.com
332 | // to reserve extension numbers. Simply provide your project name (e.g.
333 | // Objective-C plugin) and your project website (if available) -- there's no
334 | // need to explain how you intend to use them. Usually you only need one
335 | // extension number. You can declare multiple options with only one extension
336 | // number by putting them in a sub-message. See the Custom Options section of
337 | // the docs for examples:
338 | // https://developers.google.com/protocol-buffers/docs/proto#options
339 | // If this turns out to be popular, a web service will be set up
340 | // to automatically assign option numbers.
341 |
342 | message FileOptions {
343 |
344 | // Sets the Java package where classes generated from this .proto will be
345 | // placed. By default, the proto package is used, but this is often
346 | // inappropriate because proto packages do not normally start with backwards
347 | // domain names.
348 | optional string java_package = 1;
349 |
350 |
351 | // Controls the name of the wrapper Java class generated for the .proto file.
352 | // That class will always contain the .proto file's getDescriptor() method as
353 | // well as any top-level extensions defined in the .proto file.
354 | // If java_multiple_files is disabled, then all the other classes from the
355 | // .proto file will be nested inside the single wrapper outer class.
356 | optional string java_outer_classname = 8;
357 |
358 | // If enabled, then the Java code generator will generate a separate .java
359 | // file for each top-level message, enum, and service defined in the .proto
360 | // file. Thus, these types will *not* be nested inside the wrapper class
361 | // named by java_outer_classname. However, the wrapper class will still be
362 | // generated to contain the file's getDescriptor() method as well as any
363 | // top-level extensions defined in the file.
364 | optional bool java_multiple_files = 10 [default = false];
365 |
366 | // This option does nothing.
367 | optional bool java_generate_equals_and_hash = 20 [deprecated=true];
368 |
369 | // If set true, then the Java2 code generator will generate code that
370 | // throws an exception whenever an attempt is made to assign a non-UTF-8
371 | // byte sequence to a string field.
372 | // Message reflection will do the same.
373 | // However, an extension field still accepts non-UTF-8 byte sequences.
374 | // This option has no effect on when used with the lite runtime.
375 | optional bool java_string_check_utf8 = 27 [default = false];
376 |
377 |
378 | // Generated classes can be optimized for speed or code size.
379 | enum OptimizeMode {
380 | SPEED = 1; // Generate complete code for parsing, serialization,
381 | // etc.
382 | CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
383 | LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
384 | }
385 | optional OptimizeMode optimize_for = 9 [default = SPEED];
386 |
387 | // Sets the Go package where structs generated from this .proto will be
388 | // placed. If omitted, the Go package will be derived from the following:
389 | // - The basename of the package import path, if provided.
390 | // - Otherwise, the package statement in the .proto file, if present.
391 | // - Otherwise, the basename of the .proto file, without extension.
392 | optional string go_package = 11;
393 |
394 |
395 |
396 |
397 | // Should generic services be generated in each language? "Generic" services
398 | // are not specific to any particular RPC system. They are generated by the
399 | // main code generators in each language (without additional plugins).
400 | // Generic services were the only kind of service generation supported by
401 | // early versions of google.protobuf.
402 | //
403 | // Generic services are now considered deprecated in favor of using plugins
404 | // that generate code specific to your particular RPC system. Therefore,
405 | // these default to false. Old code which depends on generic services should
406 | // explicitly set them to true.
407 | optional bool cc_generic_services = 16 [default = false];
408 | optional bool java_generic_services = 17 [default = false];
409 | optional bool py_generic_services = 18 [default = false];
410 | optional bool php_generic_services = 42 [default = false];
411 |
412 | // Is this file deprecated?
413 | // Depending on the target platform, this can emit Deprecated annotations
414 | // for everything in the file, or it will be completely ignored; in the very
415 | // least, this is a formalization for deprecating files.
416 | optional bool deprecated = 23 [default = false];
417 |
418 | // Enables the use of arenas for the proto messages in this file. This applies
419 | // only to generated classes for C++.
420 | optional bool cc_enable_arenas = 31 [default = true];
421 |
422 |
423 | // Sets the objective c class prefix which is prepended to all objective c
424 | // generated classes from this .proto. There is no default.
425 | optional string objc_class_prefix = 36;
426 |
427 | // Namespace for generated classes; defaults to the package.
428 | optional string csharp_namespace = 37;
429 |
430 | // By default Swift generators will take the proto package and CamelCase it
431 | // replacing '.' with underscore and use that to prefix the types/symbols
432 | // defined. When this options is provided, they will use this value instead
433 | // to prefix the types/symbols defined.
434 | optional string swift_prefix = 39;
435 |
436 | // Sets the php class prefix which is prepended to all php generated classes
437 | // from this .proto. Default is empty.
438 | optional string php_class_prefix = 40;
439 |
440 | // Use this option to change the namespace of php generated classes. Default
441 | // is empty. When this option is empty, the package name will be used for
442 | // determining the namespace.
443 | optional string php_namespace = 41;
444 |
445 | // Use this option to change the namespace of php generated metadata classes.
446 | // Default is empty. When this option is empty, the proto file name will be
447 | // used for determining the namespace.
448 | optional string php_metadata_namespace = 44;
449 |
450 | // Use this option to change the package of ruby generated classes. Default
451 | // is empty. When this option is not set, the package name will be used for
452 | // determining the ruby package.
453 | optional string ruby_package = 45;
454 |
455 |
456 | // The parser stores options it doesn't recognize here.
457 | // See the documentation for the "Options" section above.
458 | repeated UninterpretedOption uninterpreted_option = 999;
459 |
460 | // Clients can define custom options in extensions of this message.
461 | // See the documentation for the "Options" section above.
462 | extensions 1000 to max;
463 |
464 | reserved 38;
465 | }
466 |
467 | message MessageOptions {
468 | // Set true to use the old proto1 MessageSet wire format for extensions.
469 | // This is provided for backwards-compatibility with the MessageSet wire
470 | // format. You should not use this for any other reason: It's less
471 | // efficient, has fewer features, and is more complicated.
472 | //
473 | // The message must be defined exactly as follows:
474 | // message Foo {
475 | // option message_set_wire_format = true;
476 | // extensions 4 to max;
477 | // }
478 | // Note that the message cannot have any defined fields; MessageSets only
479 | // have extensions.
480 | //
481 | // All extensions of your type must be singular messages; e.g. they cannot
482 | // be int32s, enums, or repeated messages.
483 | //
484 | // Because this is an option, the above two restrictions are not enforced by
485 | // the protocol compiler.
486 | optional bool message_set_wire_format = 1 [default = false];
487 |
488 | // Disables the generation of the standard "descriptor()" accessor, which can
489 | // conflict with a field of the same name. This is meant to make migration
490 | // from proto1 easier; new code should avoid fields named "descriptor".
491 | optional bool no_standard_descriptor_accessor = 2 [default = false];
492 |
493 | // Is this message deprecated?
494 | // Depending on the target platform, this can emit Deprecated annotations
495 | // for the message, or it will be completely ignored; in the very least,
496 | // this is a formalization for deprecating messages.
497 | optional bool deprecated = 3 [default = false];
498 |
499 | reserved 4, 5, 6;
500 |
501 | // Whether the message is an automatically generated map entry type for the
502 | // maps field.
503 | //
504 | // For maps fields:
505 | // map map_field = 1;
506 | // The parsed descriptor looks like:
507 | // message MapFieldEntry {
508 | // option map_entry = true;
509 | // optional KeyType key = 1;
510 | // optional ValueType value = 2;
511 | // }
512 | // repeated MapFieldEntry map_field = 1;
513 | //
514 | // Implementations may choose not to generate the map_entry=true message, but
515 | // use a native map in the target language to hold the keys and values.
516 | // The reflection APIs in such implementations still need to work as
517 | // if the field is a repeated message field.
518 | //
519 | // NOTE: Do not set the option in .proto files. Always use the maps syntax
520 | // instead. The option should only be implicitly set by the proto compiler
521 | // parser.
522 | optional bool map_entry = 7;
523 |
524 | reserved 8; // javalite_serializable
525 | reserved 9; // javanano_as_lite
526 |
527 |
528 | // The parser stores options it doesn't recognize here. See above.
529 | repeated UninterpretedOption uninterpreted_option = 999;
530 |
531 | // Clients can define custom options in extensions of this message. See above.
532 | extensions 1000 to max;
533 | }
534 |
535 | message FieldOptions {
536 | // The ctype option instructs the C++ code generator to use a different
537 | // representation of the field than it normally would. See the specific
538 | // options below. This option is not yet implemented in the open source
539 | // release -- sorry, we'll try to include it in a future version!
540 | optional CType ctype = 1 [default = STRING];
541 | enum CType {
542 | // Default mode.
543 | STRING = 0;
544 |
545 | CORD = 1;
546 |
547 | STRING_PIECE = 2;
548 | }
549 | // The packed option can be enabled for repeated primitive fields to enable
550 | // a more efficient representation on the wire. Rather than repeatedly
551 | // writing the tag and type for each element, the entire array is encoded as
552 | // a single length-delimited blob. In proto3, only explicit setting it to
553 | // false will avoid using packed encoding.
554 | optional bool packed = 2;
555 |
556 | // The jstype option determines the JavaScript type used for values of the
557 | // field. The option is permitted only for 64 bit integral and fixed types
558 | // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
559 | // is represented as JavaScript string, which avoids loss of precision that
560 | // can happen when a large value is converted to a floating point JavaScript.
561 | // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
562 | // use the JavaScript "number" type. The behavior of the default option
563 | // JS_NORMAL is implementation dependent.
564 | //
565 | // This option is an enum to permit additional types to be added, e.g.
566 | // goog.math.Integer.
567 | optional JSType jstype = 6 [default = JS_NORMAL];
568 | enum JSType {
569 | // Use the default type.
570 | JS_NORMAL = 0;
571 |
572 | // Use JavaScript strings.
573 | JS_STRING = 1;
574 |
575 | // Use JavaScript numbers.
576 | JS_NUMBER = 2;
577 | }
578 |
579 | // Should this field be parsed lazily? Lazy applies only to message-type
580 | // fields. It means that when the outer message is initially parsed, the
581 | // inner message's contents will not be parsed but instead stored in encoded
582 | // form. The inner message will actually be parsed when it is first accessed.
583 | //
584 | // This is only a hint. Implementations are free to choose whether to use
585 | // eager or lazy parsing regardless of the value of this option. However,
586 | // setting this option true suggests that the protocol author believes that
587 | // using lazy parsing on this field is worth the additional bookkeeping
588 | // overhead typically needed to implement it.
589 | //
590 | // This option does not affect the public interface of any generated code;
591 | // all method signatures remain the same. Furthermore, thread-safety of the
592 | // interface is not affected by this option; const methods remain safe to
593 | // call from multiple threads concurrently, while non-const methods continue
594 | // to require exclusive access.
595 | //
596 | //
597 | // Note that implementations may choose not to check required fields within
598 | // a lazy sub-message. That is, calling IsInitialized() on the outer message
599 | // may return true even if the inner message has missing required fields.
600 | // This is necessary because otherwise the inner message would have to be
601 | // parsed in order to perform the check, defeating the purpose of lazy
602 | // parsing. An implementation which chooses not to check required fields
603 | // must be consistent about it. That is, for any particular sub-message, the
604 | // implementation must either *always* check its required fields, or *never*
605 | // check its required fields, regardless of whether or not the message has
606 | // been parsed.
607 | optional bool lazy = 5 [default = false];
608 |
609 | // Is this field deprecated?
610 | // Depending on the target platform, this can emit Deprecated annotations
611 | // for accessors, or it will be completely ignored; in the very least, this
612 | // is a formalization for deprecating fields.
613 | optional bool deprecated = 3 [default = false];
614 |
615 | // For Google-internal migration only. Do not use.
616 | optional bool weak = 10 [default = false];
617 |
618 |
619 | // The parser stores options it doesn't recognize here. See above.
620 | repeated UninterpretedOption uninterpreted_option = 999;
621 |
622 | // Clients can define custom options in extensions of this message. See above.
623 | extensions 1000 to max;
624 |
625 | reserved 4; // removed jtype
626 | }
627 |
628 | message OneofOptions {
629 | // The parser stores options it doesn't recognize here. See above.
630 | repeated UninterpretedOption uninterpreted_option = 999;
631 |
632 | // Clients can define custom options in extensions of this message. See above.
633 | extensions 1000 to max;
634 | }
635 |
636 | message EnumOptions {
637 |
638 | // Set this option to true to allow mapping different tag names to the same
639 | // value.
640 | optional bool allow_alias = 2;
641 |
642 | // Is this enum deprecated?
643 | // Depending on the target platform, this can emit Deprecated annotations
644 | // for the enum, or it will be completely ignored; in the very least, this
645 | // is a formalization for deprecating enums.
646 | optional bool deprecated = 3 [default = false];
647 |
648 | reserved 5; // javanano_as_lite
649 |
650 | // The parser stores options it doesn't recognize here. See above.
651 | repeated UninterpretedOption uninterpreted_option = 999;
652 |
653 | // Clients can define custom options in extensions of this message. See above.
654 | extensions 1000 to max;
655 | }
656 |
657 | message EnumValueOptions {
658 | // Is this enum value deprecated?
659 | // Depending on the target platform, this can emit Deprecated annotations
660 | // for the enum value, or it will be completely ignored; in the very least,
661 | // this is a formalization for deprecating enum values.
662 | optional bool deprecated = 1 [default = false];
663 |
664 | // The parser stores options it doesn't recognize here. See above.
665 | repeated UninterpretedOption uninterpreted_option = 999;
666 |
667 | // Clients can define custom options in extensions of this message. See above.
668 | extensions 1000 to max;
669 | }
670 |
671 | message ServiceOptions {
672 |
673 | // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
674 | // framework. We apologize for hoarding these numbers to ourselves, but
675 | // we were already using them long before we decided to release Protocol
676 | // Buffers.
677 |
678 | // Is this service deprecated?
679 | // Depending on the target platform, this can emit Deprecated annotations
680 | // for the service, or it will be completely ignored; in the very least,
681 | // this is a formalization for deprecating services.
682 | optional bool deprecated = 33 [default = false];
683 |
684 | // The parser stores options it doesn't recognize here. See above.
685 | repeated UninterpretedOption uninterpreted_option = 999;
686 |
687 | // Clients can define custom options in extensions of this message. See above.
688 | extensions 1000 to max;
689 | }
690 |
691 | message MethodOptions {
692 |
693 | // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
694 | // framework. We apologize for hoarding these numbers to ourselves, but
695 | // we were already using them long before we decided to release Protocol
696 | // Buffers.
697 |
698 | // Is this method deprecated?
699 | // Depending on the target platform, this can emit Deprecated annotations
700 | // for the method, or it will be completely ignored; in the very least,
701 | // this is a formalization for deprecating methods.
702 | optional bool deprecated = 33 [default = false];
703 |
704 | // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
705 | // or neither? HTTP based RPC implementation may choose GET verb for safe
706 | // methods, and PUT verb for idempotent methods instead of the default POST.
707 | enum IdempotencyLevel {
708 | IDEMPOTENCY_UNKNOWN = 0;
709 | NO_SIDE_EFFECTS = 1; // implies idempotent
710 | IDEMPOTENT = 2; // idempotent, but may have side effects
711 | }
712 | optional IdempotencyLevel idempotency_level = 34
713 | [default = IDEMPOTENCY_UNKNOWN];
714 |
715 | // The parser stores options it doesn't recognize here. See above.
716 | repeated UninterpretedOption uninterpreted_option = 999;
717 |
718 | // Clients can define custom options in extensions of this message. See above.
719 | extensions 1000 to max;
720 | }
721 |
722 |
723 | // A message representing a option the parser does not recognize. This only
724 | // appears in options protos created by the compiler::Parser class.
725 | // DescriptorPool resolves these when building Descriptor objects. Therefore,
726 | // options protos in descriptor objects (e.g. returned by Descriptor::options(),
727 | // or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
728 | // in them.
729 | message UninterpretedOption {
730 | // The name of the uninterpreted option. Each string represents a segment in
731 | // a dot-separated name. is_extension is true iff a segment represents an
732 | // extension (denoted with parentheses in options specs in .proto files).
733 | // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
734 | // "foo.(bar.baz).qux".
735 | message NamePart {
736 | required string name_part = 1;
737 | required bool is_extension = 2;
738 | }
739 | repeated NamePart name = 2;
740 |
741 | // The value of the uninterpreted option, in whatever type the tokenizer
742 | // identified it as during parsing. Exactly one of these should be set.
743 | optional string identifier_value = 3;
744 | optional uint64 positive_int_value = 4;
745 | optional int64 negative_int_value = 5;
746 | optional double double_value = 6;
747 | optional bytes string_value = 7;
748 | optional string aggregate_value = 8;
749 | }
750 |
751 | // ===================================================================
752 | // Optional source code info
753 |
754 | // Encapsulates information about the original source file from which a
755 | // FileDescriptorProto was generated.
756 | message SourceCodeInfo {
757 | // A Location identifies a piece of source code in a .proto file which
758 | // corresponds to a particular definition. This information is intended
759 | // to be useful to IDEs, code indexers, documentation generators, and similar
760 | // tools.
761 | //
762 | // For example, say we have a file like:
763 | // message Foo {
764 | // optional string foo = 1;
765 | // }
766 | // Let's look at just the field definition:
767 | // optional string foo = 1;
768 | // ^ ^^ ^^ ^ ^^^
769 | // a bc de f ghi
770 | // We have the following locations:
771 | // span path represents
772 | // [a,i) [ 4, 0, 2, 0 ] The whole field definition.
773 | // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
774 | // [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
775 | // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
776 | // [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
777 | //
778 | // Notes:
779 | // - A location may refer to a repeated field itself (i.e. not to any
780 | // particular index within it). This is used whenever a set of elements are
781 | // logically enclosed in a single code segment. For example, an entire
782 | // extend block (possibly containing multiple extension definitions) will
783 | // have an outer location whose path refers to the "extensions" repeated
784 | // field without an index.
785 | // - Multiple locations may have the same path. This happens when a single
786 | // logical declaration is spread out across multiple places. The most
787 | // obvious example is the "extend" block again -- there may be multiple
788 | // extend blocks in the same scope, each of which will have the same path.
789 | // - A location's span is not always a subset of its parent's span. For
790 | // example, the "extendee" of an extension declaration appears at the
791 | // beginning of the "extend" block and is shared by all extensions within
792 | // the block.
793 | // - Just because a location's span is a subset of some other location's span
794 | // does not mean that it is a descendant. For example, a "group" defines
795 | // both a type and a field in a single declaration. Thus, the locations
796 | // corresponding to the type and field and their components will overlap.
797 | // - Code which tries to interpret locations should probably be designed to
798 | // ignore those that it doesn't understand, as more types of locations could
799 | // be recorded in the future.
800 | repeated Location location = 1;
801 | message Location {
802 | // Identifies which part of the FileDescriptorProto was defined at this
803 | // location.
804 | //
805 | // Each element is a field number or an index. They form a path from
806 | // the root FileDescriptorProto to the place where the definition. For
807 | // example, this path:
808 | // [ 4, 3, 2, 7, 1 ]
809 | // refers to:
810 | // file.message_type(3) // 4, 3
811 | // .field(7) // 2, 7
812 | // .name() // 1
813 | // This is because FileDescriptorProto.message_type has field number 4:
814 | // repeated DescriptorProto message_type = 4;
815 | // and DescriptorProto.field has field number 2:
816 | // repeated FieldDescriptorProto field = 2;
817 | // and FieldDescriptorProto.name has field number 1:
818 | // optional string name = 1;
819 | //
820 | // Thus, the above path gives the location of a field name. If we removed
821 | // the last element:
822 | // [ 4, 3, 2, 7 ]
823 | // this path refers to the whole field declaration (from the beginning
824 | // of the label to the terminating semicolon).
825 | repeated int32 path = 1 [packed = true];
826 |
827 | // Always has exactly three or four elements: start line, start column,
828 | // end line (optional, otherwise assumed same as start line), end column.
829 | // These are packed into a single field for efficiency. Note that line
830 | // and column numbers are zero-based -- typically you will want to add
831 | // 1 to each before displaying to a user.
832 | repeated int32 span = 2 [packed = true];
833 |
834 | // If this SourceCodeInfo represents a complete declaration, these are any
835 | // comments appearing before and after the declaration which appear to be
836 | // attached to the declaration.
837 | //
838 | // A series of line comments appearing on consecutive lines, with no other
839 | // tokens appearing on those lines, will be treated as a single comment.
840 | //
841 | // leading_detached_comments will keep paragraphs of comments that appear
842 | // before (but not connected to) the current element. Each paragraph,
843 | // separated by empty lines, will be one comment element in the repeated
844 | // field.
845 | //
846 | // Only the comment content is provided; comment markers (e.g. //) are
847 | // stripped out. For block comments, leading whitespace and an asterisk
848 | // will be stripped from the beginning of each line other than the first.
849 | // Newlines are included in the output.
850 | //
851 | // Examples:
852 | //
853 | // optional int32 foo = 1; // Comment attached to foo.
854 | // // Comment attached to bar.
855 | // optional int32 bar = 2;
856 | //
857 | // optional string baz = 3;
858 | // // Comment attached to baz.
859 | // // Another line attached to baz.
860 | //
861 | // // Comment attached to qux.
862 | // //
863 | // // Another line attached to qux.
864 | // optional double qux = 4;
865 | //
866 | // // Detached comment for corge. This is not leading or trailing comments
867 | // // to qux or corge because there are blank lines separating it from
868 | // // both.
869 | //
870 | // // Detached comment for corge paragraph 2.
871 | //
872 | // optional string corge = 5;
873 | // /* Block comment attached
874 | // * to corge. Leading asterisks
875 | // * will be removed. */
876 | // /* Block comment attached to
877 | // * grault. */
878 | // optional int32 grault = 6;
879 | //
880 | // // ignored detached comments.
881 | optional string leading_comments = 3;
882 | optional string trailing_comments = 4;
883 | repeated string leading_detached_comments = 6;
884 | }
885 | }
886 |
887 | // Describes the relationship between generated code and its original source
888 | // file. A GeneratedCodeInfo message is associated with only one generated
889 | // source file, but may contain references to different source .proto files.
890 | message GeneratedCodeInfo {
891 | // An Annotation connects some span of text in generated code to an element
892 | // of its generating .proto file.
893 | repeated Annotation annotation = 1;
894 | message Annotation {
895 | // Identifies the element in the original source .proto file. This field
896 | // is formatted the same as SourceCodeInfo.Location.path.
897 | repeated int32 path = 1 [packed = true];
898 |
899 | // Identifies the filesystem path to the original source .proto.
900 | optional string source_file = 2;
901 |
902 | // Identifies the starting offset in bytes in the generated code
903 | // that relates to the identified object.
904 | optional int32 begin = 3;
905 |
906 | // Identifies the ending offset in bytes in the generated code that
907 | // relates to the identified offset. The end offset should be one past
908 | // the last relevant byte (so the length of the text = end - begin).
909 | optional int32 end = 4;
910 | }
911 | }
912 |
--------------------------------------------------------------------------------