sigflint 0.0.0 → 1.0.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.
- package/CHANGELOG.md +17 -0
- package/README.md +99 -3
- package/dist/bun.js +569 -0
- package/dist/node.js +585 -0
- package/index.d.ts +44 -0
- package/package.json +71 -7
- package/src/base.js +341 -0
- package/src/bun.js +37 -0
- package/src/chunks.js +82 -0
- package/src/ecdsa-node.js +14 -0
- package/src/events.js +85 -0
- package/src/node.js +61 -0
- package/src/presign.js +51 -0
- package/src/v4a.js +96 -0
- package/src/web.js +187 -0
- package/types/base.d.ts +145 -0
- package/types/bun.d.ts +6 -0
- package/types/chunks.d.ts +38 -0
- package/types/ecdsa-node.d.ts +5 -0
- package/types/events.d.ts +25 -0
- package/types/node.d.ts +6 -0
- package/types/presign.d.ts +27 -0
- package/types/v4a.d.ts +48 -0
- package/types/web.d.ts +47 -0
package/src/node.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Node.js, Deno and Cloudflare Workers with the nodejs_compat flag: node:crypto.
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
import { createSigner, createKeys } from './base.js';
|
|
4
|
+
import { createPresigner } from './presign.js';
|
|
5
|
+
import { createSignerV4a, createPresignerV4a } from './v4a.js';
|
|
6
|
+
import { ecdsa } from './ecdsa-node.js';
|
|
7
|
+
import { createChunkSigner } from './chunks.js';
|
|
8
|
+
import { createEventSigner } from './events.js';
|
|
9
|
+
|
|
10
|
+
const utf8 = /* @__PURE__ */ new TextEncoder();
|
|
11
|
+
const maxData = 1024;
|
|
12
|
+
const inner = /* @__PURE__ */ new Uint8Array(64 + maxData); // the ipad block, then the data
|
|
13
|
+
const outer = /* @__PURE__ */ new Uint8Array(64 + 32); // the opad block, then the inner digest
|
|
14
|
+
const data = /* @__PURE__ */ inner.subarray(64);
|
|
15
|
+
const innerViews = [];
|
|
16
|
+
let blockKey;
|
|
17
|
+
const nibble = (c) => (c > 96 ? c - 87 : c - 48);
|
|
18
|
+
|
|
19
|
+
// HMAC-SHA-256 as two one-shot hashes over preallocated buffers: it avoids creating a Hmac object per signature.
|
|
20
|
+
// Only a key of at most 64 bytes is handled; the signing key is always 32 bytes.
|
|
21
|
+
function hmacHexOneShot(key, text) {
|
|
22
|
+
if (key !== blockKey) {
|
|
23
|
+
blockKey = key;
|
|
24
|
+
inner.fill(0x36, 0, 64);
|
|
25
|
+
outer.fill(0x5c, 0, 64);
|
|
26
|
+
for (let i = 0; i < key.length; i++) {
|
|
27
|
+
inner[i] ^= key[i];
|
|
28
|
+
outer[i] ^= key[i];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const end = 64 + utf8.encodeInto(text, data).written;
|
|
32
|
+
const hex = crypto.hash('sha256', (innerViews[end] ??= inner.subarray(0, end)), 'hex');
|
|
33
|
+
for (let i = 0; i < 32; i++) outer[64 + i] = (nibble(hex.charCodeAt(2 * i)) << 4) | nibble(hex.charCodeAt(2 * i + 1));
|
|
34
|
+
return crypto.hash('sha256', outer, 'hex');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const hmacHexStreaming = (key, text) => crypto.createHmac('sha256', key).update(text).digest('hex');
|
|
38
|
+
|
|
39
|
+
const hmac = (key, text) => crypto.createHmac('sha256', key).update(text).digest();
|
|
40
|
+
|
|
41
|
+
// The one-shot form is used only when it agrees with the streaming one on a sample, and only for short data. The sample is checked on first use.
|
|
42
|
+
let oneShotWorks;
|
|
43
|
+
function probe() {
|
|
44
|
+
const key = hmac('probe', 'key');
|
|
45
|
+
const text = 'AWS4-HMAC-SHA256\n20130524T000000Z';
|
|
46
|
+
const works = hmacHexOneShot(key, text) === hmacHexStreaming(key, text);
|
|
47
|
+
blockKey = undefined;
|
|
48
|
+
return works;
|
|
49
|
+
}
|
|
50
|
+
const hmacHex = (key, text) => ((oneShotWorks ??= probe()) && text.length * 3 <= maxData && key.length <= 64 ? hmacHexOneShot(key, text) : hmacHexStreaming(key, text));
|
|
51
|
+
|
|
52
|
+
const getSigningKey = /* @__PURE__ */ createKeys(hmac);
|
|
53
|
+
const hexHash = (text) => crypto.hash('sha256', text, 'hex');
|
|
54
|
+
const primitives = { hexHash, hmac, hmacHex, getSigningKey };
|
|
55
|
+
|
|
56
|
+
export const getAuthHeaders = /* @__PURE__ */ createSigner(primitives);
|
|
57
|
+
export const presign = /* @__PURE__ */ createPresigner(primitives);
|
|
58
|
+
export const getAuthHeadersV4a = /* @__PURE__ */ createSignerV4a({ hexHash, hmac, ecdsa });
|
|
59
|
+
export const presignV4a = /* @__PURE__ */ createPresignerV4a({ hexHash, hmac, ecdsa });
|
|
60
|
+
export const chunkSigner = /* @__PURE__ */ createChunkSigner(primitives);
|
|
61
|
+
export const eventSigner = /* @__PURE__ */ createEventSigner(primitives);
|
package/src/presign.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Presigned URLs: the signature goes in the query string instead of the authorization header.
|
|
2
|
+
import { symmetric, canonicalPath, canonicalQuery, insert, headerValue, signHeaderName, amzDatetime, payloadHashOf, stringToSign } from './base.js';
|
|
3
|
+
|
|
4
|
+
export const encodeQueryPart = (text) => encodeURIComponent(text).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Everything up to the hashing for a presigned URL. `request.expires` is the lifetime in seconds (default 3600). Only the host and request.signHeaders are
|
|
8
|
+
* signed; S3 URLs are always UNSIGNED-PAYLOAD, other services sign the body hash. `extra` is appended to the authentication parameters.
|
|
9
|
+
*/
|
|
10
|
+
export function preparePresign(request, credentials, payloadHash, scheme = symmetric, extra = '') {
|
|
11
|
+
const { method, url, signHeaders } = request;
|
|
12
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') throw new TypeError('presign needs an http or https URL');
|
|
13
|
+
if (url.search.length > 1 && (url.search.includes('X-Amz-Signature=') || url.search.includes('X-Amz-Algorithm='))) throw new TypeError('the URL is already presigned');
|
|
14
|
+
const expires = request.expires ?? 3600;
|
|
15
|
+
if (!Number.isInteger(expires) || expires < 1 || expires > 604800) throw new RangeError('expires must be a whole number of seconds from 1 to 604800 (7 days)');
|
|
16
|
+
const region = request.region ?? '';
|
|
17
|
+
const service = request.service ?? 's3';
|
|
18
|
+
const datetime = amzDatetime(request.now ?? Date.now());
|
|
19
|
+
const date = datetime.slice(0, 8);
|
|
20
|
+
const credentialScope = scheme.scope(date, region, service);
|
|
21
|
+
const names = ['host'];
|
|
22
|
+
const values = [url.host];
|
|
23
|
+
if (signHeaders !== undefined) for (const name of Object.keys(signHeaders)) insert(names, values, signHeaderName(name, signHeaders[name]), headerValue(signHeaders[name]));
|
|
24
|
+
let canonicalHeaders = '';
|
|
25
|
+
for (let i = 0; i < names.length; i++) canonicalHeaders += `${names[i]}:${values[i]}\n`;
|
|
26
|
+
const signedHeaders = names.join(';');
|
|
27
|
+
let auth = `X-Amz-Algorithm=${scheme.algorithm}&X-Amz-Credential=${encodeQueryPart(`${credentials.accessKeyId}/${credentialScope}`)}&X-Amz-Date=${datetime}&X-Amz-Expires=${expires}&X-Amz-SignedHeaders=${encodeQueryPart(signedHeaders)}`;
|
|
28
|
+
if (credentials.sessionToken) auth += `&X-Amz-Security-Token=${encodeQueryPart(credentials.sessionToken)}`;
|
|
29
|
+
auth += extra;
|
|
30
|
+
const query = url.search.length > 1 ? url.search + '&' + auth : '?' + auth;
|
|
31
|
+
const hash = service === 's3' || request.unsignedPayload ? 'UNSIGNED-PAYLOAD' : payloadHash;
|
|
32
|
+
const canonicalRequest = `${method}\n${canonicalPath(service, url.pathname)}\n${canonicalQuery(query)}\n${canonicalHeaders}\n${signedHeaders}\n${hash}`;
|
|
33
|
+
return { canonicalRequest, datetime, date, region, service, credentialScope, signedHeaders, algorithm: scheme.algorithm, base: url.origin + url.pathname, query };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The URL to hand out: the original URL with the authentication query parameters and the signature appended. */
|
|
37
|
+
export const presignedUrl = (prepared, signature) => `${prepared.base}${prepared.query}&X-Amz-Signature=${signature}`;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {import('./base.js').Primitives} primitives
|
|
41
|
+
* @returns {(request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string}
|
|
42
|
+
*/
|
|
43
|
+
export function createPresigner({ hexHash, hmacHex, getSigningKey }) {
|
|
44
|
+
return function presign(request, credentials) {
|
|
45
|
+
const prepared = preparePresign(request, credentials, payloadHashOf(request, hexHash));
|
|
46
|
+
return presignedUrl(
|
|
47
|
+
prepared,
|
|
48
|
+
hmacHex(getSigningKey(credentials.secretAccessKey, prepared.date, prepared.region, prepared.service), stringToSign(prepared, hexHash(prepared.canonicalRequest))),
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
}
|
package/src/v4a.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SigV4a (multi-region): ECDSA P-256 instead of HMAC, a credential scope without the region, and the region set as a signed header (or a query parameter when
|
|
2
|
+
// presigning). The runtime entries supply the ECDSA primitive. Nothing in the symmetric path imports this file.
|
|
3
|
+
import { prepare, payloadHashOf, stringToSign, authorize } from './base.js';
|
|
4
|
+
import { preparePresign, presignedUrl, encodeQueryPart } from './presign.js';
|
|
5
|
+
|
|
6
|
+
export const asymmetric = { algorithm: 'AWS4-ECDSA-P256-SHA256', scope: (date, _region, service) => `${date}/${service}/aws4_request` };
|
|
7
|
+
|
|
8
|
+
function regionSetOf(request) {
|
|
9
|
+
const regionSet = request.regionSet;
|
|
10
|
+
if (typeof regionSet !== 'string' || regionSet === '') throw new TypeError("SigV4a needs request.regionSet, for example 'us-east-1,us-west-2' or '*'");
|
|
11
|
+
return regionSet;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The request with the region set added as a signed header; `regionSet` is 'us-east-1,us-west-2' or '*'. */
|
|
15
|
+
export const withRegionSet = (request) => ({ ...request, signHeaders: { ...request.signHeaders, 'x-amz-region-set': regionSetOf(request) } });
|
|
16
|
+
|
|
17
|
+
export const regionSetQuery = (request) => `&X-Amz-Region-Set=${encodeQueryPart(regionSetOf(request))}`;
|
|
18
|
+
|
|
19
|
+
// 4-byte counter 1, label, 0x00, the access key id, the external counter, 256 as 4 bytes
|
|
20
|
+
export const kdfInput = (accessKeyId, counter) => `\0\0\0\x01AWS4-ECDSA-P256-SHA256\0${accessKeyId}${String.fromCharCode(counter)}\0\0\x01\0`;
|
|
21
|
+
|
|
22
|
+
/** The private scalar (HMAC output + 1) when the HMAC output is at most the curve order minus 2, else undefined. */
|
|
23
|
+
export function scalarFrom(k) {
|
|
24
|
+
const nMinusTwo = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254fn;
|
|
25
|
+
let c = 0n;
|
|
26
|
+
for (let i = 0; i < 32; i++) c = (c << 8n) | BigInt(k[i]);
|
|
27
|
+
if (c > nMinusTwo) return undefined;
|
|
28
|
+
c += 1n;
|
|
29
|
+
const d = new Uint8Array(32);
|
|
30
|
+
for (let i = 31; i >= 0; i--, c >>= 8n) d[i] = Number(c & 0xffn);
|
|
31
|
+
return d;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The SigV4a private key: the P-256 scalar derived from the secret key by the counter-mode HMAC construction in the AWS documentation (32 bytes, big-endian). */
|
|
35
|
+
export function deriveP256(hmac, accessKeyId, secretAccessKey) {
|
|
36
|
+
for (let counter = 1; counter < 128; counter++) {
|
|
37
|
+
const d = scalarFrom(hmac('AWS4A' + secretAccessKey, kdfInput(accessKeyId, counter)));
|
|
38
|
+
if (d !== undefined) return d;
|
|
39
|
+
}
|
|
40
|
+
throw new Error('no SigV4a key for these credentials');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const signers = /* @__PURE__ */ new WeakMap(); // ecdsa -> its signer, so the header and presign functions of one entry share one key cache
|
|
44
|
+
|
|
45
|
+
/** A signer from the string to sign to a hex DER ECDSA signature, with a cache of the keys built from credentials. Asking again for the same `ecdsa` returns the same signer. */
|
|
46
|
+
export function createAsymmetric(hmac, ecdsa) {
|
|
47
|
+
let sign = signers.get(ecdsa);
|
|
48
|
+
if (sign === undefined) signers.set(ecdsa, (sign = signerFor(hmac, ecdsa)));
|
|
49
|
+
return sign;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function signerFor(hmac, ecdsa) {
|
|
53
|
+
const keys = new Map();
|
|
54
|
+
return function sign(credentials, text) {
|
|
55
|
+
const id = `${credentials.accessKeyId}/${credentials.secretAccessKey}`;
|
|
56
|
+
let key = keys.get(id);
|
|
57
|
+
if (key === undefined) {
|
|
58
|
+
key = ecdsa.key(deriveP256(hmac, credentials.accessKeyId, credentials.secretAccessKey));
|
|
59
|
+
if (keys.size >= 1000) keys.delete(keys.keys().next().value);
|
|
60
|
+
keys.set(id, key);
|
|
61
|
+
}
|
|
62
|
+
return ecdsa.sign(key, text);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {Object} AsymmetricPrimitives
|
|
68
|
+
* @property {(data: string | Uint8Array) => string} hexHash - SHA-256 as lowercase hex
|
|
69
|
+
* @property {(key: string | Uint8Array, data: string) => Uint8Array} hmac - HMAC-SHA-256 as bytes
|
|
70
|
+
* @property {{ key: (scalar: Uint8Array) => unknown, sign: (key: any, text: string) => string }} ecdsa - the entry's ECDSA P-256 primitive; built here rather than in the entry so a bundle without SigV4a does not carry it
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The synchronous entries' SigV4a header signer.
|
|
75
|
+
* @param {AsymmetricPrimitives} primitives
|
|
76
|
+
* @returns {(request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => import('./base.js').AuthHeaders}
|
|
77
|
+
*/
|
|
78
|
+
export function createSignerV4a({ hexHash, hmac, ecdsa }) {
|
|
79
|
+
const sign = createAsymmetric(hmac, ecdsa);
|
|
80
|
+
return function getAuthHeadersV4a(request, credentials) {
|
|
81
|
+
const prepared = prepare(withRegionSet(request), credentials, payloadHashOf(request, hexHash), asymmetric);
|
|
82
|
+
return authorize(prepared, credentials.accessKeyId, sign(credentials, stringToSign(prepared, hexHash(prepared.canonicalRequest))));
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {AsymmetricPrimitives} primitives
|
|
88
|
+
* @returns {(request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string}
|
|
89
|
+
*/
|
|
90
|
+
export function createPresignerV4a({ hexHash, hmac, ecdsa }) {
|
|
91
|
+
const sign = createAsymmetric(hmac, ecdsa);
|
|
92
|
+
return function presignV4a(request, credentials) {
|
|
93
|
+
const prepared = preparePresign(request, credentials, payloadHashOf(request, hexHash), asymmetric, regionSetQuery(request));
|
|
94
|
+
return presignedUrl(prepared, sign(credentials, stringToSign(prepared, hexHash(prepared.canonicalRequest))));
|
|
95
|
+
};
|
|
96
|
+
}
|
package/src/web.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Cloudflare Workers without nodejs_compat, browsers, and anywhere else that has WebCrypto but not node:crypto.
|
|
2
|
+
// WebCrypto only offers promises, so this entry returns a promise; everything else matches the other entries.
|
|
3
|
+
import { payloadHashOf, prepare, stringToSign, authorize } from './base.js';
|
|
4
|
+
import { preparePresign, presignedUrl } from './presign.js';
|
|
5
|
+
import { asymmetric, withRegionSet, regionSetQuery, kdfInput, scalarFrom } from './v4a.js';
|
|
6
|
+
import { seedOf, chunkStringToSign, trailerStringToSign, canonicalTrailers, frameChunk, frameEnd } from './chunks.js';
|
|
7
|
+
import { dateHeader, frameEvent, eventStringToSign, eventScope } from './events.js';
|
|
8
|
+
|
|
9
|
+
const subtle = /* @__PURE__ */ (() => globalThis.crypto.subtle)();
|
|
10
|
+
const utf8 = /* @__PURE__ */ new TextEncoder();
|
|
11
|
+
|
|
12
|
+
const hexOfByte = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
|
|
13
|
+
function toHex(buffer) {
|
|
14
|
+
const bytes = new Uint8Array(buffer);
|
|
15
|
+
let out = '';
|
|
16
|
+
for (let i = 0; i < bytes.length; i++) out += hexOfByte[bytes[i]];
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const hexHash = async (data) => toHex(await subtle.digest('SHA-256', typeof data === 'string' ? utf8.encode(data) : data));
|
|
21
|
+
const importKey = (raw) => subtle.importKey('raw', raw, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
22
|
+
// the next key in the derivation chain: the HMAC of data, as a key
|
|
23
|
+
const deriveKey = async (key, data) => importKey(await subtle.sign('HMAC', key, utf8.encode(data)));
|
|
24
|
+
|
|
25
|
+
const signingKeys = new Map();
|
|
26
|
+
const maxSigningKeys = 1000;
|
|
27
|
+
let lastSecret;
|
|
28
|
+
let lastDate;
|
|
29
|
+
let lastRegion;
|
|
30
|
+
let lastService;
|
|
31
|
+
let lastKey;
|
|
32
|
+
|
|
33
|
+
async function getSigningKey(secretAccessKey, date, region, service) {
|
|
34
|
+
if (secretAccessKey === lastSecret && date === lastDate && region === lastRegion && service === lastService) return lastKey;
|
|
35
|
+
const cacheKey = `${date}\n${region}\n${service}\n${secretAccessKey}`;
|
|
36
|
+
let signingKey = signingKeys.get(cacheKey);
|
|
37
|
+
if (signingKey === undefined) {
|
|
38
|
+
const dateKey = await deriveKey(await importKey(utf8.encode('AWS4' + secretAccessKey)), date);
|
|
39
|
+
signingKey = await deriveKey(await deriveKey(await deriveKey(dateKey, region), service), 'aws4_request');
|
|
40
|
+
if (signingKeys.size >= maxSigningKeys) signingKeys.delete(signingKeys.keys().next().value);
|
|
41
|
+
signingKeys.set(cacheKey, signingKey);
|
|
42
|
+
}
|
|
43
|
+
lastSecret = secretAccessKey;
|
|
44
|
+
lastDate = date;
|
|
45
|
+
lastRegion = region;
|
|
46
|
+
lastService = service;
|
|
47
|
+
lastKey = signingKey;
|
|
48
|
+
return signingKey;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const signSymmetric = async (credentials, prepared, text) =>
|
|
52
|
+
toHex(await subtle.sign('HMAC', await getSigningKey(credentials.secretAccessKey, prepared.date, prepared.region, prepared.service), utf8.encode(text)));
|
|
53
|
+
|
|
54
|
+
// SigV4a: the derived scalar goes in as a PKCS#8 private key without the public point, which WebCrypto computes itself
|
|
55
|
+
const pkcs8 = /* @__PURE__ */ Uint8Array.from([
|
|
56
|
+
0x30, 0x41, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x27, 0x30,
|
|
57
|
+
0x25, 0x02, 0x01, 0x01, 0x04, 0x20,
|
|
58
|
+
]);
|
|
59
|
+
const asymmetricKeys = new Map();
|
|
60
|
+
async function getAsymmetricKey({ accessKeyId, secretAccessKey }) {
|
|
61
|
+
const id = `${accessKeyId}/${secretAccessKey}`;
|
|
62
|
+
let key = asymmetricKeys.get(id);
|
|
63
|
+
if (key === undefined) {
|
|
64
|
+
const root = await importKey(utf8.encode('AWS4A' + secretAccessKey));
|
|
65
|
+
for (let counter = 1; key === undefined; counter++) {
|
|
66
|
+
if (counter > 127) throw new Error('no SigV4a key for these credentials');
|
|
67
|
+
const d = scalarFrom(new Uint8Array(await subtle.sign('HMAC', root, utf8.encode(kdfInput(accessKeyId, counter)))));
|
|
68
|
+
if (d !== undefined) {
|
|
69
|
+
const der = new Uint8Array(pkcs8.length + 32);
|
|
70
|
+
der.set(pkcs8);
|
|
71
|
+
der.set(d, pkcs8.length);
|
|
72
|
+
key = await subtle.importKey('pkcs8', der, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (asymmetricKeys.size >= 1000) asymmetricKeys.delete(asymmetricKeys.keys().next().value);
|
|
76
|
+
asymmetricKeys.set(id, key);
|
|
77
|
+
}
|
|
78
|
+
return key;
|
|
79
|
+
}
|
|
80
|
+
// r and s as DER integers: leading zeros dropped, a zero byte in front when the top bit is set
|
|
81
|
+
function derInteger(bytes) {
|
|
82
|
+
let start = 0;
|
|
83
|
+
while (start < bytes.length - 1 && bytes[start] === 0) start++;
|
|
84
|
+
const body = bytes.subarray(start);
|
|
85
|
+
return body[0] & 0x80 ? [0x02, body.length + 1, 0, ...body] : [0x02, body.length, ...body];
|
|
86
|
+
}
|
|
87
|
+
async function signAsymmetric(credentials, text) {
|
|
88
|
+
const raw = new Uint8Array(await subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, await getAsymmetricKey(credentials), utf8.encode(text)));
|
|
89
|
+
const body = [...derInteger(raw.subarray(0, 32)), ...derInteger(raw.subarray(32))];
|
|
90
|
+
return toHex(Uint8Array.from([0x30, body.length, ...body]));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Same request and credentials as the other entries; resolves to the headers to send.
|
|
95
|
+
* @param {import('./base.js').SignRequest} request
|
|
96
|
+
* @param {import('./base.js').Credentials} credentials
|
|
97
|
+
* @returns {Promise<import('./base.js').AuthHeaders>}
|
|
98
|
+
*/
|
|
99
|
+
export async function getAuthHeaders(request, credentials) {
|
|
100
|
+
const prepared = prepare(request, credentials, await payloadHashOf(request, hexHash));
|
|
101
|
+
return authorize(prepared, credentials.accessKeyId, await signSymmetric(credentials, prepared, stringToSign(prepared, await hexHash(prepared.canonicalRequest))));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* SigV4a: the same, signed with ECDSA for the regions in `request.regionSet` ('us-east-1,us-west-2' or '*').
|
|
106
|
+
* @param {import('./base.js').SignRequest} request
|
|
107
|
+
* @param {import('./base.js').Credentials} credentials
|
|
108
|
+
* @returns {Promise<import('./base.js').AuthHeaders>}
|
|
109
|
+
*/
|
|
110
|
+
export async function getAuthHeadersV4a(request, credentials) {
|
|
111
|
+
const prepared = prepare(withRegionSet(request), credentials, await payloadHashOf(request, hexHash), asymmetric);
|
|
112
|
+
return authorize(prepared, credentials.accessKeyId, await signAsymmetric(credentials, stringToSign(prepared, await hexHash(prepared.canonicalRequest))));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* A presigned URL for the same request and credentials; `request.expires` is the lifetime in seconds (default 3600).
|
|
117
|
+
* @param {import('./base.js').SignRequest} request
|
|
118
|
+
* @param {import('./base.js').Credentials} credentials
|
|
119
|
+
* @returns {Promise<string>}
|
|
120
|
+
*/
|
|
121
|
+
export async function presign(request, credentials) {
|
|
122
|
+
const prepared = preparePresign(request, credentials, await payloadHashOf(request, hexHash));
|
|
123
|
+
return presignedUrl(prepared, await signSymmetric(credentials, prepared, stringToSign(prepared, await hexHash(prepared.canonicalRequest))));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* SigV4a presigned URL for the regions in `request.regionSet`.
|
|
128
|
+
* @param {import('./base.js').SignRequest} request
|
|
129
|
+
* @param {import('./base.js').Credentials} credentials
|
|
130
|
+
* @returns {Promise<string>}
|
|
131
|
+
*/
|
|
132
|
+
export async function presignV4a(request, credentials) {
|
|
133
|
+
const prepared = preparePresign(request, credentials, await payloadHashOf(request, hexHash), asymmetric, regionSetQuery(request));
|
|
134
|
+
return presignedUrl(prepared, await signAsymmetric(credentials, stringToSign(prepared, await hexHash(prepared.canonicalRequest))));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const signWith = async (key, text) => toHex(await subtle.sign('HMAC', key, utf8.encode(text)));
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Signs an aws-chunked upload chunk by chunk; the same shape as the other entries, with promises.
|
|
141
|
+
* @param {import('./base.js').AuthHeaders} authHeaders - the headers getAuthHeaders returned for the request
|
|
142
|
+
* @param {import('./base.js').Credentials} credentials
|
|
143
|
+
* @returns {{ chunk: (data: Uint8Array) => Promise<Uint8Array>, end: (trailers?: Record<string, string>) => Promise<Uint8Array> }}
|
|
144
|
+
*/
|
|
145
|
+
export function chunkSigner(authHeaders, credentials) {
|
|
146
|
+
const seed = seedOf(authHeaders);
|
|
147
|
+
let previous = seed.signature;
|
|
148
|
+
const keyPromise = getSigningKey(credentials.secretAccessKey, seed.date, seed.region, seed.service);
|
|
149
|
+
// chunks are signed one after the other: a call waits for the one before it
|
|
150
|
+
/** @type {Promise<any>} */
|
|
151
|
+
let turn = Promise.resolve();
|
|
152
|
+
const sign = (data) => (turn = turn.then(async () => (previous = await signWith(await keyPromise, chunkStringToSign(seed.datetime, seed.scope, previous, await hexHash(data))))));
|
|
153
|
+
return {
|
|
154
|
+
chunk: async (data) => (data.length === 0 ? data : frameChunk(await sign(data), data)),
|
|
155
|
+
async end(trailers) {
|
|
156
|
+
const signature = await sign(new Uint8Array(0));
|
|
157
|
+
if (trailers === undefined) return frameEnd(signature);
|
|
158
|
+
const text = canonicalTrailers(trailers);
|
|
159
|
+
return frameEnd(signature, text, await signWith(await keyPromise, trailerStringToSign(seed.datetime, seed.scope, signature, await hexHash(text))));
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Signs event-stream messages in order.
|
|
166
|
+
* @param {import('./base.js').AuthHeaders} authHeaders - the headers getAuthHeaders returned for the request
|
|
167
|
+
* @param {import('./base.js').Credentials} credentials
|
|
168
|
+
* @returns {{ sign: (message: Uint8Array, now?: number) => Promise<Uint8Array> }}
|
|
169
|
+
*/
|
|
170
|
+
export function eventSigner(authHeaders, credentials) {
|
|
171
|
+
const seed = seedOf(authHeaders);
|
|
172
|
+
let previous = seed.signature;
|
|
173
|
+
/** @type {Promise<any>} */
|
|
174
|
+
let turn = Promise.resolve();
|
|
175
|
+
return {
|
|
176
|
+
sign: (message, now = Date.now()) =>
|
|
177
|
+
(turn = turn.then(async () => {
|
|
178
|
+
const { datetime, scope, date } = eventScope(now, seed.region, seed.service);
|
|
179
|
+
const header = dateHeader(now);
|
|
180
|
+
previous = await signWith(
|
|
181
|
+
await getSigningKey(credentials.secretAccessKey, date, seed.region, seed.service),
|
|
182
|
+
eventStringToSign(datetime, scope, previous, await hexHash(header), await hexHash(message)),
|
|
183
|
+
);
|
|
184
|
+
return frameEvent(header, previous, message);
|
|
185
|
+
})),
|
|
186
|
+
};
|
|
187
|
+
}
|
package/types/base.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
export declare const emptyBodyHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
2
|
+
/** The scheme of a signature: its algorithm name and how the credential scope is written. SigV4a (v4a.js) has its own. */
|
|
3
|
+
export declare const symmetric: {
|
|
4
|
+
algorithm: string;
|
|
5
|
+
scope: (date: any, region: any, service: any) => string;
|
|
6
|
+
};
|
|
7
|
+
/** The canonical path for a service. */
|
|
8
|
+
export declare const canonicalPath: (service: any, pathname: any) => any;
|
|
9
|
+
/** search is url.search, which is '' or starts with '?' */
|
|
10
|
+
export declare function canonicalQuery(search: any): string;
|
|
11
|
+
/** inserts into the name-ordered lists, replacing an entry with the same name */
|
|
12
|
+
export declare function insert(names: any, values: any, name: any, value: any): void;
|
|
13
|
+
/**
|
|
14
|
+
* Refuses a signHeaders entry that cannot be signed as given: x-amz-date and authorization are set by the signer, `__proto__` cannot be a property of the
|
|
15
|
+
* returned headers, and a line break in a value would start another canonical header line. Returns the lowercase name.
|
|
16
|
+
*/
|
|
17
|
+
export declare function signHeaderName(name: any, value: any): any;
|
|
18
|
+
/** A header value as it is signed: trimmed, runs of spaces and tabs collapsed. */
|
|
19
|
+
export declare const headerValue: (value: any) => string;
|
|
20
|
+
export declare function amzDatetime(now: any): string;
|
|
21
|
+
export type SignRequest = {
|
|
22
|
+
/**
|
|
23
|
+
* - GET, HEAD, PUT, POST, DELETE
|
|
24
|
+
*/
|
|
25
|
+
method: string;
|
|
26
|
+
url: URL;
|
|
27
|
+
/**
|
|
28
|
+
* - default 's3'
|
|
29
|
+
*/
|
|
30
|
+
service?: string;
|
|
31
|
+
/**
|
|
32
|
+
* - for example us-west-2
|
|
33
|
+
*/
|
|
34
|
+
region?: string;
|
|
35
|
+
/**
|
|
36
|
+
* - hashed into x-amz-content-sha256; an empty or missing body is the hash of an empty string
|
|
37
|
+
*/
|
|
38
|
+
body?: string;
|
|
39
|
+
/**
|
|
40
|
+
* - send UNSIGNED-PAYLOAD as x-amz-content-sha256 instead of hashing the body
|
|
41
|
+
*/
|
|
42
|
+
unsignedPayload?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* - extra headers to sign and return; names are lowercased in the result
|
|
45
|
+
*/
|
|
46
|
+
signHeaders?: Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* - signing time in milliseconds since the epoch; defaults to the current time
|
|
49
|
+
*/
|
|
50
|
+
now?: number;
|
|
51
|
+
/**
|
|
52
|
+
* - presigned URLs: lifetime in seconds, default 3600
|
|
53
|
+
*/
|
|
54
|
+
expires?: number;
|
|
55
|
+
/**
|
|
56
|
+
* - SigV4a: the regions the signature is valid for, 'us-east-1,us-west-2' or '*'
|
|
57
|
+
*/
|
|
58
|
+
regionSet?: string;
|
|
59
|
+
};
|
|
60
|
+
export type Credentials = {
|
|
61
|
+
accessKeyId: string;
|
|
62
|
+
secretAccessKey: string;
|
|
63
|
+
/**
|
|
64
|
+
* - temporary credentials: signed and returned as x-amz-security-token
|
|
65
|
+
*/
|
|
66
|
+
sessionToken?: string;
|
|
67
|
+
};
|
|
68
|
+
export type AuthHeaders = Record<string, string>;
|
|
69
|
+
export type Primitives = {
|
|
70
|
+
/**
|
|
71
|
+
* - SHA-256 as lowercase hex
|
|
72
|
+
*/
|
|
73
|
+
hexHash: (data: string | Uint8Array) => string;
|
|
74
|
+
/**
|
|
75
|
+
* - HMAC-SHA-256 as bytes
|
|
76
|
+
*/
|
|
77
|
+
hmac: (key: string | Uint8Array, data: string) => Uint8Array;
|
|
78
|
+
/**
|
|
79
|
+
* - HMAC-SHA-256 as lowercase hex
|
|
80
|
+
*/
|
|
81
|
+
hmacHex: (key: Uint8Array, data: string) => string;
|
|
82
|
+
/**
|
|
83
|
+
* - the entry's one signing-key cache, shared by every symmetric signer
|
|
84
|
+
*/
|
|
85
|
+
getSigningKey: ReturnType<typeof createKeys>;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* @typedef {Object} SignRequest
|
|
89
|
+
* @property {string} method - GET, HEAD, PUT, POST, DELETE
|
|
90
|
+
* @property {URL} url
|
|
91
|
+
* @property {string} [service] - default 's3'
|
|
92
|
+
* @property {string} [region] - for example us-west-2
|
|
93
|
+
* @property {string} [body] - hashed into x-amz-content-sha256; an empty or missing body is the hash of an empty string
|
|
94
|
+
* @property {boolean} [unsignedPayload] - send UNSIGNED-PAYLOAD as x-amz-content-sha256 instead of hashing the body
|
|
95
|
+
* @property {Record<string, string>} [signHeaders] - extra headers to sign and return; names are lowercased in the result
|
|
96
|
+
* @property {number} [now] - signing time in milliseconds since the epoch; defaults to the current time
|
|
97
|
+
* @property {number} [expires] - presigned URLs: lifetime in seconds, default 3600
|
|
98
|
+
* @property {string} [regionSet] - SigV4a: the regions the signature is valid for, 'us-east-1,us-west-2' or '*'
|
|
99
|
+
*/
|
|
100
|
+
/**
|
|
101
|
+
* @typedef {Object} Credentials
|
|
102
|
+
* @property {string} accessKeyId
|
|
103
|
+
* @property {string} secretAccessKey
|
|
104
|
+
* @property {string} [sessionToken] - temporary credentials: signed and returned as x-amz-security-token
|
|
105
|
+
*/
|
|
106
|
+
/** @typedef {Record<string, string>} AuthHeaders */
|
|
107
|
+
/**
|
|
108
|
+
* @typedef {Object} Primitives
|
|
109
|
+
* @property {(data: string | Uint8Array) => string} hexHash - SHA-256 as lowercase hex
|
|
110
|
+
* @property {(key: string | Uint8Array, data: string) => Uint8Array} hmac - HMAC-SHA-256 as bytes
|
|
111
|
+
* @property {(key: Uint8Array, data: string) => string} hmacHex - HMAC-SHA-256 as lowercase hex
|
|
112
|
+
* @property {ReturnType<typeof createKeys>} getSigningKey - the entry's one signing-key cache, shared by every symmetric signer
|
|
113
|
+
*/
|
|
114
|
+
/** The payload hash for x-amz-content-sha256. The hash function may return a promise, which is then the result. */
|
|
115
|
+
export declare function payloadHashOf(request: any, hexHash: any): any;
|
|
116
|
+
/** Everything up to the hashing: the headers to return, the canonical request and the credential scope. */
|
|
117
|
+
export declare function prepare(request: any, credentials: any, payloadHash: any, scheme?: {
|
|
118
|
+
algorithm: string;
|
|
119
|
+
scope: (date: any, region: any, service: any) => string;
|
|
120
|
+
}): {
|
|
121
|
+
headers: {
|
|
122
|
+
'x-amz-date': string;
|
|
123
|
+
'x-amz-content-sha256': any;
|
|
124
|
+
} | {
|
|
125
|
+
'x-amz-content-sha256'?: undefined;
|
|
126
|
+
'x-amz-date': string;
|
|
127
|
+
};
|
|
128
|
+
canonicalRequest: string;
|
|
129
|
+
datetime: string;
|
|
130
|
+
date: string;
|
|
131
|
+
region: any;
|
|
132
|
+
service: any;
|
|
133
|
+
credentialScope: string;
|
|
134
|
+
signedHeaders: any;
|
|
135
|
+
algorithm: string;
|
|
136
|
+
};
|
|
137
|
+
export declare const stringToSign: (prepared: any, canonicalRequestHash: any) => string;
|
|
138
|
+
/** Adds the authorization header to the headers to return. */
|
|
139
|
+
export declare function authorize(prepared: any, accessKeyId: any, signature: any): any;
|
|
140
|
+
export declare function createKeys(hmac: any): (secretAccessKey: any, date: any, region: any, service: any) => any;
|
|
141
|
+
/**
|
|
142
|
+
* @param {Primitives} primitives
|
|
143
|
+
* @returns {(request: SignRequest, credentials: Credentials) => AuthHeaders}
|
|
144
|
+
*/
|
|
145
|
+
export declare function createSigner({ hexHash, hmacHex, getSigningKey }: Primitives): (request: SignRequest, credentials: Credentials) => AuthHeaders;
|
package/types/bun.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const getAuthHeaders: (request: import("./base.js").SignRequest, credentials: import("./base.js").Credentials) => import("./base.js").AuthHeaders;
|
|
2
|
+
export declare const presign: (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string;
|
|
3
|
+
export declare const getAuthHeadersV4a: (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => import('./base.js').AuthHeaders;
|
|
4
|
+
export declare const presignV4a: (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string;
|
|
5
|
+
export declare const chunkSigner: (authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => import("./chunks.js").ChunkSigner;
|
|
6
|
+
export declare const eventSigner: (authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => import("./events.js").EventSigner;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const utf8: TextEncoder;
|
|
2
|
+
/** The seed signature, timestamp and scope of a signed request, from the headers getAuthHeaders returned. */
|
|
3
|
+
export declare function seedOf(authHeaders: any): {
|
|
4
|
+
signature: string;
|
|
5
|
+
datetime: any;
|
|
6
|
+
date: string;
|
|
7
|
+
region: string;
|
|
8
|
+
service: string;
|
|
9
|
+
scope: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const chunkStringToSign: (datetime: any, scope: any, previous: any, payloadHash: any) => string;
|
|
12
|
+
export declare const trailerStringToSign: (datetime: any, scope: any, previous: any, trailerHash: any) => string;
|
|
13
|
+
/** Trailing headers in canonical form: lowercase names in name order, each line ending in a newline. */
|
|
14
|
+
export declare function canonicalTrailers(trailers: any): string;
|
|
15
|
+
/** The bytes of one chunk on the wire: size in hex, signature, the data. */
|
|
16
|
+
export declare function frameChunk(signature: any, data: any): Uint8Array<any>;
|
|
17
|
+
/** The last chunk (no data), with the signed trailing headers when there are any. */
|
|
18
|
+
export declare function frameEnd(signature: any, trailers: any, trailerSignature: any): Uint8Array<ArrayBuffer>;
|
|
19
|
+
export type ChunkSigner = {
|
|
20
|
+
/**
|
|
21
|
+
* - the bytes to send for one chunk of the body
|
|
22
|
+
*/
|
|
23
|
+
chunk: (data: Uint8Array) => Uint8Array;
|
|
24
|
+
/**
|
|
25
|
+
* - the last chunk, with the signed trailing headers when there are any
|
|
26
|
+
*/
|
|
27
|
+
end: (trailers?: Record<string, string>) => Uint8Array;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} ChunkSigner
|
|
31
|
+
* @property {(data: Uint8Array) => Uint8Array} chunk - the bytes to send for one chunk of the body
|
|
32
|
+
* @property {(trailers?: Record<string, string>) => Uint8Array} end - the last chunk, with the signed trailing headers when there are any
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('./base.js').Primitives} primitives
|
|
36
|
+
* @returns {(authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => ChunkSigner}
|
|
37
|
+
*/
|
|
38
|
+
export declare function createChunkSigner({ hexHash, hmacHex, getSigningKey }: import('./base.js').Primitives): (authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => ChunkSigner;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The :date header of an event: name, type 8 (timestamp), milliseconds since the epoch as 8 bytes. */
|
|
2
|
+
export declare function dateHeader(ms: any): Uint8Array<ArrayBuffer>;
|
|
3
|
+
/** The signed event: prelude, the :date and :chunk-signature headers, the inner message as payload, checksums. */
|
|
4
|
+
export declare function frameEvent(dateBytes: any, signatureHex: any, inner: any): Uint8Array<any>;
|
|
5
|
+
export declare const eventStringToSign: (datetime: any, scope: any, previous: any, headersHash: any, payloadHash: any) => string;
|
|
6
|
+
export declare const eventScope: (ms: any, region: any, service: any) => {
|
|
7
|
+
datetime: string;
|
|
8
|
+
scope: string;
|
|
9
|
+
date: string;
|
|
10
|
+
};
|
|
11
|
+
export type EventSigner = {
|
|
12
|
+
/**
|
|
13
|
+
* - the signed event to send for one encoded message
|
|
14
|
+
*/
|
|
15
|
+
sign: (message: Uint8Array, now?: number) => Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} EventSigner
|
|
19
|
+
* @property {(message: Uint8Array, now?: number) => Uint8Array} sign - the signed event to send for one encoded message
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* @param {import('./base.js').Primitives} primitives
|
|
23
|
+
* @returns {(authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => EventSigner}
|
|
24
|
+
*/
|
|
25
|
+
export declare function createEventSigner({ hexHash, hmacHex, getSigningKey }: import('./base.js').Primitives): (authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => EventSigner;
|
package/types/node.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const getAuthHeaders: (request: import("./base.js").SignRequest, credentials: import("./base.js").Credentials) => import("./base.js").AuthHeaders;
|
|
2
|
+
export declare const presign: (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string;
|
|
3
|
+
export declare const getAuthHeadersV4a: (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => import('./base.js').AuthHeaders;
|
|
4
|
+
export declare const presignV4a: (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string;
|
|
5
|
+
export declare const chunkSigner: (authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => import("./chunks.js").ChunkSigner;
|
|
6
|
+
export declare const eventSigner: (authHeaders: import('./base.js').AuthHeaders, credentials: import('./base.js').Credentials) => import("./events.js").EventSigner;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const encodeQueryPart: (text: any) => string;
|
|
2
|
+
/**
|
|
3
|
+
* Everything up to the hashing for a presigned URL. `request.expires` is the lifetime in seconds (default 3600). Only the host and request.signHeaders are
|
|
4
|
+
* signed; S3 URLs are always UNSIGNED-PAYLOAD, other services sign the body hash. `extra` is appended to the authentication parameters.
|
|
5
|
+
*/
|
|
6
|
+
export declare function preparePresign(request: any, credentials: any, payloadHash: any, scheme?: {
|
|
7
|
+
algorithm: string;
|
|
8
|
+
scope: (date: any, region: any, service: any) => string;
|
|
9
|
+
}, extra?: string): {
|
|
10
|
+
canonicalRequest: string;
|
|
11
|
+
datetime: string;
|
|
12
|
+
date: string;
|
|
13
|
+
region: any;
|
|
14
|
+
service: any;
|
|
15
|
+
credentialScope: string;
|
|
16
|
+
signedHeaders: string;
|
|
17
|
+
algorithm: string;
|
|
18
|
+
base: any;
|
|
19
|
+
query: string;
|
|
20
|
+
};
|
|
21
|
+
/** The URL to hand out: the original URL with the authentication query parameters and the signature appended. */
|
|
22
|
+
export declare const presignedUrl: (prepared: any, signature: any) => string;
|
|
23
|
+
/**
|
|
24
|
+
* @param {import('./base.js').Primitives} primitives
|
|
25
|
+
* @returns {(request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string}
|
|
26
|
+
*/
|
|
27
|
+
export declare function createPresigner({ hexHash, hmacHex, getSigningKey }: import('./base.js').Primitives): (request: import('./base.js').SignRequest, credentials: import('./base.js').Credentials) => string;
|