securecookie.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. // Copyright 2012 The Gorilla Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package securecookie
  5. import (
  6. "bytes"
  7. "crypto/aes"
  8. "crypto/cipher"
  9. "crypto/hmac"
  10. "crypto/rand"
  11. "crypto/sha256"
  12. "crypto/subtle"
  13. "encoding/base64"
  14. "encoding/gob"
  15. "encoding/json"
  16. "fmt"
  17. "hash"
  18. "io"
  19. "strconv"
  20. "strings"
  21. "time"
  22. )
  23. // Error is the interface of all errors returned by functions in this library.
  24. type Error interface {
  25. error
  26. // IsUsage returns true for errors indicating the client code probably
  27. // uses this library incorrectly. For example, the client may have
  28. // failed to provide a valid hash key, or may have failed to configure
  29. // the Serializer adequately for encoding value.
  30. IsUsage() bool
  31. // IsDecode returns true for errors indicating that a cookie could not
  32. // be decoded and validated. Since cookies are usually untrusted
  33. // user-provided input, errors of this type should be expected.
  34. // Usually, the proper action is simply to reject the request.
  35. IsDecode() bool
  36. // IsInternal returns true for unexpected errors occurring in the
  37. // securecookie implementation.
  38. IsInternal() bool
  39. // Cause, if it returns a non-nil value, indicates that this error was
  40. // propagated from some underlying library. If this method returns nil,
  41. // this error was raised directly by this library.
  42. //
  43. // Cause is provided principally for debugging/logging purposes; it is
  44. // rare that application logic should perform meaningfully different
  45. // logic based on Cause. See, for example, the caveats described on
  46. // (MultiError).Cause().
  47. Cause() error
  48. }
  49. // errorType is a bitmask giving the error type(s) of an cookieError value.
  50. type errorType int
  51. const (
  52. usageError = errorType(1 << iota)
  53. decodeError
  54. internalError
  55. )
  56. type cookieError struct {
  57. typ errorType
  58. msg string
  59. cause error
  60. }
  61. func (e cookieError) IsUsage() bool { return (e.typ & usageError) != 0 }
  62. func (e cookieError) IsDecode() bool { return (e.typ & decodeError) != 0 }
  63. func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 }
  64. func (e cookieError) Cause() error { return e.cause }
  65. func (e cookieError) Error() string {
  66. parts := []string{"securecookie: "}
  67. if e.msg == "" {
  68. parts = append(parts, "error")
  69. } else {
  70. parts = append(parts, e.msg)
  71. }
  72. if c := e.Cause(); c != nil {
  73. parts = append(parts, " - caused by: ", c.Error())
  74. }
  75. return strings.Join(parts, "")
  76. }
  77. var (
  78. errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"}
  79. errNoCodecs = cookieError{typ: usageError, msg: "no codecs provided"}
  80. errHashKeyNotSet = cookieError{typ: usageError, msg: "hash key is not set"}
  81. errBlockKeyNotSet = cookieError{typ: usageError, msg: "block key is not set"}
  82. errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"}
  83. errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"}
  84. errTimestampInvalid = cookieError{typ: decodeError, msg: "invalid timestamp"}
  85. errTimestampTooNew = cookieError{typ: decodeError, msg: "timestamp is too new"}
  86. errTimestampExpired = cookieError{typ: decodeError, msg: "expired timestamp"}
  87. errDecryptionFailed = cookieError{typ: decodeError, msg: "the value could not be decrypted"}
  88. errValueNotByte = cookieError{typ: decodeError, msg: "value not a []byte."}
  89. errValueNotBytePtr = cookieError{typ: decodeError, msg: "value not a pointer to []byte."}
  90. // ErrMacInvalid indicates that cookie decoding failed because the HMAC
  91. // could not be extracted and verified. Direct use of this error
  92. // variable is deprecated; it is public only for legacy compatibility,
  93. // and may be privatized in the future, as it is rarely useful to
  94. // distinguish between this error and other Error implementations.
  95. ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"}
  96. )
  97. // Codec defines an interface to encode and decode cookie values.
  98. type Codec interface {
  99. Encode(name string, value interface{}) (string, error)
  100. Decode(name, value string, dst interface{}) error
  101. }
  102. // New returns a new SecureCookie.
  103. //
  104. // hashKey is required, used to authenticate values using HMAC. Create it using
  105. // GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
  106. //
  107. // blockKey is optional, used to encrypt values. Create it using
  108. // GenerateRandomKey(). The key length must correspond to the block size
  109. // of the encryption algorithm. For AES, used by default, valid lengths are
  110. // 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
  111. // The default encoder used for cookie serialization is encoding/gob.
  112. //
  113. // Note that keys created using GenerateRandomKey() are not automatically
  114. // persisted. New keys will be created when the application is restarted, and
  115. // previously issued cookies will not be able to be decoded.
  116. func New(hashKey, blockKey []byte) *SecureCookie {
  117. s := &SecureCookie{
  118. hashKey: hashKey,
  119. blockKey: blockKey,
  120. hashFunc: sha256.New,
  121. maxAge: 86400 * 30,
  122. maxLength: 4096,
  123. sz: GobEncoder{},
  124. }
  125. if len(hashKey) == 0 {
  126. s.err = errHashKeyNotSet
  127. }
  128. if blockKey != nil {
  129. s.BlockFunc(aes.NewCipher)
  130. }
  131. return s
  132. }
  133. // SecureCookie encodes and decodes authenticated and optionally encrypted
  134. // cookie values.
  135. type SecureCookie struct {
  136. hashKey []byte
  137. hashFunc func() hash.Hash
  138. blockKey []byte
  139. block cipher.Block
  140. maxLength int
  141. maxAge int64
  142. minAge int64
  143. err error
  144. sz Serializer
  145. // For testing purposes, the function that returns the current timestamp.
  146. // If not set, it will use time.Now().UTC().Unix().
  147. timeFunc func() int64
  148. }
  149. // Serializer provides an interface for providing custom serializers for cookie
  150. // values.
  151. type Serializer interface {
  152. Serialize(src interface{}) ([]byte, error)
  153. Deserialize(src []byte, dst interface{}) error
  154. }
  155. // GobEncoder encodes cookie values using encoding/gob. This is the simplest
  156. // encoder and can handle complex types via gob.Register.
  157. type GobEncoder struct{}
  158. // JSONEncoder encodes cookie values using encoding/json. Users who wish to
  159. // encode complex types need to satisfy the json.Marshaller and
  160. // json.Unmarshaller interfaces.
  161. type JSONEncoder struct{}
  162. // NopEncoder does not encode cookie values, and instead simply accepts a []byte
  163. // (as an interface{}) and returns a []byte. This is particularly useful when
  164. // you encoding an object upstream and do not wish to re-encode it.
  165. type NopEncoder struct{}
  166. // MaxLength restricts the maximum length, in bytes, for the cookie value.
  167. //
  168. // Default is 4096, which is the maximum value accepted by Internet Explorer.
  169. func (s *SecureCookie) MaxLength(value int) *SecureCookie {
  170. s.maxLength = value
  171. return s
  172. }
  173. // MaxAge restricts the maximum age, in seconds, for the cookie value.
  174. //
  175. // Default is 86400 * 30. Set it to 0 for no restriction.
  176. func (s *SecureCookie) MaxAge(value int) *SecureCookie {
  177. s.maxAge = int64(value)
  178. return s
  179. }
  180. // MinAge restricts the minimum age, in seconds, for the cookie value.
  181. //
  182. // Default is 0 (no restriction).
  183. func (s *SecureCookie) MinAge(value int) *SecureCookie {
  184. s.minAge = int64(value)
  185. return s
  186. }
  187. // HashFunc sets the hash function used to create HMAC.
  188. //
  189. // Default is crypto/sha256.New.
  190. func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
  191. s.hashFunc = f
  192. return s
  193. }
  194. // BlockFunc sets the encryption function used to create a cipher.Block.
  195. //
  196. // Default is crypto/aes.New.
  197. func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
  198. if s.blockKey == nil {
  199. s.err = errBlockKeyNotSet
  200. } else if block, err := f(s.blockKey); err == nil {
  201. s.block = block
  202. } else {
  203. s.err = cookieError{cause: err, typ: usageError}
  204. }
  205. return s
  206. }
  207. // Encoding sets the encoding/serialization method for cookies.
  208. //
  209. // Default is encoding/gob. To encode special structures using encoding/gob,
  210. // they must be registered first using gob.Register().
  211. func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie {
  212. s.sz = sz
  213. return s
  214. }
  215. // Encode encodes a cookie value.
  216. //
  217. // It serializes, optionally encrypts, signs with a message authentication code,
  218. // and finally encodes the value.
  219. //
  220. // The name argument is the cookie name. It is stored with the encoded value.
  221. // The value argument is the value to be encoded. It can be any value that can
  222. // be encoded using the currently selected serializer; see SetSerializer().
  223. //
  224. // It is the client's responsibility to ensure that value, when encoded using
  225. // the current serialization/encryption settings on s and then base64-encoded,
  226. // is shorter than the maximum permissible length.
  227. func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
  228. if s.err != nil {
  229. return "", s.err
  230. }
  231. if s.hashKey == nil {
  232. s.err = errHashKeyNotSet
  233. return "", s.err
  234. }
  235. var err error
  236. var b []byte
  237. // 1. Serialize.
  238. if b, err = s.sz.Serialize(value); err != nil {
  239. return "", cookieError{cause: err, typ: usageError}
  240. }
  241. // 2. Encrypt (optional).
  242. if s.block != nil {
  243. if b, err = encrypt(s.block, b); err != nil {
  244. return "", cookieError{cause: err, typ: usageError}
  245. }
  246. }
  247. b = encode(b)
  248. // 3. Create MAC for "name|date|value". Extra pipe to be used later.
  249. b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
  250. mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
  251. // Append mac, remove name.
  252. b = append(b, mac...)[len(name)+1:]
  253. // 4. Encode to base64.
  254. b = encode(b)
  255. // 5. Check length.
  256. if s.maxLength != 0 && len(b) > s.maxLength {
  257. return "", errEncodedValueTooLong
  258. }
  259. // Done.
  260. return string(b), nil
  261. }
  262. // Decode decodes a cookie value.
  263. //
  264. // It decodes, verifies a message authentication code, optionally decrypts and
  265. // finally deserializes the value.
  266. //
  267. // The name argument is the cookie name. It must be the same name used when
  268. // it was stored. The value argument is the encoded cookie value. The dst
  269. // argument is where the cookie will be decoded. It must be a pointer.
  270. func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
  271. if s.err != nil {
  272. return s.err
  273. }
  274. if s.hashKey == nil {
  275. s.err = errHashKeyNotSet
  276. return s.err
  277. }
  278. // 1. Check length.
  279. if s.maxLength != 0 && len(value) > s.maxLength {
  280. return errValueToDecodeTooLong
  281. }
  282. // 2. Decode from base64.
  283. b, err := decode([]byte(value))
  284. if err != nil {
  285. return err
  286. }
  287. // 3. Verify MAC. Value is "date|value|mac".
  288. parts := bytes.SplitN(b, []byte("|"), 3)
  289. if len(parts) != 3 {
  290. return ErrMacInvalid
  291. }
  292. h := hmac.New(s.hashFunc, s.hashKey)
  293. b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
  294. if err = verifyMac(h, b, parts[2]); err != nil {
  295. return err
  296. }
  297. // 4. Verify date ranges.
  298. var t1 int64
  299. if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
  300. return errTimestampInvalid
  301. }
  302. t2 := s.timestamp()
  303. if s.minAge != 0 && t1 > t2-s.minAge {
  304. return errTimestampTooNew
  305. }
  306. if s.maxAge != 0 && t1 < t2-s.maxAge {
  307. return errTimestampExpired
  308. }
  309. // 5. Decrypt (optional).
  310. b, err = decode(parts[1])
  311. if err != nil {
  312. return err
  313. }
  314. if s.block != nil {
  315. if b, err = decrypt(s.block, b); err != nil {
  316. return err
  317. }
  318. }
  319. // 6. Deserialize.
  320. if err = s.sz.Deserialize(b, dst); err != nil {
  321. return cookieError{cause: err, typ: decodeError}
  322. }
  323. // Done.
  324. return nil
  325. }
  326. // timestamp returns the current timestamp, in seconds.
  327. //
  328. // For testing purposes, the function that generates the timestamp can be
  329. // overridden. If not set, it will return time.Now().UTC().Unix().
  330. func (s *SecureCookie) timestamp() int64 {
  331. if s.timeFunc == nil {
  332. return time.Now().UTC().Unix()
  333. }
  334. return s.timeFunc()
  335. }
  336. // Authentication -------------------------------------------------------------
  337. // createMac creates a message authentication code (MAC).
  338. func createMac(h hash.Hash, value []byte) []byte {
  339. h.Write(value)
  340. return h.Sum(nil)
  341. }
  342. // verifyMac verifies that a message authentication code (MAC) is valid.
  343. func verifyMac(h hash.Hash, value []byte, mac []byte) error {
  344. mac2 := createMac(h, value)
  345. // Check that both MACs are of equal length, as subtle.ConstantTimeCompare
  346. // does not do this prior to Go 1.4.
  347. if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
  348. return nil
  349. }
  350. return ErrMacInvalid
  351. }
  352. // Encryption -----------------------------------------------------------------
  353. // encrypt encrypts a value using the given block in counter mode.
  354. //
  355. // A random initialization vector (http://goo.gl/zF67k) with the length of the
  356. // block size is prepended to the resulting ciphertext.
  357. func encrypt(block cipher.Block, value []byte) ([]byte, error) {
  358. iv := GenerateRandomKey(block.BlockSize())
  359. if iv == nil {
  360. return nil, errGeneratingIV
  361. }
  362. // Encrypt it.
  363. stream := cipher.NewCTR(block, iv)
  364. stream.XORKeyStream(value, value)
  365. // Return iv + ciphertext.
  366. return append(iv, value...), nil
  367. }
  368. // decrypt decrypts a value using the given block in counter mode.
  369. //
  370. // The value to be decrypted must be prepended by a initialization vector
  371. // (http://goo.gl/zF67k) with the length of the block size.
  372. func decrypt(block cipher.Block, value []byte) ([]byte, error) {
  373. size := block.BlockSize()
  374. if len(value) > size {
  375. // Extract iv.
  376. iv := value[:size]
  377. // Extract ciphertext.
  378. value = value[size:]
  379. // Decrypt it.
  380. stream := cipher.NewCTR(block, iv)
  381. stream.XORKeyStream(value, value)
  382. return value, nil
  383. }
  384. return nil, errDecryptionFailed
  385. }
  386. // Serialization --------------------------------------------------------------
  387. // Serialize encodes a value using gob.
  388. func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
  389. buf := new(bytes.Buffer)
  390. enc := gob.NewEncoder(buf)
  391. if err := enc.Encode(src); err != nil {
  392. return nil, cookieError{cause: err, typ: usageError}
  393. }
  394. return buf.Bytes(), nil
  395. }
  396. // Deserialize decodes a value using gob.
  397. func (e GobEncoder) Deserialize(src []byte, dst interface{}) error {
  398. dec := gob.NewDecoder(bytes.NewBuffer(src))
  399. if err := dec.Decode(dst); err != nil {
  400. return cookieError{cause: err, typ: decodeError}
  401. }
  402. return nil
  403. }
  404. // Serialize encodes a value using encoding/json.
  405. func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) {
  406. buf := new(bytes.Buffer)
  407. enc := json.NewEncoder(buf)
  408. if err := enc.Encode(src); err != nil {
  409. return nil, cookieError{cause: err, typ: usageError}
  410. }
  411. return buf.Bytes(), nil
  412. }
  413. // Deserialize decodes a value using encoding/json.
  414. func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error {
  415. dec := json.NewDecoder(bytes.NewReader(src))
  416. if err := dec.Decode(dst); err != nil {
  417. return cookieError{cause: err, typ: decodeError}
  418. }
  419. return nil
  420. }
  421. // Serialize passes a []byte through as-is.
  422. func (e NopEncoder) Serialize(src interface{}) ([]byte, error) {
  423. if b, ok := src.([]byte); ok {
  424. return b, nil
  425. }
  426. return nil, errValueNotByte
  427. }
  428. // Deserialize passes a []byte through as-is.
  429. func (e NopEncoder) Deserialize(src []byte, dst interface{}) error {
  430. if dat, ok := dst.(*[]byte); ok {
  431. *dat = src
  432. return nil
  433. }
  434. return errValueNotBytePtr
  435. }
  436. // Encoding -------------------------------------------------------------------
  437. // encode encodes a value using base64.
  438. func encode(value []byte) []byte {
  439. encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
  440. base64.URLEncoding.Encode(encoded, value)
  441. return encoded
  442. }
  443. // decode decodes a cookie using base64.
  444. func decode(value []byte) ([]byte, error) {
  445. decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
  446. b, err := base64.URLEncoding.Decode(decoded, value)
  447. if err != nil {
  448. return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"}
  449. }
  450. return decoded[:b], nil
  451. }
  452. // Helpers --------------------------------------------------------------------
  453. // GenerateRandomKey creates a random key with the given length in bytes.
  454. // On failure, returns nil.
  455. //
  456. // Note that keys created using `GenerateRandomKey()` are not automatically
  457. // persisted. New keys will be created when the application is restarted, and
  458. // previously issued cookies will not be able to be decoded.
  459. //
  460. // Callers should explicitly check for the possibility of a nil return, treat
  461. // it as a failure of the system random number generator, and not continue.
  462. func GenerateRandomKey(length int) []byte {
  463. k := make([]byte, length)
  464. if _, err := io.ReadFull(rand.Reader, k); err != nil {
  465. return nil
  466. }
  467. return k
  468. }
  469. // CodecsFromPairs returns a slice of SecureCookie instances.
  470. //
  471. // It is a convenience function to create a list of codecs for key rotation. Note
  472. // that the generated Codecs will have the default options applied: callers
  473. // should iterate over each Codec and type-assert the underlying *SecureCookie to
  474. // change these.
  475. //
  476. // Example:
  477. //
  478. // codecs := securecookie.CodecsFromPairs(
  479. // []byte("new-hash-key"),
  480. // []byte("new-block-key"),
  481. // []byte("old-hash-key"),
  482. // []byte("old-block-key"),
  483. // )
  484. //
  485. // // Modify each instance.
  486. // for _, s := range codecs {
  487. // if cookie, ok := s.(*securecookie.SecureCookie); ok {
  488. // cookie.MaxAge(86400 * 7)
  489. // cookie.SetSerializer(securecookie.JSONEncoder{})
  490. // cookie.HashFunc(sha512.New512_256)
  491. // }
  492. // }
  493. //
  494. func CodecsFromPairs(keyPairs ...[]byte) []Codec {
  495. codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2)
  496. for i := 0; i < len(keyPairs); i += 2 {
  497. var blockKey []byte
  498. if i+1 < len(keyPairs) {
  499. blockKey = keyPairs[i+1]
  500. }
  501. codecs[i/2] = New(keyPairs[i], blockKey)
  502. }
  503. return codecs
  504. }
  505. // EncodeMulti encodes a cookie value using a group of codecs.
  506. //
  507. // The codecs are tried in order. Multiple codecs are accepted to allow
  508. // key rotation.
  509. //
  510. // On error, may return a MultiError.
  511. func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) {
  512. if len(codecs) == 0 {
  513. return "", errNoCodecs
  514. }
  515. var errors MultiError
  516. for _, codec := range codecs {
  517. encoded, err := codec.Encode(name, value)
  518. if err == nil {
  519. return encoded, nil
  520. }
  521. errors = append(errors, err)
  522. }
  523. return "", errors
  524. }
  525. // DecodeMulti decodes a cookie value using a group of codecs.
  526. //
  527. // The codecs are tried in order. Multiple codecs are accepted to allow
  528. // key rotation.
  529. //
  530. // On error, may return a MultiError.
  531. func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error {
  532. if len(codecs) == 0 {
  533. return errNoCodecs
  534. }
  535. var errors MultiError
  536. for _, codec := range codecs {
  537. err := codec.Decode(name, value, dst)
  538. if err == nil {
  539. return nil
  540. }
  541. errors = append(errors, err)
  542. }
  543. return errors
  544. }
  545. // MultiError groups multiple errors.
  546. type MultiError []error
  547. func (m MultiError) IsUsage() bool { return m.any(func(e Error) bool { return e.IsUsage() }) }
  548. func (m MultiError) IsDecode() bool { return m.any(func(e Error) bool { return e.IsDecode() }) }
  549. func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) }
  550. // Cause returns nil for MultiError; there is no unique underlying cause in the
  551. // general case.
  552. //
  553. // Note: we could conceivably return a non-nil Cause only when there is exactly
  554. // one child error with a Cause. However, it would be brittle for client code
  555. // to rely on the arity of causes inside a MultiError, so we have opted not to
  556. // provide this functionality. Clients which really wish to access the Causes
  557. // of the underlying errors are free to iterate through the errors themselves.
  558. func (m MultiError) Cause() error { return nil }
  559. func (m MultiError) Error() string {
  560. s, n := "", 0
  561. for _, e := range m {
  562. if e != nil {
  563. if n == 0 {
  564. s = e.Error()
  565. }
  566. n++
  567. }
  568. }
  569. switch n {
  570. case 0:
  571. return "(0 errors)"
  572. case 1:
  573. return s
  574. case 2:
  575. return s + " (and 1 other error)"
  576. }
  577. return fmt.Sprintf("%s (and %d other errors)", s, n-1)
  578. }
  579. // any returns true if any element of m is an Error for which pred returns true.
  580. func (m MultiError) any(pred func(Error) bool) bool {
  581. for _, e := range m {
  582. if ourErr, ok := e.(Error); ok && pred(ourErr) {
  583. return true
  584. }
  585. }
  586. return false
  587. }