"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.postPresignSignatureV4 = postPresignSignatureV4;
exports.presignSignatureV4 = presignSignatureV4;
exports.signV4 = signV4;
exports.signV4ByServiceName = signV4ByServiceName;
var crypto = _interopRequireWildcard(require("crypto"), true);
var errors = _interopRequireWildcard(require("./errors.js"), true);
var _helpers = require("./helpers.js");
var _helper = require("./internal/helper.js");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
/*
 * MinIO Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2016 MinIO, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const signV4Algorithm = 'AWS4-HMAC-SHA256';

// getCanonicalRequest generate a canonical request of style.
//
// canonicalRequest =
//  <HTTPMethod>\n
//  <CanonicalURI>\n
//  <CanonicalQueryString>\n
//  <CanonicalHeaders>\n
//  <SignedHeaders>\n
//  <HashedPayload>
//
function getCanonicalRequest(method, path, headers, signedHeaders, hashedPayload) {
  if (!(0, _helper.isString)(method)) {
    throw new TypeError('method should be of type "string"');
  }
  if (!(0, _helper.isString)(path)) {
    throw new TypeError('path should be of type "string"');
  }
  if (!(0, _helper.isObject)(headers)) {
    throw new TypeError('headers should be of type "object"');
  }
  if (!Array.isArray(signedHeaders)) {
    throw new TypeError('signedHeaders should be of type "array"');
  }
  if (!(0, _helper.isString)(hashedPayload)) {
    throw new TypeError('hashedPayload should be of type "string"');
  }
  const headersArray = signedHeaders.reduce((acc, i) => {
    // Trim spaces from the value (required by V4 spec)
    const val = `${headers[i]}`.replace(/ +/g, ' ');
    acc.push(`${i.toLowerCase()}:${val}`);
    return acc;
  }, []);
  const requestResource = path.split('?')[0];
  let requestQuery = path.split('?')[1];
  if (!requestQuery) {
    requestQuery = '';
  }
  if (requestQuery) {
    requestQuery = requestQuery.split('&').sort().map(element => !element.includes('=') ? element + '=' : element).join('&');
  }
  return [method.toUpperCase(), requestResource, requestQuery, headersArray.join('\n') + '\n', signedHeaders.join(';').toLowerCase(), hashedPayload].join('\n');
}

// generate a credential string
function getCredential(accessKey, region, requestDate, serviceName = 's3') {
  if (!(0, _helper.isString)(accessKey)) {
    throw new TypeError('accessKey should be of type "string"');
  }
  if (!(0, _helper.isString)(region)) {
    throw new TypeError('region should be of type "string"');
  }
  if (!(0, _helper.isObject)(requestDate)) {
    throw new TypeError('requestDate should be of type "object"');
  }
  return `${accessKey}/${(0, _helper.getScope)(region, requestDate, serviceName)}`;
}

// Returns signed headers array - alphabetically sorted
function getSignedHeaders(headers) {
  if (!(0, _helper.isObject)(headers)) {
    throw new TypeError('request should be of type "object"');
  }
  // Excerpts from @lsegal - https://github.com/aws/aws-sdk-js/issues/659#issuecomment-120477258
  //
  //  User-Agent:
  //
  //      This is ignored from signing because signing this causes problems with generating pre-signed URLs
  //      (that are executed by other agents) or when customers pass requests through proxies, which may
  //      modify the user-agent.
  //
  //  Content-Length:
  //
  //      This is ignored from signing because generating a pre-signed URL should not provide a content-length
  //      constraint, specifically when vending a S3 pre-signed PUT URL. The corollary to this is that when
  //      sending regular requests (non-pre-signed), the signature contains a checksum of the body, which
  //      implicitly validates the payload length (since changing the number of bytes would change the checksum)
  //      and therefore this header is not valuable in the signature.
  //
  //  Content-Type:
  //
  //      Signing this header causes quite a number of problems in browser environments, where browsers
  //      like to modify and normalize the content-type header in different ways. There is more information
  //      on this in https://github.com/aws/aws-sdk-js/issues/244. Avoiding this field simplifies logic
  //      and reduces the possibility of future bugs
  //
  //  Authorization:
  //
  //      Is skipped for obvious reasons

  const ignoredHeaders = ['authorization', 'content-length', 'content-type', 'user-agent'];
  return Object.keys(headers).filter(header => !ignoredHeaders.includes(header)).sort();
}

// returns the key used for calculating signature
function getSigningKey(date, region, secretKey, serviceName = 's3') {
  if (!(0, _helper.isObject)(date)) {
    throw new TypeError('date should be of type "object"');
  }
  if (!(0, _helper.isString)(region)) {
    throw new TypeError('region should be of type "string"');
  }
  if (!(0, _helper.isString)(secretKey)) {
    throw new TypeError('secretKey should be of type "string"');
  }
  const dateLine = (0, _helper.makeDateShort)(date);
  const hmac1 = crypto.createHmac('sha256', 'AWS4' + secretKey).update(dateLine).digest(),
    hmac2 = crypto.createHmac('sha256', hmac1).update(region).digest(),
    hmac3 = crypto.createHmac('sha256', hmac2).update(serviceName).digest();
  return crypto.createHmac('sha256', hmac3).update('aws4_request').digest();
}

// returns the string that needs to be signed
function getStringToSign(canonicalRequest, requestDate, region, serviceName = 's3') {
  if (!(0, _helper.isString)(canonicalRequest)) {
    throw new TypeError('canonicalRequest should be of type "string"');
  }
  if (!(0, _helper.isObject)(requestDate)) {
    throw new TypeError('requestDate should be of type "object"');
  }
  if (!(0, _helper.isString)(region)) {
    throw new TypeError('region should be of type "string"');
  }
  const hash = crypto.createHash('sha256').update(canonicalRequest).digest('hex');
  const scope = (0, _helper.getScope)(region, requestDate, serviceName);
  const stringToSign = [signV4Algorithm, (0, _helper.makeDateLong)(requestDate), scope, hash];
  return stringToSign.join('\n');
}

// calculate the signature of the POST policy
function postPresignSignatureV4(region, date, secretKey, policyBase64) {
  if (!(0, _helper.isString)(region)) {
    throw new TypeError('region should be of type "string"');
  }
  if (!(0, _helper.isObject)(date)) {
    throw new TypeError('date should be of type "object"');
  }
  if (!(0, _helper.isString)(secretKey)) {
    throw new TypeError('secretKey should be of type "string"');
  }
  if (!(0, _helper.isString)(policyBase64)) {
    throw new TypeError('policyBase64 should be of type "string"');
  }
  const signingKey = getSigningKey(date, region, secretKey);
  return crypto.createHmac('sha256', signingKey).update(policyBase64).digest('hex').toLowerCase();
}

// Returns the authorization header
function signV4(request, accessKey, secretKey, region, requestDate, sha256sum, serviceName = 's3') {
  if (!(0, _helper.isObject)(request)) {
    throw new TypeError('request should be of type "object"');
  }
  if (!(0, _helper.isString)(accessKey)) {
    throw new TypeError('accessKey should be of type "string"');
  }
  if (!(0, _helper.isString)(secretKey)) {
    throw new TypeError('secretKey should be of type "string"');
  }
  if (!(0, _helper.isString)(region)) {
    throw new TypeError('region should be of type "string"');
  }
  if (!accessKey) {
    throw new errors.AccessKeyRequiredError('accessKey is required for signing');
  }
  if (!secretKey) {
    throw new errors.SecretKeyRequiredError('secretKey is required for signing');
  }
  const signedHeaders = getSignedHeaders(request.headers);
  const canonicalRequest = getCanonicalRequest(request.method, request.path, request.headers, signedHeaders, sha256sum);
  const serviceIdentifier = serviceName || 's3';
  const stringToSign = getStringToSign(canonicalRequest, requestDate, region, serviceIdentifier);
  const signingKey = getSigningKey(requestDate, region, secretKey, serviceIdentifier);
  const credential = getCredential(accessKey, region, requestDate, serviceIdentifier);
  const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex').toLowerCase();
  return `${signV4Algorithm} Credential=${credential}, SignedHeaders=${signedHeaders.join(';').toLowerCase()}, Signature=${signature}`;
}
function signV4ByServiceName(request, accessKey, secretKey, region, requestDate, contentSha256, serviceName = 's3') {
  return signV4(request, accessKey, secretKey, region, requestDate, contentSha256, serviceName);
}

// returns a presigned URL string
function presignSignatureV4(request, accessKey, secretKey, sessionToken, region, requestDate, expires) {
  if (!(0, _helper.isObject)(request)) {
    throw new TypeError('request should be of type "object"');
  }
  if (!(0, _helper.isString)(accessKey)) {
    throw new TypeError('accessKey should be of type "string"');
  }
  if (!(0, _helper.isString)(secretKey)) {
    throw new TypeError('secretKey should be of type "string"');
  }
  if (!(0, _helper.isString)(region)) {
    throw new TypeError('region should be of type "string"');
  }
  if (!accessKey) {
    throw new errors.AccessKeyRequiredError('accessKey is required for presigning');
  }
  if (!secretKey) {
    throw new errors.SecretKeyRequiredError('secretKey is required for presigning');
  }
  if (expires && !(0, _helper.isNumber)(expires)) {
    throw new TypeError('expires should be of type "number"');
  }
  if (expires && expires < 1) {
    throw new errors.ExpiresParamError('expires param cannot be less than 1 seconds');
  }
  if (expires && expires > _helpers.PRESIGN_EXPIRY_DAYS_MAX) {
    throw new errors.ExpiresParamError('expires param cannot be greater than 7 days');
  }
  const iso8601Date = (0, _helper.makeDateLong)(requestDate);
  const signedHeaders = getSignedHeaders(request.headers);
  const credential = getCredential(accessKey, region, requestDate);
  const hashedPayload = 'UNSIGNED-PAYLOAD';
  const requestQuery = [];
  requestQuery.push(`X-Amz-Algorithm=${signV4Algorithm}`);
  requestQuery.push(`X-Amz-Credential=${(0, _helper.uriEscape)(credential)}`);
  requestQuery.push(`X-Amz-Date=${iso8601Date}`);
  requestQuery.push(`X-Amz-Expires=${expires}`);
  requestQuery.push(`X-Amz-SignedHeaders=${(0, _helper.uriEscape)(signedHeaders.join(';').toLowerCase())}`);
  if (sessionToken) {
    requestQuery.push(`X-Amz-Security-Token=${(0, _helper.uriEscape)(sessionToken)}`);
  }
  const resource = request.path.split('?')[0];
  let query = request.path.split('?')[1];
  if (query) {
    query = query + '&' + requestQuery.join('&');
  } else {
    query = requestQuery.join('&');
  }
  const path = resource + '?' + query;
  const canonicalRequest = getCanonicalRequest(request.method, path, request.headers, signedHeaders, hashedPayload);
  const stringToSign = getStringToSign(canonicalRequest, requestDate, region);
  const signingKey = getSigningKey(requestDate, region, secretKey);
  const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex').toLowerCase();
  return request.protocol + '//' + request.headers.host + path + `&X-Amz-Signature=${signature}`;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,[encoded data, 31252 characters]