package pdf import "core:crypto" import "core:crypto/aes" import "core:crypto/legacy/md5" import "core:crypto/sha2" // The standard security handler (ISO 32000-2, 7.6.4). // // Decryption is transparent: `crypt_init` derives the file encryption key once, // and the parser then routes every string and every stream payload through // `decrypt_string` / `decrypt_stream` as it reads them, so nothing above this // file ever sees ciphertext. // // Two things are deliberately never decrypted, because they are the inputs the // key was derived from: the /Encrypt dictionary itself, and the /ID strings in // the trailer. A third is never encrypted in the first place, because it has // to be readable before the /Encrypt dictionary it points at can be found: a // cross-reference stream (ISO 32000-2, 7.5.8.2). A fourth is not decrypted // here because it already was: anything that came out of an object stream, // which is decrypted once as a whole when its container is. Those objects // carry Ref{0,0}, and that is the signal used below. // Crypt_Method is what a crypt filter does to bytes. `Identity` covers both // /Identity and a filter with /CFM /None -- "hand the bytes back untouched". Crypt_Method :: enum u8 { Identity, Rc4, Aes_128, Aes_256, } // Password_Kind records which of the two passwords the caller supplied. // A file encrypted with an empty user password -- "opens without prompting, // but you may not print it" -- authenticates as `User`. Password_Kind :: enum u8 { None, User, Owner, } // Permission is one bit of the /P entry, numbered as the format numbers them // minus one: /P bit position 3 (printing) is bit index 2 here. // ISO 32000-2, table 22. Permission :: enum u32 { Print = 2, Modify = 3, Copy = 4, Annotate = 5, Fill_Forms = 8, Extract = 9, // extraction for accessibility Assemble = 10, Print_High_Res = 11, } Permissions :: bit_set[Permission; u32] // Encryption is the decrypted state of the standard security handler: what was // negotiated, and the file encryption key everything else is derived from. // It lives in the Document's arena and is reclaimed with it. Encryption :: struct { // The /Encrypt dictionary's own object, so it can be excluded from // decryption if anything asks for it a second time. {0,0} when the // dictionary was written directly in the trailer. ref: Ref, version: int, // /V revision: int, // /R key_bits: int, // effective file key length, in bits // /P, as the signed 32-bit value the format defines. Stored signed because // almost every file writes it negative (bits 13..32 are all 1s). permission_bits: i32, encrypt_metadata: bool, // /EncryptMetadata authenticated_as: Password_Kind, stream_method: Crypt_Method, // /StmF string_method: Crypt_Method, // /StrF embedded_method: Crypt_Method, // /EFF // The file encryption key: 5..16 bytes for RC4 and AES-128, 32 for // AES-256. Zeroed by `crypt_destroy`. key: []byte, } // The padding string of ISO 32000-2, 7.6.4.3.2 (Algorithm 2, step a). Every // password shorter than 32 bytes is extended with a prefix of it. @(private, rodata) CRYPT_PAD := [32]byte{ 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A, } // "sAlT", appended to the per-object key input for AES-128 so that a file // cannot be downgraded from AESV2 to V2 with the same key (7.6.3.1). @(private, rodata) CRYPT_AES_SALT := [4]byte{0x73, 0x41, 0x6C, 0x54} // --------------------------------------------------------------------------- // Setup // --------------------------------------------------------------------------- // crypt_init reads the trailer's /Encrypt dictionary and derives the file // encryption key from `password`. // // The password is tried as the user password first and then as the owner // password, because the empty user password -- a document that opens without // prompting but restricts printing or copying -- is by far the common case. // An unencrypted document clears `doc.encrypt` and returns .None. // // Internal: this is a step of `open_bytes`, not an entry point. There is no // public equivalent because there is nothing a caller could call it on -- a // document whose password was refused is closed before `open_bytes` returns, // so no locked `^Document` ever escapes the package. Retrying with a second // password means calling `open_bytes` again with a different // `Open_Options.password`; the file bytes are already in memory, and reusing // the Document would mean re-decrypting objects the old key already cached. @(private) crypt_init :: proc(doc: ^Document, password: []byte) -> Error { if doc == nil { return .Invalid_Argument } doc.encrypt = nil raw := dict_get_raw(doc.trailer, "Encrypt") if raw == nil { return .None } enc_ref, _ := to_ref(raw) // Resolving happens while doc.encrypt is still nil, which is exactly why // the /Encrypt dictionary's own strings come back untouched. ed, is_dict := to_dict(resolve(doc, raw)) if !is_dict || ed == nil { // /Encrypt is present, so the file is encrypted, and we cannot read the // dictionary that says how. There is nothing to degrade to: carrying on // would hand ciphertext to every layer above. return .Bad_Encrypt_Dict } // A missing /Filter is assumed to be the standard handler; a named one that // is not /Standard is a plug-in we do not have. if f := dict_name(doc, ed, "Filter", "Standard"); f != "Standard" { return .Unsupported_Encryption } version := int(dict_int(doc, ed, "V", 0)) revision := int(dict_int(doc, ed, "R", 0)) switch { case version <= 0: version = 1 // V3 is an unpublished Adobe variant with a 40..128 bit key; every viewer // treats it as V2, and the files that use it decrypt correctly that way. case version == 3: version = 2 } if version > 5 { return .Unsupported_Encryption } if revision == 0 { switch version { case 1: revision = 2 case 2: revision = 3 case 4: revision = 4 case: revision = 6 } } if revision < 2 || revision > 6 { return .Unsupported_Encryption } // V5 without R5/R6, or R5/R6 without V5, is a file that contradicts itself: // /V 5 promises a 32-byte key and only R5/R6 can produce one. The /U value // settles it -- 48 bytes is the R5/R6 layout, 32 the older one -- because // that is the field the algorithms actually consume. u_len := len(dict_bytes(doc, ed, "U")) if revision >= 5 && version != 5 { version = 5 add_repair(doc, .Bad_Filter_Data, 0, enc_ref) } else if version == 5 && revision < 5 { if u_len >= 48 { revision = 6 } else { version = 4 } add_repair(doc, .Bad_Filter_Data, 0, enc_ref) } enc := new(Encryption, doc.allocator) enc.ref = enc_ref enc.version = version enc.revision = revision enc.encrypt_metadata = dict_bool(doc, ed, "EncryptMetadata", true) // /P is a 32-bit field. Files write it as a negative integer, as the // equivalent unsigned value, and occasionally as a real; take the low 32 // bits of whatever arrived and read them as signed. p_raw := dict_int(doc, ed, "P", -1) p_u := u32(transmute(u64)p_raw & 0xffff_ffff) enc.permission_bits = transmute(i32)p_u // Crypt filters (V4 and V5). Below that, everything is RC4 with the file key. cf_bits := 0 if version >= 4 { cf := dict_dict(doc, ed, "CF") stmf := dict_name(doc, ed, "StmF", "Identity") strf := dict_name(doc, ed, "StrF", "Identity") // /EFF defaults to the stream filter, so an unspecified embedded-file // filter never leaves attachments in the clear when streams are not. eff := dict_name(doc, ed, "EFF", stmf) m, b, err := crypt_filter_lookup(doc, cf, stmf, enc_ref) if err != .None { return err } enc.stream_method = m if b > cf_bits { cf_bits = b } m, b, err = crypt_filter_lookup(doc, cf, strf, enc_ref) if err != .None { return err } enc.string_method = m if b > cf_bits { cf_bits = b } m, b, err = crypt_filter_lookup(doc, cf, eff, enc_ref) if err != .None { return err } enc.embedded_method = m if b > cf_bits { cf_bits = b } // /CF is required for V5, so a V5 file that names no filter at all is // broken and the only thing it can have meant is AES-256. An explicit // /Identity is left alone: that one is a deliberate configuration. if version == 5 { if !dict_has(ed, "StmF") && enc.stream_method == .Identity { enc.stream_method = .Aes_256 add_repair(doc, .Bad_Filter_Data, 0, enc_ref) } if !dict_has(ed, "StrF") && enc.string_method == .Identity { enc.string_method = .Aes_256 } if !dict_has(ed, "EFF") && enc.embedded_method == .Identity { enc.embedded_method = enc.stream_method } } } else { enc.stream_method = .Rc4 enc.string_method = .Rc4 enc.embedded_method = .Rc4 } // How long the file key is. /Length is in bits; a crypt filter's /Length is // in bytes per the spec but in bits in plenty of files, so it is normalised // in crypt_filter_lookup. key_bits := 40 if version >= 2 { key_bits = int(dict_int(doc, ed, "Length", 40)) } if version == 4 && cf_bits > 0 { key_bits = cf_bits } uses_aes_128 := enc.stream_method == .Aes_128 || enc.string_method == .Aes_128 || enc.embedded_method == .Aes_128 n: int switch { case version == 5: n = 32 // An AES-128 filter needs a full 16-byte key whatever /Length claims, and // files that pair /AESV2 with /Length 40 exist. A 10-byte key is not a // legal AES key at all, so the method wins over the declared length. case uses_aes_128: n = 16 // V1 is 40-bit by definition, and so is R2 whatever /Length says // (7.6.4.3.2, step i). case version == 1, revision == 2: n = 5 case: // The clamp is what keeps a /Length of 2^31 from sizing a buffer. n = clamp(key_bits / 8, 5, 16) } enc.key_bits = n * 8 enc.key = make([]byte, n, doc.allocator) id0 := crypt_first_id(doc) err: Error if revision >= 5 { err = crypt_auth_r56(doc, ed, enc, password) } else { err = crypt_auth_r234(doc, ed, enc, password, id0) } if err != .None { return err } doc.encrypt = enc return .None } // crypt_destroy wipes the file encryption key and detaches the handler. The // memory itself belongs to the Document's arena and is released by `close`. // // Internal: `close` calls this, and calling it on a Document still in use // would leave every later stream decoding to ciphertext. @(private) crypt_destroy :: proc(doc: ^Document) { if doc == nil || doc.encrypt == nil { return } if len(doc.encrypt.key) > 0 { crypto.zero_explicit(raw_data(doc.encrypt.key), len(doc.encrypt.key)) } doc.encrypt.authenticated_as = .None doc.encrypt = nil } // --------------------------------------------------------------------------- // Public accessors // --------------------------------------------------------------------------- // document_is_encrypted reports whether the file carried an /Encrypt // dictionary. It stays true after a successful open: the document was // encrypted, it is simply readable now. document_is_encrypted :: proc(doc: ^Document) -> bool { return doc != nil && doc.encrypt != nil } // encryption_permissions returns what the document's /P bits allow. // // An unencrypted document, and one opened with the owner password, allow // everything: the owner is not subject to the restrictions they set // (ISO 32000-2, 7.6.4.2). encryption_permissions :: proc(doc: ^Document) -> Permissions { all := Permissions{.Print, .Modify, .Copy, .Annotate, .Fill_Forms, .Extract, .Assemble, .Print_High_Res} if doc == nil || doc.encrypt == nil { return all } if doc.encrypt.authenticated_as == .Owner { return all } bits := transmute(u32)doc.encrypt.permission_bits out: Permissions for p in Permission { if bits & (u32(1) << u32(p)) != 0 { out += {p} } } // Revision 2 only defined bits 3..6; the rest were reserved and set to 1, // so reporting them would be meaningless. if doc.encrypt.revision == 2 { out &= {.Print, .Modify, .Copy, .Annotate} } return out } // encryption_method_name names the scheme in use, for a UI or a log line. // Not stable enough to parse. encryption_method_name :: proc(doc: ^Document) -> string { if doc == nil || doc.encrypt == nil { return "none" } // Streams in the clear but strings encrypted is a real configuration -- a // file that protects only its attachments -- so fall back to reporting the // string filter rather than calling the whole document unencrypted. if doc.encrypt.stream_method == .Identity { if doc.encrypt.string_method == .Identity { return "none (identity crypt filter)" } return crypt_method_name(doc.encrypt.string_method, doc.encrypt.key_bits) } return crypt_method_name(doc.encrypt.stream_method, doc.encrypt.key_bits) } @(private) crypt_method_name :: proc(method: Crypt_Method, key_bits: int) -> string { switch method { case .Identity: return "none" case .Rc4: // 40 and 128 are the only lengths anything produces; a file that picked // some other multiple of 8 is legal and gets the unqualified name. switch key_bits { case 40: return "RC4 40-bit" case 128: return "RC4 128-bit" } return "RC4" case .Aes_128: return "AES-128" case .Aes_256: return "AES-256" } return "unknown" } // --------------------------------------------------------------------------- // Decryption of parsed objects // --------------------------------------------------------------------------- // decrypt_string decrypts one string object read out of indirect object `ref`. // The result lives in the Document's arena. When nothing has to be done -- no // encryption, an /Identity filter, or an object that was already decrypted as // part of its object stream -- `data` is returned unchanged. decrypt_string :: proc(doc: ^Document, ref: Ref, data: []byte) -> []byte { if doc == nil || doc.encrypt == nil { return data } return crypt_apply(doc, ref, data, doc.encrypt.string_method) } // decrypt_stream decrypts one stream payload read out of indirect object `ref`. // The result lives in the Document's arena; see `decrypt_string` for when the // input is passed through. // // `dict` is the stream's own dictionary when the caller has it, and is what // says "this is a cross-reference stream, leave it alone". Passing it matters // only after the document is open: `xref_load` reads the /Type /XRef stream // before `crypt_init` has run, so `doc.encrypt` is still nil there and the // exemption is free -- but the same object read again through `get_stream` // reaches here with the key in place, and a second pass over plaintext turns // the xref table into noise. // // One stream this cannot recognise even from its dictionary is the document's // /Metadata, which is left in the clear when /EncryptMetadata is false. The // caller knows which object that is; `Encryption.encrypt_metadata` says // whether it matters. decrypt_stream :: proc(doc: ^Document, ref: Ref, data: []byte, dict: ^Dict = nil) -> []byte { if doc == nil || doc.encrypt == nil { return data } // ISO 32000-2, 7.5.8.2: "the cross-reference stream shall not be // encrypted". Read the /Type without resolving it: this runs while the // object is still being parsed, and a genuine cross-reference stream // always spells its /Type out directly. if dict != nil { if ty, ok := to_name(dict_get_raw(dict, "Type")); ok && ty == "XRef" { return data } } return crypt_apply(doc, ref, data, doc.encrypt.stream_method) } // decrypt_embedded_file decrypts an embedded file stream, which a file may // protect with a different filter from the one used for page content -- the // "encrypt the attachments only" configuration, where /StmF and /StrF are // /Identity and only /EFF names a real filter. decrypt_embedded_file :: proc(doc: ^Document, ref: Ref, data: []byte) -> []byte { if doc == nil || doc.encrypt == nil { return data } return crypt_apply(doc, ref, data, doc.encrypt.embedded_method) } @(private) crypt_apply :: proc(doc: ^Document, ref: Ref, data: []byte, method: Crypt_Method) -> []byte { enc := doc.encrypt if method == .Identity || len(data) == 0 { return data } // Ref{0,0} means "came out of an object stream", which was decrypted whole. // Decrypting again would destroy it. if ref.num == 0 { return data } // The /Encrypt dictionary is the key's own input and is never encrypted. if enc.ref.num != 0 && ref.num == enc.ref.num { return data } key_buf: [32]byte key_len := crypt_object_key(enc, method, ref, key_buf[:]) if key_len == 0 { return data } key := key_buf[:key_len] switch method { case .Identity: return data case .Rc4: out := make([]byte, len(data), doc.allocator) rc4_crypt(key, out, data) return out case .Aes_128, .Aes_256: return crypt_aes_decrypt(doc, key, data, ref) } return data } // crypt_aes_decrypt strips the 16-byte IV prefix, decrypts in CBC mode and // removes the PKCS#7 padding (ISO 32000-2, 7.6.3.2). // // Anything that cannot be a valid payload -- shorter than one IV plus one // block, a trailing partial block, padding that does not check out -- decrypts // to nothing. A truncated or corrupt stream is a damaged object, not a reason // to fail the document, and an empty stream is what every viewer shows for one. @(private) crypt_aes_decrypt :: proc(doc: ^Document, key, data: []byte, ref: Ref) -> []byte { if len(data) <= aes.BLOCK_SIZE { add_repair(doc, .Bad_Filter_Data, 0, ref) return nil } body := data[aes.BLOCK_SIZE:] // Trailing bytes that do not fill a block cannot be decrypted; dropping // them recovers the blocks that came before, which is all that is left. n := (len(body) / aes.BLOCK_SIZE) * aes.BLOCK_SIZE if n == 0 { add_repair(doc, .Bad_Filter_Data, 0, ref) return nil } if n != len(body) { add_repair(doc, .Bad_Filter_Data, 0, ref) } out := make([]byte, n, doc.allocator) if !aes_cbc_decrypt(out, body[:n], key, data[:aes.BLOCK_SIZE]) { add_repair(doc, .Bad_Filter_Data, 0, ref) return nil } pad := int(out[n - 1]) if pad < 1 || pad > aes.BLOCK_SIZE || pad > n { add_repair(doc, .Bad_Filter_Data, 0, ref) return nil } for i in n - pad ..< n { if int(out[i]) != pad { add_repair(doc, .Bad_Filter_Data, 0, ref) return nil } } return out[:n - pad] } // crypt_object_key builds the key for one indirect object (Algorithm 1, // ISO 32000-2, 7.6.3.1). Returns the number of bytes written to `out`. // // AES-256 skips this entirely: V5 uses the file key for every object, which is // what made the 2048-object-per-key limit of the earlier scheme go away. @(private) crypt_object_key :: proc(enc: ^Encryption, method: Crypt_Method, ref: Ref, out: []byte) -> int { if method == .Aes_256 { if len(enc.key) != aes.KEY_SIZE_256 { return 0 } copy(out, enc.key) return aes.KEY_SIZE_256 } n := len(enc.key) if n == 0 || n > 16 { return 0 } ctx: md5.Context md5.init(&ctx) md5.update(&ctx, enc.key) // Low three bytes of the object number then low two of the generation, // both low-order byte first. ext := [5]byte{ byte(ref.num), byte(ref.num >> 8), byte(ref.num >> 16), byte(ref.gen), byte(ref.gen >> 8), } md5.update(&ctx, ext[:]) if method == .Aes_128 { md5.update(&ctx, CRYPT_AES_SALT[:]) } digest: [md5.DIGEST_SIZE]byte md5.final(&ctx, digest[:]) key_len := min(n + 5, 16) copy(out[:key_len], digest[:key_len]) return key_len } // --------------------------------------------------------------------------- // Crypt filters // --------------------------------------------------------------------------- @(private) crypt_filter_lookup :: proc(doc: ^Document, cf: ^Dict, name: string, ref: Ref) -> (method: Crypt_Method, bits: int, err: Error) { if name == "" || name == "Identity" { return .Identity, 0, .None } f := dict_dict(doc, cf, name) if f == nil { // /StmF naming a filter that /CF does not define. The file is broken; // passing the bytes through at least leaves any part of the document // that uses a filter we *can* read intact. add_repair(doc, .Bad_Filter_Data, 0, ref) return .Identity, 0, .None } // The spec says a crypt filter's /Length is in bytes, but producers write // bits often enough that every reader guesses. Nothing legal is both a // plausible byte count and a plausible bit count, so 40 splits them. n := int(dict_int(doc, f, "Length", 0)) if n > 0 && n < 40 { n *= 8 } if n > 256 { n = 256 } switch dict_name(doc, f, "CFM", "None") { case "None": return .Identity, 0, .None case "V2": return .Rc4, n, .None case "AESV2": return .Aes_128, 128, .None case "AESV3": return .Aes_256, 256, .None } // An unrecognised /CFM is a scheme we would silently mangle every object // with. Better to say so. return .Identity, 0, .Unsupported_Encryption } // crypt_first_id returns the first element of the trailer's /ID array, an input // to the key derivation for R2..R4. A missing or malformed /ID is common in // damaged files and reads as empty, which is what other readers do. @(private) crypt_first_id :: proc(doc: ^Document) -> []byte { id, _ := to_bytes(array_get(doc, dict_array(doc, doc.trailer, "ID"), 0)) return id } // --------------------------------------------------------------------------- // Key derivation, revisions 2 to 4 // --------------------------------------------------------------------------- @(private) crypt_auth_r234 :: proc(doc: ^Document, ed: ^Dict, enc: ^Encryption, password, id0: []byte) -> Error { ovalue, uvalue: [32]byte crypt_fixed(dict_bytes(doc, ed, "O"), ovalue[:]) crypt_fixed(dict_bytes(doc, ed, "U"), uvalue[:]) // As the user password. crypt_key_r234(password, ovalue[:], enc.permission_bits, id0, enc.revision, len(enc.key), enc.encrypt_metadata, enc.key) if crypt_check_user(enc.key, enc.revision, id0, uvalue[:]) { enc.authenticated_as = .User return .None } // As the owner password: /O decrypts to the user password (Algorithm 7), // which is then checked the ordinary way. recovered: [32]byte crypt_owner_password(password, ovalue[:], enc.revision, len(enc.key), recovered[:]) crypt_key_r234(recovered[:], ovalue[:], enc.permission_bits, id0, enc.revision, len(enc.key), enc.encrypt_metadata, enc.key) if crypt_check_user(enc.key, enc.revision, id0, uvalue[:]) { enc.authenticated_as = .Owner return .None } crypto.zero_explicit(raw_data(enc.key), len(enc.key)) return .Wrong_Password } // crypt_pad_password writes the 32-byte padded password of Algorithm 2 step (a). @(private) crypt_pad_password :: proc(password: []byte, out: []byte) { n := min(len(password), 32) copy(out[:n], password[:n]) copy(out[n:32], CRYPT_PAD[:32 - n]) } // crypt_fixed copies `src` into `out`, zero-filling what is missing. /O and /U // are fixed-width fields, and a file that stores them short would otherwise // index out of bounds. @(private) crypt_fixed :: proc(src: []byte, out: []byte) { n := min(len(src), len(out)) copy(out[:n], src[:n]) for i in n ..< len(out) { out[i] = 0 } } // crypt_key_r234 is Algorithm 2: the file encryption key for revisions 2 to 4. @(private) crypt_key_r234 :: proc(password, ovalue: []byte, permissions: i32, id0: []byte, revision, n: int, encrypt_metadata: bool, out: []byte) { // The key cannot be longer than the MD5 digest it is cut from, whatever a // contradictory /V and /R pair asked for. key_len := min(n, len(out), md5.DIGEST_SIZE) if key_len <= 0 { return } padded: [32]byte crypt_pad_password(password, padded[:]) ctx: md5.Context md5.init(&ctx) md5.update(&ctx, padded[:]) md5.update(&ctx, ovalue[:32]) // /P as four bytes, low-order first, of the *unsigned* interpretation. pu := transmute(u32)permissions pbytes := [4]byte{byte(pu), byte(pu >> 8), byte(pu >> 16), byte(pu >> 24)} md5.update(&ctx, pbytes[:]) md5.update(&ctx, id0) // R4 with /EncryptMetadata false folds in four 0xFF bytes, so that a file // whose metadata was left in the clear gets a different key from one whose // metadata was not (7.6.4.3.2, step f). if revision >= 4 && !encrypt_metadata { ff := [4]byte{0xff, 0xff, 0xff, 0xff} md5.update(&ctx, ff[:]) } digest: [md5.DIGEST_SIZE]byte md5.final(&ctx, digest[:]) // R3 and later iterate 50 times over the *first n bytes* of the digest. // Algorithm 3, which builds /O, iterates over all 16 -- the asymmetry is // real and getting it wrong only shows up on owner passwords. if revision >= 3 { for _ in 0 ..< 50 { md5.init(&ctx) md5.update(&ctx, digest[:key_len]) md5.final(&ctx, digest[:]) } } copy(out[:key_len], digest[:key_len]) } // crypt_user_value is Algorithm 4 (R2) and Algorithm 5 (R3+): what /U must // contain if `key` is the right file encryption key. @(private) crypt_user_value :: proc(key: []byte, revision: int, id0: []byte, out: []byte) { n := min(len(key), 16) if n == 0 { crypt_fixed(nil, out[:32]) return } if revision == 2 { copy(out[:32], CRYPT_PAD[:]) rc4_crypt(key[:n], out[:32], out[:32]) return } ctx: md5.Context md5.init(&ctx) md5.update(&ctx, CRYPT_PAD[:]) md5.update(&ctx, id0) digest: [md5.DIGEST_SIZE]byte md5.final(&ctx, digest[:]) rc4_crypt(key[:n], digest[:], digest[:]) tmp: [16]byte for i in 1 ..= 19 { for j in 0 ..< n { tmp[j] = key[j] ~ byte(i) } rc4_crypt(tmp[:n], digest[:], digest[:]) } copy(out[:16], digest[:]) // The last 16 bytes are explicitly arbitrary padding and are never checked. for i in 16 ..< 32 { out[i] = 0 } } // crypt_check_user is Algorithm 6. R2 compares all 32 bytes; R3 and later only // the first 16, the rest being padding the producer chose freely. @(private) crypt_check_user :: proc(key: []byte, revision: int, id0: []byte, uvalue: []byte) -> bool { computed: [32]byte crypt_user_value(key, revision, id0, computed[:]) n := revision == 2 ? 32 : 16 return crypt_equal(computed[:n], uvalue[:n]) } // crypt_owner_password is Algorithm 7: decrypt /O back into the padded user // password. Whether it *is* the user password is decided by the caller. @(private) crypt_owner_password :: proc(password, ovalue: []byte, revision, n: int, out: []byte) { padded: [32]byte crypt_pad_password(password, padded[:]) ctx: md5.Context md5.init(&ctx) md5.update(&ctx, padded[:]) digest: [md5.DIGEST_SIZE]byte md5.final(&ctx, digest[:]) if revision >= 3 { for _ in 0 ..< 50 { md5.init(&ctx) md5.update(&ctx, digest[:]) md5.final(&ctx, digest[:]) } } key_len := revision == 2 ? 5 : min(max(n, 1), 16) copy(out[:32], ovalue[:32]) if revision == 2 { rc4_crypt(digest[:key_len], out[:32], out[:32]) return } tmp: [16]byte for i := 19; i >= 0; i -= 1 { for j in 0 ..< key_len { tmp[j] = digest[j] ~ byte(i) } rc4_crypt(tmp[:key_len], out[:32], out[:32]) } } // --------------------------------------------------------------------------- // Key derivation, revisions 5 and 6 (AES-256) // // R5 is Adobe's deprecated extension: a bare SHA-256. R6 is what ISO 32000-2 // standardised, with the hardening loop of Algorithm 2.B in front of it. // Both share the /U and /O layout: 32 bytes of hash, an 8-byte validation // salt, an 8-byte key salt. // --------------------------------------------------------------------------- @(private) crypt_auth_r56 :: proc(doc: ^Document, ed: ^Dict, enc: ^Encryption, password: []byte) -> Error { ovalue, uvalue: [48]byte crypt_fixed(dict_bytes(doc, ed, "O"), ovalue[:]) crypt_fixed(dict_bytes(doc, ed, "U"), uvalue[:]) // ISO 32000-2 asks for SASLprep here. Every implementation in practice uses // the UTF-8 bytes as given, truncated to 127, and files produced from // non-ASCII passwords are rare enough that the difference has never bitten. pw := password if len(pw) > 127 { pw = pw[:127] } hash: [32]byte intermediate: [32]byte wrapped: []byte crypt_hash_r56(enc.revision, pw, uvalue[32:40], nil, hash[:]) if crypt_equal(hash[:], uvalue[:32]) { enc.authenticated_as = .User crypt_hash_r56(enc.revision, pw, uvalue[40:48], nil, intermediate[:]) wrapped = dict_bytes(doc, ed, "UE") } else { // The owner check folds in the whole 48-byte /U value, which is what // stops an /O from being lifted into another document. crypt_hash_r56(enc.revision, pw, ovalue[32:40], uvalue[:], hash[:]) if !crypt_equal(hash[:], ovalue[:32]) { crypto.zero_explicit(raw_data(enc.key), len(enc.key)) return .Wrong_Password } enc.authenticated_as = .Owner crypt_hash_r56(enc.revision, pw, ovalue[40:48], uvalue[:], intermediate[:]) wrapped = dict_bytes(doc, ed, "OE") } if len(wrapped) < 32 || len(enc.key) != 32 { // Authenticated, but the wrapped file key is missing or truncated. return .Bad_Encrypt_Dict } zero_iv: [16]byte if !aes_cbc_decrypt(enc.key, wrapped[:32], intermediate[:], zero_iv[:]) { return .Bad_Encrypt_Dict } crypt_check_perms(doc, ed, enc) return .None } // crypt_check_perms is Algorithm 13: /Perms is the permission bits encrypted // with the file key, so that they cannot be edited without it. A mismatch is // recorded and ignored -- the document still decrypts, and refusing to open a // file whose permissions were tampered with helps nobody. @(private) crypt_check_perms :: proc(doc: ^Document, ed: ^Dict, enc: ^Encryption) { perms := dict_bytes(doc, ed, "Perms") if len(perms) < 16 || len(enc.key) != aes.KEY_SIZE_256 { return } ctx: aes.Context_ECB aes.init_ecb(&ctx, enc.key) defer aes.reset_ecb(&ctx) out: [16]byte aes.decrypt_ecb(&ctx, out[:], perms[:16]) if out[9] != 'a' || out[10] != 'd' || out[11] != 'b' { add_repair(doc, .Bad_Filter_Data, 0, enc.ref) return } p := u32(out[0]) | u32(out[1]) << 8 | u32(out[2]) << 16 | u32(out[3]) << 24 if transmute(i32)p != enc.permission_bits { add_repair(doc, .Bad_Filter_Data, 0, enc.ref) } } // The hardening loop of Algorithm 2.B works on `password || K || udata` // repeated 64 times. K is a SHA-2 digest, so at most 64 bytes; the password is // truncated to 127 and udata is the 48-byte /U value. That caps one round at // 239 * 64 = 15296 bytes, small enough to size the buffers for the worst case // and keep the whole thing off the heap. @(private) CRYPT_R6_MAX_SEGMENT :: 127 + 64 + 48 @(private) CRYPT_R6_MAX_BLOCK :: CRYPT_R6_MAX_SEGMENT * 64 // The exit test is `last byte of E <= round - 32`, and the last byte is at most // 255, so round 288 ends the loop whatever the data does. The cap exists only // so that a bug here cannot become a hang. @(private) CRYPT_R6_MAX_ROUNDS :: 512 // crypt_hash_r56 is Algorithm 2.B for R6, and the plain SHA-256 of R5. // `out` receives 32 bytes. @(private) crypt_hash_r56 :: proc(revision: int, password, salt, udata: []byte, out: []byte) { pw := password if len(pw) > 127 { pw = pw[:127] } ud := udata if len(ud) > 48 { ud = ud[:48] } k: [64]byte ctx: sha2.Context_256 sha2.init_256(&ctx) sha2.update(&ctx, pw) sha2.update(&ctx, salt) sha2.update(&ctx, ud) sha2.final(&ctx, k[:32]) if revision >= 6 { crypt_hash_2b(pw, ud, k[:]) } copy(out[:32], k[:32]) } // crypt_hash_2b runs the hardening rounds of Algorithm 2.B in place on `k`, // whose first 32 bytes arrive holding the initial SHA-256 and whose first 32 // bytes leave holding the answer. // // It is a separate procedure so that its two 15 KB buffers are not on the stack // of every R5 hash as well. @(private) crypt_hash_2b :: proc(pw, ud: []byte, k: []byte) { k1: [CRYPT_R6_MAX_BLOCK]byte e: [CRYPT_R6_MAX_BLOCK]byte k_len := 32 last: byte = 0 for round := 0; round < 64 || int(last) > round - 32; round += 1 { if round >= CRYPT_R6_MAX_ROUNDS { break } seg := len(pw) + k_len + len(ud) if seg <= 0 || seg > CRYPT_R6_MAX_SEGMENT { break } off := 0 copy(k1[off:], pw); off += len(pw) copy(k1[off:], k[:k_len]); off += k_len copy(k1[off:], ud) for j in 1 ..< 64 { copy(k1[j * seg:], k1[:seg]) } // seg * 64 is always a whole number of AES blocks, whatever seg is. total := seg * 64 if !aes_cbc_encrypt(e[:total], k1[:total], k[:16], k[16:32]) { break } // The first 16 bytes of E as a big-endian integer, modulo 3. 256 is // 1 mod 3, so the byte sum has the same residue. sum := 0 for z in 0 ..< 16 { sum += int(e[z]) } switch sum % 3 { case 0: ctx: sha2.Context_256 sha2.init_256(&ctx) sha2.update(&ctx, e[:total]) sha2.final(&ctx, k[:32]) k_len = 32 case 1: ctx: sha2.Context_512 sha2.init_384(&ctx) sha2.update(&ctx, e[:total]) sha2.final(&ctx, k[:48]) k_len = 48 case: ctx: sha2.Context_512 sha2.init_512(&ctx) sha2.update(&ctx, e[:total]) sha2.final(&ctx, k[:64]) k_len = 64 } last = e[total - 1] } } // --------------------------------------------------------------------------- // RC4 // // core has no ARC4 -- reasonably, since nothing new should use it -- so this is // the whole of it. Keys here are 5 to 16 bytes. // --------------------------------------------------------------------------- @(private) Rc4_State :: struct { s: [256]byte, i, j: byte, } @(private) rc4_init :: proc "contextless" (ctx: ^Rc4_State, key: []byte) { for i in 0 ..< 256 { ctx.s[i] = byte(i) } ctx.i, ctx.j = 0, 0 if len(key) == 0 { return } j: byte = 0 for i in 0 ..< 256 { j = j + ctx.s[i] + key[i % len(key)] ctx.s[i], ctx.s[j] = ctx.s[j], ctx.s[i] } } // rc4_xor runs the keystream over `src` into `dst`. The two may be the same // slice. Bytes past len(src) in dst are untouched. @(private) rc4_xor :: proc "contextless" (ctx: ^Rc4_State, dst, src: []byte) { n := min(len(dst), len(src)) i, j := ctx.i, ctx.j for pos in 0 ..< n { i += 1 j += ctx.s[i] ctx.s[i], ctx.s[j] = ctx.s[j], ctx.s[i] dst[pos] = src[pos] ~ ctx.s[byte(ctx.s[i] + ctx.s[j])] } ctx.i, ctx.j = i, j } // rc4_crypt is the one-shot form. RC4 is its own inverse, so this both // encrypts and decrypts. An empty key passes the data through rather than // dividing by zero. @(private) rc4_crypt :: proc "contextless" (key, dst, src: []byte) { if len(key) == 0 { copy(dst, src) return } ctx: Rc4_State rc4_init(&ctx, key) rc4_xor(&ctx, dst, src) } // --------------------------------------------------------------------------- // AES-CBC // // core:crypto/aes has ECB, CTR and GCM but not CBC, which is the one mode PDF // uses. Building it on encrypt_ecb/decrypt_ecb is a dozen lines and keeps the // constant-time block implementation core provides. // // Neither of these does padding: PDF's PKCS#7 padding is handled by // crypt_aes_decrypt, and Algorithm 2.B's inner loop is explicitly unpadded. // --------------------------------------------------------------------------- @(private) aes_key_size_ok :: proc "contextless" (key: []byte) -> bool { switch len(key) { case aes.KEY_SIZE_128, aes.KEY_SIZE_192, aes.KEY_SIZE_256: return true } return false } // aes_cbc_encrypt encrypts `src` into `dst`. `src` must be a whole number of // blocks; `dst` must be at least as long. Returns false on a bad key size, a // short destination or a partial final block -- never a panic, because every // one of those can be reached from a file. @(private) aes_cbc_encrypt :: proc(dst, src, key, iv: []byte) -> bool { if !aes_key_size_ok(key) { return false } if len(iv) != aes.BLOCK_SIZE { return false } if len(src) % aes.BLOCK_SIZE != 0 { return false } if len(dst) < len(src) { return false } ctx: aes.Context_ECB aes.init_ecb(&ctx, key) defer aes.reset_ecb(&ctx) prev: [aes.BLOCK_SIZE]byte copy(prev[:], iv) block: [aes.BLOCK_SIZE]byte for off := 0; off < len(src); off += aes.BLOCK_SIZE { for i in 0 ..< aes.BLOCK_SIZE { block[i] = src[off + i] ~ prev[i] } aes.encrypt_ecb(&ctx, dst[off:off + aes.BLOCK_SIZE], block[:]) copy(prev[:], dst[off:off + aes.BLOCK_SIZE]) } return true } // aes_cbc_decrypt decrypts `src` into `dst`, which may be the same slice. @(private) aes_cbc_decrypt :: proc(dst, src, key, iv: []byte) -> bool { if !aes_key_size_ok(key) { return false } if len(iv) != aes.BLOCK_SIZE { return false } if len(src) % aes.BLOCK_SIZE != 0 { return false } if len(dst) < len(src) { return false } ctx: aes.Context_ECB aes.init_ecb(&ctx, key) defer aes.reset_ecb(&ctx) prev: [aes.BLOCK_SIZE]byte copy(prev[:], iv) saved: [aes.BLOCK_SIZE]byte for off := 0; off < len(src); off += aes.BLOCK_SIZE { // Saved before decrypting, so that dst and src may alias. copy(saved[:], src[off:off + aes.BLOCK_SIZE]) aes.decrypt_ecb(&ctx, dst[off:off + aes.BLOCK_SIZE], src[off:off + aes.BLOCK_SIZE]) for i in 0 ..< aes.BLOCK_SIZE { dst[off + i] ~= prev[i] } copy(prev[:], saved[:]) } return true } // crypt_equal compares in constant time. Password checks are the one place in // this library where how long a comparison took is information. @(private) crypt_equal :: proc "contextless" (a, b: []byte) -> bool { if len(a) != len(b) { return false } diff: byte = 0 for i in 0 ..< len(a) { diff |= a[i] ~ b[i] } return diff == 0 }