-
-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathcrypto_test.go
More file actions
177 lines (161 loc) · 5.61 KB
/
Copy pathcrypto_test.go
File metadata and controls
177 lines (161 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package pdfsign_test
import (
"crypto/x509"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/digitorus/pdfsign"
"github.com/digitorus/pdfsign/internal/testpki"
)
func TestCryptoAlgorithms(t *testing.T) {
testCases := []struct {
name string
profile testpki.KeyProfile
}{
{"RSA_2048", testpki.RSA_2048},
{"RSA_3072", testpki.RSA_3072},
{"RSA_4096", testpki.RSA_4096},
{"ECDSA_P256", testpki.ECDSA_P256},
{"ECDSA_P384", testpki.ECDSA_P384},
{"ECDSA_P521", testpki.ECDSA_P521},
{"MLDSA_44", testpki.MLDSA_44},
{"MLDSA_65", testpki.MLDSA_65},
{"MLDSA_87", testpki.MLDSA_87},
}
inputFile := "testfiles/testfile12.pdf"
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// 1. Initialize PKI with specific profile
pki := testpki.NewTestPKIWithConfig(t, testpki.TestPKIConfig{
Profile: tc.profile,
IntermediateCAs: 1, // Standard chain
})
defer pki.Close()
pki.StartCRLServer()
// 2. Issue Leaf Certificate
key, cert := pki.IssueLeaf("Crypto Test User")
chain := pki.Chain()
// 3. Prepare PDF
f, err := os.Open(inputFile)
if err != nil {
t.Fatalf("failed to open input file: %v", err)
}
defer func() { _ = f.Close() }()
info, err := f.Stat()
if err != nil {
t.Fatalf("failed to stat input file: %v", err)
}
doc, err := pdfsign.Open(f, info.Size())
if err != nil {
t.Fatalf("failed to create document: %v", err)
}
// 4. Sign PDF
// Sign() stages the operation. Write() executes it.
doc.Sign(key, cert, chain...)
// 5. Verify Output
outputDir := "testfiles/crypto_test_output"
_ = os.MkdirAll(outputDir, 0755)
outputPath := filepath.Join(outputDir, fmt.Sprintf("signed_%s.pdf", tc.name))
outFile, err := os.Create(outputPath)
if err != nil {
t.Fatalf("failed to create output file: %v", err)
}
defer func() { _ = outFile.Close() }()
if _, err := doc.Write(outFile); err != nil {
t.Errorf("Sign() failed for %s: %v", tc.name, err)
}
_ = outFile.Close() // Close explicitly before validation
// 6. Internal Verification (pdf package)
// Verify using our own library to ensure cryptographic validity.
verifyDoc, err := pdfsign.OpenFile(outputPath)
if err != nil {
t.Fatalf("failed to open signed pdf for verification: %v", err)
}
// Configure verification based on profile.
// TrustSelfSigned(true) is required because this test signs with a
// self-signed test CA that isn't in the system trust store; the
// test is only checking cryptographic/key-size correctness here.
var verifyResult *pdfsign.VerifyBuilder
switch tc.profile {
case testpki.RSA_2048:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.RSA).
MinRSAKeySize(2048)
case testpki.RSA_3072:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.RSA).
MinRSAKeySize(3072)
case testpki.RSA_4096:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.RSA).
MinRSAKeySize(4096)
case testpki.ECDSA_P256:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.ECDSA).
MinECDSAKeySize(256)
case testpki.ECDSA_P384:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.ECDSA).
MinECDSAKeySize(384)
case testpki.ECDSA_P521:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.ECDSA).
MinECDSAKeySize(521)
case testpki.MLDSA_44, testpki.MLDSA_65, testpki.MLDSA_87:
verifyResult = verifyDoc.Verify().
TrustSelfSigned(true).
AllowedAlgorithms(x509.MLDSA)
}
if verifyResult.Err() != nil {
t.Fatalf("internal verification failed to execute: %v", verifyResult.Err())
}
if !verifyResult.Valid() {
t.Errorf("internal verification reported invalid signature for %s", tc.name)
for _, sig := range verifyResult.Signatures() {
if !sig.Valid {
t.Errorf(" invalid signature: %s (Reason: %s)", sig.SignerName, sig.Reason)
for _, w := range sig.Warnings {
t.Logf(" warning: %s", w)
}
}
}
} else {
t.Logf("internal verification passed for %s (algo/size checked via API)", tc.name)
}
// 7. External Validation (pdfcpu) if available
// Validate PDF structure to ensure no corruption was introduced.
// We use a known-good input file (testfile12.pdf) so validation should pass.
pdfcpuPath, err := exec.LookPath("pdfcpu")
if err == nil {
// Try strict mode first; fall back to relaxed. Our catalog
// intentionally sets /Version to bump the effective PDF
// version above the input file's header version (needed for
// SigFlags/AcroForm compliance, see sign/pdfcatalog.go) -
// this is valid, spec-sanctioned usage (ISO 32000-1 7.5.2)
// that some strict validators, including pdfcpu, still flag.
// Only a relaxed-mode failure indicates actual corruption.
cmd := exec.Command(pdfcpuPath, "validate", "-m", "strict", outputPath)
if strictOut, strictErr := cmd.CombinedOutput(); strictErr != nil {
cmd := exec.Command(pdfcpuPath, "validate", "-m", "relaxed", outputPath)
if out, err := cmd.CombinedOutput(); err != nil {
t.Errorf("pdfcpu validation failed for %s: %v\nOutput: %s", tc.name, err, out)
} else {
t.Logf("pdfcpu validated %s successfully (relaxed mode; strict mode output: %s)", tc.name, strictOut)
}
} else {
t.Logf("pdfcpu validated %s successfully", tc.name)
}
} else {
t.Logf("pdfcpu not found, skipping external validation for %s", tc.name)
}
})
}
}