Skip to content

Commit

Permalink
Fix import and constructor issue after package upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
tngan committed Sep 19, 2021
1 parent 68181d8 commit b7ec658
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 60 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
"@ava/typescript": "^1.1.1",
"@types/node": "^11.11.3",
"@types/node-forge": "^0.10.3",
"@types/node-rsa": "^1.1.1",
"@types/pako": "^1.0.1",
"@types/xmldom": "^0.1.31",
"@types/uuid": "3.0.0",
"@types/xmldom": "^0.1.31",
"ava": "^3.15.0",
"coveralls": "^3.1.0",
"nyc": "^15.0.1",
Expand Down
15 changes: 13 additions & 2 deletions src/binding-redirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,18 @@ function buildRedirectURL(opts: BuildRedirectConfig) {
if (isSigned) {
const sigAlg = pvPair(urlParams.sigAlg, encodeURIComponent(entitySetting.requestSignatureAlgorithm));
const octetString = samlRequest + relayState + sigAlg;
return baseUrl + pvPair(queryParam, octetString, noParams) + pvPair(urlParams.signature, encodeURIComponent(libsaml.constructMessageSignature(queryParam + '=' + octetString, entitySetting.privateKey, entitySetting.privateKeyPass, undefined, entitySetting.requestSignatureAlgorithm)));
return baseUrl
+ pvPair(queryParam, octetString, noParams)
+ pvPair(urlParams.signature, encodeURIComponent(
libsaml.constructMessageSignature(
queryParam + '=' + octetString,
entitySetting.privateKey,
entitySetting.privateKeyPass,
undefined,
entitySetting.requestSignatureAlgorithm
).toString()
)
);
}
return baseUrl + pvPair(queryParam, samlRequest + relayState, noParams);
}
Expand Down Expand Up @@ -140,7 +151,7 @@ function loginResponseRedirectURL(requestInfo: any, entity: any, user: any = {},
const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
const nowTime = new Date();
// Five minutes later : nowtime + 5 * 60 * 1000 (in milliseconds)
const fiveMinutesLaterTime = new Date(nowTime.getTime() + 300_000 );
const fiveMinutesLaterTime = new Date(nowTime.getTime() + 300_000);
const tvalue: any = {
ID: id,
AssertionID: idpSetting.generateID(),
Expand Down
8 changes: 7 additions & 1 deletion src/binding-simplesign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ function buildSimpleSignature(opts: BuildSimpleSignConfig) : string {

const sigAlg = pvPair(urlParams.sigAlg, entitySetting.requestSignatureAlgorithm);
const octetString = context + relayState + sigAlg;
return libsaml.constructMessageSignature(queryParam + '=' + octetString, entitySetting.privateKey, entitySetting.privateKeyPass, undefined, entitySetting.requestSignatureAlgorithm);
return libsaml.constructMessageSignature(
queryParam + '=' + octetString,
entitySetting.privateKey,
entitySetting.privateKeyPass,
undefined,
entitySetting.requestSignatureAlgorithm
).toString();
}

/**
Expand Down
91 changes: 52 additions & 39 deletions src/libsaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import { DOMParser } from '@xmldom/xmldom';
import utility, { flattenDeep, isString } from './utility';
import { algorithms, wording, namespace } from './urn';
import { select, SelectedValue } from 'xpath';
import { select } from 'xpath';
import { MetadataInterface } from './metadata';
import * as nrsa from 'node-rsa';
import nrsa, { SigningSchemeHash } from 'node-rsa';
import { SignedXml, FileKeyInfo } from 'xml-crypto';
import * as xmlenc from '@authenio/xml-encryption';
import { extract } from './extractor';
Expand Down Expand Up @@ -60,7 +60,7 @@ export interface LoginResponseAttribute {

export interface LoginResponseAdditionalTemplates {
attributeStatementTemplate?: AttributeStatementTemplate;
attributeTemplate?:AttributeTemplate;
attributeTemplate?: AttributeTemplate;
}

export interface BaseSamlTemplate {
Expand Down Expand Up @@ -91,7 +91,7 @@ export interface LibSamlInterface {
getQueryParamByType: (type: string) => string;
createXPath: (local, isExtractAll?: boolean) => string;
replaceTagsByValue: (rawXML: string, tagValues: any) => string;
attributeStatementBuilder: (attributes: LoginResponseAttribute[], attributeTemplate : AttributeTemplate, attributeStatementTemplate : AttributeStatementTemplate) => string;
attributeStatementBuilder: (attributes: LoginResponseAttribute[], attributeTemplate: AttributeTemplate, attributeStatementTemplate: AttributeStatementTemplate) => string;
constructSAMLSignature: (opts: SignatureConstructor) => string;
verifySignature: (xml: string, opts) => [boolean, any];
createKeySection: (use: KeyUse, cert: string | Buffer) => {};
Expand Down Expand Up @@ -132,9 +132,9 @@ const libSaml = () => {
*
*/
const nrsaAliasMapping = {
'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'sha1',
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'sha256',
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'sha512',
'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'pkcs1-sha1',
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'pkcs1-sha256',
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'pkcs1-sha512',
};
/**
* @desc Default login request template
Expand Down Expand Up @@ -164,7 +164,7 @@ const libSaml = () => {
* @type {AttributeTemplate}
*/
const defaultAttributeTemplate = {
context: '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}"><AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</AttributeValue></Attribute>',
context: '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}"><saml:AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</saml:AttributeValue></saml:Attribute>',
};

/**
Expand All @@ -174,7 +174,7 @@ const libSaml = () => {
const defaultLoginResponseTemplate = {
context: '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AuthnStatement}{AttributeStatement}</saml:Assertion></samlp:Response>',
attributes: [],
additionalTemplates:{
additionalTemplates: {
"attributeStatementTemplate": defaultAttributeStatementTemplate,
"attributeTemplate": defaultAttributeTemplate
}
Expand All @@ -192,14 +192,14 @@ const libSaml = () => {
* @param {string} sigAlg signature algorithm
* @return {string/null} signing algorithm short-hand for the module node-rsa
*/
function getSigningScheme(sigAlg?: string): string | null {
function getSigningScheme(sigAlg?: string): SigningSchemeHash {
if (sigAlg) {
const algAlias = nrsaAliasMapping[sigAlg];
if (!(algAlias === undefined)) {
return algAlias;
}
}
return nrsaAliasMapping[signatureAlgorithms.RSA_SHA1]; // default value
return nrsaAliasMapping[signatureAlgorithms.RSA_SHA1];
}
/**
* @private
Expand Down Expand Up @@ -270,27 +270,25 @@ const libSaml = () => {
* @param {AttributeStatementTemplate} attributeStatementTemplate the attributStatement tag template to be used
* @return {string}
*/
attributeStatementBuilder(attributes: LoginResponseAttribute[], attributeTemplate : AttributeTemplate, attributeStatementTemplate : AttributeStatementTemplate): string {
if (!attributeStatementTemplate){
attributeStatementTemplate = defaultAttributeStatementTemplate;
}
if (!attributeTemplate){
attributeTemplate = defaultAttributeTemplate;
}
const attr = attributes.map(({ name, nameFormat, valueTag, valueXsiType, valueXmlnsXs, valueXmlnsXsi }) => {
const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
let attributeLine = attributeTemplate.context;
attributeLine = attributeLine.replace('{Name}',name);
attributeLine = attributeLine.replace('{NameFormat}',nameFormat);
attributeLine = attributeLine.replace('{ValueXmlnsXs}',valueXmlnsXs ? valueXmlnsXs : defaultValueXmlnsXs);
attributeLine = attributeLine.replace('{ValueXmlnsXsi}',valueXmlnsXsi ? valueXmlnsXsi : defaultValueXmlnsXsi);
attributeLine = attributeLine.replace('{ValueXsiType}',valueXsiType);
attributeLine = attributeLine.replace('{Value}',`{${tagging('attr', valueTag)}}`);
return attributeLine;
}).join('');
return attributeStatementTemplate.context.replace('{Attributes}',attr);
},
attributeStatementBuilder(
attributes: LoginResponseAttribute[],
attributeTemplate: AttributeTemplate = defaultAttributeTemplate,
attributeStatementTemplate: AttributeStatementTemplate = defaultAttributeStatementTemplate
): string {
const attr = attributes.map(({ name, nameFormat, valueTag, valueXsiType, valueXmlnsXs, valueXmlnsXsi }) => {
const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
let attributeLine = attributeTemplate.context;
attributeLine = attributeLine.replace('{Name}', name);
attributeLine = attributeLine.replace('{NameFormat}', nameFormat);
attributeLine = attributeLine.replace('{ValueXmlnsXs}', valueXmlnsXs ? valueXmlnsXs : defaultValueXmlnsXs);
attributeLine = attributeLine.replace('{ValueXmlnsXsi}', valueXmlnsXsi ? valueXmlnsXsi : defaultValueXmlnsXsi);
attributeLine = attributeLine.replace('{ValueXsiType}', valueXsiType);
attributeLine = attributeLine.replace('{Value}', `{${tagging('attr', valueTag)}}`);
return attributeLine;
}).join('');
return attributeStatementTemplate.context.replace('{Attributes}', attr);
},

/**
* @desc Construct the XML signature for POST binding
Expand Down Expand Up @@ -537,12 +535,22 @@ const libSaml = () => {
* @param {string} signingAlgorithm signing algorithm
* @return {string} message signature
*/
constructMessageSignature(octetString: string, key: string, passphrase?: string, isBase64?: boolean, signingAlgorithm?: string) {
constructMessageSignature(
octetString: string,
key: string,
passphrase?: string,
isBase64?: boolean,
signingAlgorithm?: string
) {
// Default returning base64 encoded signature
// Embed with node-rsa module
const decryptedKey = new nrsa(utility.readPrivateKey(key, passphrase), {
signingScheme: getSigningScheme(signingAlgorithm),
});
const decryptedKey = new nrsa(
utility.readPrivateKey(key, passphrase),
'private',
{
signingScheme: getSigningScheme(signingAlgorithm),
}
);
const signature = decryptedKey.sign(octetString);
// Use private key to sign data
return isBase64 !== false ? signature.toString('base64') : signature;
Expand All @@ -555,11 +563,16 @@ const libSaml = () => {
* @param {string} verifyAlgorithm algorithm used to verify
* @return {boolean} verification result
*/
verifyMessageSignature(metadata, octetString: string, signature: string | Buffer, verifyAlgorithm?: string) {
verifyMessageSignature(
metadata,
octetString: string,
signature: string | Buffer,
verifyAlgorithm?: string
) {
const signCert = metadata.getX509Certificate(certUse.signing);
const signingScheme = getSigningScheme(verifyAlgorithm);
const key = new nrsa(utility.getPublicKeyPemFromCertificate(signCert), { signingScheme });
return key.verify(new Buffer(octetString), signature);
const key = new nrsa(utility.getPublicKeyPemFromCertificate(signCert), 'public', { signingScheme });
return key.verify(Buffer.from(octetString), Buffer.from(signature));
},
/**
* @desc Get the public key in string format
Expand Down
2 changes: 1 addition & 1 deletion src/metadata-idp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { MetadataIdpOptions, MetadataIdpConstructor } from './types';
import { namespace } from './urn';
import libsaml from './libsaml';
import { isNonEmptyArray, isString } from './utility';
import * as xml from 'xml';
import xml from 'xml';

export interface IdpMetadataInterface extends MetadataInterface {

Expand Down
2 changes: 1 addition & 1 deletion src/metadata-sp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { MetadataSpConstructor, MetadataSpOptions } from './types';
import { namespace, elementsOrder as order } from './urn';
import libsaml from './libsaml';
import { isNonEmptyArray, isString } from './utility';
import * as xml from 'xml';
import xml from 'xml';

export interface SpMetadataInterface extends MetadataInterface {

Expand Down
4 changes: 2 additions & 2 deletions test/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as url from 'url';
import util from '../src/utility';
import * as tk from 'timekeeper';

import * as validator from '@authenio/samlify-xsd-schema-validator';
// import * as validator from '@authenio/samlify-xsd-schema-validator';
// import * as validator from '@authenio/samlify-validate-with-xmllint';
// import * as validator from '@authenio/samlify-node-xmllint';
// import * as validator from '@authenio/samlify-libxml-xsd';
Expand All @@ -17,7 +17,7 @@ import * as validator from '@authenio/samlify-xsd-schema-validator';
// const validator = require('@authenio/samlify-node-xmllint');
// const validator = require('@authenio/samlify-libxml-xsd');

esaml2.setSchemaValidator(validator);
// esaml2.setSchemaValidator(validator);

const isString = util.isString;

Expand Down
6 changes: 3 additions & 3 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,15 @@ test('getAssertionConsumerService with two bindings', t => {
});
test('verify stringified SAML message signed with RSA-SHA1', t => {
const signature = libsaml.constructMessageSignature(octetString, _spPrivPem, _spPrivKeyPass);
t.is(libsaml.verifyMessageSignature(SPMetadata, octetString, Buffer.from(signature, 'base64')), true);
t.is(libsaml.verifyMessageSignature(SPMetadata, octetString, Buffer.from(signature.toString(), 'base64')), true);
});
test('verify stringified SAML message signed with RSA-SHA256', t => {
const signature = libsaml.constructMessageSignature(octetStringSHA256, _spPrivPem, _spPrivKeyPass);
t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA256, Buffer.from(signature, 'base64')), true);
t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA256, Buffer.from(signature.toString(), 'base64')), true);
});
test('verify stringified SAML message signed with RSA-SHA512', t => {
const signature = libsaml.constructMessageSignature(octetStringSHA512, _spPrivPem, _spPrivKeyPass);
t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA512, Buffer.from(signature, 'base64')), true);
t.is(libsaml.verifyMessageSignature(SPMetadata, octetStringSHA512, Buffer.from(signature.toString(), 'base64')), true);
});
test('construct signature with RSA-SHA1', t => {
t.is(libsaml.constructSAMLSignature({
Expand Down
22 changes: 12 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
# yarn lockfile v1


"@authenio/xml-encryption@^1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@authenio/xml-encryption/-/xml-encryption-1.2.4.tgz#6e3310a2d629859554404cc1a213a68f4f055c08"
integrity sha512-71ENOSuUbXh5WO5oVQx7JPMdbPoXOYxfZvxWlaQEMzy8/psz1UR2r2QVsFiqbnlaJUGaKUo/HoNPB3Bq97zVxg==
"@authenio/xml-encryption@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@authenio/xml-encryption/-/xml-encryption-1.3.0.tgz#9079f227d5f54daf8bec25245a7a98a46186445d"
integrity sha512-643tVCHSXDXOk8vBt37I0iSgvoO+yJrj1iY/RuQDePoyF2jveUyQhSHYmFdXsG63Zmx/4zmKU9DFjGGKcuIduw==
dependencies:
"@xmldom/xmldom" "^0.7.0"
escape-html "^1.0.3"
node-forge "^0.10.0"
xmldom "~0.6.0"
xpath "0.0.32"

"@ava/typescript@^1.1.1":
Expand Down Expand Up @@ -261,6 +261,13 @@
dependencies:
"@types/node" "*"

"@types/node-rsa@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/node-rsa/-/node-rsa-1.1.1.tgz#c3649c0ae7fdb93d3e65076a08d3fc9feb017a67"
integrity sha512-itzxtaBgk4OMbrCawVCvas934waMZWjW17v7EYgFVlfYS/cl0/P7KZdojWCq9SDJMI5cnLQLUP8ayhVCTY8TEg==
dependencies:
"@types/node" "*"

"@types/node@*":
version "13.13.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.5.tgz#96ec3b0afafd64a4ccea9107b75bf8489f0e5765"
Expand Down Expand Up @@ -2919,11 +2926,6 @@ xml@^1.0.1:
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=

xmldom@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.6.0.tgz#43a96ecb8beece991cef382c08397d82d4d0c46f"
integrity sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==

[email protected], xpath@^0.0.32:
version "0.0.32"
resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.32.tgz#1b73d3351af736e17ec078d6da4b8175405c48af"
Expand Down

0 comments on commit b7ec658

Please sign in to comment.