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