@embedpdf/core-signature 3.0.0-next.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/cms/engine.ts","../src/cms/oids.ts","../src/cms/parse.ts","../src/cms/verify.ts","../src/cms/build.ts","../src/gate.ts","../src/trust.ts","../src/sign.ts","../src/verdict.ts","../src/signers/self-signed.ts","../src/signers/webcrypto.ts","../src/signers/remote.ts","../src/signers/personal.ts"],"sourcesContent":["import * as pkijs from 'pkijs';\n\nlet ensured = false;\n\n/**\n * PKI.js needs a WebCrypto engine. Browsers and Node ≥ 19 expose\n * `globalThis.crypto`; register it once, lazily, so importing this package\n * has no side effect.\n */\nexport function ensureEngine(): pkijs.ICryptoEngine {\n if (!ensured) {\n ensured = true;\n if (!pkijs.getCrypto()) {\n const webcrypto = globalThis.crypto;\n if (!webcrypto?.subtle) {\n throw new Error('@embedpdf/core-signature needs WebCrypto (globalThis.crypto.subtle)');\n }\n pkijs.setEngine('embedpdf', new pkijs.CryptoEngine({ name: 'embedpdf', crypto: webcrypto }));\n }\n }\n return pkijs.getCrypto(true);\n}\n\nexport function toArrayBuffer(view: Uint8Array): ArrayBuffer {\n const copy = new Uint8Array(view.byteLength);\n copy.set(view);\n return copy.buffer;\n}\n\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.byteLength !== b.byteLength) return false;\n for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\nexport function toHex(bytes: Uint8Array): string {\n let out = '';\n for (const b of bytes) out += b.toString(16).padStart(2, '0');\n return out;\n}\n","import type { DigestAlgorithm } from '@embedpdf/engine-core/runtime';\n\nexport const OID = {\n data: '1.2.840.113549.1.7.1',\n signedData: '1.2.840.113549.1.7.2',\n contentType: '1.2.840.113549.1.9.3',\n messageDigest: '1.2.840.113549.1.9.4',\n signingTime: '1.2.840.113549.1.9.5',\n signingCertificateV2: '1.2.840.113549.1.9.16.2.47',\n timestampToken: '1.2.840.113549.1.9.16.2.14',\n sha1: '1.3.14.3.2.26',\n sha256: '2.16.840.1.101.3.4.2.1',\n sha384: '2.16.840.1.101.3.4.2.2',\n sha512: '2.16.840.1.101.3.4.2.3',\n rsaEncryption: '1.2.840.113549.1.1.1',\n sha256WithRsa: '1.2.840.113549.1.1.11',\n sha384WithRsa: '1.2.840.113549.1.1.12',\n sha512WithRsa: '1.2.840.113549.1.1.13',\n rsaPss: '1.2.840.113549.1.1.10',\n mgf1: '1.2.840.113549.1.1.8',\n ecdsaSha256: '1.2.840.10045.4.3.2',\n ecdsaSha384: '1.2.840.10045.4.3.3',\n ecdsaSha512: '1.2.840.10045.4.3.4',\n subjectKeyIdentifier: '2.5.29.14',\n keyUsage: '2.5.29.15',\n basicConstraints: '2.5.29.19',\n commonName: '2.5.4.3',\n} as const;\n\nexport const DIGEST_BY_OID: Record<string, DigestAlgorithm> = {\n [OID.sha1]: 'sha1',\n [OID.sha256]: 'sha256',\n [OID.sha384]: 'sha384',\n [OID.sha512]: 'sha512',\n};\n\nexport const OID_BY_DIGEST: Record<DigestAlgorithm, string> = {\n sha1: OID.sha1,\n sha256: OID.sha256,\n sha384: OID.sha384,\n sha512: OID.sha512,\n};\n\n/** WebCrypto hash names. */\nexport const WEBCRYPTO_HASH: Record<DigestAlgorithm, string> = {\n sha1: 'SHA-1',\n sha256: 'SHA-256',\n sha384: 'SHA-384',\n sha512: 'SHA-512',\n};\n\nexport const DIGEST_LENGTH: Record<DigestAlgorithm, number> = {\n sha1: 20,\n sha256: 32,\n sha384: 48,\n sha512: 64,\n};\n","import type { DigestAlgorithm } from '@embedpdf/engine-core/runtime';\nimport * as asn1js from 'asn1js';\nimport * as pkijs from 'pkijs';\n\nimport { bytesEqual, toArrayBuffer } from './engine';\nimport { DIGEST_BY_OID, OID } from './oids';\n\n/** What a detached CMS says about itself. Nothing here is verified. */\nexport interface ParsedCms {\n digestAlgorithm: DigestAlgorithm;\n /** The `message-digest` signed attribute: what the signer claims the content hashes to. */\n messageDigest: Uint8Array;\n /** The CMS `signing-time` signed attribute, when present (PAdES forbids it; the PDF's `/M` is the claim there). */\n signingTime: Date | null;\n /** DER of the signer's certificate. */\n signerCertificate: Uint8Array;\n /** DER of every certificate carried by the CMS, signer first. */\n certificates: Uint8Array[];\n /** The signature algorithm OID (`1.2.840.113549.1.1.11` = sha256WithRSAEncryption, …). */\n signatureAlgorithm: string;\n /** The ESS `signing-certificate-v2` attribute is present (a CAdES requirement). */\n signingCertificateV2: boolean;\n /** DER of an RFC 3161 timestamp token carried as an unsigned attribute, when present. */\n timestampToken: Uint8Array | null;\n}\n\nexport class CmsError extends Error {\n constructor(\n readonly reason: 'malformed' | 'unsupported',\n message: string,\n ) {\n super(message);\n this.name = 'CmsError';\n }\n}\n\nexport interface ParsedCmsInternal {\n parsed: ParsedCms;\n signedData: pkijs.SignedData;\n signerInfo: pkijs.SignerInfo;\n signerCertificate: pkijs.Certificate;\n certificates: pkijs.Certificate[];\n /** DER of the signing-certificate-v2 attribute's first certHash, when present. */\n essCertHash: Uint8Array | null;\n}\n\n/** Parse a detached CMS SignedData with exactly one signer. Throws `CmsError`. */\nexport function parseDetachedCms(cms: Uint8Array): ParsedCms {\n return parseCmsInternal(cms).parsed;\n}\n\nexport function parseCmsInternal(cms: Uint8Array): ParsedCmsInternal {\n if (cms.byteLength === 0) throw new CmsError('malformed', 'empty CMS');\n const asn1 = asn1js.fromBER(toArrayBuffer(cms));\n if (asn1.offset === -1) throw new CmsError('malformed', 'CMS is not valid BER');\n\n let contentInfo: pkijs.ContentInfo;\n let signedData: pkijs.SignedData;\n try {\n contentInfo = new pkijs.ContentInfo({ schema: asn1.result });\n if (contentInfo.contentType !== OID.signedData) {\n throw new CmsError('unsupported', `CMS content type ${contentInfo.contentType} is not SignedData`);\n }\n signedData = new pkijs.SignedData({ schema: contentInfo.content });\n } catch (err) {\n if (err instanceof CmsError) throw err;\n throw new CmsError('malformed', `CMS does not parse as SignedData: ${(err as Error).message}`);\n }\n if (signedData.encapContentInfo.eContent) {\n throw new CmsError('unsupported', 'CMS carries encapsulated content; a PDF signature is detached');\n }\n if (signedData.signerInfos.length !== 1) {\n throw new CmsError('unsupported', `CMS has ${signedData.signerInfos.length} signers; exactly one is expected`);\n }\n const signerInfo = signedData.signerInfos[0];\n const digestAlgorithm = DIGEST_BY_OID[signerInfo.digestAlgorithm.algorithmId];\n if (!digestAlgorithm) {\n throw new CmsError('unsupported', `digest algorithm ${signerInfo.digestAlgorithm.algorithmId}`);\n }\n\n const certificates = (signedData.certificates ?? []).filter(\n (c): c is pkijs.Certificate => c instanceof pkijs.Certificate,\n );\n const signerCertificate = findSignerCertificate(signerInfo, certificates);\n if (!signerCertificate) {\n throw new CmsError('malformed', 'the signer certificate is not carried by the CMS');\n }\n\n const signedAttrs = signerInfo.signedAttrs?.attributes ?? [];\n if (signedAttrs.length === 0) {\n throw new CmsError('unsupported', 'CMS has no signed attributes; a PDF signature needs message-digest');\n }\n const attr = (type: string): pkijs.Attribute | undefined => signedAttrs.find((a) => a.type === type);\n const contentType = attr(OID.contentType);\n if (contentType && contentType.values[0]?.valueBlock?.toString?.() !== OID.data) {\n const oid = contentType.values[0] as asn1js.ObjectIdentifier | undefined;\n if (!oid || oid.valueBlock.toString() !== OID.data) {\n throw new CmsError('unsupported', 'CMS content-type attribute is not id-data');\n }\n }\n const messageDigestAttr = attr(OID.messageDigest);\n const messageDigestValue = messageDigestAttr?.values[0] as asn1js.OctetString | undefined;\n if (!messageDigestValue || !(messageDigestValue instanceof asn1js.OctetString)) {\n throw new CmsError('malformed', 'CMS has no message-digest attribute');\n }\n const messageDigest = new Uint8Array(messageDigestValue.valueBlock.valueHexView);\n\n let signingTime: Date | null = null;\n const signingTimeAttr = attr(OID.signingTime);\n const timeValue = signingTimeAttr?.values[0];\n if (timeValue instanceof asn1js.UTCTime || timeValue instanceof asn1js.GeneralizedTime) {\n signingTime = timeValue.toDate();\n }\n\n const essAttr = attr(OID.signingCertificateV2);\n let essCertHash: Uint8Array | null = null;\n if (essAttr) {\n // SigningCertificateV2 ::= SEQUENCE { certs SEQUENCE OF ESSCertIDv2, ... }\n // ESSCertIDv2 ::= SEQUENCE { hashAlgorithm DEFAULT sha256, certHash OCTET STRING, ... }\n const seq = essAttr.values[0] as asn1js.Sequence | undefined;\n const certs = seq?.valueBlock?.value?.[0] as asn1js.Sequence | undefined;\n const first = certs?.valueBlock?.value?.[0] as asn1js.Sequence | undefined;\n const members = first?.valueBlock?.value ?? [];\n // hashAlgorithm is OPTIONAL (DEFAULT sha256) and, when present, the\n // first member: a SEQUENCE starting with an OID. issuerSerial is also a\n // SEQUENCE but follows the certHash.\n let cursor = 0;\n const head = members[0];\n if (head instanceof asn1js.Sequence && head.valueBlock.value[0] instanceof asn1js.ObjectIdentifier) {\n const algOid = head.valueBlock.value[0] as asn1js.ObjectIdentifier;\n if (algOid.valueBlock.toString() !== OID.sha256) {\n throw new CmsError('unsupported', 'signing-certificate-v2 uses a hash other than SHA-256');\n }\n cursor = 1;\n }\n const hash = members[cursor];\n if (!(hash instanceof asn1js.OctetString)) {\n throw new CmsError('malformed', 'signing-certificate-v2 carries no certHash');\n }\n essCertHash = new Uint8Array(hash.valueBlock.valueHexView);\n }\n\n let timestampToken: Uint8Array | null = null;\n const tokenAttr = signerInfo.unsignedAttrs?.attributes.find((a) => a.type === OID.timestampToken);\n if (tokenAttr?.values[0]) {\n timestampToken = new Uint8Array((tokenAttr.values[0] as asn1js.Sequence).toBER(false));\n }\n\n const ordered = [signerCertificate, ...certificates.filter((c) => c !== signerCertificate)];\n return {\n parsed: {\n digestAlgorithm,\n messageDigest,\n signingTime,\n signerCertificate: certDer(signerCertificate),\n certificates: ordered.map(certDer),\n signatureAlgorithm: signerInfo.signatureAlgorithm.algorithmId,\n signingCertificateV2: essAttr !== undefined,\n timestampToken,\n },\n signedData,\n signerInfo,\n signerCertificate,\n certificates: ordered,\n essCertHash,\n };\n}\n\nexport function certDer(cert: pkijs.Certificate): Uint8Array {\n return new Uint8Array(cert.toSchema(true).toBER(false));\n}\n\nfunction findSignerCertificate(\n signerInfo: pkijs.SignerInfo,\n certificates: pkijs.Certificate[],\n): pkijs.Certificate | null {\n const sid = signerInfo.sid;\n if (sid instanceof pkijs.IssuerAndSerialNumber) {\n for (const cert of certificates) {\n if (\n cert.issuer.isEqual(sid.issuer) &&\n bytesEqual(\n new Uint8Array(cert.serialNumber.valueBlock.valueHexView),\n new Uint8Array(sid.serialNumber.valueBlock.valueHexView),\n )\n ) {\n return cert;\n }\n }\n return null;\n }\n // SubjectKeyIdentifier: [0] IMPLICIT OCTET STRING.\n const ski = (sid as asn1js.Primitive | undefined)?.valueBlock?.valueHexView;\n if (!ski) return null;\n for (const cert of certificates) {\n const ext = cert.extensions?.find((e) => e.extnID === OID.subjectKeyIdentifier);\n const value = ext ? asn1js.fromBER(ext.extnValue.valueBlock.valueHexView) : null;\n const inner = value && value.offset !== -1 ? (value.result as asn1js.OctetString) : null;\n if (inner && bytesEqual(new Uint8Array(inner.valueBlock.valueHexView), new Uint8Array(ski))) {\n return cert;\n }\n }\n return null;\n}\n","import * as asn1js from 'asn1js';\nimport * as pkijs from 'pkijs';\n\nimport { bytesEqual, ensureEngine, toArrayBuffer } from './engine';\nimport { WEBCRYPTO_HASH } from './oids';\nimport { CmsError, parseCmsInternal, type ParsedCmsInternal } from './parse';\n\nexport type CryptographyVerdict = 'valid' | 'invalid' | 'unsupported';\n\n/** The DER of the signed attributes as they are signed: re-tagged as a SET. */\nexport function signedAttributesDer(signerInfo: pkijs.SignerInfo): Uint8Array {\n const set = new asn1js.Set({ value: (signerInfo.signedAttrs?.attributes ?? []).map((a) => a.toSchema()) });\n return new Uint8Array(set.toBER(false));\n}\n\n/**\n * Does the CMS's signature verify with the certificate it names, over the\n * attributes it signed? Says nothing about the content: the message digest\n * is compared by the caller (`validateSignatures`, `verifyForCompletion`).\n * `unsupported` means the algorithm is unknown to this engine, which is\n * never `invalid`.\n */\nexport async function verifyCmsSignature(\n cms: Uint8Array | ParsedCmsInternal,\n): Promise<CryptographyVerdict> {\n let internal: ParsedCmsInternal;\n try {\n internal = cms instanceof Uint8Array ? parseCmsInternal(cms) : cms;\n } catch (err) {\n return err instanceof CmsError && err.reason === 'unsupported' ? 'unsupported' : 'invalid';\n }\n const engine = ensureEngine();\n const { signerInfo, signerCertificate, parsed, essCertHash } = internal;\n\n if (essCertHash) {\n const certHash = new Uint8Array(\n await engine.digest('SHA-256', toArrayBuffer(parsed.signerCertificate)),\n );\n if (!bytesEqual(certHash, essCertHash)) return 'invalid';\n }\n\n try {\n const ok = await engine.verifyWithPublicKey(\n toArrayBuffer(signedAttributesDer(signerInfo)),\n signerInfo.signature,\n signerCertificate.subjectPublicKeyInfo,\n signerInfo.signatureAlgorithm,\n WEBCRYPTO_HASH[parsed.digestAlgorithm],\n );\n return ok ? 'valid' : 'invalid';\n } catch (err) {\n const message = (err as Error).message ?? '';\n return /unsupported|unknown|not supported|not implemented/i.test(message) ? 'unsupported' : 'invalid';\n }\n}\n","import type { DigestAlgorithm } from '@embedpdf/engine-core/runtime';\nimport * as asn1js from 'asn1js';\nimport * as pkijs from 'pkijs';\n\nimport { ensureEngine, toArrayBuffer } from './engine';\nimport { DIGEST_LENGTH, OID, OID_BY_DIGEST } from './oids';\n\nexport type SignatureProfile = 'pkcs7' | 'cades-b';\nexport type RawSignatureAlgorithm = 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA';\n\n/**\n * Something that signs bytes with a private key it never hands out: a\n * WebCrypto key, an HSM, a smart card. `sign` receives the DER of the CMS\n * signed attributes and returns the raw signature (for ECDSA: `r || s`,\n * as WebCrypto produces it; the CMS encoding is done here).\n */\nexport interface RawSigner {\n readonly kind: 'raw';\n /** DER certificates, the signer's first, then its issuers. */\n readonly certificateChain: Uint8Array[];\n readonly algorithm: RawSignatureAlgorithm;\n readonly hash: Exclude<DigestAlgorithm, 'sha1'>;\n sign(signedAttributesDer: Uint8Array): Promise<Uint8Array>;\n}\n\nexport interface BuildDetachedCmsInput {\n /** The digest the engine prepared (over the `/ByteRange`). */\n digest: Uint8Array;\n hash: Exclude<DigestAlgorithm, 'sha1'>;\n profile: SignatureProfile;\n signer: RawSigner;\n /** `pkcs7` only: the CMS `signing-time` attribute. PAdES (`cades-b`) never carries one. */\n signingTime?: Date;\n}\n\n/**\n * Build a detached CMS SignedData around a raw signature: content-type,\n * message-digest, and for CAdES-B the ESS signing-certificate-v2 attribute;\n * the signer's certificate chain; one SignerInfo identified by issuer and\n * serial number.\n */\nexport async function buildDetachedCms(input: BuildDetachedCmsInput): Promise<Uint8Array> {\n const engine = ensureEngine();\n if (input.digest.byteLength !== DIGEST_LENGTH[input.hash]) {\n throw new Error(`digest is ${input.digest.byteLength} bytes; ${input.hash} needs ${DIGEST_LENGTH[input.hash]}`);\n }\n if (input.signer.hash !== input.hash) {\n throw new Error(`the signer hashes with ${input.signer.hash}, the document was prepared with ${input.hash}`);\n }\n if (input.signer.certificateChain.length === 0) {\n throw new Error('the signer carries no certificate');\n }\n const chain = input.signer.certificateChain.map((der) => pkijs.Certificate.fromBER(toArrayBuffer(der)));\n const signerCert = chain[0];\n\n const attributes: pkijs.Attribute[] = [\n new pkijs.Attribute({\n type: OID.contentType,\n values: [new asn1js.ObjectIdentifier({ value: OID.data })],\n }),\n new pkijs.Attribute({\n type: OID.messageDigest,\n values: [new asn1js.OctetString({ valueHex: toArrayBuffer(input.digest) })],\n }),\n ];\n if (input.profile === 'pkcs7' && input.signingTime) {\n attributes.push(\n new pkijs.Attribute({\n type: OID.signingTime,\n values: [new asn1js.UTCTime({ valueDate: input.signingTime })],\n }),\n );\n }\n if (input.profile === 'cades-b') {\n const certHash = await engine.digest('SHA-256', toArrayBuffer(input.signer.certificateChain[0]));\n const issuerSerial = new pkijs.IssuerSerial({\n issuer: new pkijs.GeneralNames({\n names: [new pkijs.GeneralName({ type: 4, value: signerCert.issuer })],\n }),\n serialNumber: signerCert.serialNumber,\n });\n // ESSCertIDv2 with the default hash algorithm (SHA-256) omitted.\n const essCertId = new asn1js.Sequence({\n value: [new asn1js.OctetString({ valueHex: certHash }), issuerSerial.toSchema()],\n });\n attributes.push(\n new pkijs.Attribute({\n type: OID.signingCertificateV2,\n values: [new asn1js.Sequence({ value: [new asn1js.Sequence({ value: [essCertId] })] })],\n }),\n );\n }\n\n const digestAlgorithm = new pkijs.AlgorithmIdentifier({ algorithmId: OID_BY_DIGEST[input.hash] });\n const signerInfo = new pkijs.SignerInfo({\n version: 1,\n sid: new pkijs.IssuerAndSerialNumber({\n issuer: signerCert.issuer,\n serialNumber: signerCert.serialNumber,\n }),\n digestAlgorithm,\n signedAttrs: new pkijs.SignedAndUnsignedAttributes({ type: 0, attributes }),\n signatureAlgorithm: signatureAlgorithmFor(input.signer.algorithm, input.hash),\n });\n\n const toSign = new asn1js.Set({ value: attributes.map((a) => a.toSchema()) }).toBER(false);\n const raw = await input.signer.sign(new Uint8Array(toSign));\n const signature =\n input.signer.algorithm === 'ECDSA' ? pkijs.createCMSECDSASignature(toArrayBuffer(raw)) : toArrayBuffer(raw);\n signerInfo.signature = new asn1js.OctetString({ valueHex: signature });\n\n const signedData = new pkijs.SignedData({\n version: 1,\n digestAlgorithms: [digestAlgorithm],\n encapContentInfo: new pkijs.EncapsulatedContentInfo({ eContentType: OID.data }),\n certificates: chain,\n signerInfos: [signerInfo],\n });\n const contentInfo = new pkijs.ContentInfo({\n contentType: OID.signedData,\n content: signedData.toSchema(true),\n });\n return new Uint8Array(contentInfo.toSchema().toBER(false));\n}\n\nfunction signatureAlgorithmFor(\n algorithm: RawSignatureAlgorithm,\n hash: Exclude<DigestAlgorithm, 'sha1'>,\n): pkijs.AlgorithmIdentifier {\n switch (algorithm) {\n case 'RSA-PKCS1-v1_5':\n return new pkijs.AlgorithmIdentifier({\n algorithmId: { sha256: OID.sha256WithRsa, sha384: OID.sha384WithRsa, sha512: OID.sha512WithRsa }[hash],\n algorithmParams: new asn1js.Null(),\n });\n case 'ECDSA':\n return new pkijs.AlgorithmIdentifier({\n algorithmId: { sha256: OID.ecdsaSha256, sha384: OID.ecdsaSha384, sha512: OID.ecdsaSha512 }[hash],\n });\n case 'RSA-PSS': {\n const hashAlgorithm = new pkijs.AlgorithmIdentifier({\n algorithmId: OID_BY_DIGEST[hash],\n algorithmParams: new asn1js.Null(),\n });\n const params = new pkijs.RSASSAPSSParams({\n hashAlgorithm,\n maskGenAlgorithm: new pkijs.AlgorithmIdentifier({\n algorithmId: OID.mgf1,\n algorithmParams: hashAlgorithm.toSchema(),\n }),\n saltLength: DIGEST_LENGTH[hash],\n });\n return new pkijs.AlgorithmIdentifier({ algorithmId: OID.rsaPss, algorithmParams: params.toSchema() });\n }\n }\n}\n","import type { SignaturePrepared } from '@embedpdf/engine-core/runtime';\n\nimport type { SignatureProfile } from './cms/build';\nimport { bytesEqual } from './cms/engine';\nimport { CmsError, parseCmsInternal } from './cms/parse';\nimport { verifyCmsSignature } from './cms/verify';\n\nexport type CompletionRefusal =\n | 'malformed'\n | 'too-large'\n | 'algorithm-mismatch'\n | 'digest-mismatch'\n | 'signature-invalid'\n | 'profile-violation';\n\nexport type CompletionGate = { ok: true } | { ok: false; reason: CompletionRefusal; detail: string };\n\n/**\n * The gate between a signer's answer and `signatures.complete`: the CMS\n * must fit the reserved room, hash with the prepared algorithm, carry the\n * prepared digest as its message digest, verify with its own certificate,\n * and match the profile the `/SubFilter` promises (CAdES: ESS\n * signing-certificate-v2 present, no CMS signing-time). A bad CMS never\n * reaches the document.\n */\nexport async function verifyForCompletion(input: {\n cms: Uint8Array;\n prepared: SignaturePrepared;\n profile: SignatureProfile;\n}): Promise<CompletionGate> {\n const { cms, prepared, profile } = input;\n if (cms.byteLength > prepared.contentsSize) {\n return {\n ok: false,\n reason: 'too-large',\n detail: `the CMS is ${cms.byteLength} bytes; ${prepared.contentsSize} were reserved`,\n };\n }\n let internal;\n try {\n internal = parseCmsInternal(cms);\n } catch (err) {\n return { ok: false, reason: 'malformed', detail: err instanceof CmsError ? err.message : String(err) };\n }\n const parsed = internal.parsed;\n if (parsed.digestAlgorithm !== prepared.algorithm) {\n return {\n ok: false,\n reason: 'algorithm-mismatch',\n detail: `the CMS digests with ${parsed.digestAlgorithm}, the document was prepared with ${prepared.algorithm}`,\n };\n }\n if (!bytesEqual(parsed.messageDigest, prepared.digest)) {\n return { ok: false, reason: 'digest-mismatch', detail: 'the CMS message digest is not the prepared digest' };\n }\n if (profile === 'cades-b') {\n if (!parsed.signingCertificateV2) {\n return { ok: false, reason: 'profile-violation', detail: 'CAdES requires the ESS signing-certificate-v2 attribute' };\n }\n if (parsed.signingTime) {\n return { ok: false, reason: 'profile-violation', detail: 'PAdES forbids the CMS signing-time attribute' };\n }\n }\n const cryptography = await verifyCmsSignature(internal);\n if (cryptography !== 'valid') {\n return {\n ok: false,\n reason: 'signature-invalid',\n detail: cryptography === 'unsupported' ? 'the signature algorithm is not supported' : 'the signature does not verify',\n };\n }\n return { ok: true };\n}\n","import * as pkijs from 'pkijs';\n\nimport { ensureEngine, toArrayBuffer } from './cms/engine';\nimport { parseCmsInternal, type ParsedCmsInternal } from './cms/parse';\n\n/**\n * Where trust comes from. The engine has no opinion: an application\n * supplies its anchors (a company root, a national trust list, the AATL).\n */\nexport interface TrustPort {\n /** DER certificates trusted as anchors. */\n anchors(): Promise<Uint8Array[]>;\n}\n\n/**\n * When a chain is judged. `now` is the honest default; `timestamp` is only\n * legitimate for a verified RFC 3161 token, which is why it carries the\n * proof flag. The signature's own `/M` is a claim and never a validation\n * time.\n */\nexport type ValidationTime =\n | { kind: 'now' }\n | { kind: 'timestamp'; genTime: Date; tokenVerified: true };\n\nexport type TrustStatus = 'trusted' | 'untrusted' | 'unknown';\n\nexport interface TrustVerdict {\n status: TrustStatus;\n /** Why, when not trusted. */\n reason?: string;\n /** DER certificates of the validated path, anchor last, when trusted. */\n path?: Uint8Array[];\n}\n\n/**\n * Build and validate the certificate path from the CMS's signer to one\n * of the port's anchors at `at`. No port, or a port with no anchors, is\n * `unknown`: nothing was checked, and nothing was refused.\n */\nexport async function validateChain(\n cms: Uint8Array | ParsedCmsInternal,\n trust: TrustPort | null | undefined,\n at: ValidationTime = { kind: 'now' },\n): Promise<TrustVerdict> {\n if (!trust) return { status: 'unknown', reason: 'no trust anchors configured' };\n const anchors = await trust.anchors();\n if (anchors.length === 0) return { status: 'unknown', reason: 'no trust anchors configured' };\n ensureEngine();\n let internal: ParsedCmsInternal;\n try {\n internal = cms instanceof Uint8Array ? parseCmsInternal(cms) : cms;\n } catch (err) {\n return { status: 'untrusted', reason: (err as Error).message };\n }\n const trustedCerts = anchors.map((der) => pkijs.Certificate.fromBER(toArrayBuffer(der)));\n const chainEngine = new pkijs.CertificateChainValidationEngine({\n certs: internal.certificates,\n trustedCerts,\n checkDate: at.kind === 'now' ? new Date() : at.genTime,\n });\n try {\n const result = await chainEngine.verify();\n if (!result.result) {\n return { status: 'untrusted', reason: result.resultMessage || `chain validation code ${result.resultCode}` };\n }\n return {\n status: 'trusted',\n path: (result.certificatePath ?? []).map((c) => new Uint8Array(c.toSchema(true).toBER(false))),\n };\n } catch (err) {\n return { status: 'untrusted', reason: (err as Error).message };\n }\n}\n","import type {\n DigestAlgorithm,\n DocumentHandle,\n SignatureCompleteResult,\n SignaturePrepareInput,\n SignatureSubFilter,\n} from '@embedpdf/engine-core/runtime';\n\nimport { buildDetachedCms, type RawSigner, type SignatureProfile } from './cms/build';\nimport { verifyForCompletion, type CompletionRefusal } from './gate';\n\n/** A service that turns a digest into a finished detached CMS (most hosted signing APIs). */\nexport interface CmsSigner {\n readonly kind: 'cms';\n sign(input: {\n digest: Uint8Array;\n algorithm: Exclude<DigestAlgorithm, 'sha1'>;\n subFilter: SignatureSubFilter;\n }): Promise<Uint8Array>;\n}\n\nexport type SignerPort = CmsSigner | RawSigner;\n\nexport type SignInput = Omit<SignaturePrepareInput, 'kind'> & { signer: SignerPort };\n\nexport class SigningError extends Error {\n constructor(\n readonly reason: CompletionRefusal,\n detail: string,\n ) {\n super(`signing refused (${reason}): ${detail}`);\n this.name = 'SigningError';\n }\n}\n\nexport function profileFor(subFilter: SignatureSubFilter): SignatureProfile {\n return subFilter === 'adbe.pkcs7.detached' ? 'pkcs7' : 'cades-b';\n}\n\n/**\n * Sign a field in one call: `prepare` on the engine, the signer turns the\n * digest into a CMS (or a raw signature this package wraps), the gate\n * checks the CMS matches what was prepared, `complete` installs it. Any\n * failure aborts the candidate and rethrows; the document is never left\n * with a pending signing.\n */\nexport async function sign(doc: DocumentHandle, input: SignInput): Promise<SignatureCompleteResult> {\n if (!doc.signatures) throw new Error('this engine does not implement signatures');\n const { signer, ...rest } = input;\n const subFilter: SignatureSubFilter = rest.subFilter ?? 'ETSI.CAdES.detached';\n const profile = profileFor(subFilter);\n const digest = rest.digest ?? (signer.kind === 'raw' ? signer.hash : 'sha256');\n const prepared = await doc.signatures.prepare({ ...rest, subFilter, digest });\n try {\n const cms =\n signer.kind === 'cms'\n ? await signer.sign({ digest: prepared.digest, algorithm: prepared.algorithm, subFilter })\n : await buildDetachedCms({\n digest: prepared.digest,\n hash: prepared.algorithm,\n profile,\n signer,\n signingTime: profile === 'pkcs7' ? new Date() : undefined,\n });\n const gate = await verifyForCompletion({ cms, prepared, profile });\n if (!gate.ok) throw new SigningError(gate.reason, gate.detail);\n return await doc.signatures.complete({\n signingId: prepared.signingId,\n cms,\n expectedVersion: prepared.expectedVersion,\n });\n } catch (err) {\n await doc.signatures.abort(prepared.signingId).catch(() => undefined);\n throw err;\n }\n}\n","import type {\n DocumentHandle,\n SignatureDTO,\n SignatureSnapshot,\n} from '@embedpdf/engine-core/runtime';\n\nimport { bytesEqual } from './cms/engine';\nimport { CmsError, parseCmsInternal, type ParsedCms, type ParsedCmsInternal } from './cms/parse';\nimport { verifyCmsSignature, type CryptographyVerdict } from './cms/verify';\nimport { validateChain, type TrustPort, type TrustStatus, type ValidationTime } from './trust';\n\nexport type IntegrityVerdict = 'valid' | 'invalid' | 'indeterminate';\nexport type ModificationsVerdict = 'unchanged' | 'permitted' | 'forbidden' | 'indeterminate';\n\n/**\n * Four independent facts and a summary, never one boolean — the shape\n * Acrobat reports (\"signature valid\", \"signer unknown\", \"document changed,\n * changes allowed\") and the shape a UI can explain.\n */\nexport interface SignatureVerdict {\n signature: SignatureDTO;\n /** The sealed bytes are intact: whole-revision coverage and the CMS message digest matches them. */\n integrity: IntegrityVerdict;\n /** The CMS verifies with its own certificate. */\n cryptography: CryptographyVerdict;\n /** The certificate chains to one of the caller's anchors. */\n trust: TrustStatus;\n trustReason?: string;\n validatedAt: ValidationTime;\n /**\n * What changed after the signature, and which bytes were judged: the loaded\n * file (`persisted`), or the session's unsaved edits snapshotted as one\n * more revision (`working-copy` — only when such edits existed).\n */\n modifications: {\n verdict: ModificationsVerdict;\n detail?: string;\n basis: 'persisted' | 'working-copy';\n /** Revisions appended after the one this signature sealed (present when there are any). */\n laterRevisions?: number;\n /** Some object was changed after the signature and holds the sealed value again. */\n undone?: boolean;\n };\n /** `invalid` only on positive evidence; `indeterminate` whenever a fact could not be established. */\n summary: 'valid' | 'valid-untrusted' | 'invalid' | 'indeterminate';\n cms: ParsedCms | null;\n}\n\nexport interface ValidateSignaturesOptions {\n trust?: TrustPort | null;\n at?: ValidationTime;\n /**\n * `persisted` (default): the loaded bytes — what a file on disk says.\n * `working-copy`: unsaved edits are judged too, as the revision a save would\n * write — what the file a save produces will say. A viewer shows this one.\n */\n until?: 'persisted' | 'working-copy';\n}\n\n/**\n * Validate every signature of a document: integrity from the engine's\n * bytes, cryptography from the CMS, trust from the caller's anchors.\n */\nexport async function validateSignatures(\n doc: DocumentHandle,\n opts: ValidateSignaturesOptions = {},\n): Promise<SignatureVerdict[]> {\n if (!doc.signatures) throw new Error('this engine does not implement signatures');\n const snapshot = await doc.signatures.list();\n const verdicts: SignatureVerdict[] = [];\n for (const signature of snapshot.signatures) {\n if (!signature.signed) continue;\n verdicts.push(await validateOne(doc, snapshot, signature, opts));\n }\n return verdicts;\n}\n\nasync function validateOne(\n doc: DocumentHandle,\n snapshot: SignatureSnapshot,\n signature: SignatureDTO,\n opts: ValidateSignaturesOptions,\n): Promise<SignatureVerdict> {\n const at = opts.at ?? { kind: 'now' };\n let integrity: IntegrityVerdict = 'indeterminate';\n let cryptography: CryptographyVerdict = 'unsupported';\n let trust: TrustStatus = 'unknown';\n let trustReason: string | undefined;\n let internal: ParsedCmsInternal | null = null;\n let cms: ParsedCms | null = null;\n\n if (!snapshot.chainValid || signature.coverage === 'malformed') {\n integrity = snapshot.chainValid ? 'invalid' : 'indeterminate';\n } else {\n try {\n const contents = await doc.signatures!.contents(signature.field);\n internal = parseCmsInternal(contents);\n cms = internal.parsed;\n const digest = await doc.signatures!.digest(signature.field, cms.digestAlgorithm);\n const digestMatches = bytesEqual(digest, cms.messageDigest);\n integrity = signature.coverage === 'whole-revision' && digestMatches ? 'valid' : 'invalid';\n } catch (err) {\n // A CMS this engine cannot read is not evidence of tampering.\n integrity =\n err instanceof CmsError && err.reason === 'unsupported' ? 'indeterminate' : 'invalid';\n if (!(err instanceof CmsError)) integrity = 'indeterminate';\n }\n }\n\n if (internal) {\n cryptography = await verifyCmsSignature(internal);\n const chain = await validateChain(internal, opts.trust, at);\n trust = chain.status;\n trustReason = chain.reason;\n }\n\n const modifications = await modificationsOf(doc, snapshot, signature, opts.until ?? 'persisted');\n\n let summary: SignatureVerdict['summary'];\n if (\n integrity === 'invalid' ||\n cryptography === 'invalid' ||\n modifications.verdict === 'forbidden'\n ) {\n summary = 'invalid';\n } else if (\n integrity === 'indeterminate' ||\n cryptography === 'unsupported' ||\n modifications.verdict === 'indeterminate'\n ) {\n summary = 'indeterminate';\n } else {\n summary = trust === 'trusted' ? 'valid' : 'valid-untrusted';\n }\n\n return {\n signature,\n integrity,\n cryptography,\n trust,\n trustReason,\n validatedAt: at,\n modifications,\n summary,\n cms,\n };\n}\n\n/**\n * What changed after the signature, from the engine's revision analysis:\n * the current document judged against the revision the signature sealed\n * (net state; a certification also replays every intervening revision).\n * A signature that seals the last revision is `unchanged` without analysis.\n */\nasync function modificationsOf(\n doc: DocumentHandle,\n snapshot: SignatureSnapshot,\n signature: SignatureDTO,\n until: 'persisted' | 'working-copy',\n): Promise<SignatureVerdict['modifications']> {\n if (signature.revisionIndex === null) {\n return {\n verdict: 'indeterminate',\n detail: 'the signature seals no whole revision',\n basis: 'persisted',\n };\n }\n // \"Nothing after it\" is only knowable from the loaded bytes when unsaved\n // edits are not part of the question.\n if (until === 'persisted' && signature.revisionIndex === snapshot.revisions.length - 1) {\n return { verdict: 'unchanged', basis: 'persisted' };\n }\n try {\n // The analysis reports its own basis: `persisted` when there were no\n // unsaved edits to include, even when the working copy was asked for.\n const analysis = await doc.signatures!.analyze({\n since: { signatureIndex: signature.index },\n until,\n });\n // The explanation comes from the verdict that counts, never from a step\n // the final state has since undone.\n const primary = analysis.current.primary;\n const detail =\n primary && primary.verdict === 'forbidden'\n ? `object ${primary.objectNumber}, ${primary.rule}${primary.detail ? `: ${primary.detail}` : ''}`\n : primary && primary.verdict === 'incomplete'\n ? `object ${primary.objectNumber}: ${primary.detail ?? 'incomplete evidence'}`\n : undefined;\n return {\n verdict: analysis.current.verdict,\n detail,\n basis: analysis.basis.source,\n ...(analysis.later.revisionCount > 0 ? { laterRevisions: analysis.later.revisionCount } : {}),\n ...(analysis.later.undoneObjectNumbers.length > 0 ? { undone: true } : {}),\n };\n } catch (err) {\n return {\n verdict: 'indeterminate',\n detail: `analysis failed: ${(err as Error).message}`,\n basis: 'persisted',\n };\n }\n}\n","import * as asn1js from 'asn1js';\nimport * as pkijs from 'pkijs';\n\nimport { ensureEngine } from '../cms/engine';\nimport { OID, WEBCRYPTO_HASH } from '../cms/oids';\n\n/**\n * A self-signed certificate over a WebCrypto key pair: the shape a personal\n * signer, a test signer, and a demo all need. It is its own trust anchor and\n * nothing else's — Acrobat shows a signature made with it as \"validity\n * unknown\" until the reader trusts the certificate explicitly.\n */\nexport async function selfSignedCertificate(input: {\n publicKey: CryptoKey;\n privateKey: CryptoKey;\n commonName: string;\n hash: 'sha256' | 'sha384' | 'sha512';\n /** Validity in days; default one year. */\n validityDays?: number;\n}): Promise<Uint8Array> {\n ensureEngine();\n const cert = new pkijs.Certificate();\n cert.version = 2;\n const serial = new Uint8Array(8);\n globalThis.crypto.getRandomValues(serial);\n serial[0] &= 0x7f;\n cert.serialNumber = new asn1js.Integer({ valueHex: serial.buffer });\n const name = new pkijs.AttributeTypeAndValue({\n type: OID.commonName,\n value: new asn1js.Utf8String({ value: input.commonName }),\n });\n cert.issuer.typesAndValues.push(name);\n cert.subject.typesAndValues.push(name);\n const now = Date.now();\n cert.notBefore.value = new Date(now - 24 * 3600 * 1000);\n cert.notAfter.value = new Date(now + (input.validityDays ?? 365) * 24 * 3600 * 1000);\n const basicConstraints = new pkijs.BasicConstraints({ cA: true });\n // digitalSignature (bit 0) | keyCertSign (bit 5)\n const keyUsage = new asn1js.BitString({ valueHex: new Uint8Array([0x84]).buffer, unusedBits: 2 });\n cert.extensions = [\n new pkijs.Extension({\n extnID: OID.basicConstraints,\n critical: true,\n extnValue: basicConstraints.toSchema().toBER(false),\n parsedValue: basicConstraints,\n }),\n new pkijs.Extension({ extnID: OID.keyUsage, critical: true, extnValue: keyUsage.toBER(false) }),\n ];\n await cert.subjectPublicKeyInfo.importKey(input.publicKey);\n await cert.sign(input.privateKey, WEBCRYPTO_HASH[input.hash]);\n return new Uint8Array(cert.toSchema(true).toBER(false));\n}\n\n/** The subject common name of a DER certificate, or null when it has none. */\nexport function certificateCommonName(der: Uint8Array): string | null {\n ensureEngine();\n const copy = new Uint8Array(der.byteLength);\n copy.set(der);\n const cert = pkijs.Certificate.fromBER(copy.buffer);\n const cn = cert.subject.typesAndValues.find((entry) => entry.type === OID.commonName);\n const value = cn?.value as { valueBlock?: { value?: unknown } } | undefined;\n const text = value?.valueBlock?.value;\n return typeof text === 'string' && text.length > 0 ? text : null;\n}\n","import type { DigestAlgorithm } from '@embedpdf/engine-core/runtime';\nimport type { RawSigner, RawSignatureAlgorithm } from '../cms/build';\nimport { ensureEngine } from '../cms/engine';\nimport { WEBCRYPTO_HASH } from '../cms/oids';\nimport { selfSignedCertificate } from './self-signed';\n\n/**\n * A `RawSigner` over a WebCrypto private key: the key never leaves the\n * runtime. The algorithm and hash come from the key itself.\n */\nexport function webCryptoSigner(input: {\n privateKey: CryptoKey;\n certificateChain: Uint8Array[];\n /** Required for ECDSA keys (the key carries no hash); ignored for RSA keys. */\n hash?: Exclude<DigestAlgorithm, 'sha1'>;\n}): RawSigner {\n const keyAlgorithm = input.privateKey.algorithm as RsaHashedKeyAlgorithm | EcKeyAlgorithm;\n let algorithm: RawSignatureAlgorithm;\n let hash: Exclude<DigestAlgorithm, 'sha1'>;\n switch (keyAlgorithm.name) {\n case 'RSASSA-PKCS1-v1_5':\n algorithm = 'RSA-PKCS1-v1_5';\n hash = hashFromWebCrypto((keyAlgorithm as RsaHashedKeyAlgorithm).hash.name);\n break;\n case 'RSA-PSS':\n algorithm = 'RSA-PSS';\n hash = hashFromWebCrypto((keyAlgorithm as RsaHashedKeyAlgorithm).hash.name);\n break;\n case 'ECDSA':\n algorithm = 'ECDSA';\n hash = input.hash ?? 'sha256';\n break;\n default:\n throw new Error(`unsupported WebCrypto key algorithm ${keyAlgorithm.name}`);\n }\n return {\n kind: 'raw',\n certificateChain: input.certificateChain,\n algorithm,\n hash,\n async sign(data) {\n const params: AlgorithmIdentifier | RsaPssParams | EcdsaParams =\n algorithm === 'RSA-PSS'\n ? { name: 'RSA-PSS', saltLength: { sha256: 32, sha384: 48, sha512: 64 }[hash] }\n : algorithm === 'ECDSA'\n ? { name: 'ECDSA', hash: WEBCRYPTO_HASH[hash] }\n : { name: 'RSASSA-PKCS1-v1_5' };\n const copy = new Uint8Array(data.byteLength);\n copy.set(data);\n const signature = await globalThis.crypto.subtle.sign(params, input.privateKey, copy);\n return new Uint8Array(signature);\n },\n };\n}\n\nfunction hashFromWebCrypto(name: string): Exclude<DigestAlgorithm, 'sha1'> {\n switch (name) {\n case 'SHA-256':\n return 'sha256';\n case 'SHA-384':\n return 'sha384';\n case 'SHA-512':\n return 'sha512';\n default:\n throw new Error(`unsupported key hash ${name}`);\n }\n}\n\nexport interface TestSigner extends RawSigner {\n /** DER of the self-signed certificate — also the only trust anchor that validates it. */\n readonly certificate: Uint8Array;\n readonly privateKey: CryptoKey;\n}\n\n/**\n * A throwaway signer for tests and demos: a fresh key pair and a\n * self-signed certificate, never persisted. Trust it with\n * `{ anchors: async () => [signer.certificate] }`.\n */\nexport async function createTestSigner(\n opts: {\n commonName?: string;\n algorithm?: 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA';\n hash?: 'sha256' | 'sha384' | 'sha512';\n } = {},\n): Promise<TestSigner> {\n ensureEngine();\n const hash = opts.hash ?? 'sha256';\n const keys = await generateSigningKeyPair(opts.algorithm ?? 'RSA-PKCS1-v1_5', hash, true);\n const certificate = await selfSignedCertificate({\n publicKey: keys.publicKey,\n privateKey: keys.privateKey,\n commonName: opts.commonName ?? 'EmbedPDF test signer',\n hash,\n });\n const signer = webCryptoSigner({\n privateKey: keys.privateKey,\n certificateChain: [certificate],\n hash,\n });\n return { ...signer, certificate, privateKey: keys.privateKey };\n}\n\n/** A WebCrypto signing key pair; `extractable: false` keeps the private key inside the runtime for good. */\nexport async function generateSigningKeyPair(\n algorithm: 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA',\n hash: 'sha256' | 'sha384' | 'sha512',\n extractable: boolean,\n): Promise<CryptoKeyPair> {\n return (await globalThis.crypto.subtle.generateKey(\n algorithm === 'ECDSA'\n ? { name: 'ECDSA', namedCurve: 'P-256' }\n : {\n name: algorithm === 'RSA-PSS' ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5',\n modulusLength: 2048,\n publicExponent: new Uint8Array([1, 0, 1]),\n hash: WEBCRYPTO_HASH[hash],\n },\n extractable,\n ['sign', 'verify'],\n )) as CryptoKeyPair;\n}\n","import type { CmsSigner } from '../sign';\n\n/**\n * A `CmsSigner` over the customer's own signing service or HSM: the engine's\n * digest goes out, a finished detached CMS comes back. Nothing but the\n * digest (and which hash and profile it is for) ever leaves the runtime.\n */\nexport function remoteSigner(input: {\n sign: (request: {\n digest: Uint8Array;\n algorithm: 'sha256' | 'sha384' | 'sha512';\n subFilter: 'adbe.pkcs7.detached' | 'ETSI.CAdES.detached';\n }) => Promise<Uint8Array>;\n}): CmsSigner {\n return {\n kind: 'cms',\n sign: (request) => input.sign(request),\n };\n}\n","import type { RawSigner } from '../cms/build';\nimport { ensureEngine } from '../cms/engine';\nimport { selfSignedCertificate } from './self-signed';\nimport { generateSigningKeyPair, webCryptoSigner } from './webcrypto';\n\n/** What a personal signer keeps: a NON-extractable private key and its self-signed certificate. */\nexport interface PersonalKeyRecord {\n privateKey: CryptoKey;\n certificate: Uint8Array;\n}\n\n/** Where personal keys live. Keys are structured-cloneable, so a store can keep the `CryptoKey` itself. */\nexport interface PersonalKeyStore {\n load(subject: string): Promise<PersonalKeyRecord | null>;\n save(subject: string, record: PersonalKeyRecord): Promise<void>;\n remove(subject: string): Promise<void>;\n}\n\nexport interface PersonalSigner extends RawSigner {\n /** DER of the self-signed certificate — the only anchor that validates it. */\n readonly certificate: Uint8Array;\n readonly subject: string;\n}\n\n/**\n * One self-signed identity per subject (a person's name), persisted in the\n * caller's store and created on first use. The private key is generated\n * non-extractable: it can sign, it can be stored by the browser, it can\n * never be read out. This is Preview's \"sign with your own signature\"\n * experience with real cryptography behind it — a reader who trusts the\n * certificate validates the signature; every other reader sees \"validity\n * unknown\", which is the honest answer for a self-issued identity.\n */\nexport async function personalSigner(input: {\n subject: string;\n store: PersonalKeyStore;\n algorithm?: 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA';\n hash?: 'sha256' | 'sha384' | 'sha512';\n}): Promise<PersonalSigner> {\n ensureEngine();\n const hash = input.hash ?? 'sha256';\n let record = await input.store.load(input.subject);\n if (!record) {\n const keys = await generateSigningKeyPair(input.algorithm ?? 'RSA-PKCS1-v1_5', hash, false);\n const certificate = await selfSignedCertificate({\n publicKey: keys.publicKey,\n privateKey: keys.privateKey,\n commonName: input.subject,\n hash,\n });\n record = { privateKey: keys.privateKey, certificate };\n await input.store.save(input.subject, record);\n }\n const signer = webCryptoSigner({\n privateKey: record.privateKey,\n certificateChain: [record.certificate],\n hash,\n });\n return { ...signer, certificate: record.certificate, subject: input.subject };\n}\n\n/** An in-memory store: tests, and runtimes without IndexedDB. */\nexport function memoryKeyStore(): PersonalKeyStore {\n const records = new Map<string, PersonalKeyRecord>();\n return {\n load: async (subject) => records.get(subject) ?? null,\n save: async (subject, record) => {\n records.set(subject, record);\n },\n remove: async (subject) => {\n records.delete(subject);\n },\n };\n}\n\n/**\n * An IndexedDB store: the browser keeps the non-extractable `CryptoKey`\n * itself (structured clone), so the key survives reloads without ever\n * existing as bytes.\n */\nexport function indexedDbKeyStore(\n dbName: string,\n opts: { storeName?: string } = {},\n): PersonalKeyStore {\n const storeName = opts.storeName ?? 'keys';\n let opening: Promise<IDBDatabase> | null = null;\n const open = (): Promise<IDBDatabase> => {\n opening ??= new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, 1);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(storeName)) {\n request.result.createObjectStore(storeName);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('indexedDB open failed'));\n });\n return opening;\n };\n const run = async <T>(\n mode: IDBTransactionMode,\n op: (store: IDBObjectStore) => IDBRequest<T>,\n ): Promise<T> => {\n const db = await open();\n return new Promise<T>((resolve, reject) => {\n const tx = db.transaction(storeName, mode);\n const request = op(tx.objectStore(storeName));\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('indexedDB request failed'));\n });\n };\n return {\n load: async (subject) =>\n ((await run('readonly', (s) => s.get(subject))) as PersonalKeyRecord | undefined) ?? null,\n save: (subject, record) =>\n run('readwrite', (s) => s.put(record, subject)).then(() => undefined),\n remove: (subject) => run('readwrite', (s) => s.delete(subject)).then(() => undefined),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,328 @@
1
+ import { DigestAlgorithm, DocumentHandle, SignatureCompleteResult, SignatureDTO, SignaturePrepareInput, SignaturePrepared, SignatureSubFilter } from "@embedpdf/engine-core/runtime";
2
+ import * as pkijs from "pkijs";
3
+ //#region src/cms/parse.d.ts
4
+ /** What a detached CMS says about itself. Nothing here is verified. */
5
+ interface ParsedCms {
6
+ digestAlgorithm: DigestAlgorithm;
7
+ /** The `message-digest` signed attribute: what the signer claims the content hashes to. */
8
+ messageDigest: Uint8Array;
9
+ /** The CMS `signing-time` signed attribute, when present (PAdES forbids it; the PDF's `/M` is the claim there). */
10
+ signingTime: Date | null;
11
+ /** DER of the signer's certificate. */
12
+ signerCertificate: Uint8Array;
13
+ /** DER of every certificate carried by the CMS, signer first. */
14
+ certificates: Uint8Array[];
15
+ /** The signature algorithm OID (`1.2.840.113549.1.1.11` = sha256WithRSAEncryption, …). */
16
+ signatureAlgorithm: string;
17
+ /** The ESS `signing-certificate-v2` attribute is present (a CAdES requirement). */
18
+ signingCertificateV2: boolean;
19
+ /** DER of an RFC 3161 timestamp token carried as an unsigned attribute, when present. */
20
+ timestampToken: Uint8Array | null;
21
+ }
22
+ declare class CmsError extends Error {
23
+ readonly reason: 'malformed' | 'unsupported';
24
+ constructor(reason: 'malformed' | 'unsupported', message: string);
25
+ }
26
+ interface ParsedCmsInternal {
27
+ parsed: ParsedCms;
28
+ signedData: pkijs.SignedData;
29
+ signerInfo: pkijs.SignerInfo;
30
+ signerCertificate: pkijs.Certificate;
31
+ certificates: pkijs.Certificate[];
32
+ /** DER of the signing-certificate-v2 attribute's first certHash, when present. */
33
+ essCertHash: Uint8Array | null;
34
+ }
35
+ /** Parse a detached CMS SignedData with exactly one signer. Throws `CmsError`. */
36
+ declare function parseDetachedCms(cms: Uint8Array): ParsedCms;
37
+ //#endregion
38
+ //#region src/cms/verify.d.ts
39
+ type CryptographyVerdict = 'valid' | 'invalid' | 'unsupported';
40
+ /**
41
+ * Does the CMS's signature verify with the certificate it names, over the
42
+ * attributes it signed? Says nothing about the content: the message digest
43
+ * is compared by the caller (`validateSignatures`, `verifyForCompletion`).
44
+ * `unsupported` means the algorithm is unknown to this engine, which is
45
+ * never `invalid`.
46
+ */
47
+ declare function verifyCmsSignature(cms: Uint8Array | ParsedCmsInternal): Promise<CryptographyVerdict>;
48
+ //#endregion
49
+ //#region src/cms/build.d.ts
50
+ type SignatureProfile = 'pkcs7' | 'cades-b';
51
+ type RawSignatureAlgorithm = 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA';
52
+ /**
53
+ * Something that signs bytes with a private key it never hands out: a
54
+ * WebCrypto key, an HSM, a smart card. `sign` receives the DER of the CMS
55
+ * signed attributes and returns the raw signature (for ECDSA: `r || s`,
56
+ * as WebCrypto produces it; the CMS encoding is done here).
57
+ */
58
+ interface RawSigner {
59
+ readonly kind: 'raw';
60
+ /** DER certificates, the signer's first, then its issuers. */
61
+ readonly certificateChain: Uint8Array[];
62
+ readonly algorithm: RawSignatureAlgorithm;
63
+ readonly hash: Exclude<DigestAlgorithm, 'sha1'>;
64
+ sign(signedAttributesDer: Uint8Array): Promise<Uint8Array>;
65
+ }
66
+ interface BuildDetachedCmsInput {
67
+ /** The digest the engine prepared (over the `/ByteRange`). */
68
+ digest: Uint8Array;
69
+ hash: Exclude<DigestAlgorithm, 'sha1'>;
70
+ profile: SignatureProfile;
71
+ signer: RawSigner;
72
+ /** `pkcs7` only: the CMS `signing-time` attribute. PAdES (`cades-b`) never carries one. */
73
+ signingTime?: Date;
74
+ }
75
+ /**
76
+ * Build a detached CMS SignedData around a raw signature: content-type,
77
+ * message-digest, and for CAdES-B the ESS signing-certificate-v2 attribute;
78
+ * the signer's certificate chain; one SignerInfo identified by issuer and
79
+ * serial number.
80
+ */
81
+ declare function buildDetachedCms(input: BuildDetachedCmsInput): Promise<Uint8Array>;
82
+ //#endregion
83
+ //#region src/gate.d.ts
84
+ type CompletionRefusal = 'malformed' | 'too-large' | 'algorithm-mismatch' | 'digest-mismatch' | 'signature-invalid' | 'profile-violation';
85
+ type CompletionGate = {
86
+ ok: true;
87
+ } | {
88
+ ok: false;
89
+ reason: CompletionRefusal;
90
+ detail: string;
91
+ };
92
+ /**
93
+ * The gate between a signer's answer and `signatures.complete`: the CMS
94
+ * must fit the reserved room, hash with the prepared algorithm, carry the
95
+ * prepared digest as its message digest, verify with its own certificate,
96
+ * and match the profile the `/SubFilter` promises (CAdES: ESS
97
+ * signing-certificate-v2 present, no CMS signing-time). A bad CMS never
98
+ * reaches the document.
99
+ */
100
+ declare function verifyForCompletion(input: {
101
+ cms: Uint8Array;
102
+ prepared: SignaturePrepared;
103
+ profile: SignatureProfile;
104
+ }): Promise<CompletionGate>;
105
+ //#endregion
106
+ //#region src/trust.d.ts
107
+ /**
108
+ * Where trust comes from. The engine has no opinion: an application
109
+ * supplies its anchors (a company root, a national trust list, the AATL).
110
+ */
111
+ interface TrustPort {
112
+ /** DER certificates trusted as anchors. */
113
+ anchors(): Promise<Uint8Array[]>;
114
+ }
115
+ /**
116
+ * When a chain is judged. `now` is the honest default; `timestamp` is only
117
+ * legitimate for a verified RFC 3161 token, which is why it carries the
118
+ * proof flag. The signature's own `/M` is a claim and never a validation
119
+ * time.
120
+ */
121
+ type ValidationTime = {
122
+ kind: 'now';
123
+ } | {
124
+ kind: 'timestamp';
125
+ genTime: Date;
126
+ tokenVerified: true;
127
+ };
128
+ type TrustStatus = 'trusted' | 'untrusted' | 'unknown';
129
+ interface TrustVerdict {
130
+ status: TrustStatus;
131
+ /** Why, when not trusted. */
132
+ reason?: string;
133
+ /** DER certificates of the validated path, anchor last, when trusted. */
134
+ path?: Uint8Array[];
135
+ }
136
+ /**
137
+ * Build and validate the certificate path from the CMS's signer to one
138
+ * of the port's anchors at `at`. No port, or a port with no anchors, is
139
+ * `unknown`: nothing was checked, and nothing was refused.
140
+ */
141
+ declare function validateChain(cms: Uint8Array | ParsedCmsInternal, trust: TrustPort | null | undefined, at?: ValidationTime): Promise<TrustVerdict>;
142
+ //#endregion
143
+ //#region src/sign.d.ts
144
+ /** A service that turns a digest into a finished detached CMS (most hosted signing APIs). */
145
+ interface CmsSigner {
146
+ readonly kind: 'cms';
147
+ sign(input: {
148
+ digest: Uint8Array;
149
+ algorithm: Exclude<DigestAlgorithm, 'sha1'>;
150
+ subFilter: SignatureSubFilter;
151
+ }): Promise<Uint8Array>;
152
+ }
153
+ type SignerPort = CmsSigner | RawSigner;
154
+ type SignInput = Omit<SignaturePrepareInput, 'kind'> & {
155
+ signer: SignerPort;
156
+ };
157
+ declare class SigningError extends Error {
158
+ readonly reason: CompletionRefusal;
159
+ constructor(reason: CompletionRefusal, detail: string);
160
+ }
161
+ declare function profileFor(subFilter: SignatureSubFilter): SignatureProfile;
162
+ /**
163
+ * Sign a field in one call: `prepare` on the engine, the signer turns the
164
+ * digest into a CMS (or a raw signature this package wraps), the gate
165
+ * checks the CMS matches what was prepared, `complete` installs it. Any
166
+ * failure aborts the candidate and rethrows; the document is never left
167
+ * with a pending signing.
168
+ */
169
+ declare function sign(doc: DocumentHandle, input: SignInput): Promise<SignatureCompleteResult>;
170
+ //#endregion
171
+ //#region src/verdict.d.ts
172
+ type IntegrityVerdict = 'valid' | 'invalid' | 'indeterminate';
173
+ type ModificationsVerdict = 'unchanged' | 'permitted' | 'forbidden' | 'indeterminate';
174
+ /**
175
+ * Four independent facts and a summary, never one boolean — the shape
176
+ * Acrobat reports ("signature valid", "signer unknown", "document changed,
177
+ * changes allowed") and the shape a UI can explain.
178
+ */
179
+ interface SignatureVerdict {
180
+ signature: SignatureDTO;
181
+ /** The sealed bytes are intact: whole-revision coverage and the CMS message digest matches them. */
182
+ integrity: IntegrityVerdict;
183
+ /** The CMS verifies with its own certificate. */
184
+ cryptography: CryptographyVerdict;
185
+ /** The certificate chains to one of the caller's anchors. */
186
+ trust: TrustStatus;
187
+ trustReason?: string;
188
+ validatedAt: ValidationTime;
189
+ /**
190
+ * What changed after the signature, and which bytes were judged: the loaded
191
+ * file (`persisted`), or the session's unsaved edits snapshotted as one
192
+ * more revision (`working-copy` — only when such edits existed).
193
+ */
194
+ modifications: {
195
+ verdict: ModificationsVerdict;
196
+ detail?: string;
197
+ basis: 'persisted' | 'working-copy';
198
+ /** Revisions appended after the one this signature sealed (present when there are any). */
199
+ laterRevisions?: number;
200
+ /** Some object was changed after the signature and holds the sealed value again. */
201
+ undone?: boolean;
202
+ };
203
+ /** `invalid` only on positive evidence; `indeterminate` whenever a fact could not be established. */
204
+ summary: 'valid' | 'valid-untrusted' | 'invalid' | 'indeterminate';
205
+ cms: ParsedCms | null;
206
+ }
207
+ interface ValidateSignaturesOptions {
208
+ trust?: TrustPort | null;
209
+ at?: ValidationTime;
210
+ /**
211
+ * `persisted` (default): the loaded bytes — what a file on disk says.
212
+ * `working-copy`: unsaved edits are judged too, as the revision a save would
213
+ * write — what the file a save produces will say. A viewer shows this one.
214
+ */
215
+ until?: 'persisted' | 'working-copy';
216
+ }
217
+ /**
218
+ * Validate every signature of a document: integrity from the engine's
219
+ * bytes, cryptography from the CMS, trust from the caller's anchors.
220
+ */
221
+ declare function validateSignatures(doc: DocumentHandle, opts?: ValidateSignaturesOptions): Promise<SignatureVerdict[]>;
222
+ //#endregion
223
+ //#region src/signers/webcrypto.d.ts
224
+ /**
225
+ * A `RawSigner` over a WebCrypto private key: the key never leaves the
226
+ * runtime. The algorithm and hash come from the key itself.
227
+ */
228
+ declare function webCryptoSigner(input: {
229
+ privateKey: CryptoKey;
230
+ certificateChain: Uint8Array[];
231
+ /** Required for ECDSA keys (the key carries no hash); ignored for RSA keys. */
232
+ hash?: Exclude<DigestAlgorithm, 'sha1'>;
233
+ }): RawSigner;
234
+ interface TestSigner extends RawSigner {
235
+ /** DER of the self-signed certificate — also the only trust anchor that validates it. */
236
+ readonly certificate: Uint8Array;
237
+ readonly privateKey: CryptoKey;
238
+ }
239
+ /**
240
+ * A throwaway signer for tests and demos: a fresh key pair and a
241
+ * self-signed certificate, never persisted. Trust it with
242
+ * `{ anchors: async () => [signer.certificate] }`.
243
+ */
244
+ declare function createTestSigner(opts?: {
245
+ commonName?: string;
246
+ algorithm?: 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA';
247
+ hash?: 'sha256' | 'sha384' | 'sha512';
248
+ }): Promise<TestSigner>;
249
+ /** A WebCrypto signing key pair; `extractable: false` keeps the private key inside the runtime for good. */
250
+ declare function generateSigningKeyPair(algorithm: 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA', hash: 'sha256' | 'sha384' | 'sha512', extractable: boolean): Promise<CryptoKeyPair>;
251
+ //#endregion
252
+ //#region src/signers/remote.d.ts
253
+ /**
254
+ * A `CmsSigner` over the customer's own signing service or HSM: the engine's
255
+ * digest goes out, a finished detached CMS comes back. Nothing but the
256
+ * digest (and which hash and profile it is for) ever leaves the runtime.
257
+ */
258
+ declare function remoteSigner(input: {
259
+ sign: (request: {
260
+ digest: Uint8Array;
261
+ algorithm: 'sha256' | 'sha384' | 'sha512';
262
+ subFilter: 'adbe.pkcs7.detached' | 'ETSI.CAdES.detached';
263
+ }) => Promise<Uint8Array>;
264
+ }): CmsSigner;
265
+ //#endregion
266
+ //#region src/signers/personal.d.ts
267
+ /** What a personal signer keeps: a NON-extractable private key and its self-signed certificate. */
268
+ interface PersonalKeyRecord {
269
+ privateKey: CryptoKey;
270
+ certificate: Uint8Array;
271
+ }
272
+ /** Where personal keys live. Keys are structured-cloneable, so a store can keep the `CryptoKey` itself. */
273
+ interface PersonalKeyStore {
274
+ load(subject: string): Promise<PersonalKeyRecord | null>;
275
+ save(subject: string, record: PersonalKeyRecord): Promise<void>;
276
+ remove(subject: string): Promise<void>;
277
+ }
278
+ interface PersonalSigner extends RawSigner {
279
+ /** DER of the self-signed certificate — the only anchor that validates it. */
280
+ readonly certificate: Uint8Array;
281
+ readonly subject: string;
282
+ }
283
+ /**
284
+ * One self-signed identity per subject (a person's name), persisted in the
285
+ * caller's store and created on first use. The private key is generated
286
+ * non-extractable: it can sign, it can be stored by the browser, it can
287
+ * never be read out. This is Preview's "sign with your own signature"
288
+ * experience with real cryptography behind it — a reader who trusts the
289
+ * certificate validates the signature; every other reader sees "validity
290
+ * unknown", which is the honest answer for a self-issued identity.
291
+ */
292
+ declare function personalSigner(input: {
293
+ subject: string;
294
+ store: PersonalKeyStore;
295
+ algorithm?: 'RSA-PKCS1-v1_5' | 'RSA-PSS' | 'ECDSA';
296
+ hash?: 'sha256' | 'sha384' | 'sha512';
297
+ }): Promise<PersonalSigner>;
298
+ /** An in-memory store: tests, and runtimes without IndexedDB. */
299
+ declare function memoryKeyStore(): PersonalKeyStore;
300
+ /**
301
+ * An IndexedDB store: the browser keeps the non-extractable `CryptoKey`
302
+ * itself (structured clone), so the key survives reloads without ever
303
+ * existing as bytes.
304
+ */
305
+ declare function indexedDbKeyStore(dbName: string, opts?: {
306
+ storeName?: string;
307
+ }): PersonalKeyStore;
308
+ //#endregion
309
+ //#region src/signers/self-signed.d.ts
310
+ /**
311
+ * A self-signed certificate over a WebCrypto key pair: the shape a personal
312
+ * signer, a test signer, and a demo all need. It is its own trust anchor and
313
+ * nothing else's — Acrobat shows a signature made with it as "validity
314
+ * unknown" until the reader trusts the certificate explicitly.
315
+ */
316
+ declare function selfSignedCertificate(input: {
317
+ publicKey: CryptoKey;
318
+ privateKey: CryptoKey;
319
+ commonName: string;
320
+ hash: 'sha256' | 'sha384' | 'sha512';
321
+ /** Validity in days; default one year. */
322
+ validityDays?: number;
323
+ }): Promise<Uint8Array>;
324
+ /** The subject common name of a DER certificate, or null when it has none. */
325
+ declare function certificateCommonName(der: Uint8Array): string | null;
326
+ //#endregion
327
+ export { type BuildDetachedCmsInput, CmsError, type CmsSigner, type CompletionGate, type CompletionRefusal, type CryptographyVerdict, type IntegrityVerdict, type ModificationsVerdict, type ParsedCms, type PersonalKeyRecord, type PersonalKeyStore, type PersonalSigner, type RawSignatureAlgorithm, type RawSigner, type SignInput, type SignatureProfile, type SignatureVerdict, type SignerPort, SigningError, type TestSigner, type TrustPort, type TrustStatus, type TrustVerdict, type ValidateSignaturesOptions, type ValidationTime, buildDetachedCms, certificateCommonName, createTestSigner, generateSigningKeyPair, indexedDbKeyStore, memoryKeyStore, parseDetachedCms, personalSigner, profileFor, remoteSigner, selfSignedCertificate, sign, validateChain, validateSignatures, verifyCmsSignature, verifyForCompletion, webCryptoSigner };
328
+ //# sourceMappingURL=index.d.cts.map