diff --git a/.vscode/settings.json b/.vscode/settings.json index 5470362f..db9e4a58 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "canonicalized", "codecov", "feide", + "HMAC", "reserialization", "wsfederation", "wssecurity" diff --git a/README.md b/README.md index 2a349ab2..a136027d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## xml-crypto +# xml-crypto ![Build](https://github.com/yaronn/xml-crypto/actions/workflows/ci.yml/badge.svg) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/from-referrer/) @@ -8,9 +8,12 @@ An xml digital signature library for node. Xml encryption is coming soon. Writte For more information visit [my blog](http://webservices20.blogspot.com/) or [my twitter](https://twitter.com/YaronNaveh). ## Install + Install with [npm](http://github.com/isaacs/npm): - npm install xml-crypto +```shell +npm install xml-crypto +``` A pre requisite it to have [openssl](http://www.openssl.org/) installed and its /bin to be on the system path. I used version 1.0.1c but it should work on older versions too. @@ -18,46 +21,48 @@ A pre requisite it to have [openssl](http://www.openssl.org/) installed and its ### Canonicalization and Transformation Algorithms -* Canonicalization http://www.w3.org/TR/2001/REC-xml-c14n-20010315 -* Canonicalization with comments http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments -* Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n# -* Exclusive Canonicalization with comments http://www.w3.org/2001/10/xml-exc-c14n#WithComments -* Enveloped Signature transform http://www.w3.org/2000/09/xmldsig#enveloped-signature +- Canonicalization http://www.w3.org/TR/2001/REC-xml-c14n-20010315 +- Canonicalization with comments http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments +- Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n# +- Exclusive Canonicalization with comments http://www.w3.org/2001/10/xml-exc-c14n#WithComments +- Enveloped Signature transform http://www.w3.org/2000/09/xmldsig#enveloped-signature ### Hashing Algorithms -* SHA1 digests http://www.w3.org/2000/09/xmldsig#sha1 -* SHA256 digests http://www.w3.org/2001/04/xmlenc#sha256 -* SHA512 digests http://www.w3.org/2001/04/xmlenc#sha512 +- SHA1 digests http://www.w3.org/2000/09/xmldsig#sha1 +- SHA256 digests http://www.w3.org/2001/04/xmlenc#sha256 +- SHA512 digests http://www.w3.org/2001/04/xmlenc#sha512 ### Signature Algorithms -* RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1 -* RSA-SHA256 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 -* RSA-SHA512 http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 +- RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1 +- RSA-SHA256 http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 +- RSA-SHA512 http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 HMAC-SHA1 is also available but it is disabled by default -* HMAC-SHA1 http://www.w3.org/2000/09/xmldsig#hmac-sha1 + +- HMAC-SHA1 http://www.w3.org/2000/09/xmldsig#hmac-sha1 to enable HMAC-SHA1, do: + ```javascript -require( 'xml-crypto' ).SignedXml.enableHMAC(); +require("xml-crypto").SignedXml.enableHMAC(); ``` + This will enable HMAC and disable digital signature algorithms. Due to key confusion issues, it is risky to have both HMAC-based and public key digital signature algorithms enabled at same time. by default the following algorithms are used: -*Canonicalization/Transformation Algorithm:* Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n# +_Canonicalization/Transformation Algorithm:_ Exclusive Canonicalization http://www.w3.org/2001/10/xml-exc-c14n# -*Hashing Algorithm:* SHA1 digest http://www.w3.org/2000/09/xmldsig#sha1 +_Hashing Algorithm:_ SHA1 digest http://www.w3.org/2000/09/xmldsig#sha1 -*Signature Algorithm:* RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1 +_Signature Algorithm:_ RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1 [You are able to extend xml-crypto with custom algorithms.](#customizing-algorithms) - ## Signing Xml documents When signing a xml document you can specify the following properties on a `SignedXml` instance to customize the signature process: @@ -69,48 +74,42 @@ When signing a xml document you can specify the following properties on a `Signe Use this code: -`````javascript - var SignedXml = require('xml-crypto').SignedXml - , fs = require('fs') - - var xml = "" + - "" + - "Harry Potter" + - "" + - "" +```javascript +var SignedXml = require("xml-crypto").SignedXml, + fs = require("fs"); - var sig = new SignedXml() - sig.addReference("//*[local-name(.)='book']") - sig.signingKey = fs.readFileSync("client.pem") - sig.computeSignature(xml) - fs.writeFileSync("signed.xml", sig.getSignedXml()) +var xml = "" + "" + "Harry Potter" + "" + ""; -````` +var sig = new SignedXml(); +sig.addReference("//*[local-name(.)='book']"); +sig.signingKey = fs.readFileSync("client.pem"); +sig.computeSignature(xml); +fs.writeFileSync("signed.xml", sig.getSignedXml()); +``` The result will be: - -`````xml - - - Harry Potter - - - - - - - - - - - cdiS43aFDQMnb3X8yaIUej3+z9Q= - - - vhWzpQyIYuncHUZV9W...[long base64 removed]... - - -````` +```xml + + + Harry Potter + + + + + + + + + + + cdiS43aFDQMnb3X8yaIUej3+z9Q= + + + vhWzpQyIYuncHUZV9W...[long base64 removed]... + + +``` Note: @@ -124,45 +123,51 @@ When verifying a xml document you must specify the following properties on a ``S You can use any dom parser you want in your code (or none, depending on your usage). This sample uses [xmldom](https://github.com/jindw/xmldom) so you should install it first: - npm install xmldom +```shell +npm install xmldom +``` Example: -`````javascript - var select = require('xml-crypto').xpath - , dom = require('@xmldom/xmldom').DOMParser - , SignedXml = require('xml-crypto').SignedXml - , FileKeyInfo = require('xml-crypto').FileKeyInfo - , fs = require('fs') - - var xml = fs.readFileSync("signed.xml").toString() - var doc = new dom().parseFromString(xml) - - var signature = select(doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']")[0] - var sig = new SignedXml() - sig.keyInfoProvider = new FileKeyInfo("client_public.pem") - sig.loadSignature(signature) - var res = sig.checkSignature(xml) - if (!res) console.log(sig.validationErrors) -````` +```javascript +var select = require("xml-crypto").xpath, + dom = require("@xmldom/xmldom").DOMParser, + SignedXml = require("xml-crypto").SignedXml, + FileKeyInfo = require("xml-crypto").FileKeyInfo, + fs = require("fs"); + +var xml = fs.readFileSync("signed.xml").toString(); +var doc = new dom().parseFromString(xml); + +var signature = select( + doc, + "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']" +)[0]; +var sig = new SignedXml(); +sig.keyInfoProvider = new FileKeyInfo("client_public.pem"); +sig.loadSignature(signature); +var res = sig.checkSignature(xml); +if (!res) console.log(sig.validationErrors); +``` if the verification process fails `sig.validationErrors` will have the errors. In order to protect from some attacks we must check the content we want to use is the one that has been signed: -`````javascript - var elem = select(doc, "/xpath_to_interesting_element"); - var uri = sig.references[0].uri; // might not be 0 - depending on the document you verify - var id = (uri[0] === '#') ? uri.substring(1) : uri; - if (elem.getAttribute('ID') != id && elem.getAttribute('Id') != id && elem.getAttribute('id') != id) - throw new Error('the interesting element was not the one verified by the signature') -````` + +```javascript +var elem = select(doc, "/xpath_to_interesting_element"); +var uri = sig.references[0].uri; // might not be 0 - depending on the document you verify +var id = uri[0] === "#" ? uri.substring(1) : uri; +if (elem.getAttribute("ID") != id && elem.getAttribute("Id") != id && elem.getAttribute("id") != id) + throw new Error("the interesting element was not the one verified by the signature"); +``` Note: The xml-crypto api requires you to supply it separately the xml signature ("<Signature>...</Signature>", in loadSignature) and the signed xml (in checkSignature). The signed xml may or may not contain the signature in it, but you are still required to supply the signature separately. - ### Caring for Implicit transform + If you fail to verify signed XML, then one possible cause is that there are some hidden implicit transforms(#). (#) Normalizing XML document to be verified. i.e. remove extra space within a tag, sorting attributes, importing namespace declared in ancestor nodes, etc. @@ -172,11 +177,11 @@ which makes XML developers confused and then leads to incorrect implementation f If you keep failing verification, it is worth trying to guess such a hidden transform and specify it to the option as below: ```javascript -var option = {implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"]} -var sig = new SignedXml(null, option) -sig.keyInfoProvider = new FileKeyInfo("client_public.pem") -sig.loadSignature(signature) -var res = sig.checkSignature(xml) +var option = { implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"] }; +var sig = new SignedXml(null, option); +sig.keyInfoProvider = new FileKeyInfo("client_public.pem"); +sig.loadSignature(signature); +var res = sig.checkSignature(xml); ``` You might find it difficult to guess such transforms, but there are typical transforms you can try. @@ -199,23 +204,23 @@ The `SignedXml` constructor provides an abstraction for sign and verify xml docu - `idMode` - if the value of `"wssecurity"` is passed it will create/validate id's with the ws-security namespace. -*API* +#### API A `SignedXml` object provides the following methods: To sign xml documents: - `addReference(xpath, [transforms], [digestAlgorithm])` - adds a reference to a xml element where: - - `xpath` - a string containing a XPath expression referencing a xml element - - `transforms` - an array of [transform algorithms](#canonicalization-and-transformation-algorithms), the referenced element will be transformed for each value in the array - - `digestAlgorithm` - one of the supported [hashing algorithms](#hashing-algorithms) + - `xpath` - a string containing a XPath expression referencing a xml element + - `transforms` - an array of [transform algorithms](#canonicalization-and-transformation-algorithms), the referenced element will be transformed for each value in the array + - `digestAlgorithm` - one of the supported [hashing algorithms](#hashing-algorithms) - `computeSignature(xml, [options])` - compute the signature of the given xml where: - - `xml` - a string containing a xml document - - `options` - an object with the following properties: - - `prefix` - adds this value as a prefix for the generated signature tags - - `attrs` - a hash of attributes and values `attrName: value` to add to the signature root node - - `location` - customize the location of the signature, pass an object with a `reference` key which should contain a XPath expression to a reference node, an `action` key which should contain one of the following values: `append`, `prepend`, `before`, `after` - - `existingPrefixes` - A hash of prefixes and namespaces `prefix: namespace` that shouldn't be in the signature because they already exist in the xml + - `xml` - a string containing a xml document + - `options` - an object with the following properties: + - `prefix` - adds this value as a prefix for the generated signature tags + - `attrs` - a hash of attributes and values `attrName: value` to add to the signature root node + - `location` - customize the location of the signature, pass an object with a `reference` key which should contain a XPath expression to a reference node, an `action` key which should contain one of the following values: `append`, `prepend`, `before`, `after` + - `existingPrefixes` - A hash of prefixes and namespaces `prefix: namespace` that shouldn't be in the signature because they already exist in the xml - `getSignedXml()` - returns the original xml document with the signature in it, **must be called only after `computeSignature`** - `getSignatureXml()` - returns just the signature part, **must be called only after `computeSignature`** - `getOriginalXmlWithIds()` - returns the original xml with Id attributes added on relevant elements (required for validation), **must be called only after `computeSignature`** @@ -223,10 +228,9 @@ To sign xml documents: To verify xml documents: - `loadSignature(signatureXml)` - loads the signature where: - - `signatureXml` - a string or node object (like an [xml-dom](https://github.com/jindw/xmldom) node) containing the xml representation of the signature + - `signatureXml` - a string or node object (like an [xml-dom](https://github.com/jindw/xmldom) node) containing the xml representation of the signature - `checkSignature(xml)` - validates the given xml document and returns true if the validation was successful, `sig.validationErrors` will have the validation errors if any, where: - - `xml` - a string containing a xml document - + - `xml` - a string containing a xml document ### FileKeyInfo @@ -236,143 +240,135 @@ A basic key info provider implementation using `fs.readFileSync(file)`, is const See [verifying xml documents](#verifying-xml-documents) for an example usage - ## Customizing Algorithms + The following sample shows how to sign a message using custom algorithms. First import some modules: -`````javascript - var SignedXml = require('xml-crypto').SignedXml - , fs = require('fs') -````` - +```javascript +var SignedXml = require("xml-crypto").SignedXml, + fs = require("fs"); +``` Now define the extension point you want to implement. You can choose one or more. A key info provider is used to extract and construct the key and the KeyInfo xml section. Implement it if you want to create a signature with a KeyInfo section, or you want to read your key in a different way then the default file read option. -`````javascript - /**/ - function MyKeyInfo() { - this.getKeyInfo = function(key, prefix) { - prefix = prefix || '' - prefix = prefix ? prefix + ':' : prefix - return "<" + prefix + "X509Data>" - } - this.getKey = function(keyInfo) { - //you can use the keyInfo parameter to extract the key in any way you want - return fs.readFileSync("key.pem") - } - } -````` - -A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the default SHA1. - -`````javascript - function MyDigest() { +```javascript +function MyKeyInfo() { + this.getKeyInfo = function (key, prefix) { + prefix = prefix || ""; + prefix = prefix ? prefix + ":" : prefix; + return "<" + prefix + "X509Data>"; + }; + this.getKey = function (keyInfo) { + //you can use the keyInfo parameter to extract the key in any way you want + return fs.readFileSync("key.pem"); + }; +} +``` - this.getHash = function(xml) { - return "the base64 hash representation of the given xml string" - } +A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the default SHA1. - this.getAlgorithmName = function() { - return "http://myDigestAlgorithm" - } - } -````` +```javascript +function MyDigest() { + this.getHash = function (xml) { + return "the base64 hash representation of the given xml string"; + }; + + this.getAlgorithmName = function () { + return "http://myDigestAlgorithm"; + }; +} +``` A custom signing algorithm. The default is RSA-SHA1 -`````javascript - function MySignatureAlgorithm() { - - /*sign the given SignedInfo using the key. return base64 signature value*/ - this.getSignature = function(signedInfo, signingKey) { - return "signature of signedInfo as base64..." - } - this.getAlgorithmName = function() { - return "http://mySigningAlgorithm" - } - - } -````` +```javascript +function MySignatureAlgorithm() { + /*sign the given SignedInfo using the key. return base64 signature value*/ + this.getSignature = function (signedInfo, signingKey) { + return "signature of signedInfo as base64..."; + }; + + this.getAlgorithmName = function () { + return "http://mySigningAlgorithm"; + }; +} +``` Custom transformation algorithm. The default is exclusive canonicalization. -`````javascript - function MyTransformation() { - - /*given a node (from the xmldom module) return its canonical representation (as string)*/ - this.process = function(node) { - //you should apply your transformation before returning - return node.toString() - } +```javascript +function MyTransformation() { + /*given a node (from the xmldom module) return its canonical representation (as string)*/ + this.process = function (node) { + //you should apply your transformation before returning + return node.toString(); + }; + + this.getAlgorithmName = function () { + return "http://myTransformation"; + }; +} +``` - this.getAlgorithmName = function() { - return "http://myTransformation" - } - } -````` Custom canonicalization is actually the same as custom transformation. It is applied on the SignedInfo rather than on references. -`````javascript - function MyCanonicalization() { - - /*given a node (from the xmldom module) return its canonical representation (as string)*/ - this.process = function(node) { - //you should apply your transformation before returning - return "< x/>" - } - - this.getAlgorithmName = function() { - return "http://myCanonicalization" - } - } -````` +```javascript +function MyCanonicalization() { + /*given a node (from the xmldom module) return its canonical representation (as string)*/ + this.process = function (node) { + //you should apply your transformation before returning + return "< x/>"; + }; + + this.getAlgorithmName = function () { + return "http://myCanonicalization"; + }; +} +``` Now you need to register the new algorithms: -`````javascript - /*register all the custom algorithms*/ +```javascript +/*register all the custom algorithms*/ - SignedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation - SignedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization - SignedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest - SignedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm -````` +SignedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation; +SignedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization; +SignedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest; +SignedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm; +``` Now do the signing. Note how we configure the signature to use the above algorithms: -`````javascript - function signXml(xml, xpath, key, dest) - { - var sig = new SignedXml() - - /*configure the signature object to use the custom algorithms*/ - sig.signatureAlgorithm = "http://mySignatureAlgorithm" - sig.keyInfoProvider = new MyKeyInfo() - sig.canonicalizationAlgorithm = "http://MyCanonicalization" - sig.addReference("//*[local-name(.)='x']", ["http://MyTransformation"], "http://myDigestAlgorithm") - - sig.signingKey = fs.readFileSync(key) - sig.addReference(xpath) - sig.computeSignature(xml) - fs.writeFileSync(dest, sig.getSignedXml()) - } - - var xml = "" + - "" + - "Harry Potter" + - "" - "" - - signXml(xml, - "//*[local-name(.)='book']", - "client.pem", - "result.xml") -````` +```javascript +function signXml(xml, xpath, key, dest) { + var sig = new SignedXml(); + + /*configure the signature object to use the custom algorithms*/ + sig.signatureAlgorithm = "http://mySignatureAlgorithm"; + sig.keyInfoProvider = new MyKeyInfo(); + sig.canonicalizationAlgorithm = "http://MyCanonicalization"; + sig.addReference( + "//*[local-name(.)='x']", + ["http://MyTransformation"], + "http://myDigestAlgorithm" + ); + + sig.signingKey = fs.readFileSync(key); + sig.addReference(xpath); + sig.computeSignature(xml); + fs.writeFileSync(dest, sig.getSignedXml()); +} + +var xml = "" + "" + "Harry Potter" + ""; +(""); + +signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml"); +``` You can always look at the actual code as a sample (or drop me a [mail](mailto:yaronn01@gmail.com)). @@ -380,111 +376,117 @@ You can always look at the actual code as a sample (or drop me a [mail](mailto:y If the private key is not stored locally and you wish to use a signing server or Hardware Security Module (HSM) to sign documents you can create a custom signing algorithm that uses an asynchronous callback. -`````javascript - function AsyncSignatureAlgorithm() { - this.getSignature = function (signedInfo, signingKey, callback) { - var signer = crypto.createSign("RSA-SHA1") - signer.update(signedInfo) - var res = signer.sign(signingKey, 'base64') - //Do some asynchronous things here - callback(null, res) - } - this.getAlgorithmName = function () { - return "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - } - } - - SignedXml.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm - var sig = new SignedXml() - sig.signatureAlgorithm = "http://asyncSignatureAlgorithm" - sig.computeSignature(xml, opts, function(err){ - var signedResponse = sig.getSignedXml() - }) -````` +```javascript +function AsyncSignatureAlgorithm() { + this.getSignature = function (signedInfo, signingKey, callback) { + var signer = crypto.createSign("RSA-SHA1"); + signer.update(signedInfo); + var res = signer.sign(signingKey, "base64"); + //Do some asynchronous things here + callback(null, res); + }; + this.getAlgorithmName = function () { + return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + }; +} + +SignedXml.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm; +var sig = new SignedXml(); +sig.signatureAlgorithm = "http://asyncSignatureAlgorithm"; +sig.computeSignature(xml, opts, function (err) { + var signedResponse = sig.getSignedXml(); +}); +``` The function `sig.checkSignature` may also use a callback if asynchronous verification is needed. ## X.509 / Key formats -Xml-Crypto internally relies on node's crypto module. This means pem encoded certificates are supported. So to sign an xml use key.pem that looks like this (only the begining of the key content is shown): - -----BEGIN PRIVATE KEY----- - MIICdwIBADANBgkqhkiG9w0... - -----END PRIVATE KEY----- +Xml-Crypto internally relies on node's crypto module. This means pem encoded certificates are supported. So to sign an xml use key.pem that looks like this (only the beginning of the key content is shown): + +```text +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0... +-----END PRIVATE KEY----- +``` And for verification use key_public.pem: - -----BEGIN CERTIFICATE----- - MIIBxDCCAW6gAwIBAgIQxUSX... - -----END CERTIFICATE----- +```text +-----BEGIN CERTIFICATE----- +MIIBxDCCAW6gAwIBAgIQxUSX... +-----END CERTIFICATE----- +``` -**Converting .pfx certificates to pem** +### Converting .pfx certificates to pem If you have .pfx certificates you can convert them to .pem using [openssl](http://www.openssl.org/): - openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem +```shell +openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem +``` -Then you could use the result as is for the purpose of signing. For the purpose of validation open the resulting .pem with a text editor and copy from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE----- (including) to a new text file and save it as .pem. +Then you could use the result as is for the purpose of signing. For the purpose of validation open the resulting .pem with a text editor and copy from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE----- (including) to a new text file and save it as .pem. ## Examples -- [how to sign a root node](#) *coming soon* +### how to sign a root node (_coming soon_) + +### how to add a prefix for the signature -###how to add a prefix for the signature### -Use the `prefix` option when calling `computeSignature` to add a prefix to the signature. -`````javascript -var SignedXml = require('xml-crypto').SignedXml - , fs = require('fs'); +Use the `prefix` option when calling `computeSignature` to add a prefix to the signature. + +```javascript +var SignedXml = require("xml-crypto").SignedXml, + fs = require("fs"); -var xml = "" + - "" + - "Harry Potter" + - "" + - ""; +var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new SignedXml(); sig.addReference("//*[local-name(.)='book']"); sig.signingKey = fs.readFileSync("client.pem"); -sig.computeSignature(xml,{ - prefix: 'ds' +sig.computeSignature(xml, { + prefix: "ds", }); -````` +``` + +### how to specify the location of the signature + +Use the `location` option when calling `computeSignature` to move the signature around. +Set `action` to one of the following: -###how to specify the location of the signature### -Use the `location` option when calling `computeSignature` to move the signature around. -Set `action` to one of the following: - append(default) - append to the end of the xml document - prepend - prepend to the xml document - before - prepend to a specific node (use the `referenceNode` property) - after - append to specific node (use the `referenceNode` property) -`````javascript -var SignedXml = require('xml-crypto').SignedXml - , fs = require('fs'); +```javascript +var SignedXml = require("xml-crypto").SignedXml, + fs = require("fs"); -var xml = "" + - "" + - "Harry Potter" + - "" + - ""; +var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new SignedXml(); sig.addReference("//*[local-name(.)='book']"); sig.signingKey = fs.readFileSync("client.pem"); -sig.computeSignature(xml,{ - location: { reference: "//*[local-name(.)='book']", action: "after" } //This will place the signature after the book element +sig.computeSignature(xml, { + location: { reference: "//*[local-name(.)='book']", action: "after" }, //This will place the signature after the book element }); +``` -````` -*more examples coming soon* +### more examples (_coming soon_) ## Development + The test framework is [nodeunit](https://github.com/caolan/nodeunit). To run tests use: - $> npm test +```shell +npm test +``` ## More information -Visit my [blog](http://webservices20.blogspot.com/) or my [twitter](http://twitter.com/#!/YaronNaveh) +Visit my [blog](http://webservices20.blogspot.com/) or my [twitter](http://twitter.com/#!/YaronNaveh) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/yaronn/xml-crypto/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/example/example.js b/example/example.js index 351a3052..cc2428c9 100644 --- a/example/example.js +++ b/example/example.js @@ -1,49 +1,43 @@ -var select = require('xml-crypto').xpath - , dom = require('@xmldom/xmldom').DOMParser - , SignedXml = require('xml-crypto').SignedXml - , FileKeyInfo = require('xml-crypto').FileKeyInfo - , fs = require('fs') - -function signXml(xml, xpath, key, dest) -{ - var sig = new SignedXml() - sig.signingKey = fs.readFileSync(key) - sig.addReference(xpath) - sig.computeSignature(xml) - fs.writeFileSync(dest, sig.getSignedXml()) +/* eslint-disable no-console */ + +var select = require("xml-crypto").xpath, + dom = require("@xmldom/xmldom").DOMParser, + SignedXml = require("xml-crypto").SignedXml, + FileKeyInfo = require("xml-crypto").FileKeyInfo, + fs = require("fs"); + +function signXml(xml, xpath, key, dest) { + var sig = new SignedXml(); + sig.signingKey = fs.readFileSync(key); + sig.addReference(xpath); + sig.computeSignature(xml); + fs.writeFileSync(dest, sig.getSignedXml()); } -function validateXml(xml, key) -{ - var doc = new dom().parseFromString(xml) - var signature = select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0] - var sig = new SignedXml() - sig.keyInfoProvider = new FileKeyInfo(key) - sig.loadSignature(signature.toString()) - var res = sig.checkSignature(xml) - if (!res) console.log(sig.validationErrors) +function validateXml(xml, key) { + var doc = new dom().parseFromString(xml); + var signature = select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var sig = new SignedXml(); + sig.keyInfoProvider = new FileKeyInfo(key); + sig.loadSignature(signature.toString()); + var res = sig.checkSignature(xml); + if (!res) console.log(sig.validationErrors); return res; } -var xml = "" + - "" + - "Harry Potter" + - "" + - "" +var xml = "" + "" + "Harry Potter" + "" + ""; //sign an xml document -signXml(xml, - "//*[local-name(.)='book']", - "client.pem", - "result.xml") +signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml"); -console.log("xml signed succesfully") +console.log("xml signed successfully"); -var signedXml = fs.readFileSync("result.xml").toString() -console.log("validating signature...") +var signedXml = fs.readFileSync("result.xml").toString(); +console.log("validating signature..."); //validate an xml document -if (validateXml(signedXml, "client_public.pem")) - console.log("signature is valid") -else - console.log("signature not valid") +if (validateXml(signedXml, "client_public.pem")) console.log("signature is valid"); +else console.log("signature not valid"); diff --git a/index.d.ts b/index.d.ts index c54595c3..20644918 100644 --- a/index.d.ts +++ b/index.d.ts @@ -5,97 +5,102 @@ /// -import { SelectedValue } from 'xpath'; +import { SelectedValue } from "xpath"; export class HashAlgorithm { - getAlgorithmName(): string; - getHash(xml: string): string; + getAlgorithmName(): string; + getHash(xml: string): string; } export interface Reference { - xpath: string; - transforms?: ReadonlyArray | undefined; - digestAlgorithm?: string | undefined; - uri?: string | undefined; - digestValue?: string | undefined; - inclusiveNamespacesPrefixList?: string | undefined; - isEmptyUri?: boolean | undefined; + xpath: string; + transforms?: ReadonlyArray | undefined; + digestAlgorithm?: string | undefined; + uri?: string | undefined; + digestValue?: string | undefined; + inclusiveNamespacesPrefixList?: string | undefined; + isEmptyUri?: boolean | undefined; } export class SignatureAlgorithm { - getAlgorithmName(): string; - getSignature(signedInfo: Node, signingKey: Buffer): string; + getAlgorithmName(): string; + getSignature(signedInfo: Node, signingKey: Buffer): string; } export class TransformationAlgorithm { - getAlgorithmName(): string; - process(node: Node): string; + getAlgorithmName(): string; + process(node: Node): string; } export class SignedXml { - static CanonicalizationAlgorithms: {[uri: string]: new () => TransformationAlgorithm }; - static HashAlgorithms: {[uri: string]: new () => HashAlgorithm}; - static SignatureAlgorithms: {[uri: string]: new () => SignatureAlgorithm}; - canonicalizationAlgorithm: string; - inclusiveNamespacesPrefixList: string; - keyInfoProvider: KeyInfo; - references: Reference[]; - signatureAlgorithm: string; - signingKey: Buffer | string; - validationErrors: string[]; - constructor(idMode?: string | null, options?: { - canonicalizationAlgorithm?: string | undefined - inclusiveNamespacesPrefixList?: string | undefined - idAttribute?: string | undefined - implicitTransforms?: ReadonlyArray | undefined - signatureAlgorithm?: string | undefined - }) - addReference( - xpath: string, - transforms?: ReadonlyArray, - digestAlgorithm?: string, - uri?: string, - digestValue?: string, - inclusiveNamespacesPrefixList?: string, - isEmptyUri?: boolean - ): void; - checkSignature(xml: string): boolean; - computeSignature( - xml: string, - opts?: { - prefix?: string | undefined, - attrs?: {[key: string]: any} | undefined, - location?: { - reference: string, - action: 'append' | 'prepend' | 'before' | 'after' - } | undefined, - existingPrefixes?: {[prefix: string]: string} | undefined - } - ): void; - getOriginalXmlWithIds(): string; - getSignatureXml(): string; - getSignedXml(): string; - loadSignature(signatureNode: string | Node): void; + static CanonicalizationAlgorithms: { [uri: string]: new () => TransformationAlgorithm }; + static HashAlgorithms: { [uri: string]: new () => HashAlgorithm }; + static SignatureAlgorithms: { [uri: string]: new () => SignatureAlgorithm }; + canonicalizationAlgorithm: string; + inclusiveNamespacesPrefixList: string; + keyInfoProvider: KeyInfo; + references: Reference[]; + signatureAlgorithm: string; + signingKey: Buffer | string; + validationErrors: string[]; + constructor( + idMode?: string | null, + options?: { + canonicalizationAlgorithm?: string | undefined; + inclusiveNamespacesPrefixList?: string | undefined; + idAttribute?: string | undefined; + implicitTransforms?: ReadonlyArray | undefined; + signatureAlgorithm?: string | undefined; + } + ); + addReference( + xpath: string, + transforms?: ReadonlyArray, + digestAlgorithm?: string, + uri?: string, + digestValue?: string, + inclusiveNamespacesPrefixList?: string, + isEmptyUri?: boolean + ): void; + checkSignature(xml: string): boolean; + computeSignature( + xml: string, + opts?: { + prefix?: string | undefined; + attrs?: { [key: string]: any } | undefined; + location?: + | { + reference: string; + action: "append" | "prepend" | "before" | "after"; + } + | undefined; + existingPrefixes?: { [prefix: string]: string } | undefined; + } + ): void; + getOriginalXmlWithIds(): string; + getSignatureXml(): string; + getSignedXml(): string; + loadSignature(signatureNode: string | Node): void; } export interface KeyInfo { - getKey(keyInfo?: Node[] | null): Buffer; - getKeyInfo(key?: string, prefix?: string): string; - attrs?: {[key: string]: any} | undefined; + getKey(keyInfo?: Node[] | null): Buffer; + getKeyInfo(key?: string, prefix?: string): string; + attrs?: { [key: string]: any } | undefined; } export class FileKeyInfo implements KeyInfo { - file: string; - constructor(file?: string); - getKey(keyInfo?: Node[] | null): Buffer; - getKeyInfo(key?: string, prefix?: string): string; + file: string; + constructor(file?: string); + getKey(keyInfo?: Node[] | null): Buffer; + getKeyInfo(key?: string, prefix?: string): string; } export class StringKeyInfo implements KeyInfo { - key: string; - constructor(key?: string); - getKey(keyInfo?: Node[] | null): Buffer; - getKeyInfo(key?: string, prefix?: string): string; + key: string; + constructor(key?: string); + getKey(keyInfo?: Node[] | null): Buffer; + getKeyInfo(key?: string, prefix?: string): string; } export function xpath(node: Node, xpath: string): SelectedValue[]; diff --git a/index.js b/index.js index 23abdf5a..b9c6fcf1 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ -var select = require('xpath').select +var select = require("xpath").select; -module.exports = require('./lib/signed-xml') -module.exports.xpath = function(node, xpath) { - return select(xpath, node) -} \ No newline at end of file +module.exports = require("./lib/signed-xml"); +module.exports.xpath = function (node, xpath) { + return select(xpath, node); +}; diff --git a/lib/c14n-canonicalization.js b/lib/c14n-canonicalization.js index 9d0419b4..0e4ba696 100644 --- a/lib/c14n-canonicalization.js +++ b/lib/c14n-canonicalization.js @@ -1,45 +1,56 @@ /* jshint laxcomma: true */ -var utils = require('./utils'); +var utils = require("./utils"); exports.C14nCanonicalization = C14nCanonicalization; exports.C14nCanonicalizationWithComments = C14nCanonicalizationWithComments; function C14nCanonicalization() { - this.includeComments = false; -}; - -C14nCanonicalization.prototype.attrCompare = function(a,b) { - if (!a.namespaceURI && b.namespaceURI) { return -1; } - if (!b.namespaceURI && a.namespaceURI) { return 1; } + this.includeComments = false; +} - var left = a.namespaceURI + a.localName - var right = b.namespaceURI + b.localName +C14nCanonicalization.prototype.attrCompare = function (a, b) { + if (!a.namespaceURI && b.namespaceURI) { + return -1; + } + if (!b.namespaceURI && a.namespaceURI) { + return 1; + } - if (left===right) return 0 - else if (left 0){ + + if (Array.isArray(ancestorNamespaces) && ancestorNamespaces.length > 0) { // Remove namespaces which are already present in nsListToRender - for(var p1 in ancestorNamespaces){ - if(!ancestorNamespaces.hasOwnProperty(p1)) continue; + for (var p1 in ancestorNamespaces) { + if (!ancestorNamespaces.hasOwnProperty(p1)) continue; var alreadyListed = false; - for(var p2 in nsListToRender){ - if(nsListToRender[p2].prefix === ancestorNamespaces[p1].prefix - && nsListToRender[p2].namespaceURI === ancestorNamespaces[p1].namespaceURI) - { + for (var p2 in nsListToRender) { + if ( + nsListToRender[p2].prefix === ancestorNamespaces[p1].prefix && + nsListToRender[p2].namespaceURI === ancestorNamespaces[p1].namespaceURI + ) { alreadyListed = true; } } - - if(!alreadyListed){ + + if (!alreadyListed) { nsListToRender.push(ancestorNamespaces[p1]); } } @@ -133,27 +162,41 @@ C14nCanonicalization.prototype.renderNs = function(node, prefixesInScope, defaul //render namespaces for (a in nsListToRender) { - if (!nsListToRender.hasOwnProperty(a)) { continue; } + if (!nsListToRender.hasOwnProperty(a)) { + continue; + } p = nsListToRender[a]; res.push(" xmlns:", p.prefix, '="', p.namespaceURI, '"'); } - return {"rendered": res.join(""), "newDefaultNs": newDefaultNs}; + return { rendered: res.join(""), newDefaultNs: newDefaultNs }; }; -C14nCanonicalization.prototype.processInner = function(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) { - - if (node.nodeType === 8) { return this.renderComment(node); } - if (node.data) { return utils.encodeSpecialCharactersInText(node.data); } +C14nCanonicalization.prototype.processInner = function ( + node, + prefixesInScope, + defaultNs, + defaultNsForPrefix, + ancestorNamespaces +) { + if (node.nodeType === 8) { + return this.renderComment(node); + } + if (node.data) { + return utils.encodeSpecialCharactersInText(node.data); + } - var i, pfxCopy - , ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) - , res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"]; + var i, + pfxCopy, + ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces), + res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"]; for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); - res.push(this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, [])); + res.push( + this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, []) + ); } res.push(""); @@ -162,16 +205,17 @@ C14nCanonicalization.prototype.processInner = function(node, prefixesInScope, de // Thanks to deoxxa/xml-c14n for comment renderer C14nCanonicalization.prototype.renderComment = function (node) { + if (!this.includeComments) { + return ""; + } - if (!this.includeComments) { return ""; } - - var isOutsideDocument = (node.ownerDocument === node.parentNode), - isBeforeDocument = null, - isAfterDocument = null; + var isOutsideDocument = node.ownerDocument === node.parentNode, + isBeforeDocument = null, + isAfterDocument = null; if (isOutsideDocument) { var nextNode = node, - previousNode = node; + previousNode = node; while (nextNode !== null) { if (nextNode === node.ownerDocument.documentElement) { @@ -192,7 +236,13 @@ C14nCanonicalization.prototype.renderComment = function (node) { } } - return (isAfterDocument ? "\n" : "") + "" + (isBeforeDocument ? "\n" : ""); + return ( + (isAfterDocument ? "\n" : "") + + "" + + (isBeforeDocument ? "\n" : "") + ); }; /** @@ -202,7 +252,7 @@ C14nCanonicalization.prototype.renderComment = function (node) { * @return {String} * @api public */ -C14nCanonicalization.prototype.process = function(node, options) { +C14nCanonicalization.prototype.process = function (node, options) { options = options || {}; var defaultNs = options.defaultNs || ""; var defaultNsForPrefix = options.defaultNsForPrefix || {}; @@ -213,11 +263,17 @@ C14nCanonicalization.prototype.process = function(node, options) { prefixesInScope.push(ancestorNamespaces[i].prefix); } - var res = this.processInner(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces); + var res = this.processInner( + node, + prefixesInScope, + defaultNs, + defaultNsForPrefix, + ancestorNamespaces + ); return res; }; -C14nCanonicalization.prototype.getAlgorithmName = function() { +C14nCanonicalization.prototype.getAlgorithmName = function () { return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; }; @@ -225,14 +281,14 @@ C14nCanonicalization.prototype.getAlgorithmName = function() { exports.C14nCanonicalizationWithComments = C14nCanonicalizationWithComments; function C14nCanonicalizationWithComments() { - C14nCanonicalization.call(this); - this.includeComments = true; -}; + C14nCanonicalization.call(this); + this.includeComments = true; +} C14nCanonicalizationWithComments.prototype = Object.create(C14nCanonicalization.prototype); C14nCanonicalizationWithComments.prototype.constructor = C14nCanonicalizationWithComments; -C14nCanonicalizationWithComments.prototype.getAlgorithmName = function() { +C14nCanonicalizationWithComments.prototype.getAlgorithmName = function () { return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; }; diff --git a/lib/enveloped-signature.js b/lib/enveloped-signature.js index f6971c43..c438e0d6 100644 --- a/lib/enveloped-signature.js +++ b/lib/enveloped-signature.js @@ -1,25 +1,36 @@ -var xpath = require('xpath'); -var utils = require('./utils'); +var xpath = require("xpath"); +var utils = require("./utils"); exports.EnvelopedSignature = EnvelopedSignature; -function EnvelopedSignature() { -} +function EnvelopedSignature() {} EnvelopedSignature.prototype.process = function (node, options) { if (null == options.signatureNode) { // leave this for the moment... - var signature = xpath.select("./*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", node)[0]; + var signature = xpath.select( + "./*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + node + )[0]; if (signature) signature.parentNode.removeChild(signature); return node; } var signatureNode = options.signatureNode; - var expectedSignatureValue = utils.findFirst(signatureNode, ".//*[local-name(.)='SignatureValue']/text()").data; - var signatures = xpath.select(".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", node); + var expectedSignatureValue = utils.findFirst( + signatureNode, + ".//*[local-name(.)='SignatureValue']/text()" + ).data; + var signatures = xpath.select( + ".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + node + ); for (var h in signatures) { if (!signatures.hasOwnProperty(h)) continue; var nodeSignature = signatures[h]; - var signatureValue = utils.findFirst(nodeSignature, ".//*[local-name(.)='SignatureValue']/text()").data; + var signatureValue = utils.findFirst( + nodeSignature, + ".//*[local-name(.)='SignatureValue']/text()" + ).data; if (expectedSignatureValue === signatureValue) { nodeSignature.parentNode.removeChild(nodeSignature); } diff --git a/lib/exclusive-canonicalization.js b/lib/exclusive-canonicalization.js index 7566908b..758ea474 100644 --- a/lib/exclusive-canonicalization.js +++ b/lib/exclusive-canonicalization.js @@ -1,45 +1,56 @@ /* jshint laxcomma: true */ -var utils = require('./utils'); +var utils = require("./utils"); exports.ExclusiveCanonicalization = ExclusiveCanonicalization; exports.ExclusiveCanonicalizationWithComments = ExclusiveCanonicalizationWithComments; function ExclusiveCanonicalization() { - this.includeComments = false; -}; - -ExclusiveCanonicalization.prototype.attrCompare = function(a,b) { - if (!a.namespaceURI && b.namespaceURI) { return -1; } - if (!b.namespaceURI && a.namespaceURI) { return 1; } + this.includeComments = false; +} - var left = a.namespaceURI + a.localName - var right = b.namespaceURI + b.localName +ExclusiveCanonicalization.prototype.attrCompare = function (a, b) { + if (!a.namespaceURI && b.namespaceURI) { + return -1; + } + if (!b.namespaceURI && a.namespaceURI) { + return 1; + } - if (left===right) return 0 - else if (left= 0) { - nsListToRender.push({"prefix": attr.localName, "namespaceURI": attr.value}); - prefixesInScope.push({"prefix": attr.localName, "namespaceURI": attr.value}); + if ( + attr.prefix && + !isPrefixInScope(prefixesInScope, attr.localName, attr.value) && + inclusiveNamespacesPrefixList.indexOf(attr.localName) >= 0 + ) { + nsListToRender.push({ prefix: attr.localName, namespaceURI: attr.value }); + prefixesInScope.push({ prefix: attr.localName, namespaceURI: attr.value }); } //handle all prefixed attributes that are not xmlns definitions and where //the prefix is not defined already - if (attr.prefix && !isPrefixInScope(prefixesInScope, attr.prefix, attr.namespaceURI) && attr.prefix!="xmlns" && attr.prefix!="xml") { - nsListToRender.push({"prefix": attr.prefix, "namespaceURI": attr.namespaceURI}); - prefixesInScope.push({"prefix": attr.prefix, "namespaceURI": attr.namespaceURI}); + if ( + attr.prefix && + !isPrefixInScope(prefixesInScope, attr.prefix, attr.namespaceURI) && + attr.prefix != "xmlns" && + attr.prefix != "xml" + ) { + nsListToRender.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI }); + prefixesInScope.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI }); } } } @@ -123,27 +164,53 @@ ExclusiveCanonicalization.prototype.renderNs = function(node, prefixesInScope, d //render namespaces for (a in nsListToRender) { - if (!nsListToRender.hasOwnProperty(a)) { continue; } + if (!nsListToRender.hasOwnProperty(a)) { + continue; + } p = nsListToRender[a]; res.push(" xmlns:", p.prefix, '="', p.namespaceURI, '"'); } - return {"rendered": res.join(""), "newDefaultNs": newDefaultNs}; + return { rendered: res.join(""), newDefaultNs: newDefaultNs }; }; -ExclusiveCanonicalization.prototype.processInner = function(node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList) { - - if (node.nodeType === 8) { return this.renderComment(node); } - if (node.data) { return utils.encodeSpecialCharactersInText(node.data); } +ExclusiveCanonicalization.prototype.processInner = function ( + node, + prefixesInScope, + defaultNs, + defaultNsForPrefix, + inclusiveNamespacesPrefixList +) { + if (node.nodeType === 8) { + return this.renderComment(node); + } + if (node.data) { + return utils.encodeSpecialCharactersInText(node.data); + } - var i, pfxCopy - , ns = this.renderNs(node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList) - , res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"]; + var i, + pfxCopy, + ns = this.renderNs( + node, + prefixesInScope, + defaultNs, + defaultNsForPrefix, + inclusiveNamespacesPrefixList + ), + res = ["<", node.tagName, ns.rendered, this.renderAttrs(node, ns.newDefaultNs), ">"]; for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); - res.push(this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList)); + res.push( + this.processInner( + node.childNodes[i], + pfxCopy, + ns.newDefaultNs, + defaultNsForPrefix, + inclusiveNamespacesPrefixList + ) + ); } res.push(""); @@ -152,16 +219,17 @@ ExclusiveCanonicalization.prototype.processInner = function(node, prefixesInScop // Thanks to deoxxa/xml-c14n for comment renderer ExclusiveCanonicalization.prototype.renderComment = function (node) { + if (!this.includeComments) { + return ""; + } - if (!this.includeComments) { return ""; } - - var isOutsideDocument = (node.ownerDocument === node.parentNode), - isBeforeDocument = null, - isAfterDocument = null; + var isOutsideDocument = node.ownerDocument === node.parentNode, + isBeforeDocument = null, + isAfterDocument = null; if (isOutsideDocument) { var nextNode = node, - previousNode = node; + previousNode = node; while (nextNode !== null) { if (nextNode === node.ownerDocument.documentElement) { @@ -182,7 +250,13 @@ ExclusiveCanonicalization.prototype.renderComment = function (node) { } } - return (isAfterDocument ? "\n" : "") + "" + (isBeforeDocument ? "\n" : ""); + return ( + (isAfterDocument ? "\n" : "") + + "" + + (isBeforeDocument ? "\n" : "") + ); }; /** @@ -192,49 +266,66 @@ ExclusiveCanonicalization.prototype.renderComment = function (node) { * @return {String} * @api public */ -ExclusiveCanonicalization.prototype.process = function(node, options) { +ExclusiveCanonicalization.prototype.process = function (node, options) { options = options || {}; var inclusiveNamespacesPrefixList = options.inclusiveNamespacesPrefixList || []; var defaultNs = options.defaultNs || ""; var defaultNsForPrefix = options.defaultNsForPrefix || {}; - if (!(inclusiveNamespacesPrefixList instanceof Array)) { inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList.split(' '); } + if (!(inclusiveNamespacesPrefixList instanceof Array)) { + inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList.split(" "); + } var ancestorNamespaces = options.ancestorNamespaces || []; - + /** * If the inclusiveNamespacesPrefixList has not been explicitly provided then look it up in CanonicalizationMethod/InclusiveNamespaces - */ + */ if (inclusiveNamespacesPrefixList.length == 0) { - var CanonicalizationMethod = utils.findChilds(node, "CanonicalizationMethod") + var CanonicalizationMethod = utils.findChilds(node, "CanonicalizationMethod"); if (CanonicalizationMethod.length != 0) { - var inclusiveNamespaces = utils.findChilds(CanonicalizationMethod[0], "InclusiveNamespaces") + var inclusiveNamespaces = utils.findChilds(CanonicalizationMethod[0], "InclusiveNamespaces"); if (inclusiveNamespaces.length != 0) { - inclusiveNamespacesPrefixList = inclusiveNamespaces[0].getAttribute('PrefixList').split(" "); + inclusiveNamespacesPrefixList = inclusiveNamespaces[0] + .getAttribute("PrefixList") + .split(" "); } } } - + /** * If you have a PrefixList then use it and the ancestors to add the necessary namespaces */ if (inclusiveNamespacesPrefixList) { - var prefixList = inclusiveNamespacesPrefixList instanceof Array ? inclusiveNamespacesPrefixList : inclusiveNamespacesPrefixList.split(' '); + var prefixList = + inclusiveNamespacesPrefixList instanceof Array + ? inclusiveNamespacesPrefixList + : inclusiveNamespacesPrefixList.split(" "); prefixList.forEach(function (prefix) { - if (ancestorNamespaces) { + if (ancestorNamespaces) { ancestorNamespaces.forEach(function (ancestorNamespace) { if (prefix == ancestorNamespace.prefix) { - node.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' + prefix, ancestorNamespace.namespaceURI); + node.setAttributeNS( + "http://www.w3.org/2000/xmlns/", + "xmlns:" + prefix, + ancestorNamespace.namespaceURI + ); } - }) + }); } - }) + }); } - var res = this.processInner(node, [], defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList); + var res = this.processInner( + node, + [], + defaultNs, + defaultNsForPrefix, + inclusiveNamespacesPrefixList + ); return res; }; -ExclusiveCanonicalization.prototype.getAlgorithmName = function() { +ExclusiveCanonicalization.prototype.getAlgorithmName = function () { return "http://www.w3.org/2001/10/xml-exc-c14n#"; }; @@ -242,14 +333,16 @@ ExclusiveCanonicalization.prototype.getAlgorithmName = function() { exports.ExclusiveCanonicalizationWithComments = ExclusiveCanonicalizationWithComments; function ExclusiveCanonicalizationWithComments() { - ExclusiveCanonicalization.call(this); - this.includeComments = true; -}; + ExclusiveCanonicalization.call(this); + this.includeComments = true; +} -ExclusiveCanonicalizationWithComments.prototype = Object.create(ExclusiveCanonicalization.prototype); +ExclusiveCanonicalizationWithComments.prototype = Object.create( + ExclusiveCanonicalization.prototype +); ExclusiveCanonicalizationWithComments.prototype.constructor = ExclusiveCanonicalizationWithComments; -ExclusiveCanonicalizationWithComments.prototype.getAlgorithmName = function() { +ExclusiveCanonicalizationWithComments.prototype.getAlgorithmName = function () { return "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; }; diff --git a/lib/file-key-info.js b/lib/file-key-info.js index 3accb30d..63726233 100644 --- a/lib/file-key-info.js +++ b/lib/file-key-info.js @@ -1,17 +1,17 @@ -var StringKeyInfo = require("./string-key-info") - , fs = require("fs") +var StringKeyInfo = require("./string-key-info"), + fs = require("fs"); /** * A key info provider implementation - * - * @param {string} file path to public certificate + * + * @param {string} file path to public certificate */ function FileKeyInfo(file) { - var key = fs.readFileSync(file) - StringKeyInfo.apply(this, [key]) + var key = fs.readFileSync(file); + StringKeyInfo.apply(this, [key]); } -FileKeyInfo.prototype = StringKeyInfo.prototype -FileKeyInfo.prototype.constructor = FileKeyInfo +FileKeyInfo.prototype = StringKeyInfo.prototype; +FileKeyInfo.prototype.constructor = FileKeyInfo; -module.exports = FileKeyInfo +module.exports = FileKeyInfo; diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 99bdaf72..7d26d726 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -1,135 +1,126 @@ -var xpath = require('xpath') - , Dom = require('@xmldom/xmldom').DOMParser - , utils = require('./utils') - , c14n = require('./c14n-canonicalization') - , execC14n = require('./exclusive-canonicalization') - , EnvelopedSignature = require('./enveloped-signature').EnvelopedSignature - , StringKeyInfo = require('./string-key-info') - , FileKeyInfo = require('./file-key-info') - , crypto = require('crypto') - -exports.SignedXml = SignedXml -exports.StringKeyInfo = StringKeyInfo -exports.FileKeyInfo = FileKeyInfo +var xpath = require("xpath"), + Dom = require("@xmldom/xmldom").DOMParser, + utils = require("./utils"), + c14n = require("./c14n-canonicalization"), + execC14n = require("./exclusive-canonicalization"), + EnvelopedSignature = require("./enveloped-signature").EnvelopedSignature, + StringKeyInfo = require("./string-key-info"), + FileKeyInfo = require("./file-key-info"), + crypto = require("crypto"); + +exports.SignedXml = SignedXml; +exports.StringKeyInfo = StringKeyInfo; +exports.FileKeyInfo = FileKeyInfo; /** * Hash algorithm implementation * */ function SHA1() { + this.getHash = function (xml) { + var shasum = crypto.createHash("sha1"); + shasum.update(xml, "utf8"); + var res = shasum.digest("base64"); + return res; + }; - this.getHash = function(xml) { - var shasum = crypto.createHash('sha1') - shasum.update(xml, 'utf8') - var res = shasum.digest('base64') - return res - } - - this.getAlgorithmName = function() { - return "http://www.w3.org/2000/09/xmldsig#sha1" - } + this.getAlgorithmName = function () { + return "http://www.w3.org/2000/09/xmldsig#sha1"; + }; } function SHA256() { + this.getHash = function (xml) { + var shasum = crypto.createHash("sha256"); + shasum.update(xml, "utf8"); + var res = shasum.digest("base64"); + return res; + }; - this.getHash = function(xml) { - var shasum = crypto.createHash('sha256') - shasum.update(xml, 'utf8') - var res = shasum.digest('base64') - return res - } - - this.getAlgorithmName = function() { - return "http://www.w3.org/2001/04/xmlenc#sha256" - } + this.getAlgorithmName = function () { + return "http://www.w3.org/2001/04/xmlenc#sha256"; + }; } function SHA512() { + this.getHash = function (xml) { + var shasum = crypto.createHash("sha512"); + shasum.update(xml, "utf8"); + var res = shasum.digest("base64"); + return res; + }; - this.getHash = function(xml) { - var shasum = crypto.createHash('sha512') - shasum.update(xml, 'utf8') - var res = shasum.digest('base64') - return res - } - - this.getAlgorithmName = function() { - return "http://www.w3.org/2001/04/xmlenc#sha512" - } + this.getAlgorithmName = function () { + return "http://www.w3.org/2001/04/xmlenc#sha512"; + }; } - /** * Signature algorithm implementation * */ function RSASHA1() { - /** - * Sign the given string using the given key - * - */ - this.getSignature = function(signedInfo, signingKey, callback) { - var signer = crypto.createSign("RSA-SHA1") - signer.update(signedInfo) - var res = signer.sign(signingKey, 'base64') - if (callback) callback(null, res) - return res - } + * Sign the given string using the given key + * + */ + this.getSignature = function (signedInfo, signingKey, callback) { + var signer = crypto.createSign("RSA-SHA1"); + signer.update(signedInfo); + var res = signer.sign(signingKey, "base64"); + if (callback) callback(null, res); + return res; + }; /** - * Verify the given signature of the given string using key - * - */ - this.verifySignature = function(str, key, signatureValue, callback) { - var verifier = crypto.createVerify("RSA-SHA1") - verifier.update(str) - var res = verifier.verify(key, signatureValue, 'base64') - if (callback) callback(null, res) - return res - } - - this.getAlgorithmName = function() { - return "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - } + * Verify the given signature of the given string using key + * + */ + this.verifySignature = function (str, key, signatureValue, callback) { + var verifier = crypto.createVerify("RSA-SHA1"); + verifier.update(str); + var res = verifier.verify(key, signatureValue, "base64"); + if (callback) callback(null, res); + return res; + }; + this.getAlgorithmName = function () { + return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + }; } - /** * Signature algorithm implementation * */ function RSASHA256() { - /** - * Sign the given string using the given key - * - */ - this.getSignature = function(signedInfo, signingKey, callback) { - var signer = crypto.createSign("RSA-SHA256") - signer.update(signedInfo) - var res = signer.sign(signingKey, 'base64') - if (callback) callback(null, res) - return res - } + * Sign the given string using the given key + * + */ + this.getSignature = function (signedInfo, signingKey, callback) { + var signer = crypto.createSign("RSA-SHA256"); + signer.update(signedInfo); + var res = signer.sign(signingKey, "base64"); + if (callback) callback(null, res); + return res; + }; /** - * Verify the given signature of the given string using key - * - */ - this.verifySignature = function(str, key, signatureValue, callback) { - var verifier = crypto.createVerify("RSA-SHA256") - verifier.update(str) - var res = verifier.verify(key, signatureValue, 'base64') - if (callback) callback(null, res) - return res - } - - this.getAlgorithmName = function() { - return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" - } + * Verify the given signature of the given string using key + * + */ + this.verifySignature = function (str, key, signatureValue, callback) { + var verifier = crypto.createVerify("RSA-SHA256"); + verifier.update(str); + var res = verifier.verify(key, signatureValue, "base64"); + if (callback) callback(null, res); + return res; + }; + this.getAlgorithmName = function () { + return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + }; } /** @@ -137,57 +128,54 @@ function RSASHA256() { * */ function RSASHA512() { - /** - * Sign the given string using the given key - * - */ - this.getSignature = function(signedInfo, signingKey, callback) { - var signer = crypto.createSign("RSA-SHA512") - signer.update(signedInfo) - var res = signer.sign(signingKey, 'base64') - if (callback) callback(null, res) - return res - } + * Sign the given string using the given key + * + */ + this.getSignature = function (signedInfo, signingKey, callback) { + var signer = crypto.createSign("RSA-SHA512"); + signer.update(signedInfo); + var res = signer.sign(signingKey, "base64"); + if (callback) callback(null, res); + return res; + }; /** - * Verify the given signature of the given string using key - * - */ - this.verifySignature = function(str, key, signatureValue, callback) { - var verifier = crypto.createVerify("RSA-SHA512") - verifier.update(str) - var res = verifier.verify(key, signatureValue, 'base64') - if (callback) callback(null, res) - return res - } + * Verify the given signature of the given string using key + * + */ + this.verifySignature = function (str, key, signatureValue, callback) { + var verifier = crypto.createVerify("RSA-SHA512"); + verifier.update(str); + var res = verifier.verify(key, signatureValue, "base64"); + if (callback) callback(null, res); + return res; + }; - this.getAlgorithmName = function() { - return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" - } + this.getAlgorithmName = function () { + return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; + }; } function HMACSHA1() { - this.verifySignature = function(str, key, signatureValue) { - var verifier = crypto.createHmac("SHA1", key); - verifier.update(str); - var res = verifier.digest('base64'); - return res === signatureValue; - }; - - this.getAlgorithmName = function() { - return "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; - }; - - this.getSignature = function(signedInfo, signingKey) { - var verifier = crypto.createHmac("SHA1", signingKey); - verifier.update(signedInfo); - var res = verifier.digest('base64'); - return res; - }; -} + this.verifySignature = function (str, key, signatureValue) { + var verifier = crypto.createHmac("SHA1", key); + verifier.update(str); + var res = verifier.digest("base64"); + return res === signatureValue; + }; + this.getAlgorithmName = function () { + return "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; + }; + this.getSignature = function (signedInfo, signingKey) { + var verifier = crypto.createHmac("SHA1", signingKey); + verifier.update(signedInfo); + var res = verifier.digest("base64"); + return res; + }; +} /** * Extract ancestor namespaces in order to import it to root of document subset @@ -198,128 +186,133 @@ function HMACSHA1() { * @param {object} namespaceResolver - xpath namespace resolver * @returns {Array} i.e. [{prefix: "saml", namespaceURI: "urn:oasis:names:tc:SAML:2.0:assertion"}] */ -function findAncestorNs(doc, docSubsetXpath, namespaceResolver){ +function findAncestorNs(doc, docSubsetXpath, namespaceResolver) { var docSubset = xpath.selectWithResolver(docSubsetXpath, doc, namespaceResolver); - - if(!Array.isArray(docSubset) || docSubset.length < 1){ + + if (!Array.isArray(docSubset) || docSubset.length < 1) { return []; } - + // Remove duplicate on ancestor namespace var ancestorNs = collectAncestorNamespaces(docSubset[0]); var ancestorNsWithoutDuplicate = []; - for(var i=0;i 0){ - for(var i=0;i 0) { + for (var i = 0; i < parent.attributes.length; i++) { var attr = parent.attributes[i]; - if(attr && attr.nodeName && attr.nodeName.search(/^xmlns:/) !== -1){ - nsArray.push({prefix: attr.nodeName.replace(/^xmlns:/, ""), namespaceURI: attr.nodeValue}) + if (attr && attr.nodeName && attr.nodeName.search(/^xmlns:/) !== -1) { + nsArray.push({ + prefix: attr.nodeName.replace(/^xmlns:/, ""), + namespaceURI: attr.nodeValue, + }); } } } - + return collectAncestorNamespaces(parent, nsArray); } /** -* Xml signature implementation -* -* @param {string} idMode. Value of "wssecurity" will create/validate id's with the ws-security namespace -* @param {object} options. Initial configurations -*/ + * Xml signature implementation + * + * @param {string} idMode. Value of "wssecurity" will create/validate id's with the ws-security namespace + * @param {object} options. Initial configurations + */ function SignedXml(idMode, options) { this.options = options || {}; - this.idMode = idMode - this.references = [] - this.id = 0 - this.signingKey = null - this.signingCert = null - this.signatureAlgorithm = this.options.signatureAlgorithm || "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; - this.keyInfoProvider = null - this.canonicalizationAlgorithm = this.options.canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#" - this.inclusiveNamespacesPrefixList = this.options.inclusiveNamespacesPrefixList || "" - this.signedXml = "" - this.signatureXml = "" - this.signatureNode = null - this.signatureValue = "" - this.originalXmlWithIds = "" - this.validationErrors = [] - this.keyInfo = null - this.idAttributes = [ 'Id', 'ID', 'id' ]; + this.idMode = idMode; + this.references = []; + this.id = 0; + this.signingKey = null; + this.signingCert = null; + this.signatureAlgorithm = + this.options.signatureAlgorithm || "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + this.keyInfoProvider = null; + this.canonicalizationAlgorithm = + this.options.canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#"; + this.inclusiveNamespacesPrefixList = this.options.inclusiveNamespacesPrefixList || ""; + this.signedXml = ""; + this.signatureXml = ""; + this.signatureNode = null; + this.signatureValue = ""; + this.originalXmlWithIds = ""; + this.validationErrors = []; + this.keyInfo = null; + this.idAttributes = ["Id", "ID", "id"]; if (this.options.idAttribute) this.idAttributes.splice(0, 0, this.options.idAttribute); this.implicitTransforms = this.options.implicitTransforms || []; } SignedXml.CanonicalizationAlgorithms = { - 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315': c14n.C14nCanonicalization, - 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments': c14n.C14nCanonicalizationWithComments, - 'http://www.w3.org/2001/10/xml-exc-c14n#': execC14n.ExclusiveCanonicalization, - 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments': execC14n.ExclusiveCanonicalizationWithComments, - 'http://www.w3.org/2000/09/xmldsig#enveloped-signature': EnvelopedSignature -} + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315": c14n.C14nCanonicalization, + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments": + c14n.C14nCanonicalizationWithComments, + "http://www.w3.org/2001/10/xml-exc-c14n#": execC14n.ExclusiveCanonicalization, + "http://www.w3.org/2001/10/xml-exc-c14n#WithComments": + execC14n.ExclusiveCanonicalizationWithComments, + "http://www.w3.org/2000/09/xmldsig#enveloped-signature": EnvelopedSignature, +}; SignedXml.HashAlgorithms = { - 'http://www.w3.org/2000/09/xmldsig#sha1': SHA1, - 'http://www.w3.org/2001/04/xmlenc#sha256': SHA256, - 'http://www.w3.org/2001/04/xmlenc#sha512': SHA512 -} + "http://www.w3.org/2000/09/xmldsig#sha1": SHA1, + "http://www.w3.org/2001/04/xmlenc#sha256": SHA256, + "http://www.w3.org/2001/04/xmlenc#sha512": SHA512, +}; SignedXml.SignatureAlgorithms = { - 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': RSASHA1, - 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': RSASHA256, - 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': RSASHA512, + "http://www.w3.org/2000/09/xmldsig#rsa-sha1": RSASHA1, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256": RSASHA256, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512": RSASHA512, // Disabled by default due to key confusion concerns. // 'http://www.w3.org/2000/09/xmldsig#hmac-sha1': HMACSHA1 -} +}; /** * Due to key-confusion issues, its risky to have both hmac @@ -328,175 +321,178 @@ SignedXml.SignatureAlgorithms = { */ SignedXml.enableHMAC = function () { SignedXml.SignatureAlgorithms = { - 'http://www.w3.org/2000/09/xmldsig#hmac-sha1': HMACSHA1 - } -} + "http://www.w3.org/2000/09/xmldsig#hmac-sha1": HMACSHA1, + }; +}; SignedXml.defaultNsForPrefix = { - ds: 'http://www.w3.org/2000/09/xmldsig#' + ds: "http://www.w3.org/2000/09/xmldsig#", }; SignedXml.findAncestorNs = findAncestorNs; -SignedXml.prototype.checkSignature = function(xml, callback) { - if (callback != null && typeof callback !== 'function') { - throw new Error("Last parameter must be a callback function") +SignedXml.prototype.checkSignature = function (xml, callback) { + if (callback != null && typeof callback !== "function") { + throw new Error("Last parameter must be a callback function"); } - this.validationErrors = [] - this.signedXml = xml + this.validationErrors = []; + this.signedXml = xml; if (!this.keyInfoProvider) { - var err = new Error("cannot validate signature since no key info resolver was provided") + var err = new Error("cannot validate signature since no key info resolver was provided"); if (!callback) { - throw err + throw err; } else { - callback(err) - return + callback(err); + return; } } - this.signingKey = this.keyInfoProvider.getKey(this.keyInfo) + this.signingKey = this.keyInfoProvider.getKey(this.keyInfo); if (!this.signingKey) { - var err2 = new Error("key info provider could not resolve key info " + this.keyInfo) + var err2 = new Error("key info provider could not resolve key info " + this.keyInfo); if (!callback) { - throw err2 + throw err2; } else { - callback(err2) - return + callback(err2); + return; } } - var doc = new Dom().parseFromString(xml) + var doc = new Dom().parseFromString(xml); if (!this.validateReferences(doc)) { if (!callback) { return false; } else { - callback(new Error('Could not validate references')) - return + callback(new Error("Could not validate references")); + return; } } if (!callback) { // Synchronous flow if (!this.validateSignatureValue(doc)) { - return false + return false; } - return true + return true; } else { // Asynchronous flow this.validateSignatureValue(doc, function (err, isValidSignature) { if (err) { - this.validationErrors.push("invalid signature: the signature value " + - this.signatureValue + " is incorrect") - callback(err) + this.validationErrors.push( + "invalid signature: the signature value " + this.signatureValue + " is incorrect" + ); + callback(err); } else { - callback(null, isValidSignature) + callback(null, isValidSignature); } - }) + }); } -} +}; + +SignedXml.prototype.getCanonSignedInfoXml = function (doc) { + var signedInfo = utils.findChilds(this.signatureNode, "SignedInfo"); + if (signedInfo.length == 0) throw new Error("could not find SignedInfo element in the message"); -SignedXml.prototype.getCanonSignedInfoXml = function(doc) { - var signedInfo = utils.findChilds(this.signatureNode, "SignedInfo") - if (signedInfo.length==0) throw new Error("could not find SignedInfo element in the message") - - if(this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" - || this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments") - { - if(!doc || typeof(doc) !== "object"){ + if ( + this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" || + this.canonicalizationAlgorithm === + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" + ) { + if (!doc || typeof doc !== "object") { throw new Error( "When canonicalization method is non-exclusive, whole xml dom must be provided as an argument" ); } } - + /** * Search for ancestor namespaces before canonicalization. */ var ancestorNamespaces = []; ancestorNamespaces = findAncestorNs(doc, "//*[local-name()='SignedInfo']"); - + var c14nOptions = { - ancestorNamespaces: ancestorNamespaces + ancestorNamespaces: ancestorNamespaces, }; - return this.getCanonXml([this.canonicalizationAlgorithm], signedInfo[0], c14nOptions) -} + return this.getCanonXml([this.canonicalizationAlgorithm], signedInfo[0], c14nOptions); +}; -SignedXml.prototype.getCanonReferenceXml = function(doc, ref, node) { +SignedXml.prototype.getCanonReferenceXml = function (doc, ref, node) { /** * Search for ancestor namespaces before canonicalization. */ - if(Array.isArray(ref.transforms)){ - ref.ancestorNamespaces = findAncestorNs(doc, ref.xpath, this.namespaceResolver) + if (Array.isArray(ref.transforms)) { + ref.ancestorNamespaces = findAncestorNs(doc, ref.xpath, this.namespaceResolver); } var c14nOptions = { inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList, - ancestorNamespaces: ref.ancestorNamespaces - } + ancestorNamespaces: ref.ancestorNamespaces, + }; - return this.getCanonXml(ref.transforms, node, c14nOptions) -} + return this.getCanonXml(ref.transforms, node, c14nOptions); +}; -SignedXml.prototype.validateSignatureValue = function(doc, callback) { - var signedInfoCanon = this.getCanonSignedInfoXml(doc) - var signer = this.findSignatureAlgorithm(this.signatureAlgorithm) - var res = signer.verifySignature(signedInfoCanon, this.signingKey, this.signatureValue, callback) - if (!res && !callback) this.validationErrors.push("invalid signature: the signature value " + - this.signatureValue + " is incorrect") - return res -} +SignedXml.prototype.validateSignatureValue = function (doc, callback) { + var signedInfoCanon = this.getCanonSignedInfoXml(doc); + var signer = this.findSignatureAlgorithm(this.signatureAlgorithm); + var res = signer.verifySignature(signedInfoCanon, this.signingKey, this.signatureValue, callback); + if (!res && !callback) + this.validationErrors.push( + "invalid signature: the signature value " + this.signatureValue + " is incorrect" + ); + return res; +}; -SignedXml.prototype.calculateSignatureValue = function(doc, callback) { - var signedInfoCanon = this.getCanonSignedInfoXml(doc) - var signer = this.findSignatureAlgorithm(this.signatureAlgorithm) - this.signatureValue = signer.getSignature(signedInfoCanon, this.signingKey, callback) -} +SignedXml.prototype.calculateSignatureValue = function (doc, callback) { + var signedInfoCanon = this.getCanonSignedInfoXml(doc); + var signer = this.findSignatureAlgorithm(this.signatureAlgorithm); + this.signatureValue = signer.getSignature(signedInfoCanon, this.signingKey, callback); +}; -SignedXml.prototype.findSignatureAlgorithm = function(name) { - var algo = SignedXml.SignatureAlgorithms[name] - if (algo) return new algo() +SignedXml.prototype.findSignatureAlgorithm = function (name) { + var algo = SignedXml.SignatureAlgorithms[name]; + if (algo) return new algo(); else throw new Error("signature algorithm '" + name + "' is not supported"); -} +}; -SignedXml.prototype.findCanonicalizationAlgorithm = function(name) { - var algo = SignedXml.CanonicalizationAlgorithms[name] - if (algo) return new algo() +SignedXml.prototype.findCanonicalizationAlgorithm = function (name) { + var algo = SignedXml.CanonicalizationAlgorithms[name]; + if (algo) return new algo(); else throw new Error("canonicalization algorithm '" + name + "' is not supported"); -} +}; -SignedXml.prototype.findHashAlgorithm = function(name) { - var algo = SignedXml.HashAlgorithms[name] - if (algo) return new algo() +SignedXml.prototype.findHashAlgorithm = function (name) { + var algo = SignedXml.HashAlgorithms[name]; + if (algo) return new algo(); else throw new Error("hash algorithm '" + name + "' is not supported"); -} - +}; -SignedXml.prototype.validateReferences = function(doc) { +SignedXml.prototype.validateReferences = function (doc) { for (var r in this.references) { if (!this.references.hasOwnProperty(r)) continue; - var ref = this.references[r] + var ref = this.references[r]; - var uri = ref.uri[0]=="#" ? ref.uri.substring(1) : ref.uri + var uri = ref.uri[0] == "#" ? ref.uri.substring(1) : ref.uri; var elem = []; - if (uri=="") { - elem = xpath.select("//*", doc) - } - else if (uri.indexOf("'") != -1) { + if (uri == "") { + elem = xpath.select("//*", doc); + } else if (uri.indexOf("'") != -1) { // xpath injection throw new Error("Cannot validate a uri with quotes inside it"); - } - else { + } else { var elemXpath; var num_elements_for_id = 0; for (var index in this.idAttributes) { if (!this.idAttributes.hasOwnProperty(index)) continue; - var tmp_elemXpath = "//*[@*[local-name(.)='" + this.idAttributes[index] + "']='" + uri + "']"; - var tmp_elem = xpath.select(tmp_elemXpath, doc) + var tmp_elemXpath = + "//*[@*[local-name(.)='" + this.idAttributes[index] + "']='" + uri + "']"; + var tmp_elem = xpath.select(tmp_elemXpath, doc); num_elements_for_id += tmp_elem.length; if (tmp_elem.length > 0) { elem = tmp_elem; @@ -504,35 +500,45 @@ SignedXml.prototype.validateReferences = function(doc) { } } if (num_elements_for_id > 1) { - throw new Error('Cannot validate a document which contains multiple elements with the ' + - 'same value for the ID / Id / Id attributes, in order to prevent ' + - 'signature wrapping attack.'); + throw new Error( + "Cannot validate a document which contains multiple elements with the " + + "same value for the ID / Id / Id attributes, in order to prevent " + + "signature wrapping attack." + ); } ref.xpath = elemXpath; } - if (elem.length==0) { - this.validationErrors.push("invalid signature: the signature references an element with uri "+ - ref.uri + " but could not find such element in the xml") - return false + if (elem.length == 0) { + this.validationErrors.push( + "invalid signature: the signature references an element with uri " + + ref.uri + + " but could not find such element in the xml" + ); + return false; } - - var canonXml = this.getCanonReferenceXml(doc, ref, elem[0]) - var hash = this.findHashAlgorithm(ref.digestAlgorithm) - var digest = hash.getHash(canonXml) + var canonXml = this.getCanonReferenceXml(doc, ref, elem[0]); + + var hash = this.findHashAlgorithm(ref.digestAlgorithm); + var digest = hash.getHash(canonXml); if (!validateDigestValue(digest, ref.digestValue)) { - this.validationErrors.push("invalid signature: for uri " + ref.uri + - " calculated digest is " + digest + - " but the xml to validate supplies digest " + ref.digestValue) + this.validationErrors.push( + "invalid signature: for uri " + + ref.uri + + " calculated digest is " + + digest + + " but the xml to validate supplies digest " + + ref.digestValue + ); - return false + return false; } } - return true -} + return true; +}; function validateDigestValue(digest, expectedDigest) { var buffer, expectedBuffer; @@ -540,15 +546,15 @@ function validateDigestValue(digest, expectedDigest) { var majorVersion = /^v(\d+)/.exec(process.version)[1]; if (+majorVersion >= 6) { - buffer = Buffer.from(digest, 'base64'); - expectedBuffer = Buffer.from(expectedDigest, 'base64'); + buffer = Buffer.from(digest, "base64"); + expectedBuffer = Buffer.from(expectedDigest, "base64"); } else { // Compatibility with Node < 5.10.0 - buffer = new Buffer(digest, 'base64'); - expectedBuffer = new Buffer(expectedDigest, 'base64'); + buffer = new Buffer(digest, "base64"); + expectedBuffer = new Buffer(expectedDigest, "base64"); } - if (typeof buffer.equals === 'function') { + if (typeof buffer.equals === "function") { return buffer.equals(expectedBuffer); } @@ -566,8 +572,8 @@ function validateDigestValue(digest, expectedDigest) { return true; } -SignedXml.prototype.loadSignature = function(signatureNode) { - if (typeof signatureNode === 'string') { +SignedXml.prototype.loadSignature = function (signatureNode) { + if (typeof signatureNode === "string") { this.signatureNode = signatureNode = new Dom().parseFromString(signatureNode); } else { this.signatureNode = signatureNode; @@ -575,110 +581,143 @@ SignedXml.prototype.loadSignature = function(signatureNode) { this.signatureXml = signatureNode.toString(); - var nodes = xpath.select(".//*[local-name(.)='CanonicalizationMethod']/@Algorithm", signatureNode) - if (nodes.length==0) throw new Error("could not find CanonicalizationMethod/@Algorithm element") - this.canonicalizationAlgorithm = nodes[0].value - - this.signatureAlgorithm = - utils.findFirst(signatureNode, ".//*[local-name(.)='SignatureMethod']/@Algorithm").value - - this.references = [] - var references = xpath.select(".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']", signatureNode) - if (references.length == 0) throw new Error("could not find any Reference elements") + var nodes = xpath.select( + ".//*[local-name(.)='CanonicalizationMethod']/@Algorithm", + signatureNode + ); + if (nodes.length == 0) + throw new Error("could not find CanonicalizationMethod/@Algorithm element"); + this.canonicalizationAlgorithm = nodes[0].value; + + this.signatureAlgorithm = utils.findFirst( + signatureNode, + ".//*[local-name(.)='SignatureMethod']/@Algorithm" + ).value; + + this.references = []; + var references = xpath.select( + ".//*[local-name(.)='SignedInfo']/*[local-name(.)='Reference']", + signatureNode + ); + if (references.length == 0) throw new Error("could not find any Reference elements"); for (var i in references) { if (!references.hasOwnProperty(i)) continue; - this.loadReference(references[i]) + this.loadReference(references[i]); } - this.signatureValue = - utils.findFirst(signatureNode, ".//*[local-name(.)='SignatureValue']/text()").data.replace(/\r?\n/g, '') + this.signatureValue = utils + .findFirst(signatureNode, ".//*[local-name(.)='SignatureValue']/text()") + .data.replace(/\r?\n/g, ""); - this.keyInfo = xpath.select(".//*[local-name(.)='KeyInfo']", signatureNode) -} + this.keyInfo = xpath.select(".//*[local-name(.)='KeyInfo']", signatureNode); +}; /** * Load the reference xml node to a model * */ -SignedXml.prototype.loadReference = function(ref) { - var nodes = utils.findChilds(ref, "DigestMethod") - if (nodes.length==0) throw new Error("could not find DigestMethod in reference " + ref.toString()) - var digestAlgoNode = nodes[0] - - var attr = utils.findAttr(digestAlgoNode, "Algorithm") - if (!attr) throw new Error("could not find Algorithm attribute in node " + digestAlgoNode.toString()) - var digestAlgo = attr.value - - nodes = utils.findChilds(ref, "DigestValue") - if (nodes.length==0) throw new Error("could not find DigestValue node in reference " + ref.toString()) - if (nodes[0].childNodes.length==0 || !nodes[0].firstChild.data) - { - throw new Error("could not find the value of DigestValue in " + nodes[0].toString()) +SignedXml.prototype.loadReference = function (ref) { + var nodes = utils.findChilds(ref, "DigestMethod"); + if (nodes.length == 0) + throw new Error("could not find DigestMethod in reference " + ref.toString()); + var digestAlgoNode = nodes[0]; + + var attr = utils.findAttr(digestAlgoNode, "Algorithm"); + if (!attr) + throw new Error("could not find Algorithm attribute in node " + digestAlgoNode.toString()); + var digestAlgo = attr.value; + + nodes = utils.findChilds(ref, "DigestValue"); + if (nodes.length == 0) + throw new Error("could not find DigestValue node in reference " + ref.toString()); + if (nodes[0].childNodes.length == 0 || !nodes[0].firstChild.data) { + throw new Error("could not find the value of DigestValue in " + nodes[0].toString()); } - var digestValue = nodes[0].firstChild.data + var digestValue = nodes[0].firstChild.data; - var transforms = [] + var transforms = []; var inclusiveNamespacesPrefixList; - nodes = utils.findChilds(ref, "Transforms") - if (nodes.length!=0) { - var transformsNode = nodes[0] - var transformsAll = utils.findChilds(transformsNode, "Transform") + nodes = utils.findChilds(ref, "Transforms"); + if (nodes.length != 0) { + var transformsNode = nodes[0]; + var transformsAll = utils.findChilds(transformsNode, "Transform"); for (var t in transformsAll) { if (!transformsAll.hasOwnProperty(t)) continue; - var trans = transformsAll[t] - transforms.push(utils.findAttr(trans, "Algorithm").value) + var trans = transformsAll[t]; + transforms.push(utils.findAttr(trans, "Algorithm").value); } - var inclusiveNamespaces = utils.findChilds(trans, "InclusiveNamespaces") + var inclusiveNamespaces = utils.findChilds(trans, "InclusiveNamespaces"); if (inclusiveNamespaces.length > 0) { //Should really only be one prefix list, but maybe there's some circumstances where more than one to lets handle it - for (var i = 0; i 0); - if(hasImplicitTransforms){ - this.implicitTransforms.forEach(function(t){ + var hasImplicitTransforms = + Array.isArray(this.implicitTransforms) && this.implicitTransforms.length > 0; + if (hasImplicitTransforms) { + this.implicitTransforms.forEach(function (t) { transforms.push(t); }); } - -/** - * DigestMethods take an octet stream rather than a node set. If the output of the last transform is a node set, we - * need to canonicalize the node set to an octet stream using non-exclusive canonicalization. If there are no - * transforms, we need to canonicalize because URI dereferencing for a same-document reference will return a node-set. - * See: - * https://www.w3.org/TR/xmldsig-core1/#sec-DigestMethod - * https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel - * https://www.w3.org/TR/xmldsig-core1/#sec-Same-Document - */ - if (transforms.length === 0 || transforms[transforms.length - 1] === "http://www.w3.org/2000/09/xmldsig#enveloped-signature") { - transforms.push("http://www.w3.org/TR/2001/REC-xml-c14n-20010315") + + /** + * DigestMethods take an octet stream rather than a node set. If the output of the last transform is a node set, we + * need to canonicalize the node set to an octet stream using non-exclusive canonicalization. If there are no + * transforms, we need to canonicalize because URI dereferencing for a same-document reference will return a node-set. + * See: + * https://www.w3.org/TR/xmldsig-core1/#sec-DigestMethod + * https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel + * https://www.w3.org/TR/xmldsig-core1/#sec-Same-Document + */ + if ( + transforms.length === 0 || + transforms[transforms.length - 1] === "http://www.w3.org/2000/09/xmldsig#enveloped-signature" + ) { + transforms.push("http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); } - this.addReference(null, transforms, digestAlgo, utils.findAttr(ref, "URI").value, digestValue, inclusiveNamespacesPrefixList, false) -} + this.addReference( + null, + transforms, + digestAlgo, + utils.findAttr(ref, "URI").value, + digestValue, + inclusiveNamespacesPrefixList, + false + ); +}; -SignedXml.prototype.addReference = function(xpath, transforms, digestAlgorithm, uri, digestValue, inclusiveNamespacesPrefixList, isEmptyUri) { +SignedXml.prototype.addReference = function ( + xpath, + transforms, + digestAlgorithm, + uri, + digestValue, + inclusiveNamespacesPrefixList, + isEmptyUri +) { this.references.push({ - "xpath": xpath, - "transforms": transforms ? transforms : ["http://www.w3.org/2001/10/xml-exc-c14n#"] , - "digestAlgorithm": digestAlgorithm ? digestAlgorithm : "http://www.w3.org/2000/09/xmldsig#sha1", - "uri": uri, - "digestValue": digestValue, - "inclusiveNamespacesPrefixList": inclusiveNamespacesPrefixList, - "isEmptyUri": isEmptyUri + xpath: xpath, + transforms: transforms ? transforms : ["http://www.w3.org/2001/10/xml-exc-c14n#"], + digestAlgorithm: digestAlgorithm ? digestAlgorithm : "http://www.w3.org/2000/09/xmldsig#sha1", + uri: uri, + digestValue: digestValue, + inclusiveNamespacesPrefixList: inclusiveNamespacesPrefixList, + isEmptyUri: isEmptyUri, }); -} +}; /** * Compute the signature of the given xml (using the already defined settings) @@ -695,22 +734,22 @@ SignedXml.prototype.addReference = function(xpath, transforms, digestAlgorithm, * `append`, `prepend`, `before`, `after` * */ -SignedXml.prototype.computeSignature = function(xml, opts, callback) { - if (typeof opts === 'function' && callback == null) { - callback = opts +SignedXml.prototype.computeSignature = function (xml, opts, callback) { + if (typeof opts === "function" && callback == null) { + callback = opts; } - if (callback != null && typeof callback !== 'function') { - throw new Error("Last parameter must be a callback function") + if (callback != null && typeof callback !== "function") { + throw new Error("Last parameter must be a callback function"); } var doc = new Dom().parseFromString(xml), - xmlNsAttr = "xmlns", - signatureAttrs = [], - location, - attrs, - prefix, - currentPrefix; + xmlNsAttr = "xmlns", + signatureAttrs = [], + location, + attrs, + prefix, + currentPrefix; var validActions = ["append", "prepend", "before", "after"]; @@ -721,10 +760,10 @@ SignedXml.prototype.computeSignature = function(xml, opts, callback) { var existingPrefixes = opts.existingPrefixes || {}; this.namespaceResolver = { - lookupNamespaceURI: function(prefix) { + lookupNamespaceURI: function (prefix) { return existingPrefixes[prefix]; - } - } + }, + }; // defaults to the root node location.reference = location.reference || "/*"; @@ -732,13 +771,17 @@ SignedXml.prototype.computeSignature = function(xml, opts, callback) { location.action = location.action || "append"; if (validActions.indexOf(location.action) === -1) { - var err = new Error("location.action option has an invalid action: " + location.action + - ", must be any of the following values: " + validActions.join(", ")); + var err = new Error( + "location.action option has an invalid action: " + + location.action + + ", must be any of the following values: " + + validActions.join(", ") + ); if (!callback) { throw err; } else { - callback(err, null) - return + callback(err, null); + return; } } @@ -750,43 +793,45 @@ SignedXml.prototype.computeSignature = function(xml, opts, callback) { currentPrefix = ""; } - Object.keys(attrs).forEach(function(name) { + Object.keys(attrs).forEach(function (name) { if (name !== "xmlns" && name !== xmlNsAttr) { - signatureAttrs.push(name + "=\"" + attrs[name] + "\""); + signatureAttrs.push(name + '="' + attrs[name] + '"'); } }); // add the xml namespace attribute - signatureAttrs.push(xmlNsAttr + "=\"http://www.w3.org/2000/09/xmldsig#\""); + signatureAttrs.push(xmlNsAttr + '="http://www.w3.org/2000/09/xmldsig#"'); - var signatureXml = "<" + currentPrefix + "Signature " + signatureAttrs.join(" ") + ">" + var signatureXml = "<" + currentPrefix + "Signature " + signatureAttrs.join(" ") + ">"; signatureXml += this.createSignedInfo(doc, prefix); - signatureXml += this.getKeyInfo(prefix) - signatureXml += "" + signatureXml += this.getKeyInfo(prefix); + signatureXml += ""; - this.originalXmlWithIds = doc.toString() + this.originalXmlWithIds = doc.toString(); - var existingPrefixesString = "" - Object.keys(existingPrefixes).forEach(function(key) { - existingPrefixesString += "xmlns:" + key + '="' + existingPrefixes[key] + '" ' + var existingPrefixesString = ""; + Object.keys(existingPrefixes).forEach(function (key) { + existingPrefixesString += "xmlns:" + key + '="' + existingPrefixes[key] + '" '; }); // A trick to remove the namespaces that already exist in the xml // This only works if the prefix and namespace match with those in te xml - var dummySignatureWrapper = "" + signatureXml + "" - var nodeXml = new Dom().parseFromString(dummySignatureWrapper) + var dummySignatureWrapper = "" + signatureXml + ""; + var nodeXml = new Dom().parseFromString(dummySignatureWrapper); var signatureDoc = nodeXml.documentElement.firstChild; var referenceNode = xpath.select(location.reference, doc); if (!referenceNode || referenceNode.length === 0) { - var err2 = new Error("the following xpath cannot be used because it was not found: " + location.reference); + var err2 = new Error( + "the following xpath cannot be used because it was not found: " + location.reference + ); if (!callback) { - throw err2 + throw err2; } else { - callback(err2, null) - return + callback(err2, null); + return; } } @@ -802,136 +847,156 @@ SignedXml.prototype.computeSignature = function(xml, opts, callback) { referenceNode.parentNode.insertBefore(signatureDoc, referenceNode.nextSibling); } - this.signatureNode = signatureDoc - var signedInfoNode = utils.findChilds(this.signatureNode, "SignedInfo") + this.signatureNode = signatureDoc; + var signedInfoNode = utils.findChilds(this.signatureNode, "SignedInfo"); if (signedInfoNode.length == 0) { - var err3 = new Error("could not find SignedInfo element in the message") + var err3 = new Error("could not find SignedInfo element in the message"); if (!callback) { - throw err3 + throw err3; } else { - callback(err3) - return + callback(err3); + return; } } signedInfoNode = signedInfoNode[0]; if (!callback) { //Synchronous flow - this.calculateSignatureValue(doc) - signatureDoc.insertBefore(this.createSignature(prefix), signedInfoNode.nextSibling) - this.signatureXml = signatureDoc.toString() - this.signedXml = doc.toString() + this.calculateSignatureValue(doc); + signatureDoc.insertBefore(this.createSignature(prefix), signedInfoNode.nextSibling); + this.signatureXml = signatureDoc.toString(); + this.signedXml = doc.toString(); } else { - var self = this + var self = this; //Asynchronous flow - this.calculateSignatureValue(doc, function(err, signature) { + this.calculateSignatureValue(doc, function (err, signature) { if (err) { - callback(err) + callback(err); } else { - self.signatureValue = signature - signatureDoc.insertBefore(self.createSignature(prefix), signedInfoNode.nextSibling) - self.signatureXml = signatureDoc.toString() - self.signedXml = doc.toString() - callback(null, self) + self.signatureValue = signature; + signatureDoc.insertBefore(self.createSignature(prefix), signedInfoNode.nextSibling); + self.signatureXml = signatureDoc.toString(); + self.signedXml = doc.toString(); + callback(null, self); } - }) + }); } -} +}; -SignedXml.prototype.getKeyInfo = function(prefix) { - var res = "" - var currentPrefix +SignedXml.prototype.getKeyInfo = function (prefix) { + var res = ""; + var currentPrefix; - currentPrefix = prefix || '' - currentPrefix = currentPrefix ? currentPrefix + ':' : currentPrefix + currentPrefix = prefix || ""; + currentPrefix = currentPrefix ? currentPrefix + ":" : currentPrefix; if (this.keyInfoProvider) { - var keyInfoAttrs = "" + var keyInfoAttrs = ""; if (this.keyInfoProvider.attrs) { Object.keys(this.keyInfoProvider.attrs).forEach((name) => { - keyInfoAttrs += " " + name + "=\"" + this.keyInfoProvider.attrs[name] + "\"" - }) + keyInfoAttrs += " " + name + '="' + this.keyInfoProvider.attrs[name] + '"'; + }); } - res += "<" + currentPrefix + "KeyInfo" + keyInfoAttrs + ">" - res += this.keyInfoProvider.getKeyInfo(this.signingCert || this.signingKey, prefix) - res += "" + res += "<" + currentPrefix + "KeyInfo" + keyInfoAttrs + ">"; + res += this.keyInfoProvider.getKeyInfo(this.signingCert || this.signingKey, prefix); + res += ""; } - return res -} + return res; +}; /** * Generate the Reference nodes (as part of the signature process) * */ -SignedXml.prototype.createReferences = function(doc, prefix) { - - var res = "" +SignedXml.prototype.createReferences = function (doc, prefix) { + var res = ""; - prefix = prefix || '' - prefix = prefix ? prefix + ':' : prefix + prefix = prefix || ""; + prefix = prefix ? prefix + ":" : prefix; for (var n in this.references) { if (!this.references.hasOwnProperty(n)) continue; - var ref = this.references[n] - , nodes = xpath.selectWithResolver(ref.xpath, doc, this.namespaceResolver) + var ref = this.references[n], + nodes = xpath.selectWithResolver(ref.xpath, doc, this.namespaceResolver); - if (nodes.length==0) { - throw new Error('the following xpath cannot be signed because it was not found: ' + ref.xpath) + if (nodes.length == 0) { + throw new Error( + "the following xpath cannot be signed because it was not found: " + ref.xpath + ); } for (var h in nodes) { if (!nodes.hasOwnProperty(h)) continue; - var node = nodes[h] + var node = nodes[h]; if (ref.isEmptyUri) { - res += "<" + prefix + "Reference URI=\"\">" - } - else { + res += "<" + prefix + 'Reference URI="">'; + } else { var id = this.ensureHasId(node); - ref.uri = id - res += "<" + prefix + "Reference URI=\"#" + id + "\">" + ref.uri = id; + res += "<" + prefix + 'Reference URI="#' + id + '">'; } - res += "<" + prefix + "Transforms>" + res += "<" + prefix + "Transforms>"; for (var t in ref.transforms) { if (!ref.transforms.hasOwnProperty(t)) continue; - var trans = ref.transforms[t] - var transform = this.findCanonicalizationAlgorithm(trans) - res += "<" + prefix + "Transform Algorithm=\"" + transform.getAlgorithmName() + "\"" + var trans = ref.transforms[t]; + var transform = this.findCanonicalizationAlgorithm(trans); + res += "<" + prefix + 'Transform Algorithm="' + transform.getAlgorithmName() + '"'; if (ref.inclusiveNamespacesPrefixList) { - res += ">" - res += "" - res += "" + res += ">"; + res += + ''; + res += ""; } else { - res += " />" + res += " />"; } } - var canonXml = this.getCanonReferenceXml(doc, ref, node) - - var digestAlgorithm = this.findHashAlgorithm(ref.digestAlgorithm) - res += ""+ - "<" + prefix + "DigestMethod Algorithm=\"" + digestAlgorithm.getAlgorithmName() + "\" />"+ - "<" + prefix + "DigestValue>" + digestAlgorithm.getHash(canonXml) + ""+ - "" + var canonXml = this.getCanonReferenceXml(doc, ref, node); + + var digestAlgorithm = this.findHashAlgorithm(ref.digestAlgorithm); + res += + "" + + "<" + + prefix + + 'DigestMethod Algorithm="' + + digestAlgorithm.getAlgorithmName() + + '" />' + + "<" + + prefix + + "DigestValue>" + + digestAlgorithm.getHash(canonXml) + + "" + + ""; } } - return res -} + return res; +}; -SignedXml.prototype.getCanonXml = function(transforms, node, options) { +SignedXml.prototype.getCanonXml = function (transforms, node, options) { options = options || {}; options.defaultNsForPrefix = options.defaultNsForPrefix || SignedXml.defaultNsForPrefix; options.signatureNode = this.signatureNode; - var canonXml = node.cloneNode(true) // Deep clone + var canonXml = node.cloneNode(true); // Deep clone for (var t in transforms) { if (!transforms.hasOwnProperty(t)) continue; - var transform = this.findCanonicalizationAlgorithm(transforms[t]) + var transform = this.findCanonicalizationAlgorithm(transforms[t]); canonXml = transform.process(canonXml, options); //TODO: currently transform.process may return either Node or String value (enveloped transformation returns Node, exclusive-canonicalization returns String). //This either needs to be more explicit in the API, or all should return the same. @@ -941,22 +1006,23 @@ SignedXml.prototype.getCanonXml = function(transforms, node, options) { // //if only y is the node to sign then a string would be without the definition of the p namespace. probably xmldom toString() should have added it. } - return canonXml.toString() -} + return canonXml.toString(); +}; /** * Ensure an element has Id attribute. If not create it with unique value. * Work with both normal and wssecurity Id flavour */ -SignedXml.prototype.ensureHasId = function(node) { - var attr +SignedXml.prototype.ensureHasId = function (node) { + var attr; - if (this.idMode=="wssecurity") { - attr = utils.findAttr(node, + if (this.idMode == "wssecurity") { + attr = utils.findAttr( + node, "Id", - "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd") - } - else { + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" + ); + } else { for (var index in this.idAttributes) { if (!this.idAttributes.hasOwnProperty(index)) continue; @@ -965,88 +1031,104 @@ SignedXml.prototype.ensureHasId = function(node) { } } - if (attr) return attr.value + if (attr) return attr.value; //add the attribute - var id = "_" + this.id++ + var id = "_" + this.id++; - if (this.idMode=="wssecurity") { - node.setAttributeNS("http://www.w3.org/2000/xmlns/", + if (this.idMode == "wssecurity") { + node.setAttributeNS( + "http://www.w3.org/2000/xmlns/", "xmlns:wsu", - "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd") - node.setAttributeNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" + ); + node.setAttributeNS( + "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "wsu:Id", - id) - } - else { - node.setAttribute("Id", id) + id + ); + } else { + node.setAttribute("Id", id); } - return id -} + return id; +}; /** * Create the SignedInfo element * */ -SignedXml.prototype.createSignedInfo = function(doc, prefix) { - var transform = this.findCanonicalizationAlgorithm(this.canonicalizationAlgorithm) - var algo = this.findSignatureAlgorithm(this.signatureAlgorithm) - var currentPrefix +SignedXml.prototype.createSignedInfo = function (doc, prefix) { + var transform = this.findCanonicalizationAlgorithm(this.canonicalizationAlgorithm); + var algo = this.findSignatureAlgorithm(this.signatureAlgorithm); + var currentPrefix; - currentPrefix = prefix || '' - currentPrefix = currentPrefix ? currentPrefix + ':' : currentPrefix + currentPrefix = prefix || ""; + currentPrefix = currentPrefix ? currentPrefix + ":" : currentPrefix; - var res = "<" + currentPrefix + "SignedInfo>" - res += "<" + currentPrefix + "CanonicalizationMethod Algorithm=\"" + transform.getAlgorithmName() + "\"" + var res = "<" + currentPrefix + "SignedInfo>"; + res += + "<" + currentPrefix + 'CanonicalizationMethod Algorithm="' + transform.getAlgorithmName() + '"'; if (this.inclusiveNamespacesPrefixList) { - res += ">" - res += "" - res += "" + res += ">"; + res += + ''; + res += ""; } else { - res += " />" + res += " />"; } - res += "<" + currentPrefix + "SignatureMethod Algorithm=\"" + algo.getAlgorithmName() + "\" />" + res += "<" + currentPrefix + 'SignatureMethod Algorithm="' + algo.getAlgorithmName() + '" />'; - res += this.createReferences(doc, prefix) - res += "" - return res -} + res += this.createReferences(doc, prefix); + res += ""; + return res; +}; /** * Create the Signature element * */ -SignedXml.prototype.createSignature = function(prefix) { - var xmlNsAttr = 'xmlns' +SignedXml.prototype.createSignature = function (prefix) { + var xmlNsAttr = "xmlns"; if (prefix) { - xmlNsAttr += ':' + prefix; - prefix += ':'; + xmlNsAttr += ":" + prefix; + prefix += ":"; } else { - prefix = ''; + prefix = ""; } - var signatureValueXml = "<" + prefix + "SignatureValue>" + this.signatureValue + "" + var signatureValueXml = + "<" + prefix + "SignatureValue>" + this.signatureValue + ""; //the canonicalization requires to get a valid xml node. //we need to wrap the info in a dummy signature since it contains the default namespace. - var dummySignatureWrapper = "<" + prefix + "Signature " + xmlNsAttr + "=\"http://www.w3.org/2000/09/xmldsig#\">" + - signatureValueXml + - "" - - var doc = new Dom().parseFromString(dummySignatureWrapper) + var dummySignatureWrapper = + "<" + + prefix + + "Signature " + + xmlNsAttr + + '="http://www.w3.org/2000/09/xmldsig#">' + + signatureValueXml + + ""; + + var doc = new Dom().parseFromString(dummySignatureWrapper); return doc.documentElement.firstChild; -} - +}; -SignedXml.prototype.getSignatureXml = function() { - return this.signatureXml -} +SignedXml.prototype.getSignatureXml = function () { + return this.signatureXml; +}; -SignedXml.prototype.getOriginalXmlWithIds = function() { - return this.originalXmlWithIds -} +SignedXml.prototype.getOriginalXmlWithIds = function () { + return this.originalXmlWithIds; +}; -SignedXml.prototype.getSignedXml = function() { - return this.signedXml -} +SignedXml.prototype.getSignedXml = function () { + return this.signedXml; +}; diff --git a/lib/string-key-info.js b/lib/string-key-info.js index 86b6c6ea..2387f0bc 100644 --- a/lib/string-key-info.js +++ b/lib/string-key-info.js @@ -1,6 +1,6 @@ /** * A basic string based implementation of a FileInfoProvider - * + * * @param {string} key the string contents of a public certificate */ function StringKeyInfo(key) { diff --git a/lib/utils.js b/lib/utils.js index bab75333..33b8d381 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,81 +1,84 @@ -var select = require('xpath').select +var select = require("xpath").select; function findAttr(node, localName, namespace) { - for (var i = 0; i': '>', - '\r': ' ' -} + "&": "&", + "<": "<", + ">": ">", + "\r": " ", +}; -function encodeSpecialCharactersInAttribute(attributeValue){ - return attributeValue - .replace(/([&<"\r\n\t])/g, function(str, item){ - // Special character normalization. See: - // - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes) - // - https://www.w3.org/TR/xml-c14n#Example-Chars - return xml_special_to_encoded_attribute[item] - }) +function encodeSpecialCharactersInAttribute(attributeValue) { + return attributeValue.replace(/([&<"\r\n\t])/g, function (str, item) { + // Special character normalization. See: + // - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes) + // - https://www.w3.org/TR/xml-c14n#Example-Chars + return xml_special_to_encoded_attribute[item]; + }); } -function encodeSpecialCharactersInText(text){ - return text - .replace(/([&<>\r])/g, function(str, item){ - // Special character normalization. See: - // - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes) - // - https://www.w3.org/TR/xml-c14n#Example-Chars - return xml_special_to_encoded_text[item] - }) +function encodeSpecialCharactersInText(text) { + return text.replace(/([&<>\r])/g, function (str, item) { + // Special character normalization. See: + // - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes) + // - https://www.w3.org/TR/xml-c14n#Example-Chars + return xml_special_to_encoded_text[item]; + }); } - -exports.findAttr = findAttr -exports.findChilds = findChilds -exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute -exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText -exports.findFirst = findFirst +exports.findAttr = findAttr; +exports.findChilds = findChilds; +exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute; +exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText; +exports.findFirst = findFirst; diff --git a/test/c14n-non-exclusive-unit-test.js b/test/c14n-non-exclusive-unit-test.js index e4a45e22..cdf47e9b 100644 --- a/test/c14n-non-exclusive-unit-test.js +++ b/test/c14n-non-exclusive-unit-test.js @@ -1,186 +1,199 @@ -var C14nCanonicalization = require("../lib/c14n-canonicalization").C14nCanonicalization - , Dom = require('@xmldom/xmldom').DOMParser - , select = require('xpath').select - , findAncestorNs = require('../lib/signed-xml').SignedXml.findAncestorNs +var C14nCanonicalization = require("../lib/c14n-canonicalization").C14nCanonicalization, + Dom = require("@xmldom/xmldom").DOMParser, + select = require("xpath").select, + findAncestorNs = require("../lib/signed-xml").SignedXml.findAncestorNs; -var test_C14nCanonicalization = function(test, xml, xpath, expected) { +var test_C14nCanonicalization = function (test, xml, xpath, expected) { var doc = new Dom().parseFromString(xml); var elem = select(xpath, doc)[0]; var can = new C14nCanonicalization(); - var result = can.process(elem, { - ancestorNamespaces: findAncestorNs(doc, xpath) - }).toString(); - + var result = can + .process(elem, { + ancestorNamespaces: findAncestorNs(doc, xpath), + }) + .toString(); + test.equal(result, expected); - test.done() + test.done(); }; -var test_findAncestorNs = function(test, xml, xpath, expected){ +var test_findAncestorNs = function (test, xml, xpath, expected) { var doc = new Dom().parseFromString(xml); var result = findAncestorNs(doc, xpath); test.deepEqual(result, expected); - + test.done(); }; // Tests for findAncestorNs -exports["findAncestorNs: Correctly picks up root ancestor namespace"] = function(test){ +exports["findAncestorNs: Correctly picks up root ancestor namespace"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; - var expected = [ - {prefix: "aaa", namespaceURI: "bbb"} - ]; - + var expected = [{ prefix: "aaa", namespaceURI: "bbb" }]; + test_findAncestorNs(test, xml, xpath, expected); }; -exports["findAncestorNs: Correctly picks up intermediate ancestor namespace"] = function(test){ +exports["findAncestorNs: Correctly picks up intermediate ancestor namespace"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; - var expected = [ - {prefix: "aaa", namespaceURI: "bbb"} - ]; - + var expected = [{ prefix: "aaa", namespaceURI: "bbb" }]; + test_findAncestorNs(test, xml, xpath, expected); }; -exports["findAncestorNs: Correctly picks up multiple ancestor namespaces declared in the one same element"] = function(test){ +exports[ + "findAncestorNs: Correctly picks up multiple ancestor namespaces declared in the one same element" +] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; var expected = [ - {prefix: "aaa", namespaceURI: "bbb"}, - {prefix: "ccc", namespaceURI: "ddd"} + { prefix: "aaa", namespaceURI: "bbb" }, + { prefix: "ccc", namespaceURI: "ddd" }, ]; - - test_findAncestorNs(test, xml, xpath, expected); -}; -exports["findAncestorNs: Correctly picks up multiple ancestor namespaces scattered among depth"] = function(test){ - var xml = ""; - var xpath = "/root/child1/child2"; - var expected = [ - {prefix: "ccc", namespaceURI: "ddd"}, - {prefix: "aaa", namespaceURI: "bbb"} - ]; - test_findAncestorNs(test, xml, xpath, expected); }; -exports["findAncestorNs: Correctly picks up multiple ancestor namespaces without duplicate"] = function(test){ - var xml = ""; - var xpath = "/root/child1/child2"; - var expected = [ - {prefix: "ccc", namespaceURI: "bbb"} - ]; - - test_findAncestorNs(test, xml, xpath, expected); -}; +exports["findAncestorNs: Correctly picks up multiple ancestor namespaces scattered among depth"] = + function (test) { + var xml = ""; + var xpath = "/root/child1/child2"; + var expected = [ + { prefix: "ccc", namespaceURI: "ddd" }, + { prefix: "aaa", namespaceURI: "bbb" }, + ]; + + test_findAncestorNs(test, xml, xpath, expected); + }; -exports["findAncestorNs: Correctly eliminates duplicate prefix"] = function(test){ +exports["findAncestorNs: Correctly picks up multiple ancestor namespaces without duplicate"] = + function (test) { + var xml = ""; + var xpath = "/root/child1/child2"; + var expected = [{ prefix: "ccc", namespaceURI: "bbb" }]; + + test_findAncestorNs(test, xml, xpath, expected); + }; + +exports["findAncestorNs: Correctly eliminates duplicate prefix"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; - var expected = [ - {prefix: "ccc", namespaceURI: "AAA"} - ]; - + var expected = [{ prefix: "ccc", namespaceURI: "AAA" }]; + test_findAncestorNs(test, xml, xpath, expected); }; -exports["findAncestorNs: Exclude namespace which is already declared with same prefix on target node"] = function(test){ - var xml = ""; +exports[ + "findAncestorNs: Exclude namespace which is already declared with same prefix on target node" +] = function (test) { + var xml = + ""; var xpath = "/root/child1/child2"; var expected = []; - + test_findAncestorNs(test, xml, xpath, expected); }; -exports["findAncestorNs: Ignores namespace declared in the target xpath node"] = function(test){ +exports["findAncestorNs: Ignores namespace declared in the target xpath node"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; - var expected = [ - {prefix: "aaa", namespaceURI: "bbb"} - ]; - + var expected = [{ prefix: "aaa", namespaceURI: "bbb" }]; + test_findAncestorNs(test, xml, xpath, expected); }; // Tests for c14nCanonicalization -exports["C14n: Correctly picks up root ancestor namespace"] = function(test){ +exports["C14n: Correctly picks up root ancestor namespace"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; var expected = ''; - + test_C14nCanonicalization(test, xml, xpath, expected); }; -exports["C14n: Correctly picks up intermediate ancestor namespace"] = function(test){ +exports["C14n: Correctly picks up intermediate ancestor namespace"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; var expected = ''; - - test_C14nCanonicalization(test, xml, xpath, expected); -}; -exports["C14n: Correctly picks up multiple ancestor namespaces declared in the one same element"] = function(test){ - var xml = ""; - var xpath = "/root/child1/child2"; - var expected = ''; - test_C14nCanonicalization(test, xml, xpath, expected); }; -exports["C14n: Correctly picks up multiple ancestor namespaces scattered among depth"] = function(test){ +exports["C14n: Correctly picks up multiple ancestor namespaces declared in the one same element"] = + function (test) { + var xml = ""; + var xpath = "/root/child1/child2"; + var expected = ''; + + test_C14nCanonicalization(test, xml, xpath, expected); + }; + +exports["C14n: Correctly picks up multiple ancestor namespaces scattered among depth"] = function ( + test +) { var xml = ""; var xpath = "/root/child1/child2"; var expected = ''; - + test_C14nCanonicalization(test, xml, xpath, expected); }; -exports["C14n: Correctly picks up multiple ancestor namespaces without duplicate"] = function(test){ +exports["C14n: Correctly picks up multiple ancestor namespaces without duplicate"] = function ( + test +) { var xml = ""; var xpath = "/root/child1/child2"; var expected = ''; - + test_C14nCanonicalization(test, xml, xpath, expected); }; -exports["C14n: Correctly eliminates duplicate prefix"] = function(test){ +exports["C14n: Correctly eliminates duplicate prefix"] = function (test) { var xml = ""; var xpath = "/root/child1/child2"; var expected = ''; - - test_C14nCanonicalization(test, xml, xpath, expected); -}; -exports["C14n: Exclude namespace which is already declared with same prefix on target node"] = function(test){ - var xml = ""; - var xpath = "/root/child1/child2"; - var expected = ''; - test_C14nCanonicalization(test, xml, xpath, expected); }; -exports["C14n: Preserve namespace declared in the target xpath node"] = function(test){ +exports["C14n: Exclude namespace which is already declared with same prefix on target node"] = + function (test) { + var xml = + ""; + var xpath = "/root/child1/child2"; + var expected = ''; + + test_C14nCanonicalization(test, xml, xpath, expected); + }; + +exports["C14n: Preserve namespace declared in the target xpath node"] = function (test) { var xml = ''; var xpath = "/root/child1/child2"; var expected = ''; - + test_C14nCanonicalization(test, xml, xpath, expected); }; -exports["C14n: Don't redeclare an attribute's namespace prefix if already in scope"] = function(test) { - var xml = "" +exports["C14n: Don't redeclare an attribute's namespace prefix if already in scope"] = function ( + test +) { + var xml = + ""; var xpath = "/root/child1/child2"; var expected = ''; test_C14nCanonicalization(test, xml, xpath, expected); -} +}; -exports["C14n: Don't declare an attribute's namespace prefix if in scope from parent"] = function(test) { - var xml = "" +exports["C14n: Don't declare an attribute's namespace prefix if in scope from parent"] = function ( + test +) { + var xml = + ""; var xpath = "/root/child1"; - var expected = ''; + var expected = + ''; test_C14nCanonicalization(test, xml, xpath, expected); -} +}; diff --git a/test/c14nWithComments-unit-tests.js b/test/c14nWithComments-unit-tests.js index 7a885dcc..223afd97 100644 --- a/test/c14nWithComments-unit-tests.js +++ b/test/c14nWithComments-unit-tests.js @@ -1,220 +1,269 @@ -var c14nWithComments = require("../lib/exclusive-canonicalization").ExclusiveCanonicalizationWithComments - , Dom = require('@xmldom/xmldom').DOMParser - , select = require('xpath').select - , SignedXml = require('../lib/signed-xml.js').SignedXml - - -var compare = function(test, xml, xpath, expected, inclusiveNamespacesPrefixList) { - test.expect(1) - var doc = new Dom().parseFromString(xml) - var elem = select(xpath, doc)[0] - var can = new c14nWithComments() - var result = can.process(elem, { inclusiveNamespacesPrefixList: inclusiveNamespacesPrefixList }).toString() - - test.equal(expected, result) - test.done() -} +var c14nWithComments = + require("../lib/exclusive-canonicalization").ExclusiveCanonicalizationWithComments, + Dom = require("@xmldom/xmldom").DOMParser, + select = require("xpath").select, + SignedXml = require("../lib/signed-xml.js").SignedXml; + +var compare = function (test, xml, xpath, expected, inclusiveNamespacesPrefixList) { + test.expect(1); + var doc = new Dom().parseFromString(xml); + var elem = select(xpath, doc)[0]; + var can = new c14nWithComments(); + var result = can + .process(elem, { inclusiveNamespacesPrefixList: inclusiveNamespacesPrefixList }) + .toString(); + + test.equal(expected, result); + test.done(); +}; module.exports = { - "Exclusive canonicalization works on xml with no namespaces": function (test) { - compare(test, - "123", - "//*", - "123") - }, + compare(test, "123", "//*", "123"); + }, "Exclusive canonicalization works on inner xpath": function (test) { - compare(test, + compare( + test, "123", "//*[local-name(.)='child']", - "123") + "123" + ); }, - "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, + "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, "element used prefixed ns which is also the default": function (test) { - compare(test, - "123", + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - - "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes. ns definition is not duplicated on each usage": function (test) { - compare(test, - "123", + "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes. ns definition is not duplicated on each usage": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes but before used": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespaces defined outside output nodes": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123', + "inclusive" + ); + }, + + "Exclusive canonicalization works on xml with multiple prefixed namespaces defined in inclusive list": + function (test) { + compare( + test, + '123456', + "//*[local-name(.)='child']", + '123456', + "inclusive inclusive2" + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list defined outside output nodes": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123', + "inclusive" + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list used on attribute": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123', + "inclusive" + ); + }, + + "Exclusive canonicalization works on xml with default namespace inside output nodes": function ( + test + ) { + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - - "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes but before used": function (test) { - compare(test, - "123", + "Exclusive canonicalization works on xml with multiple different default namespaces": function ( + test + ) { + compare( + test, + '123', "//*[local-name(.)='child']", - "123") - }, - - - "Exclusive canonicalization works on xml with prefixed namespaces defined outside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123", - "inclusive") - }, - - "Exclusive canonicalization works on xml with multiple prefixed namespaces defined in inclusive list": function (test) { - compare(test, - "123456", - "//*[local-name(.)='child']", - "123456", - "inclusive inclusive2") - }, - - "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list defined outside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123", - "inclusive") - }, - - - "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list used on attribute": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123", - "inclusive") + '123' + ); }, - - "Exclusive canonicalization works on xml with default namespace inside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - - "Exclusive canonicalization works on xml with multiple different default namespaces": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - -"Exclusive canonicalization works on xml with multiple similar default namespaces": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") + "Exclusive canonicalization works on xml with multiple similar default namespaces": function ( + test + ) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); }, - - "Exclusive canonicalization works on xml with default namespace outside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") + "Exclusive canonicalization works on xml with default namespace outside output nodes": function ( + test + ) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); }, - "Exclusive canonicalization works when prefixed namespace is defined in output nodes not in the parent chain of who needs it": function (test) { - compare(test, - "", - "//*[local-name(.)='child']", - "") - }, + "Exclusive canonicalization works when prefixed namespace is defined in output nodes not in the parent chain of who needs it": + function (test) { + compare( + test, + '', + "//*[local-name(.)='child']", + '' + ); + }, "Exclusive canonicalization works on xml with unordered attributes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); }, "Exclusive canonicalization sorts upper case attributes before lower case": function (test) { - compare(test, - "", - "//*[local-name(.)='x']", - "") + compare(test, '', "//*[local-name(.)='x']", ''); }, "C14N#WithComments retains Comments": function (test) { - compare(test, - "", - "//*[local-name(.)='x']", - "") - }, - - "Exclusive canonicalization works on xml with attributes with different namespace than element": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - - "Exclusive canonicalization works on xml with attribute and element values with special characters": function (test) { - compare(test, - "&11", - "//*[local-name(.)='child']", - "&11") + compare( + test, + '', + "//*[local-name(.)='x']", + '' + ); }, + "Exclusive canonicalization works on xml with attributes with different namespace than element": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with attribute and element values with special characters": + function (test) { + compare( + test, + '&11', + "//*[local-name(.)='child']", + '&11' + ); + }, "Exclusive canonicalization preserves white space in values": function (test) { - compare(test, + compare( + test, "12\n3\t", - "//*[local-name(.)='child']", - "12\n3\t") + "//*[local-name(.)='child']", + "12\n3\t" + ); }, - "Exclusive canonicalization preserves white space between elements": function (test) { - compare(test, + compare( + test, "123\n", - "//*[local-name(.)='child']", - "123\n") - }, - + "//*[local-name(.)='child']", + "123\n" + ); + }, "Exclusive canonicalization turns empty element to start-end tag pairs": function (test) { - compare(test, - "", - "//*[local-name(.)='child']", - "") - }, - - -"Exclusive canonicalization preserves empty start-end tag pairs": function (test) { - compare(test, - "", - "//*[local-name(.)='child']", - "") - }, + compare( + test, + "", + "//*[local-name(.)='child']", + "" + ); + }, + "Exclusive canonicalization preserves empty start-end tag pairs": function (test) { + compare( + test, + "", + "//*[local-name(.)='child']", + "" + ); + }, "Exclusive canonicalization with empty default namespace outside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, + compare( + test, + '123', + "//*[local-name(.)='child']", + "123" + ); + }, /* Uncomment this when this issue is fixed "Exclusive canonicalization removal of whitespace between PITarget and its data": function (test) { @@ -226,19 +275,22 @@ module.exports = { */ "Exclusive canonicalization with empty default namespace inside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, "The XML declaration and document type declaration (DTD) are removed": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, + compare( + test, + '123', + "//*[local-name(.)='child']", + "123" + ); + }, /* Uncomment this when this issue is fixed "The XML declaration and document type declaration (DTD) are removed, stylesheet retained": function (test) { @@ -248,121 +300,134 @@ module.exports = { "123") }, */ - "Attribute value delimiters are set to quotation marks (double quotes)": function (test) { - compare(test, - "123 ", - "//*[local-name(.)='child']", - "123 ") + compare( + test, + "123 ", + "//*[local-name(.)='child']", + '123 ' + ); }, - "CDATA sections are replaced with their character content": function (test) { - compare(test, - "123]]>", - "//*[local-name(.)='child']", - "foo & bar in the <x>123</x>") - }, + compare( + test, + "123]]>", + "//*[local-name(.)='child']", + "foo & bar in the <x>123</x>" + ); + }, - "SignedInfo canonization": function (test) { - compare(test, - "http://stockservice.contoso.com/wse/samples/2003/06/StockQuoteRequestuuid:6250c037-bcde-40ab-82b3-3a08efc86cdchttp://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymoushttp://localhost:8889/2008-09-01T17:44:21Z2008-09-01T17:49:21ZMIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=Fkm7AbwiJCiOzY8ldfuA9pTW1G+EtE+UX4Cv7SoMIqeUdfWRDVHZpJAQyf7aoQnlpJNV/3k9L1PT6rJbfV478CkULJENPLm1m0fmDeLzhIHDEANuzp/AirC60tMD5jCARb4B4Nr/6bTmoyDQsTY8VLRiiINng7Mpweg1FZvd8a0=FABRIKAM", - "//*[local-name(.)='SignedInfo']", - "+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=") - }, + "SignedInfo canonization": function (test) { + compare( + test, + 'http://stockservice.contoso.com/wse/samples/2003/06/StockQuoteRequestuuid:6250c037-bcde-40ab-82b3-3a08efc86cdchttp://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymoushttp://localhost:8889/2008-09-01T17:44:21Z2008-09-01T17:49:21ZMIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=Fkm7AbwiJCiOzY8ldfuA9pTW1G+EtE+UX4Cv7SoMIqeUdfWRDVHZpJAQyf7aoQnlpJNV/3k9L1PT6rJbfV478CkULJENPLm1m0fmDeLzhIHDEANuzp/AirC60tMD5jCARb4B4Nr/6bTmoyDQsTY8VLRiiINng7Mpweg1FZvd8a0=FABRIKAM', + "//*[local-name(.)='SignedInfo']", + '+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=' + ); + }, "Exclusive canonicalization works on complex xml": function (test) { - compare(test, - "\n" + - "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " ererer\n" + - " dfdf\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " erer\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \r" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - "", - "//*[local-name(.)='Body']", - "\n \n \n \n \n \n \n \n \n \n \n \n \n ererer\n dfdf\n \n \n \n \n \n \n \n \n \n erer\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ") + compare( + test, + '\n' + + '\n' + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + " ererer\n" + + " dfdf\n" + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + ' \n' + + ' \n' + + " \n" + + " \n" + + " erer\n" + + ' \n' + + " \n" + + ' \n' + + " \n" + + " \n" + + ' \n' + + " \n" + + ' \n' + + " \n" + + ' \n' + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \r" + + " \n" + + " \n" + + " \n" + + ' \n' + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "", + "//*[local-name(.)='Body']", + '\n \n \n \n \n \n \n \n \n \n \n \n \n ererer\n dfdf\n \n \n \n \n \n \n \n \n \n erer\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ' + ); }, "Multiple Canonicalization with namespace definition outside of signed element": function (test) { - //var doc = new Dom().parseFromString("") - var doc = new Dom().parseFromString("") - var node = select("//*[local-name(.)='y']", doc)[0] - var sig = new SignedXml() - var res = sig.getCanonXml(["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"], node) - test.equal("", res) - test.done() - }, - - "Enveloped-signature canonicalization respects currentnode": function(test) { + //var doc = new Dom().parseFromString("") + var doc = new Dom().parseFromString(''); + var node = select("//*[local-name(.)='y']", doc)[0]; + var sig = new SignedXml(); + var res = sig.getCanonXml( + [ + "http://www.w3.org/2000/09/xmldsig#enveloped-signature", + "http://www.w3.org/2001/10/xml-exc-c14n#", + ], + node + ); + test.equal('', res); + test.done(); + }, + + "Enveloped-signature canonicalization respects currentnode": function (test) { // older versions of enveloped-signature removed the first signature in the whole doc, but should // be the signature inside the current node if we want to be able to verify multiple signatures // in a document. - var xml = ''; + var xml = + ''; var doc = new Dom().parseFromString(xml); var node = select("//*[local-name(.)='y']", doc)[0]; var sig = new SignedXml(); var transforms = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"]; var res = sig.getCanonXml(transforms, node); - test.equal("", res ); + test.equal("", res); test.done(); }, @@ -371,17 +436,22 @@ module.exports = { Namespace nodes have a lesser document order position than attribute nodes. \ - An element's namespace nodes are sorted lexicographically by local name (the default namespace node, if one exists, has no local name and is therefore lexicographically least). \ - An element's attribute nodes are sorted lexicographically with namespace URI as the primary key and local name as the secondary key (an empty namespace URI is lexicographically least). \ - Lexicographic comparison, which orders strings from least to greatest alphabetically, is based on the UCS codepoint values, which is equivalent to lexicographic ordering based on UTF-8.": function (test) { - compare(test, - '', - "//*[local-name(.)='root']", - '') - }, - - "saml attributed order (bug #25)": function (test) { - compare(test, + Lexicographic comparison, which orders strings from least to greatest alphabetically, is based on the UCS codepoint values, which is equivalent to lexicographic ordering based on UTF-8.": + function (test) { + compare( + test, + '', + "//*[local-name(.)='root']", + '' + ); + }, + + "saml attributed order (bug #25)": function (test) { + compare( + test, '', - "//*[local-name(.)='root']", - '') - } -} + "//*[local-name(.)='root']", + '' + ); + }, +}; diff --git a/test/canonicalization-unit-tests.js b/test/canonicalization-unit-tests.js index 31e33441..03bf8500 100644 --- a/test/canonicalization-unit-tests.js +++ b/test/canonicalization-unit-tests.js @@ -1,255 +1,331 @@ -var ExclusiveCanonicalization = require("../lib/exclusive-canonicalization").ExclusiveCanonicalization - , Dom = require('@xmldom/xmldom').DOMParser - , select = require('xpath').select - , SignedXml = require('../lib/signed-xml.js').SignedXml - - -var compare = function(test, xml, xpath, expected, inclusiveNamespacesPrefixList, defaultNsForPrefix) { - test.expect(1) - var doc = new Dom().parseFromString(xml) - var elem = select(xpath, doc)[0] - var can = new ExclusiveCanonicalization() - var result = can.process(elem, { +var ExclusiveCanonicalization = + require("../lib/exclusive-canonicalization").ExclusiveCanonicalization, + Dom = require("@xmldom/xmldom").DOMParser, + select = require("xpath").select, + SignedXml = require("../lib/signed-xml.js").SignedXml; + +var compare = function ( + test, + xml, + xpath, + expected, + inclusiveNamespacesPrefixList, + defaultNsForPrefix +) { + test.expect(1); + var doc = new Dom().parseFromString(xml); + var elem = select(xpath, doc)[0]; + var can = new ExclusiveCanonicalization(); + var result = can + .process(elem, { inclusiveNamespacesPrefixList: inclusiveNamespacesPrefixList, - defaultNsForPrefix: defaultNsForPrefix - }).toString() - - test.equal(expected, result) - test.done() -} + defaultNsForPrefix: defaultNsForPrefix, + }) + .toString(); -module.exports = { + test.equal(expected, result); + test.done(); +}; +module.exports = { "Exclusive canonicalization works on xml with no namespaces": function (test) { - compare(test, - "123", - "//*", - "123") - }, + compare(test, "123", "//*", "123"); + }, "Exclusive canonicalization works on inner xpath": function (test) { - compare(test, + compare( + test, "123", "//*[local-name(.)='child']", - "123") + "123" + ); }, - "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, + "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, "element used prefixed ns which is also the default": function (test) { - compare(test, - "123", + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - "Exclusive canonicalization works with default namespace for prefix": function(test) { - compare(test, + "Exclusive canonicalization works with default namespace for prefix": function (test) { + compare( + test, '', "//*[local-name(.)='SignedInfo']", '', undefined, - { ds: 'http://www.w3.org/2000/09/xmldsig#' }) - }, - - "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes. ns definition is not duplicated on each usage": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - - "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes but before used": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - - "Exclusive canonicalization works on xml with prefixed namespaces defined outside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123", - "inclusive") - }, - - "Exclusive canonicalization works on xml with multiple prefixed namespaces defined in inclusive list": function (test) { - compare(test, - "123456", - "//*[local-name(.)='child']", - "123456", - "inclusive inclusive2") + { ds: "http://www.w3.org/2000/09/xmldsig#" } + ); }, - "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list defined outside output nodes": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123", - "inclusive") - }, - - - "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list used on attribute": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123", - "inclusive") - }, - - - "Exclusive canonicalization works on xml with default namespace inside output nodes": function (test) { - compare(test, - "123", + "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes. ns definition is not duplicated on each usage": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes but before used": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespaces defined outside output nodes": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123' + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123', + "inclusive" + ); + }, + + "Exclusive canonicalization works on xml with multiple prefixed namespaces defined in inclusive list": + function (test) { + compare( + test, + '123456', + "//*[local-name(.)='child']", + '123456', + "inclusive inclusive2" + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list defined outside output nodes": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123', + "inclusive" + ); + }, + + "Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list used on attribute": + function (test) { + compare( + test, + '123', + "//*[local-name(.)='child']", + '123', + "inclusive" + ); + }, + + "Exclusive canonicalization works on xml with default namespace inside output nodes": function ( + test + ) { + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - - "Exclusive canonicalization works on xml with multiple different default namespaces": function (test) { - compare(test, - "123", + "Exclusive canonicalization works on xml with multiple different default namespaces": function ( + test + ) { + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, -"Exclusive canonicalization works on xml with multiple similar default namespaces": function (test) { - compare(test, - "123", + "Exclusive canonicalization works on xml with multiple similar default namespaces": function ( + test + ) { + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - - "Exclusive canonicalization works on xml with default namespace outside output nodes": function (test) { - compare(test, - "123", + "Exclusive canonicalization works on xml with default namespace outside output nodes": function ( + test + ) { + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - "Exclusive canonicalization works when prefixed namespace is defined in output nodes not in the parent chain of who needs it": function (test) { - compare(test, - "", - "//*[local-name(.)='child']", - "") - }, + "Exclusive canonicalization works when prefixed namespace is defined in output nodes not in the parent chain of who needs it": + function (test) { + compare( + test, + '', + "//*[local-name(.)='child']", + '' + ); + }, "Exclusive canonicalization works on xml with unordered attributes": function (test) { - compare(test, - "123", + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, "Exclusive canonicalization sorts upper case attributes before lower case": function (test) { - compare(test, - "", - "//*[local-name(.)='x']", - "") + compare(test, '', "//*[local-name(.)='x']", ''); }, "Exclusive canonicalization removes Comments": function (test) { - compare(test, - "", + compare( + test, + '', "//*[local-name(.)='x']", - "") + '' + ); }, - "Exclusive canonicalization works on xml with attributes with different namespace than element": function (test) { - compare(test, - "123", - "//*[local-name(.)='child']", - "123") - }, - - "Exclusive canonicalization works on xml with attribute values with special characters": function (test) { - compare(test, - ""11 \" attrUnencoded='&>\"11\r\n'>11", - "//*[local-name(.)='child']", - ""11 \" attrUnencoded=\"&>"11 \">11") - }, - - "Exclusive canonicalization does not normalize whitespace characters into single spaces": function (test) { - compare(test, - "11", + "Exclusive canonicalization works on xml with attributes with different namespace than element": + function (test) { + compare( + test, + '123', "//*[local-name(.)='child']", - "11") - }, - - "Exclusive canonicalization works on xml with element values with special characters": function (test) { - compare(test, - // eslint-disable-next-line no-useless-escape - "&<>"11 &>\"11\r\", + '123' + ); + }, + + "Exclusive canonicalization works on xml with attribute values with special characters": + function (test) { + compare( + test, + '"11\r\n\'>11', "//*[local-name(.)='child']", - "&<>\"11 &>\"11\n") + '11' + ); + }, + + "Exclusive canonicalization does not normalize whitespace characters into single spaces": + function (test) { + compare( + test, + '11', + "//*[local-name(.)='child']", + '11' + ); + }, + + "Exclusive canonicalization works on xml with element values with special characters": function ( + test + ) { + compare( + test, + // eslint-disable-next-line no-useless-escape + '&<>"11 &>"11\r', + "//*[local-name(.)='child']", + '&<>"11 &>"11\n' + ); }, "Exclusive canonicalization preserves white space in values": function (test) { - compare(test, + compare( + test, "12\n3\t", "//*[local-name(.)='child']", - "12\n3\t") + "12\n3\t" + ); }, - "Exclusive canonicalization does not alter CR-NL (windows line separator) sequences": function(test){ - compare(test, - "123\r\n", - "//*[local-name(.)='child']", - "123\n") + "Exclusive canonicalization does not alter CR-NL (windows line separator) sequences": function ( + test + ) { + compare( + test, + "123\r\n", + "//*[local-name(.)='child']", + "123\n" + ); }, - "Exclusive canonicalization preserves and encodes CR white space": function(test){ - compare(test, - "\r12\r3\r", - "//*[local-name(.)='child']", - "\n12\n3\n") + "Exclusive canonicalization preserves and encodes CR white space": function (test) { + compare( + test, + "\r12\r3\r", + "//*[local-name(.)='child']", + "\n12\n3\n" + ); }, "Exclusive canonicalization preserves white space between elements": function (test) { - compare(test, + compare( + test, "123\n", "//*[local-name(.)='child']", - "123\n") + "123\n" + ); }, "Exclusive canonicalization turns empty element to start-end tag pairs": function (test) { - compare(test, + compare( + test, "", "//*[local-name(.)='child']", - "") + "" + ); }, - -"Exclusive canonicalization preserves empty start-end tag pairs": function (test) { - compare(test, + "Exclusive canonicalization preserves empty start-end tag pairs": function (test) { + compare( + test, "", "//*[local-name(.)='child']", - "") + "" + ); }, - "Exclusive canonicalization with empty default namespace outside output nodes": function (test) { - compare(test, - "123", + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + "123" + ); }, /* Uncomment this when this issue is fixed @@ -262,21 +338,23 @@ module.exports = { */ "Exclusive canonicalization with empty default namespace inside output nodes": function (test) { - compare(test, - "123", + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + '123' + ); }, - "The XML declaration and document type declaration (DTD) are removed": function (test) { - compare(test, - "123", + compare( + test, + '123', "//*[local-name(.)='child']", - "123") + "123" + ); }, - /* Uncomment this when this issue is fixed "The XML declaration and document type declaration (DTD) are removed, stylesheet retained": function (test) { compare(test, @@ -287,118 +365,132 @@ module.exports = { */ "Attribute value delimiters are set to quotation marks (double quotes)": function (test) { - compare(test, + compare( + test, "123 ", "//*[local-name(.)='child']", - "123 ") + '123 ' + ); }, - "CDATA sections are replaced with their character content": function (test) { - compare(test, + compare( + test, "123]]>", "//*[local-name(.)='child']", - "foo & bar in the <x>123</x>") + "foo & bar in the <x>123</x>" + ); }, - "SignedInfo canonization": function (test) { - compare(test, - "http://stockservice.contoso.com/wse/samples/2003/06/StockQuoteRequestuuid:6250c037-bcde-40ab-82b3-3a08efc86cdchttp://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymoushttp://localhost:8889/2008-09-01T17:44:21Z2008-09-01T17:49:21ZMIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=Fkm7AbwiJCiOzY8ldfuA9pTW1G+EtE+UX4Cv7SoMIqeUdfWRDVHZpJAQyf7aoQnlpJNV/3k9L1PT6rJbfV478CkULJENPLm1m0fmDeLzhIHDEANuzp/AirC60tMD5jCARb4B4Nr/6bTmoyDQsTY8VLRiiINng7Mpweg1FZvd8a0=FABRIKAM", + "SignedInfo canonization": function (test) { + compare( + test, + 'http://stockservice.contoso.com/wse/samples/2003/06/StockQuoteRequestuuid:6250c037-bcde-40ab-82b3-3a08efc86cdchttp://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymoushttp://localhost:8889/2008-09-01T17:44:21Z2008-09-01T17:49:21ZMIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=Fkm7AbwiJCiOzY8ldfuA9pTW1G+EtE+UX4Cv7SoMIqeUdfWRDVHZpJAQyf7aoQnlpJNV/3k9L1PT6rJbfV478CkULJENPLm1m0fmDeLzhIHDEANuzp/AirC60tMD5jCARb4B4Nr/6bTmoyDQsTY8VLRiiINng7Mpweg1FZvd8a0=FABRIKAM', "//*[local-name(.)='SignedInfo']", - "+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=") + '+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=' + ); }, "Exclusive canonicalization works on complex xml": function (test) { - compare(test, - "\n" + - "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " ererer\n" + - " dfdf\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " erer\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \r" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - "", + compare( + test, + '\n' + + '\n' + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + " ererer\n" + + " dfdf\n" + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + ' \n' + + ' \n' + + " \n" + + " \n" + + " erer\n" + + ' \n' + + " \n" + + ' \n' + + " \n" + + " \n" + + ' \n' + + " \n" + + ' \n' + + " \n" + + ' \n' + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \r" + + " \n" + + " \n" + + " \n" + + ' \n' + + " \n" + + ' \n' + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "", "//*[local-name(.)='Body']", - "\n \n \n \n \n \n \n \n \n \n \n \n \n ererer\n dfdf\n \n \n \n \n \n \n \n \n \n erer\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ") + '\n \n \n \n \n \n \n \n \n \n \n \n \n ererer\n dfdf\n \n \n \n \n \n \n \n \n \n erer\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ' + ); }, "Multiple Canonicalization with namespace definition outside of signed element": function (test) { - //var doc = new Dom().parseFromString("") - var doc = new Dom().parseFromString("") - var node = select("//*[local-name(.)='y']", doc)[0] - var sig = new SignedXml() - var res = sig.getCanonXml(["http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#"], node) - test.equal("", res) - test.done() + //var doc = new Dom().parseFromString("") + var doc = new Dom().parseFromString(''); + var node = select("//*[local-name(.)='y']", doc)[0]; + var sig = new SignedXml(); + var res = sig.getCanonXml( + [ + "http://www.w3.org/2000/09/xmldsig#enveloped-signature", + "http://www.w3.org/2001/10/xml-exc-c14n#", + ], + node + ); + test.equal('', res); + test.done(); }, - "Enveloped-signature canonicalization respects currentnode": function(test) { + "Enveloped-signature canonicalization respects currentnode": function (test) { // older versions of enveloped-signature removed the first signature in the whole doc, but should // be the signature inside the current node if we want to be able to verify multiple signatures // in a document. - var xml = ''; + var xml = + ''; var doc = new Dom().parseFromString(xml); var node = select("//*[local-name(.)='y']", doc)[0]; var sig = new SignedXml(); var transforms = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"]; var res = sig.getCanonXml(transforms, node); - test.equal("", res ); + test.equal("", res); test.done(); }, @@ -407,41 +499,50 @@ module.exports = { Namespace nodes have a lesser document order position than attribute nodes. \ - An element's namespace nodes are sorted lexicographically by local name (the default namespace node, if one exists, has no local name and is therefore lexicographically least). \ - An element's attribute nodes are sorted lexicographically with namespace URI as the primary key and local name as the secondary key (an empty namespace URI is lexicographically least). \ - Lexicographic comparison, which orders strings from least to greatest alphabetically, is based on the UCS codepoint values, which is equivalent to lexicographic ordering based on UTF-8.": function (test) { - compare(test, - '', - "//*[local-name(.)='root']", - '') - }, - - "saml attributed order (bug #25)": function (test) { - compare(test, + Lexicographic comparison, which orders strings from least to greatest alphabetically, is based on the UCS codepoint values, which is equivalent to lexicographic ordering based on UTF-8.": + function (test) { + compare( + test, + '', + "//*[local-name(.)='root']", + '' + ); + }, + + "saml attributed order (bug #25)": function (test) { + compare( + test, '', "//*[local-name(.)='root']", - '') - }, - - "Body Xml Element Canonicalization": function (test) { - compare(test, - ''+ - ''+ - '3810016849-201501-KB-0000'+ - ''+ - '', + '' + ); + }, + + "Body Xml Element Canonicalization": function (test) { + compare( + test, + '' + + '' + + '3810016849-201501-KB-0000' + + "" + + "", "//*", - ''+ - ''+ - '3810016849-201501-KB-0000'+ - ''+ - '') - }, + '' + + '' + + '3810016849-201501-KB-0000' + + "" + + "" + ); + }, "Overriding namespace in canonicalization": function (test) { - compare(test, - '', - '//*', - '', - 'ds') - } -} + compare( + test, + '', + "//*", + '', + "ds" + ); + }, +}; diff --git a/test/document-test.js b/test/document-test.js index be36a9d8..cf53931c 100644 --- a/test/document-test.js +++ b/test/document-test.js @@ -1,12 +1,19 @@ -var crypto = require('../index'); -var xpath = require('xpath'); -var xmldom = require('@xmldom/xmldom'); -var fs = require('fs'); +var crypto = require("../index"); +var xpath = require("xpath"); +var xmldom = require("@xmldom/xmldom"); +var fs = require("fs"); -exports['test with a document (using FileKeyInfo)'] = function (test) { - var xml = fs.readFileSync('./test/static/valid_saml.xml', 'utf-8'); +exports["test with a document (using FileKeyInfo)"] = function (test) { + var xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = new xmldom.DOMParser().parseFromString(xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0].toString()); + var signature = new xmldom.DOMParser().parseFromString( + xpath + .select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0] + .toString() + ); var sig = new crypto.SignedXml(); sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); sig.loadSignature(signature); @@ -15,16 +22,22 @@ exports['test with a document (using FileKeyInfo)'] = function (test) { test.done(); }; -exports['test with a document (using StringKeyInfo)'] = function (test) { - var xml = fs.readFileSync('./test/static/valid_saml.xml', 'utf-8'); +exports["test with a document (using StringKeyInfo)"] = function (test) { + var xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = new xmldom.DOMParser().parseFromString(xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0].toString()); + var signature = new xmldom.DOMParser().parseFromString( + xpath + .select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0] + .toString() + ); var sig = new crypto.SignedXml(); - var feidePublicCert = fs.readFileSync('./test/static/feide_public.pem'); + var feidePublicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.keyInfoProvider = new crypto.StringKeyInfo(feidePublicCert); sig.loadSignature(signature); var result = sig.checkSignature(xml); test.equal(result, true); test.done(); }; - diff --git a/test/hmac-tests.js b/test/hmac-tests.js index f7b5adf4..95d49388 100644 --- a/test/hmac-tests.js +++ b/test/hmac-tests.js @@ -1,54 +1,58 @@ -var crypto = require('../index'); -var xpath = require('xpath'); -var xmldom = require('@xmldom/xmldom'); -var fs = require('fs'); +var crypto = require("../index"); +var xpath = require("xpath"); +var xmldom = require("@xmldom/xmldom"); +var fs = require("fs"); -crypto.SignedXml.enableHMAC() +crypto.SignedXml.enableHMAC(); -exports['test validating HMAC signature'] = function (test) { - var xml = fs.readFileSync('./test/static/hmac_signature.xml', 'utf-8'); - var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; - var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key"); - sig.loadSignature(signature); - var result = sig.checkSignature(xml); - test.equal(result, true); - test.done(); +exports["test validating HMAC signature"] = function (test) { + var xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); + var doc = new xmldom.DOMParser().parseFromString(xml); + var signature = xpath.select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var sig = new crypto.SignedXml(); + sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key"); + sig.loadSignature(signature); + var result = sig.checkSignature(xml); + test.equal(result, true); + test.done(); }; -exports['test HMAC signature with incorrect key'] = function (test) { - var xml = fs.readFileSync('./test/static/hmac_signature.xml', 'utf-8'); - var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; - var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac-foobar.key"); - sig.loadSignature(signature); - var result = sig.checkSignature(xml); - test.equal(result, false); - test.done(); +exports["test HMAC signature with incorrect key"] = function (test) { + var xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); + var doc = new xmldom.DOMParser().parseFromString(xml); + var signature = xpath.select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var sig = new crypto.SignedXml(); + sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac-foobar.key"); + sig.loadSignature(signature); + var result = sig.checkSignature(xml); + test.equal(result, false); + test.done(); }; +exports["test create and validate HMAC signature"] = function (test) { + var xml = "" + "" + "Harry Potter" + "" + ""; + var sig = new crypto.SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/hmac.key"); + sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; + sig.addReference("//*[local-name(.)='book']"); + sig.computeSignature(xml); -exports['test create and validate HMAC signature'] = function (test) { - var xml = "" + - "" + - "Harry Potter" + - "" + - ""; - var sig = new crypto.SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/hmac.key"); - sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; - sig.addReference("//*[local-name(.)='book']"); - sig.computeSignature(xml); + var doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); + var signature = xpath.select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var verify = new crypto.SignedXml(); + verify.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key"); + verify.loadSignature(signature); + var result = verify.checkSignature(sig.getSignedXml()); + test.equal(result, true); - var doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); - var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; - var verify = new crypto.SignedXml(); - verify.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key"); - verify.loadSignature(signature); - var result = verify.checkSignature(sig.getSignedXml()); - test.equal(result, true); - - test.done(); + test.done(); }; diff --git a/test/saml-response-test.js b/test/saml-response-test.js index d2cddb43..f97990e1 100644 --- a/test/saml-response-test.js +++ b/test/saml-response-test.js @@ -1,12 +1,15 @@ -var crypto = require('../index'); -var xpath = require('xpath'); -var xmldom = require('@xmldom/xmldom'); -var fs = require('fs'); +var crypto = require("../index"); +var xpath = require("xpath"); +var xmldom = require("@xmldom/xmldom"); +var fs = require("fs"); -exports['test validating SAML response'] = function (test) { - var xml = fs.readFileSync('./test/static/valid_saml.xml', 'utf-8'); +exports["test validating SAML response"] = function (test) { + var xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var signature = xpath.select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; var sig = new crypto.SignedXml(); sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); sig.loadSignature(signature); @@ -15,60 +18,73 @@ exports['test validating SAML response'] = function (test) { test.done(); }; -exports['test validating wrapped assertion signature'] = function (test) { - var xml = fs.readFileSync('./test/static/valid_saml_signature_wrapping.xml', 'utf-8'); +exports["test validating wrapped assertion signature"] = function (test) { + var xml = fs.readFileSync("./test/static/valid_saml_signature_wrapping.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); var assertion = xpath.select("//*[local-name(.)='Assertion']", doc)[0]; - var signature = xpath.select("//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion)[0]; + var signature = xpath.select( + "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + assertion + )[0]; var sig = new crypto.SignedXml(); sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); sig.loadSignature(signature); test.throws( - function() { + function () { sig.checkSignature(xml); }, Error, - 'Should not validate a document which contains multiple elements with the ' + - 'same value for the ID / Id / Id attributes, in order to prevent ' + - 'signature wrapping attack.' + "Should not validate a document which contains multiple elements with the " + + "same value for the ID / Id / Id attributes, in order to prevent " + + "signature wrapping attack." ); test.done(); }; -exports['test validating SAML response where a namespace is defined outside the signed element'] = function (test) { - var xml = fs.readFileSync('./test/static/saml_external_ns.xml', 'utf-8'); - var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xpath.select("//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; - var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/saml_external_ns.pem"); - sig.loadSignature(signature); - var result = sig.checkSignature(xml); - test.equal(result, true); - test.done(); -}; +exports["test validating SAML response where a namespace is defined outside the signed element"] = + function (test) { + var xml = fs.readFileSync("./test/static/saml_external_ns.xml", "utf-8"); + var doc = new xmldom.DOMParser().parseFromString(xml); + var signature = xpath.select( + "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var sig = new crypto.SignedXml(); + sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/saml_external_ns.pem"); + sig.loadSignature(signature); + var result = sig.checkSignature(xml); + test.equal(result, true); + test.done(); + }; -exports['test reference id does not contain quotes'] = function (test) { - var xml = fs.readFileSync('./test/static/id_with_quotes.xml', 'utf-8'); +exports["test reference id does not contain quotes"] = function (test) { + var xml = fs.readFileSync("./test/static/id_with_quotes.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); var assertion = xpath.select("//*[local-name(.)='Assertion']", doc)[0]; - var signature = xpath.select("//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion)[0]; + var signature = xpath.select( + "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + assertion + )[0]; var sig = new crypto.SignedXml(); sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); sig.loadSignature(signature); test.throws( - function() { + function () { sig.checkSignature(xml); }, Error, - 'id should not contain quotes' + "id should not contain quotes" ); test.done(); }; -exports['test validating SAML response WithComments'] = function (test) { - var xml = fs.readFileSync('./test/static/valid_saml_withcomments.xml', 'utf-8'); +exports["test validating SAML response WithComments"] = function (test) { + var xml = fs.readFileSync("./test/static/valid_saml_withcomments.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var signature = xpath.select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; var sig = new crypto.SignedXml(); sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); sig.loadSignature(signature); diff --git a/test/signature-integration-tests.js b/test/signature-integration-tests.js index a97bfbe6..0ae4ee87 100644 --- a/test/signature-integration-tests.js +++ b/test/signature-integration-tests.js @@ -1,159 +1,164 @@ -var xpath = require('xpath') - , Dom = require('@xmldom/xmldom').DOMParser - , SignedXml = require('../lib/signed-xml.js').SignedXml - , fs = require('fs') - , crypto = require('../index') - -module.exports = { - +var xpath = require("xpath"), + Dom = require("@xmldom/xmldom").DOMParser, + SignedXml = require("../lib/signed-xml.js").SignedXml, + fs = require("fs"), + crypto = require("../index"); +module.exports = { "verify signature": function (test) { - var xml = "" + var xml = + ''; verifySignature(test, xml, "./test/static/integration/expectedVerify.xml", [ - "//*[local-name(.)='x']", - "//*[local-name(.)='y']", - "//*[local-name(.)='w']"]) + "//*[local-name(.)='x']", + "//*[local-name(.)='y']", + "//*[local-name(.)='w']", + ]); }, - - "verify signature of complex element": function (test) { - var xml = "" + - "" + - "Harry Potter" + - "" + - "Joanne K" + - "Rowling" + - "" + - "" + - "" - - verifySignature(test, xml, "./test/static/integration/expectedVerifyComplex.xml", ["//*[local-name(.)='book']"]) + var xml = + "" + + "" + + "Harry Potter" + + '' + + "Joanne K" + + "Rowling" + + "" + + "" + + ""; + + verifySignature(test, xml, "./test/static/integration/expectedVerifyComplex.xml", [ + "//*[local-name(.)='book']", + ]); }, + "empty URI reference should consider the whole document": function (test) { + var xml = "" + "" + "Harry Potter" + "" + ""; + + var signature = + '' + + "" + + '' + + '' + + '' + + "" + + '' + + "" + + '' + + "1tjZsV007JgvE1YFe1C8sMQ+iEg=" + + "" + + "" + + "FONRc5/nnQE2GMuEV0wK5/ofUJMHH7dzZ6VVd+oHDLfjfWax/lCMzUahJxW1i/dtm9Pl0t2FbJONVd3wwDSZzy6u5uCnj++iWYkRpIEN19RAzEMD1ejfZET8j3db9NeBq2JjrPbw81Fm7qKvte6jGa9ThTTB+1MHFRkC8qjukRM=" + + ""; - - "empty URI reference should consider the whole document": function(test) { - var xml = "" + - "" + - "Harry Potter" + - "" + - ""; - - var signature = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '1tjZsV007JgvE1YFe1C8sMQ+iEg=' + - '' + - '' + - 'FONRc5/nnQE2GMuEV0wK5/ofUJMHH7dzZ6VVd+oHDLfjfWax/lCMzUahJxW1i/dtm9Pl0t2FbJONVd3wwDSZzy6u5uCnj++iWYkRpIEN19RAzEMD1ejfZET8j3db9NeBq2JjrPbw81Fm7qKvte6jGa9ThTTB+1MHFRkC8qjukRM=' + - ''; - - var sig = new crypto.SignedXml() - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/client_public.pem") + var sig = new crypto.SignedXml(); + sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/client_public.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); test.equal(result, true); test.done(); }, - "add canonicalization if output of transforms will be a node-set rather than an octet stream": function(test) { - - var xml = fs.readFileSync('./test/static/windows_store_signature.xml', 'utf-8'); - - // Make sure that whitespace in the source document is removed -- see xml-crypto issue #23 and post at - // http://webservices20.blogspot.co.il/2013/06/validating-windows-mobile-app-store.html - // This regex is naive but works for this test case; for a more general solution consider - // the xmldom-fork-fixed library which can pass {ignoreWhiteSpace: true} into the Dom constructor. - xml = xml.replace(/>\s*<'); - + "add canonicalization if output of transforms will be a node-set rather than an octet stream": + function (test) { + var xml = fs.readFileSync("./test/static/windows_store_signature.xml", "utf-8"); + + // Make sure that whitespace in the source document is removed -- see xml-crypto issue #23 and post at + // http://webservices20.blogspot.co.il/2013/06/validating-windows-mobile-app-store.html + // This regex is naive but works for this test case; for a more general solution consider + // the xmldom-fork-fixed library which can pass {ignoreWhiteSpace: true} into the Dom constructor. + xml = xml.replace(/>\s*<"); + + var doc = new Dom().parseFromString(xml); + xml = doc.firstChild.toString(); + + var signature = xpath.select( + "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var sig = new crypto.SignedXml(); + sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/windows_store_certificate.pem"); + sig.loadSignature(signature); + var result = sig.checkSignature(xml); + test.equal(result, true); + test.done(); + }, + + "signature with inclusive namespaces": function (test) { + var xml = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.xml", "utf-8"); var doc = new Dom().parseFromString(xml); xml = doc.firstChild.toString(); - var signature = xpath.select("//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var signature = xpath.select( + "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/windows_store_certificate.pem"); + sig.keyInfoProvider = new crypto.FileKeyInfo( + "./test/static/signature_with_inclusivenamespaces.pem" + ); sig.loadSignature(signature); var result = sig.checkSignature(xml); test.equal(result, true); test.done(); }, - - "signature with inclusive namespaces": function(test) { - - var xml = fs.readFileSync('./test/static/signature_with_inclusivenamespaces.xml', 'utf-8'); - var doc = new Dom().parseFromString(xml); - xml = doc.firstChild.toString() - - var signature = xpath.select("//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; - var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/signature_with_inclusivenamespaces.pem"); - sig.loadSignature(signature); - var result = sig.checkSignature(xml); - test.equal(result, true); - test.done(); - }, - - - - "signature with inclusive namespaces with unix line separators": function(test) { - - var xml = fs.readFileSync('./test/static/signature_with_inclusivenamespaces_lines.xml', 'utf-8'); + "signature with inclusive namespaces with unix line separators": function (test) { + var xml = fs.readFileSync( + "./test/static/signature_with_inclusivenamespaces_lines.xml", + "utf-8" + ); var doc = new Dom().parseFromString(xml); - xml = doc.firstChild.toString() + xml = doc.firstChild.toString(); - var signature = xpath.select("//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var signature = xpath.select( + "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/signature_with_inclusivenamespaces.pem"); + sig.keyInfoProvider = new crypto.FileKeyInfo( + "./test/static/signature_with_inclusivenamespaces.pem" + ); sig.loadSignature(signature); var result = sig.checkSignature(xml); test.equal(result, true); test.done(); }, - - - "signature with inclusive namespaces with windows line separators": function(test) { - - var xml = fs.readFileSync('./test/static/signature_with_inclusivenamespaces_lines_windows.xml', 'utf-8'); + "signature with inclusive namespaces with windows line separators": function (test) { + var xml = fs.readFileSync( + "./test/static/signature_with_inclusivenamespaces_lines_windows.xml", + "utf-8" + ); var doc = new Dom().parseFromString(xml); - xml = doc.firstChild.toString() + xml = doc.firstChild.toString(); - var signature = xpath.select("//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var signature = xpath.select( + "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/signature_with_inclusivenamespaces.pem"); + sig.keyInfoProvider = new crypto.FileKeyInfo( + "./test/static/signature_with_inclusivenamespaces.pem" + ); sig.loadSignature(signature); var result = sig.checkSignature(xml); test.equal(result, true); test.done(); }, + "should create single root xml document when signing inner node": function (test) { + var xml = "" + "" + "Harry Potter" + "" + ""; + var sig = new SignedXml(); + sig.addReference("//*[local-name(.)='book']"); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.computeSignature(xml); - - "should create single root xml document when signing inner node": function(test) { - var xml = "" + - "" + - "Harry Potter" + - "" + - "" - - var sig = new SignedXml() - sig.addReference("//*[local-name(.)='book']") - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.computeSignature(xml) - var signed = sig.getSignedXml(); - + var doc = new Dom().parseFromString(signed); - + /* Expecting this structure: @@ -174,31 +179,34 @@ module.exports = { */ - + test.ok(doc.documentElement.nodeName == "library", "root node = ."); test.ok(doc.childNodes.length == 1, "only one root node is expected."); - test.ok(doc.documentElement.childNodes.length == 2, " should have two child nodes : and "); - - test.done(); - } + test.ok( + doc.documentElement.childNodes.length == 2, + " should have two child nodes : and " + ); -} + test.done(); + }, +}; -function verifySignature(test, xml, expected, xpath) { - - var sig = new SignedXml() - sig.signingKey = fs.readFileSync("./test/static/client.pem") +function verifySignature(test, xml, expected, xpath) { + var sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.keyInfo = null; - - xpath.map(function(n) { sig.addReference(n) }) - sig.computeSignature(xml) - var signed = sig.getSignedXml() + xpath.map(function (n) { + sig.addReference(n); + }); + + sig.computeSignature(xml); + var signed = sig.getSignedXml(); - //fs.writeFileSync("./test/validators/XmlCryptoUtilities/XmlCryptoUtilities/bin/Debug/signedExample.xml", signed) - var expectedContent = fs.readFileSync(expected).toString() - test.equal(signed, expectedContent, "signature xml different than expected") - test.done() + //fs.writeFileSync("./test/validators/XmlCryptoUtilities/XmlCryptoUtilities/bin/Debug/signedExample.xml", signed) + var expectedContent = fs.readFileSync(expected).toString(); + test.equal(signed, expectedContent, "signature xml different than expected"); + test.done(); /* var spawn = require('child_process').spawn var proc = spawn('./test/validators/XmlCryptoUtilities/XmlCryptoUtilities/bin/Debug/XmlCryptoUtilities.exe', ['verify']) @@ -215,6 +223,5 @@ function verifySignature(test, xml, expected, xpath) { test.equal(0, code, "signature validation failed") test.done() }); - */ - + */ } diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index ea9df1dd..493a5e89 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1,595 +1,652 @@ -var select = require('xpath').select - , dom = require('@xmldom/xmldom').DOMParser - , SignedXml = require('../lib/signed-xml.js').SignedXml - , FileKeyInfo = require('../lib/signed-xml.js').FileKeyInfo - , fs = require('fs') - , crypto = require('crypto') +var select = require("xpath").select, + dom = require("@xmldom/xmldom").DOMParser, + SignedXml = require("../lib/signed-xml.js").SignedXml, + FileKeyInfo = require("../lib/signed-xml.js").FileKeyInfo, + fs = require("fs"), + crypto = require("crypto"); module.exports = { - "signer adds increasing id attributes to elements": function (test) { - verifyAddsId(test, "wssecurity", "equal") - verifyAddsId(test, null, "different") + verifyAddsId(test, "wssecurity", "equal"); + verifyAddsId(test, null, "different"); test.done(); }, - "signer adds references with namespaces": function(test) { + "signer adds references with namespaces": function (test) { verifyReferenceNS(test); test.done(); }, "signer does not duplicate existing id attributes": function (test) { - verifyDoesNotDuplicateIdAttributes(test, null, "") - verifyDoesNotDuplicateIdAttributes(test, "wssecurity", "wsu:") + verifyDoesNotDuplicateIdAttributes(test, null, ""); + verifyDoesNotDuplicateIdAttributes(test, "wssecurity", "wsu:"); test.done(); }, - "signer adds custom attributes to the signature root node": function(test) { + "signer adds custom attributes to the signature root node": function (test) { verifyAddsAttrs(test); test.done(); }, - "signer appends signature to the root node by default": function(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml() + "signer appends signature to the root node by default": function (test) { + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.addReference("//*[local-name(.)='name']") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.addReference("//*[local-name(.)='name']"); sig.computeSignature(xml); - var doc = new dom().parseFromString(sig.getSignedXml()) + var doc = new dom().parseFromString(sig.getSignedXml()); - test.strictEqual(doc.documentElement.lastChild.localName, "Signature", "the signature must be appended to the root node by default"); + test.strictEqual( + doc.documentElement.lastChild.localName, + "Signature", + "the signature must be appended to the root node by default" + ); test.done(); }, - "signer appends signature to a reference node": function(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml() + "signer appends signature to a reference node": function (test) { + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.addReference("//*[local-name(.)='repository']") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.addReference("//*[local-name(.)='repository']"); sig.computeSignature(xml, { location: { - reference: '/root/name', - action: 'append' - } + reference: "/root/name", + action: "append", + }, }); - var doc = new dom().parseFromString(sig.getSignedXml()) - var referenceNode = select('/root/name', doc)[0] + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; - test.strictEqual(referenceNode.lastChild.localName, "Signature", "the signature should be appended to root/name"); + test.strictEqual( + referenceNode.lastChild.localName, + "Signature", + "the signature should be appended to root/name" + ); test.done(); }, - "signer prepends signature to a reference node": function(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml() + "signer prepends signature to a reference node": function (test) { + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.addReference("//*[local-name(.)='repository']") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.addReference("//*[local-name(.)='repository']"); sig.computeSignature(xml, { location: { - reference: '/root/name', - action: 'prepend' - } + reference: "/root/name", + action: "prepend", + }, }); - var doc = new dom().parseFromString(sig.getSignedXml()) - var referenceNode = select('/root/name', doc)[0] + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; - test.strictEqual(referenceNode.firstChild.localName, "Signature", "the signature should be prepended to root/name"); + test.strictEqual( + referenceNode.firstChild.localName, + "Signature", + "the signature should be prepended to root/name" + ); test.done(); }, - "signer inserts signature before a reference node": function(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml() + "signer inserts signature before a reference node": function (test) { + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.addReference("//*[local-name(.)='repository']") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.addReference("//*[local-name(.)='repository']"); sig.computeSignature(xml, { location: { - reference: '/root/name', - action: 'before' - } + reference: "/root/name", + action: "before", + }, }); - var doc = new dom().parseFromString(sig.getSignedXml()) - var referenceNode = select('/root/name', doc)[0] + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; - test.strictEqual(referenceNode.previousSibling.localName, "Signature", "the signature should be inserted before to root/name"); + test.strictEqual( + referenceNode.previousSibling.localName, + "Signature", + "the signature should be inserted before to root/name" + ); test.done(); }, - "signer inserts signature after a reference node": function(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml() + "signer inserts signature after a reference node": function (test) { + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.addReference("//*[local-name(.)='repository']") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.addReference("//*[local-name(.)='repository']"); sig.computeSignature(xml, { location: { - reference: '/root/name', - action: 'after' - } + reference: "/root/name", + action: "after", + }, }); - var doc = new dom().parseFromString(sig.getSignedXml()) - var referenceNode = select('/root/name', doc)[0] + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; - test.strictEqual(referenceNode.nextSibling.localName, "Signature", "the signature should be inserted after to root/name"); + test.strictEqual( + referenceNode.nextSibling.localName, + "Signature", + "the signature should be inserted after to root/name" + ); test.done(); }, - "signer creates signature with correct structure": function(test) { - + "signer creates signature with correct structure": function (test) { function DummyKeyInfo() { - this.getKeyInfo = function() { - return "dummy key info" - } + this.getKeyInfo = function () { + return "dummy key info"; + }; } function DummyDigest() { + this.getHash = function () { + return "dummy digest"; + }; - this.getHash = function() { - return "dummy digest" - } - - this.getAlgorithmName = function() { - return "dummy digest algorithm" - } + this.getAlgorithmName = function () { + return "dummy digest algorithm"; + }; } function DummySignatureAlgorithm() { + this.getSignature = function () { + return "dummy signature"; + }; - this.getSignature = function() { - return "dummy signature" - } - - this.getAlgorithmName = function() { - return "dummy algorithm" - } - + this.getAlgorithmName = function () { + return "dummy algorithm"; + }; } function DummyTransformation() { - this.process = function() { - return "< x/>" - } + this.process = function () { + return "< x/>"; + }; - this.getAlgorithmName = function() { - return "dummy transformation" - } + this.getAlgorithmName = function () { + return "dummy transformation"; + }; } function DummyCanonicalization() { - this.process = function() { - return "< x/>" - } + this.process = function () { + return "< x/>"; + }; - this.getAlgorithmName = function() { - return "dummy canonicalization" - } + this.getAlgorithmName = function () { + return "dummy canonicalization"; + }; } - var xml = "" - var sig = new SignedXml() - - - SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation - SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization - SignedXml.HashAlgorithms["http://dummyDigest"] = DummyDigest - SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm - - sig.signatureAlgorithm = "http://dummySignatureAlgorithm" - sig.keyInfoProvider = new DummyKeyInfo() - sig.canonicalizationAlgorithm = "http://DummyCanonicalization" - - sig.addReference("//*[local-name(.)='x']", ["http://DummyTransformation"], "http://dummyDigest") - sig.addReference("//*[local-name(.)='y']", ["http://DummyTransformation"], "http://dummyDigest") - sig.addReference("//*[local-name(.)='w']", ["http://DummyTransformation"], "http://dummyDigest") - - sig.computeSignature(xml) - var signature = sig.getSignatureXml() - var expected = ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - "dummy signature"+ - ""+ - "dummy key info"+ - ""+ - "" - - - test.equal(expected, signature, "wrong signature format") - - var signedXml = sig.getSignedXml() - var expectedSignedXml = "" + - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - "dummy signature"+ - ""+ - "dummy key info"+ - ""+ - "" + - "" - - test.equal(expectedSignedXml, signedXml, "wrong signedXml format") - - - - var originalXmlWithIds = sig.getOriginalXmlWithIds() - var expectedOriginalXmlWithIds = "" - test.equal(expectedOriginalXmlWithIds, originalXmlWithIds, "wrong OriginalXmlWithIds") + var xml = ''; + var sig = new SignedXml(); + + SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; + SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; + SignedXml.HashAlgorithms["http://dummyDigest"] = DummyDigest; + SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; + + sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; + sig.keyInfoProvider = new DummyKeyInfo(); + sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; + + sig.addReference( + "//*[local-name(.)='x']", + ["http://DummyTransformation"], + "http://dummyDigest" + ); + sig.addReference( + "//*[local-name(.)='y']", + ["http://DummyTransformation"], + "http://dummyDigest" + ); + sig.addReference( + "//*[local-name(.)='w']", + ["http://DummyTransformation"], + "http://dummyDigest" + ); + + sig.computeSignature(xml); + var signature = sig.getSignatureXml(); + var expected = + '' + + "" + + '' + + '' + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + "" + + "dummy signature" + + "" + + "dummy key info" + + "" + + ""; + + test.equal(expected, signature, "wrong signature format"); + + var signedXml = sig.getSignedXml(); + var expectedSignedXml = + '' + + '' + + "" + + '' + + '' + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + "" + + "dummy signature" + + "" + + "dummy key info" + + "" + + "" + + ""; + + test.equal(expectedSignedXml, signedXml, "wrong signedXml format"); + + var originalXmlWithIds = sig.getOriginalXmlWithIds(); + var expectedOriginalXmlWithIds = + ''; + test.equal(expectedOriginalXmlWithIds, originalXmlWithIds, "wrong OriginalXmlWithIds"); test.done(); }, - "signer creates signature with correct structure (with prefix)": function(test) { - var prefix = 'ds'; + "signer creates signature with correct structure (with prefix)": function (test) { + var prefix = "ds"; function DummyKeyInfo() { - this.getKeyInfo = function() { - return "dummy key info" - } + this.getKeyInfo = function () { + return "dummy key info"; + }; } function DummyDigest() { + this.getHash = function () { + return "dummy digest"; + }; - this.getHash = function() { - return "dummy digest" - } - - this.getAlgorithmName = function() { - return "dummy digest algorithm" - } + this.getAlgorithmName = function () { + return "dummy digest algorithm"; + }; } function DummySignatureAlgorithm() { + this.getSignature = function () { + return "dummy signature"; + }; - this.getSignature = function( ) { - return "dummy signature" - } - - this.getAlgorithmName = function() { - return "dummy algorithm" - } - + this.getAlgorithmName = function () { + return "dummy algorithm"; + }; } function DummyTransformation() { - this.process = function() { - return "< x/>" - } + this.process = function () { + return "< x/>"; + }; - this.getAlgorithmName = function() { - return "dummy transformation" - } + this.getAlgorithmName = function () { + return "dummy transformation"; + }; } function DummyCanonicalization() { - this.process = function() { - return "< x/>" - } + this.process = function () { + return "< x/>"; + }; - this.getAlgorithmName = function() { - return "dummy canonicalization" - } + this.getAlgorithmName = function () { + return "dummy canonicalization"; + }; } - var xml = "" - var sig = new SignedXml() - + var xml = ''; + var sig = new SignedXml(); - SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation - SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization - SignedXml.HashAlgorithms["http://dummyDigest"] = DummyDigest - SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm + SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; + SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; + SignedXml.HashAlgorithms["http://dummyDigest"] = DummyDigest; + SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; - sig.signatureAlgorithm = "http://dummySignatureAlgorithm" - sig.keyInfoProvider = new DummyKeyInfo() - sig.canonicalizationAlgorithm = "http://DummyCanonicalization" + sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; + sig.keyInfoProvider = new DummyKeyInfo(); + sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; - sig.addReference("//*[local-name(.)='x']", ["http://DummyTransformation"], "http://dummyDigest") - sig.addReference("//*[local-name(.)='y']", ["http://DummyTransformation"], "http://dummyDigest") - sig.addReference("//*[local-name(.)='w']", ["http://DummyTransformation"], "http://dummyDigest") + sig.addReference( + "//*[local-name(.)='x']", + ["http://DummyTransformation"], + "http://dummyDigest" + ); + sig.addReference( + "//*[local-name(.)='y']", + ["http://DummyTransformation"], + "http://dummyDigest" + ); + sig.addReference( + "//*[local-name(.)='w']", + ["http://DummyTransformation"], + "http://dummyDigest" + ); sig.computeSignature(xml, { prefix: prefix }); - var signature = sig.getSignatureXml() - - var expected = ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - "dummy signature"+ - ""+ - "dummy key info"+ - ""+ - "" - - test.equal(expected, signature, "wrong signature format") - - var signedXml = sig.getSignedXml() - var expectedSignedXml = "" + - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "dummy digest"+ - ""+ - ""+ - "dummy signature"+ - ""+ - "dummy key info"+ - ""+ - "" + - "" - - test.equal(expectedSignedXml, signedXml, "wrong signedXml format") - - - - var originalXmlWithIds = sig.getOriginalXmlWithIds() - var expectedOriginalXmlWithIds = "" - test.equal(expectedOriginalXmlWithIds, originalXmlWithIds, "wrong OriginalXmlWithIds") + var signature = sig.getSignatureXml(); + + var expected = + '' + + "" + + '' + + '' + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + "" + + "dummy signature" + + "" + + "dummy key info" + + "" + + ""; + + test.equal(expected, signature, "wrong signature format"); + + var signedXml = sig.getSignedXml(); + var expectedSignedXml = + '' + + '' + + "" + + '' + + '' + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + '' + + "" + + '' + + "" + + '' + + "dummy digest" + + "" + + "" + + "dummy signature" + + "" + + "dummy key info" + + "" + + "" + + ""; + + test.equal(expectedSignedXml, signedXml, "wrong signedXml format"); + + var originalXmlWithIds = sig.getOriginalXmlWithIds(); + var expectedOriginalXmlWithIds = + ''; + test.equal(expectedOriginalXmlWithIds, originalXmlWithIds, "wrong OriginalXmlWithIds"); test.done(); }, - "signer creates correct signature values": function(test) { - - var xml = "" - var sig = new SignedXml() - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.keyInfoProvider = null - - sig.addReference("//*[local-name(.)='x']") - sig.addReference("//*[local-name(.)='y']") - sig.addReference("//*[local-name(.)='w']") - - sig.computeSignature(xml) - var signedXml = sig.getSignedXml() - var expected = "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=" + - "" + - "" + - "" + - "" + - "" + - "" + - "4Pq/sBri+AyOtxtSFsPSOyylyzk=" + - "" + - "" + - "" + - "" + - "" + - "" + - "6I7SDu1iV2YOajTlf+iMLIBfLnE=" + - "" + - "" + - "NejzGB9MDUddKCt3GL2vJhEd5q6NBuhLdQc3W4bJI5q34hk7Hk6zBRoW3OliX+/f7Hpi9y0INYoqMSUfrsAVm3IuPzUETKlI6xiNZo07ULRj1DwxRo6cU66ar1EKUQLRuCZas795FjB8jvUI2lyhcax/00uMJ+Cjf4bwAQ+9gOQ=" + - "" + - "" - - test.equal(expected, signedXml, "wrong signature format") + "signer creates correct signature values": function (test) { + var xml = + ''; + var sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; + + sig.addReference("//*[local-name(.)='x']"); + sig.addReference("//*[local-name(.)='y']"); + sig.addReference("//*[local-name(.)='w']"); + + sig.computeSignature(xml); + var signedXml = sig.getSignedXml(); + var expected = + '' + + '' + + "" + + '' + + '' + + '' + + "" + + '' + + '' + + "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=" + + "" + + '' + + "" + + '' + + "" + + '' + + "4Pq/sBri+AyOtxtSFsPSOyylyzk=" + + "" + + '' + + "" + + '' + + "" + + '' + + "6I7SDu1iV2YOajTlf+iMLIBfLnE=" + + "" + + "" + + "NejzGB9MDUddKCt3GL2vJhEd5q6NBuhLdQc3W4bJI5q34hk7Hk6zBRoW3OliX+/f7Hpi9y0INYoqMSUfrsAVm3IuPzUETKlI6xiNZo07ULRj1DwxRo6cU66ar1EKUQLRuCZas795FjB8jvUI2lyhcax/00uMJ+Cjf4bwAQ+9gOQ=" + + "" + + ""; + + test.equal(expected, signedXml, "wrong signature format"); test.done(); }, "signer creates correct signature values using async callback": function (test) { - function DummySignatureAlgorithm() { this.getSignature = function (signedInfo, signingKey, callback) { - var signer = crypto.createSign("RSA-SHA1") - signer.update(signedInfo) - var res = signer.sign(signingKey, 'base64') + var signer = crypto.createSign("RSA-SHA1"); + signer.update(signedInfo); + var res = signer.sign(signingKey, "base64"); //Do some asynchronous things here - callback(null, res) - } + callback(null, res); + }; this.getAlgorithmName = function () { - return "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - } + return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + }; } - var xml = "" - SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm - var sig = new SignedXml() - sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync" - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.keyInfoProvider = null - - sig.addReference("//*[local-name(.)='x']") - sig.addReference("//*[local-name(.)='y']") - sig.addReference("//*[local-name(.)='w']") - - sig.computeSignature(xml, function() { - var signedXml = sig.getSignedXml() - var expected = "" + - "" + + var xml = + ''; + SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm; + var sig = new SignedXml(); + sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync"; + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; + + sig.addReference("//*[local-name(.)='x']"); + sig.addReference("//*[local-name(.)='y']"); + sig.addReference("//*[local-name(.)='w']"); + + sig.computeSignature(xml, function () { + var signedXml = sig.getSignedXml(); + var expected = + '' + + '' + "" + - "" + - "" + - "" + + '' + + '' + + '' + "" + - "" + - "" + + '' + + '' + "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=" + "" + - "" + + '' + "" + - "" + + '' + "" + - "" + + '' + "4Pq/sBri+AyOtxtSFsPSOyylyzk=" + "" + - "" + + '' + "" + - "" + + '' + "" + - "" + + '' + "6I7SDu1iV2YOajTlf+iMLIBfLnE=" + "" + "" + "NejzGB9MDUddKCt3GL2vJhEd5q6NBuhLdQc3W4bJI5q34hk7Hk6zBRoW3OliX+/f7Hpi9y0INYoqMSUfrsAVm3IuPzUETKlI6xiNZo07ULRj1DwxRo6cU66ar1EKUQLRuCZas795FjB8jvUI2lyhcax/00uMJ+Cjf4bwAQ+9gOQ=" + "" + - "" + ""; - test.equal(expected, signedXml, "wrong signature format") + test.equal(expected, signedXml, "wrong signature format"); test.done(); - }) + }); }, - "correctly loads signature": function(test) { - passLoadSignature(test, "./test/static/valid_signature.xml") - passLoadSignature(test, "./test/static/valid_signature.xml", true) - passLoadSignature(test, "./test/static/valid_signature_with_root_level_sig_namespace.xml") - test.done() + "correctly loads signature": function (test) { + passLoadSignature(test, "./test/static/valid_signature.xml"); + passLoadSignature(test, "./test/static/valid_signature.xml", true); + passLoadSignature(test, "./test/static/valid_signature_with_root_level_sig_namespace.xml"); + test.done(); }, - "verify valid signature": function(test) { - passValidSignature(test, "./test/static/valid_signature.xml") - passValidSignature(test, "./test/static/valid_signature_with_lowercase_id_attribute.xml") - passValidSignature(test, "./test/static/valid_signature wsu.xml", "wssecurity") - passValidSignature(test, "./test/static/valid_signature_with_reference_keyInfo.xml") - passValidSignature(test, "./test/static/valid_signature_with_whitespace_in_digestvalue.xml") - passValidSignature(test, "./test/static/valid_signature_utf8.xml") - passValidSignature(test, "./test/static/valid_signature_with_unused_prefixes.xml") - test.done() + "verify valid signature": function (test) { + passValidSignature(test, "./test/static/valid_signature.xml"); + passValidSignature(test, "./test/static/valid_signature_with_lowercase_id_attribute.xml"); + passValidSignature(test, "./test/static/valid_signature wsu.xml", "wssecurity"); + passValidSignature(test, "./test/static/valid_signature_with_reference_keyInfo.xml"); + passValidSignature(test, "./test/static/valid_signature_with_whitespace_in_digestvalue.xml"); + passValidSignature(test, "./test/static/valid_signature_utf8.xml"); + passValidSignature(test, "./test/static/valid_signature_with_unused_prefixes.xml"); + test.done(); }, - "fail invalid signature": function(test) { - failInvalidSignature(test, "./test/static/invalid_signature - signature value.xml") - failInvalidSignature(test, "./test/static/invalid_signature - hash.xml") - failInvalidSignature(test, "./test/static/invalid_signature - non existing reference.xml") - failInvalidSignature(test, "./test/static/invalid_signature - changed content.xml") - failInvalidSignature(test, "./test/static/invalid_signature - wsu - invalid signature value.xml", "wssecurity") - failInvalidSignature(test, "./test/static/invalid_signature - wsu - hash.xml", "wssecurity") - failInvalidSignature(test, "./test/static/invalid_signature - wsu - non existing reference.xml", "wssecurity") - failInvalidSignature(test, "./test/static/invalid_signature - wsu - changed content.xml", "wssecurity") - - test.done() + "fail invalid signature": function (test) { + failInvalidSignature(test, "./test/static/invalid_signature - signature value.xml"); + failInvalidSignature(test, "./test/static/invalid_signature - hash.xml"); + failInvalidSignature(test, "./test/static/invalid_signature - non existing reference.xml"); + failInvalidSignature(test, "./test/static/invalid_signature - changed content.xml"); + failInvalidSignature( + test, + "./test/static/invalid_signature - wsu - invalid signature value.xml", + "wssecurity" + ); + failInvalidSignature(test, "./test/static/invalid_signature - wsu - hash.xml", "wssecurity"); + failInvalidSignature( + test, + "./test/static/invalid_signature - wsu - non existing reference.xml", + "wssecurity" + ); + failInvalidSignature( + test, + "./test/static/invalid_signature - wsu - changed content.xml", + "wssecurity" + ); + + test.done(); }, - "allow empty reference uri when signing": function(test) { - var xml = "" - var sig = new SignedXml() - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.keyInfoProvider = null + "allow empty reference uri when signing": function (test) { + var xml = ""; + var sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; - sig.addReference("//*[local-name(.)='root']", ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], "http://www.w3.org/2000/09/xmldsig#sha1", "", "", "", true) + sig.addReference( + "//*[local-name(.)='root']", + ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], + "http://www.w3.org/2000/09/xmldsig#sha1", + "", + "", + "", + true + ); - sig.computeSignature(xml) - var signedXml = sig.getSignedXml() - var doc = new dom().parseFromString(signedXml) - var URI = select("//*[local-name(.)='Reference']/@URI", doc)[0] - test.equal(URI.value, "", "uri should be empty but instead was " + URI.value) - test.done() + sig.computeSignature(xml); + var signedXml = sig.getSignedXml(); + var doc = new dom().parseFromString(signedXml); + var URI = select("//*[local-name(.)='Reference']/@URI", doc)[0]; + test.equal(URI.value, "", "uri should be empty but instead was " + URI.value); + test.done(); }, - "signer appends signature to a non-existing reference node": function(test) { + "signer appends signature to a non-existing reference node": function (test) { var xml = "xml-cryptogithub"; var sig = new SignedXml(); @@ -597,142 +654,197 @@ module.exports = { sig.addReference("//*[local-name(.)='repository']"); try { - sig.computeSignature(xml, { - location: { - reference: '/root/foobar', - action: 'append' - } - }); - test.ok(false); - } - catch (err) { - test.ok(!(err instanceof TypeError)); + sig.computeSignature(xml, { + location: { + reference: "/root/foobar", + action: "append", + }, + }); + test.ok(false); + } catch (err) { + test.ok(!(err instanceof TypeError)); } test.done(); }, - "signer adds existing prefixes": function(test) { + "signer adds existing prefixes": function (test) { function AssertionKeyInfo(assertionId) { - this.getKeyInfo = function() { - return ' ' + - ''+assertionId+'' + - ''; + this.getKeyInfo = function () { + return ( + ' ' + + '' + + assertionId + + "" + + "" + ); }; } var xml = ' ' + - ' ' + - ' ' + - ' '+ - ' '+ - ' '+ - '' + " " + + " ' + + " " + + " " + + " " + + ""; var sig = new SignedXml(); - sig.keyInfoProvider = new AssertionKeyInfo( - "_81d5fba5c807be9e9cf60c58566349b1" - ); + sig.keyInfoProvider = new AssertionKeyInfo("_81d5fba5c807be9e9cf60c58566349b1"); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.computeSignature(xml, { prefix: "ds", location: { reference: "//Assertion", - action: "after" + action: "after", }, existingPrefixes: { wsse: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", - wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" - } + wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", + }, }); var result = sig.getSignedXml(); - test.equal((result.match(/xmlns:wsu=/g) || []).length, 1) - test.equal((result.match(/xmlns:wsse=/g) || []).length, 1) - test.done(); - }, - - "creates InclusiveNamespaces element when inclusiveNamespacesPrefixList is set on Reference": function (test) { - var xml = ""; - var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; - - sig.addReference("//*[local-name(.)='root']", ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], "http://www.w3.org/2000/09/xmldsig#sha1", "", "", "prefix1 prefix2"); - - sig.computeSignature(xml); - var signedXml = sig.getSignedXml() - - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select("//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement); - test.equal(inclusiveNamespaces.length, 1, "InclusiveNamespaces element should exist"); - - var prefixListAttribute = inclusiveNamespaces[0].getAttribute('PrefixList'); - test.equal(prefixListAttribute, 'prefix1 prefix2', "InclusiveNamespaces element should have the correct PrefixList attribute value"); - + test.equal((result.match(/xmlns:wsu=/g) || []).length, 1); + test.equal((result.match(/xmlns:wsse=/g) || []).length, 1); test.done(); }, - "does not create InclusiveNamespaces element when inclusiveNamespacesPrefixList is not set on Reference": function (test) { - var xml = ""; - var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; - - sig.addReference("//*[local-name(.)='root']", ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], "http://www.w3.org/2000/09/xmldsig#sha1", "", "", ""); - - sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); - - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select("//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement); - test.equal(inclusiveNamespaces.length, 0, "InclusiveNamespaces element should not exist"); - - test.done(); - }, + "creates InclusiveNamespaces element when inclusiveNamespacesPrefixList is set on Reference": + function (test) { + var xml = ""; + var sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; + + sig.addReference( + "//*[local-name(.)='root']", + ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], + "http://www.w3.org/2000/09/xmldsig#sha1", + "", + "", + "prefix1 prefix2" + ); + + sig.computeSignature(xml); + var signedXml = sig.getSignedXml(); + + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( + "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", + doc.documentElement + ); + test.equal(inclusiveNamespaces.length, 1, "InclusiveNamespaces element should exist"); + + var prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); + test.equal( + prefixListAttribute, + "prefix1 prefix2", + "InclusiveNamespaces element should have the correct PrefixList attribute value" + ); - "creates InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is set on SignedXml options": function (test) { - var xml = ""; - var sig = new SignedXml(null, {inclusiveNamespacesPrefixList: "prefix1 prefix2"}); - sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; - - sig.addReference("//*[local-name(.)='root']", ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], "http://www.w3.org/2000/09/xmldsig#sha1"); - - sig.computeSignature(xml); - var signedXml = sig.getSignedXml() - - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select("//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement); - - test.equal(inclusiveNamespaces.length, 1, "InclusiveNamespaces element should exist inside CanonicalizationMethod"); - - var prefixListAttribute = inclusiveNamespaces[0].getAttribute('PrefixList'); - test.equal(prefixListAttribute, 'prefix1 prefix2', "InclusiveNamespaces element inside CanonicalizationMethod should have the correct PrefixList attribute value"); - - test.done(); - }, - - "does not create InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is not set on SignedXml options": function (test) { - var xml = ""; - var sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property - sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; - - sig.addReference("//*[local-name(.)='root']", ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], "http://www.w3.org/2000/09/xmldsig#sha1"); - - sig.computeSignature(xml); - var signedXml = sig.getSignedXml() + test.done(); + }, + + "does not create InclusiveNamespaces element when inclusiveNamespacesPrefixList is not set on Reference": + function (test) { + var xml = ""; + var sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; + + sig.addReference( + "//*[local-name(.)='root']", + ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], + "http://www.w3.org/2000/09/xmldsig#sha1", + "", + "", + "" + ); + + sig.computeSignature(xml); + var signedXml = sig.getSignedXml(); + + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( + "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", + doc.documentElement + ); + test.equal(inclusiveNamespaces.length, 0, "InclusiveNamespaces element should not exist"); - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select("//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement); + test.done(); + }, + + "creates InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is set on SignedXml options": + function (test) { + var xml = ""; + var sig = new SignedXml(null, { inclusiveNamespacesPrefixList: "prefix1 prefix2" }); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; + + sig.addReference( + "//*[local-name(.)='root']", + ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], + "http://www.w3.org/2000/09/xmldsig#sha1" + ); + + sig.computeSignature(xml); + var signedXml = sig.getSignedXml(); + + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( + "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", + doc.documentElement + ); + + test.equal( + inclusiveNamespaces.length, + 1, + "InclusiveNamespaces element should exist inside CanonicalizationMethod" + ); + + var prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); + test.equal( + prefixListAttribute, + "prefix1 prefix2", + "InclusiveNamespaces element inside CanonicalizationMethod should have the correct PrefixList attribute value" + ); - test.equal(inclusiveNamespaces.length, 0, "InclusiveNamespaces element should not exist inside CanonicalizationMethod"); + test.done(); + }, + + "does not create InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is not set on SignedXml options": + function (test) { + var xml = ""; + var sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.keyInfoProvider = null; + + sig.addReference( + "//*[local-name(.)='root']", + ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], + "http://www.w3.org/2000/09/xmldsig#sha1" + ); + + sig.computeSignature(xml); + var signedXml = sig.getSignedXml(); + + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( + "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", + doc.documentElement + ); + + test.equal( + inclusiveNamespaces.length, + 0, + "InclusiveNamespaces element should not exist inside CanonicalizationMethod" + ); - test.done(); - }, + test.done(); + }, "adds attributes to KeyInfo element when attrs are present in keyInfoProvider": function (test) { var xml = ""; @@ -741,11 +853,11 @@ module.exports = { sig.keyInfoProvider = { attrs: { CustomUri: "http://www.example.com/keyinfo", - CustomAttribute: "custom-value" + CustomAttribute: "custom-value", + }, + getKeyInfo: function () { + return ""; }, - getKeyInfo: function () { - return ""; - } }; sig.computeSignature(xml); @@ -755,166 +867,215 @@ module.exports = { var keyInfoElement = select("//*[local-name(.)='KeyInfo']", doc.documentElement); test.equal(keyInfoElement.length, 1, "KeyInfo element should exist"); - var algorithmAttribute = keyInfoElement[0].getAttribute('CustomUri'); - test.equal(algorithmAttribute, 'http://www.example.com/keyinfo', "KeyInfo element should have the correct CustomUri attribute value"); + var algorithmAttribute = keyInfoElement[0].getAttribute("CustomUri"); + test.equal( + algorithmAttribute, + "http://www.example.com/keyinfo", + "KeyInfo element should have the correct CustomUri attribute value" + ); - var customAttribute = keyInfoElement[0].getAttribute('CustomAttribute'); - test.equal(customAttribute, 'custom-value', "KeyInfo element should have the correct CustomAttribute attribute value"); + var customAttribute = keyInfoElement[0].getAttribute("CustomAttribute"); + test.equal( + customAttribute, + "custom-value", + "KeyInfo element should have the correct CustomAttribute attribute value" + ); test.done(); }, - -} +}; function passValidSignature(test, file, mode) { - var xml = fs.readFileSync(file).toString() - var res = verifySignature(xml, mode) - test.equal(true, res, "expected signature to be valid, but it was reported invalid") + var xml = fs.readFileSync(file).toString(); + var res = verifySignature(xml, mode); + test.equal(true, res, "expected signature to be valid, but it was reported invalid"); } function passLoadSignature(test, file, toString) { - var xml = fs.readFileSync(file).toString() - var doc = new dom().parseFromString(xml) - var node = select("/*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0] - var sig = new SignedXml() - sig.loadSignature(toString ? node.toString() : node) - - test.equal("http://www.w3.org/2001/10/xml-exc-c14n#", + var xml = fs.readFileSync(file).toString(); + var doc = new dom().parseFromString(xml); + var node = select( + "/*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; + var sig = new SignedXml(); + sig.loadSignature(toString ? node.toString() : node); + + test.equal( + "http://www.w3.org/2001/10/xml-exc-c14n#", sig.canonicalizationAlgorithm, - "wrong canonicalization method") + "wrong canonicalization method" + ); - test.equal("http://www.w3.org/2000/09/xmldsig#rsa-sha1", + test.equal( + "http://www.w3.org/2000/09/xmldsig#rsa-sha1", sig.signatureAlgorithm, - "wrong signature method") + "wrong signature method" + ); - test.equal("PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI=", + test.equal( + "PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI=", sig.signatureValue, - "wrong signature value") - - var keyInfo = select("//*[local-name(.)='KeyInfo']/*[local-name(.)='dummyKey']", sig.keyInfo[0])[0]; - test.equal(keyInfo.firstChild.data, "1234", "keyInfo clause not correctly loaded") - - test.equal(3, sig.references.length) - - var digests = ["b5GCZ2xpP5T7tbLWBTkOl4CYupQ=", "K4dI497ZCxzweDIrbndUSmtoezY=", "sH1gxKve8wlU8LlFVa2l6w3HMJ0="] - - - for (var i=0; i" - var sig = new SignedXml(mode) - sig.signingKey = fs.readFileSync("./test/static/client.pem") - sig.addReference("//*[local-name(.)='x']") - sig.computeSignature(xml) - var signedXml = sig.getOriginalXmlWithIds() - var doc = new dom().parseFromString(signedXml) - var attrs = select("//@*", doc) - test.equals(2, attrs.length, "wrong number of attributes") - + var xml = + ""; + var sig = new SignedXml(mode); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.addReference("//*[local-name(.)='x']"); + sig.computeSignature(xml); + var signedXml = sig.getOriginalXmlWithIds(); + var doc = new dom().parseFromString(signedXml); + var attrs = select("//@*", doc); + test.equals(2, attrs.length, "wrong number of attributes"); } function verifyAddsId(test, mode, nsMode) { - var xml = "" - var sig = new SignedXml(mode) - sig.signingKey = fs.readFileSync("./test/static/client.pem") + var xml = ''; + var sig = new SignedXml(mode); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.addReference("//*[local-name(.)='x']") - sig.addReference("//*[local-name(.)='y']") - sig.addReference("//*[local-name(.)='w']") + sig.addReference("//*[local-name(.)='x']"); + sig.addReference("//*[local-name(.)='y']"); + sig.addReference("//*[local-name(.)='w']"); - sig.computeSignature(xml) - var signedXml = sig.getOriginalXmlWithIds() - var doc = new dom().parseFromString(signedXml) + sig.computeSignature(xml); + var signedXml = sig.getOriginalXmlWithIds(); + var doc = new dom().parseFromString(signedXml); - var op = nsMode == "equal" ? "=" : "!=" + var op = nsMode == "equal" ? "=" : "!="; - var xpath = "//*[local-name(.)='{elem}' and '_{id}' = @*[local-name(.)='Id' and namespace-uri(.)" + op + "'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd']]" + var xpath = + "//*[local-name(.)='{elem}' and '_{id}' = @*[local-name(.)='Id' and namespace-uri(.)" + + op + + "'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd']]"; //verify each of the signed nodes now has an "Id" attribute with the right value - nodeExists(test, doc, xpath.replace("{id}", "0").replace("{elem}", "x")) - nodeExists(test, doc, xpath.replace("{id}", "1").replace("{elem}", "y")) - nodeExists(test, doc, xpath.replace("{id}", "2").replace("{elem}", "w")) - + nodeExists(test, doc, xpath.replace("{id}", "0").replace("{elem}", "x")); + nodeExists(test, doc, xpath.replace("{id}", "1").replace("{elem}", "y")); + nodeExists(test, doc, xpath.replace("{id}", "2").replace("{elem}", "w")); } function verifyAddsAttrs(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml() + var xml = 'xml-cryptogithub'; + var sig = new SignedXml(); var attrs = { - Id: 'signatureTest', - data: 'dataValue', - xmlns: 'http://custom-xmlns#' - } + Id: "signatureTest", + data: "dataValue", + xmlns: "http://custom-xmlns#", + }; - sig.signingKey = fs.readFileSync("./test/static/client.pem") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.addReference("//*[local-name(.)='name']") + sig.addReference("//*[local-name(.)='name']"); sig.computeSignature(xml, { - attrs: attrs - }) - - var signedXml = sig.getSignatureXml() - var doc = new dom().parseFromString(signedXml) - var signatureNode = doc.documentElement - - test.strictEqual(signatureNode.getAttribute("Id"), attrs.Id, "Id attribute is not equal to the expected value: \"" + attrs.Id + "\"") - test.strictEqual(signatureNode.getAttribute("data"), attrs.data, "data attribute is not equal to the expected value: \"" + attrs.data + "\"") - test.notStrictEqual(signatureNode.getAttribute("xmlns"), attrs.xmlns, "xmlns attribute can not be overridden") - test.strictEqual(signatureNode.getAttribute("xmlns"), "http://www.w3.org/2000/09/xmldsig#", "xmlns attribute is not equal to the expected value: \"http://www.w3.org/2000/09/xmldsig#\"") + attrs: attrs, + }); + + var signedXml = sig.getSignatureXml(); + var doc = new dom().parseFromString(signedXml); + var signatureNode = doc.documentElement; + + test.strictEqual( + signatureNode.getAttribute("Id"), + attrs.Id, + 'Id attribute is not equal to the expected value: "' + attrs.Id + '"' + ); + test.strictEqual( + signatureNode.getAttribute("data"), + attrs.data, + 'data attribute is not equal to the expected value: "' + attrs.data + '"' + ); + test.notStrictEqual( + signatureNode.getAttribute("xmlns"), + attrs.xmlns, + "xmlns attribute can not be overridden" + ); + test.strictEqual( + signatureNode.getAttribute("xmlns"), + "http://www.w3.org/2000/09/xmldsig#", + 'xmlns attribute is not equal to the expected value: "http://www.w3.org/2000/09/xmldsig#"' + ); } function verifyReferenceNS(test) { - var xml = "xml-cryptogithub" - var sig = new SignedXml("wssecurity") + var xml = + 'xml-cryptogithub'; + var sig = new SignedXml("wssecurity"); - sig.signingKey = fs.readFileSync("./test/static/client.pem") + sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.addReference("//*[@wsu:Id]") + sig.addReference("//*[@wsu:Id]"); sig.computeSignature(xml, { existingPrefixes: { - wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" - } - }) - - var signedXml = sig.getSignatureXml() - var doc = new dom().parseFromString(signedXml) - var references = select("//*[local-name(.)='Reference']", doc) - test.equal(references.length, 2) + wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", + }, + }); + + var signedXml = sig.getSignatureXml(); + var doc = new dom().parseFromString(signedXml); + var references = select("//*[local-name(.)='Reference']", doc); + test.equal(references.length, 2); } function nodeExists(test, doc, xpath) { - if (!doc && !xpath) return - var node = select(xpath, doc) - test.ok(node.length==1, "xpath " + xpath + " not found") + if (!doc && !xpath) return; + var node = select(xpath, doc); + test.ok(node.length == 1, "xpath " + xpath + " not found"); } diff --git a/test/wsfed-metadata-test.js b/test/wsfed-metadata-test.js index 54dd2360..de10a252 100644 --- a/test/wsfed-metadata-test.js +++ b/test/wsfed-metadata-test.js @@ -1,12 +1,15 @@ -var crypto = require('../index'); -var xpath = require('xpath'); -var xmldom = require('@xmldom/xmldom'); -var fs = require('fs'); +var crypto = require("../index"); +var xpath = require("xpath"); +var xmldom = require("@xmldom/xmldom"); +var fs = require("fs"); -exports['test validating WS-Fed Metadata'] = function (test) { - var xml = fs.readFileSync('./test/static/wsfederation_metadata.xml', 'utf-8'); +exports["test validating WS-Fed Metadata"] = function (test) { + var xml = fs.readFileSync("./test/static/wsfederation_metadata.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); - var signature = xpath.select("/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]; + var signature = xpath.select( + "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", + doc + )[0]; var sig = new crypto.SignedXml(); sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/wsfederation_metadata.pem"); sig.loadSignature(signature); diff --git a/test/xml-assert.js b/test/xml-assert.js index 823744a8..ccaafb22 100644 --- a/test/xml-assert.js +++ b/test/xml-assert.js @@ -1,9 +1,9 @@ -var select = require('xpath').select +var select = require("xpath").select; -function nodeExists(test, doc, xpath) { - if (!doc && !xpath) return - var node = select(xpath, doc) - test.ok(node.length==1, "xpath " + xpath + " not found") +function nodeExists(test, doc, xpath) { + if (!doc && !xpath) return; + var node = select(xpath, doc); + test.ok(node.length == 1, "xpath " + xpath + " not found"); } -exports.nodeExists = nodeExists +exports.nodeExists = nodeExists;