Files
documenso/packages/signing/transports/local-cert.ts

81 lines
2.7 KiB
TypeScript

import * as fs from 'node:fs';
import { getCertificateStatus } from '@documenso/lib/server-only/cert/cert-status';
import { env } from '@documenso/lib/utils/env';
import { signWithP12 } from '@documenso/pdf-sign';
import { addSigningPlaceholder } from '../helpers/add-signing-placeholder';
import { updateSigningPlaceholder } from '../helpers/update-signing-placeholder';
export type SignWithLocalCertOptions = {
pdf: Buffer;
};
export const signWithLocalCert = async ({ pdf }: SignWithLocalCertOptions) => {
const { pdf: pdfWithPlaceholder, byteRange } = updateSigningPlaceholder({
pdf: await addSigningPlaceholder({ pdf }),
});
const pdfWithoutSignature = Buffer.concat([
new Uint8Array(pdfWithPlaceholder.subarray(0, byteRange[1])),
new Uint8Array(pdfWithPlaceholder.subarray(byteRange[2])),
]);
const signatureLength = byteRange[2] - byteRange[1];
const certStatus = getCertificateStatus();
if (!certStatus.isAvailable) {
console.error('Certificate error: Certificate not available for document signing');
throw new Error('Document signing failed: Certificate not available');
}
let cert: Buffer | null = null;
const localFileContents = env('NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS');
if (localFileContents) {
try {
cert = Buffer.from(localFileContents, 'base64');
} catch {
throw new Error('Failed to decode certificate contents');
}
}
if (!cert) {
let certPath = env('NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH') || '/opt/documenso/cert.p12';
// We don't want to make the development server suddenly crash when using the `dx` script
// so we retain this when NODE_ENV isn't set to production which it should be in most production
// deployments.
//
// Our docker image automatically sets this so it shouldn't be an issue for self-hosters.
if (env('NODE_ENV') !== 'production') {
certPath = env('NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH') || './example/cert.p12';
}
try {
cert = Buffer.from(fs.readFileSync(certPath));
} catch {
console.error('Certificate error: Failed to read certificate file');
throw new Error('Document signing failed: Certificate file not accessible');
}
}
const signature = signWithP12({
cert,
content: pdfWithoutSignature,
password: env('NEXT_PRIVATE_SIGNING_PASSPHRASE') || undefined,
});
const signatureAsHex = signature.toString('hex');
const signedPdf = Buffer.concat([
new Uint8Array(pdfWithPlaceholder.subarray(0, byteRange[1])),
new Uint8Array(Buffer.from(`<${signatureAsHex.padEnd(signatureLength - 2, '0')}>`)),
new Uint8Array(pdfWithPlaceholder.subarray(byteRange[2])),
]);
return signedPdf;
};