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 constructor(signed, signatures, unrecognizedFields) { 43 this.signed = signed; 44 this.signatures = signatures || {}; 45 this.unrecognizedFields = unrecognizedFields || {}; 46 } 47 sign(signer, append = true) { 48 const bytes = Buffer.from((0, canonical_json_1.canonicalize)(this.signed.toJSON())); 49 const signature = signer(bytes); 50 if (!append) { 51 this.signatures = {}; 52 } 53 this.signatures[signature.keyID] = signature; 54 } 55 verifyDelegate(delegatedRole, delegatedMetadata) { 56 let role; 57 let keys = {}; 58 switch (this.signed.type) { 59 case base_1.MetadataKind.Root: 60 keys = this.signed.keys; 61 role = this.signed.roles[delegatedRole]; 62 break; 63 case base_1.MetadataKind.Targets: 64 if (!this.signed.delegations) { 65 throw new error_1.ValueError(`No delegations found for ${delegatedRole}`); 66 } 67 keys = this.signed.delegations.keys; 68 if (this.signed.delegations.roles) { 69 role = this.signed.delegations.roles[delegatedRole]; 70 } 71 else if (this.signed.delegations.succinctRoles) { 72 if (this.signed.delegations.succinctRoles.isDelegatedRole(delegatedRole)) { 73 role = this.signed.delegations.succinctRoles; 74 } 75 } 76 break; 77 default: 78 throw new TypeError('invalid metadata type'); 79 } 80 if (!role) { 81 throw new error_1.ValueError(`no delegation found for ${delegatedRole}`); 82 } 83 const signingKeys = new Set(); 84 role.keyIDs.forEach((keyID) => { 85 const key = keys[keyID]; 86 // If we dont' have the key, continue checking other keys 87 if (!key) { 88 return; 89 } 90 try { 91 key.verifySignature(delegatedMetadata); 92 signingKeys.add(key.keyID); 93 } 94 catch (error) { 95 // continue 96 } 97 }); 98 if (signingKeys.size < role.threshold) { 99 throw new error_1.UnsignedMetadataError(`${delegatedRole} was signed by ${signingKeys.size}/${role.threshold} keys`); 100 } 101 } 102 equals(other) { 103 if (!(other instanceof Metadata)) { 104 return false; 105 } 106 return (this.signed.equals(other.signed) && 107 util_1.default.isDeepStrictEqual(this.signatures, other.signatures) && 108 util_1.default.isDeepStrictEqual(this.unrecognizedFields, other.unrecognizedFields)); 109 } 110 toJSON() { 111 const signatures = Object.values(this.signatures).map((signature) => { 112 return signature.toJSON(); 113 }); 114 return { 115 signatures, 116 signed: this.signed.toJSON(), 117 ...this.unrecognizedFields, 118 }; 119 } 120 static fromJSON(type, data) { 121 const { signed, signatures, ...rest } = data; 122 if (!utils_1.guard.isDefined(signed) || !utils_1.guard.isObject(signed)) { 123 throw new TypeError('signed is not defined'); 124 } 125 if (type !== signed._type) { 126 throw new error_1.ValueError(`expected '${type}', got ${signed['_type']}`); 127 } 128 let signedObj; 129 switch (type) { 130 case base_1.MetadataKind.Root: 131 signedObj = root_1.Root.fromJSON(signed); 132 break; 133 case base_1.MetadataKind.Timestamp: 134 signedObj = timestamp_1.Timestamp.fromJSON(signed); 135 break; 136 case base_1.MetadataKind.Snapshot: 137 signedObj = snapshot_1.Snapshot.fromJSON(signed); 138 break; 139 case base_1.MetadataKind.Targets: 140 signedObj = targets_1.Targets.fromJSON(signed); 141 break; 142 default: 143 throw new TypeError('invalid metadata type'); 144 } 145 const sigMap = signaturesFromJSON(signatures); 146 return new Metadata(signedObj, sigMap, rest); 147 } 148} 149exports.Metadata = Metadata; 150function signaturesFromJSON(data) { 151 if (!utils_1.guard.isObjectArray(data)) { 152 throw new TypeError('signatures is not an array'); 153 } 154 return data.reduce((acc, sigData) => { 155 const signature = signature_1.Signature.fromJSON(sigData); 156 return { ...acc, [signature.keyID]: signature }; 157 }, {}); 158} 159