@@ -19,13 +19,12 @@ import (
19
19
"os"
20
20
"os/exec"
21
21
"path/filepath"
22
- "reflect"
23
22
"runtime"
24
23
"sort"
25
24
"strings"
26
25
"time"
27
26
28
- "go.mozilla.org /pkcs7"
27
+ "github.com/secDre4mer /pkcs7"
29
28
)
30
29
31
30
// The options for the WIN_CERTIFICATE Revision member include
@@ -66,13 +65,18 @@ var (
66
65
`invalid certificate header in security directory` )
67
66
)
68
67
68
+ type CertificateSection struct {
69
+ Header WinCertificate `json:"header"`
70
+ Raw []byte `json:"-"`
71
+
72
+ Certificates []Certificate
73
+ }
74
+
69
75
// Certificate directory.
70
76
type Certificate struct {
71
- Header WinCertificate `json:"header"`
72
77
Content pkcs7.PKCS7 `json:"-"`
73
78
SignatureContent AuthenticodeContent `json:"-"`
74
79
SignatureValid bool `json:"signature_valid"`
75
- Raw []byte `json:"-"`
76
80
Info CertInfo `json:"info"`
77
81
Verified bool `json:"verified"`
78
82
}
@@ -309,101 +313,68 @@ func (pe *File) AuthentihashExt(hashers ...hash.Hash) [][]byte {
309
313
// bind an Authenticode-signed file to the identity of a software publisher.
310
314
// This data are not loaded into memory as part of the image file.
311
315
func (pe * File ) parseSecurityDirectory (rva , size uint32 ) error {
312
-
313
- var pkcs * pkcs7.PKCS7
314
- var certValid bool
315
- certInfo := CertInfo {}
316
- certHeader := WinCertificate {}
316
+ var certHeader WinCertificate
317
317
certSize := uint32 (binary .Size (certHeader ))
318
318
signatureContent := AuthenticodeContent {}
319
- var signatureValid bool
320
- var certContent []byte
321
319
322
320
// The virtual address value from the Certificate Table entry in the
323
321
// Optional Header Data Directory is a file offset to the first attribute
324
322
// certificate entry.
325
323
fileOffset := rva
326
324
327
- // PE file can be dual signed by applying multiple signatures, which is
328
- // strongly recommended when using deprecated hashing algorithms such as MD5.
329
- for {
330
- err := pe .structUnpack (& certHeader , fileOffset , certSize )
331
- if err != nil {
332
- return ErrOutsideBoundary
333
- }
325
+ err := pe .structUnpack (& certHeader , fileOffset , certSize )
326
+ if err != nil {
327
+ return ErrOutsideBoundary
328
+ }
334
329
335
- if fileOffset + certHeader .Length > pe . size {
336
- return ErrOutsideBoundary
337
- }
330
+ if certHeader .Length > size {
331
+ return ErrOutsideBoundary
332
+ }
338
333
339
- if certHeader .Length == 0 {
340
- return ErrSecurityDataDirInvalid
341
- }
334
+ if fileOffset + certHeader .Length > pe . size {
335
+ return ErrOutsideBoundary
336
+ }
342
337
343
- certContent = pe .data [fileOffset + certSize : fileOffset + certHeader .Length ]
344
- pkcs , err = pkcs7 .Parse (certContent )
338
+ if certHeader .Length == 0 {
339
+ return ErrSecurityDataDirInvalid
340
+ }
341
+
342
+ pe .HasCertificate = true
343
+ pe .Certificates .Header = certHeader
344
+ pe .Certificates .Raw = pe .data [fileOffset + certSize : fileOffset + certHeader .Length ]
345
+
346
+ certContent := pe .Certificates .Raw
347
+ for {
348
+ pkcs , err := pkcs7 .Parse (certContent )
345
349
if err != nil {
346
- pe .Certificates = Certificate {Header : certHeader , Raw : certContent }
347
- pe .HasCertificate = true
348
350
return err
349
351
}
350
-
351
352
// The pkcs7.PKCS7 structure contains many fields that we are not
352
353
// interested to, so create another structure, similar to _CERT_INFO
353
354
// structure which contains only the important information.
354
- serialNumber := pkcs .Signers [0 ].IssuerAndSerialNumber .SerialNumber
355
- for _ , cert := range pkcs .Certificates {
356
- if ! reflect .DeepEqual (cert .SerialNumber , serialNumber ) {
357
- continue
358
- }
359
-
360
- certInfo .SerialNumber = hex .EncodeToString (cert .SerialNumber .Bytes ())
361
- certInfo .PublicKeyAlgorithm = cert .PublicKeyAlgorithm
362
- certInfo .SignatureAlgorithm = cert .SignatureAlgorithm
363
-
364
- certInfo .NotAfter = cert .NotAfter
365
- certInfo .NotBefore = cert .NotBefore
366
-
367
- // Issuer infos
368
- if len (cert .Issuer .Country ) > 0 {
369
- certInfo .Issuer = cert .Issuer .Country [0 ]
370
- }
371
-
372
- if len (cert .Issuer .Province ) > 0 {
373
- certInfo .Issuer += ", " + cert .Issuer .Province [0 ]
374
- }
375
-
376
- if len (cert .Issuer .Locality ) > 0 {
377
- certInfo .Issuer += ", " + cert .Issuer .Locality [0 ]
378
- }
379
-
380
- certInfo .Issuer += ", " + cert .Issuer .CommonName
381
-
382
- // Subject infos
383
- if len (cert .Subject .Country ) > 0 {
384
- certInfo .Subject = cert .Subject .Country [0 ]
385
- }
355
+ var signerCertificate = pkcs .GetOnlySigner ()
356
+ if signerCertificate == nil {
357
+ return errors .New ("could not find signer certificate" )
358
+ }
386
359
387
- if len (cert .Subject .Province ) > 0 {
388
- certInfo .Subject += ", " + cert .Subject .Province [0 ]
389
- }
360
+ var certInfo CertInfo
390
361
391
- if len (cert .Subject .Locality ) > 0 {
392
- certInfo .Subject += ", " + cert .Subject .Locality [0 ]
393
- }
362
+ certInfo .SerialNumber = hex .EncodeToString (signerCertificate .SerialNumber .Bytes ())
363
+ certInfo .PublicKeyAlgorithm = signerCertificate .PublicKeyAlgorithm
394
364
395
- if len (cert .Subject .Organization ) > 0 {
396
- certInfo .Subject += ", " + cert .Subject .Organization [0 ]
397
- }
365
+ certInfo .NotAfter = signerCertificate .NotAfter
366
+ certInfo .NotBefore = signerCertificate .NotBefore
398
367
399
- certInfo .Subject += ", " + cert .Subject .CommonName
368
+ // Issuer infos
369
+ certInfo .Issuer = formatPkixName (signerCertificate .Issuer )
400
370
401
- break
402
- }
371
+ // Subject infos
372
+ certInfo . Subject = formatPkixName ( signerCertificate . Subject )
403
373
404
374
// Let's mark the file as signed, then we verify if the signature is valid.
405
375
pe .IsSigned = true
406
376
377
+ var certValid bool
407
378
// Let's load the system root certs.
408
379
if ! pe .opts .DisableCertValidation {
409
380
var certPool * x509.CertPool
@@ -425,6 +396,7 @@ func (pe *File) parseSecurityDirectory(rva, size uint32) error {
425
396
}
426
397
}
427
398
399
+ var signatureValid bool
428
400
signatureContent , err = parseAuthenticodeContent (pkcs .Content )
429
401
if err != nil {
430
402
pe .logger .Errorf ("could not parse authenticode content: %v" , err )
@@ -434,24 +406,30 @@ func (pe *File) parseSecurityDirectory(rva, size uint32) error {
434
406
signatureValid = bytes .Equal (authentihash , signatureContent .HashResult )
435
407
}
436
408
437
- // Subsequent entries are accessed by advancing that entry's dwLength
438
- // bytes, rounded up to an 8-byte multiple, from the start of the
439
- // current attribute certificate entry.
440
- nextOffset := certHeader .Length + fileOffset
441
- nextOffset = ((nextOffset + 8 - 1 ) / 8 ) * 8
409
+ certInfo .SignatureAlgorithm = signatureContent .Algorithm
442
410
443
- // Check if we walked the entire table.
444
- if nextOffset == fileOffset + size {
445
- break
446
- }
411
+ pe .Certificates .Certificates = append (pe .Certificates .Certificates , Certificate {
412
+ Content : * pkcs ,
413
+ SignatureContent : signatureContent ,
414
+ SignatureValid : signatureValid ,
415
+ Info : certInfo ,
416
+ Verified : certValid ,
417
+ })
447
418
448
- fileOffset = nextOffset
419
+ // Subsequent certificates are an (unsigned) attribute of the PKCS#7
420
+ var newCert asn1.RawValue
421
+ nestedSignatureOid := asn1.ObjectIdentifier {1 , 3 , 6 , 1 , 4 , 1 , 311 , 2 , 4 , 1 }
422
+ err = pkcs .UnmarshalUnsignedAttribute (nestedSignatureOid , & newCert )
423
+ if err != nil {
424
+ var attributeNotFound pkcs7.AttributeNotFoundError
425
+ if errors .As (err , & attributeNotFound ) {
426
+ break // No further nested certificates
427
+ }
428
+ return err
429
+ }
430
+ certContent = newCert .FullBytes
449
431
}
450
432
451
- pe .Certificates = Certificate {Header : certHeader , Content : * pkcs ,
452
- Raw : certContent , Info : certInfo , Verified : certValid ,
453
- SignatureContent : signatureContent , SignatureValid : signatureValid }
454
- pe .HasCertificate = true
455
433
return nil
456
434
}
457
435
@@ -542,26 +520,35 @@ type DigestInfo struct {
542
520
}
543
521
544
522
// Translation of algorithm identifier to hash algorithm, copied from pkcs7.getHashForOID
545
- func parseHashAlgorithm (identifier pkix.AlgorithmIdentifier ) (crypto.Hash , error ) {
523
+ func parseHashAlgorithm (identifier pkix.AlgorithmIdentifier ) (crypto.Hash , x509. SignatureAlgorithm , error ) {
546
524
oid := identifier .Algorithm
547
525
switch {
548
- case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA1 ), oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA1 ),
549
- oid .Equal (pkcs7 .OIDDigestAlgorithmDSA ), oid .Equal (pkcs7 .OIDDigestAlgorithmDSASHA1 ),
550
- oid .Equal (pkcs7 .OIDEncryptionAlgorithmRSA ):
551
- return crypto .SHA1 , nil
552
- case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA256 ), oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA256 ):
553
- return crypto .SHA256 , nil
554
- case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA384 ), oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA384 ):
555
- return crypto .SHA384 , nil
556
- case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA512 ), oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA512 ):
557
- return crypto .SHA512 , nil
558
- }
559
- return crypto .Hash (0 ), pkcs7 .ErrUnsupportedAlgorithm
526
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA1 ), oid .Equal (pkcs7 .OIDEncryptionAlgorithmRSA ):
527
+ return crypto .SHA1 , x509 .SHA1WithRSA , nil
528
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA1 ):
529
+ return crypto .SHA1 , x509 .ECDSAWithSHA1 , nil
530
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmDSA ), oid .Equal (pkcs7 .OIDDigestAlgorithmDSASHA1 ):
531
+ return crypto .SHA1 , x509 .DSAWithSHA1 , nil
532
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA256 ):
533
+ return crypto .SHA256 , x509 .SHA256WithRSA , nil
534
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA256 ):
535
+ return crypto .SHA256 , x509 .ECDSAWithSHA256 , nil
536
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA384 ):
537
+ return crypto .SHA384 , x509 .SHA256WithRSA , nil
538
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA384 ):
539
+ return crypto .SHA384 , x509 .ECDSAWithSHA384 , nil
540
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmSHA512 ):
541
+ return crypto .SHA512 , x509 .ECDSAWithSHA512 , nil
542
+ case oid .Equal (pkcs7 .OIDDigestAlgorithmECDSASHA512 ):
543
+ return crypto .SHA512 , x509 .ECDSAWithSHA512 , nil
544
+ }
545
+ return 0 , 0 , pkcs7 .ErrUnsupportedAlgorithm
560
546
}
561
547
562
548
// AuthenticodeContent provides a simplified view on SpcIndirectDataContent, which specifies the ASN.1 encoded values of
563
549
// the authenticode signature content.
564
550
type AuthenticodeContent struct {
551
+ Algorithm x509.SignatureAlgorithm
565
552
HashFunction crypto.Hash
566
553
HashResult []byte
567
554
}
@@ -576,12 +563,36 @@ func parseAuthenticodeContent(content []byte) (AuthenticodeContent, error) {
576
563
if err != nil {
577
564
return AuthenticodeContent {}, err
578
565
}
579
- hashFunction , err := parseHashAlgorithm (authenticodeContent .MessageDigest .DigestAlgorithm )
566
+ hashFunction , algorithmId , err := parseHashAlgorithm (authenticodeContent .MessageDigest .DigestAlgorithm )
580
567
if err != nil {
581
568
return AuthenticodeContent {}, err
582
569
}
583
570
return AuthenticodeContent {
571
+ Algorithm : algorithmId ,
584
572
HashFunction : hashFunction ,
585
573
HashResult : authenticodeContent .MessageDigest .Digest ,
586
574
}, nil
587
575
}
576
+
577
+ func formatPkixName (name pkix.Name ) string {
578
+ var formattedName string
579
+ if len (name .Country ) > 0 {
580
+ formattedName = name .Country [0 ]
581
+ }
582
+
583
+ if len (name .Province ) > 0 {
584
+ formattedName += ", " + name .Province [0 ]
585
+ }
586
+
587
+ if len (name .Locality ) > 0 {
588
+ formattedName += ", " + name .Locality [0 ]
589
+ }
590
+
591
+ if len (name .Organization ) > 0 {
592
+ formattedName += ", " + name .Organization [0 ]
593
+ }
594
+
595
+ formattedName += ", " + name .CommonName
596
+
597
+ return formattedName
598
+ }
0 commit comments