1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.SignatureV4 = void 0;
|
4 | const eventstream_codec_1 = require("@aws-sdk/eventstream-codec");
|
5 | const util_hex_encoding_1 = require("@aws-sdk/util-hex-encoding");
|
6 | const util_middleware_1 = require("@aws-sdk/util-middleware");
|
7 | const util_utf8_1 = require("@aws-sdk/util-utf8");
|
8 | const constants_1 = require("./constants");
|
9 | const credentialDerivation_1 = require("./credentialDerivation");
|
10 | const getCanonicalHeaders_1 = require("./getCanonicalHeaders");
|
11 | const getCanonicalQuery_1 = require("./getCanonicalQuery");
|
12 | const getPayloadHash_1 = require("./getPayloadHash");
|
13 | const headerUtil_1 = require("./headerUtil");
|
14 | const moveHeadersToQuery_1 = require("./moveHeadersToQuery");
|
15 | const prepareRequest_1 = require("./prepareRequest");
|
16 | const utilDate_1 = require("./utilDate");
|
17 | class SignatureV4 {
|
18 | constructor({ applyChecksum, credentials, region, service, sha256, uriEscapePath = true, }) {
|
19 | this.headerMarshaller = new eventstream_codec_1.HeaderMarshaller(util_utf8_1.toUtf8, util_utf8_1.fromUtf8);
|
20 | this.service = service;
|
21 | this.sha256 = sha256;
|
22 | this.uriEscapePath = uriEscapePath;
|
23 | this.applyChecksum = typeof applyChecksum === "boolean" ? applyChecksum : true;
|
24 | this.regionProvider = (0, util_middleware_1.normalizeProvider)(region);
|
25 | this.credentialProvider = (0, util_middleware_1.normalizeProvider)(credentials);
|
26 | }
|
27 | async presign(originalRequest, options = {}) {
|
28 | const { signingDate = new Date(), expiresIn = 3600, unsignableHeaders, unhoistableHeaders, signableHeaders, signingRegion, signingService, } = options;
|
29 | const credentials = await this.credentialProvider();
|
30 | this.validateResolvedCredentials(credentials);
|
31 | const region = signingRegion !== null && signingRegion !== void 0 ? signingRegion : (await this.regionProvider());
|
32 | const { longDate, shortDate } = formatDate(signingDate);
|
33 | if (expiresIn > constants_1.MAX_PRESIGNED_TTL) {
|
34 | return Promise.reject("Signature version 4 presigned URLs" + " must have an expiration date less than one week in" + " the future");
|
35 | }
|
36 | const scope = (0, credentialDerivation_1.createScope)(shortDate, region, signingService !== null && signingService !== void 0 ? signingService : this.service);
|
37 | const request = (0, moveHeadersToQuery_1.moveHeadersToQuery)((0, prepareRequest_1.prepareRequest)(originalRequest), { unhoistableHeaders });
|
38 | if (credentials.sessionToken) {
|
39 | request.query[constants_1.TOKEN_QUERY_PARAM] = credentials.sessionToken;
|
40 | }
|
41 | request.query[constants_1.ALGORITHM_QUERY_PARAM] = constants_1.ALGORITHM_IDENTIFIER;
|
42 | request.query[constants_1.CREDENTIAL_QUERY_PARAM] = `${credentials.accessKeyId}/${scope}`;
|
43 | request.query[constants_1.AMZ_DATE_QUERY_PARAM] = longDate;
|
44 | request.query[constants_1.EXPIRES_QUERY_PARAM] = expiresIn.toString(10);
|
45 | const canonicalHeaders = (0, getCanonicalHeaders_1.getCanonicalHeaders)(request, unsignableHeaders, signableHeaders);
|
46 | request.query[constants_1.SIGNED_HEADERS_QUERY_PARAM] = getCanonicalHeaderList(canonicalHeaders);
|
47 | request.query[constants_1.SIGNATURE_QUERY_PARAM] = await this.getSignature(longDate, scope, this.getSigningKey(credentials, region, shortDate, signingService), this.createCanonicalRequest(request, canonicalHeaders, await (0, getPayloadHash_1.getPayloadHash)(originalRequest, this.sha256)));
|
48 | return request;
|
49 | }
|
50 | async sign(toSign, options) {
|
51 | if (typeof toSign === "string") {
|
52 | return this.signString(toSign, options);
|
53 | }
|
54 | else if (toSign.headers && toSign.payload) {
|
55 | return this.signEvent(toSign, options);
|
56 | }
|
57 | else if (toSign.message) {
|
58 | return this.signMessage(toSign, options);
|
59 | }
|
60 | else {
|
61 | return this.signRequest(toSign, options);
|
62 | }
|
63 | }
|
64 | async signEvent({ headers, payload }, { signingDate = new Date(), priorSignature, signingRegion, signingService }) {
|
65 | const region = signingRegion !== null && signingRegion !== void 0 ? signingRegion : (await this.regionProvider());
|
66 | const { shortDate, longDate } = formatDate(signingDate);
|
67 | const scope = (0, credentialDerivation_1.createScope)(shortDate, region, signingService !== null && signingService !== void 0 ? signingService : this.service);
|
68 | const hashedPayload = await (0, getPayloadHash_1.getPayloadHash)({ headers: {}, body: payload }, this.sha256);
|
69 | const hash = new this.sha256();
|
70 | hash.update(headers);
|
71 | const hashedHeaders = (0, util_hex_encoding_1.toHex)(await hash.digest());
|
72 | const stringToSign = [
|
73 | constants_1.EVENT_ALGORITHM_IDENTIFIER,
|
74 | longDate,
|
75 | scope,
|
76 | priorSignature,
|
77 | hashedHeaders,
|
78 | hashedPayload,
|
79 | ].join("\n");
|
80 | return this.signString(stringToSign, { signingDate, signingRegion: region, signingService });
|
81 | }
|
82 | async signMessage(signableMessage, { signingDate = new Date(), signingRegion, signingService }) {
|
83 | const promise = this.signEvent({
|
84 | headers: this.headerMarshaller.format(signableMessage.message.headers),
|
85 | payload: signableMessage.message.body,
|
86 | }, {
|
87 | signingDate,
|
88 | signingRegion,
|
89 | signingService,
|
90 | priorSignature: signableMessage.priorSignature,
|
91 | });
|
92 | return promise.then((signature) => {
|
93 | return { message: signableMessage.message, signature };
|
94 | });
|
95 | }
|
96 | async signString(stringToSign, { signingDate = new Date(), signingRegion, signingService } = {}) {
|
97 | const credentials = await this.credentialProvider();
|
98 | this.validateResolvedCredentials(credentials);
|
99 | const region = signingRegion !== null && signingRegion !== void 0 ? signingRegion : (await this.regionProvider());
|
100 | const { shortDate } = formatDate(signingDate);
|
101 | const hash = new this.sha256(await this.getSigningKey(credentials, region, shortDate, signingService));
|
102 | hash.update((0, util_utf8_1.toUint8Array)(stringToSign));
|
103 | return (0, util_hex_encoding_1.toHex)(await hash.digest());
|
104 | }
|
105 | async signRequest(requestToSign, { signingDate = new Date(), signableHeaders, unsignableHeaders, signingRegion, signingService, } = {}) {
|
106 | const credentials = await this.credentialProvider();
|
107 | this.validateResolvedCredentials(credentials);
|
108 | const region = signingRegion !== null && signingRegion !== void 0 ? signingRegion : (await this.regionProvider());
|
109 | const request = (0, prepareRequest_1.prepareRequest)(requestToSign);
|
110 | const { longDate, shortDate } = formatDate(signingDate);
|
111 | const scope = (0, credentialDerivation_1.createScope)(shortDate, region, signingService !== null && signingService !== void 0 ? signingService : this.service);
|
112 | request.headers[constants_1.AMZ_DATE_HEADER] = longDate;
|
113 | if (credentials.sessionToken) {
|
114 | request.headers[constants_1.TOKEN_HEADER] = credentials.sessionToken;
|
115 | }
|
116 | const payloadHash = await (0, getPayloadHash_1.getPayloadHash)(request, this.sha256);
|
117 | if (!(0, headerUtil_1.hasHeader)(constants_1.SHA256_HEADER, request.headers) && this.applyChecksum) {
|
118 | request.headers[constants_1.SHA256_HEADER] = payloadHash;
|
119 | }
|
120 | const canonicalHeaders = (0, getCanonicalHeaders_1.getCanonicalHeaders)(request, unsignableHeaders, signableHeaders);
|
121 | const signature = await this.getSignature(longDate, scope, this.getSigningKey(credentials, region, shortDate, signingService), this.createCanonicalRequest(request, canonicalHeaders, payloadHash));
|
122 | request.headers[constants_1.AUTH_HEADER] =
|
123 | `${constants_1.ALGORITHM_IDENTIFIER} ` +
|
124 | `Credential=${credentials.accessKeyId}/${scope}, ` +
|
125 | `SignedHeaders=${getCanonicalHeaderList(canonicalHeaders)}, ` +
|
126 | `Signature=${signature}`;
|
127 | return request;
|
128 | }
|
129 | createCanonicalRequest(request, canonicalHeaders, payloadHash) {
|
130 | const sortedHeaders = Object.keys(canonicalHeaders).sort();
|
131 | return `${request.method}
|
132 | ${this.getCanonicalPath(request)}
|
133 | ${(0, getCanonicalQuery_1.getCanonicalQuery)(request)}
|
134 | ${sortedHeaders.map((name) => `${name}:${canonicalHeaders[name]}`).join("\n")}
|
135 |
|
136 | ${sortedHeaders.join(";")}
|
137 | ${payloadHash}`;
|
138 | }
|
139 | async createStringToSign(longDate, credentialScope, canonicalRequest) {
|
140 | const hash = new this.sha256();
|
141 | hash.update((0, util_utf8_1.toUint8Array)(canonicalRequest));
|
142 | const hashedRequest = await hash.digest();
|
143 | return `${constants_1.ALGORITHM_IDENTIFIER}
|
144 | ${longDate}
|
145 | ${credentialScope}
|
146 | ${(0, util_hex_encoding_1.toHex)(hashedRequest)}`;
|
147 | }
|
148 | getCanonicalPath({ path }) {
|
149 | if (this.uriEscapePath) {
|
150 | const normalizedPathSegments = [];
|
151 | for (const pathSegment of path.split("/")) {
|
152 | if ((pathSegment === null || pathSegment === void 0 ? void 0 : pathSegment.length) === 0)
|
153 | continue;
|
154 | if (pathSegment === ".")
|
155 | continue;
|
156 | if (pathSegment === "..") {
|
157 | normalizedPathSegments.pop();
|
158 | }
|
159 | else {
|
160 | normalizedPathSegments.push(pathSegment);
|
161 | }
|
162 | }
|
163 | const normalizedPath = `${(path === null || path === void 0 ? void 0 : path.startsWith("/")) ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && (path === null || path === void 0 ? void 0 : path.endsWith("/")) ? "/" : ""}`;
|
164 | const doubleEncoded = encodeURIComponent(normalizedPath);
|
165 | return doubleEncoded.replace(/%2F/g, "/");
|
166 | }
|
167 | return path;
|
168 | }
|
169 | async getSignature(longDate, credentialScope, keyPromise, canonicalRequest) {
|
170 | const stringToSign = await this.createStringToSign(longDate, credentialScope, canonicalRequest);
|
171 | const hash = new this.sha256(await keyPromise);
|
172 | hash.update((0, util_utf8_1.toUint8Array)(stringToSign));
|
173 | return (0, util_hex_encoding_1.toHex)(await hash.digest());
|
174 | }
|
175 | getSigningKey(credentials, region, shortDate, service) {
|
176 | return (0, credentialDerivation_1.getSigningKey)(this.sha256, credentials, shortDate, region, service || this.service);
|
177 | }
|
178 | validateResolvedCredentials(credentials) {
|
179 | if (typeof credentials !== "object" ||
|
180 | typeof credentials.accessKeyId !== "string" ||
|
181 | typeof credentials.secretAccessKey !== "string") {
|
182 | throw new Error("Resolved credential object is not valid");
|
183 | }
|
184 | }
|
185 | }
|
186 | exports.SignatureV4 = SignatureV4;
|
187 | const formatDate = (now) => {
|
188 | const longDate = (0, utilDate_1.iso8601)(now).replace(/[\-:]/g, "");
|
189 | return {
|
190 | longDate,
|
191 | shortDate: longDate.slice(0, 8),
|
192 | };
|
193 | };
|
194 | const getCanonicalHeaderList = (headers) => Object.keys(headers).sort().join(";");
|