Return additional info about pre-cert from ValidatePrecert

This commit is contained in:
Andrew Ayer 2017-04-18 12:29:04 -07:00
parent 06c253a0ea
commit 1814cb87e5
1 changed files with 30 additions and 21 deletions

View File

@ -26,18 +26,24 @@ var (
oidExtensionCTPoison = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3} oidExtensionCTPoison = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
) )
func ValidatePrecert(precertBytes []byte, tbsBytes []byte) error { type PrecertInfo struct {
SameIssuer bool // The pre-certificate was issued from the same CA as the final certificate
Issuer []byte // The pre-certificate's issuer, if different from the final certificate
AKI []byte // The pre-certificate's AKI, if present and different from the final certificate
}
func ValidatePrecert(precertBytes []byte, tbsBytes []byte) (*PrecertInfo, error) {
precert, err := ParseCertificate(precertBytes) precert, err := ParseCertificate(precertBytes)
if err != nil { if err != nil {
return errors.New("failed to parse pre-certificate: " + err.Error()) return nil, errors.New("failed to parse pre-certificate: " + err.Error())
} }
precertTBS, err := precert.ParseTBSCertificate() precertTBS, err := precert.ParseTBSCertificate()
if err != nil { if err != nil {
return errors.New("failed to parse pre-certificate TBS: " + err.Error()) return nil, errors.New("failed to parse pre-certificate TBS: " + err.Error())
} }
tbs, err := ParseTBSCertificate(tbsBytes) tbs, err := ParseTBSCertificate(tbsBytes)
if err != nil { if err != nil {
return errors.New("failed to parse TBS: " + err.Error()) return nil, errors.New("failed to parse TBS: " + err.Error())
} }
// Everything must be equal except: // Everything must be equal except:
@ -45,39 +51,40 @@ func ValidatePrecert(precertBytes []byte, tbsBytes []byte) error {
// Authority Key Identifier extension (both must have it OR neither can have it) // Authority Key Identifier extension (both must have it OR neither can have it)
// CT poison extension (precert must have it, TBS must not have it) // CT poison extension (precert must have it, TBS must not have it)
if precertTBS.Version != tbs.Version { if precertTBS.Version != tbs.Version {
return errors.New("version not equal") return nil, errors.New("version not equal")
} }
if !bytes.Equal(precertTBS.SerialNumber.FullBytes, tbs.SerialNumber.FullBytes) { if !bytes.Equal(precertTBS.SerialNumber.FullBytes, tbs.SerialNumber.FullBytes) {
return errors.New("serial number not equal") return nil, errors.New("serial number not equal")
} }
sameIssuer := bytes.Equal(precertTBS.Issuer.FullBytes, tbs.Issuer.FullBytes) sameIssuer := bytes.Equal(precertTBS.Issuer.FullBytes, tbs.Issuer.FullBytes)
if !bytes.Equal(precertTBS.SignatureAlgorithm.FullBytes, tbs.SignatureAlgorithm.FullBytes) { if !bytes.Equal(precertTBS.SignatureAlgorithm.FullBytes, tbs.SignatureAlgorithm.FullBytes) {
return errors.New("SignatureAlgorithm not equal") return nil, errors.New("SignatureAlgorithm not equal")
} }
if !bytes.Equal(precertTBS.Validity.FullBytes, tbs.Validity.FullBytes) { if !bytes.Equal(precertTBS.Validity.FullBytes, tbs.Validity.FullBytes) {
return errors.New("Validity not equal") return nil, errors.New("Validity not equal")
} }
if !bytes.Equal(precertTBS.Subject.FullBytes, tbs.Subject.FullBytes) { if !bytes.Equal(precertTBS.Subject.FullBytes, tbs.Subject.FullBytes) {
return errors.New("Subject not equal") return nil, errors.New("Subject not equal")
} }
if !bytes.Equal(precertTBS.PublicKey.FullBytes, tbs.PublicKey.FullBytes) { if !bytes.Equal(precertTBS.PublicKey.FullBytes, tbs.PublicKey.FullBytes) {
return errors.New("PublicKey not equal") return nil, errors.New("PublicKey not equal")
} }
if !bitStringEqual(&precertTBS.UniqueId, &tbs.UniqueId) { if !bitStringEqual(&precertTBS.UniqueId, &tbs.UniqueId) {
return errors.New("UniqueId not equal") return nil, errors.New("UniqueId not equal")
} }
if !bitStringEqual(&precertTBS.SubjectUniqueId, &tbs.SubjectUniqueId) { if !bitStringEqual(&precertTBS.SubjectUniqueId, &tbs.SubjectUniqueId) {
return errors.New("SubjectUniqueId not equal") return nil, errors.New("SubjectUniqueId not equal")
} }
precertHasPoison := false precertHasPoison := false
tbsIndex := 0 tbsIndex := 0
var aki []byte
for precertIndex := range precertTBS.Extensions { for precertIndex := range precertTBS.Extensions {
precertExt := &precertTBS.Extensions[precertIndex] precertExt := &precertTBS.Extensions[precertIndex]
if precertExt.Id.Equal(oidExtensionCTPoison) { if precertExt.Id.Equal(oidExtensionCTPoison) {
if !precertExt.Critical { if !precertExt.Critical {
return errors.New("pre-cert poison extension is not critical") return nil, errors.New("pre-cert poison extension is not critical")
} }
/* CAs can't even get this right, and Google's logs don't check. Fortunately, /* CAs can't even get this right, and Google's logs don't check. Fortunately,
it's not that important. it's not that important.
@ -90,32 +97,34 @@ func ValidatePrecert(precertBytes []byte, tbsBytes []byte) error {
} }
if tbsIndex >= len(tbs.Extensions) { if tbsIndex >= len(tbs.Extensions) {
return errors.New("pre-cert contains extension not in TBS") return nil, errors.New("pre-cert contains extension not in TBS")
} }
tbsExt := &tbs.Extensions[tbsIndex] tbsExt := &tbs.Extensions[tbsIndex]
if !precertExt.Id.Equal(tbsExt.Id) { if !precertExt.Id.Equal(tbsExt.Id) {
return fmt.Errorf("pre-cert and TBS contain different extensions (%v vs %v)", precertExt.Id, tbsExt.Id) return nil, fmt.Errorf("pre-cert and TBS contain different extensions (%v vs %v)", precertExt.Id, tbsExt.Id)
} }
if precertExt.Critical != tbsExt.Critical { if precertExt.Critical != tbsExt.Critical {
return fmt.Errorf("pre-cert and TBS %v extension differs in criticality", precertExt.Id) return nil, fmt.Errorf("pre-cert and TBS %v extension differs in criticality", precertExt.Id)
} }
if !precertExt.Id.Equal(oidExtensionAuthorityKeyId) || sameIssuer { if !sameIssuer && precertExt.Id.Equal(oidExtensionAuthorityKeyId) {
aki = precertExt.Value
} else {
if !bytes.Equal(precertExt.Value, tbsExt.Value) { if !bytes.Equal(precertExt.Value, tbsExt.Value) {
return fmt.Errorf("pre-cert and TBS %v extension differs in value", precertExt.Id) return nil, fmt.Errorf("pre-cert and TBS %v extension differs in value", precertExt.Id)
} }
} }
tbsIndex++ tbsIndex++
} }
if tbsIndex < len(tbs.Extensions) { if tbsIndex < len(tbs.Extensions) {
return errors.New("TBS contains extension not in pre-cert") return nil, errors.New("TBS contains extension not in pre-cert")
} }
if !precertHasPoison { if !precertHasPoison {
return errors.New("pre-cert does not have poison extension") return nil, errors.New("pre-cert does not have poison extension")
} }
return nil return &PrecertInfo{SameIssuer: sameIssuer, Issuer: precertTBS.Issuer.FullBytes, AKI: aki}, nil
} }
func ReconstructPrecertTBS(tbs *TBSCertificate) (*TBSCertificate, error) { func ReconstructPrecertTBS(tbs *TBSCertificate) (*TBSCertificate, error) {
precertTBS := TBSCertificate{ precertTBS := TBSCertificate{