Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xml enc c14# inclusivenamespace fixes #179

Merged
31 changes: 31 additions & 0 deletions lib/exclusive-canonicalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,37 @@ ExclusiveCanonicalization.prototype.process = function(node, options) {
var defaultNsForPrefix = options.defaultNsForPrefix || {};
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")
if (CanonicalizationMethod.length != 0) {
var inclusiveNamespaces = utils.findChilds(CanonicalizationMethod[0], "InclusiveNamespaces")
if (inclusiveNamespaces.length != 0) {
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(' ');
prefixList.forEach(function (prefix) {
if (ancestorNamespaces) {
ancestorNamespaces.forEach(function (ancestorNamespace) {
if (prefix == ancestorNamespace.prefix) {
node.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' + prefix, ancestorNamespace.namespaceURI);
}
})
}
})
}

var res = this.processInner(node, [], defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList);
return res;
};
Expand Down
80 changes: 24 additions & 56 deletions lib/signed-xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ function FileKeyInfo(file) {
this.file = file

this.getKeyInfo = function(key, prefix) {
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
prefix = prefix || ''
prefix = prefix ? prefix + ':' : prefix
return "<" + prefix + "X509Data></" + prefix + "X509Data>"
}

Expand Down Expand Up @@ -361,21 +361,20 @@ SignedXml.prototype.validateSignatureValue = function(doc) {
var signedInfo = utils.findChilds(this.signatureNode, "SignedInfo")
if (signedInfo.length==0) throw new Error("could not find SignedInfo element in the message")

/**
* When canonicalization algorithm is non-exclusive, search for ancestor namespaces
* before validating signature.
*/
var ancestorNamespaces = [];
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");
}

ancestorNamespaces = findAncestorNs(doc, "//*[local-name()='SignedInfo']");
}

/**
* Search for ancestor namespaces before validating signature.
*/
var ancestorNamespaces = [];
ancestorNamespaces = findAncestorNs(doc, "//*[local-name()='SignedInfo']");

var c14nOptions = {
ancestorNamespaces: ancestorNamespaces
};
Expand Down Expand Up @@ -449,59 +448,21 @@ SignedXml.prototype.validateReferences = function(doc) {
}

/**
* When canonicalization algorithm is non-exclusive, search for ancestor namespaces
* before validating references.
* Search for ancestor namespaces before validating references.
*/
if(Array.isArray(ref.transforms)){
var hasNonExcC14nTransform = false;
for(var t in ref.transforms){
if(!ref.transforms.hasOwnProperty(t)) continue;

if(ref.transforms[t] === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
|| ref.transforms[t] === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments")
{
hasNonExcC14nTransform = true;
break;
}
}

if(hasNonExcC14nTransform){
ref.ancestorNamespaces = findAncestorNs(doc, elemXpath);
}
if(Array.isArray(ref.transforms)){
ref.ancestorNamespaces = findAncestorNs(doc, elemXpath);
}

var c14nOptions = {
inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList,
ancestorNamespaces: ref.ancestorNamespaces
};

var canonXml = this.getCanonXml(ref.transforms, elem[0], c14nOptions);

var hash = this.findHashAlgorithm(ref.digestAlgorithm)
var digest = hash.getHash(canonXml)

if (!validateDigestValue(digest, ref.digestValue)) {
if (ref.inclusiveNamespacesPrefixList) {
// fallback: apply InclusiveNamespaces workaround (https://github.com/yaronn/xml-crypto/issues/72)
var prefixList = ref.inclusiveNamespacesPrefixList instanceof Array ? ref.inclusiveNamespacesPrefixList : ref.inclusiveNamespacesPrefixList.split(' ');
var supported_definitions = {
'xs': 'http://www.w3.org/2001/XMLSchema',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'saml': 'urn:oasis:names:tc:SAML:2.0:assertion'
}

prefixList.forEach(function (prefix) {
if (supported_definitions[prefix]) {
elem[0].setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' + prefix, supported_definitions[prefix]);
}
});

canonXml = this.getCanonXml(ref.transforms, elem[0], { inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList });
digest = hash.getHash(canonXml);
if (digest === ref.digestValue) {
return true;
}
}
}

if (!validateDigestValue(digest, ref.digestValue)) {
this.validationErrors.push("invalid signature: for uri " + ref.uri +
Expand Down Expand Up @@ -612,9 +573,16 @@ SignedXml.prototype.loadReference = function(ref) {
transforms.push(utils.findAttr(trans, "Algorithm").value)
}

var inclusiveNamespaces = xpath.select("//*[local-name(.)='InclusiveNamespaces']", transformsNode);
var inclusiveNamespaces = utils.findChilds(trans, "InclusiveNamespaces")
if (inclusiveNamespaces.length > 0) {
inclusiveNamespacesPrefixList = inclusiveNamespaces[0].getAttribute('PrefixList');
//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<inclusiveNamespaces.length; i++) {
if (inclusiveNamespacesPrefixList) {
inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList + " " + inclusiveNamespaces[i].getAttribute('PrefixList');
} else {
inclusiveNamespacesPrefixList = inclusiveNamespaces[i].getAttribute('PrefixList');
}
}
}
}

Expand Down Expand Up @@ -916,10 +884,10 @@ SignedXml.prototype.createSignature = function(signedInfo, prefix) {
var xmlNsAttr = 'xmlns'

if (prefix) {
xmlNsAttr += ':' + prefix;
prefix += ':';
xmlNsAttr += ':' + prefix;
prefix += ':';
} else {
prefix = '';
prefix = '';
}

//the canonicalization requires to get a valid xml node.
Expand Down
13 changes: 7 additions & 6 deletions test/signature-unit-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -474,19 +474,20 @@ module.exports = {
},

"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");
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_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()
},

Expand Down Expand Up @@ -639,8 +640,8 @@ function failInvalidSignature(test, file, mode) {
function verifySignature(xml, mode) {

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 node = select("//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0]
var sig = new SignedXml(mode)
sig.keyInfoProvider = new FileKeyInfo("./test/static/client_public.pem")
sig.loadSignature(node)
Expand Down
1 change: 1 addition & 0 deletions test/static/valid_signature_with_unused_prefixes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<soapenv:Envelope xmlns:eg="http://www.example.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><ds:Signature Id="SIG-14" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="wsa eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#id-9"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>GAEFzMhRbXD8lZeTd5GwSqkB73Q=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-10"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>c2bqLRa0Q+/P7+vuXZHO8/iiac8=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-11"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>95WoCzf9J63UgdLCj/05PYaJIAw=</ds:DigestValue></ds:Reference><ds:Reference URI="#TS-8"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="wsse wsa eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>pa04vRHbAdvfr6Re5wgx2qFQepE=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-12"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>YYNZUGNHN8uRGzMqPVC2n5XODP8=</ds:DigestValue></ds:Reference><ds:Reference URI="#id-13"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="eg soapenv" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>GvVowT6jH/4fExGYfFzDbzhSwTs=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>F8Lm5aUouA5FtxQ1krQ3unE9NFh0QSl+QscEyTcK3FrIpWCB195Z7cbmYa9RsiYMdr2wTHHmou/+wrjpk9pZFJq+b0tpHKCpfj6B302Rexb5f+cDpUjBB/NGb11qaUiM65keVIWzmYnHC0iCxsCaG3lwHMELNr7GxNun1U7LzzI=</ds:SignatureValue><ds:KeyInfo Id="KI-7E559E62741B121FC215529264679845"><wsse:SecurityTokenReference wsu:Id="STR-7E559E62741B121FC215529264679846"><wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl</wsse:KeyIdentifier></wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature><wsu:Timestamp wsu:Id="TS-8"><wsu:Created>2019-03-18T16:27:47.984Z</wsu:Created><wsu:Expires>2019-03-19T04:27:47.984Z</wsu:Expires></wsu:Timestamp></wsse:Security><wsa:Action wsu:Id="id-13" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">http://www.example.com/testAction</wsa:Action><wsa:From wsu:Id="id-12" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsa:Address>client</wsa:Address></wsa:From><wsa:MessageID wsu:Id="id-11" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">uuid:c519e597-0570-4d35-92e5-0df733a17cc1</wsa:MessageID><wsa:To wsu:Id="id-10" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">test</wsa:To></soapenv:Header><soapenv:Body wsu:Id="id-9" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><eg:node1>testMessage</eg:node1></soapenv:Body></soapenv:Envelope>