Standard Security Handler V=4, R=4 — PDF 1.6 legacy (RC4-128 / AES-128-CBC).
Module pdfStandardV4 | Source packages/front/office/pdf/src/crypto/standardV4.js | Deps pdfErrors, aes, cbc, bitArray | Worker-safe yes
Implements ISO 32000-1 §7.6.3 Algorithm 2 (128-bit file-key derivation, 50
rounds of MD5), Algorithm 3 (/O), Algorithm 5 (/U), and per-object key
derivation for the two V=4 crypt filter methods selectable via /CF /StdCF:
- AESV2 — AES-128-CBC; per-object key =
MD5(fileKey ‖ objId(3 LE) ‖ gen(2 LE) ‖ "sAlT")truncated tomin(fileKey.len + 5, 16); ciphertext is IV-prefixed, PKCS#7 padded. - V2 — RC4-128; per-object key =
MD5(fileKey ‖ objId(3 LE) ‖ gen(2 LE))truncated the same way.
MD5 and RC4 are implemented locally (this handler predates fw's hash/cipher
family and both primitives are legacy-only) — aes/cbc are the only fw
crypto deps, for the AESV2 path.
Resolve
const v4 = runtime.resolve('pdfStandardV4');
// Returns: { tryPassword, decryptString, decryptStream,
// decryptEmbeddedFile, encryptEmbeddedFile,
// encryptString, encryptStream,
// buildOU, buildPerms,
// computeFileKey, computeU, computeO,
// objectKey, rc4, md5, padPassword, PASSWORD_PADDING }
API
| Method | Signature | Returns |
|---|---|---|
tryPassword |
(typedEncrypt: { O, U, P, idFirst, EncryptMetadata? }, password, isOwner: boolean) => { fileEncryptionKey: Uint8Array | null } |
null FEK on rejection; recovers the user password from /O first when isOwner. |
decryptString |
(typedEncrypt: { method }, fek, objNum, gen, ciphertext) => Uint8Array |
Dispatches on typedEncrypt.method ('AESV2' or 'V2'/'RC4'). |
decryptStream |
same signature as decryptString |
Alias — identical implementation. |
decryptEmbeddedFile |
same signature as decryptString |
Alias for the /EFF-resolved method (ISO 32000-1 §7.6.5). |
encryptString |
(typedEncrypt: { method }, fek, objNum, gen, plaintext, iv?) => Uint8Array |
iv (16 bytes) required for 'AESV2'; ignored for RC4. |
encryptStream |
same signature as encryptString |
Alias — identical implementation. |
encryptEmbeddedFile |
same signature as encryptString |
Alias for the /EFF write path. |
buildOU |
(ownerPassword, userPassword, P, idFirst: Uint8Array, encryptMetadata) => { O, U, fek } |
Builds a fresh /O + /U pair and the derived 16-byte file key. |
buildPerms |
() => null |
No-op — /Perms (Algorithm 8) is V≥5 only; kept for handler-dispatch symmetry with V5/V6. |
computeFileKey / computeU / computeO |
internal Algorithm 2/5/3 primitives | Exposed for testing/interop. |
objectKey |
(fileKey, objNum, gen, isAes: boolean) => Uint8Array |
Per-object key derivation. |
rc4 / md5 |
stream-cipher / hash primitives | Exposed for testing. |
padPassword |
(password: string | Uint8Array) => Uint8Array |
The 32-byte padded password of Algorithm 2 step (a). Exposed for testing/interop. |
PASSWORD_PADDING |
Uint8Array (32 bytes) |
The Algorithm 2 padding string. |
Examples
Password check then decrypt (read side)
const v4 = runtime.resolve('pdfStandardV4');
const r = v4.tryPassword(
{ O, U, P, idFirst, EncryptMetadata: true }, 'user', false);
if (!r.fileEncryptionKey) throw new Error('bad password');
const plain = v4.decryptStream(
{ method: 'AESV2' }, r.fileEncryptionKey, objNum, gen, encryptedBytes);
Building /O + /U for a fresh document (write side)
const v4 = runtime.resolve('pdfStandardV4');
const { O, U, fek } = v4.buildOU('owner', 'user', -4, idFirst, true);
This is the handler pdfEncryptedWriter selects internally for version: 4, revision: 4.
Errors
All thrown as EncryptionError.
| Code | When |
|---|---|
pdf/crypto/v4/missing-fw |
aes/cbc/bitArray fw modules unavailable (thrown at module construction). |
pdf/crypto/v4/bad-password |
Password argument is neither string nor Uint8Array. |
pdf/crypto/v4/bad-O |
/O is not a 32-byte Uint8Array (computeFileKey). |
pdf/crypto/v4/bad-id |
First /ID element missing/invalid (computeFileKey, buildOU). |
pdf/crypto/v4/bad-O-U |
/O or /U not exactly 32 bytes (tryPassword). |
pdf/crypto/v4/bad-string |
AESV2 ciphertext shorter than 16 bytes (missing IV). |
pdf/crypto/v4/bad-method |
method neither 'AESV2' nor 'V2'/'RC4' (decrypt or encrypt side). |
pdf/crypto/v4/encrypt-bad-input |
encryptString plaintext is not a Uint8Array. |
pdf/crypto/v4/encrypt-bad-iv |
AESV2 encrypt iv is not exactly 16 bytes. |
pdf/crypto/v4/bad-ciphertext-len / bad-pt-len |
AES-CBC input length not a multiple of 16. |
pdf/crypto/v4/aes-schedule-failed / aes-encrypt-schedule |
fw AES-128 key schedule failed. |
pdf/crypto/v4/cbc-decrypt-failed / cbc-encrypt-failed |
fw CBC operation failed. |
See also
pdfEncryptedWriter— selects this handler forversion: 4.pdfStandardV5·pdfStandardV6— modern (V=5) handlers.pdfSecurity— read-side dispatch.