diff --git a/read.go b/read.go index 5cadd7c..31148b3 100644 --- a/read.go +++ b/read.go @@ -982,6 +982,11 @@ func (r *Reader) initEncrypt(password string) error { h.Write([]byte(O)) h.Write([]byte{byte(P), byte(P >> 8), byte(P >> 16), byte(P >> 24)}) h.Write([]byte(ID)) + if R >= 4 { + if encryptMetadata, ok := encrypt["EncryptMetadata"].(bool); ok && !encryptMetadata { + h.Write([]byte{0xff, 0xff, 0xff, 0xff}) + } + } key := h.Sum(nil) if R >= 3 { diff --git a/read_test.go b/read_test.go index edea184..3296676 100644 --- a/read_test.go +++ b/read_test.go @@ -2,6 +2,8 @@ package pdf import ( "bytes" + "context" + "io" "strings" "testing" ) @@ -151,3 +153,26 @@ func TestNewReaderMaliciousPDF(t *testing.T) { t.Fatal("expected error from malicious PDF, got nil") } } + +// Regression test for https://github.com/ledongthuc/pdf/issues/82: +// AES-128 encrypted PDFs (V=4, R=4) with /EncryptMetadata false and an empty +// user password must open successfully instead of returning ErrInvalidPassword. +func TestOpenEncryptedMetadataFalse(t *testing.T) { + f, r, err := Open("testdata/encrypted_metadata_false.pdf") + if err != nil { + t.Fatalf("Open: %v", err) + } + defer f.Close() + + b, err := r.GetPlainText(context.Background()) + if err != nil { + t.Fatalf("GetPlainText: %v", err) + } + text, err := io.ReadAll(b) + if err != nil { + t.Fatalf("ReadAll: %v", err) + } + if !strings.Contains(string(text), "hello world") { + t.Fatalf("unexpected text: %q", text) + } +} diff --git a/testdata/encrypted_metadata_false.pdf b/testdata/encrypted_metadata_false.pdf new file mode 100644 index 0000000..1431736 Binary files /dev/null and b/testdata/encrypted_metadata_false.pdf differ