package errors import ( "crypto/x509" "encoding/json" "fmt" ) // Error is the error type usually returned by functions in CF SSL package. // It contains a 4-digit error code where the most significant digit // describes the category where the error occurred and the rest 3 digits // describe the specific error reason. type Error struct { ErrorCode int `json:"code"` Message string `json:"message"` } // Category is the most significant digit of the error code. type Category int // Reason is the last 3 digits of the error code. type Reason int const ( // Success indicates no error occurred. Success Category = 1000 * iota // 0XXX // CertificateError indicates a fault in a certificate. CertificateError // 1XXX // PrivateKeyError indicates a fault in a private key. PrivateKeyError // 2XXX // IntermediatesError indicates a fault in an intermediate. IntermediatesError // 3XXX // RootError indicates a fault in a root. RootError // 4XXX // PolicyError indicates an error arising from a malformed or // non-existent policy, or a breach of policy. PolicyError // 5XXX // DialError indicates a network fault. DialError // 6XXX // APIClientError indicates a problem with the API client. APIClientError // 7XXX // OCSPError indicates a problem with OCSP signing OCSPError // 8XXX // CSRError indicates a problem with CSR parsing CSRError // 9XXX // CTError indicates a problem with the certificate transparency process CTError // 10XXX // CertStoreError indicates a problem with the certificate store CertStoreError // 11XXX ) // None is a non-specified error. const ( None Reason = iota ) // Warning code for a success const ( BundleExpiringBit int = 1 << iota // 0x01 BundleNotUbiquitousBit // 0x02 ) // Parsing errors const ( Unknown Reason = iota // X000 ReadFailed // X001 DecodeFailed // X002 ParseFailed // X003 ) // The following represent certificate non-parsing errors, and must be // specified along with CertificateError. const ( // SelfSigned indicates that a certificate is self-signed and // cannot be used in the manner being attempted. SelfSigned Reason = 100 * (iota + 1) // Code 11XX // VerifyFailed is an X.509 verification failure. The least two // significant digits of 12XX is determined as the actual x509 // error is examined. VerifyFailed // Code 12XX // BadRequest indicates that the certificate request is invalid. BadRequest // Code 13XX // MissingSerial indicates that the profile specified // 'ClientProvidesSerialNumbers', but the SignRequest did not include a serial // number. MissingSerial // Code 14XX ) const ( certificateInvalid = 10 * (iota + 1) //121X unknownAuthority //122x ) // The following represent private-key non-parsing errors, and must be // specified with PrivateKeyError. const ( // Encrypted indicates that the private key is a PKCS #8 encrypted // private key. At this time, CFSSL does not support decrypting // these keys. Encrypted Reason = 100 * (iota + 1) //21XX // NotRSAOrECC indicates that they key is not an RSA or ECC // private key; these are the only two private key types supported // at this time by CFSSL. NotRSAOrECC //22XX // KeyMismatch indicates that the private key does not match // the public key or certificate being presented with the key. KeyMismatch //23XX // GenerationFailed indicates that a private key could not // be generated. GenerationFailed //24XX // Unavailable indicates that a private key mechanism (such as // PKCS #11) was requested but support for that mechanism is // not available. Unavailable ) // The following are policy-related non-parsing errors, and must be // specified along with PolicyError. const ( // NoKeyUsages indicates that the profile does not permit any // key usages for the certificate. NoKeyUsages Reason = 100 * (iota + 1) // 51XX // InvalidPolicy indicates that policy being requested is not // a valid policy or does not exist. InvalidPolicy // 52XX // InvalidRequest indicates a certificate request violated the // constraints of the policy being applied to the request. InvalidRequest // 53XX // UnknownProfile indicates that the profile does not exist. UnknownProfile // 54XX UnmatchedWhitelist // 55xx ) // The following are API client related errors, and should be // specified with APIClientError. const ( // AuthenticationFailure occurs when the client is unable // to obtain an authentication token for the request. AuthenticationFailure Reason = 100 * (iota + 1) // JSONError wraps an encoding/json error. JSONError // IOError wraps an io/ioutil error. IOError // ClientHTTPError wraps a net/http error. ClientHTTPError // ServerRequestFailed covers any other failures from the API // client. ServerRequestFailed ) // The following are OCSP related errors, and should be // specified with OCSPError const ( // IssuerMismatch ocurs when the certificate in the OCSP signing // request was not issued by the CA that this responder responds for. IssuerMismatch Reason = 100 * (iota + 1) // 81XX // InvalidStatus occurs when the OCSP signing requests includes an // invalid value for the certificate status. InvalidStatus ) // Certificate transparency related errors specified with CTError const ( // PrecertSubmissionFailed occurs when submitting a precertificate to // a log server fails PrecertSubmissionFailed = 100 * (iota + 1) // CTClientConstructionFailed occurs when the construction of a new // github.com/google/certificate-transparency client fails. CTClientConstructionFailed // PrecertMissingPoison occurs when a precert is passed to SignFromPrecert // and is missing the CT poison extension. PrecertMissingPoison // PrecertInvalidPoison occurs when a precert is passed to SignFromPrecert // and has a invalid CT poison extension value or the extension is not // critical. PrecertInvalidPoison ) // Certificate persistence related errors specified with CertStoreError const ( // InsertionFailed occurs when a SQL insert query failes to complete. InsertionFailed = 100 * (iota + 1) // RecordNotFound occurs when a SQL query targeting on one unique // record failes to update the specified row in the table. RecordNotFound ) // The error interface implementation, which formats to a JSON object string. func (e *Error) Error() string { marshaled, err := json.Marshal(e) if err != nil { panic(err) } return string(marshaled) } // New returns an error that contains an error code and message derived from // the given category, reason. Currently, to avoid confusion, it is not // allowed to create an error of category Success func New(category Category, reason Reason) *Error { errorCode := int(category) + int(reason) var msg string switch category { case OCSPError: switch reason { case ReadFailed: msg = "No certificate provided" case IssuerMismatch: msg = "Certificate not issued by this issuer" case InvalidStatus: msg = "Invalid revocation status" } case CertificateError: switch reason { case Unknown: msg = "Unknown certificate error" case ReadFailed: msg = "Failed to read certificate" case DecodeFailed: msg = "Failed to decode certificate" case ParseFailed: msg = "Failed to parse certificate" case SelfSigned: msg = "Certificate is self signed" case VerifyFailed: msg = "Unable to verify certificate" case BadRequest: msg = "Invalid certificate request" case MissingSerial: msg = "Missing serial number in request" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category CertificateError.", reason)) } case PrivateKeyError: switch reason { case Unknown: msg = "Unknown private key error" case ReadFailed: msg = "Failed to read private key" case DecodeFailed: msg = "Failed to decode private key" case ParseFailed: msg = "Failed to parse private key" case Encrypted: msg = "Private key is encrypted." case NotRSAOrECC: msg = "Private key algorithm is not RSA or ECC" case KeyMismatch: msg = "Private key does not match public key" case GenerationFailed: msg = "Failed to new private key" case Unavailable: msg = "Private key is unavailable" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PrivateKeyError.", reason)) } case IntermediatesError: switch reason { case Unknown: msg = "Unknown intermediate certificate error" case ReadFailed: msg = "Failed to read intermediate certificate" case DecodeFailed: msg = "Failed to decode intermediate certificate" case ParseFailed: msg = "Failed to parse intermediate certificate" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category IntermediatesError.", reason)) } case RootError: switch reason { case Unknown: msg = "Unknown root certificate error" case ReadFailed: msg = "Failed to read root certificate" case DecodeFailed: msg = "Failed to decode root certificate" case ParseFailed: msg = "Failed to parse root certificate" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category RootError.", reason)) } case PolicyError: switch reason { case Unknown: msg = "Unknown policy error" case NoKeyUsages: msg = "Invalid policy: no key usage available" case InvalidPolicy: msg = "Invalid or unknown policy" case InvalidRequest: msg = "Policy violation request" case UnknownProfile: msg = "Unknown policy profile" case UnmatchedWhitelist: msg = "Request does not match policy whitelist" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PolicyError.", reason)) } case DialError: switch reason { case Unknown: msg = "Failed to dial remote server" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category DialError.", reason)) } case APIClientError: switch reason { case AuthenticationFailure: msg = "API client authentication failure" case JSONError: msg = "API client JSON config error" case ClientHTTPError: msg = "API client HTTP error" case IOError: msg = "API client IO error" case ServerRequestFailed: msg = "API client error: Server request failed" default: panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category APIClientError.", reason)) } case CSRError: switch reason { case Unknown: msg = "CSR parsing failed due to unknown error" case ReadFailed: msg = "CSR file read failed" case ParseFailed: msg = "CSR Parsing failed" case DecodeFailed: msg = "CSR Decode failed" case BadRequest: msg = "CSR Bad request" default: panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category APIClientError.", reason)) } case CTError: switch reason { case Unknown: msg = "Certificate transparency parsing failed due to unknown error" case PrecertSubmissionFailed: msg = "Certificate transparency precertificate submission failed" case PrecertMissingPoison: msg = "Precertificate is missing CT poison extension" case PrecertInvalidPoison: msg = "Precertificate contains an invalid CT poison extension" default: panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CTError.", reason)) } case CertStoreError: switch reason { case Unknown: msg = "Certificate store action failed due to unknown error" default: panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CertStoreError.", reason)) } default: panic(fmt.Sprintf("Unsupported CFSSL error type: %d.", category)) } return &Error{ErrorCode: errorCode, Message: msg} } // Wrap returns an error that contains the given error and an error code derived from // the given category, reason and the error. Currently, to avoid confusion, it is not // allowed to create an error of category Success func Wrap(category Category, reason Reason, err error) *Error { errorCode := int(category) + int(reason) if err == nil { panic("Wrap needs a supplied error to initialize.") } // do not double wrap a error switch err.(type) { case *Error: panic("Unable to wrap a wrapped error.") } switch category { case CertificateError: // given VerifyFailed , report the status with more detailed status code // for some certificate errors we care. if reason == VerifyFailed { switch errorType := err.(type) { case x509.CertificateInvalidError: errorCode += certificateInvalid + int(errorType.Reason) case x509.UnknownAuthorityError: errorCode += unknownAuthority } } case PrivateKeyError, IntermediatesError, RootError, PolicyError, DialError, APIClientError, CSRError, CTError, CertStoreError, OCSPError: // no-op, just use the error default: panic(fmt.Sprintf("Unsupported CFSSL error type: %d.", category)) } return &Error{ErrorCode: errorCode, Message: err.Error()} }