@ -5,15 +5,26 @@
package x509
package x509
import (
import (
"crypto/sha256"
"encoding/pem"
"encoding/pem"
"errors"
"errors"
"runtime"
"runtime"
"sync"
)
)
type sum224 [ sha256 . Size224 ] byte
// CertPool is a set of certificates.
// CertPool is a set of certificates.
type CertPool struct {
type CertPool struct {
bySubjectKeyId map [ string ] [ ] int // cert.SubjectKeyId => getCert index
bySubjectKeyId map [ string ] [ ] int // cert.SubjectKeyId => getCert index(es)
byName map [ string ] [ ] int // cert.RawSubject => getCert index
byName map [ string ] [ ] int // cert.RawSubject => getCert index(es)
// haveSum maps from sum224(cert.Raw) to true. It's used only
// for AddCert duplicate detection, to avoid CertPool.contains
// calls in the AddCert path (because the contains method can
// call getCert and otherwise negate savings from lazy getCert
// funcs).
haveSum map [ sum224 ] bool
// getCert contains funcs that return the certificates.
// getCert contains funcs that return the certificates.
getCert [ ] func ( ) ( * Certificate , error )
getCert [ ] func ( ) ( * Certificate , error )
@ -28,6 +39,7 @@ func NewCertPool() *CertPool {
return & CertPool {
return & CertPool {
bySubjectKeyId : make ( map [ string ] [ ] int ) ,
bySubjectKeyId : make ( map [ string ] [ ] int ) ,
byName : make ( map [ string ] [ ] int ) ,
byName : make ( map [ string ] [ ] int ) ,
haveSum : make ( map [ sum224 ] bool ) ,
}
}
}
}
@ -49,6 +61,7 @@ func (s *CertPool) copy() *CertPool {
p := & CertPool {
p := & CertPool {
bySubjectKeyId : make ( map [ string ] [ ] int , len ( s . bySubjectKeyId ) ) ,
bySubjectKeyId : make ( map [ string ] [ ] int , len ( s . bySubjectKeyId ) ) ,
byName : make ( map [ string ] [ ] int , len ( s . byName ) ) ,
byName : make ( map [ string ] [ ] int , len ( s . byName ) ) ,
haveSum : make ( map [ sum224 ] bool , len ( s . haveSum ) ) ,
getCert : make ( [ ] func ( ) ( * Certificate , error ) , len ( s . getCert ) ) ,
getCert : make ( [ ] func ( ) ( * Certificate , error ) , len ( s . getCert ) ) ,
rawSubjects : make ( [ ] [ ] byte , len ( s . rawSubjects ) ) ,
rawSubjects : make ( [ ] [ ] byte , len ( s . rawSubjects ) ) ,
}
}
@ -62,6 +75,9 @@ func (s *CertPool) copy() *CertPool {
copy ( indexes , v )
copy ( indexes , v )
p . byName [ k ] = indexes
p . byName [ k ] = indexes
}
}
for k := range s . haveSum {
p . haveSum [ k ] = true
}
copy ( p . getCert , s . getCert )
copy ( p . getCert , s . getCert )
copy ( p . rawSubjects , s . rawSubjects )
copy ( p . rawSubjects , s . rawSubjects )
return p
return p
@ -127,7 +143,7 @@ func (s *CertPool) AddCert(cert *Certificate) {
if cert == nil {
if cert == nil {
panic ( "adding nil Certificate to CertPool" )
panic ( "adding nil Certificate to CertPool" )
}
}
err := s . AddCertFunc ( string ( cert . RawSubject ) , string ( cert . SubjectKeyId ) , func ( ) ( * Certificate , error ) {
err := s . AddCertFunc ( sha256 . Sum224 ( cert . Raw ) , string ( cert . RawSubject ) , string ( cert . SubjectKeyId ) , func ( ) ( * Certificate , error ) {
return cert , nil
return cert , nil
} )
} )
if err != nil {
if err != nil {
@ -141,23 +157,16 @@ func (s *CertPool) AddCert(cert *Certificate) {
// The rawSubject is Certificate.RawSubject and must be non-empty.
// The rawSubject is Certificate.RawSubject and must be non-empty.
// The subjectKeyID is Certificate.SubjectKeyId and may be empty.
// The subjectKeyID is Certificate.SubjectKeyId and may be empty.
// The getCert func may be called 0 or more times.
// The getCert func may be called 0 or more times.
func ( s * CertPool ) AddCertFunc ( rawSu bject, subjectKeyID string , getCert func ( ) ( * Certificate , error ) ) error {
func ( s * CertPool ) AddCertFunc ( rawSu m224 sum224 , rawSu bject, subjectKeyID string , getCert func ( ) ( * Certificate , error ) ) error {
if getCert == nil {
if getCert == nil {
panic ( "getCert can't be nil" )
panic ( "getCert can't be nil" )
}
}
// Check that the certificate isn't being added twice.
// Check that the certificate isn't being added twice.
if len ( s . byName [ rawSubject ] ) > 0 {
if s . haveSum [ rawSum224 ] {
c , err := getCert ( )
if err != nil {
return err
}
if dup , err := s . contains ( c ) ; dup {
return nil
return nil
} else if err != nil {
return err
}
}
}
s . haveSum [ rawSum224 ] = true
n := len ( s . getCert )
n := len ( s . getCert )
s . getCert = append ( s . getCert , getCert )
s . getCert = append ( s . getCert , getCert )
@ -187,16 +196,26 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
continue
continue
}
}
cert , err := ParseCertificate ( block . Bytes )
certBytes := block . Bytes
cert , err := ParseCertificate ( certBytes )
if err != nil {
if err != nil {
continue
continue
}
}
var lazyCert struct {
s . AddCert ( cert )
sync . Once
v * Certificate
}
s . AddCertFunc ( sha256 . Sum224 ( cert . Raw ) , string ( cert . RawSubject ) , string ( cert . SubjectKeyId ) , func ( ) ( * Certificate , error ) {
lazyCert . Do ( func ( ) {
// This can't fail, as the same bytes already parsed above.
lazyCert . v , _ = ParseCertificate ( certBytes )
certBytes = nil
} )
return lazyCert . v , nil
} )
ok = true
ok = true
}
}
return ok
return
}
}
// Subjects returns a list of the DER-encoded subjects of
// Subjects returns a list of the DER-encoded subjects of