'use strict'; var signatureV4MultiRegion = require('@aws-sdk/signature-v4-multi-region'); var crtLoader = require('@aws-sdk/crt-loader'); var querystringParser = require('@smithy/querystring-parser'); var signatureV4 = require('@smithy/signature-v4'); var utilMiddleware = require('@smithy/util-middleware'); const SHA256_HEADER = "x-amz-content-sha256"; const MAX_PRESIGNED_TTL = 60 * 60 * 24 * 7; function deleteHeader(soughtHeader, headers) { soughtHeader = soughtHeader.toLowerCase(); for (const headerName of Object.keys(headers)) { if (soughtHeader === headerName.toLowerCase()) { delete headers[headerName]; } } } function sdkHttpRequest2crtHttpRequest(sdkRequest) { deleteHeader(SHA256_HEADER, sdkRequest.headers); const headersArray = Object.entries(sdkRequest.headers); const crtHttpHeaders = new crtLoader.http.HttpHeaders(headersArray); const queryString = signatureV4.getCanonicalQuery(sdkRequest); return new crtLoader.http.HttpRequest(sdkRequest.method, sdkRequest.path + "?" + queryString, crtHttpHeaders); } class CrtSignerV4 { service; regionProvider; credentialProvider; sha256; uriEscapePath; applyChecksum; signingAlgorithm; constructor({ credentials, region, service, sha256, applyChecksum = true, uriEscapePath = true, signingAlgorithm = crtLoader.auth.AwsSigningAlgorithm.SigV4, }) { this.service = service; this.sha256 = sha256; this.uriEscapePath = uriEscapePath; this.signingAlgorithm = signingAlgorithm; this.applyChecksum = applyChecksum; this.regionProvider = utilMiddleware.normalizeProvider(region); this.credentialProvider = utilMiddleware.normalizeProvider(credentials); crtLoader.io.enable_logging(crtLoader.io.LogLevel.ERROR); } async options2crtConfigure({ signingDate = new Date(), signableHeaders, unsignableHeaders, signingRegion, signingService, } = {}, viaHeader, payloadHash, expiresIn, _credentials) { const credentials = _credentials ?? (await this.credentialProvider()); const region = signingRegion ?? (await this.regionProvider()); const service = signingService ?? this.service; if (signableHeaders?.has("x-amzn-trace-id") || signableHeaders?.has("user-agent")) { throw new Error("internal check (x-amzn-trace-id, user-agent) is not supported to be included to sign with CRT."); } const headersUnsignable = getHeadersUnsignable(unsignableHeaders, signableHeaders); return { algorithm: this.signingAlgorithm, signature_type: viaHeader ? crtLoader.auth.AwsSignatureType.HttpRequestViaHeaders : crtLoader.auth.AwsSignatureType.HttpRequestViaQueryParams, provider: sdk2crtCredentialsProvider(credentials), region: region, service: service, date: new Date(signingDate), header_blacklist: headersUnsignable, use_double_uri_encode: this.uriEscapePath, signed_body_value: payloadHash, signed_body_header: this.applyChecksum && viaHeader ? crtLoader.auth.AwsSignedBodyHeaderType.XAmzContentSha256 : crtLoader.auth.AwsSignedBodyHeaderType.None, expiration_in_seconds: expiresIn, }; } async presign(originalRequest, options = {}) { if (options.expiresIn && options.expiresIn > MAX_PRESIGNED_TTL) { return Promise.reject("Signature version 4 presigned URLs" + " must have an expiration date less than one week in" + " the future"); } const request = signatureV4.moveHeadersToQuery(signatureV4.prepareRequest(originalRequest)); const crtSignedRequest = await this.signRequest(request, await this.options2crtConfigure(options, false, await signatureV4.getPayloadHash(originalRequest, this.sha256), options.expiresIn ? options.expiresIn : 3600)); request.query = this.getQueryParam(crtSignedRequest.path); return request; } async sign(toSign, options) { const request = signatureV4.prepareRequest(toSign); const crtSignedRequest = await this.signRequest(request, await this.options2crtConfigure(options, true, await signatureV4.getPayloadHash(toSign, this.sha256))); request.headers = crtSignedRequest.headers._flatten().reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); return request; } async signWithCredentials(toSign, credentials, options) { const request = signatureV4.prepareRequest(toSign); const crtSignedRequest = await this.signRequest(request, await this.options2crtConfigure(options, true, await signatureV4.getPayloadHash(toSign, this.sha256), undefined, credentials)); request.headers = crtSignedRequest.headers._flatten().reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); return request; } getQueryParam(crtPath) { const start = crtPath.search(/\?/); const startHash = crtPath.search(/\#/); const end = startHash == -1 ? undefined : startHash; const queryParam = {}; if (start == -1) { return queryParam; } const queryString = crtPath.slice(start + 1, end); return querystringParser.parseQueryString(queryString); } async signRequest(requestToSign, crtConfig) { const request = sdkHttpRequest2crtHttpRequest(requestToSign); try { return await crtLoader.auth.aws_sign_request(request, crtConfig); } catch (error) { throw new Error(error); } } async verifySigv4aSigning(request, signature, expectedCanonicalRequest, eccPubKeyX, eccPubKeyY, options = {}) { const sdkRequest = signatureV4.prepareRequest(request); const crtRequest = sdkHttpRequest2crtHttpRequest(sdkRequest); const payloadHash = await signatureV4.getPayloadHash(request, this.sha256); const crtConfig = await this.options2crtConfigure(options, true, payloadHash); return crtLoader.auth.aws_verify_sigv4a_signing(crtRequest, crtConfig, expectedCanonicalRequest, signature, eccPubKeyX, eccPubKeyY); } async verifySigv4aPreSigning(request, signature, expectedCanonicalRequest, eccPubKeyX, eccPubKeyY, options = {}) { if (typeof signature != "string") { return false; } const sdkRequest = signatureV4.prepareRequest(request); const crtRequest = sdkHttpRequest2crtHttpRequest(sdkRequest); const crtConfig = await this.options2crtConfigure(options, false, await signatureV4.getPayloadHash(request, this.sha256), options.expiresIn ? options.expiresIn : 3600); return crtLoader.auth.aws_verify_sigv4a_signing(crtRequest, crtConfig, expectedCanonicalRequest, signature, eccPubKeyX, eccPubKeyY); } } function sdk2crtCredentialsProvider(credentials) { return crtLoader.auth.AwsCredentialsProvider.newStatic(credentials.accessKeyId, credentials.secretAccessKey, credentials.sessionToken); } function getHeadersUnsignable(unsignableHeaders, signableHeaders) { if (!unsignableHeaders) { return []; } if (!signableHeaders) { return [...unsignableHeaders]; } const result = new Set([...unsignableHeaders]); for (let it = signableHeaders.values(), val = null; (val = it.next().value);) { if (result.has(val)) { result.delete(val); } } return [...result]; } signatureV4MultiRegion.signatureV4CrtContainer.CrtSignerV4 = CrtSignerV4; exports.CrtSignerV4 = CrtSignerV4;