231 lines
5.6 KiB
Go
231 lines
5.6 KiB
Go
|
package x509
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Error implements the error interface and describes a single error in an X.509 certificate or CRL.
|
||
|
type Error struct {
|
||
|
ID ErrorID
|
||
|
Category ErrCategory
|
||
|
Summary string
|
||
|
Field string
|
||
|
SpecRef string
|
||
|
SpecText string
|
||
|
// Fatal indicates that parsing has been aborted.
|
||
|
Fatal bool
|
||
|
}
|
||
|
|
||
|
func (err Error) Error() string {
|
||
|
var msg bytes.Buffer
|
||
|
if err.ID != ErrInvalidID {
|
||
|
if err.Fatal {
|
||
|
msg.WriteRune('E')
|
||
|
} else {
|
||
|
msg.WriteRune('W')
|
||
|
}
|
||
|
msg.WriteString(fmt.Sprintf("%03d: ", err.ID))
|
||
|
}
|
||
|
msg.WriteString(err.Summary)
|
||
|
return msg.String()
|
||
|
}
|
||
|
|
||
|
// VerboseError creates a more verbose error string, including spec details.
|
||
|
func (err Error) VerboseError() string {
|
||
|
var msg bytes.Buffer
|
||
|
msg.WriteString(err.Error())
|
||
|
if len(err.Field) > 0 || err.Category != UnknownCategory || len(err.SpecRef) > 0 || len(err.SpecText) > 0 {
|
||
|
msg.WriteString(" (")
|
||
|
needSep := false
|
||
|
if len(err.Field) > 0 {
|
||
|
msg.WriteString(err.Field)
|
||
|
needSep = true
|
||
|
}
|
||
|
if err.Category != UnknownCategory {
|
||
|
if needSep {
|
||
|
msg.WriteString(": ")
|
||
|
}
|
||
|
msg.WriteString(err.Category.String())
|
||
|
needSep = true
|
||
|
}
|
||
|
if len(err.SpecRef) > 0 {
|
||
|
if needSep {
|
||
|
msg.WriteString(": ")
|
||
|
}
|
||
|
msg.WriteString(err.SpecRef)
|
||
|
needSep = true
|
||
|
}
|
||
|
if len(err.SpecText) > 0 {
|
||
|
if needSep {
|
||
|
if len(err.SpecRef) > 0 {
|
||
|
msg.WriteString(", ")
|
||
|
} else {
|
||
|
msg.WriteString(": ")
|
||
|
}
|
||
|
}
|
||
|
msg.WriteString("'")
|
||
|
msg.WriteString(err.SpecText)
|
||
|
msg.WriteString("'")
|
||
|
}
|
||
|
msg.WriteString(")")
|
||
|
}
|
||
|
|
||
|
return msg.String()
|
||
|
}
|
||
|
|
||
|
// ErrCategory indicates the category of an x509.Error.
|
||
|
type ErrCategory int
|
||
|
|
||
|
// ErrCategory values.
|
||
|
const (
|
||
|
UnknownCategory ErrCategory = iota
|
||
|
// Errors in ASN.1 encoding
|
||
|
InvalidASN1Encoding
|
||
|
InvalidASN1Content
|
||
|
InvalidASN1DER
|
||
|
// Errors in ASN.1 relative to schema
|
||
|
InvalidValueRange
|
||
|
InvalidASN1Type
|
||
|
UnexpectedAdditionalData
|
||
|
// Errors in X.509
|
||
|
PoorlyFormedCertificate // Fails a SHOULD clause
|
||
|
MalformedCertificate // Fails a MUST clause
|
||
|
PoorlyFormedCRL // Fails a SHOULD clause
|
||
|
MalformedCRL // Fails a MUST clause
|
||
|
// Errors relative to CA/Browser Forum guidelines
|
||
|
BaselineRequirementsFailure
|
||
|
EVRequirementsFailure
|
||
|
// Other errors
|
||
|
InsecureAlgorithm
|
||
|
UnrecognizedValue
|
||
|
)
|
||
|
|
||
|
func (category ErrCategory) String() string {
|
||
|
switch category {
|
||
|
case InvalidASN1Encoding:
|
||
|
return "Invalid ASN.1 encoding"
|
||
|
case InvalidASN1Content:
|
||
|
return "Invalid ASN.1 content"
|
||
|
case InvalidASN1DER:
|
||
|
return "Invalid ASN.1 distinguished encoding"
|
||
|
case InvalidValueRange:
|
||
|
return "Invalid value for range given in schema"
|
||
|
case InvalidASN1Type:
|
||
|
return "Invalid ASN.1 type for schema"
|
||
|
case UnexpectedAdditionalData:
|
||
|
return "Unexpected additional data present"
|
||
|
case PoorlyFormedCertificate:
|
||
|
return "Certificate does not comply with SHOULD clause in spec"
|
||
|
case MalformedCertificate:
|
||
|
return "Certificate does not comply with MUST clause in spec"
|
||
|
case PoorlyFormedCRL:
|
||
|
return "Certificate Revocation List does not comply with SHOULD clause in spec"
|
||
|
case MalformedCRL:
|
||
|
return "Certificate Revocation List does not comply with MUST clause in spec"
|
||
|
case BaselineRequirementsFailure:
|
||
|
return "Certificate does not comply with CA/BF baseline requirements"
|
||
|
case EVRequirementsFailure:
|
||
|
return "Certificate does not comply with CA/BF EV requirements"
|
||
|
case InsecureAlgorithm:
|
||
|
return "Certificate uses an insecure algorithm"
|
||
|
case UnrecognizedValue:
|
||
|
return "Certificate uses an unrecognized value"
|
||
|
default:
|
||
|
return fmt.Sprintf("Unknown (%d)", category)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ErrorID is an identifier for an x509.Error, to allow filtering.
|
||
|
type ErrorID int
|
||
|
|
||
|
// Errors implements the error interface and holds a collection of errors found in a certificate or CRL.
|
||
|
type Errors struct {
|
||
|
Errs []Error
|
||
|
}
|
||
|
|
||
|
// Error converts to a string.
|
||
|
func (e *Errors) Error() string {
|
||
|
return e.combineErrors(Error.Error)
|
||
|
}
|
||
|
|
||
|
// VerboseError creates a more verbose error string, including spec details.
|
||
|
func (e *Errors) VerboseError() string {
|
||
|
return e.combineErrors(Error.VerboseError)
|
||
|
}
|
||
|
|
||
|
// Fatal indicates whether e includes a fatal error
|
||
|
func (e *Errors) Fatal() bool {
|
||
|
return (e.FirstFatal() != nil)
|
||
|
}
|
||
|
|
||
|
// Empty indicates whether e has no errors.
|
||
|
func (e *Errors) Empty() bool {
|
||
|
return len(e.Errs) == 0
|
||
|
}
|
||
|
|
||
|
// FirstFatal returns the first fatal error in e, or nil
|
||
|
// if there is no fatal error.
|
||
|
func (e *Errors) FirstFatal() error {
|
||
|
for _, err := range e.Errs {
|
||
|
if err.Fatal {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
|
||
|
}
|
||
|
|
||
|
// AddID adds the Error identified by the given id to an x509.Errors.
|
||
|
func (e *Errors) AddID(id ErrorID, args ...interface{}) {
|
||
|
e.Errs = append(e.Errs, NewError(id, args...))
|
||
|
}
|
||
|
|
||
|
func (e Errors) combineErrors(errfn func(Error) string) string {
|
||
|
if len(e.Errs) == 0 {
|
||
|
return ""
|
||
|
}
|
||
|
if len(e.Errs) == 1 {
|
||
|
return errfn((e.Errs)[0])
|
||
|
}
|
||
|
var msg bytes.Buffer
|
||
|
msg.WriteString("Errors:")
|
||
|
for _, err := range e.Errs {
|
||
|
msg.WriteString("\n ")
|
||
|
msg.WriteString(errfn(err))
|
||
|
}
|
||
|
return msg.String()
|
||
|
}
|
||
|
|
||
|
// Filter creates a new Errors object with any entries from the filtered
|
||
|
// list of IDs removed.
|
||
|
func (e Errors) Filter(filtered []ErrorID) Errors {
|
||
|
var results Errors
|
||
|
eloop:
|
||
|
for _, v := range e.Errs {
|
||
|
for _, f := range filtered {
|
||
|
if v.ID == f {
|
||
|
break eloop
|
||
|
}
|
||
|
}
|
||
|
results.Errs = append(results.Errs, v)
|
||
|
}
|
||
|
return results
|
||
|
}
|
||
|
|
||
|
// ErrorFilter builds a list of error IDs (suitable for use with Errors.Filter) from a comma-separated string.
|
||
|
func ErrorFilter(ignore string) []ErrorID {
|
||
|
var ids []ErrorID
|
||
|
filters := strings.Split(ignore, ",")
|
||
|
for _, f := range filters {
|
||
|
v, err := strconv.Atoi(f)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
ids = append(ids, ErrorID(v))
|
||
|
}
|
||
|
return ids
|
||
|
}
|