azot 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{cli.cjs → cli/main.cjs} +207 -20
- package/dist/{chunk-4ZXWXL4G.js → lib/main.js} +7 -16
- package/package.json +12 -12
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -447
- package/dist/lib.js +0 -24
- /package/dist/{lib.cjs → lib/main.cjs} +0 -0
- /package/dist/{lib.d.cts → lib/main.d.cts} +0 -0
- /package/dist/{lib.d.ts → lib/main.d.ts} +0 -0
|
@@ -7820,24 +7820,25 @@ license.help = help2;
|
|
|
7820
7820
|
// package.json
|
|
7821
7821
|
var package_default = {
|
|
7822
7822
|
name: "azot",
|
|
7823
|
-
version: "0.7.
|
|
7823
|
+
version: "0.7.2",
|
|
7824
7824
|
description: "Swiss Army knife for pentesting DRMs",
|
|
7825
7825
|
type: "module",
|
|
7826
7826
|
files: [
|
|
7827
7827
|
"dist",
|
|
7828
7828
|
"scripts"
|
|
7829
7829
|
],
|
|
7830
|
-
main: "dist/lib.cjs",
|
|
7831
|
-
module: "dist/lib.js",
|
|
7830
|
+
main: "dist/lib/main.cjs",
|
|
7831
|
+
module: "dist/lib/main.js",
|
|
7832
7832
|
bin: {
|
|
7833
|
-
azot: "dist/cli.
|
|
7833
|
+
azot: "dist/cli/main.cjs"
|
|
7834
7834
|
},
|
|
7835
7835
|
scripts: {
|
|
7836
|
-
prepublishOnly: "npm run build:lib",
|
|
7836
|
+
prepublishOnly: "npm run build:lib && npm run build:cli",
|
|
7837
7837
|
dev: "wxt",
|
|
7838
7838
|
"dev:firefox": "wxt -b firefox",
|
|
7839
|
-
build: "npm run build:lib && npm run build:chrome",
|
|
7840
|
-
"build:lib": "tsup --
|
|
7839
|
+
build: "npm run build:lib && npm run build:cli && npm run build:chrome",
|
|
7840
|
+
"build:lib": "tsup --config tsup.lib.config.ts",
|
|
7841
|
+
"build:cli": "tsup --config tsup.cli.config.ts",
|
|
7841
7842
|
"build:chrome": "wxt build",
|
|
7842
7843
|
"build:firefox": "wxt build -b firefox",
|
|
7843
7844
|
zip: "wxt zip",
|
|
@@ -7879,12 +7880,12 @@ var package_default = {
|
|
|
7879
7880
|
exports: {
|
|
7880
7881
|
".": {
|
|
7881
7882
|
require: {
|
|
7882
|
-
types: "./dist/lib.d.cts",
|
|
7883
|
-
default: "./dist/lib.cjs"
|
|
7883
|
+
types: "./dist/lib/main.d.cts",
|
|
7884
|
+
default: "./dist/lib/main.cjs"
|
|
7884
7885
|
},
|
|
7885
7886
|
import: {
|
|
7886
|
-
types: "./dist/lib.d.ts",
|
|
7887
|
-
default: "./dist/lib.js"
|
|
7887
|
+
types: "./dist/lib/main.d.ts",
|
|
7888
|
+
default: "./dist/lib/main.js"
|
|
7888
7889
|
}
|
|
7889
7890
|
}
|
|
7890
7891
|
},
|
|
@@ -7892,7 +7893,6 @@ var package_default = {
|
|
|
7892
7893
|
node: "20 || 21 || 22 || 23"
|
|
7893
7894
|
},
|
|
7894
7895
|
dependencies: {
|
|
7895
|
-
"@fastify/autoload": "^6.0.2",
|
|
7896
7896
|
"@fastify/helmet": "^13.0.0",
|
|
7897
7897
|
"@fastify/rate-limit": "^10.2.1",
|
|
7898
7898
|
"@fastify/sensible": "^6.0.1",
|
|
@@ -7929,10 +7929,9 @@ var package_default = {
|
|
|
7929
7929
|
};
|
|
7930
7930
|
|
|
7931
7931
|
// src/cli/commands/serve/serve.ts
|
|
7932
|
-
var
|
|
7933
|
-
var
|
|
7932
|
+
var import_promises6 = require("fs/promises");
|
|
7933
|
+
var import_node_path5 = require("path");
|
|
7934
7934
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
7935
|
-
var import_autoload = __toESM(require("@fastify/autoload"), 1);
|
|
7936
7935
|
var import_rate_limit = __toESM(require("@fastify/rate-limit"), 1);
|
|
7937
7936
|
var import_helmet = __toESM(require("@fastify/helmet"), 1);
|
|
7938
7937
|
var import_sensible = __toESM(require("@fastify/sensible"), 1);
|
|
@@ -7958,6 +7957,8 @@ var help3 = () => {
|
|
|
7958
7957
|
|
|
7959
7958
|
// src/cli/commands/serve/state.ts
|
|
7960
7959
|
var import_promises4 = require("fs/promises");
|
|
7960
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
7961
|
+
var clients = /* @__PURE__ */ new Map();
|
|
7961
7962
|
var defaultConfig = {
|
|
7962
7963
|
host: "0.0.0.0",
|
|
7963
7964
|
port: 4e3,
|
|
@@ -7971,30 +7972,216 @@ var loadConfig = async (configPath) => {
|
|
|
7971
7972
|
Object.assign(config, data);
|
|
7972
7973
|
};
|
|
7973
7974
|
|
|
7975
|
+
// src/cli/commands/serve/api/session.ts
|
|
7976
|
+
var import_promises5 = require("fs/promises");
|
|
7977
|
+
var import_node_path4 = require("path");
|
|
7978
|
+
var import_typebox = require("@sinclair/typebox");
|
|
7979
|
+
var plugin = async (server) => {
|
|
7980
|
+
server.post(
|
|
7981
|
+
"/session",
|
|
7982
|
+
{
|
|
7983
|
+
schema: {
|
|
7984
|
+
body: import_typebox.Type.Object({
|
|
7985
|
+
client: import_typebox.Type.String()
|
|
7986
|
+
}),
|
|
7987
|
+
response: {
|
|
7988
|
+
200: import_typebox.Type.Object({
|
|
7989
|
+
id: import_typebox.Type.String()
|
|
7990
|
+
}),
|
|
7991
|
+
403: import_typebox.Type.Object({
|
|
7992
|
+
error: import_typebox.Type.String()
|
|
7993
|
+
})
|
|
7994
|
+
}
|
|
7995
|
+
}
|
|
7996
|
+
},
|
|
7997
|
+
async (request, reply) => {
|
|
7998
|
+
const secretKey = request.headers["x-secret-key"];
|
|
7999
|
+
const clientName = request.body.client;
|
|
8000
|
+
const user = config.users[secretKey];
|
|
8001
|
+
const clientPath = config.clients.find(
|
|
8002
|
+
(client2) => (0, import_node_path4.basename)(client2).includes(clientName)
|
|
8003
|
+
);
|
|
8004
|
+
const clientAllowed = user?.clients.includes(clientName);
|
|
8005
|
+
if (!clientPath || !clientAllowed) {
|
|
8006
|
+
return reply.forbidden(
|
|
8007
|
+
"Client is not found or you are not authorized to use it."
|
|
8008
|
+
);
|
|
8009
|
+
}
|
|
8010
|
+
if (!clients.has(clientName)) {
|
|
8011
|
+
const wvd = await (0, import_promises5.readFile)(clientPath);
|
|
8012
|
+
const client2 = await Client.fromPacked(wvd);
|
|
8013
|
+
clients.set(clientName, client2);
|
|
8014
|
+
}
|
|
8015
|
+
const session = new Session("temporary", clients.get(clientName));
|
|
8016
|
+
const sessionKey = `${secretKey}:${session.sessionId}`;
|
|
8017
|
+
sessions.set(sessionKey, session);
|
|
8018
|
+
return { id: session.sessionId };
|
|
8019
|
+
}
|
|
8020
|
+
);
|
|
8021
|
+
server.post(
|
|
8022
|
+
"/session/:id/generate-request",
|
|
8023
|
+
{
|
|
8024
|
+
schema: {
|
|
8025
|
+
params: import_typebox.Type.Object({
|
|
8026
|
+
id: import_typebox.Type.String()
|
|
8027
|
+
}),
|
|
8028
|
+
body: import_typebox.Type.Object({
|
|
8029
|
+
initDataType: import_typebox.Type.Optional(import_typebox.Type.String()),
|
|
8030
|
+
initData: import_typebox.Type.String()
|
|
8031
|
+
}),
|
|
8032
|
+
response: {
|
|
8033
|
+
200: import_typebox.Type.Object({
|
|
8034
|
+
licenseRequest: import_typebox.Type.String()
|
|
8035
|
+
}),
|
|
8036
|
+
400: import_typebox.Type.Object({
|
|
8037
|
+
error: import_typebox.Type.String()
|
|
8038
|
+
})
|
|
8039
|
+
}
|
|
8040
|
+
}
|
|
8041
|
+
},
|
|
8042
|
+
async (request, reply) => {
|
|
8043
|
+
const secretKey = request.headers["x-secret-key"];
|
|
8044
|
+
const sessionId = request.params.id;
|
|
8045
|
+
const sessionKey = `${secretKey}:${sessionId}`;
|
|
8046
|
+
const session = sessions.get(sessionKey);
|
|
8047
|
+
if (!session) {
|
|
8048
|
+
return reply.badGateway(
|
|
8049
|
+
"Session not found. Unable to generate request."
|
|
8050
|
+
);
|
|
8051
|
+
}
|
|
8052
|
+
const initDataType = request.body.initDataType || "cenc";
|
|
8053
|
+
const initData = Buffer.from(request.body.initData, "base64");
|
|
8054
|
+
session.generateRequest(initDataType, initData);
|
|
8055
|
+
const data = await session.waitForLicenseRequest();
|
|
8056
|
+
const licenseRequest = Buffer.from(data).toString("base64");
|
|
8057
|
+
return reply.send({ licenseRequest });
|
|
8058
|
+
}
|
|
8059
|
+
);
|
|
8060
|
+
server.post(
|
|
8061
|
+
"/session/:id/update",
|
|
8062
|
+
{
|
|
8063
|
+
schema: {
|
|
8064
|
+
params: import_typebox.Type.Object({
|
|
8065
|
+
id: import_typebox.Type.String()
|
|
8066
|
+
}),
|
|
8067
|
+
body: import_typebox.Type.Object({
|
|
8068
|
+
response: import_typebox.Type.String()
|
|
8069
|
+
}),
|
|
8070
|
+
response: {
|
|
8071
|
+
400: import_typebox.Type.Object({
|
|
8072
|
+
error: import_typebox.Type.String()
|
|
8073
|
+
})
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
8076
|
+
},
|
|
8077
|
+
async (request, reply) => {
|
|
8078
|
+
const secretKey = request.headers["x-secret-key"];
|
|
8079
|
+
const sessionId = request.params.id;
|
|
8080
|
+
const sessionKey = `${secretKey}:${sessionId}`;
|
|
8081
|
+
const session = sessions.get(sessionKey);
|
|
8082
|
+
if (!session) {
|
|
8083
|
+
return reply.badGateway("Session not found. Unable to update.");
|
|
8084
|
+
}
|
|
8085
|
+
const response = Buffer.from(request.body.response, "base64");
|
|
8086
|
+
await session.update(response);
|
|
8087
|
+
return reply.send();
|
|
8088
|
+
}
|
|
8089
|
+
);
|
|
8090
|
+
server.get(
|
|
8091
|
+
"/session/:id/keys",
|
|
8092
|
+
{
|
|
8093
|
+
schema: {
|
|
8094
|
+
params: import_typebox.Type.Object({
|
|
8095
|
+
id: import_typebox.Type.String()
|
|
8096
|
+
}),
|
|
8097
|
+
response: {
|
|
8098
|
+
200: import_typebox.Type.Array(
|
|
8099
|
+
import_typebox.Type.Object({
|
|
8100
|
+
kid: import_typebox.Type.String(),
|
|
8101
|
+
key: import_typebox.Type.String(),
|
|
8102
|
+
type: import_typebox.Type.String()
|
|
8103
|
+
})
|
|
8104
|
+
),
|
|
8105
|
+
400: import_typebox.Type.Object({
|
|
8106
|
+
error: import_typebox.Type.String()
|
|
8107
|
+
})
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
},
|
|
8111
|
+
async (request, reply) => {
|
|
8112
|
+
const secretKey = request.headers["x-secret-key"];
|
|
8113
|
+
const sessionId = request.params.id;
|
|
8114
|
+
const sessionKey = `${secretKey}:${sessionId}`;
|
|
8115
|
+
const session = sessions.get(sessionKey);
|
|
8116
|
+
if (!session) {
|
|
8117
|
+
return reply.badGateway("Session not found. Unable to get keys.");
|
|
8118
|
+
}
|
|
8119
|
+
const keys = [];
|
|
8120
|
+
for (const key of session.keys.values()) {
|
|
8121
|
+
keys.push({
|
|
8122
|
+
kid: key.id,
|
|
8123
|
+
key: key.value,
|
|
8124
|
+
type: key.type
|
|
8125
|
+
});
|
|
8126
|
+
}
|
|
8127
|
+
return reply.send(keys);
|
|
8128
|
+
}
|
|
8129
|
+
);
|
|
8130
|
+
server.delete(
|
|
8131
|
+
"/session/:id",
|
|
8132
|
+
{
|
|
8133
|
+
schema: {
|
|
8134
|
+
params: import_typebox.Type.Object({
|
|
8135
|
+
id: import_typebox.Type.String()
|
|
8136
|
+
}),
|
|
8137
|
+
response: {
|
|
8138
|
+
400: import_typebox.Type.Object({
|
|
8139
|
+
error: import_typebox.Type.String()
|
|
8140
|
+
})
|
|
8141
|
+
}
|
|
8142
|
+
}
|
|
8143
|
+
},
|
|
8144
|
+
async (request, reply) => {
|
|
8145
|
+
const secretKey = request.headers["x-secret-key"];
|
|
8146
|
+
const sessionId = request.params.id;
|
|
8147
|
+
const sessionKey = `${secretKey}:${sessionId}`;
|
|
8148
|
+
const session = sessions.get(sessionKey);
|
|
8149
|
+
if (!session) {
|
|
8150
|
+
return reply.badGateway(
|
|
8151
|
+
"No session has been opened yet. No session to close."
|
|
8152
|
+
);
|
|
8153
|
+
}
|
|
8154
|
+
await session.close();
|
|
8155
|
+
sessions.delete(sessionKey);
|
|
8156
|
+
return reply.send();
|
|
8157
|
+
}
|
|
8158
|
+
);
|
|
8159
|
+
};
|
|
8160
|
+
var session_default = plugin;
|
|
8161
|
+
|
|
7974
8162
|
// src/cli/commands/serve/serve.ts
|
|
7975
|
-
var import_meta = {};
|
|
7976
8163
|
var serve = async (options = {}) => {
|
|
7977
8164
|
const server = (0, import_fastify.default)({ logger: true });
|
|
7978
8165
|
const configPath = options.config || "azot.config.json";
|
|
7979
8166
|
await loadConfig(configPath);
|
|
7980
8167
|
if (options.client) config.clients.push(options.client);
|
|
7981
8168
|
if (!config.clients.length) {
|
|
7982
|
-
const files = await (0,
|
|
8169
|
+
const files = await (0, import_promises6.readdir)(process.cwd());
|
|
7983
8170
|
const clientPath = files.find((file) => file.endsWith(".wvd"));
|
|
7984
8171
|
if (clientPath) config.clients.push(clientPath);
|
|
7985
8172
|
}
|
|
7986
8173
|
if (options.secret) {
|
|
7987
8174
|
const anonymousUser = { name: "anonymous", clients: [] };
|
|
7988
8175
|
const user = config.users[options.secret] || anonymousUser;
|
|
7989
|
-
const clientFilename = (0,
|
|
7990
|
-
const clientName = clientFilename.replace((0,
|
|
8176
|
+
const clientFilename = (0, import_node_path5.basename)(options.client || config.clients.at(-1));
|
|
8177
|
+
const clientName = clientFilename.replace((0, import_node_path5.extname)(clientFilename), "");
|
|
7991
8178
|
if (!user.clients.length) user.clients.push(clientName);
|
|
7992
8179
|
config.users[options.secret] = user;
|
|
7993
8180
|
}
|
|
7994
|
-
server.register(import_autoload.default, { dir: `${import_meta.dirname || __dirname}/api` });
|
|
7995
8181
|
server.register(import_rate_limit.default, { max: 100, timeWindow: "1 minute" });
|
|
7996
8182
|
server.register(import_helmet.default, { global: true });
|
|
7997
8183
|
server.register(import_sensible.default);
|
|
8184
|
+
session_default(server, {});
|
|
7998
8185
|
server.listen({ port: options.port || 4e3, host: options.host || "0.0.0.0" }).catch((err) => {
|
|
7999
8186
|
server.log.error(err);
|
|
8000
8187
|
process.exit(1);
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// node_modules/tsup/assets/esm_shims.js
|
|
2
|
-
import { fileURLToPath } from "url";
|
|
3
|
-
import path from "path";
|
|
4
|
-
var getFilename = () => fileURLToPath(import.meta.url);
|
|
5
|
-
var getDirname = () => path.dirname(getFilename());
|
|
6
|
-
var __dirname = /* @__PURE__ */ getDirname();
|
|
7
|
-
|
|
8
1
|
// src/lib/proto/license_protocol.pb.js
|
|
9
2
|
import * as protobufjs from "protobufjs/minimal.js";
|
|
10
3
|
var $protobuf = protobufjs.default || protobufjs;
|
|
@@ -7676,17 +7669,15 @@ var fetchDecryptionKeys = async (params) => {
|
|
|
7676
7669
|
const keys = Array.from(session.keys.values());
|
|
7677
7670
|
return keys;
|
|
7678
7671
|
};
|
|
7679
|
-
|
|
7680
7672
|
export {
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7673
|
+
CLIENT_TYPE,
|
|
7674
|
+
Client,
|
|
7675
|
+
Key,
|
|
7676
|
+
Session,
|
|
7677
|
+
fetchDecryptionKeys,
|
|
7684
7678
|
fromBase64,
|
|
7679
|
+
fromBinary,
|
|
7685
7680
|
fromBuffer,
|
|
7686
7681
|
fromHex,
|
|
7687
|
-
|
|
7688
|
-
Session,
|
|
7689
|
-
CLIENT_TYPE,
|
|
7690
|
-
Client,
|
|
7691
|
-
fetchDecryptionKeys
|
|
7682
|
+
fromText
|
|
7692
7683
|
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azot",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Swiss Army knife for pentesting DRMs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
8
8
|
"scripts"
|
|
9
9
|
],
|
|
10
|
-
"main": "dist/lib.cjs",
|
|
11
|
-
"module": "dist/lib.js",
|
|
10
|
+
"main": "dist/lib/main.cjs",
|
|
11
|
+
"module": "dist/lib/main.js",
|
|
12
12
|
"bin": {
|
|
13
|
-
"azot": "dist/cli.
|
|
13
|
+
"azot": "dist/cli/main.cjs"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"prepublishOnly": "npm run build:lib",
|
|
16
|
+
"prepublishOnly": "npm run build:lib && npm run build:cli",
|
|
17
17
|
"dev": "wxt",
|
|
18
18
|
"dev:firefox": "wxt -b firefox",
|
|
19
|
-
"build": "npm run build:lib && npm run build:chrome",
|
|
20
|
-
"build:lib": "tsup --
|
|
19
|
+
"build": "npm run build:lib && npm run build:cli && npm run build:chrome",
|
|
20
|
+
"build:lib": "tsup --config tsup.lib.config.ts",
|
|
21
|
+
"build:cli": "tsup --config tsup.cli.config.ts",
|
|
21
22
|
"build:chrome": "wxt build",
|
|
22
23
|
"build:firefox": "wxt build -b firefox",
|
|
23
24
|
"zip": "wxt zip",
|
|
@@ -59,12 +60,12 @@
|
|
|
59
60
|
"exports": {
|
|
60
61
|
".": {
|
|
61
62
|
"require": {
|
|
62
|
-
"types": "./dist/lib.d.cts",
|
|
63
|
-
"default": "./dist/lib.cjs"
|
|
63
|
+
"types": "./dist/lib/main.d.cts",
|
|
64
|
+
"default": "./dist/lib/main.cjs"
|
|
64
65
|
},
|
|
65
66
|
"import": {
|
|
66
|
-
"types": "./dist/lib.d.ts",
|
|
67
|
-
"default": "./dist/lib.js"
|
|
67
|
+
"types": "./dist/lib/main.d.ts",
|
|
68
|
+
"default": "./dist/lib/main.js"
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
},
|
|
@@ -72,7 +73,6 @@
|
|
|
72
73
|
"node": "20 || 21 || 22 || 23"
|
|
73
74
|
},
|
|
74
75
|
"dependencies": {
|
|
75
|
-
"@fastify/autoload": "^6.0.2",
|
|
76
76
|
"@fastify/helmet": "^13.0.0",
|
|
77
77
|
"@fastify/rate-limit": "^10.2.1",
|
|
78
78
|
"@fastify/sensible": "^6.0.1",
|
package/dist/cli.d.cts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
package/dist/cli.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
package/dist/cli.js
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
Client,
|
|
4
|
-
__dirname,
|
|
5
|
-
fetchDecryptionKeys
|
|
6
|
-
} from "./chunk-4ZXWXL4G.js";
|
|
7
|
-
|
|
8
|
-
// src/cli/main.ts
|
|
9
|
-
import { parseArgs } from "node:util";
|
|
10
|
-
|
|
11
|
-
// src/cli/utils.ts
|
|
12
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
var importClient = async (input) => {
|
|
15
|
-
const inputStat = await stat(input);
|
|
16
|
-
const isDir = inputStat.isDirectory();
|
|
17
|
-
if (isDir) {
|
|
18
|
-
const entries = isDir ? await readdir(input) : [];
|
|
19
|
-
const idFilename = entries.find((entry) => entry.includes("client_id"));
|
|
20
|
-
const keyFilename = entries.find((entry) => entry.includes("private_key"));
|
|
21
|
-
const wvdFilename = entries.find((entry) => entry.endsWith("wvd"));
|
|
22
|
-
const isUnpacked = !!(idFilename && keyFilename);
|
|
23
|
-
const isPacked = !!wvdFilename;
|
|
24
|
-
if (isUnpacked) {
|
|
25
|
-
const idPath = join(input, idFilename);
|
|
26
|
-
const keyPath = join(input, keyFilename);
|
|
27
|
-
const id = await readFile(idPath);
|
|
28
|
-
const key = await readFile(keyPath);
|
|
29
|
-
return Client.fromUnpacked(id, key);
|
|
30
|
-
} else if (isPacked) {
|
|
31
|
-
const wvdPath = join(input, wvdFilename);
|
|
32
|
-
const wvd = await readFile(wvdPath);
|
|
33
|
-
return await Client.fromPacked(wvd);
|
|
34
|
-
} else {
|
|
35
|
-
console.log(`Unable to find client files in ${input}`);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
} else if (input.endsWith(".wvd")) {
|
|
39
|
-
const wvd = await readFile(input);
|
|
40
|
-
return Client.fromPacked(wvd);
|
|
41
|
-
} else {
|
|
42
|
-
console.log(`Unable to find client files in ${input}`);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
var col = (str, offset = 2, width = 30) => `${" ".repeat(offset)}${str.padEnd(width)}`;
|
|
47
|
-
|
|
48
|
-
// src/cli/commands/client/help.ts
|
|
49
|
-
var help = () => {
|
|
50
|
-
console.log(`azot client: Widevine client utilities
|
|
51
|
-
`);
|
|
52
|
-
console.log(`Usage: azot client <subcommand> [...flags]
|
|
53
|
-
`);
|
|
54
|
-
console.log(`Commands:`);
|
|
55
|
-
console.log(
|
|
56
|
-
col(`info <input>`) + "print info about client that is on <input> path (*.wvd filepath or directory with id and private key)"
|
|
57
|
-
);
|
|
58
|
-
console.log(
|
|
59
|
-
col(`pack <input> <output>`) + "pack client id and private key from <input> directory into single *.wvd file with <output> path"
|
|
60
|
-
);
|
|
61
|
-
console.log(
|
|
62
|
-
col(`unpack <input> <output>`) + "unpack *.wvd from <input> path to separate client id and private key placed in <output> directory"
|
|
63
|
-
);
|
|
64
|
-
console.log("");
|
|
65
|
-
console.log(`Flags:`);
|
|
66
|
-
console.log(col(`-h, --help`) + "Display this menu and exit");
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// src/cli/commands/client/info.ts
|
|
70
|
-
var info = async (input) => {
|
|
71
|
-
const client2 = await importClient(input);
|
|
72
|
-
for (const [key, value] of client2.info.entries()) {
|
|
73
|
-
console.log(`${key}: ${value}`);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// src/cli/commands/client/pack.ts
|
|
78
|
-
import { writeFile } from "node:fs/promises";
|
|
79
|
-
import { join as join2 } from "node:path";
|
|
80
|
-
var pack = async (input = process.cwd(), output) => {
|
|
81
|
-
const client2 = await importClient(input);
|
|
82
|
-
const wvd = await client2.pack();
|
|
83
|
-
const wvdName = `${client2.info.get("company_name")}_${client2.info.get("model_name")}`;
|
|
84
|
-
const wvdOutput = output || join2(process.cwd(), `${wvdName}.wvd`);
|
|
85
|
-
await writeFile(wvdOutput, wvd);
|
|
86
|
-
console.log(`Client packed: ${wvdOutput}`);
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// src/cli/commands/client/unpack.ts
|
|
90
|
-
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
91
|
-
import { join as join3 } from "node:path";
|
|
92
|
-
var unpack = async (input = process.cwd(), output) => {
|
|
93
|
-
const client2 = await importClient(input);
|
|
94
|
-
const [id, key] = await client2.unpack();
|
|
95
|
-
const idOutput = join3(output || process.cwd(), `device_client_id_blob`);
|
|
96
|
-
const keyOutput = join3(output || process.cwd(), `device_private_key`);
|
|
97
|
-
await writeFile2(idOutput, id);
|
|
98
|
-
await writeFile2(keyOutput, key);
|
|
99
|
-
console.log(`Client unpacked: ${idOutput}, ${keyOutput}`);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// src/cli/commands/client/index.ts
|
|
103
|
-
var client = { info, pack, unpack, help };
|
|
104
|
-
|
|
105
|
-
// src/cli/commands/license/help.ts
|
|
106
|
-
var help2 = () => {
|
|
107
|
-
console.log(`azot license: Make license request
|
|
108
|
-
`);
|
|
109
|
-
console.log(`Usage: azot license <url> [...flags]
|
|
110
|
-
`);
|
|
111
|
-
console.log(`Commands:`);
|
|
112
|
-
console.log(
|
|
113
|
-
col(`<url>`) + "URL of license server (e.g. https://cwip-shaka-proxy.appspot.com/no_auth)"
|
|
114
|
-
);
|
|
115
|
-
console.log("");
|
|
116
|
-
console.log(`Flags:`);
|
|
117
|
-
console.log(
|
|
118
|
-
col(`-H, --header`) + 'headers to send with license request, compatible with curl (e.g. -H "Authorization: Bearer ...")'
|
|
119
|
-
);
|
|
120
|
-
console.log(col(`-p, --pssh`) + "widevine PSSH data in Base64");
|
|
121
|
-
console.log(
|
|
122
|
-
col(`-c, --client`) + "path to client (directory with id and private key or path to *.wvd file)"
|
|
123
|
-
);
|
|
124
|
-
console.log(
|
|
125
|
-
col(`-e, --encrypt`) + "enable client encryption with service certificate, disabled by default"
|
|
126
|
-
);
|
|
127
|
-
console.log(col(`-h, --help`) + "display this menu and exit");
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// src/cli/commands/license/license.ts
|
|
131
|
-
var license = async (params) => {
|
|
132
|
-
const headers = Object.fromEntries(
|
|
133
|
-
params.headers?.map((header) => header.split(":").map((s) => s.trim())) || []
|
|
134
|
-
);
|
|
135
|
-
const keys = await fetchDecryptionKeys({
|
|
136
|
-
server: params.url,
|
|
137
|
-
pssh: params.pssh,
|
|
138
|
-
client: await importClient(params.clientPath || process.cwd()),
|
|
139
|
-
headers
|
|
140
|
-
});
|
|
141
|
-
for (const key of keys) {
|
|
142
|
-
console.log(`${key.id}:${key.value}`);
|
|
143
|
-
}
|
|
144
|
-
return keys;
|
|
145
|
-
};
|
|
146
|
-
license.help = help2;
|
|
147
|
-
|
|
148
|
-
// package.json
|
|
149
|
-
var package_default = {
|
|
150
|
-
name: "azot",
|
|
151
|
-
version: "0.7.0",
|
|
152
|
-
description: "Swiss Army knife for pentesting DRMs",
|
|
153
|
-
type: "module",
|
|
154
|
-
files: [
|
|
155
|
-
"dist",
|
|
156
|
-
"scripts"
|
|
157
|
-
],
|
|
158
|
-
main: "dist/lib.cjs",
|
|
159
|
-
module: "dist/lib.js",
|
|
160
|
-
bin: {
|
|
161
|
-
azot: "dist/cli.js"
|
|
162
|
-
},
|
|
163
|
-
scripts: {
|
|
164
|
-
prepublishOnly: "npm run build:lib",
|
|
165
|
-
dev: "wxt",
|
|
166
|
-
"dev:firefox": "wxt -b firefox",
|
|
167
|
-
build: "npm run build:lib && npm run build:chrome",
|
|
168
|
-
"build:lib": "tsup --entry.lib src/lib/main.ts --entry.cli src/cli/main.ts --format cjs,esm --shims --dts --clean",
|
|
169
|
-
"build:chrome": "wxt build",
|
|
170
|
-
"build:firefox": "wxt build -b firefox",
|
|
171
|
-
zip: "wxt zip",
|
|
172
|
-
"zip:firefox": "wxt zip -b firefox",
|
|
173
|
-
compile: "tsc --noEmit",
|
|
174
|
-
postinstall: "node scripts/postinstall.js",
|
|
175
|
-
test: "vitest"
|
|
176
|
-
},
|
|
177
|
-
repository: {
|
|
178
|
-
type: "git",
|
|
179
|
-
url: "https://github.com/vitalygashkov/azot"
|
|
180
|
-
},
|
|
181
|
-
keywords: [
|
|
182
|
-
"widevine",
|
|
183
|
-
"drm",
|
|
184
|
-
"google",
|
|
185
|
-
"pssh",
|
|
186
|
-
"eme",
|
|
187
|
-
"encrypted-media-extensions",
|
|
188
|
-
"media"
|
|
189
|
-
],
|
|
190
|
-
author: "Vitaly Gashkov <vitalygashkov@vk.com>",
|
|
191
|
-
license: "AGPL-3.0",
|
|
192
|
-
readmeFilename: "README.md",
|
|
193
|
-
bugs: {
|
|
194
|
-
url: "https://github.com/vitalygashkov/azot/issues",
|
|
195
|
-
email: "vitalygashkov@vk.com"
|
|
196
|
-
},
|
|
197
|
-
funding: [
|
|
198
|
-
{
|
|
199
|
-
type: "individual",
|
|
200
|
-
url: "https://boosty.to/vitalygashkov"
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
type: "patreon",
|
|
204
|
-
url: "https://www.patreon.com/vitalygashkov"
|
|
205
|
-
}
|
|
206
|
-
],
|
|
207
|
-
exports: {
|
|
208
|
-
".": {
|
|
209
|
-
require: {
|
|
210
|
-
types: "./dist/lib.d.cts",
|
|
211
|
-
default: "./dist/lib.cjs"
|
|
212
|
-
},
|
|
213
|
-
import: {
|
|
214
|
-
types: "./dist/lib.d.ts",
|
|
215
|
-
default: "./dist/lib.js"
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
engines: {
|
|
220
|
-
node: "20 || 21 || 22 || 23"
|
|
221
|
-
},
|
|
222
|
-
dependencies: {
|
|
223
|
-
"@fastify/autoload": "^6.0.2",
|
|
224
|
-
"@fastify/helmet": "^13.0.0",
|
|
225
|
-
"@fastify/rate-limit": "^10.2.1",
|
|
226
|
-
"@fastify/sensible": "^6.0.1",
|
|
227
|
-
"@fastify/type-provider-typebox": "^5.1.0",
|
|
228
|
-
"@sinclair/typebox": "^0.34.11",
|
|
229
|
-
fastify: "^5.1.0",
|
|
230
|
-
jsrsasign: "^11.1.0",
|
|
231
|
-
protobufjs: "^7.4.0"
|
|
232
|
-
},
|
|
233
|
-
devDependencies: {
|
|
234
|
-
"@eslint/js": "^9.12.0",
|
|
235
|
-
"@solidjs/router": "^0.14.10",
|
|
236
|
-
"@types/jsrsasign": "^10.5.14",
|
|
237
|
-
"@types/node": "^22.7.5",
|
|
238
|
-
"@wxt-dev/module-solid": "^1.1.2",
|
|
239
|
-
autoprefixer: "^10.4.17",
|
|
240
|
-
clsx: "^2.1.1",
|
|
241
|
-
eslint: "^9.12.0",
|
|
242
|
-
"eslint-config-prettier": "^9.1.0",
|
|
243
|
-
"eslint-plugin-prettier": "^5.2.1",
|
|
244
|
-
globals: "^15.11.0",
|
|
245
|
-
postcss: "^8.4.33",
|
|
246
|
-
"solid-icons": "^1.1.0",
|
|
247
|
-
"solid-js": "^1.9.1",
|
|
248
|
-
"tailwind-merge": "^2.5.4",
|
|
249
|
-
tailwindcss: "^3.4.1",
|
|
250
|
-
tsup: "^8.3.0",
|
|
251
|
-
typescript: "^5.6.2",
|
|
252
|
-
"typescript-eslint": "^8.9.0",
|
|
253
|
-
"vite-plugin-arraybuffer": "^0.0.8",
|
|
254
|
-
vitest: "^2.1.3",
|
|
255
|
-
wxt: "^0.19.11"
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// src/cli/commands/serve/serve.ts
|
|
260
|
-
import { readdir as readdir2 } from "node:fs/promises";
|
|
261
|
-
import { basename, extname } from "node:path";
|
|
262
|
-
import fastify from "fastify";
|
|
263
|
-
import AutoLoad from "@fastify/autoload";
|
|
264
|
-
import RateLimit from "@fastify/rate-limit";
|
|
265
|
-
import Helmet from "@fastify/helmet";
|
|
266
|
-
import Sensible from "@fastify/sensible";
|
|
267
|
-
|
|
268
|
-
// src/cli/commands/serve/help.ts
|
|
269
|
-
var help3 = () => {
|
|
270
|
-
console.log(`azot serve: Run your API instance
|
|
271
|
-
`);
|
|
272
|
-
console.log(`Usage: azot serve [...flags]
|
|
273
|
-
`);
|
|
274
|
-
console.log(`Flags:`);
|
|
275
|
-
console.log(col(`--host`) + "server host (default: 0.0.0.0)");
|
|
276
|
-
console.log(col(`--port`) + "server port (default: 4000)");
|
|
277
|
-
console.log(
|
|
278
|
-
col(`--config`) + "path to config file (default: azot.config.json)"
|
|
279
|
-
);
|
|
280
|
-
console.log(
|
|
281
|
-
col(`-c, --client`) + "path to client (directory with id and private key or path to *.wvd file)"
|
|
282
|
-
);
|
|
283
|
-
console.log(col(`-s, --secret`) + "secret key to access API endpoints");
|
|
284
|
-
console.log(col(`-h, --help`) + "display this menu and exit");
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
// src/cli/commands/serve/state.ts
|
|
288
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
289
|
-
var defaultConfig = {
|
|
290
|
-
host: "0.0.0.0",
|
|
291
|
-
port: 4e3,
|
|
292
|
-
clients: [],
|
|
293
|
-
users: {},
|
|
294
|
-
forcePrivacyMode: true
|
|
295
|
-
};
|
|
296
|
-
var config = defaultConfig;
|
|
297
|
-
var loadConfig = async (configPath) => {
|
|
298
|
-
const data = await readFile2(configPath, { encoding: "utf-8" }).then((data2) => JSON.parse(data2)).catch(() => defaultConfig);
|
|
299
|
-
Object.assign(config, data);
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// src/cli/commands/serve/serve.ts
|
|
303
|
-
var serve = async (options = {}) => {
|
|
304
|
-
const server = fastify({ logger: true });
|
|
305
|
-
const configPath = options.config || "azot.config.json";
|
|
306
|
-
await loadConfig(configPath);
|
|
307
|
-
if (options.client) config.clients.push(options.client);
|
|
308
|
-
if (!config.clients.length) {
|
|
309
|
-
const files = await readdir2(process.cwd());
|
|
310
|
-
const clientPath = files.find((file) => file.endsWith(".wvd"));
|
|
311
|
-
if (clientPath) config.clients.push(clientPath);
|
|
312
|
-
}
|
|
313
|
-
if (options.secret) {
|
|
314
|
-
const anonymousUser = { name: "anonymous", clients: [] };
|
|
315
|
-
const user = config.users[options.secret] || anonymousUser;
|
|
316
|
-
const clientFilename = basename(options.client || config.clients.at(-1));
|
|
317
|
-
const clientName = clientFilename.replace(extname(clientFilename), "");
|
|
318
|
-
if (!user.clients.length) user.clients.push(clientName);
|
|
319
|
-
config.users[options.secret] = user;
|
|
320
|
-
}
|
|
321
|
-
server.register(AutoLoad, { dir: `${import.meta.dirname || __dirname}/api` });
|
|
322
|
-
server.register(RateLimit, { max: 100, timeWindow: "1 minute" });
|
|
323
|
-
server.register(Helmet, { global: true });
|
|
324
|
-
server.register(Sensible);
|
|
325
|
-
server.listen({ port: options.port || 4e3, host: options.host || "0.0.0.0" }).catch((err) => {
|
|
326
|
-
server.log.error(err);
|
|
327
|
-
process.exit(1);
|
|
328
|
-
});
|
|
329
|
-
};
|
|
330
|
-
serve.help = help3;
|
|
331
|
-
|
|
332
|
-
// src/cli/main.ts
|
|
333
|
-
var args = parseArgs({
|
|
334
|
-
args: process.argv.slice(2),
|
|
335
|
-
options: {
|
|
336
|
-
pssh: { type: "string", short: "p" },
|
|
337
|
-
client: { type: "string", short: "c" },
|
|
338
|
-
encrypt: { type: "boolean", short: "e", default: false },
|
|
339
|
-
header: { type: "string", short: "H", multiple: true },
|
|
340
|
-
help: { type: "boolean", short: "h" },
|
|
341
|
-
version: { type: "boolean", short: "v" },
|
|
342
|
-
debug: { type: "boolean", short: "d" },
|
|
343
|
-
host: { type: "string" },
|
|
344
|
-
port: { type: "string" },
|
|
345
|
-
secret: { type: "string", short: "s" },
|
|
346
|
-
config: { type: "string" }
|
|
347
|
-
},
|
|
348
|
-
strict: false,
|
|
349
|
-
allowPositionals: true
|
|
350
|
-
});
|
|
351
|
-
var help4 = () => {
|
|
352
|
-
console.log(
|
|
353
|
-
`Azot is a research & pentesting toolkit for Google's Widevine DRM. (${package_default.version})
|
|
354
|
-
`
|
|
355
|
-
);
|
|
356
|
-
console.log(`Usage: azot <command> [...flags]
|
|
357
|
-
`);
|
|
358
|
-
console.log(`Commands:`);
|
|
359
|
-
console.log(col(`serve`) + "Run your API instance");
|
|
360
|
-
console.log(col(`license <url>`) + "Make a license request");
|
|
361
|
-
console.log(
|
|
362
|
-
col(`client <subcommand>`) + "Additional Widevine client utilities"
|
|
363
|
-
);
|
|
364
|
-
console.log("");
|
|
365
|
-
console.log(`Flags:`);
|
|
366
|
-
console.log(col(`-d, --debug`) + "Enable debug level logging");
|
|
367
|
-
console.log(col(`-v, --version`) + "Print version and exit");
|
|
368
|
-
console.log(col(`-h, --help`) + "Display this menu and exit");
|
|
369
|
-
console.log("");
|
|
370
|
-
console.log(`(more flags in azot license --help and azot client --help)`);
|
|
371
|
-
};
|
|
372
|
-
(async () => {
|
|
373
|
-
if (args.values.version) {
|
|
374
|
-
console.log(package_default.version);
|
|
375
|
-
process.exit(0);
|
|
376
|
-
}
|
|
377
|
-
const [command, ...positionals] = args.positionals;
|
|
378
|
-
switch (command) {
|
|
379
|
-
case "serve": {
|
|
380
|
-
if (args.values.help) {
|
|
381
|
-
serve.help();
|
|
382
|
-
process.exit(0);
|
|
383
|
-
}
|
|
384
|
-
serve({
|
|
385
|
-
host: args.values.host,
|
|
386
|
-
port: args.values.port ? parseInt(args.values.port) : void 0,
|
|
387
|
-
config: args.values.config,
|
|
388
|
-
client: args.values.client,
|
|
389
|
-
secret: args.values.secret
|
|
390
|
-
});
|
|
391
|
-
break;
|
|
392
|
-
}
|
|
393
|
-
case "client": {
|
|
394
|
-
const subcommand = positionals.shift();
|
|
395
|
-
const [input, output] = positionals;
|
|
396
|
-
switch (subcommand) {
|
|
397
|
-
case "pack":
|
|
398
|
-
client.pack(input, output);
|
|
399
|
-
break;
|
|
400
|
-
case "unpack":
|
|
401
|
-
client.unpack(input, output);
|
|
402
|
-
break;
|
|
403
|
-
case "info":
|
|
404
|
-
client.info(input);
|
|
405
|
-
break;
|
|
406
|
-
default: {
|
|
407
|
-
if (args.values.help) {
|
|
408
|
-
client.help();
|
|
409
|
-
process.exit(0);
|
|
410
|
-
} else {
|
|
411
|
-
console.log("Client subcommand required: pack, unpack, info");
|
|
412
|
-
process.exit(1);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
case "license": {
|
|
419
|
-
if (args.values.help) {
|
|
420
|
-
license.help();
|
|
421
|
-
process.exit(0);
|
|
422
|
-
}
|
|
423
|
-
const url = positionals.shift();
|
|
424
|
-
if (!url) throw new Error("License URL required");
|
|
425
|
-
const pssh = args.values.pssh;
|
|
426
|
-
if (!pssh) throw new Error("PSSH required");
|
|
427
|
-
const clientPath = args.values.client;
|
|
428
|
-
const encrypt = args.values.encrypt;
|
|
429
|
-
const headers = args.values.header;
|
|
430
|
-
await license({ url, pssh, clientPath, encrypt, headers });
|
|
431
|
-
break;
|
|
432
|
-
}
|
|
433
|
-
case "pssh":
|
|
434
|
-
break;
|
|
435
|
-
case "test":
|
|
436
|
-
break;
|
|
437
|
-
default: {
|
|
438
|
-
if (args.values.help) {
|
|
439
|
-
help4();
|
|
440
|
-
process.exit(0);
|
|
441
|
-
} else {
|
|
442
|
-
console.log("Command not found");
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
})();
|
package/dist/lib.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CLIENT_TYPE,
|
|
3
|
-
Client,
|
|
4
|
-
Key,
|
|
5
|
-
Session,
|
|
6
|
-
fetchDecryptionKeys,
|
|
7
|
-
fromBase64,
|
|
8
|
-
fromBinary,
|
|
9
|
-
fromBuffer,
|
|
10
|
-
fromHex,
|
|
11
|
-
fromText
|
|
12
|
-
} from "./chunk-4ZXWXL4G.js";
|
|
13
|
-
export {
|
|
14
|
-
CLIENT_TYPE,
|
|
15
|
-
Client,
|
|
16
|
-
Key,
|
|
17
|
-
Session,
|
|
18
|
-
fetchDecryptionKeys,
|
|
19
|
-
fromBase64,
|
|
20
|
-
fromBinary,
|
|
21
|
-
fromBuffer,
|
|
22
|
-
fromHex,
|
|
23
|
-
fromText
|
|
24
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|