1package chat 2 3import ( 4 "errors" 5 "fmt" 6 "net" 7 "time" 8 9 "github.com/keybase/client/go/chat/types" 10 "github.com/keybase/client/go/ephemeral" 11 "github.com/keybase/client/go/libkb" 12 "github.com/keybase/client/go/protocol/chat1" 13 "github.com/keybase/client/go/protocol/gregor1" 14 "github.com/keybase/client/go/protocol/keybase1" 15 "github.com/keybase/client/go/teams" 16 "github.com/keybase/go-framed-msgpack-rpc/rpc" 17 "golang.org/x/net/context" 18) 19 20var ErrChatServerTimeout = errors.New("timeout calling chat server") 21var ErrDuplicateConnection = errors.New("error calling chat server") 22var ErrKeyServerTimeout = errors.New("timeout calling into key server") 23 24func NewPermanentUnboxingError(inner error) types.UnboxingError { 25 return PermanentUnboxingError{inner} 26} 27 28type PermanentUnboxingError struct{ inner error } 29 30func (e PermanentUnboxingError) Error() string { 31 switch err := e.inner.(type) { 32 case EphemeralUnboxingError, NotAuthenticatedForThisDeviceError: 33 return err.Error() 34 default: 35 return fmt.Sprintf("Unable to decrypt chat message: %s", err.Error()) 36 } 37} 38 39func (e PermanentUnboxingError) IsPermanent() bool { return true } 40 41func (e PermanentUnboxingError) Inner() error { return e.inner } 42 43func (e PermanentUnboxingError) ExportType() chat1.MessageUnboxedErrorType { 44 switch err := e.inner.(type) { 45 case VersionError: 46 return err.ExportType() 47 case EphemeralUnboxingError: 48 return chat1.MessageUnboxedErrorType_EPHEMERAL 49 case NotAuthenticatedForThisDeviceError, InvalidMACError: 50 return chat1.MessageUnboxedErrorType_PAIRWISE_MISSING 51 default: 52 return chat1.MessageUnboxedErrorType_MISC 53 } 54} 55 56func (e PermanentUnboxingError) VersionKind() chat1.VersionKind { 57 switch err := e.inner.(type) { 58 case VersionError: 59 return err.VersionKind() 60 default: 61 return "" 62 } 63} 64 65func (e PermanentUnboxingError) VersionNumber() int { 66 switch err := e.inner.(type) { 67 case VersionError: 68 return err.VersionNumber() 69 default: 70 return 0 71 } 72} 73 74func (e PermanentUnboxingError) IsCritical() bool { 75 switch err := e.inner.(type) { 76 case VersionError: 77 return err.IsCritical() 78 default: 79 return false 80 } 81} 82 83func (e PermanentUnboxingError) InternalError() string { 84 switch err := e.Inner().(type) { 85 case types.InternalError: 86 return err.InternalError() 87 default: 88 return err.Error() 89 } 90} 91 92func (e PermanentUnboxingError) ToStatus() (status keybase1.Status) { 93 if ee, ok := e.inner.(libkb.ExportableError); ok { 94 status = ee.ToStatus() 95 status.Desc = e.Error() 96 } else { 97 status = keybase1.Status{ 98 Name: "GENERIC", 99 Code: libkb.SCGeneric, 100 Desc: e.Error(), 101 } 102 } 103 return status 104} 105 106//============================================================================= 107 108func NewTransientUnboxingError(inner error) types.UnboxingError { 109 return TransientUnboxingError{inner} 110} 111 112type TransientUnboxingError struct{ inner error } 113 114func (e TransientUnboxingError) Error() string { 115 return fmt.Sprintf("Unable to decrypt chat message (transient): %s", e.inner.Error()) 116} 117 118func (e TransientUnboxingError) IsPermanent() bool { return false } 119 120func (e TransientUnboxingError) Inner() error { return e.inner } 121 122func (e TransientUnboxingError) ExportType() chat1.MessageUnboxedErrorType { 123 return chat1.MessageUnboxedErrorType_MISC 124} 125 126func (e TransientUnboxingError) VersionKind() chat1.VersionKind { 127 return "" 128} 129 130func (e TransientUnboxingError) VersionNumber() int { 131 return 0 132} 133 134func (e TransientUnboxingError) IsCritical() bool { 135 return false 136} 137 138func (e TransientUnboxingError) InternalError() string { 139 switch err := e.Inner().(type) { 140 case types.InternalError: 141 return err.InternalError() 142 default: 143 return err.Error() 144 } 145} 146 147func (e TransientUnboxingError) ToStatus() (status keybase1.Status) { 148 if ee, ok := e.inner.(libkb.ExportableError); ok { 149 status = ee.ToStatus() 150 status.Desc = e.Error() 151 } else { 152 status = keybase1.Status{ 153 Name: "GENERIC", 154 Code: libkb.SCGeneric, 155 Desc: e.Error(), 156 } 157 } 158 return status 159} 160 161//============================================================================= 162 163type EphemeralAlreadyExpiredError struct{} 164 165func NewEphemeralAlreadyExpiredError() EphemeralAlreadyExpiredError { 166 return EphemeralAlreadyExpiredError{} 167} 168 169func (e EphemeralAlreadyExpiredError) Error() string { 170 return "Exploding message is expired" 171} 172 173func (e EphemeralAlreadyExpiredError) InternalError() string { 174 return e.Error() 175} 176 177//============================================================================= 178 179type EphemeralUnboxingError struct { 180 inner ephemeral.EphemeralKeyError 181} 182 183func NewEphemeralUnboxingError(inner ephemeral.EphemeralKeyError) EphemeralUnboxingError { 184 return EphemeralUnboxingError{inner} 185} 186 187func (e EphemeralUnboxingError) Error() string { 188 return e.inner.HumanError() 189} 190 191func (e EphemeralUnboxingError) InternalError() string { 192 return e.inner.Error() 193} 194 195//============================================================================= 196 197type PublicTeamEphemeralKeyError struct{} 198 199func NewPublicTeamEphemeralKeyError() PublicTeamEphemeralKeyError { 200 return PublicTeamEphemeralKeyError{} 201} 202 203func (e PublicTeamEphemeralKeyError) Error() string { 204 return "Cannot use exploding messages for a public team." 205} 206 207//============================================================================= 208 209type NotAuthenticatedForThisDeviceError struct{ inner ephemeral.EphemeralKeyError } 210 211func NewNotAuthenticatedForThisDeviceError(mctx libkb.MetaContext, memberCtime *keybase1.Time, 212 contentCtime gregor1.Time) NotAuthenticatedForThisDeviceError { 213 inner := ephemeral.NewNotAuthenticatedForThisDeviceError(mctx, memberCtime, contentCtime) 214 return NotAuthenticatedForThisDeviceError{inner: inner} 215} 216 217func (e NotAuthenticatedForThisDeviceError) Error() string { 218 return e.inner.HumanError() 219} 220 221func (e NotAuthenticatedForThisDeviceError) InternalError() string { 222 return e.inner.Error() 223} 224 225//============================================================================= 226 227type InvalidMACError struct{} 228 229func NewInvalidMACError() InvalidMACError { 230 return InvalidMACError{} 231} 232 233func (e InvalidMACError) Error() string { 234 return "invalid MAC" 235} 236 237//============================================================================= 238 239type ConsistencyErrorCode int 240 241const ( 242 DuplicateID ConsistencyErrorCode = iota 243 OutOfOrderID 244 InconsistentHash 245 IncorrectHash 246) 247 248type ChatThreadConsistencyError interface { 249 error 250 Code() ConsistencyErrorCode 251} 252 253type chatThreadConsistencyErrorImpl struct { 254 msg string 255 code ConsistencyErrorCode 256} 257 258func (e chatThreadConsistencyErrorImpl) Error() string { 259 return e.msg 260} 261 262func (e chatThreadConsistencyErrorImpl) Code() ConsistencyErrorCode { 263 return e.code 264} 265 266func NewChatThreadConsistencyError(code ConsistencyErrorCode, msg string, formatArgs ...interface{}) ChatThreadConsistencyError { 267 return &chatThreadConsistencyErrorImpl{ 268 code: code, 269 msg: fmt.Sprintf(msg, formatArgs...), 270 } 271} 272 273//============================================================================= 274 275type BoxingError struct { 276 Msg string 277 Perm bool 278} 279 280func NewBoxingError(msg string, perm bool) BoxingError { 281 return BoxingError{ 282 Msg: msg, 283 Perm: perm, 284 } 285} 286 287func (e BoxingError) Error() string { 288 return fmt.Sprintf("encryption error: %s perm: %v", e.Msg, e.Perm) 289} 290 291func (e BoxingError) IsImmediateFail() (chat1.OutboxErrorType, bool) { 292 if e.Perm { 293 return chat1.OutboxErrorType_MISC, true 294 } 295 return 0, false 296} 297 298//============================================================================= 299 300type RestrictedBotChannelError struct{} 301 302func NewRestrictedBotChannelError() RestrictedBotChannelError { 303 return RestrictedBotChannelError{} 304} 305 306func (e RestrictedBotChannelError) Error() string { 307 return "bot restricted from sending to this channel" 308} 309 310func (e RestrictedBotChannelError) IsImmediateFail() (chat1.OutboxErrorType, bool) { 311 return chat1.OutboxErrorType_RESTRICTEDBOT, true 312} 313 314//============================================================================= 315 316type BoxingCryptKeysError struct { 317 Err error 318} 319 320// Cause implements the pkg/errors Cause() method, also cloned in libkb via HumanError, 321// so that we know which error to show to the human being using keybase (rather than 322// for our own internal uses). 323func (e BoxingCryptKeysError) Cause() error { 324 return e.Err 325} 326 327func NewBoxingCryptKeysError(err error) BoxingCryptKeysError { 328 return BoxingCryptKeysError{ 329 Err: err, 330 } 331} 332 333func (e BoxingCryptKeysError) Error() string { 334 return fmt.Sprintf("boxing error: unable to get crypt keys: %s", e.Err.Error()) 335} 336 337func (e BoxingCryptKeysError) Inner() error { 338 return e.Err 339} 340 341func (e BoxingCryptKeysError) IsImmediateFail() (chat1.OutboxErrorType, bool) { 342 if _, ok := e.Err.(libkb.IdentifySummaryError); ok { 343 return chat1.OutboxErrorType_IDENTIFY, true 344 } 345 return 0, false 346} 347 348//============================================================================= 349 350type BodyHashInvalid struct{} 351 352func (e BodyHashInvalid) Error() string { 353 return "chat body hash invalid" 354} 355 356type VersionError struct { 357 Kind string 358 Version int 359 Critical bool 360} 361 362func (e VersionError) Error() string { 363 return fmt.Sprintf("Unable to decrypt because current client is out of date. Please update your version of Keybase! Chat version error: [ unhandled: %s version: %d critical: %v ]", e.Kind, e.Version, e.Critical) 364} 365 366func (e VersionError) ExportType() chat1.MessageUnboxedErrorType { 367 if e.Critical { 368 return chat1.MessageUnboxedErrorType_BADVERSION_CRITICAL 369 } 370 return chat1.MessageUnboxedErrorType_BADVERSION 371} 372 373func (e VersionError) VersionKind() chat1.VersionKind { 374 return chat1.VersionKind(e.Kind) 375} 376 377func (e VersionError) VersionNumber() int { 378 return e.Version 379} 380 381func (e VersionError) IsCritical() bool { 382 return e.Critical 383} 384 385func NewMessageBoxedVersionError(version chat1.MessageBoxedVersion) VersionError { 386 return VersionError{ 387 Kind: string(chat1.VersionErrorMessageBoxed), 388 Version: int(version), 389 Critical: true, 390 } 391} 392 393func NewHeaderVersionError(version chat1.HeaderPlaintextVersion, 394 defaultHeader chat1.HeaderPlaintextUnsupported) VersionError { 395 return VersionError{ 396 Kind: string(chat1.VersionErrorHeader), 397 Version: int(version), 398 Critical: defaultHeader.Mi.Crit, 399 } 400} 401 402func NewBodyVersionError(version chat1.BodyPlaintextVersion, defaultBody chat1.BodyPlaintextUnsupported) VersionError { 403 return VersionError{ 404 Kind: string(chat1.VersionErrorBody), 405 Version: int(version), 406 Critical: defaultBody.Mi.Crit, 407 } 408} 409 410//============================================================================= 411 412type HeaderMismatchError struct { 413 Field string 414} 415 416var _ error = (*HeaderMismatchError)(nil) 417 418func (e HeaderMismatchError) Error() string { 419 return fmt.Sprintf("chat header mismatch on %q", e.Field) 420} 421 422func NewHeaderMismatchError(field string) HeaderMismatchError { 423 return HeaderMismatchError{Field: field} 424} 425 426//============================================================================= 427 428type OfflineError struct { 429} 430 431func (e OfflineError) Error() string { 432 return "operation failed: no connection to chat server" 433} 434 435type OfflineClient struct { 436} 437 438func (e OfflineClient) Call(ctx context.Context, method string, arg interface{}, 439 res interface{}, timeout time.Duration) error { 440 return OfflineError{} 441} 442 443func (e OfflineClient) CallCompressed(ctx context.Context, method string, arg interface{}, 444 res interface{}, ctype rpc.CompressionType, timeout time.Duration) error { 445 return OfflineError{} 446} 447 448func (e OfflineClient) Notify(ctx context.Context, method string, arg interface{}, timeout time.Duration) error { 449 return OfflineError{} 450} 451 452//============================================================================= 453 454type DuplicateTopicNameError struct { 455 Conv chat1.ConversationLocal 456} 457 458func (e DuplicateTopicNameError) Error() string { 459 return fmt.Sprintf("channel name %s is already in use in %v", 460 e.Conv.GetTopicName(), e.Conv.Info.TlfName) 461} 462 463//============================================================================= 464 465type ImpteamBadteamError struct { 466 Msg string 467} 468 469func (e ImpteamBadteamError) Error() string { 470 return fmt.Sprintf("bad iteam found in conv: %s", e.Msg) 471} 472 473//============================================================================= 474 475type UnknownTLFNameError struct { 476 tlfName string 477} 478 479func NewUnknownTLFNameError(name string) UnknownTLFNameError { 480 return UnknownTLFNameError{ 481 tlfName: name, 482 } 483} 484 485func (e UnknownTLFNameError) Error() string { 486 return fmt.Sprintf("unknown conversation name: %s", e.tlfName) 487} 488 489//============================================================================= 490 491type AttachmentUploadError struct { 492 Msg string 493 Perm bool 494} 495 496func NewAttachmentUploadError(msg string, perm bool) AttachmentUploadError { 497 return AttachmentUploadError{ 498 Msg: msg, 499 Perm: perm, 500 } 501} 502 503func (e AttachmentUploadError) Error() string { 504 return fmt.Sprintf("attachment failed to upload; %s", e.Msg) 505} 506 507func (e AttachmentUploadError) IsImmediateFail() (chat1.OutboxErrorType, bool) { 508 return chat1.OutboxErrorType_MISC, e.Perm 509} 510 511//============================================================================= 512 513type SenderTestImmediateFailError struct { 514} 515 516func (e SenderTestImmediateFailError) Error() string { 517 return "sender test immediate fail error" 518} 519 520func (e SenderTestImmediateFailError) IsImmediateFail() (chat1.OutboxErrorType, bool) { 521 return chat1.OutboxErrorType_MISC, true 522} 523 524//============================================================================= 525 526type DecryptionKeyNotFoundError struct { 527 generation int 528 kbfsEncrypted, public bool 529} 530 531func NewDecryptionKeyNotFoundError(generation int, public, kbfsEncrypted bool) DecryptionKeyNotFoundError { 532 return DecryptionKeyNotFoundError{ 533 generation: generation, 534 kbfsEncrypted: kbfsEncrypted, 535 public: public, 536 } 537} 538 539func (e DecryptionKeyNotFoundError) Error() string { 540 return fmt.Sprintf("decryption key not found for generation: %v kbfsEncrypted: %v public: %v", 541 e.generation, e.kbfsEncrypted, e.public) 542} 543 544//============================================================================= 545 546type OfflineErrorKind int 547 548const ( 549 OfflineErrorKindOnline OfflineErrorKind = iota 550 OfflineErrorKindOfflineBasic 551 OfflineErrorKindOfflineReconnect 552) 553 554func IsOfflineError(err error) OfflineErrorKind { 555 // Check type 556 switch terr := err.(type) { 557 case net.Error: 558 return OfflineErrorKindOfflineReconnect 559 case libkb.APINetError: 560 return OfflineErrorKindOfflineBasic 561 case OfflineError: 562 return OfflineErrorKindOfflineBasic 563 case TransientUnboxingError: 564 return IsOfflineError(terr.Inner()) 565 } 566 // Check error itself 567 switch err { 568 case context.DeadlineExceeded: 569 fallthrough 570 case ErrChatServerTimeout: 571 return OfflineErrorKindOfflineReconnect 572 case ErrDuplicateConnection: 573 return OfflineErrorKindOfflineBasic 574 } 575 576 // Unfortunately, Go throws these without a type and they can occasionally 577 // propagate up. The strings were copied from 578 // https://golang.org/src/crypto/tls/conn.go 579 switch err.Error() { 580 case "tls: use of closed connection", 581 "tls: protocol is shutdown": 582 return OfflineErrorKindOfflineReconnect 583 } 584 return OfflineErrorKindOnline 585} 586 587func IsRekeyError(err error) (typ chat1.ConversationErrorType, ok bool) { 588 switch err := err.(type) { 589 case types.UnboxingError: 590 return IsRekeyError(err.Inner()) 591 case libkb.NeedSelfRekeyError: 592 return chat1.ConversationErrorType_SELFREKEYNEEDED, true 593 case libkb.NeedOtherRekeyError: 594 return chat1.ConversationErrorType_OTHERREKEYNEEDED, true 595 default: 596 if teams.IsTeamReadError(err) { 597 return chat1.ConversationErrorType_OTHERREKEYNEEDED, true 598 } 599 } 600 return chat1.ConversationErrorType_NONE, false 601} 602 603//============================================================================= 604 605type FTLError struct { 606 msg string 607} 608 609func NewFTLError(s string) error { 610 return &FTLError{msg: s} 611} 612 613func (f FTLError) Error() string { 614 return fmt.Sprintf("FTL Error: %s", f.msg) 615} 616 617//============================================================================= 618 619type DevStoragePermissionDeniedError struct { 620 role keybase1.TeamRole 621} 622 623func NewDevStoragePermissionDeniedError(role keybase1.TeamRole) error { 624 return &DevStoragePermissionDeniedError{role: role} 625} 626 627func (e *DevStoragePermissionDeniedError) Error() string { 628 return fmt.Sprintf("role %q is not high enough", e.role) 629} 630 631//============================================================================= 632 633type DevStorageAdminOnlyError struct { 634 msg string 635} 636 637func NewDevStorageAdminOnlyError(msg string) error { 638 return &DevStorageAdminOnlyError{msg: msg} 639} 640 641func (e *DevStorageAdminOnlyError) Error() string { 642 return fmt.Sprintf("found a conversation and a message, but role checking failed: %s", e.msg) 643} 644