152 lines
7.5 KiB
JavaScript
152 lines
7.5 KiB
JavaScript
'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;
|