@mysten/signers 1.0.2 → 1.0.4
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 +33 -0
- package/README.md +1 -1
- package/dist/aws/index.d.mts +1 -3
- package/dist/aws/index.mjs +2 -2
- package/dist/gcp/index.d.mts +1 -2
- package/dist/gcp/index.mjs +2 -2
- package/dist/ledger/index.d.mts +1 -74
- package/dist/ledger/index.mjs +2 -109
- package/dist/webcrypto/index.d.mts +1 -32
- package/dist/webcrypto/index.mjs +2 -69
- package/package.json +9 -19
- package/src/aws/index.ts +1 -6
- package/src/gcp/index.ts +1 -6
- package/src/ledger/index.ts +1 -160
- package/src/webcrypto/index.ts +1 -108
- package/dist/aws/aws-client.d.mts +0 -48
- package/dist/aws/aws-client.d.mts.map +0 -1
- package/dist/aws/aws-client.mjs +0 -46
- package/dist/aws/aws-client.mjs.map +0 -1
- package/dist/aws/aws-kms-signer.d.mts +0 -63
- package/dist/aws/aws-kms-signer.d.mts.map +0 -1
- package/dist/aws/aws-kms-signer.mjs +0 -78
- package/dist/aws/aws-kms-signer.mjs.map +0 -1
- package/dist/aws/aws4fetch.d.mts +0 -62
- package/dist/aws/aws4fetch.d.mts.map +0 -1
- package/dist/aws/aws4fetch.mjs +0 -313
- package/dist/aws/aws4fetch.mjs.map +0 -1
- package/dist/gcp/gcp-kms-client.d.mts +0 -71
- package/dist/gcp/gcp-kms-client.d.mts.map +0 -1
- package/dist/gcp/gcp-kms-client.mjs +0 -104
- package/dist/gcp/gcp-kms-client.mjs.map +0 -1
- package/dist/ledger/index.d.mts.map +0 -1
- package/dist/ledger/index.mjs.map +0 -1
- package/dist/ledger/objects.d.mts +0 -10
- package/dist/ledger/objects.d.mts.map +0 -1
- package/dist/ledger/objects.mjs +0 -16
- package/dist/ledger/objects.mjs.map +0 -1
- package/dist/utils/utils.mjs +0 -71
- package/dist/utils/utils.mjs.map +0 -1
- package/dist/webcrypto/index.d.mts.map +0 -1
- package/dist/webcrypto/index.mjs.map +0 -1
- package/src/aws/aws-client.ts +0 -107
- package/src/aws/aws-kms-signer.ts +0 -102
- package/src/aws/aws4fetch.ts +0 -502
- package/src/gcp/gcp-kms-client.ts +0 -156
- package/src/ledger/objects.ts +0 -32
- package/src/utils/utils.ts +0 -127
package/src/aws/aws4fetch.ts
DELETED
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Original implementation https://github.com/mhart/aws4fetch, inlined to reduce external dependencies
|
|
6
|
-
* @license MIT <https://opensource.org/licenses/MIT>
|
|
7
|
-
* @copyright Michael Hart 2024
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const encoder = new TextEncoder();
|
|
11
|
-
|
|
12
|
-
/** @type {Record<string, string>} */
|
|
13
|
-
const HOST_SERVICES: Record<string, string> = {
|
|
14
|
-
appstream2: 'appstream',
|
|
15
|
-
cloudhsmv2: 'cloudhsm',
|
|
16
|
-
email: 'ses',
|
|
17
|
-
marketplace: 'aws-marketplace',
|
|
18
|
-
mobile: 'AWSMobileHubService',
|
|
19
|
-
pinpoint: 'mobiletargeting',
|
|
20
|
-
queue: 'sqs',
|
|
21
|
-
'git-codecommit': 'codecommit',
|
|
22
|
-
'mturk-requester-sandbox': 'mturk-requester',
|
|
23
|
-
'personalize-runtime': 'personalize',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// https://github.com/aws/aws-sdk-js/blob/cc29728c1c4178969ebabe3bbe6b6f3159436394/lib/signers/v4.js#L190-L198
|
|
27
|
-
const UNSIGNABLE_HEADERS = new Set([
|
|
28
|
-
'authorization',
|
|
29
|
-
'content-type',
|
|
30
|
-
'content-length',
|
|
31
|
-
'user-agent',
|
|
32
|
-
'presigned-expires',
|
|
33
|
-
'expect',
|
|
34
|
-
'x-amzn-trace-id',
|
|
35
|
-
'range',
|
|
36
|
-
'connection',
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
type AwsRequestInit = RequestInit & {
|
|
40
|
-
aws?: {
|
|
41
|
-
accessKeyId?: string;
|
|
42
|
-
secretAccessKey?: string;
|
|
43
|
-
sessionToken?: string;
|
|
44
|
-
service?: string;
|
|
45
|
-
region?: string;
|
|
46
|
-
cache?: Map<string, ArrayBuffer>;
|
|
47
|
-
datetime?: string;
|
|
48
|
-
signQuery?: boolean;
|
|
49
|
-
appendSessionToken?: boolean;
|
|
50
|
-
allHeaders?: boolean;
|
|
51
|
-
singleEncode?: boolean;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export class AwsClient {
|
|
56
|
-
accessKeyId: string;
|
|
57
|
-
secretAccessKey: string;
|
|
58
|
-
sessionToken: string | undefined;
|
|
59
|
-
service: string | undefined;
|
|
60
|
-
region: string | undefined;
|
|
61
|
-
cache: Map<any, any>;
|
|
62
|
-
retries: number;
|
|
63
|
-
initRetryMs: number;
|
|
64
|
-
/**
|
|
65
|
-
* @param {} options
|
|
66
|
-
*/
|
|
67
|
-
constructor({
|
|
68
|
-
accessKeyId,
|
|
69
|
-
secretAccessKey,
|
|
70
|
-
sessionToken,
|
|
71
|
-
service,
|
|
72
|
-
region,
|
|
73
|
-
cache,
|
|
74
|
-
retries,
|
|
75
|
-
initRetryMs,
|
|
76
|
-
}: {
|
|
77
|
-
accessKeyId: string;
|
|
78
|
-
secretAccessKey: string;
|
|
79
|
-
sessionToken?: string;
|
|
80
|
-
service?: string;
|
|
81
|
-
region?: string;
|
|
82
|
-
cache?: Map<string, ArrayBuffer>;
|
|
83
|
-
retries?: number;
|
|
84
|
-
initRetryMs?: number;
|
|
85
|
-
}) {
|
|
86
|
-
if (accessKeyId == null) throw new TypeError('accessKeyId is a required option');
|
|
87
|
-
if (secretAccessKey == null) throw new TypeError('secretAccessKey is a required option');
|
|
88
|
-
this.accessKeyId = accessKeyId;
|
|
89
|
-
this.secretAccessKey = secretAccessKey;
|
|
90
|
-
this.sessionToken = sessionToken;
|
|
91
|
-
this.service = service;
|
|
92
|
-
this.region = region;
|
|
93
|
-
/** @type {Map<string, ArrayBuffer>} */
|
|
94
|
-
this.cache = cache || new Map();
|
|
95
|
-
this.retries = retries != null ? retries : 10; // Up to 25.6 secs
|
|
96
|
-
this.initRetryMs = initRetryMs || 50;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async sign(input: Request | { toString: () => string }, init: AwsRequestInit): Promise<Request> {
|
|
100
|
-
if (input instanceof Request) {
|
|
101
|
-
const { method, url, headers, body } = input;
|
|
102
|
-
init = Object.assign({ method, url, headers }, init);
|
|
103
|
-
if (init.body == null && headers.has('Content-Type')) {
|
|
104
|
-
init.body =
|
|
105
|
-
body != null && headers.has('X-Amz-Content-Sha256')
|
|
106
|
-
? body
|
|
107
|
-
: await input.clone().arrayBuffer();
|
|
108
|
-
}
|
|
109
|
-
input = url;
|
|
110
|
-
}
|
|
111
|
-
const signer = new AwsV4Signer(
|
|
112
|
-
Object.assign({ url: input.toString() }, init, this, init && init.aws),
|
|
113
|
-
);
|
|
114
|
-
const signed = Object.assign({}, init, await signer.sign());
|
|
115
|
-
delete signed.aws;
|
|
116
|
-
try {
|
|
117
|
-
return new Request(signed.url.toString(), signed);
|
|
118
|
-
} catch (e) {
|
|
119
|
-
if (e instanceof TypeError) {
|
|
120
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1360943
|
|
121
|
-
return new Request(signed.url.toString(), Object.assign({ duplex: 'half' }, signed));
|
|
122
|
-
}
|
|
123
|
-
throw e;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @param {Request | { toString: () => string }} input
|
|
129
|
-
* @param {?AwsRequestInit} [init]
|
|
130
|
-
* @returns {Promise<Response>}
|
|
131
|
-
*/
|
|
132
|
-
async fetch(input: Request | { toString: () => string }, init: AwsRequestInit) {
|
|
133
|
-
for (let i = 0; i <= this.retries; i++) {
|
|
134
|
-
const fetched = fetch(await this.sign(input, init));
|
|
135
|
-
if (i === this.retries) {
|
|
136
|
-
return fetched; // No need to await if we're returning anyway
|
|
137
|
-
}
|
|
138
|
-
const res = await fetched;
|
|
139
|
-
if (res.status < 500 && res.status !== 429) {
|
|
140
|
-
return res;
|
|
141
|
-
}
|
|
142
|
-
await new Promise((resolve) =>
|
|
143
|
-
setTimeout(resolve, Math.random() * this.initRetryMs * Math.pow(2, i)),
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
throw new Error('An unknown error occurred, ensure retries is not negative');
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export class AwsV4Signer {
|
|
151
|
-
method: any;
|
|
152
|
-
url: URL;
|
|
153
|
-
headers: Headers;
|
|
154
|
-
body: any;
|
|
155
|
-
accessKeyId: any;
|
|
156
|
-
secretAccessKey: any;
|
|
157
|
-
sessionToken: any;
|
|
158
|
-
service: any;
|
|
159
|
-
region: any;
|
|
160
|
-
cache: any;
|
|
161
|
-
datetime: any;
|
|
162
|
-
signQuery: any;
|
|
163
|
-
appendSessionToken: any;
|
|
164
|
-
signableHeaders: any[];
|
|
165
|
-
signedHeaders: any;
|
|
166
|
-
canonicalHeaders: any;
|
|
167
|
-
credentialString: string;
|
|
168
|
-
encodedPath: string;
|
|
169
|
-
encodedSearch: string;
|
|
170
|
-
/**
|
|
171
|
-
* @param {} options
|
|
172
|
-
*/
|
|
173
|
-
constructor({
|
|
174
|
-
method,
|
|
175
|
-
url,
|
|
176
|
-
headers,
|
|
177
|
-
body,
|
|
178
|
-
accessKeyId,
|
|
179
|
-
secretAccessKey,
|
|
180
|
-
sessionToken,
|
|
181
|
-
service,
|
|
182
|
-
region,
|
|
183
|
-
cache,
|
|
184
|
-
datetime,
|
|
185
|
-
signQuery,
|
|
186
|
-
appendSessionToken,
|
|
187
|
-
allHeaders,
|
|
188
|
-
singleEncode,
|
|
189
|
-
}: {
|
|
190
|
-
method?: string;
|
|
191
|
-
url: string;
|
|
192
|
-
headers?: HeadersInit;
|
|
193
|
-
body?: BodyInit | null;
|
|
194
|
-
accessKeyId: string;
|
|
195
|
-
secretAccessKey: string;
|
|
196
|
-
sessionToken?: string;
|
|
197
|
-
service?: string;
|
|
198
|
-
region?: string;
|
|
199
|
-
cache?: Map<string, ArrayBuffer>;
|
|
200
|
-
datetime?: string;
|
|
201
|
-
signQuery?: boolean;
|
|
202
|
-
appendSessionToken?: boolean;
|
|
203
|
-
allHeaders?: boolean;
|
|
204
|
-
singleEncode?: boolean;
|
|
205
|
-
}) {
|
|
206
|
-
if (url == null) throw new TypeError('url is a required option');
|
|
207
|
-
if (accessKeyId == null) throw new TypeError('accessKeyId is a required option');
|
|
208
|
-
if (secretAccessKey == null) throw new TypeError('secretAccessKey is a required option');
|
|
209
|
-
|
|
210
|
-
this.method = method || (body ? 'POST' : 'GET');
|
|
211
|
-
this.url = new URL(url);
|
|
212
|
-
this.headers = new Headers(headers || {});
|
|
213
|
-
this.body = body;
|
|
214
|
-
|
|
215
|
-
this.accessKeyId = accessKeyId;
|
|
216
|
-
this.secretAccessKey = secretAccessKey;
|
|
217
|
-
this.sessionToken = sessionToken;
|
|
218
|
-
|
|
219
|
-
let guessedService, guessedRegion;
|
|
220
|
-
if (!service || !region) {
|
|
221
|
-
[guessedService, guessedRegion] = guessServiceRegion(this.url, this.headers);
|
|
222
|
-
}
|
|
223
|
-
this.service = service || guessedService || '';
|
|
224
|
-
this.region = region || guessedRegion || 'us-east-1';
|
|
225
|
-
|
|
226
|
-
/** @type {Map<string, ArrayBuffer>} */
|
|
227
|
-
this.cache = cache || new Map();
|
|
228
|
-
this.datetime = datetime || new Date().toISOString().replace(/[:-]|\.\d{3}/g, '');
|
|
229
|
-
this.signQuery = signQuery;
|
|
230
|
-
this.appendSessionToken = appendSessionToken || this.service === 'iotdevicegateway';
|
|
231
|
-
|
|
232
|
-
this.headers.delete('Host'); // Can't be set in insecure env anyway
|
|
233
|
-
|
|
234
|
-
if (this.service === 's3' && !this.signQuery && !this.headers.has('X-Amz-Content-Sha256')) {
|
|
235
|
-
this.headers.set('X-Amz-Content-Sha256', 'UNSIGNED-PAYLOAD');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const params = this.signQuery ? this.url.searchParams : this.headers;
|
|
239
|
-
|
|
240
|
-
params.set('X-Amz-Date', this.datetime);
|
|
241
|
-
if (this.sessionToken && !this.appendSessionToken) {
|
|
242
|
-
params.set('X-Amz-Security-Token', this.sessionToken);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// headers are always lowercase in keys()
|
|
246
|
-
|
|
247
|
-
this.signableHeaders = ['host', ...((this.headers as any).keys() as string[])]
|
|
248
|
-
.filter((header) => allHeaders || !UNSIGNABLE_HEADERS.has(header))
|
|
249
|
-
.sort();
|
|
250
|
-
|
|
251
|
-
this.signedHeaders = this.signableHeaders.join(';');
|
|
252
|
-
|
|
253
|
-
// headers are always trimmed:
|
|
254
|
-
// https://fetch.spec.whatwg.org/#concept-header-value-normalize
|
|
255
|
-
this.canonicalHeaders = this.signableHeaders
|
|
256
|
-
.map(
|
|
257
|
-
(header) =>
|
|
258
|
-
header +
|
|
259
|
-
':' +
|
|
260
|
-
(header === 'host'
|
|
261
|
-
? this.url.host
|
|
262
|
-
: (this.headers.get(header) || '').replace(/\s+/g, ' ')),
|
|
263
|
-
)
|
|
264
|
-
.join('\n');
|
|
265
|
-
|
|
266
|
-
this.credentialString = [
|
|
267
|
-
this.datetime.slice(0, 8),
|
|
268
|
-
this.region,
|
|
269
|
-
this.service,
|
|
270
|
-
'aws4_request',
|
|
271
|
-
].join('/');
|
|
272
|
-
|
|
273
|
-
if (this.signQuery) {
|
|
274
|
-
if (this.service === 's3' && !params.has('X-Amz-Expires')) {
|
|
275
|
-
params.set('X-Amz-Expires', '86400'); // 24 hours
|
|
276
|
-
}
|
|
277
|
-
params.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256');
|
|
278
|
-
params.set('X-Amz-Credential', this.accessKeyId + '/' + this.credentialString);
|
|
279
|
-
params.set('X-Amz-SignedHeaders', this.signedHeaders);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (this.service === 's3') {
|
|
283
|
-
try {
|
|
284
|
-
this.encodedPath = decodeURIComponent(this.url.pathname.replace(/\+/g, ' '));
|
|
285
|
-
} catch {
|
|
286
|
-
this.encodedPath = this.url.pathname;
|
|
287
|
-
}
|
|
288
|
-
} else {
|
|
289
|
-
this.encodedPath = this.url.pathname.replace(/\/+/g, '/');
|
|
290
|
-
}
|
|
291
|
-
if (!singleEncode) {
|
|
292
|
-
this.encodedPath = encodeURIComponent(this.encodedPath).replace(/%2F/g, '/');
|
|
293
|
-
}
|
|
294
|
-
this.encodedPath = encodeRfc3986(this.encodedPath);
|
|
295
|
-
|
|
296
|
-
const seenKeys = new Set();
|
|
297
|
-
this.encodedSearch = [...this.url.searchParams]
|
|
298
|
-
.filter(([k]) => {
|
|
299
|
-
if (!k) return false; // no empty keys
|
|
300
|
-
if (this.service === 's3') {
|
|
301
|
-
if (seenKeys.has(k)) return false; // first val only for S3
|
|
302
|
-
seenKeys.add(k);
|
|
303
|
-
}
|
|
304
|
-
return true;
|
|
305
|
-
})
|
|
306
|
-
.map((pair) => pair.map((p) => encodeRfc3986(encodeURIComponent(p))))
|
|
307
|
-
.sort(([k1, v1], [k2, v2]) => (k1 < k2 ? -1 : k1 > k2 ? 1 : v1 < v2 ? -1 : v1 > v2 ? 1 : 0))
|
|
308
|
-
.map((pair) => pair.join('='))
|
|
309
|
-
.join('&');
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* @returns {Promise<{
|
|
314
|
-
* method: string
|
|
315
|
-
* url: URL
|
|
316
|
-
* headers: Headers
|
|
317
|
-
* body?: BodyInit | null
|
|
318
|
-
* }>}
|
|
319
|
-
*/
|
|
320
|
-
async sign() {
|
|
321
|
-
if (this.signQuery) {
|
|
322
|
-
this.url.searchParams.set('X-Amz-Signature', await this.signature());
|
|
323
|
-
if (this.sessionToken && this.appendSessionToken) {
|
|
324
|
-
this.url.searchParams.set('X-Amz-Security-Token', this.sessionToken);
|
|
325
|
-
}
|
|
326
|
-
} else {
|
|
327
|
-
this.headers.set('Authorization', await this.authHeader());
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
method: this.method,
|
|
332
|
-
url: this.url,
|
|
333
|
-
headers: this.headers,
|
|
334
|
-
body: this.body,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* @returns {Promise<string>}
|
|
340
|
-
*/
|
|
341
|
-
async authHeader() {
|
|
342
|
-
return [
|
|
343
|
-
'AWS4-HMAC-SHA256 Credential=' + this.accessKeyId + '/' + this.credentialString,
|
|
344
|
-
'SignedHeaders=' + this.signedHeaders,
|
|
345
|
-
'Signature=' + (await this.signature()),
|
|
346
|
-
].join(', ');
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* @returns {Promise<string>}
|
|
351
|
-
*/
|
|
352
|
-
async signature() {
|
|
353
|
-
const date = this.datetime.slice(0, 8);
|
|
354
|
-
const cacheKey = [this.secretAccessKey, date, this.region, this.service].join();
|
|
355
|
-
let kCredentials = this.cache.get(cacheKey);
|
|
356
|
-
if (!kCredentials) {
|
|
357
|
-
const kDate = await hmac('AWS4' + this.secretAccessKey, date);
|
|
358
|
-
const kRegion = await hmac(kDate, this.region);
|
|
359
|
-
const kService = await hmac(kRegion, this.service);
|
|
360
|
-
kCredentials = await hmac(kService, 'aws4_request');
|
|
361
|
-
this.cache.set(cacheKey, kCredentials);
|
|
362
|
-
}
|
|
363
|
-
return buf2hex(await hmac(kCredentials, await this.stringToSign()));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* @returns {Promise<string>}
|
|
368
|
-
*/
|
|
369
|
-
async stringToSign() {
|
|
370
|
-
return [
|
|
371
|
-
'AWS4-HMAC-SHA256',
|
|
372
|
-
this.datetime,
|
|
373
|
-
this.credentialString,
|
|
374
|
-
buf2hex(await hash(await this.canonicalString())),
|
|
375
|
-
].join('\n');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* @returns {Promise<string>}
|
|
380
|
-
*/
|
|
381
|
-
async canonicalString() {
|
|
382
|
-
return [
|
|
383
|
-
this.method.toUpperCase(),
|
|
384
|
-
this.encodedPath,
|
|
385
|
-
this.encodedSearch,
|
|
386
|
-
this.canonicalHeaders + '\n',
|
|
387
|
-
this.signedHeaders,
|
|
388
|
-
await this.hexBodyHash(),
|
|
389
|
-
].join('\n');
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* @returns {Promise<string>}
|
|
394
|
-
*/
|
|
395
|
-
async hexBodyHash() {
|
|
396
|
-
let hashHeader =
|
|
397
|
-
this.headers.get('X-Amz-Content-Sha256') ||
|
|
398
|
-
(this.service === 's3' && this.signQuery ? 'UNSIGNED-PAYLOAD' : null);
|
|
399
|
-
if (hashHeader == null) {
|
|
400
|
-
if (this.body && typeof this.body !== 'string' && !('byteLength' in this.body)) {
|
|
401
|
-
throw new Error(
|
|
402
|
-
'body must be a string, ArrayBuffer or ArrayBufferView, unless you include the X-Amz-Content-Sha256 header',
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
hashHeader = buf2hex(await hash(this.body || ''));
|
|
406
|
-
}
|
|
407
|
-
return hashHeader;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* @param {string | BufferSource} key
|
|
413
|
-
* @param {string} string
|
|
414
|
-
* @returns {Promise<ArrayBuffer>}
|
|
415
|
-
*/
|
|
416
|
-
async function hmac(key: string | BufferSource, string: string): Promise<ArrayBuffer> {
|
|
417
|
-
const cryptoKey = await crypto.subtle.importKey(
|
|
418
|
-
'raw',
|
|
419
|
-
typeof key === 'string' ? encoder.encode(key) : key,
|
|
420
|
-
{ name: 'HMAC', hash: { name: 'SHA-256' } },
|
|
421
|
-
false,
|
|
422
|
-
['sign'],
|
|
423
|
-
);
|
|
424
|
-
return crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(string));
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
async function hash(content: string | ArrayBufferLike): Promise<ArrayBuffer> {
|
|
428
|
-
return crypto.subtle.digest(
|
|
429
|
-
'SHA-256',
|
|
430
|
-
(typeof content === 'string' ? encoder.encode(content) : content) as ArrayBuffer,
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const HEX_CHARS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
|
435
|
-
|
|
436
|
-
function buf2hex(arrayBuffer: ArrayBufferLike): string {
|
|
437
|
-
const buffer = new Uint8Array(arrayBuffer);
|
|
438
|
-
let out = '';
|
|
439
|
-
for (let idx = 0; idx < buffer.length; idx++) {
|
|
440
|
-
const n = buffer[idx];
|
|
441
|
-
|
|
442
|
-
out += HEX_CHARS[(n >>> 4) & 0xf];
|
|
443
|
-
out += HEX_CHARS[n & 0xf];
|
|
444
|
-
}
|
|
445
|
-
return out;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function encodeRfc3986(urlEncodedStr: string): string {
|
|
449
|
-
return urlEncodedStr.replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
function guessServiceRegion(url: URL, headers: Headers): [string, string] {
|
|
453
|
-
const { hostname, pathname } = url;
|
|
454
|
-
|
|
455
|
-
if (hostname.endsWith('.on.aws')) {
|
|
456
|
-
const match = hostname.match(/^[^.]{1,63}\.lambda-url\.([^.]{1,63})\.on\.aws$/);
|
|
457
|
-
return match != null ? ['lambda', match[1] || ''] : ['', ''];
|
|
458
|
-
}
|
|
459
|
-
if (hostname.endsWith('.r2.cloudflarestorage.com')) {
|
|
460
|
-
return ['s3', 'auto'];
|
|
461
|
-
}
|
|
462
|
-
if (hostname.endsWith('.backblazeb2.com')) {
|
|
463
|
-
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/);
|
|
464
|
-
return match != null ? ['s3', match[1] || ''] : ['', ''];
|
|
465
|
-
}
|
|
466
|
-
const match = hostname
|
|
467
|
-
.replace('dualstack.', '')
|
|
468
|
-
.match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/);
|
|
469
|
-
let service = (match && match[1]) || '';
|
|
470
|
-
let region = match && match[2];
|
|
471
|
-
|
|
472
|
-
if (region === 'us-gov') {
|
|
473
|
-
region = 'us-gov-west-1';
|
|
474
|
-
} else if (region === 's3' || region === 's3-accelerate') {
|
|
475
|
-
region = 'us-east-1';
|
|
476
|
-
service = 's3';
|
|
477
|
-
} else if (service === 'iot') {
|
|
478
|
-
if (hostname.startsWith('iot.')) {
|
|
479
|
-
service = 'execute-api';
|
|
480
|
-
} else if (hostname.startsWith('data.jobs.iot.')) {
|
|
481
|
-
service = 'iot-jobs-data';
|
|
482
|
-
} else {
|
|
483
|
-
service = pathname === '/mqtt' ? 'iotdevicegateway' : 'iotdata';
|
|
484
|
-
}
|
|
485
|
-
} else if (service === 'autoscaling') {
|
|
486
|
-
const targetPrefix = (headers.get('X-Amz-Target') || '').split('.')[0];
|
|
487
|
-
if (targetPrefix === 'AnyScaleFrontendService') {
|
|
488
|
-
service = 'application-autoscaling';
|
|
489
|
-
} else if (targetPrefix === 'AnyScaleScalingPlannerFrontendService') {
|
|
490
|
-
service = 'autoscaling-plans';
|
|
491
|
-
}
|
|
492
|
-
} else if (region == null && service.startsWith('s3-')) {
|
|
493
|
-
region = service.slice(3).replace(/^fips-|^external-1/, '');
|
|
494
|
-
service = 's3';
|
|
495
|
-
} else if (service.endsWith('-fips')) {
|
|
496
|
-
service = service.slice(0, -5);
|
|
497
|
-
} else if (region && /-\d$/.test(service) && !/-\d$/.test(region)) {
|
|
498
|
-
[service, region] = [region, service];
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return [HOST_SERVICES[service] || service, region || ''];
|
|
502
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { KeyManagementServiceClient } from '@google-cloud/kms';
|
|
4
|
-
import type { PublicKey, SignatureFlag } from '@mysten/sui/cryptography';
|
|
5
|
-
import { SIGNATURE_FLAG_TO_SCHEME, Signer } from '@mysten/sui/cryptography';
|
|
6
|
-
import { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';
|
|
7
|
-
import { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';
|
|
8
|
-
import { fromBase64 } from '@mysten/sui/utils';
|
|
9
|
-
|
|
10
|
-
import { getConcatenatedSignature, publicKeyFromDER } from '../utils/utils.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Configuration options for initializing the GcpKmsSigner.
|
|
14
|
-
*/
|
|
15
|
-
export interface GcpKmsSignerOptions {
|
|
16
|
-
/** The version name generated from `client.cryptoKeyVersionPath()` */
|
|
17
|
-
versionName: string;
|
|
18
|
-
/** Options for setting up the GCP KMS client */
|
|
19
|
-
client: KeyManagementServiceClient;
|
|
20
|
-
/** Public key */
|
|
21
|
-
publicKey: PublicKey;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* GCP KMS Signer integrates GCP Key Management Service (KMS) with the Sui blockchain
|
|
26
|
-
* to provide signing capabilities using GCP-managed cryptographic keys.
|
|
27
|
-
*/
|
|
28
|
-
export class GcpKmsSigner extends Signer {
|
|
29
|
-
#publicKey: PublicKey;
|
|
30
|
-
/** GCP KMS client instance */
|
|
31
|
-
#client: KeyManagementServiceClient;
|
|
32
|
-
/** GCP KMS version name (generated from `client.cryptoKeyVersionPath()`) */
|
|
33
|
-
#versionName: string;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Creates an instance of GcpKmsSigner. It's expected to call the static `fromOptions`
|
|
37
|
-
* or `fromVersionName` method to create an instance.
|
|
38
|
-
* For example:
|
|
39
|
-
* ```
|
|
40
|
-
* const signer = await GcpKmsSigner.fromVersionName(versionName);
|
|
41
|
-
* ```
|
|
42
|
-
* @throws Will throw an error if required GCP credentials are not provided.
|
|
43
|
-
*/
|
|
44
|
-
constructor({ versionName, client, publicKey }: GcpKmsSignerOptions) {
|
|
45
|
-
super();
|
|
46
|
-
if (!versionName) throw new Error('Version name is required');
|
|
47
|
-
|
|
48
|
-
this.#client = client;
|
|
49
|
-
this.#versionName = versionName;
|
|
50
|
-
this.#publicKey = publicKey;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Retrieves the key scheme used by this signer.
|
|
55
|
-
* @returns GCP supports only `Secp256k1` and `Secp256r1` schemes.
|
|
56
|
-
*/
|
|
57
|
-
getKeyScheme() {
|
|
58
|
-
return SIGNATURE_FLAG_TO_SCHEME[this.#publicKey.flag() as SignatureFlag];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Retrieves the public key associated with this signer.
|
|
63
|
-
* @returns The Secp256k1PublicKey instance.
|
|
64
|
-
* @throws Will throw an error if the public key has not been initialized.
|
|
65
|
-
*/
|
|
66
|
-
getPublicKey() {
|
|
67
|
-
return this.#publicKey;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Signs the given data using GCP KMS.
|
|
72
|
-
* @param bytes - The data to be signed as a Uint8Array.
|
|
73
|
-
* @returns A promise that resolves to the signature as a Uint8Array.
|
|
74
|
-
* @throws Will throw an error if the public key is not initialized or if signing fails.
|
|
75
|
-
*/
|
|
76
|
-
async sign(bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {
|
|
77
|
-
const [signResponse] = await this.#client.asymmetricSign({
|
|
78
|
-
name: this.#versionName,
|
|
79
|
-
data: bytes,
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
if (!signResponse.signature) {
|
|
83
|
-
throw new Error('No signature returned from GCP KMS');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return getConcatenatedSignature(signResponse.signature as Uint8Array, this.getKeyScheme());
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Creates a GCP KMS signer from the provided options.
|
|
91
|
-
* Expects the credentials file to be set as an env variable
|
|
92
|
-
* (GOOGLE_APPLICATION_CREDENTIALS).
|
|
93
|
-
*/
|
|
94
|
-
static async fromOptions(options: {
|
|
95
|
-
projectId: string;
|
|
96
|
-
location: string;
|
|
97
|
-
keyRing: string;
|
|
98
|
-
cryptoKey: string;
|
|
99
|
-
cryptoKeyVersion: string;
|
|
100
|
-
}) {
|
|
101
|
-
const client = new KeyManagementServiceClient();
|
|
102
|
-
|
|
103
|
-
const versionName = client.cryptoKeyVersionPath(
|
|
104
|
-
options.projectId,
|
|
105
|
-
options.location,
|
|
106
|
-
options.keyRing,
|
|
107
|
-
options.cryptoKey,
|
|
108
|
-
options.cryptoKeyVersion,
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
return new GcpKmsSigner({
|
|
112
|
-
versionName,
|
|
113
|
-
client,
|
|
114
|
-
publicKey: await getPublicKey(client, versionName),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
static async fromVersionName(versionName: string) {
|
|
119
|
-
const client = new KeyManagementServiceClient();
|
|
120
|
-
return new GcpKmsSigner({
|
|
121
|
-
versionName,
|
|
122
|
-
client,
|
|
123
|
-
publicKey: await getPublicKey(client, versionName),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Retrieves the public key associated with the given version name.
|
|
130
|
-
*/
|
|
131
|
-
async function getPublicKey(
|
|
132
|
-
client: KeyManagementServiceClient,
|
|
133
|
-
versionName: string,
|
|
134
|
-
): Promise<PublicKey> {
|
|
135
|
-
const [publicKey] = await client.getPublicKey({ name: versionName });
|
|
136
|
-
|
|
137
|
-
const { algorithm, pem } = publicKey;
|
|
138
|
-
|
|
139
|
-
if (!pem) throw new Error('No PEM key returned from GCP KMS');
|
|
140
|
-
|
|
141
|
-
const base64 = pem
|
|
142
|
-
.replace('-----BEGIN PUBLIC KEY-----', '')
|
|
143
|
-
.replace('-----END PUBLIC KEY-----', '')
|
|
144
|
-
.replace(/\s/g, '');
|
|
145
|
-
|
|
146
|
-
const compressedKey = publicKeyFromDER(fromBase64(base64));
|
|
147
|
-
|
|
148
|
-
switch (algorithm) {
|
|
149
|
-
case 'EC_SIGN_SECP256K1_SHA256':
|
|
150
|
-
return new Secp256k1PublicKey(compressedKey);
|
|
151
|
-
case 'EC_SIGN_P256_SHA256':
|
|
152
|
-
return new Secp256r1PublicKey(compressedKey);
|
|
153
|
-
default:
|
|
154
|
-
throw new Error(`Unsupported algorithm: ${algorithm}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
package/src/ledger/objects.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Mysten Labs, Inc.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
import type { Transaction } from '@mysten/sui/transactions';
|
|
5
|
-
import type { ClientWithCoreApi } from '@mysten/sui/client';
|
|
6
|
-
|
|
7
|
-
export const getInputObjects = async (transaction: Transaction, client: ClientWithCoreApi) => {
|
|
8
|
-
const data = transaction.getData();
|
|
9
|
-
|
|
10
|
-
const gasObjectIds = data.gasData.payment?.map((object) => object.objectId) ?? [];
|
|
11
|
-
const inputObjectIds = data.inputs
|
|
12
|
-
.map((input) => {
|
|
13
|
-
return input.$kind === 'Object' && input.Object.$kind === 'ImmOrOwnedObject'
|
|
14
|
-
? input.Object.ImmOrOwnedObject.objectId
|
|
15
|
-
: null;
|
|
16
|
-
})
|
|
17
|
-
.filter((objectId): objectId is string => !!objectId);
|
|
18
|
-
|
|
19
|
-
const response = await client.core.getObjects({
|
|
20
|
-
objectIds: [...gasObjectIds, ...inputObjectIds],
|
|
21
|
-
include: {
|
|
22
|
-
objectBcs: true,
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const bcsObjects = response.objects
|
|
27
|
-
.filter((obj): obj is Exclude<typeof obj, Error> => !(obj instanceof Error))
|
|
28
|
-
.map((object) => object.objectBcs)
|
|
29
|
-
.filter((bytes): bytes is Uint8Array<ArrayBuffer> => !!bytes);
|
|
30
|
-
|
|
31
|
-
return { bcsObjects };
|
|
32
|
-
};
|