github.com/unidoc/unidoc@v2.2.0+incompatible/pdf/core/crypt_test.go (about)

     1  /*
     2   * This file is subject to the terms and conditions defined in
     3   * file 'LICENSE.md', which is part of this source code package.
     4   */
     5  
     6  // Test the PDF crypt support.
     7  
     8  package core
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"math"
    14  	"math/rand"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/unidoc/unidoc/common"
    20  )
    21  
    22  func init() {
    23  	common.SetLogger(common.ConsoleLogger{})
    24  }
    25  
    26  func TestPadding(t *testing.T) {
    27  	crypter := PdfCrypt{}
    28  
    29  	// Case 1 empty pass, should match padded string.
    30  	key := crypter.paddedPass([]byte(""))
    31  	if len(key) != 32 {
    32  		t.Errorf("Fail, expected padded pass length = 32 (%d)", len(key))
    33  	}
    34  	if key[0] != 0x28 {
    35  		t.Errorf("key[0] != 0x28 (%q in %q)", key[0], key)
    36  	}
    37  	if key[31] != 0x7A {
    38  		t.Errorf("key[31] != 0x7A (%q in %q)", key[31], key)
    39  	}
    40  
    41  	// Case 2, non empty pass.
    42  	key = crypter.paddedPass([]byte("bla"))
    43  	if len(key) != 32 {
    44  		t.Errorf("Fail, expected padded pass length = 32 (%d)", len(key))
    45  	}
    46  	if string(key[0:3]) != "bla" {
    47  		t.Errorf("Expecting start with bla (%s)", key)
    48  	}
    49  	if key[3] != 0x28 {
    50  		t.Errorf("key[3] != 0x28 (%q in %q)", key[3], key)
    51  	}
    52  	if key[31] != 0x64 {
    53  		t.Errorf("key[31] != 0x64 (%q in %q)", key[31], key)
    54  	}
    55  }
    56  
    57  // Test algorithm 2.
    58  func TestAlg2(t *testing.T) {
    59  	crypter := PdfCrypt{}
    60  	crypter.V = 2
    61  	crypter.R = 3
    62  	crypter.P = -3904
    63  	crypter.Id0 = string([]byte{0x4e, 0x00, 0x99, 0xe5, 0x36, 0x78, 0x93, 0x24,
    64  		0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4})
    65  	crypter.O = []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B,
    66  		0x5C, 0x72, 0x64, 0xA9, 0x5C, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51,
    67  		0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86,
    68  		0x72, 0x6A, 0x8C, 0xDB}
    69  	crypter.Length = 128
    70  	crypter.EncryptMetadata = true
    71  
    72  	key := crypter.Alg2([]byte(""))
    73  
    74  	keyExp := []byte{0xf8, 0x94, 0x9c, 0x5a, 0xf5, 0xa0, 0xc0, 0xca,
    75  		0x30, 0xb8, 0x91, 0xc1, 0xbb, 0x2c, 0x4f, 0xf5}
    76  
    77  	if string(key) != string(keyExp) {
    78  		common.Log.Debug("   Key (%d): % x", len(key), key)
    79  		common.Log.Debug("KeyExp (%d): % x", len(keyExp), keyExp)
    80  		t.Errorf("alg2 -> key != expected\n")
    81  	}
    82  
    83  }
    84  
    85  // Test algorithm 3.
    86  func TestAlg3(t *testing.T) {
    87  	crypter := PdfCrypt{}
    88  	crypter.V = 2
    89  	crypter.R = 3
    90  	crypter.P = -3904
    91  	crypter.Id0 = string([]byte{0x4e, 0x00, 0x99, 0xe5, 0x36, 0x78, 0x93, 0x24,
    92  		0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4})
    93  	Oexp := []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B,
    94  		0x0d, 0x64, 0xA9, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51,
    95  		0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86,
    96  		0x72, 0x6A, 0x8C, 0xDB}
    97  	crypter.Length = 128
    98  	crypter.EncryptMetadata = true
    99  
   100  	O, err := crypter.Alg3([]byte(""), []byte("test"))
   101  	if err != nil {
   102  		t.Errorf("crypt alg3 error %s", err)
   103  		return
   104  	}
   105  
   106  	if string(O) != string(Oexp) {
   107  		common.Log.Debug("   O (%d): % x", len(O), O)
   108  		common.Log.Debug("Oexp (%d): % x", len(Oexp), Oexp)
   109  		t.Errorf("alg3 -> key != expected")
   110  	}
   111  }
   112  
   113  // Test algorithm 5 for computing dictionary's U (user password) value
   114  // valid for R >= 3.
   115  func TestAlg5(t *testing.T) {
   116  	crypter := PdfCrypt{}
   117  	crypter.V = 2
   118  	crypter.R = 3
   119  	crypter.P = -3904
   120  	crypter.Id0 = string([]byte{0x4e, 0x00, 0x99, 0xe5, 0x36, 0x78, 0x93, 0x24,
   121  		0xff, 0xd5, 0x82, 0xe4, 0xec, 0x0e, 0xa3, 0xb4})
   122  	crypter.O = []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B,
   123  		0x5C, 0x72, 0x64, 0xA9, 0x5C, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51,
   124  		0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86,
   125  		0x72, 0x6A, 0x8C, 0xDB}
   126  	crypter.Length = 128
   127  	crypter.EncryptMetadata = true
   128  
   129  	U, _, err := crypter.Alg5([]byte(""))
   130  	if err != nil {
   131  		t.Errorf("Error %s", err)
   132  		return
   133  	}
   134  
   135  	Uexp := []byte{0x59, 0x66, 0x38, 0x6c, 0x76, 0xfe, 0x95, 0x7d, 0x3d,
   136  		0x0d, 0x14, 0x3d, 0x36, 0xfd, 0x01, 0x3d, 0x00, 0x00, 0x00, 0x00,
   137  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
   138  
   139  	if string(U[0:16]) != string(Uexp[0:16]) {
   140  		common.Log.Info("   U (%d): % x", len(U), U)
   141  		common.Log.Info("Uexp (%d): % x", len(Uexp), Uexp)
   142  		t.Errorf("U != expected\n")
   143  	}
   144  }
   145  
   146  // Test decrypting. Example with V=2, R=3, using standard algorithm.
   147  func TestDecryption1(t *testing.T) {
   148  	crypter := PdfCrypt{}
   149  	crypter.DecryptedObjects = map[PdfObject]bool{}
   150  	// Default algorithm is V2 (RC4).
   151  	crypter.CryptFilters = newCryptFiltersV2(crypter.Length)
   152  	crypter.V = 2
   153  	crypter.R = 3
   154  	crypter.P = -3904
   155  	crypter.Id0 = string([]byte{0x5f, 0x91, 0xff, 0xf2, 0x00, 0x88, 0x13,
   156  		0x5f, 0x30, 0x24, 0xd1, 0x0f, 0x28, 0x31, 0xc6, 0xfa})
   157  	crypter.O = []byte{0xE6, 0x00, 0xEC, 0xC2, 0x02, 0x88, 0xAD, 0x8B,
   158  		0x0d, 0x64, 0xA9, 0x29, 0xC6, 0xA8, 0x3E, 0xE2, 0x51,
   159  		0x76, 0x79, 0xAA, 0x02, 0x18, 0xBE, 0xCE, 0xEA, 0x8B, 0x79, 0x86,
   160  		0x72, 0x6A, 0x8C, 0xDB}
   161  	crypter.U = []byte{0xED, 0x5B, 0xA7, 0x76, 0xFD, 0xD8, 0xE3, 0x89,
   162  		0x4F, 0x54, 0x05, 0xC1, 0x3B, 0xFD, 0x86, 0xCF, 0x00, 0x00, 0x00,
   163  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   164  		0x00, 0x00}
   165  	crypter.Length = 128
   166  	crypter.EncryptMetadata = true
   167  
   168  	streamData := []byte{0xBC, 0x89, 0x86, 0x8B, 0x3E, 0xCF, 0x24, 0x1C,
   169  		0xC4, 0x88, 0xF3, 0x60, 0x74, 0x8A, 0x22, 0xE3, 0xAD, 0xF4, 0x48,
   170  		0x8E, 0x20, 0x94, 0x06, 0x4B, 0x4B, 0xB5, 0x3E, 0x93, 0x89, 0x4E,
   171  		0x32, 0x38, 0xB4, 0xF6, 0x05, 0x3C, 0x5D, 0x0C, 0x12, 0xE4, 0xEB,
   172  		0x9B, 0x8D, 0x26, 0x32, 0x7B, 0x09, 0x97, 0xA1, 0xC5, 0x98, 0xF6,
   173  		0xE7, 0x1C, 0x3B}
   174  
   175  	// Plain text stream (hello world).
   176  	exp := []byte{0x20, 0x20, 0x42, 0x54, 0x0A, 0x20, 0x20, 0x20, 0x20,
   177  		0x2F, 0x46, 0x31, 0x20, 0x31, 0x38, 0x20, 0x54, 0x66, 0x0A, 0x20,
   178  		0x20, 0x20, 0x20, 0x30, 0x20, 0x30, 0x20, 0x54, 0x64, 0x0A, 0x20,
   179  		0x20, 0x20, 0x20, 0x28, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57,
   180  		0x6F, 0x72, 0x6C, 0x64, 0x29, 0x20, 0x54, 0x6A, 0x0A, 0x20, 0x20,
   181  		0x45, 0x54}
   182  	rawText := "2 0 obj\n<< /Length 55 >>\nstream\n" + string(streamData) + "\nendstream\n"
   183  
   184  	parser := PdfParser{}
   185  	parser.xrefs = make(XrefTable)
   186  	parser.objstms = make(ObjectStreams)
   187  	parser.rs, parser.reader, parser.fileSize = makeReaderForText(rawText)
   188  	parser.crypter = &crypter
   189  
   190  	obj, err := parser.ParseIndirectObject()
   191  	if err != nil {
   192  		t.Errorf("Error parsing object")
   193  		return
   194  	}
   195  
   196  	so, ok := obj.(*PdfObjectStream)
   197  	if !ok {
   198  		t.Errorf("Should be stream (is %q)", obj)
   199  		return
   200  	}
   201  
   202  	authenticated, err := parser.Decrypt([]byte(""))
   203  	if err != nil {
   204  		t.Errorf("Error authenticating")
   205  		return
   206  	}
   207  	if !authenticated {
   208  		t.Errorf("Failed to authenticate")
   209  		return
   210  	}
   211  
   212  	parser.crypter.Decrypt(so, 0, 0)
   213  	if string(so.Stream) != string(exp) {
   214  		t.Errorf("Stream content wrong")
   215  		return
   216  	}
   217  }
   218  
   219  func BenchmarkAlg2b(b *testing.B) {
   220  	// hash runs a variable number of rounds, so we need to have a
   221  	// deterministic random source to make benchmark results comparable
   222  	r := rand.New(rand.NewSource(1234567))
   223  	const n = 20
   224  	pass := make([]byte, n)
   225  	r.Read(pass)
   226  	data := make([]byte, n+8+48)
   227  	r.Read(data)
   228  	user := make([]byte, 48)
   229  	r.Read(user)
   230  
   231  	b.ResetTimer()
   232  	b.ReportAllocs()
   233  	for i := 0; i < b.N; i++ {
   234  		_ = alg2b(data, pass, user)
   235  	}
   236  }
   237  
   238  func TestAESv3(t *testing.T) {
   239  	const keySize = 32
   240  
   241  	seed := time.Now().UnixNano()
   242  	rand := rand.New(rand.NewSource(seed))
   243  
   244  	var cases = []struct {
   245  		Name      string
   246  		EncMeta   bool
   247  		UserPass  string
   248  		OwnerPass string
   249  	}{
   250  		{
   251  			Name: "simple", EncMeta: true,
   252  			UserPass: "user", OwnerPass: "owner",
   253  		},
   254  		{
   255  			Name: "utf8", EncMeta: false,
   256  			UserPass: "æøå-u", OwnerPass: "æøå-o",
   257  		},
   258  		{
   259  			Name: "long", EncMeta: true,
   260  			UserPass:  strings.Repeat("user", 80),
   261  			OwnerPass: strings.Repeat("owner", 80),
   262  		},
   263  	}
   264  
   265  	const (
   266  		perms = 0x12345678
   267  	)
   268  
   269  	for _, R := range []int{5, 6} {
   270  		R := R
   271  		t.Run(fmt.Sprintf("R=%d", R), func(t *testing.T) {
   272  			for _, c := range cases {
   273  				c := c
   274  				t.Run(c.Name, func(t *testing.T) {
   275  					fkey := make([]byte, keySize)
   276  					rand.Read(fkey)
   277  
   278  					crypt := &PdfCrypt{
   279  						V: 5, R: R,
   280  						P:               perms,
   281  						EncryptionKey:   append([]byte{}, fkey...),
   282  						EncryptMetadata: c.EncMeta,
   283  					}
   284  
   285  					// generate encryption parameters
   286  					err := crypt.generateR6([]byte(c.UserPass), []byte(c.OwnerPass))
   287  					if err != nil {
   288  						t.Fatal("Failed to encrypt:", err)
   289  					}
   290  
   291  					// Perms and EncryptMetadata are checked as a part of alg2a
   292  
   293  					// decrypt using user password
   294  					crypt.EncryptionKey = nil
   295  					ok, err := crypt.alg2a([]byte(c.UserPass))
   296  					if err != nil || !ok {
   297  						t.Error("Failed to authenticate user pass:", err)
   298  					} else if !bytes.Equal(crypt.EncryptionKey, fkey) {
   299  						t.Error("wrong encryption key")
   300  					}
   301  
   302  					// decrypt using owner password
   303  					crypt.EncryptionKey = nil
   304  					ok, err = crypt.alg2a([]byte(c.OwnerPass))
   305  					if err != nil || !ok {
   306  						t.Error("Failed to authenticate owner pass:", err)
   307  					} else if !bytes.Equal(crypt.EncryptionKey, fkey) {
   308  						t.Error("wrong encryption key")
   309  					}
   310  
   311  					// try to elevate user permissions
   312  					crypt.P = math.MaxUint32
   313  
   314  					crypt.EncryptionKey = nil
   315  					ok, err = crypt.alg2a([]byte(c.UserPass))
   316  					if R == 5 {
   317  						// it's actually possible with R=5, since Perms is not generated
   318  						if err != nil || !ok {
   319  							t.Error("Failed to authenticate user pass:", err)
   320  						}
   321  					} else {
   322  						// not possible in R=6, should return an error
   323  						if err == nil || ok {
   324  							t.Error("was able to elevate permissions with R=6")
   325  						}
   326  					}
   327  				})
   328  			}
   329  		})
   330  	}
   331  }