Skip to content

Commit

Permalink
Bundling both CJS and ESM
Browse files Browse the repository at this point in the history
LokeshRishi committed Apr 21, 2023
1 parent 425c885 commit 3fde0fd
Showing 52 changed files with 5,261 additions and 4,283 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
"parserOptions": {
"ecmaVersion": 2018
},
"ignorePatterns": ["**/lib/*"],
"rules": {
"space-before-function-paren": [
"error",
6 changes: 3 additions & 3 deletions __tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -20,9 +20,9 @@

import { Response, Request } from 'express';
import { jwtDecrypt } from 'jose';
import { constants } from '../lib/constants';
import { readKey } from '../lib/helpers/common';
import * as DigitalSignatureSDK from '../lib/index';
import { constants } from '../src/constants';
import { readKey } from '../src/helpers/common';
import * as DigitalSignatureSDK from '../src/index';

const testData = require('./test.json');

28 changes: 28 additions & 0 deletions lib/cjs/constants.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export declare const constants: {
readonly BASE64: "base64";
readonly COLON: ":";
readonly CONTENT_DIGEST_SHA256: "sha-256=:";
readonly CONTENT_DIGEST_SHA512: "sha-512=:";
readonly HEADERS: {
readonly APPLICATION_JSON: "application/json";
readonly CONTENT_DIGEST: "content-digest";
readonly SIGNATURE_INPUT: "signature-input";
readonly SIGNATURE_KEY: "x-ebay-signature-key";
readonly SIGNATURE: "signature";
};
readonly HTTP_STATUS_CODE: {
readonly NO_CONTENT: 204;
readonly OK: 200;
readonly BAD_REQUEST: 400;
readonly INTERNAL_SERVER_ERROR: 500;
};
readonly KEY_PATTERN_END: RegExp;
readonly KEY_PATTERN_START: RegExp;
readonly KEY_END: "\n-----END PUBLIC KEY-----";
readonly KEY_START: "-----BEGIN PUBLIC KEY-----\n";
readonly SHA_256: "sha256";
readonly SHA_512: "sha512";
readonly SIGNATURE_PREFIX: "sig1=:";
readonly UTF8: "utf8";
readonly X_EBAY_SIGNATURE: "x-ebay-signature";
};
File renamed without changes.
3 changes: 3 additions & 0 deletions lib/cjs/helpers/common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function needsContentDigestValidation(requestBody: string): boolean;
declare function readKey(value: string): string;
export { needsContentDigestValidation, readKey };
File renamed without changes.
18 changes: 18 additions & 0 deletions lib/cjs/helpers/digest-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference types="node" />
/**
* Generates the 'Content-Digest' header value for the input payload.
*
* @param {Buffer} payload The request payload.
* @param {string} cipher The algorithm used to calculate the digest.
* @returns {string} contentDigest The 'Content-Digest' header value.
*/
declare function generateDigestHeader(payload: Buffer, cipher: string): string;
/**
* Validates the 'Content-Digest' header value.
*
* @param {string} contentDigestHeader The Content-Digest header value.
* @param {Buffer} body The HTTP request body.
* @throws {Error} If the Content-Digest header value is invalid.
*/
declare function validateDigestHeader(contentDigestHeader: string, body: Buffer): void;
export { generateDigestHeader, validateDigestHeader };
File renamed without changes.
16 changes: 16 additions & 0 deletions lib/cjs/helpers/jwe-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Config } from '..';
/**
* Decrypts the input JWE string and returns the 'pkey' value from claims set.
*
* @param {string} jweString The JWE string.
* @param {Config} config The input config.
* @returns Promise<string> If the JWE decryption is successful, else returns Promise<undefined>.
*/
export declare function decryptJWE(jweString: string, config: Config): Promise<string | undefined>;
/**
* Generates JWE string.
*
* @param {Config} config The input config.
* @returns {Promise<string>} jwe The JWE as string.
*/
export declare function encryptJWE(config: Config): Promise<string>;
File renamed without changes.
25 changes: 25 additions & 0 deletions lib/cjs/helpers/signature-base-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Config } from '..';
/**
* Generates the base string.
*
* @param {any} headers The HTTP request headers.
* @param {Config} config The config.
* @returns {string} baseString The base string.
*/
declare function generateBase(headers: any, config: Config): string;
/**
* Generates the base string for validation.
*
* @param {any} headers The HTTP request headers.
* @param {Config} config The config.
* @returns {string} baseString the base string.
* @throws {Error} incase of an error.
*/
declare function calculateBase(headers: any, config: Config): string;
/**
* Returns the current UNIX timestamp.
*
* @returns {number} The unix timestamp.
*/
declare function getUnixTimestamp(): number;
export { calculateBase, generateBase, getUnixTimestamp };
File renamed without changes.
43 changes: 43 additions & 0 deletions lib/cjs/helpers/signature-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Config } from '..';
/**
* Generates the x-ebay-signature-key header value for the input payload.
*
* @param {Config} config The input config.
* @returns <Promise<string> The signature key value.
*/
declare function generateSignatureKey(config: Config): Promise<string>;
/**
* Generates the Signature-Input header value for the input payload.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns {string} the 'Signature-Input' header value.
*/
declare function generateSignatureInput(headers: any, config: Config): string;
/**
* Generates the 'Signature' header.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns {string} the signature header value.
*/
declare function generateSignature(headers: any, config: Config): string;
/**
* Validates the input signature key (x-ebay-signature-key header value).
*
* @param {string} signatureKey the x-ebay-signature-key header value.
* @param {Config} config The input config.
* @returns Promise<string> the public key (pkey) value from JWT claims set.
* @throws {Error} if the header generation fails.
*/
declare function validateSignatureKey(signatureKey: string, config: Config): Promise<string | undefined>;
/**
* Validates the signature header value.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns Promise<boolean> True upon successful signature validation.
* @throws Error if the Signature value is invalid.
*/
declare function validateSignatureHeader(headers: any, config: Config): Promise<boolean>;
export { generateSignature, generateSignatureInput, generateSignatureKey, validateSignatureKey, validateSignatureHeader };
File renamed without changes.
21 changes: 21 additions & 0 deletions lib/cjs/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Request, Response } from 'express';
import { generateDigestHeader, validateDigestHeader } from './helpers/digest-helper';
import { generateSignature, generateSignatureInput, generateSignatureKey, validateSignatureHeader } from './helpers/signature-helper';
import { Config } from './types/Config';
/**
* Generate signature headers and add it to the response.
*
* @param {Request} request The request object.
* @param {Response} response The response object
* @param {Config} config The input config.
*/
declare function signMessage(request: Request, response: Response, config: Config): Promise<void>;
/**
* Verifies the signature header for the given request
*
* @param {Request} request The request object.
* @param {Config} config The input config.
* @returns Promise<boolean> True upon successful signature validation.
* */
declare function validateSignature(request: Request, config: Config): Promise<boolean>;
export { generateDigestHeader, generateSignature, generateSignatureInput, generateSignatureKey, signMessage, validateDigestHeader, validateSignature, validateSignatureHeader, Config };
File renamed without changes.
13 changes: 13 additions & 0 deletions lib/cjs/types/Config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SignatureComponents } from "./SignatureComponents";
export interface Config {
digestAlgorithm: string;
jwe: string;
jwtExpiration: number;
jweHeaderParams: object;
jwtPayload: object;
masterKey: string;
privateKey: string;
publicKey: string;
signatureComponents: SignatureComponents;
signatureParams: Array<string>;
}
File renamed without changes.
8 changes: 8 additions & 0 deletions lib/cjs/types/SignatureComponents.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface SignatureComponents {
authority: string;
method: string;
path: string;
requestTarget: string;
scheme: string;
targetUri: string;
}
File renamed without changes.
28 changes: 28 additions & 0 deletions lib/esm/constants.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export declare const constants: {
readonly BASE64: "base64";
readonly COLON: ":";
readonly CONTENT_DIGEST_SHA256: "sha-256=:";
readonly CONTENT_DIGEST_SHA512: "sha-512=:";
readonly HEADERS: {
readonly APPLICATION_JSON: "application/json";
readonly CONTENT_DIGEST: "content-digest";
readonly SIGNATURE_INPUT: "signature-input";
readonly SIGNATURE_KEY: "x-ebay-signature-key";
readonly SIGNATURE: "signature";
};
readonly HTTP_STATUS_CODE: {
readonly NO_CONTENT: 204;
readonly OK: 200;
readonly BAD_REQUEST: 400;
readonly INTERNAL_SERVER_ERROR: 500;
};
readonly KEY_PATTERN_END: RegExp;
readonly KEY_PATTERN_START: RegExp;
readonly KEY_END: "\n-----END PUBLIC KEY-----";
readonly KEY_START: "-----BEGIN PUBLIC KEY-----\n";
readonly SHA_256: "sha256";
readonly SHA_512: "sha512";
readonly SIGNATURE_PREFIX: "sig1=:";
readonly UTF8: "utf8";
readonly X_EBAY_SIGNATURE: "x-ebay-signature";
};
48 changes: 48 additions & 0 deletions lib/esm/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.constants = void 0;
exports.constants = {
BASE64: 'base64',
COLON: ':',
CONTENT_DIGEST_SHA256: 'sha-256=:',
CONTENT_DIGEST_SHA512: 'sha-512=:',
HEADERS: {
APPLICATION_JSON: 'application/json',
CONTENT_DIGEST: 'content-digest',
SIGNATURE_INPUT: 'signature-input',
SIGNATURE_KEY: 'x-ebay-signature-key',
SIGNATURE: 'signature'
},
HTTP_STATUS_CODE: {
NO_CONTENT: 204,
OK: 200,
BAD_REQUEST: 400,
INTERNAL_SERVER_ERROR: 500
},
KEY_PATTERN_END: /\n-----END PUBLIC KEY-----/,
KEY_PATTERN_START: /-----BEGIN PUBLIC KEY-----\n/,
KEY_END: '\n-----END PUBLIC KEY-----',
KEY_START: '-----BEGIN PUBLIC KEY-----\n',
SHA_256: 'sha256',
SHA_512: 'sha512',
SIGNATURE_PREFIX: 'sig1=:',
UTF8: 'utf8',
X_EBAY_SIGNATURE: 'x-ebay-signature'
};
3 changes: 3 additions & 0 deletions lib/esm/helpers/common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function needsContentDigestValidation(requestBody: string): boolean;
declare function readKey(value: string): string;
export { needsContentDigestValidation, readKey };
61 changes: 61 additions & 0 deletions lib/esm/helpers/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readKey = exports.needsContentDigestValidation = void 0;
const fs = __importStar(require("fs"));
const constants_1 = require("../constants");
function needsContentDigestValidation(requestBody) {
return requestBody !== null &&
requestBody !== undefined &&
requestBody.length > 0;
}
exports.needsContentDigestValidation = needsContentDigestValidation;
function readKey(value) {
let key = value;
if (fs.existsSync(value)) {
key = fs.readFileSync(value, {
encoding: constants_1.constants.UTF8
});
}
return key;
}
exports.readKey = readKey;
18 changes: 18 additions & 0 deletions lib/esm/helpers/digest-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference types="node" />
/**
* Generates the 'Content-Digest' header value for the input payload.
*
* @param {Buffer} payload The request payload.
* @param {string} cipher The algorithm used to calculate the digest.
* @returns {string} contentDigest The 'Content-Digest' header value.
*/
declare function generateDigestHeader(payload: Buffer, cipher: string): string;
/**
* Validates the 'Content-Digest' header value.
*
* @param {string} contentDigestHeader The Content-Digest header value.
* @param {Buffer} body The HTTP request body.
* @throws {Error} If the Content-Digest header value is invalid.
*/
declare function validateDigestHeader(contentDigestHeader: string, body: Buffer): void;
export { generateDigestHeader, validateDigestHeader };
78 changes: 78 additions & 0 deletions lib/esm/helpers/digest-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateDigestHeader = exports.generateDigestHeader = void 0;
const crypto_1 = __importDefault(require("crypto"));
const constants_1 = require("../constants");
/**
* Generates the 'Content-Digest' header value for the input payload.
*
* @param {Buffer} payload The request payload.
* @param {string} cipher The algorithm used to calculate the digest.
* @returns {string} contentDigest The 'Content-Digest' header value.
*/
function generateDigestHeader(payload, cipher) {
let contentDigest = '';
// Validate the input payload
if (!payload) {
return contentDigest;
}
// Calculate the SHA-256 digest
const hash = crypto_1.default.createHash(cipher)
.update(payload)
.digest(constants_1.constants.BASE64);
const algo = cipher === constants_1.constants.SHA_512 ? constants_1.constants.CONTENT_DIGEST_SHA512 :
constants_1.constants.CONTENT_DIGEST_SHA256;
contentDigest = algo + hash + constants_1.constants.COLON;
return contentDigest;
}
exports.generateDigestHeader = generateDigestHeader;
;
/**
* Validates the 'Content-Digest' header value.
*
* @param {string} contentDigestHeader The Content-Digest header value.
* @param {Buffer} body The HTTP request body.
* @throws {Error} If the Content-Digest header value is invalid.
*/
function validateDigestHeader(contentDigestHeader, body) {
if (!contentDigestHeader) {
throw new Error("Content-Digest header missing");
}
// Validate
const contentDigestPattern = new RegExp("(.+)=:(.+):");
const contentDigestParts = contentDigestPattern.exec(contentDigestHeader);
if (!contentDigestParts || contentDigestParts.length == 0) {
throw new Error("Content-digest header invalid");
}
const cipher = contentDigestParts[1];
if (cipher !== "sha-256" && cipher !== "sha-512") {
throw new Error("Invalid cipher " + cipher);
}
const algorithm = cipher === "sha-256" ? constants_1.constants.SHA_256 : constants_1.constants.SHA_512;
const newDigest = generateDigestHeader(body, algorithm);
if (newDigest !== contentDigestHeader) {
throw new Error("Content-Digest value is invalid. Expected body digest is: "
+ newDigest);
}
}
exports.validateDigestHeader = validateDigestHeader;
16 changes: 16 additions & 0 deletions lib/esm/helpers/jwe-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Config } from '..';
/**
* Decrypts the input JWE string and returns the 'pkey' value from claims set.
*
* @param {string} jweString The JWE string.
* @param {Config} config The input config.
* @returns Promise<string> If the JWE decryption is successful, else returns Promise<undefined>.
*/
export declare function decryptJWE(jweString: string, config: Config): Promise<string | undefined>;
/**
* Generates JWE string.
*
* @param {Config} config The input config.
* @returns {Promise<string>} jwe The JWE as string.
*/
export declare function encryptJWE(config: Config): Promise<string>;
77 changes: 77 additions & 0 deletions lib/esm/helpers/jwe-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.encryptJWE = exports.decryptJWE = void 0;
const jose_1 = require("jose");
const constants_1 = require("../constants");
const common_1 = require("./common");
const signature_base_helper_1 = require("./signature-base-helper");
/**
* Decrypts the input JWE string and returns the 'pkey' value from claims set.
*
* @param {string} jweString The JWE string.
* @param {Config} config The input config.
* @returns Promise<string> If the JWE decryption is successful, else returns Promise<undefined>.
*/
async function decryptJWE(jweString, config) {
const masterKey = (0, common_1.readKey)(config.masterKey);
const masterKeyBuffer = Buffer.from(masterKey, constants_1.constants.BASE64);
const jwtDecryptResult = await (0, jose_1.jwtDecrypt)(jweString, masterKeyBuffer);
if (jwtDecryptResult['payload'] && jwtDecryptResult['payload']['pkey']) {
const pKey = jwtDecryptResult['payload']['pkey'];
return constants_1.constants.KEY_START + pKey + constants_1.constants.KEY_END;
}
}
exports.decryptJWE = decryptJWE;
/**
* Generates JWE string.
*
* @param {Config} config The input config.
* @returns {Promise<string>} jwe The JWE as string.
*/
async function encryptJWE(config) {
const masterKey = (0, common_1.readKey)(config.masterKey);
let publicKey = (0, common_1.readKey)(config.publicKey);
publicKey = formatPublicKey(publicKey);
const unixTimestamp = (0, signature_base_helper_1.getUnixTimestamp)();
const masterKeyBuffer = Buffer.from(masterKey, constants_1.constants.BASE64);
const jwe = await new jose_1.EncryptJWT(config.jwtPayload)
.setProtectedHeader(config.jweHeaderParams)
.setIssuedAt(unixTimestamp)
.setNotBefore(unixTimestamp)
.setExpirationTime(`${config.jwtExpiration}y`)
.encrypt(masterKeyBuffer);
return jwe;
}
exports.encryptJWE = encryptJWE;
/**
* Removes beginning and end markers from the input public key.
*
* @param {string} key The public key.
* @throws {Error} if the key format is invalid.
*/
function formatPublicKey(key) {
try {
const updatedKey = key.replace(constants_1.constants.KEY_PATTERN_START, '');
return updatedKey.replace(constants_1.constants.KEY_PATTERN_END, '');
}
catch (exception) {
throw new Error(`Invalid public key format`);
}
}
25 changes: 25 additions & 0 deletions lib/esm/helpers/signature-base-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Config } from '..';
/**
* Generates the base string.
*
* @param {any} headers The HTTP request headers.
* @param {Config} config The config.
* @returns {string} baseString The base string.
*/
declare function generateBase(headers: any, config: Config): string;
/**
* Generates the base string for validation.
*
* @param {any} headers The HTTP request headers.
* @param {Config} config The config.
* @returns {string} baseString the base string.
* @throws {Error} incase of an error.
*/
declare function calculateBase(headers: any, config: Config): string;
/**
* Returns the current UNIX timestamp.
*
* @returns {number} The unix timestamp.
*/
declare function getUnixTimestamp(): number;
export { calculateBase, generateBase, getUnixTimestamp };
173 changes: 173 additions & 0 deletions lib/esm/helpers/signature-base-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUnixTimestamp = exports.generateBase = exports.calculateBase = void 0;
const constants_1 = require("../constants");
/**
* Generates the base string.
*
* @param {any} headers The HTTP request headers.
* @param {Config} config The config.
* @returns {string} baseString The base string.
*/
function generateBase(headers, config) {
try {
let baseString = "";
const signatureParams = config.signatureParams;
signatureParams.forEach((header) => {
if (header === constants_1.constants.HEADERS.CONTENT_DIGEST &&
!headers[constants_1.constants.HEADERS.CONTENT_DIGEST]) {
return;
}
baseString += "\"";
baseString += header.toLowerCase();
baseString += "\": ";
if (header.startsWith("@")) {
switch (header.toLowerCase()) {
case "@method":
baseString += config.signatureComponents.method;
break;
case "@authority":
baseString += config.signatureComponents.authority;
break;
case "@target-uri":
baseString += config.signatureComponents.targetUri;
break;
case "@path":
baseString += config.signatureComponents.path;
break;
case "@scheme":
baseString += config.signatureComponents.scheme;
break;
case "@request-target":
baseString += config.signatureComponents.requestTarget;
break;
default:
throw new Error("Unknown pseudo header " + header);
}
}
else {
if (!headers[header]) {
throw new Error("Header " + header + " not included in message");
}
baseString += headers[header];
}
baseString += "\n";
});
baseString += "\"@signature-params\": ";
let signatureInput = "";
let signatureInputBuf = "";
signatureInputBuf += "(";
for (let i = 0; i < signatureParams.length; i++) {
const param = signatureParams[i];
signatureInputBuf += "\"";
signatureInputBuf += param;
signatureInputBuf += "\"";
if (i < signatureParams.length - 1) {
signatureInputBuf += " ";
}
}
signatureInputBuf += ");created=";
signatureInputBuf += getUnixTimestamp().toString();
signatureInput = signatureInputBuf.toString();
baseString = baseString + signatureInput;
return baseString;
}
catch (e) {
throw new Error(`Error calculating signature base: ${e.message}`);
}
}
exports.generateBase = generateBase;
/**
* Generates the base string for validation.
*
* @param {any} headers The HTTP request headers.
* @param {Config} config The config.
* @returns {string} baseString the base string.
* @throws {Error} incase of an error.
*/
function calculateBase(headers, config) {
try {
const signatureInputHeader = headers[constants_1.constants.HEADERS.SIGNATURE_INPUT];
if (!signatureInputHeader || signatureInputHeader.length == 0) {
throw new Error("Signature-Input header missing");
}
// Validate signature pattern
const signatureInputPattern = new RegExp(".+=(\\((.+)\\);created=(\\d+)(;keyid=.+)?)");
const signatureInputParts = signatureInputPattern.exec(signatureInputHeader);
if (!signatureInputParts || signatureInputParts.length < 3) {
throw new Error("Invalid Signature-Input. Make sure it's of format: .+=\\(.+\\;created=\\d+)");
}
const signatureInput = signatureInputParts[2].replaceAll("\"", "");
const signatureParams = signatureInput.split(" ");
let baseString = '';
signatureParams.forEach((header) => {
baseString += "\"";
baseString += header.toLowerCase();
baseString += "\": ";
if (header.startsWith("@")) {
switch (header.toLowerCase()) {
case "@method":
baseString += config.signatureComponents.method;
break;
case "@authority":
baseString += config.signatureComponents.authority;
break;
case "@target-uri":
baseString += config.signatureComponents.targetUri;
break;
case "@path":
baseString += config.signatureComponents.path;
break;
case "@scheme":
baseString += config.signatureComponents.scheme;
break;
case "@request-target":
baseString += config.signatureComponents.requestTarget;
break;
default:
throw new Error("Unknown pseudo header " + header);
}
}
else {
if (!headers[header]) {
throw new Error("Header " + header + " not included in message");
}
baseString += headers[header];
}
baseString += "\n";
});
baseString += "\"@signature-params\": ";
baseString += signatureInputParts[1];
return baseString;
}
catch (e) {
throw new Error(`Error calculating base: ${e.message}`);
}
}
exports.calculateBase = calculateBase;
/**
* Returns the current UNIX timestamp.
*
* @returns {number} The unix timestamp.
*/
function getUnixTimestamp() {
return Math.floor(Date.now() / 1000);
}
exports.getUnixTimestamp = getUnixTimestamp;
43 changes: 43 additions & 0 deletions lib/esm/helpers/signature-helper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Config } from '..';
/**
* Generates the x-ebay-signature-key header value for the input payload.
*
* @param {Config} config The input config.
* @returns <Promise<string> The signature key value.
*/
declare function generateSignatureKey(config: Config): Promise<string>;
/**
* Generates the Signature-Input header value for the input payload.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns {string} the 'Signature-Input' header value.
*/
declare function generateSignatureInput(headers: any, config: Config): string;
/**
* Generates the 'Signature' header.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns {string} the signature header value.
*/
declare function generateSignature(headers: any, config: Config): string;
/**
* Validates the input signature key (x-ebay-signature-key header value).
*
* @param {string} signatureKey the x-ebay-signature-key header value.
* @param {Config} config The input config.
* @returns Promise<string> the public key (pkey) value from JWT claims set.
* @throws {Error} if the header generation fails.
*/
declare function validateSignatureKey(signatureKey: string, config: Config): Promise<string | undefined>;
/**
* Validates the signature header value.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns Promise<boolean> True upon successful signature validation.
* @throws Error if the Signature value is invalid.
*/
declare function validateSignatureHeader(headers: any, config: Config): Promise<boolean>;
export { generateSignature, generateSignatureInput, generateSignatureKey, validateSignatureKey, validateSignatureHeader };
130 changes: 130 additions & 0 deletions lib/esm/helpers/signature-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSignatureHeader = exports.validateSignatureKey = exports.generateSignatureKey = exports.generateSignatureInput = exports.generateSignature = void 0;
const crypto_1 = __importDefault(require("crypto"));
const constants_1 = require("../constants");
const jwe_helper_1 = require("./jwe-helper");
const signature_base_helper_1 = require("./signature-base-helper");
const common_1 = require("./common");
/**
* Generates the x-ebay-signature-key header value for the input payload.
*
* @param {Config} config The input config.
* @returns <Promise<string> The signature key value.
*/
async function generateSignatureKey(config) {
const jwe = await (0, jwe_helper_1.encryptJWE)(config);
return jwe;
}
exports.generateSignatureKey = generateSignatureKey;
;
/**
* Generates the Signature-Input header value for the input payload.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns {string} the 'Signature-Input' header value.
*/
function generateSignatureInput(headers, config) {
const unixTimestamp = (0, signature_base_helper_1.getUnixTimestamp)();
let signatureInput = `sig1=(`;
config.signatureParams.forEach((param) => {
if (param === constants_1.constants.HEADERS.CONTENT_DIGEST &&
!headers[constants_1.constants.HEADERS.CONTENT_DIGEST]) {
return;
}
signatureInput += `"${param}" `;
});
signatureInput = signatureInput.trim() + `);created=${unixTimestamp}`;
return signatureInput;
}
exports.generateSignatureInput = generateSignatureInput;
;
/**
* Generates the 'Signature' header.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns {string} the signature header value.
*/
function generateSignature(headers, config) {
const baseString = (0, signature_base_helper_1.generateBase)(headers, config);
const privateKey = (0, common_1.readKey)(config.privateKey);
// If algorithm is undefined, then it is dependent upon the private key type.
const signatureBuffer = crypto_1.default.sign(undefined, Buffer.from(baseString), privateKey);
let signature = signatureBuffer.toString(constants_1.constants.BASE64);
return constants_1.constants.SIGNATURE_PREFIX + signature + constants_1.constants.COLON;
}
exports.generateSignature = generateSignature;
;
/**
* Validates the input signature key (x-ebay-signature-key header value).
*
* @param {string} signatureKey the x-ebay-signature-key header value.
* @param {Config} config The input config.
* @returns Promise<string> the public key (pkey) value from JWT claims set.
* @throws {Error} if the header generation fails.
*/
function validateSignatureKey(signatureKey, config) {
try {
return (0, jwe_helper_1.decryptJWE)(signatureKey, config);
}
catch (e) {
throw new Error(`Error parsing JWE from x-ebay-signature-key header: ${e.message}`);
}
;
}
exports.validateSignatureKey = validateSignatureKey;
/**
* Validates the signature header value.
*
* @param {any} headers The HTTP headers.
* @param {Config} config The input config.
* @returns Promise<boolean> True upon successful signature validation.
* @throws Error if the Signature value is invalid.
*/
async function validateSignatureHeader(headers, config) {
const signature = headers[constants_1.constants.HEADERS.SIGNATURE];
const signatureKey = headers[constants_1.constants.HEADERS.SIGNATURE_KEY];
if (!signatureKey) {
throw new Error(`${constants_1.constants.HEADERS.SIGNATURE_KEY} header missing`);
}
if (!signature) {
throw new Error(`${constants_1.constants.HEADERS.SIGNATURE} header missing`);
}
// Validate signature pattern
const signaturePattern = new RegExp(".+=:(.+):");
const signatureParts = signaturePattern.exec(signature);
if (!signatureParts || signatureParts.length < 2) {
throw new Error("Signature header invalid");
}
// Base64 decode
const signatureBuffer = Buffer.from(signatureParts[1], constants_1.constants.BASE64);
// Verify JWT
const publicKey = await validateSignatureKey(signatureKey, config);
const baseString = (0, signature_base_helper_1.calculateBase)(headers, config);
// If algorithm is undefined, then it is dependent upon the public key type.
const verificationResponse = crypto_1.default.verify(undefined, Buffer.from(baseString), publicKey, signatureBuffer);
return verificationResponse;
}
exports.validateSignatureHeader = validateSignatureHeader;
21 changes: 21 additions & 0 deletions lib/esm/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Request, Response } from 'express';
import { generateDigestHeader, validateDigestHeader } from './helpers/digest-helper';
import { generateSignature, generateSignatureInput, generateSignatureKey, validateSignatureHeader } from './helpers/signature-helper';
import { Config } from './types/Config';
/**
* Generate signature headers and add it to the response.
*
* @param {Request} request The request object.
* @param {Response} response The response object
* @param {Config} config The input config.
*/
declare function signMessage(request: Request, response: Response, config: Config): Promise<void>;
/**
* Verifies the signature header for the given request
*
* @param {Request} request The request object.
* @param {Config} config The input config.
* @returns Promise<boolean> True upon successful signature validation.
* */
declare function validateSignature(request: Request, config: Config): Promise<boolean>;
export { generateDigestHeader, generateSignature, generateSignatureInput, generateSignatureKey, signMessage, validateDigestHeader, validateSignature, validateSignatureHeader, Config };
95 changes: 95 additions & 0 deletions lib/esm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSignatureHeader = exports.validateSignature = exports.validateDigestHeader = exports.signMessage = exports.generateSignatureKey = exports.generateSignatureInput = exports.generateSignature = exports.generateDigestHeader = void 0;
const constants_1 = require("./constants");
const digest_helper_1 = require("./helpers/digest-helper");
Object.defineProperty(exports, "generateDigestHeader", { enumerable: true, get: function () { return digest_helper_1.generateDigestHeader; } });
Object.defineProperty(exports, "validateDigestHeader", { enumerable: true, get: function () { return digest_helper_1.validateDigestHeader; } });
const common_1 = require("./helpers/common");
const signature_helper_1 = require("./helpers/signature-helper");
Object.defineProperty(exports, "generateSignature", { enumerable: true, get: function () { return signature_helper_1.generateSignature; } });
Object.defineProperty(exports, "generateSignatureInput", { enumerable: true, get: function () { return signature_helper_1.generateSignatureInput; } });
Object.defineProperty(exports, "generateSignatureKey", { enumerable: true, get: function () { return signature_helper_1.generateSignatureKey; } });
Object.defineProperty(exports, "validateSignatureHeader", { enumerable: true, get: function () { return signature_helper_1.validateSignatureHeader; } });
/**
* Generate signature headers and add it to the response.
*
* @param {Request} request The request object.
* @param {Response} response The response object
* @param {Config} config The input config.
*/
async function signMessage(request, response, config) {
try {
const generatedHeaders = {};
if ((0, common_1.needsContentDigestValidation)(request.body)) {
const contentDigest = (0, digest_helper_1.generateDigestHeader)(request.body, config.digestAlgorithm);
response.setHeader(constants_1.constants.HEADERS.CONTENT_DIGEST, contentDigest);
generatedHeaders[constants_1.constants.HEADERS.CONTENT_DIGEST] = contentDigest;
}
const signatureInput = (0, signature_helper_1.generateSignatureInput)(generatedHeaders, config);
response.setHeader(constants_1.constants.HEADERS.SIGNATURE_INPUT, signatureInput);
// If JWE is not provided in the config, we generate it.
let signatureKey = config.jwe;
if (!signatureKey) {
signatureKey = await (0, signature_helper_1.generateSignatureKey)(config);
}
response.setHeader(constants_1.constants.HEADERS.SIGNATURE_KEY, signatureKey);
generatedHeaders[constants_1.constants.HEADERS.SIGNATURE_KEY] = signatureKey;
const signature = (0, signature_helper_1.generateSignature)(generatedHeaders, config);
response.setHeader(constants_1.constants.HEADERS.SIGNATURE, signature);
}
catch (e) {
throw new Error(e);
}
}
exports.signMessage = signMessage;
;
/**
* Verifies the signature header for the given request
*
* @param {Request} request The request object.
* @param {Config} config The input config.
* @returns Promise<boolean> True upon successful signature validation.
* */
async function validateSignature(request, config) {
let response;
try {
//Validate
if (!request || !request.headers[constants_1.constants.HEADERS.SIGNATURE]) {
throw new Error("Signature header is missing");
}
// Validate digest header, if needed.
if ((0, common_1.needsContentDigestValidation)(request.body)) {
const header = request.headers[constants_1.constants.HEADERS.CONTENT_DIGEST];
(0, digest_helper_1.validateDigestHeader)(header, request.body);
}
// Verify signature
const isSignatureValid = await (0, signature_helper_1.validateSignatureHeader)(request.headers, config);
response = isSignatureValid;
}
catch (e) {
// eslint-disable-next-line no-console
console.error(e);
response = e.message;
}
return response;
}
exports.validateSignature = validateSignature;
;
13 changes: 13 additions & 0 deletions lib/esm/types/Config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SignatureComponents } from "./SignatureComponents";
export interface Config {
digestAlgorithm: string;
jwe: string;
jwtExpiration: number;
jweHeaderParams: object;
jwtPayload: object;
masterKey: string;
privateKey: string;
publicKey: string;
signatureComponents: SignatureComponents;
signatureParams: Array<string>;
}
19 changes: 19 additions & 0 deletions lib/esm/types/Config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
8 changes: 8 additions & 0 deletions lib/esm/types/SignatureComponents.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface SignatureComponents {
authority: string;
method: string;
path: string;
requestTarget: string;
scheme: string;
targetUri: string;
}
19 changes: 19 additions & 0 deletions lib/esm/types/SignatureComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* *
* * Copyright 2022 eBay 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.
* *
*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
4 changes: 2 additions & 2 deletions package-lock.json
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
{
"name": "digital-signature-nodejs-sdk",
"version": "2.0.1",
"version": "3.0.0",
"description": "NodeJS SDK to generate and validate digital signatures",
"main": "bin/index.js",
"main": "./lib/cjs/index.js",
"module": "./lib/esm/index.js",
"files": [
"lib/"
],
"scripts": {
"start": "ts-node examples/example.ts",
"test": "eslint . && jest",
"prepare": "tsc --build tsconfig.json"
"tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
"prepublish": "npm run tsc"
},
"pre-commit": [
"test"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 7 additions & 0 deletions tsconfig-cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"outDir": "./lib/cjs"
}
}
11 changes: 5 additions & 6 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -2,8 +2,9 @@
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "bin",
"rootDir": "lib",
"declaration": true,
"outDir": "lib/esm",
"rootDir": "src",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
@@ -22,7 +23,7 @@
],
"typeRoots": [
"node_modules/@types",
"lib/types"
"src/types"
],
"esModuleInterop": true,
"skipLibCheck": true,
@@ -31,9 +32,7 @@
"useUnknownInCatchVariables": false,
},
"include": [
"lib/**/*.ts",
"lib/**/*.js",
"lib/constants.jts"
"./src"
],
"exclude": [
"node_modules",
8,454 changes: 4,185 additions & 4,269 deletions yarn.lock

Large diffs are not rendered by default.

0 comments on commit 3fde0fd

Please sign in to comment.