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

Fix formatting issue in SignatureBoxGroup class #117

Merged
merged 5 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
30 changes: 24 additions & 6 deletions packages/core/src/structure/DocumentUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import { ViewWriter } from "../ViewWriter";

export class PDFDocumentUpdate {

/**
* @internal
* %%EOF end-of-line characters.
* This parameter is more suitable for test cases and should not be used in production.
*/
public static EOF_EOL = "\n";

public static readonly DEFAULT_VIEW = new Uint8Array();

public view = PDFDocumentUpdate.DEFAULT_VIEW;
Expand Down Expand Up @@ -109,17 +116,30 @@ export class PDFDocumentUpdate {
const eofReader = new ViewReader(reader.view.buffer);
eofReader.position = reader.position + reader.view.byteOffset;
eofReader.read("%%EOF");
// read eol
let eofOffset = eofReader.position + 5;
if (eofReader.view[eofOffset] === 0x0D) {
eofOffset++;
}
if (eofReader.view[eofOffset] === 0x0A) {
eofOffset++;
}

this.view = eofReader.view.subarray(0, eofReader.position + 5);
this.view = eofReader.view.subarray(0, eofOffset);

// Check file structure
if (!this.document.wrongStructure) {
// check if update sections are in the right order
if (this.previous && this.previous.startXref > this.startXref) {
// if not, then the file structure is wrong
this.document.wrongStructure = true;
} else {
// check if xref table/stream is before any of objects in the update section
for (const obj of this.getObjects()) {
if (obj.offset > this.startXref) {
if (obj.offset > this.xref.view.byteOffset) {
// if not, then the file structure is wrong
this.document.wrongStructure = true;
break;
}
}
}
Expand Down Expand Up @@ -149,8 +169,6 @@ export class PDFDocumentUpdate {
await this.encrypt();
}

const startOffset = writer.length;

// check if the last char in the document is not a new line
// then add a new line before the update section
if (this.document.view.length && this.document.view[this.document.view.length - 1] !== 0x0A) {
Expand Down Expand Up @@ -185,8 +203,8 @@ export class PDFDocumentUpdate {
new objects.PDFNumeric(offset).writePDF(writer);
writer.writeLine();

writer.writeString("%%EOF\n");
this.view = writer.toUint8Array().subarray(startOffset, writer.length - 2); // exclude \n
writer.writeString(`%%EOF${PDFDocumentUpdate.EOF_EOL}`);
this.view = writer.toUint8Array();
}

public previous: PDFDocumentUpdate | null = null;
Expand Down
142 changes: 77 additions & 65 deletions packages/doc/src/Page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,15 +592,15 @@ context("Page", () => {
});

const page2 = doc.pages.create();
const img3 = page2.addSignatureBox({
page2.addSignatureBox({
left: "5mm",
top: "5mm",
width: "3cm",
height: "2cm",
groupName: "stepan",
});

const img4 = page2.addSignatureBox({
page2.addSignatureBox({
left: "40mm",
top: "5mm",
width: "3cm",
Expand Down Expand Up @@ -634,72 +634,84 @@ context("Page", () => {
page.addSignatureBox({
groupName: "box1",
});
page.addSignatureBox({
groupName: "box2",
});

await doc.save();

const box = doc.getComponentByName("box1");
assert.ok(box instanceof SignatureBoxGroup);

await box.sign({
dictionaryUpdate: async (dict) => {
dict.subFilter = "ETSI.CAdES.detached";
dict.Reason.get().text = "Описание причины";
dict.Location.get().text = "56.632N 47.928E";
},
containerCreate: async (data) => {

//#region Create certificate
const alg: RsaHashedKeyGenParams = {
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256",
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 2048,
};
const keys = await crypto.subtle.generateKey(alg, false, ["sign", "verify"]);
const cert = await x509.X509CertificateGenerator.createSelfSigned({
serialNumber: "0102030405",
notBefore: new Date("2021-06-29"),
notAfter: new Date("2022-06-29"),
name: "CN=Test",
keys,
signingAlgorithm: alg,
extensions: [
new x509.KeyUsagesExtension(
x509.KeyUsageFlags.digitalSignature |
x509.KeyUsageFlags.nonRepudiation |
x509.KeyUsageFlags.keyCertSign
),
new x509.BasicConstraintsExtension(false),
await x509.AuthorityKeyIdentifierExtension.create(keys.publicKey!, false, crypto),
await x509.SubjectKeyIdentifierExtension.create(keys.publicKey!, false, crypto),
new x509.ExtendedKeyUsageExtension([
"1.3.6.1.4.1.311.10.3.12", // documentSigning
"1.2.840.113583.1.1.5", // pdfAuthenticDocumentsTrust
]),
]
}, crypto);
//#endregion

//#region Create CMS
const messageDigest = await crypto.subtle.digest(alg.hash, data);
const signedData = new cms.CMSSignedData();
const signer = signedData.createSigner(cert, {
digestAlgorithm: alg.hash,
signedAttributes: [
new cms.ContentTypeAttribute(cms.CMSContentType.data),
new cms.SigningTimeAttribute(new Date()),
new cms.MessageDigestAttribute(messageDigest),
]
});

signedData.certificates.push(cert);

await signedData.sign(keys.privateKey!, signer);
//#endregion

return signedData.toBER();
}
});
const box1 = doc.getComponentByName("box1");
assert.ok(box1 instanceof SignatureBoxGroup);
const box2 = doc.getComponentByName("box2");
assert.ok(box2 instanceof SignatureBoxGroup);

const boxes = [
box1,
// box2,
];
for (const box of boxes) {
const cn = `CN=${box.name}`;
await box.sign({
dictionaryUpdate: async (dict) => {
dict.subFilter = "adbe.pkcs7.detached";
dict.Reason.get().text = "Описание причины";
dict.Location.get().text = "56.632N 47.928E";
},
containerCreate: async (data) => {

//#region Create certificate
const alg: RsaHashedKeyGenParams = {
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256",
publicExponent: new Uint8Array([1, 0, 1]),
modulusLength: 2048,
};
const keys = await crypto.subtle.generateKey(alg, false, ["sign", "verify"]);
const cert = await x509.X509CertificateGenerator.createSelfSigned({
serialNumber: "0102030405",
notBefore: new Date("2021-06-29"),
notAfter: new Date("2022-06-29"),
name: cn,
keys,
signingAlgorithm: alg,
extensions: [
new x509.KeyUsagesExtension(
x509.KeyUsageFlags.digitalSignature |
x509.KeyUsageFlags.nonRepudiation |
x509.KeyUsageFlags.keyCertSign
),
new x509.BasicConstraintsExtension(false),
await x509.AuthorityKeyIdentifierExtension.create(keys.publicKey!, false, crypto),
await x509.SubjectKeyIdentifierExtension.create(keys.publicKey!, false, crypto),
new x509.ExtendedKeyUsageExtension([
"1.3.6.1.4.1.311.10.3.12", // documentSigning
"1.2.840.113583.1.1.5", // pdfAuthenticDocumentsTrust
]),
]
}, crypto);
//#endregion

//#region Create CMS
const messageDigest = await crypto.subtle.digest(alg.hash, data);
const signedData = new cms.CMSSignedData();
const signer = signedData.createSigner(cert, {
digestAlgorithm: alg.hash,
signedAttributes: [
new cms.ContentTypeAttribute(cms.CMSContentType.data),
new cms.SigningTimeAttribute(new Date()),
new cms.MessageDigestAttribute(messageDigest),
]
});

signedData.certificates.push(cert);

await signedData.sign(keys.privateKey!, signer);
//#endregion

return signedData.toBER();
}
});
}

const pdf = await doc.save();
writeFile(pdf);
Expand Down
Loading
Loading