github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/sign.go (about) 1 /* 2 Copyright 2025 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 api 18 19 import ( 20 "fmt" 21 "os" 22 23 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" 24 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model" 25 "github.com/pkg/errors" 26 ) 27 28 func signatureStats(signValidResults []*model.SignatureValidationResult) model.SignatureStats { 29 sigStats := model.SignatureStats{Total: len(signValidResults)} 30 for _, svr := range signValidResults { 31 signed, signedVisible, unsigned, unsignedVisible := sigStats.Counter(svr) 32 if svr.Signed { 33 *signed++ 34 if svr.Visible { 35 *signedVisible++ 36 } 37 continue 38 } 39 *unsigned++ 40 if svr.Visible { 41 *unsignedVisible++ 42 } 43 } 44 return sigStats 45 } 46 47 func statsCounter(stats model.SignatureStats, ss *[]string) { 48 plural := func(count int) string { 49 if count == 1 { 50 return "" 51 } 52 return "s" 53 } 54 55 if stats.FormSigned > 0 { 56 *ss = append(*ss, fmt.Sprintf("%d signed form signature%s (%d visible)", stats.FormSigned, plural(stats.FormSigned), stats.FormSignedVisible)) 57 } 58 if stats.FormUnsigned > 0 { 59 *ss = append(*ss, fmt.Sprintf("%d unsigned form signature%s (%d visible)", stats.FormUnsigned, plural(stats.FormUnsigned), stats.FormUnsignedVisible)) 60 } 61 62 if stats.PageSigned > 0 { 63 *ss = append(*ss, fmt.Sprintf("%d signed page signature%s (%d visible)", stats.PageSigned, plural(stats.PageSigned), stats.PageSignedVisible)) 64 } 65 if stats.PageUnsigned > 0 { 66 *ss = append(*ss, fmt.Sprintf("%d unsigned page signature%s (%d visible)", stats.PageUnsigned, plural(stats.PageUnsigned), stats.PageUnsignedVisible)) 67 } 68 69 if stats.URSigned > 0 { 70 *ss = append(*ss, fmt.Sprintf("%d signed usage rights signature%s (%d visible)", stats.URSigned, plural(stats.URSigned), stats.URSignedVisible)) 71 } 72 if stats.URUnsigned > 0 { 73 *ss = append(*ss, fmt.Sprintf("%d unsigned usage rights signature%s (%d visible)", stats.URUnsigned, plural(stats.URUnsigned), stats.URUnsignedVisible)) 74 } 75 76 if stats.DTSSigned > 0 { 77 *ss = append(*ss, fmt.Sprintf("%d signed doc timestamp signature%s (%d visible)", stats.DTSSigned, plural(stats.DTSSigned), stats.DTSSignedVisible)) 78 } 79 if stats.DTSUnsigned > 0 { 80 *ss = append(*ss, fmt.Sprintf("%d unsigned doc timestamp signature%s (%d visible)", stats.DTSUnsigned, plural(stats.DTSUnsigned), stats.DTSUnsignedVisible)) 81 } 82 } 83 84 func digest(signValidResults []*model.SignatureValidationResult, full bool) []string { 85 var ss []string 86 87 if full { 88 ss = append(ss, "") 89 for i, r := range signValidResults { 90 //ss = append(ss, fmt.Sprintf("%d. Sisgnature:\n", i+1)) 91 ss = append(ss, fmt.Sprintf("%d:", i+1)) 92 ss = append(ss, r.String()+"\n") 93 } 94 return ss 95 } 96 97 if len(signValidResults) == 1 { 98 svr := signValidResults[0] 99 ss = append(ss, "") 100 ss = append(ss, fmt.Sprintf("1 %s", svr.Signature.String(svr.Status))) 101 ss = append(ss, fmt.Sprintf(" Status: %s", svr.Status)) 102 s := svr.Reason.String() 103 if svr.Reason == model.SignatureReasonInternal { 104 if len(svr.Problems) > 0 { 105 s = svr.Problems[0] 106 } 107 } 108 ss = append(ss, fmt.Sprintf(" Reason: %s", s)) 109 ss = append(ss, fmt.Sprintf(" Signed: %s", svr.SigningTime())) 110 return ss 111 } 112 113 stats := signatureStats(signValidResults) 114 115 ss = append(ss, "") 116 ss = append(ss, fmt.Sprintf("%d signatures present:", stats.Total)) 117 118 statsCounter(stats, &ss) 119 120 for i, svr := range signValidResults { 121 ss = append(ss, fmt.Sprintf("\n%d:", i+1)) 122 ss = append(ss, fmt.Sprintf(" Type: %s", svr.Signature.String(svr.Status))) 123 ss = append(ss, fmt.Sprintf(" Status: %s", svr.Status.String())) 124 s := svr.Reason.String() 125 if svr.Reason == model.SignatureReasonInternal { 126 if len(svr.Problems) > 0 { 127 s = svr.Problems[0] 128 } 129 } 130 ss = append(ss, fmt.Sprintf(" Reason: %s", s)) 131 ss = append(ss, fmt.Sprintf(" Signed: %s", svr.SigningTime())) 132 } 133 134 return ss 135 } 136 137 // ValidateSignatures validates signatures of inFile and returns the signature validation results. 138 func ValidateSignatures(inFile string, all bool, conf *model.Configuration) ([]*model.SignatureValidationResult, error) { 139 140 if conf == nil { 141 conf = model.NewDefaultConfiguration() 142 } 143 conf.Cmd = model.VALIDATESIGNATURE 144 145 if _, err := LoadCertificates(); err != nil { 146 return nil, err 147 } 148 149 f, err := os.Open(inFile) 150 if err != nil { 151 return nil, err 152 } 153 154 ctx, err := ReadValidateAndOptimize(f, conf) 155 if err != nil { 156 return nil, err 157 } 158 159 if len(ctx.Signatures) == 0 && !ctx.SignatureExist && !ctx.AppendOnly { 160 return nil, errors.New("pdfcpu: No signatures present.") 161 } 162 163 return pdfcpu.ValidateSignatures(f, ctx, all) 164 } 165 166 // ValidateSignaturesFile validates signatures of inFile. 167 // all: processes all signatures meaning not only the authoritative/certified signature.. 168 // full: verbose output including cert chain and problems encountered. 169 func ValidateSignaturesFile(inFile string, all, full bool, conf *model.Configuration) ([]string, error) { 170 if conf == nil { 171 conf = model.NewDefaultConfiguration() 172 } 173 174 signValidResults, err := ValidateSignatures(inFile, all, conf) 175 if err != nil { 176 return nil, err 177 } 178 179 return digest(signValidResults, full), nil 180 }