metadata.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. exports.Metadata = void 0;
  7. const canonical_json_1 = require("@tufjs/canonical-json");
  8. const util_1 = __importDefault(require("util"));
  9. const base_1 = require("./base");
  10. const error_1 = require("./error");
  11. const root_1 = require("./root");
  12. const signature_1 = require("./signature");
  13. const snapshot_1 = require("./snapshot");
  14. const targets_1 = require("./targets");
  15. const timestamp_1 = require("./timestamp");
  16. const 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. */
  41. class 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. if (!utils_1.guard.isObjectArray(signatures)) {
  129. throw new TypeError('signatures is not an array');
  130. }
  131. let signedObj;
  132. switch (type) {
  133. case base_1.MetadataKind.Root:
  134. signedObj = root_1.Root.fromJSON(signed);
  135. break;
  136. case base_1.MetadataKind.Timestamp:
  137. signedObj = timestamp_1.Timestamp.fromJSON(signed);
  138. break;
  139. case base_1.MetadataKind.Snapshot:
  140. signedObj = snapshot_1.Snapshot.fromJSON(signed);
  141. break;
  142. case base_1.MetadataKind.Targets:
  143. signedObj = targets_1.Targets.fromJSON(signed);
  144. break;
  145. default:
  146. throw new TypeError('invalid metadata type');
  147. }
  148. const sigMap = {};
  149. // Ensure that each signature is unique
  150. signatures.forEach((sigData) => {
  151. const sig = signature_1.Signature.fromJSON(sigData);
  152. if (sigMap[sig.keyID]) {
  153. throw new error_1.ValueError(`multiple signatures found for keyid: ${sig.keyID}`);
  154. }
  155. sigMap[sig.keyID] = sig;
  156. });
  157. return new Metadata(signedObj, sigMap, rest);
  158. }
  159. }
  160. exports.Metadata = Metadata;