# encoding: utf-8 require "tempfile" require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") describe "Document encryption" do describe "Password padding" do include Prawn::Document::Security it "should truncate long passwords" do pw = "Long long string" * 30 padded = pad_password(pw) expect(padded.length).to eq(32) expect(padded).to eq(pw[0, 32]) end it "should pad short passwords" do pw = "abcd" padded = pad_password(pw) expect(padded.length).to eq(32) expect(padded).to eq(pw + Prawn::Document::Security::PasswordPadding[0, 28]) end it "should fully pad null passwords" do pw = "" padded = pad_password(pw) expect(padded.length).to eq(32) expect(padded).to eq(Prawn::Document::Security::PasswordPadding) end end describe "Setting permissions" do def doc_with_permissions(permissions) pdf = Prawn::Document.new class << pdf # Make things easier to test public :permissions_value end pdf.encrypt_document(:permissions => permissions) pdf end it "should default to full permissions" do expect(doc_with_permissions({}).permissions_value).to eq(0xFFFFFFFF) expect(doc_with_permissions(:print_document => true, :modify_contents => true, :copy_contents => true, :modify_annotations => true).permissions_value). to eq(0xFFFFFFFF) end it "should clear the appropriate bits for each permission flag" do expect(doc_with_permissions(:print_document => false).permissions_value). to eq(0b1111_1111_1111_1111_1111_1111_1111_1011) expect(doc_with_permissions(:modify_contents => false).permissions_value). to eq(0b1111_1111_1111_1111_1111_1111_1111_0111) expect(doc_with_permissions(:copy_contents => false).permissions_value). to eq(0b1111_1111_1111_1111_1111_1111_1110_1111) expect(doc_with_permissions(:modify_annotations => false).permissions_value). to eq(0b1111_1111_1111_1111_1111_1111_1101_1111) end it "should raise_error ArgumentError if invalid option is provided" do expect { doc_with_permissions(:modify_document => false) }.to raise_error(ArgumentError) end end describe "Encryption keys" do # Since PDF::Reader doesn't read encrypted PDF files, we just take the # roundabout method of verifying each step of the encryption. This works # fine because the encryption method is deterministic. before(:each) do @pdf = Prawn::Document.new class << @pdf public :owner_password_hash, :user_password_hash, :user_encryption_key end @pdf.encrypt_document :user_password => 'foo', :owner_password => 'bar', :permissions => { :print_document => false } end it "should calculate the correct owner hash" do expect(@pdf.owner_password_hash.unpack("H*").first).to match(/^61CA855012/i) end it "should calculate the correct user hash" do expect(@pdf.user_password_hash.unpack("H*").first).to match(/^6BC8C51031/i) end it "should calculate the correct user_encryption_key" do expect(@pdf.user_encryption_key.unpack("H*").first.upcase).to eq("B100AB6429") end end describe "EncryptedPdfObject" do it "should delegate to PdfObject for simple types" do expect(PDF::Core::EncryptedPdfObject(true, nil, nil, nil)).to eq("true") expect(PDF::Core::EncryptedPdfObject(42, nil, nil, nil)).to eq("42") end it "should encrypt strings properly" do expect(PDF::Core::EncryptedPdfObject("foo", "12345", 123, 0)).to eq("<4ad6e3>") end it "should encrypt literal strings properly" do expect(PDF::Core::EncryptedPdfObject(PDF::Core::LiteralString.new("foo"), "12345", 123, 0)).to eq(bin_string("(J\xD6\xE3)")) expect(PDF::Core::EncryptedPdfObject(PDF::Core::LiteralString.new("lhfbqg3do5u0satu3fjf"), nil, 123, 0)).to eq(bin_string("(\xF1\x8B\\(\b\xBB\xE18S\x130~4*#\\(%\x87\xE7\x8E\\\n)")) end it "should encrypt time properly" do expect(PDF::Core::EncryptedPdfObject(Time.utc(2050, 04, 26, 10, 17, 10), "12345", 123, 0)).to eq(bin_string("(h\x83\xBE\xDC\xEC\x99\x0F\xD7\\)%\x13\xD4$\xB8\xF0\x16\xB8\x80\xC5\xE91+\xCF)")) end it "should properly handle compound types" do expect(PDF::Core::EncryptedPdfObject({ :Bar => "foo" }, "12345", 123, 0)).to eq( "<< /Bar <4ad6e3>\n>>" ) expect(PDF::Core::EncryptedPdfObject(["foo", "bar"], "12345", 123, 0)).to eq( "[<4ad6e3> <4ed8fe>]" ) end end describe "Reference#encrypted_object" do it "should encrypt references properly" do ref = PDF::Core::Reference(1, ["foo"]) expect(ref.encrypted_object(nil)).to eq("1 0 obj\n[<4fca3f>]\nendobj\n") end it "should encrypt references with streams properly" do ref = PDF::Core::Reference(1, {}) ref << 'foo' result = bin_string("1 0 obj\n<< /Length 3\n>>\nstream\nO\xCA?\nendstream\nendobj\n") expect(ref.encrypted_object(nil)).to eq(result) end end describe "String#encrypted_object" do it "should encrypt stream properly" do stream = PDF::Core::Stream.new stream << "foo" result = bin_string("stream\nO\xCA?\nendstream\n") expect(stream.encrypted_object(nil, 1, 0)).to eq(result) end end end