UNPKG

11.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.SignatureV4 = void 0;
4const eventstream_codec_1 = require("@aws-sdk/eventstream-codec");
5const util_hex_encoding_1 = require("@aws-sdk/util-hex-encoding");
6const util_middleware_1 = require("@aws-sdk/util-middleware");
7const util_utf8_1 = require("@aws-sdk/util-utf8");
8const constants_1 = require("./constants");
9const credentialDerivation_1 = require("./credentialDerivation");
10const getCanonicalHeaders_1 = require("./getCanonicalHeaders");
11const getCanonicalQuery_1 = require("./getCanonicalQuery");
12const getPayloadHash_1 = require("./getPayloadHash");
13const headerUtil_1 = require("./headerUtil");
14const moveHeadersToQuery_1 = require("./moveHeadersToQuery");
15const prepareRequest_1 = require("./prepareRequest");
16const utilDate_1 = require("./utilDate");
17class 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}
186exports.SignatureV4 = SignatureV4;
187const formatDate = (now) => {
188 const longDate = (0, utilDate_1.iso8601)(now).replace(/[\-:]/g, "");
189 return {
190 longDate,
191 shortDate: longDate.slice(0, 8),
192 };
193};
194const getCanonicalHeaderList = (headers) => Object.keys(headers).sort().join(";");