github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/test/encryption_test.go (about)

     1  /*
     2  Copyright 2020 The pdf Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package test
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  	"testing"
    23  
    24  	"github.com/pdfcpu/pdfcpu/pkg/api"
    25  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu"
    26  	"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
    27  )
    28  
    29  func listPermissions(t *testing.T, fileName string) ([]string, error) {
    30  	t.Helper()
    31  
    32  	msg := "listPermissions"
    33  
    34  	f, err := os.Open(fileName)
    35  	if err != nil {
    36  		t.Fatalf("%s open: %v\n", msg, err)
    37  	}
    38  	defer f.Close()
    39  
    40  	conf := model.NewDefaultConfiguration()
    41  	conf.Cmd = model.LISTPERMISSIONS
    42  
    43  	ctx, err := api.ReadValidateAndOptimize(f, conf)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	return pdfcpu.Permissions(ctx), nil
    49  }
    50  
    51  func confForAlgorithm(aes bool, keyLength int, upw, opw string) *model.Configuration {
    52  	if aes {
    53  		return model.NewAESConfiguration(upw, opw, keyLength)
    54  	}
    55  	return model.NewRC4Configuration(upw, opw, keyLength)
    56  }
    57  
    58  func setPermissions(t *testing.T, aes bool, keyLength int, msg, outFile string) {
    59  	t.Helper()
    60  	// Set all permissions of encrypted file w/o passwords should fail.
    61  	conf := confForAlgorithm(aes, keyLength, "", "")
    62  	conf.Permissions = model.PermissionsAll
    63  	if err := api.SetPermissionsFile(outFile, "", conf); err == nil {
    64  		t.Fatalf("%s: set all permissions w/o pw for %s\n", msg, outFile)
    65  	}
    66  
    67  	// Set all permissions of encrypted file with user password should fail.
    68  	conf = confForAlgorithm(aes, keyLength, "upw", "")
    69  	conf.Permissions = model.PermissionsAll
    70  	if err := api.SetPermissionsFile(outFile, "", conf); err == nil {
    71  		t.Fatalf("%s: set all permissions w/o opw for %s\n", msg, outFile)
    72  	}
    73  
    74  	// Set all permissions of encrypted file with owner password should fail.
    75  	conf = confForAlgorithm(aes, keyLength, "", "opw")
    76  	conf.Permissions = model.PermissionsAll
    77  	if err := api.SetPermissionsFile(outFile, "", conf); err == nil {
    78  		t.Fatalf("%s: set all permissions w/o both pws for %s\n", msg, outFile)
    79  	}
    80  
    81  	// Set all permissions of encrypted file using both passwords.
    82  	conf = confForAlgorithm(aes, keyLength, "upw", "opw")
    83  	conf.Permissions = model.PermissionsAll
    84  	if err := api.SetPermissionsFile(outFile, "", conf); err != nil {
    85  		t.Fatalf("%s: set all permissions for %s: %v\n", msg, outFile, err)
    86  	}
    87  
    88  	// List permissions using the owner password.
    89  	conf = confForAlgorithm(aes, keyLength, "", "opw")
    90  	p, err := api.GetPermissionsFile(outFile, conf)
    91  	if err != nil {
    92  		t.Fatalf("%s: get permissions %s: %v\n", msg, outFile, err)
    93  	}
    94  
    95  	// Ensure permissions all.
    96  	if p == nil || uint16(*p) != uint16(model.PermissionsAll) {
    97  		t.Fatal()
    98  	}
    99  
   100  }
   101  
   102  func testEncryption(t *testing.T, fileName string, alg string, keyLength int) {
   103  	t.Helper()
   104  	msg := "testEncryption"
   105  
   106  	aes := alg == "aes"
   107  	inFile := filepath.Join(inDir, fileName)
   108  	outFile := filepath.Join(outDir, "test.pdf")
   109  	t.Log(inFile)
   110  
   111  	p, err := api.GetPermissionsFile(inFile, nil)
   112  	if err != nil {
   113  		t.Fatalf("%s: get permissions %s: %v\n", msg, inFile, err)
   114  	}
   115  	// Ensure full access.
   116  	if p != nil {
   117  		t.Fatal()
   118  	}
   119  
   120  	// Encrypt file.
   121  	conf := confForAlgorithm(aes, keyLength, "upw", "opw")
   122  	if err := api.EncryptFile(inFile, outFile, conf); err != nil {
   123  		t.Fatalf("%s: encrypt %s: %v\n", msg, outFile, err)
   124  	}
   125  
   126  	// List permissions of encrypted file w/o passwords should fail.
   127  	if list, err := listPermissions(t, outFile); err == nil {
   128  		t.Fatalf("%s: list permissions w/o pw %s: %v\n", msg, outFile, list)
   129  	}
   130  
   131  	// List permissions of encrypted file using the user password.
   132  	conf = confForAlgorithm(aes, keyLength, "upw", "")
   133  	p, err = api.GetPermissionsFile(outFile, conf)
   134  	if err != nil {
   135  		t.Fatalf("%s: get permissions %s: %v\n", msg, inFile, err)
   136  	}
   137  	// Ensure permissions none.
   138  	if p == nil || uint16(*p) != uint16(model.PermissionsNone) {
   139  		t.Fatal()
   140  	}
   141  
   142  	// List permissions of encrypted file using the owner password.
   143  	conf = confForAlgorithm(aes, keyLength, "", "opw")
   144  	p, err = api.GetPermissionsFile(outFile, conf)
   145  	if err != nil {
   146  		t.Fatalf("%s: get permissions %s: %v\n", msg, inFile, err)
   147  	}
   148  	// Ensure permissions none.
   149  	if p == nil || uint16(*p) != uint16(model.PermissionsNone) {
   150  		t.Fatal()
   151  	}
   152  
   153  	setPermissions(t, aes, keyLength, msg, outFile)
   154  
   155  	// Change user password.
   156  	conf = confForAlgorithm(aes, keyLength, "upw", "opw")
   157  	if err = api.ChangeUserPasswordFile(outFile, "", "upw", "upwNew", conf); err != nil {
   158  		t.Fatalf("%s: change upw %s: %v\n", msg, outFile, err)
   159  	}
   160  
   161  	// Change owner password.
   162  	conf = confForAlgorithm(aes, keyLength, "upwNew", "opw")
   163  	if err = api.ChangeOwnerPasswordFile(outFile, "", "opw", "opwNew", conf); err != nil {
   164  		t.Fatalf("%s: change opw %s: %v\n", msg, outFile, err)
   165  	}
   166  
   167  	// Decrypt file using both passwords.
   168  	conf = confForAlgorithm(aes, keyLength, "upwNew", "opwNew")
   169  	if err = api.DecryptFile(outFile, "", conf); err != nil {
   170  		t.Fatalf("%s: decrypt %s: %v\n", msg, outFile, err)
   171  	}
   172  
   173  	// Validate decrypted file.
   174  	if err = api.ValidateFile(outFile, nil); err != nil {
   175  		t.Fatalf("%s: validate %s: %v\n", msg, outFile, err)
   176  	}
   177  }
   178  
   179  func TestEncryption(t *testing.T) {
   180  	for _, fileName := range []string{
   181  		"5116.DCT_Filter.pdf",
   182  		"adobe_errata.pdf",
   183  	} {
   184  		testEncryption(t, fileName, "rc4", 40)
   185  		testEncryption(t, fileName, "rc4", 128)
   186  		testEncryption(t, fileName, "aes", 40)
   187  		testEncryption(t, fileName, "aes", 128)
   188  		testEncryption(t, fileName, "aes", 256)
   189  	}
   190  }
   191  
   192  func TestPDF20Encryption(t *testing.T) {
   193  	// PDF 2.0 encryption assumes aes/256.
   194  	for _, fileName := range []string{
   195  		"i277.pdf",
   196  		"imageWithBPC.pdf",
   197  		"pageLevelOutputIntent.pdf",
   198  		"SimplePDF2.0.pdf",
   199  		"utf8stringAndAnnotation.pdf",
   200  		"utf8test.pdf",
   201  		"viaIncrementalSave.pdf",
   202  		"withOffsetStart.pdf",
   203  	} {
   204  		testEncryption(t, filepath.Join("pdf20", fileName), "aes", 256)
   205  	}
   206  }
   207  
   208  func TestSetPermissions(t *testing.T) {
   209  	msg := "TestSetPermissions"
   210  	inFile := filepath.Join(inDir, "5116.DCT_Filter.pdf")
   211  	outFile := filepath.Join(outDir, "out.pdf")
   212  
   213  	conf := confForAlgorithm(true, 256, "upw", "opw")
   214  	permNew := model.PermissionsNone | model.PermissionPrintRev2 | model.PermissionPrintRev3
   215  	conf.Permissions = permNew
   216  
   217  	if err := api.EncryptFile(inFile, outFile, conf); err != nil {
   218  		t.Fatalf("%s: encrypt %s: %v\n", msg, outFile, err)
   219  	}
   220  
   221  	conf = confForAlgorithm(true, 256, "upw", "opw")
   222  	p, err := api.GetPermissionsFile(outFile, conf)
   223  	if err != nil {
   224  		t.Fatalf("%s: get permissions %s: %v\n", msg, outFile, err)
   225  	}
   226  	if p == nil {
   227  		t.Fatalf("%s: missing permissions", msg)
   228  	}
   229  	if uint16(*p) != uint16(permNew) {
   230  		t.Fatalf("%s: got: %d want: %d", msg, uint16(*p), uint16(permNew))
   231  	}
   232  }