Skip to content

Commit 24fad1b

Browse files
committed
Add method for checking if element is signed
1 parent 226e0b2 commit 24fad1b

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,19 @@ If the verification process fails `sig.validationErrors` will contain the errors
152152
In order to protect from some attacks we must check the content we want to use is the one that has been signed:
153153

154154
```javascript
155-
var elem = select(doc, "/xpath_to_interesting_element");
155+
// Roll your own
156+
var elem = xpath.select("/xpath_to_interesting_element", doc);
156157
var uri = sig.references[0].uri; // might not be 0 - depending on the document you verify
157158
var id = uri[0] === "#" ? uri.substring(1) : uri;
158159
if (elem.getAttribute("ID") != id && elem.getAttribute("Id") != id && elem.getAttribute("id") != id)
159160
throw new Error("the interesting element was not the one verified by the signature");
161+
162+
// Use the built-in method
163+
let elem = xpath.select("/xpath_to_interesting_element", doc);
164+
const matchingReference = sig.validateElementAgainstReferences(elem, doc);
165+
if (!matchingReference) {
166+
throw new Error("the interesting element was not the one verified by the signature");
167+
}
160168
```
161169

162170
Note:

src/signed-xml.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,23 @@ export class SignedXml {
5959
/**
6060
* Specifies the data to be signed within an XML document. See {@link Reference}
6161
*/
62-
private references: Reference[] = [];
6362
private id = 0;
6463
private signedXml = "";
6564
private signatureXml = "";
6665
private signatureNode: Node | null = null;
6766
private signatureValue = "";
6867
private originalXmlWithIds = "";
68+
private keyInfo: Node | null = null;
69+
70+
/**
71+
* Contains the references that were signed. See {@link Reference}
72+
*/
73+
references: Reference[] = [];
74+
6975
/**
7076
* Contains validation errors (if any) after {@link checkSignature} method is called
7177
*/
7278
validationErrors: string[] = [];
73-
private keyInfo: Node | null = null;
7479

7580
/**
7681
* To add a new transformation algorithm create a new class that implements the {@link TransformationAlgorithm} interface, and register it here. More info: {@link https://github.com/node-saml/xml-crypto#customizing-algorithms|Customizing Algorithms}
@@ -389,6 +394,37 @@ export class SignedXml {
389394
}
390395
}
391396

397+
validateElementAgainstReferences(elem: Element, doc: Document): Reference | false {
398+
for (const ref of this.references) {
399+
const uri = ref.uri?.[0] === "#" ? ref.uri.substring(1) : ref.uri;
400+
let targetElem: xpath.SelectSingleReturnType;
401+
402+
for (const attr of this.idAttributes) {
403+
const elemId = elem.getAttribute(attr);
404+
if (uri === elemId) {
405+
targetElem = elem;
406+
ref.xpath = `//*[@*[local-name(.)='${attr}']='${uri}']`;
407+
break; // found the correct element, no need to check further
408+
}
409+
}
410+
411+
// @ts-expect-error This is a problem with the types on `xpath`
412+
if (!xpath.isNodeLike(targetElem)) {
413+
continue;
414+
}
415+
416+
const canonXml = this.getCanonReferenceXml(doc, ref, targetElem);
417+
const hash = this.findHashAlgorithm(ref.digestAlgorithm);
418+
const digest = hash.getHash(canonXml);
419+
420+
if (utils.validateDigestValue(digest, ref.digestValue)) {
421+
return ref;
422+
}
423+
}
424+
425+
return false; // No references passed validation
426+
}
427+
392428
validateReferences(doc) {
393429
for (const ref of this.references) {
394430
let elemXpath;

test/signature-unit-tests.spec.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ describe("Signature unit tests", function () {
8282
const checkedSignature = sig.checkSignature(xml);
8383
expect(checkedSignature).to.be.true;
8484

85-
// @ts-expect-error FIXME
8685
expect(sig.references.length).to.equal(3);
8786

8887
const digests = [
@@ -91,9 +90,17 @@ describe("Signature unit tests", function () {
9190
"sH1gxKve8wlU8LlFVa2l6w3HMJ0=",
9291
];
9392

93+
const firstGrandchild = doc.firstChild?.firstChild;
9494
// @ts-expect-error FIXME
95-
for (let i = 0; i < sig.references.length; i++) {
95+
if (xpath.isElement(firstGrandchild)) {
96+
const matchedReference = sig.validateElementAgainstReferences(firstGrandchild, doc);
97+
expect(matchedReference).to.not.be.false;
98+
} else {
9699
// @ts-expect-error FIXME
100+
expect(xpath.isElement(firstGrandchild)).to.be.true;
101+
}
102+
103+
for (let i = 0; i < sig.references.length; i++) {
97104
const ref = sig.references[i];
98105
const expectedUri = `#_${i}`;
99106
expect(

0 commit comments

Comments
 (0)