Skip to content

Add method for checking if element is signed #368

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

Merged
merged 3 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,19 @@ If the verification process fails `sig.validationErrors` will contain 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");
// Roll your own
var elem = xpath.select("/xpath_to_interesting_element", doc);
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");

// Use the built-in method
let elem = xpath.select("/xpath_to_interesting_element", doc);
const matchingReference = sig.validateElementAgainstReferences(elem, doc);
if (!matchingReference) {
throw new Error("the interesting element was not the one verified by the signature");
}
```

Note:
Expand Down
40 changes: 38 additions & 2 deletions src/signed-xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,23 @@ export class SignedXml {
/**
* Specifies the data to be signed within an XML document. See {@link Reference}
*/
private references: Reference[] = [];
private id = 0;
private signedXml = "";
private signatureXml = "";
private signatureNode: Node | null = null;
private signatureValue = "";
private originalXmlWithIds = "";
private keyInfo: Node | null = null;

/**
* Contains the references that were signed. See {@link Reference}
*/
references: Reference[] = [];

/**
* Contains validation errors (if any) after {@link checkSignature} method is called
*/
validationErrors: string[] = [];
private keyInfo: Node | null = null;

/**
* 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}
Expand Down Expand Up @@ -389,6 +394,37 @@ export class SignedXml {
}
}

validateElementAgainstReferences(elem: Element, doc: Document): Reference | false {
for (const ref of this.references) {
const uri = ref.uri?.[0] === "#" ? ref.uri.substring(1) : ref.uri;
let targetElem: xpath.SelectSingleReturnType;

for (const attr of this.idAttributes) {
const elemId = elem.getAttribute(attr);
if (uri === elemId) {
targetElem = elem;
ref.xpath = `//*[@*[local-name(.)='${attr}']='${uri}']`;
break; // found the correct element, no need to check further
}
}

// @ts-expect-error This is a problem with the types on `xpath`
if (!xpath.isNodeLike(targetElem)) {
continue;
}

const canonXml = this.getCanonReferenceXml(doc, ref, targetElem);
const hash = this.findHashAlgorithm(ref.digestAlgorithm);
const digest = hash.getHash(canonXml);

if (utils.validateDigestValue(digest, ref.digestValue)) {
return ref;
}
}

return false; // No references passed validation
}

validateReferences(doc) {
for (const ref of this.references) {
let elemXpath;
Expand Down
11 changes: 9 additions & 2 deletions test/signature-unit-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ describe("Signature unit tests", function () {
const checkedSignature = sig.checkSignature(xml);
expect(checkedSignature).to.be.true;

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

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

const firstGrandchild = doc.firstChild?.firstChild;
// @ts-expect-error FIXME
for (let i = 0; i < sig.references.length; i++) {
if (xpath.isElement(firstGrandchild)) {
const matchedReference = sig.validateElementAgainstReferences(firstGrandchild, doc);
expect(matchedReference).to.not.be.false;
} else {
// @ts-expect-error FIXME
expect(xpath.isElement(firstGrandchild)).to.be.true;
}

for (let i = 0; i < sig.references.length; i++) {
const ref = sig.references[i];
const expectedUri = `#_${i}`;
expect(
Expand Down