@@ -6,6 +6,7 @@ package x509
6
6
7
7
import (
8
8
"bytes"
9
+ "crypto"
9
10
"errors"
10
11
"fmt"
11
12
"net"
@@ -597,73 +598,101 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
597
598
leaf = currentChain [0 ]
598
599
}
599
600
600
- if (certType == intermediateCertificate || certType == rootCertificate ) &&
601
- c .hasNameConstraints () && leaf .hasSANExtension () {
602
- err := forEachSAN (leaf .getSANExtension (), func (tag int , data []byte ) error {
603
- switch tag {
604
- case nameTypeEmail :
605
- name := string (data )
606
- mailbox , ok := parseRFC2821Mailbox (name )
607
- if ! ok {
608
- return fmt .Errorf ("x509: cannot parse rfc822Name %q" , mailbox )
609
- }
610
-
611
- if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "email address" , name , mailbox ,
612
- func (parsedName , constraint any ) (bool , error ) {
613
- return matchEmailConstraint (parsedName .(rfc2821Mailbox ), constraint .(string ))
614
- }, c .PermittedEmailAddresses , c .ExcludedEmailAddresses ); err != nil {
615
- return err
616
- }
617
-
618
- case nameTypeDNS :
619
- name := string (data )
620
- if _ , ok := domainToReverseLabels (name ); ! ok {
621
- return fmt .Errorf ("x509: cannot parse dnsName %q" , name )
622
- }
623
-
624
- if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "DNS name" , name , name ,
625
- func (parsedName , constraint any ) (bool , error ) {
626
- return matchDomainConstraint (parsedName .(string ), constraint .(string ))
627
- }, c .PermittedDNSDomains , c .ExcludedDNSDomains ); err != nil {
628
- return err
629
- }
630
-
631
- case nameTypeURI :
632
- name := string (data )
633
- uri , err := url .Parse (name )
634
- if err != nil {
635
- return fmt .Errorf ("x509: internal error: URI SAN %q failed to parse" , name )
636
- }
637
-
638
- if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "URI" , name , uri ,
639
- func (parsedName , constraint any ) (bool , error ) {
640
- return matchURIConstraint (parsedName .(* url.URL ), constraint .(string ))
641
- }, c .PermittedURIDomains , c .ExcludedURIDomains ); err != nil {
642
- return err
601
+ if (len (c .ExtKeyUsage ) > 0 || len (c .UnknownExtKeyUsage ) > 0 ) && len (opts .KeyUsages ) > 0 {
602
+ acceptableUsage := false
603
+ um := make (map [ExtKeyUsage ]bool , len (opts .KeyUsages ))
604
+ for _ , u := range opts .KeyUsages {
605
+ um [u ] = true
606
+ }
607
+ if ! um [ExtKeyUsageAny ] {
608
+ for _ , u := range c .ExtKeyUsage {
609
+ if u == ExtKeyUsageAny || um [u ] {
610
+ acceptableUsage = true
611
+ break
643
612
}
613
+ }
614
+ if ! acceptableUsage {
615
+ return CertificateInvalidError {c , IncompatibleUsage , "" }
616
+ }
617
+ }
618
+ }
644
619
645
- case nameTypeIP :
646
- ip := net .IP (data )
647
- if l := len (ip ); l != net .IPv4len && l != net .IPv6len {
648
- return fmt .Errorf ("x509: internal error: IP SAN %x failed to parse" , data )
620
+ if (certType == intermediateCertificate || certType == rootCertificate ) &&
621
+ c .hasNameConstraints () {
622
+ toCheck := []* Certificate {}
623
+ if leaf .hasSANExtension () {
624
+ toCheck = append (toCheck , leaf )
625
+ }
626
+ if c .hasSANExtension () {
627
+ toCheck = append (toCheck , c )
628
+ }
629
+ for _ , sanCert := range toCheck {
630
+ err := forEachSAN (sanCert .getSANExtension (), func (tag int , data []byte ) error {
631
+ switch tag {
632
+ case nameTypeEmail :
633
+ name := string (data )
634
+ mailbox , ok := parseRFC2821Mailbox (name )
635
+ if ! ok {
636
+ return fmt .Errorf ("x509: cannot parse rfc822Name %q" , mailbox )
637
+ }
638
+
639
+ if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "email address" , name , mailbox ,
640
+ func (parsedName , constraint any ) (bool , error ) {
641
+ return matchEmailConstraint (parsedName .(rfc2821Mailbox ), constraint .(string ))
642
+ }, c .PermittedEmailAddresses , c .ExcludedEmailAddresses ); err != nil {
643
+ return err
644
+ }
645
+
646
+ case nameTypeDNS :
647
+ name := string (data )
648
+ if _ , ok := domainToReverseLabels (name ); ! ok {
649
+ return fmt .Errorf ("x509: cannot parse dnsName %q" , name )
650
+ }
651
+
652
+ if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "DNS name" , name , name ,
653
+ func (parsedName , constraint any ) (bool , error ) {
654
+ return matchDomainConstraint (parsedName .(string ), constraint .(string ))
655
+ }, c .PermittedDNSDomains , c .ExcludedDNSDomains ); err != nil {
656
+ return err
657
+ }
658
+
659
+ case nameTypeURI :
660
+ name := string (data )
661
+ uri , err := url .Parse (name )
662
+ if err != nil {
663
+ return fmt .Errorf ("x509: internal error: URI SAN %q failed to parse" , name )
664
+ }
665
+
666
+ if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "URI" , name , uri ,
667
+ func (parsedName , constraint any ) (bool , error ) {
668
+ return matchURIConstraint (parsedName .(* url.URL ), constraint .(string ))
669
+ }, c .PermittedURIDomains , c .ExcludedURIDomains ); err != nil {
670
+ return err
671
+ }
672
+
673
+ case nameTypeIP :
674
+ ip := net .IP (data )
675
+ if l := len (ip ); l != net .IPv4len && l != net .IPv6len {
676
+ return fmt .Errorf ("x509: internal error: IP SAN %x failed to parse" , data )
677
+ }
678
+
679
+ if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "IP address" , ip .String (), ip ,
680
+ func (parsedName , constraint any ) (bool , error ) {
681
+ return matchIPConstraint (parsedName .(net.IP ), constraint .(* net.IPNet ))
682
+ }, c .PermittedIPRanges , c .ExcludedIPRanges ); err != nil {
683
+ return err
684
+ }
685
+
686
+ default :
687
+ // Unknown SAN types are ignored.
649
688
}
650
689
651
- if err := c .checkNameConstraints (& comparisonCount , maxConstraintComparisons , "IP address" , ip .String (), ip ,
652
- func (parsedName , constraint any ) (bool , error ) {
653
- return matchIPConstraint (parsedName .(net.IP ), constraint .(* net.IPNet ))
654
- }, c .PermittedIPRanges , c .ExcludedIPRanges ); err != nil {
655
- return err
656
- }
690
+ return nil
691
+ })
657
692
658
- default :
659
- // Unknown SAN types are ignored.
693
+ if err != nil {
694
+ return err
660
695
}
661
-
662
- return nil
663
- })
664
-
665
- if err != nil {
666
- return err
667
696
}
668
697
}
669
698
@@ -767,6 +796,10 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
767
796
}
768
797
}
769
798
799
+ if len (opts .KeyUsages ) == 0 {
800
+ opts .KeyUsages = []ExtKeyUsage {ExtKeyUsageServerAuth }
801
+ }
802
+
770
803
err = c .isValid (leafCertificate , nil , & opts )
771
804
if err != nil {
772
805
return
@@ -779,38 +812,10 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
779
812
}
780
813
}
781
814
782
- var candidateChains [][]* Certificate
783
815
if opts .Roots .contains (c ) {
784
- candidateChains = append (candidateChains , []* Certificate {c })
785
- } else {
786
- if candidateChains , err = c .buildChains (nil , []* Certificate {c }, nil , & opts ); err != nil {
787
- return nil , err
788
- }
789
- }
790
-
791
- keyUsages := opts .KeyUsages
792
- if len (keyUsages ) == 0 {
793
- keyUsages = []ExtKeyUsage {ExtKeyUsageServerAuth }
794
- }
795
-
796
- // If any key usage is acceptable then we're done.
797
- for _ , usage := range keyUsages {
798
- if usage == ExtKeyUsageAny {
799
- return candidateChains , nil
800
- }
801
- }
802
-
803
- for _ , candidate := range candidateChains {
804
- if checkChainForKeyUsage (candidate , keyUsages ) {
805
- chains = append (chains , candidate )
806
- }
807
- }
808
-
809
- if len (chains ) == 0 {
810
- return nil , CertificateInvalidError {c , IncompatibleUsage , "" }
816
+ return [][]* Certificate {{c }}, nil
811
817
}
812
-
813
- return chains , nil
818
+ return c .buildChains ([]* Certificate {c }, nil , & opts )
814
819
}
815
820
816
821
func appendToFreshChain (chain []* Certificate , cert * Certificate ) []* Certificate {
@@ -826,15 +831,22 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
826
831
// for failed checks due to different intermediates having the same Subject.
827
832
const maxChainSignatureChecks = 100
828
833
829
- func (c * Certificate ) buildChains (cache map [ * Certificate ][][] * Certificate , currentChain []* Certificate , sigChecks * int , opts * VerifyOptions ) (chains [][]* Certificate , err error ) {
834
+ func (c * Certificate ) buildChains (currentChain []* Certificate , sigChecks * int , opts * VerifyOptions ) (chains [][]* Certificate , err error ) {
830
835
var (
831
836
hintErr error
832
837
hintCert * Certificate
833
838
)
834
839
840
+ type pubKeyEqual interface {
841
+ Equal (crypto.PublicKey ) bool
842
+ }
843
+
835
844
considerCandidate := func (certType int , candidate * Certificate ) {
836
845
for _ , cert := range currentChain {
837
- if cert .Equal (candidate ) {
846
+ // If a certificate already appeared in the chain we've built, don't
847
+ // reconsider it. This prevents loops, for isntance those created by
848
+ // mutual cross-signatures, or other cross-signature bridges oddities.
849
+ if bytes .Equal (cert .RawSubject , candidate .RawSubject ) && cert .PublicKey .(pubKeyEqual ).Equal (candidate .PublicKey ) {
838
850
return
839
851
}
840
852
}
@@ -865,14 +877,8 @@ func (c *Certificate) buildChains(cache map[*Certificate][][]*Certificate, curre
865
877
case rootCertificate :
866
878
chains = append (chains , appendToFreshChain (currentChain , candidate ))
867
879
case intermediateCertificate :
868
- if cache == nil {
869
- cache = make (map [* Certificate ][][]* Certificate )
870
- }
871
- childChains , ok := cache [candidate ]
872
- if ! ok {
873
- childChains , err = candidate .buildChains (cache , appendToFreshChain (currentChain , candidate ), sigChecks , opts )
874
- cache [candidate ] = childChains
875
- }
880
+ var childChains [][]* Certificate
881
+ childChains , err = candidate .buildChains (appendToFreshChain (currentChain , candidate ), sigChecks , opts )
876
882
chains = append (chains , childChains ... )
877
883
}
878
884
}
0 commit comments