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 }