@smithy/signature-v4 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ module.exports = require("./index.js");
package/dist-cjs/index.js CHANGED
@@ -33,10 +33,10 @@ __export(src_exports, {
33
33
  module.exports = __toCommonJS(src_exports);
34
34
 
35
35
  // src/SignatureV4.ts
36
- var import_eventstream_codec = require("@smithy/eventstream-codec");
37
36
 
38
37
  var import_util_middleware = require("@smithy/util-middleware");
39
- var import_util_utf83 = require("@smithy/util-utf8");
38
+
39
+ var import_util_utf84 = require("@smithy/util-utf8");
40
40
 
41
41
  // src/constants.ts
42
42
  var ALGORITHM_QUERY_PARAM = "X-Amz-Algorithm";
@@ -174,6 +174,128 @@ var getPayloadHash = /* @__PURE__ */ __name(async ({ headers, body }, hashConstr
174
174
  return UNSIGNED_PAYLOAD;
175
175
  }, "getPayloadHash");
176
176
 
177
+ // src/HeaderFormatter.ts
178
+
179
+ var import_util_utf83 = require("@smithy/util-utf8");
180
+ var _HeaderFormatter = class _HeaderFormatter {
181
+ format(headers) {
182
+ const chunks = [];
183
+ for (const headerName of Object.keys(headers)) {
184
+ const bytes = (0, import_util_utf83.fromUtf8)(headerName);
185
+ chunks.push(Uint8Array.from([bytes.byteLength]), bytes, this.formatHeaderValue(headers[headerName]));
186
+ }
187
+ const out = new Uint8Array(chunks.reduce((carry, bytes) => carry + bytes.byteLength, 0));
188
+ let position = 0;
189
+ for (const chunk of chunks) {
190
+ out.set(chunk, position);
191
+ position += chunk.byteLength;
192
+ }
193
+ return out;
194
+ }
195
+ formatHeaderValue(header) {
196
+ switch (header.type) {
197
+ case "boolean":
198
+ return Uint8Array.from([header.value ? 0 /* boolTrue */ : 1 /* boolFalse */]);
199
+ case "byte":
200
+ return Uint8Array.from([2 /* byte */, header.value]);
201
+ case "short":
202
+ const shortView = new DataView(new ArrayBuffer(3));
203
+ shortView.setUint8(0, 3 /* short */);
204
+ shortView.setInt16(1, header.value, false);
205
+ return new Uint8Array(shortView.buffer);
206
+ case "integer":
207
+ const intView = new DataView(new ArrayBuffer(5));
208
+ intView.setUint8(0, 4 /* integer */);
209
+ intView.setInt32(1, header.value, false);
210
+ return new Uint8Array(intView.buffer);
211
+ case "long":
212
+ const longBytes = new Uint8Array(9);
213
+ longBytes[0] = 5 /* long */;
214
+ longBytes.set(header.value.bytes, 1);
215
+ return longBytes;
216
+ case "binary":
217
+ const binView = new DataView(new ArrayBuffer(3 + header.value.byteLength));
218
+ binView.setUint8(0, 6 /* byteArray */);
219
+ binView.setUint16(1, header.value.byteLength, false);
220
+ const binBytes = new Uint8Array(binView.buffer);
221
+ binBytes.set(header.value, 3);
222
+ return binBytes;
223
+ case "string":
224
+ const utf8Bytes = (0, import_util_utf83.fromUtf8)(header.value);
225
+ const strView = new DataView(new ArrayBuffer(3 + utf8Bytes.byteLength));
226
+ strView.setUint8(0, 7 /* string */);
227
+ strView.setUint16(1, utf8Bytes.byteLength, false);
228
+ const strBytes = new Uint8Array(strView.buffer);
229
+ strBytes.set(utf8Bytes, 3);
230
+ return strBytes;
231
+ case "timestamp":
232
+ const tsBytes = new Uint8Array(9);
233
+ tsBytes[0] = 8 /* timestamp */;
234
+ tsBytes.set(Int64.fromNumber(header.value.valueOf()).bytes, 1);
235
+ return tsBytes;
236
+ case "uuid":
237
+ if (!UUID_PATTERN.test(header.value)) {
238
+ throw new Error(`Invalid UUID received: ${header.value}`);
239
+ }
240
+ const uuidBytes = new Uint8Array(17);
241
+ uuidBytes[0] = 9 /* uuid */;
242
+ uuidBytes.set((0, import_util_hex_encoding.fromHex)(header.value.replace(/\-/g, "")), 1);
243
+ return uuidBytes;
244
+ }
245
+ }
246
+ };
247
+ __name(_HeaderFormatter, "HeaderFormatter");
248
+ var HeaderFormatter = _HeaderFormatter;
249
+ var UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
250
+ var _Int64 = class _Int64 {
251
+ constructor(bytes) {
252
+ this.bytes = bytes;
253
+ if (bytes.byteLength !== 8) {
254
+ throw new Error("Int64 buffers must be exactly 8 bytes");
255
+ }
256
+ }
257
+ static fromNumber(number) {
258
+ if (number > 9223372036854776e3 || number < -9223372036854776e3) {
259
+ throw new Error(`${number} is too large (or, if negative, too small) to represent as an Int64`);
260
+ }
261
+ const bytes = new Uint8Array(8);
262
+ for (let i = 7, remaining = Math.abs(Math.round(number)); i > -1 && remaining > 0; i--, remaining /= 256) {
263
+ bytes[i] = remaining;
264
+ }
265
+ if (number < 0) {
266
+ negate(bytes);
267
+ }
268
+ return new _Int64(bytes);
269
+ }
270
+ /**
271
+ * Called implicitly by infix arithmetic operators.
272
+ */
273
+ valueOf() {
274
+ const bytes = this.bytes.slice(0);
275
+ const negative = bytes[0] & 128;
276
+ if (negative) {
277
+ negate(bytes);
278
+ }
279
+ return parseInt((0, import_util_hex_encoding.toHex)(bytes), 16) * (negative ? -1 : 1);
280
+ }
281
+ toString() {
282
+ return String(this.valueOf());
283
+ }
284
+ };
285
+ __name(_Int64, "Int64");
286
+ var Int64 = _Int64;
287
+ function negate(bytes) {
288
+ for (let i = 0; i < 8; i++) {
289
+ bytes[i] ^= 255;
290
+ }
291
+ for (let i = 7; i > -1; i--) {
292
+ bytes[i]++;
293
+ if (bytes[i] !== 0)
294
+ break;
295
+ }
296
+ }
297
+ __name(negate, "negate");
298
+
177
299
  // src/headerUtil.ts
178
300
  var hasHeader = /* @__PURE__ */ __name((soughtHeader, headers) => {
179
301
  soughtHeader = soughtHeader.toLowerCase();
@@ -253,7 +375,7 @@ var _SignatureV4 = class _SignatureV4 {
253
375
  sha256,
254
376
  uriEscapePath = true
255
377
  }) {
256
- this.headerMarshaller = new import_eventstream_codec.HeaderMarshaller(import_util_utf83.toUtf8, import_util_utf83.fromUtf8);
378
+ this.headerFormatter = new HeaderFormatter();
257
379
  this.service = service;
258
380
  this.sha256 = sha256;
259
381
  this.uriEscapePath = uriEscapePath;
@@ -331,7 +453,7 @@ var _SignatureV4 = class _SignatureV4 {
331
453
  async signMessage(signableMessage, { signingDate = /* @__PURE__ */ new Date(), signingRegion, signingService }) {
332
454
  const promise = this.signEvent(
333
455
  {
334
- headers: this.headerMarshaller.format(signableMessage.message.headers),
456
+ headers: this.headerFormatter.format(signableMessage.message.headers),
335
457
  payload: signableMessage.message.body
336
458
  },
337
459
  {
@@ -351,7 +473,7 @@ var _SignatureV4 = class _SignatureV4 {
351
473
  const region = signingRegion ?? await this.regionProvider();
352
474
  const { shortDate } = formatDate(signingDate);
353
475
  const hash = new this.sha256(await this.getSigningKey(credentials, region, shortDate, signingService));
354
- hash.update((0, import_util_utf83.toUint8Array)(stringToSign));
476
+ hash.update((0, import_util_utf84.toUint8Array)(stringToSign));
355
477
  return (0, import_util_hex_encoding.toHex)(await hash.digest());
356
478
  }
357
479
  async signRequest(requestToSign, {
@@ -397,7 +519,7 @@ ${payloadHash}`;
397
519
  }
398
520
  async createStringToSign(longDate, credentialScope, canonicalRequest) {
399
521
  const hash = new this.sha256();
400
- hash.update((0, import_util_utf83.toUint8Array)(canonicalRequest));
522
+ hash.update((0, import_util_utf84.toUint8Array)(canonicalRequest));
401
523
  const hashedRequest = await hash.digest();
402
524
  return `${ALGORITHM_IDENTIFIER}
403
525
  ${longDate}
@@ -419,7 +541,7 @@ ${(0, import_util_hex_encoding.toHex)(hashedRequest)}`;
419
541
  }
420
542
  }
421
543
  const normalizedPath = `${(path == null ? void 0 : path.startsWith("/")) ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && (path == null ? void 0 : path.endsWith("/")) ? "/" : ""}`;
422
- const doubleEncoded = encodeURIComponent(normalizedPath);
544
+ const doubleEncoded = (0, import_util_uri_escape.escapeUri)(normalizedPath);
423
545
  return doubleEncoded.replace(/%2F/g, "/");
424
546
  }
425
547
  return path;
@@ -427,7 +549,7 @@ ${(0, import_util_hex_encoding.toHex)(hashedRequest)}`;
427
549
  async getSignature(longDate, credentialScope, keyPromise, canonicalRequest) {
428
550
  const stringToSign = await this.createStringToSign(longDate, credentialScope, canonicalRequest);
429
551
  const hash = new this.sha256(await keyPromise);
430
- hash.update((0, import_util_utf83.toUint8Array)(stringToSign));
552
+ hash.update((0, import_util_utf84.toUint8Array)(stringToSign));
431
553
  return (0, import_util_hex_encoding.toHex)(await hash.digest());
432
554
  }
433
555
  getSigningKey(credentials, region, shortDate, service) {
@@ -0,0 +1,125 @@
1
+ import { fromHex, toHex } from "@smithy/util-hex-encoding";
2
+ import { fromUtf8 } from "@smithy/util-utf8";
3
+ export class HeaderFormatter {
4
+ format(headers) {
5
+ const chunks = [];
6
+ for (const headerName of Object.keys(headers)) {
7
+ const bytes = fromUtf8(headerName);
8
+ chunks.push(Uint8Array.from([bytes.byteLength]), bytes, this.formatHeaderValue(headers[headerName]));
9
+ }
10
+ const out = new Uint8Array(chunks.reduce((carry, bytes) => carry + bytes.byteLength, 0));
11
+ let position = 0;
12
+ for (const chunk of chunks) {
13
+ out.set(chunk, position);
14
+ position += chunk.byteLength;
15
+ }
16
+ return out;
17
+ }
18
+ formatHeaderValue(header) {
19
+ switch (header.type) {
20
+ case "boolean":
21
+ return Uint8Array.from([header.value ? 0 : 1]);
22
+ case "byte":
23
+ return Uint8Array.from([2, header.value]);
24
+ case "short":
25
+ const shortView = new DataView(new ArrayBuffer(3));
26
+ shortView.setUint8(0, 3);
27
+ shortView.setInt16(1, header.value, false);
28
+ return new Uint8Array(shortView.buffer);
29
+ case "integer":
30
+ const intView = new DataView(new ArrayBuffer(5));
31
+ intView.setUint8(0, 4);
32
+ intView.setInt32(1, header.value, false);
33
+ return new Uint8Array(intView.buffer);
34
+ case "long":
35
+ const longBytes = new Uint8Array(9);
36
+ longBytes[0] = 5;
37
+ longBytes.set(header.value.bytes, 1);
38
+ return longBytes;
39
+ case "binary":
40
+ const binView = new DataView(new ArrayBuffer(3 + header.value.byteLength));
41
+ binView.setUint8(0, 6);
42
+ binView.setUint16(1, header.value.byteLength, false);
43
+ const binBytes = new Uint8Array(binView.buffer);
44
+ binBytes.set(header.value, 3);
45
+ return binBytes;
46
+ case "string":
47
+ const utf8Bytes = fromUtf8(header.value);
48
+ const strView = new DataView(new ArrayBuffer(3 + utf8Bytes.byteLength));
49
+ strView.setUint8(0, 7);
50
+ strView.setUint16(1, utf8Bytes.byteLength, false);
51
+ const strBytes = new Uint8Array(strView.buffer);
52
+ strBytes.set(utf8Bytes, 3);
53
+ return strBytes;
54
+ case "timestamp":
55
+ const tsBytes = new Uint8Array(9);
56
+ tsBytes[0] = 8;
57
+ tsBytes.set(Int64.fromNumber(header.value.valueOf()).bytes, 1);
58
+ return tsBytes;
59
+ case "uuid":
60
+ if (!UUID_PATTERN.test(header.value)) {
61
+ throw new Error(`Invalid UUID received: ${header.value}`);
62
+ }
63
+ const uuidBytes = new Uint8Array(17);
64
+ uuidBytes[0] = 9;
65
+ uuidBytes.set(fromHex(header.value.replace(/\-/g, "")), 1);
66
+ return uuidBytes;
67
+ }
68
+ }
69
+ }
70
+ var HEADER_VALUE_TYPE;
71
+ (function (HEADER_VALUE_TYPE) {
72
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["boolTrue"] = 0] = "boolTrue";
73
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["boolFalse"] = 1] = "boolFalse";
74
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["byte"] = 2] = "byte";
75
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["short"] = 3] = "short";
76
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["integer"] = 4] = "integer";
77
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["long"] = 5] = "long";
78
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["byteArray"] = 6] = "byteArray";
79
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["string"] = 7] = "string";
80
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["timestamp"] = 8] = "timestamp";
81
+ HEADER_VALUE_TYPE[HEADER_VALUE_TYPE["uuid"] = 9] = "uuid";
82
+ })(HEADER_VALUE_TYPE || (HEADER_VALUE_TYPE = {}));
83
+ const UUID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
84
+ export class Int64 {
85
+ constructor(bytes) {
86
+ this.bytes = bytes;
87
+ if (bytes.byteLength !== 8) {
88
+ throw new Error("Int64 buffers must be exactly 8 bytes");
89
+ }
90
+ }
91
+ static fromNumber(number) {
92
+ if (number > 9223372036854776000 || number < -9223372036854776000) {
93
+ throw new Error(`${number} is too large (or, if negative, too small) to represent as an Int64`);
94
+ }
95
+ const bytes = new Uint8Array(8);
96
+ for (let i = 7, remaining = Math.abs(Math.round(number)); i > -1 && remaining > 0; i--, remaining /= 256) {
97
+ bytes[i] = remaining;
98
+ }
99
+ if (number < 0) {
100
+ negate(bytes);
101
+ }
102
+ return new Int64(bytes);
103
+ }
104
+ valueOf() {
105
+ const bytes = this.bytes.slice(0);
106
+ const negative = bytes[0] & 0b10000000;
107
+ if (negative) {
108
+ negate(bytes);
109
+ }
110
+ return parseInt(toHex(bytes), 16) * (negative ? -1 : 1);
111
+ }
112
+ toString() {
113
+ return String(this.valueOf());
114
+ }
115
+ }
116
+ function negate(bytes) {
117
+ for (let i = 0; i < 8; i++) {
118
+ bytes[i] ^= 0xff;
119
+ }
120
+ for (let i = 7; i > -1; i--) {
121
+ bytes[i]++;
122
+ if (bytes[i] !== 0)
123
+ break;
124
+ }
125
+ }
@@ -1,19 +1,20 @@
1
- import { HeaderMarshaller } from "@smithy/eventstream-codec";
2
1
  import { toHex } from "@smithy/util-hex-encoding";
3
2
  import { normalizeProvider } from "@smithy/util-middleware";
4
- import { fromUtf8, toUint8Array, toUtf8 } from "@smithy/util-utf8";
3
+ import { escapeUri } from "@smithy/util-uri-escape";
4
+ import { toUint8Array } from "@smithy/util-utf8";
5
5
  import { ALGORITHM_IDENTIFIER, ALGORITHM_QUERY_PARAM, AMZ_DATE_HEADER, AMZ_DATE_QUERY_PARAM, AUTH_HEADER, CREDENTIAL_QUERY_PARAM, EVENT_ALGORITHM_IDENTIFIER, EXPIRES_QUERY_PARAM, MAX_PRESIGNED_TTL, SHA256_HEADER, SIGNATURE_QUERY_PARAM, SIGNED_HEADERS_QUERY_PARAM, TOKEN_HEADER, TOKEN_QUERY_PARAM, } from "./constants";
6
6
  import { createScope, getSigningKey } from "./credentialDerivation";
7
7
  import { getCanonicalHeaders } from "./getCanonicalHeaders";
8
8
  import { getCanonicalQuery } from "./getCanonicalQuery";
9
9
  import { getPayloadHash } from "./getPayloadHash";
10
+ import { HeaderFormatter } from "./HeaderFormatter";
10
11
  import { hasHeader } from "./headerUtil";
11
12
  import { moveHeadersToQuery } from "./moveHeadersToQuery";
12
13
  import { prepareRequest } from "./prepareRequest";
13
14
  import { iso8601 } from "./utilDate";
14
15
  export class SignatureV4 {
15
16
  constructor({ applyChecksum, credentials, region, service, sha256, uriEscapePath = true, }) {
16
- this.headerMarshaller = new HeaderMarshaller(toUtf8, fromUtf8);
17
+ this.headerFormatter = new HeaderFormatter();
17
18
  this.service = service;
18
19
  this.sha256 = sha256;
19
20
  this.uriEscapePath = uriEscapePath;
@@ -78,7 +79,7 @@ export class SignatureV4 {
78
79
  }
79
80
  async signMessage(signableMessage, { signingDate = new Date(), signingRegion, signingService }) {
80
81
  const promise = this.signEvent({
81
- headers: this.headerMarshaller.format(signableMessage.message.headers),
82
+ headers: this.headerFormatter.format(signableMessage.message.headers),
82
83
  payload: signableMessage.message.body,
83
84
  }, {
84
85
  signingDate,
@@ -158,7 +159,7 @@ ${toHex(hashedRequest)}`;
158
159
  }
159
160
  }
160
161
  const normalizedPath = `${path?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && path?.endsWith("/") ? "/" : ""}`;
161
- const doubleEncoded = encodeURIComponent(normalizedPath);
162
+ const doubleEncoded = escapeUri(normalizedPath);
162
163
  return doubleEncoded.replace(/%2F/g, "/");
163
164
  }
164
165
  return path;
@@ -0,0 +1,24 @@
1
+ import type { Int64 as IInt64, MessageHeaders } from "@smithy/types";
2
+ /**
3
+ * @internal
4
+ * TODO: duplicated from @smithy/eventstream-codec to break large dependency.
5
+ * TODO: This should be moved to its own deduped submodule in @smithy/core when submodules are implemented.
6
+ */
7
+ export declare class HeaderFormatter {
8
+ format(headers: MessageHeaders): Uint8Array;
9
+ private formatHeaderValue;
10
+ }
11
+ /**
12
+ * TODO: duplicated from @smithy/eventstream-codec to break large dependency.
13
+ * TODO: This should be moved to its own deduped submodule in @smithy/core when submodules are implemented.
14
+ */
15
+ export declare class Int64 implements IInt64 {
16
+ readonly bytes: Uint8Array;
17
+ constructor(bytes: Uint8Array);
18
+ static fromNumber(number: number): Int64;
19
+ /**
20
+ * Called implicitly by infix arithmetic operators.
21
+ */
22
+ valueOf(): number;
23
+ toString(): string;
24
+ }
@@ -47,7 +47,7 @@ export declare class SignatureV4 implements RequestPresigner, RequestSigner, Str
47
47
  private readonly sha256;
48
48
  private readonly uriEscapePath;
49
49
  private readonly applyChecksum;
50
- private readonly headerMarshaller;
50
+ private readonly headerFormatter;
51
51
  constructor({ applyChecksum, credentials, region, service, sha256, uriEscapePath, }: SignatureV4Init & SignatureV4CryptoInit);
52
52
  presign(originalRequest: HttpRequest, options?: RequestPresigningArguments): Promise<HttpRequest>;
53
53
  sign(stringToSign: string, options?: SigningArguments): Promise<string>;
@@ -0,0 +1,24 @@
1
+ import { Int64 as IInt64, MessageHeaders } from "@smithy/types";
2
+ /**
3
+ * @internal
4
+ * TODO: duplicated from @smithy/eventstream-codec to break large dependency.
5
+ * TODO: This should be moved to its own deduped submodule in @smithy/core when submodules are implemented.
6
+ */
7
+ export declare class HeaderFormatter {
8
+ format(headers: MessageHeaders): Uint8Array;
9
+ private formatHeaderValue;
10
+ }
11
+ /**
12
+ * TODO: duplicated from @smithy/eventstream-codec to break large dependency.
13
+ * TODO: This should be moved to its own deduped submodule in @smithy/core when submodules are implemented.
14
+ */
15
+ export declare class Int64 implements IInt64 {
16
+ readonly bytes: Uint8Array;
17
+ constructor(bytes: Uint8Array);
18
+ static fromNumber(number: number): Int64;
19
+ /**
20
+ * Called implicitly by infix arithmetic operators.
21
+ */
22
+ valueOf(): number;
23
+ toString(): string;
24
+ }
@@ -47,7 +47,7 @@ export declare class SignatureV4 implements RequestPresigner, RequestSigner, Str
47
47
  private readonly sha256;
48
48
  private readonly uriEscapePath;
49
49
  private readonly applyChecksum;
50
- private readonly headerMarshaller;
50
+ private readonly headerFormatter;
51
51
  constructor({ applyChecksum, credentials, region, service, sha256, uriEscapePath, }: SignatureV4Init & SignatureV4CryptoInit);
52
52
  presign(originalRequest: HttpRequest, options?: RequestPresigningArguments): Promise<HttpRequest>;
53
53
  sign(stringToSign: string, options?: SigningArguments): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithy/signature-v4",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "A standalone implementation of the AWS Signature V4 request signing algorithm",
5
5
  "main": "./dist-cjs/index.js",
6
6
  "module": "./dist-es/index.js",
@@ -24,7 +24,6 @@
24
24
  },
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "@smithy/eventstream-codec": "^2.2.0",
28
27
  "@smithy/is-array-buffer": "^2.2.0",
29
28
  "@smithy/types": "^2.12.0",
30
29
  "@smithy/util-hex-encoding": "^2.2.0",