1package errors
2
3import (
4	"crypto/x509"
5	"encoding/json"
6	"fmt"
7)
8
9// Error is the error type usually returned by functions in CF SSL package.
10// It contains a 4-digit error code where the most significant digit
11// describes the category where the error occurred and the rest 3 digits
12// describe the specific error reason.
13type Error struct {
14	ErrorCode int    `json:"code"`
15	Message   string `json:"message"`
16}
17
18// Category is the most significant digit of the error code.
19type Category int
20
21// Reason is the last 3 digits of the error code.
22type Reason int
23
24const (
25	// Success indicates no error occurred.
26	Success Category = 1000 * iota // 0XXX
27
28	// CertificateError indicates a fault in a certificate.
29	CertificateError // 1XXX
30
31	// PrivateKeyError indicates a fault in a private key.
32	PrivateKeyError // 2XXX
33
34	// IntermediatesError indicates a fault in an intermediate.
35	IntermediatesError // 3XXX
36
37	// RootError indicates a fault in a root.
38	RootError // 4XXX
39
40	// PolicyError indicates an error arising from a malformed or
41	// non-existent policy, or a breach of policy.
42	PolicyError // 5XXX
43
44	// DialError indicates a network fault.
45	DialError // 6XXX
46
47	// APIClientError indicates a problem with the API client.
48	APIClientError // 7XXX
49
50	// OCSPError indicates a problem with OCSP signing
51	OCSPError // 8XXX
52
53	// CSRError indicates a problem with CSR parsing
54	CSRError // 9XXX
55
56	// CTError indicates a problem with the certificate transparency process
57	CTError // 10XXX
58
59	// CertStoreError indicates a problem with the certificate store
60	CertStoreError // 11XXX
61)
62
63// None is a non-specified error.
64const (
65	None Reason = iota
66)
67
68// Warning code for a success
69const (
70	BundleExpiringBit      int = 1 << iota // 0x01
71	BundleNotUbiquitousBit                 // 0x02
72)
73
74// Parsing errors
75const (
76	Unknown      Reason = iota // X000
77	ReadFailed                 // X001
78	DecodeFailed               // X002
79	ParseFailed                // X003
80)
81
82// The following represent certificate non-parsing errors, and must be
83// specified along with CertificateError.
84const (
85	// SelfSigned indicates that a certificate is self-signed and
86	// cannot be used in the manner being attempted.
87	SelfSigned Reason = 100 * (iota + 1) // Code 11XX
88
89	// VerifyFailed is an X.509 verification failure. The least two
90	// significant digits of 12XX is determined as the actual x509
91	// error is examined.
92	VerifyFailed // Code 12XX
93
94	// BadRequest indicates that the certificate request is invalid.
95	BadRequest // Code 13XX
96
97	// MissingSerial indicates that the profile specified
98	// 'ClientProvidesSerialNumbers', but the SignRequest did not include a serial
99	// number.
100	MissingSerial // Code 14XX
101)
102
103const (
104	certificateInvalid = 10 * (iota + 1) //121X
105	unknownAuthority                     //122x
106)
107
108// The following represent private-key non-parsing errors, and must be
109// specified with PrivateKeyError.
110const (
111	// Encrypted indicates that the private key is a PKCS #8 encrypted
112	// private key. At this time, CFSSL does not support decrypting
113	// these keys.
114	Encrypted Reason = 100 * (iota + 1) //21XX
115
116	// NotRSAOrECC indicates that they key is not an RSA or ECC
117	// private key; these are the only two private key types supported
118	// at this time by CFSSL.
119	NotRSAOrECC //22XX
120
121	// KeyMismatch indicates that the private key does not match
122	// the public key or certificate being presented with the key.
123	KeyMismatch //23XX
124
125	// GenerationFailed indicates that a private key could not
126	// be generated.
127	GenerationFailed //24XX
128
129	// Unavailable indicates that a private key mechanism (such as
130	// PKCS #11) was requested but support for that mechanism is
131	// not available.
132	Unavailable
133)
134
135// The following are policy-related non-parsing errors, and must be
136// specified along with PolicyError.
137const (
138	// NoKeyUsages indicates that the profile does not permit any
139	// key usages for the certificate.
140	NoKeyUsages Reason = 100 * (iota + 1) // 51XX
141
142	// InvalidPolicy indicates that policy being requested is not
143	// a valid policy or does not exist.
144	InvalidPolicy // 52XX
145
146	// InvalidRequest indicates a certificate request violated the
147	// constraints of the policy being applied to the request.
148	InvalidRequest // 53XX
149
150	// UnknownProfile indicates that the profile does not exist.
151	UnknownProfile // 54XX
152
153	UnmatchedWhitelist // 55xx
154)
155
156// The following are API client related errors, and should be
157// specified with APIClientError.
158const (
159	// AuthenticationFailure occurs when the client is unable
160	// to obtain an authentication token for the request.
161	AuthenticationFailure Reason = 100 * (iota + 1)
162
163	// JSONError wraps an encoding/json error.
164	JSONError
165
166	// IOError wraps an io/ioutil error.
167	IOError
168
169	// ClientHTTPError wraps a net/http error.
170	ClientHTTPError
171
172	// ServerRequestFailed covers any other failures from the API
173	// client.
174	ServerRequestFailed
175)
176
177// The following are OCSP related errors, and should be
178// specified with OCSPError
179const (
180	// IssuerMismatch ocurs when the certificate in the OCSP signing
181	// request was not issued by the CA that this responder responds for.
182	IssuerMismatch Reason = 100 * (iota + 1) // 81XX
183
184	// InvalidStatus occurs when the OCSP signing requests includes an
185	// invalid value for the certificate status.
186	InvalidStatus
187)
188
189// Certificate transparency related errors specified with CTError
190const (
191	// PrecertSubmissionFailed occurs when submitting a precertificate to
192	// a log server fails
193	PrecertSubmissionFailed = 100 * (iota + 1)
194	// CTClientConstructionFailed occurs when the construction of a new
195	// github.com/google/certificate-transparency client fails.
196	CTClientConstructionFailed
197	// PrecertMissingPoison occurs when a precert is passed to SignFromPrecert
198	// and is missing the CT poison extension.
199	PrecertMissingPoison
200	// PrecertInvalidPoison occurs when a precert is passed to SignFromPrecert
201	// and has a invalid CT poison extension value or the extension is not
202	// critical.
203	PrecertInvalidPoison
204)
205
206// Certificate persistence related errors specified with CertStoreError
207const (
208	// InsertionFailed occurs when a SQL insert query failes to complete.
209	InsertionFailed = 100 * (iota + 1)
210	// RecordNotFound occurs when a SQL query targeting on one unique
211	// record failes to update the specified row in the table.
212	RecordNotFound
213)
214
215// The error interface implementation, which formats to a JSON object string.
216func (e *Error) Error() string {
217	marshaled, err := json.Marshal(e)
218	if err != nil {
219		panic(err)
220	}
221	return string(marshaled)
222
223}
224
225// New returns an error that contains  an error code and message derived from
226// the given category, reason. Currently, to avoid confusion, it is not
227// allowed to create an error of category Success
228func New(category Category, reason Reason) *Error {
229	errorCode := int(category) + int(reason)
230	var msg string
231	switch category {
232	case OCSPError:
233		switch reason {
234		case ReadFailed:
235			msg = "No certificate provided"
236		case IssuerMismatch:
237			msg = "Certificate not issued by this issuer"
238		case InvalidStatus:
239			msg = "Invalid revocation status"
240		}
241	case CertificateError:
242		switch reason {
243		case Unknown:
244			msg = "Unknown certificate error"
245		case ReadFailed:
246			msg = "Failed to read certificate"
247		case DecodeFailed:
248			msg = "Failed to decode certificate"
249		case ParseFailed:
250			msg = "Failed to parse certificate"
251		case SelfSigned:
252			msg = "Certificate is self signed"
253		case VerifyFailed:
254			msg = "Unable to verify certificate"
255		case BadRequest:
256			msg = "Invalid certificate request"
257		case MissingSerial:
258			msg = "Missing serial number in request"
259		default:
260			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category CertificateError.",
261				reason))
262
263		}
264	case PrivateKeyError:
265		switch reason {
266		case Unknown:
267			msg = "Unknown private key error"
268		case ReadFailed:
269			msg = "Failed to read private key"
270		case DecodeFailed:
271			msg = "Failed to decode private key"
272		case ParseFailed:
273			msg = "Failed to parse private key"
274		case Encrypted:
275			msg = "Private key is encrypted."
276		case NotRSAOrECC:
277			msg = "Private key algorithm is not RSA or ECC"
278		case KeyMismatch:
279			msg = "Private key does not match public key"
280		case GenerationFailed:
281			msg = "Failed to new private key"
282		case Unavailable:
283			msg = "Private key is unavailable"
284		default:
285			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PrivateKeyError.",
286				reason))
287		}
288	case IntermediatesError:
289		switch reason {
290		case Unknown:
291			msg = "Unknown intermediate certificate error"
292		case ReadFailed:
293			msg = "Failed to read intermediate certificate"
294		case DecodeFailed:
295			msg = "Failed to decode intermediate certificate"
296		case ParseFailed:
297			msg = "Failed to parse intermediate certificate"
298		default:
299			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category IntermediatesError.",
300				reason))
301		}
302	case RootError:
303		switch reason {
304		case Unknown:
305			msg = "Unknown root certificate error"
306		case ReadFailed:
307			msg = "Failed to read root certificate"
308		case DecodeFailed:
309			msg = "Failed to decode root certificate"
310		case ParseFailed:
311			msg = "Failed to parse root certificate"
312		default:
313			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category RootError.",
314				reason))
315		}
316	case PolicyError:
317		switch reason {
318		case Unknown:
319			msg = "Unknown policy error"
320		case NoKeyUsages:
321			msg = "Invalid policy: no key usage available"
322		case InvalidPolicy:
323			msg = "Invalid or unknown policy"
324		case InvalidRequest:
325			msg = "Policy violation request"
326		case UnknownProfile:
327			msg = "Unknown policy profile"
328		case UnmatchedWhitelist:
329			msg = "Request does not match policy whitelist"
330		default:
331			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PolicyError.",
332				reason))
333		}
334	case DialError:
335		switch reason {
336		case Unknown:
337			msg = "Failed to dial remote server"
338		default:
339			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category DialError.",
340				reason))
341		}
342	case APIClientError:
343		switch reason {
344		case AuthenticationFailure:
345			msg = "API client authentication failure"
346		case JSONError:
347			msg = "API client JSON config error"
348		case ClientHTTPError:
349			msg = "API client HTTP error"
350		case IOError:
351			msg = "API client IO error"
352		case ServerRequestFailed:
353			msg = "API client error: Server request failed"
354		default:
355			panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category APIClientError.",
356				reason))
357		}
358	case CSRError:
359		switch reason {
360		case Unknown:
361			msg = "CSR parsing failed due to unknown error"
362		case ReadFailed:
363			msg = "CSR file read failed"
364		case ParseFailed:
365			msg = "CSR Parsing failed"
366		case DecodeFailed:
367			msg = "CSR Decode failed"
368		case BadRequest:
369			msg = "CSR Bad request"
370		default:
371			panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category APIClientError.", reason))
372		}
373	case CTError:
374		switch reason {
375		case Unknown:
376			msg = "Certificate transparency parsing failed due to unknown error"
377		case PrecertSubmissionFailed:
378			msg = "Certificate transparency precertificate submission failed"
379		case PrecertMissingPoison:
380			msg = "Precertificate is missing CT poison extension"
381		case PrecertInvalidPoison:
382			msg = "Precertificate contains an invalid CT poison extension"
383		default:
384			panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CTError.", reason))
385		}
386	case CertStoreError:
387		switch reason {
388		case Unknown:
389			msg = "Certificate store action failed due to unknown error"
390		default:
391			panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CertStoreError.", reason))
392		}
393
394	default:
395		panic(fmt.Sprintf("Unsupported CFSSL error type: %d.",
396			category))
397	}
398	return &Error{ErrorCode: errorCode, Message: msg}
399}
400
401// Wrap returns an error that contains the given error and an error code derived from
402// the given category, reason and the error. Currently, to avoid confusion, it is not
403// allowed to create an error of category Success
404func Wrap(category Category, reason Reason, err error) *Error {
405	errorCode := int(category) + int(reason)
406	if err == nil {
407		panic("Wrap needs a supplied error to initialize.")
408	}
409
410	// do not double wrap a error
411	switch err.(type) {
412	case *Error:
413		panic("Unable to wrap a wrapped error.")
414	}
415
416	switch category {
417	case CertificateError:
418		// given VerifyFailed , report the status with more detailed status code
419		// for some certificate errors we care.
420		if reason == VerifyFailed {
421			switch errorType := err.(type) {
422			case x509.CertificateInvalidError:
423				errorCode += certificateInvalid + int(errorType.Reason)
424			case x509.UnknownAuthorityError:
425				errorCode += unknownAuthority
426			}
427		}
428	case PrivateKeyError, IntermediatesError, RootError, PolicyError, DialError,
429		APIClientError, CSRError, CTError, CertStoreError, OCSPError:
430	// no-op, just use the error
431	default:
432		panic(fmt.Sprintf("Unsupported CFSSL error type: %d.",
433			category))
434	}
435
436	return &Error{ErrorCode: errorCode, Message: err.Error()}
437
438}
439