codekingpro/portable-devtools
114k
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.Metadata = void 0;
7const canonical_json_1 = require("@tufjs/canonical-json");
8const util_1 = __importDefault(require("util"));
9const base_1 = require("./base");
10const error_1 = require("./error");
11const root_1 = require("./root");
12const signature_1 = require("./signature");
13const snapshot_1 = require("./snapshot");
14const targets_1 = require("./targets");
15const timestamp_1 = require("./timestamp");
16const utils_1 = require("./utils");
17/***
18 * A container for signed TUF metadata.
19 *
20 * Provides methods to convert to and from json, read and write to and
21 * from JSON and to create and verify metadata signatures.
22 *
23 * ``Metadata[T]`` is a generic container type where T can be any one type of
24 * [``Root``, ``Timestamp``, ``Snapshot``, ``Targets``]. The purpose of this
25 * is to allow static type checking of the signed attribute in code using
26 * Metadata::
27 *
28 * root_md = Metadata[Root].fromJSON("root.json")
29 * # root_md type is now Metadata[Root]. This means signed and its
30 * # attributes like consistent_snapshot are now statically typed and the
31 * # types can be verified by static type checkers and shown by IDEs
32 *
33 * Using a type constraint is not required but not doing so means T is not a
34 * specific type so static typing cannot happen. Note that the type constraint
35 * ``[Root]`` is not validated at runtime (as pure annotations are not available
36 * then).
37 *
38 * Apart from ``expires`` all of the arguments to the inner constructors have
39 * reasonable default values for new metadata.
40 */
41class Metadata {
42 signed;
43 signatures;
44 unrecognizedFields;
45 constructor(signed, signatures, unrecognizedFields) {
46 this.signed = signed;
47 this.signatures = signatures || {};
48 this.unrecognizedFields = unrecognizedFields || {};
49 }
50 sign(signer, append = true) {
51 const bytes = Buffer.from((0, canonical_json_1.canonicalize)(this.signed.toJSON()));
52 const signature = signer(bytes);
53 if (!append) {
54 this.signatures = {};
55 }
56 this.signatures[signature.keyID] = signature;
57 }
58 verifyDelegate(delegatedRole, delegatedMetadata) {
59 let role;
60 let keys = {};
61 switch (this.signed.type) {
62 case base_1.MetadataKind.Root:
63 keys = this.signed.keys;
64 role = this.signed.roles[delegatedRole];
65 break;
66 case base_1.MetadataKind.Targets:
67 if (!this.signed.delegations) {
68 throw new error_1.ValueError(`No delegations found for ${delegatedRole}`);
69 }
70 keys = this.signed.delegations.keys;
71 if (this.signed.delegations.roles) {
72 role = this.signed.delegations.roles[delegatedRole];
73 }
74 else if (this.signed.delegations.succinctRoles) {
75 if (this.signed.delegations.succinctRoles.isDelegatedRole(delegatedRole)) {
76 role = this.signed.delegations.succinctRoles;
77 }
78 }
79 break;
80 default:
81 throw new TypeError('invalid metadata type');
82 }
83 if (!role) {
84 throw new error_1.ValueError(`no delegation found for ${delegatedRole}`);
85 }
86 const signingKeys = new Set();
87 role.keyIDs.forEach((keyID) => {
88 const key = keys[keyID];
89 // If we dont' have the key, continue checking other keys
90 if (!key) {
91 return;
92 }
93 try {
94 key.verifySignature(delegatedMetadata);
95 signingKeys.add(key.keyID);
96 }
97 catch (error) {
98 // continue
99 }
100 });
101 if (signingKeys.size < role.threshold) {
102 throw new error_1.UnsignedMetadataError(`${delegatedRole} was signed by ${signingKeys.size}/${role.threshold} keys`);
103 }
104 }
105 equals(other) {
106 if (!(other instanceof Metadata)) {
107 return false;
108 }
109 return (
110 // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
111 this.signed.equals(other.signed) &&
112 util_1.default.isDeepStrictEqual(this.signatures, other.signatures) &&
113 util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields));
114 }
115 toJSON() {
116 const signatures = Object.values(this.signatures).map((signature) => {
117 return signature.toJSON();
118 });
119 return {
120 signatures,
121 signed: this.signed.toJSON(),
122 ...this.unrecognizedFields,
123 };
124 }
125 static fromJSON(type, data) {
126 const { signed, signatures, ...rest } = data;
127 if (!utils_1.guard.isDefined(signed) || !utils_1.guard.isObject(signed)) {
128 throw new TypeError('signed is not defined');
129 }
130 if (type !== signed._type) {
131 throw new error_1.ValueError(`expected '${type}', got ${signed['_type']}`);
132 }
133 if (!utils_1.guard.isObjectArray(signatures)) {
134 throw new TypeError('signatures is not an array');
135 }
136 let signedObj;
137 switch (type) {
138 case base_1.MetadataKind.Root:
139 signedObj = root_1.Root.fromJSON(signed);
140 break;
141 case base_1.MetadataKind.Timestamp:
142 signedObj = timestamp_1.Timestamp.fromJSON(signed);
143 break;
144 case base_1.MetadataKind.Snapshot:
145 signedObj = snapshot_1.Snapshot.fromJSON(signed);
146 break;
147 case base_1.MetadataKind.Targets:
148 signedObj = targets_1.Targets.fromJSON(signed);
149 break;
150 default:
151 throw new TypeError('invalid metadata type');
152 }
153 const sigMap = {};
154 // Ensure that each signature is unique
155 signatures.forEach((sigData) => {
156 const sig = signature_1.Signature.fromJSON(sigData);
157 if (sigMap[sig.keyID]) {
158 throw new error_1.ValueError(`multiple signatures found for keyid: ${sig.keyID}`);
159 }
160 sigMap[sig.keyID] = sig;
161 });
162 return new Metadata(signedObj, sigMap, rest);
163 }
164}
165exports.Metadata = Metadata;
166 