Working with PDF security (Document & User Passwords, Permissions)

Q: We are using PDFTron to validate both the document and user
passwords and have the following questions:

a) Is it possible to determine if the PDF document requires both a
document and user password before either are entered? It seems that
the document password is required to initialize the standard security
handler, so it is impossible to know what permissions are available
before entering that.
b) Is it possible to validate user passwords? We found that there
doesn’t seem to be a method to check to see if a string matches the
user-password that is required for permissions on a pdf file.
------------
A: Attached (http://pdfnet-sdk.googlegroups.com/web/
ProcessSecuredPDF.cpp) is a bit extended version of 'EncTest' sample
project that shows how to obtain different security settings from an
existing PDF document (e.g. document permissions, info on whether
'owner' or 'user' password was specified, etc).

Is it possible to determine if the PDF document requires both a
document and user password before either are entered? It seems that
the document password is required to initialize the standard security
handler, so it is impossible to know what permissions are available before entering that.

If the document is secured using 'open' password you can't check the
permissions (or whether the document requires a specific password)
until one of the passwords is provided. This is a feature of PDF
specification (if it was the other way, it would be very easy to break
document security).

Is it possible to validate user passwords?

Password validation is performed within
pdfdoc.InitStdSecurityHandler() method. If the 'user' password does
not allow certain permissions of the document, you could request a
'master' password from the user (e.g. in order to allow modifications
to security settings). After obtaining the mater password from the
user you can call InitStdSecurityHandler() again with the master
password.

ProcessSecuredPDF.cpp (based in EncTest sample project).

// The sample is written using C++, but the same approach will work in
C#, VB.Net, or Java.

#include <PDF/PDFNet.h>
#include <PDF/PDFDoc.h>
#include <SDF/SecurityHandler.h>
#include <iostream>

using namespace pdftron;
using namespace PDF;
using namespace SDF;
using namespace std;

//---------------------------------------------------------------------------------
// This sample is an extended example of how to process security
setting on
// the input PDF document.
//---------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
  int ret = 0;
  PDFNet::Initialize();
  try
  {
    PDFDoc doc("encrypted.pdf");
    if (doc.IsEncrypted())
    {
      // Check if the document is protected using 'open' and/or
      // 'permission' password.
      if (doc.InitSecurityHandler()) {
        cout << "The document is protected only using 'permission' (i.e.
master) password." << endl;
      }
      else {
        cout << "The document is protected using 'open' or 'permission'
password." << endl;
        // Get a password from a user...
        bool success=false;
        for(int count=0; count<3;count++)
        {
          char password[255];
          cout << "A password required to open the document." << endl
            << "Please enter the password:";
          cin >> password;
          if(doc.InitStdSecurityHandler(password,strlen(password)))
          {
            success=true;
            cout << "The password is correct." << endl;
            break;
          }
          else if(count<3)
          {
            cout << "The password is incorrect, please try again" << endl;
          }
        }

        if(!success) {
          cout << "Document authentication error...." << endl;
          return 1;
        }

        SecurityHandler hdlr = doc.GetSecurityHandler();
        const char *user_pass = 0, *master_pass = 0;
        if (hdlr.IsUserPasswordRequired()) {
          user_pass = hdlr.GetUserPassword();
          cout << "User Password: " << user_pass << endl;
        }

        if (hdlr.IsMasterPasswordRequired()) {
          master_pass = hdlr.GetMasterPassword();
          cout << "Master Password: " << master_pass << endl;
        }

        // Check whether the derived user password is the same as owner
password.
        // If they are the same it means that owner password is used only
to derive
        // user password and is in fact empty string (or master password
is not
        // specified).
        bool is_master_pass = master_pass && user_pass && !
strncmp(master_pass, user_pass, strlen(master_pass));
        cout << "User entered password type: "
           << (is_master_pass ? "USER" : "OWNER") << " password\n";
      }

      SecurityHandler hdlr = doc.GetSecurityHandler();
      cout << "Permissions: "
        << "\n\tOpen and decrypt the document: " <<
hdlr.GetPermission(SecurityHandler::e_doc_open)
        << "\n\tAllow content extraction: " <<
hdlr.GetPermission(SecurityHandler::e_extract_content)
        << "\n\tAllow changes to security settings: " <<
hdlr.GetPermission(SecurityHandler::e_doc_secure)
        << "\n\tAllow full document editing: " <<
hdlr.GetPermission(SecurityHandler::e_doc_modify)
        << "\n\tAllow printing: " <<
hdlr.GetPermission(SecurityHandler::e_print)
        << "\n\tAllow high resolution printing: " <<
hdlr.GetPermission(SecurityHandler::e_print_high)
        << "\n\tAllow annotation editing: " <<
hdlr.GetPermission(SecurityHandler::e_mod_annot)
        << "\n\tAllow form fill: " <<
hdlr.GetPermission(SecurityHandler::e_fill_forms)
        << "\n\tAllow content extraction for accessibility: " <<
hdlr.GetPermission(SecurityHandler::e_access_support)
        << "\n\tAllow document assembly: " <<
hdlr.GetPermission(SecurityHandler::e_assemble_doc)
        << endl;
    }

    // Use the document (e.g. remove all security on the document)
    doc.RemoveSecurity();
    doc.Save("not_secured.pdf", 0, NULL);
  }
  catch(Common::Exception& e) {
    cout << e << endl;
    ret = 1;
  }
  catch(...) {
    cout << "Unknown Exception" << endl;
    ret = 1;
  }

  cout << "Tests completed." << endl;

  PDFNet::Terminate();
  return ret;
}