From 3dbf8ef2f0192bc903cb62b2a0f3b863bbfc8087 Mon Sep 17 00:00:00 2001 From: Sumner Evans Date: Tue, 21 May 2024 15:59:48 -0600 Subject: [PATCH] verificationhelper: better errors/logs and more aggressive cancellations Signed-off-by: Sumner Evans --- crypto/verificationhelper/reciprocate.go | 51 ++++---- crypto/verificationhelper/sas.go | 10 +- .../verificationhelper/verificationhelper.go | 116 +++++++++++++----- event/verification.go | 4 + 4 files changed, 119 insertions(+), 62 deletions(-) diff --git a/crypto/verificationhelper/reciprocate.go b/crypto/verificationhelper/reciprocate.go index c86f7d19..9676f295 100644 --- a/crypto/verificationhelper/reciprocate.go +++ b/crypto/verificationhelper/reciprocate.go @@ -35,11 +35,9 @@ func (vh *VerificationHelper) HandleScannedQRData(ctx context.Context, data []by txn, ok := vh.activeTransactions[qrCode.TransactionID] if !ok { - log.Warn().Msg("Ignoring QR code scan for an unknown transaction") - return nil + return fmt.Errorf("unknown transaction ID found in QR code") } else if txn.VerificationState != verificationStateReady { - log.Warn().Msg("Ignoring QR code scan for a transaction that is not in the ready state") - return nil + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "transaction found in the QR code is not in the ready state") } txn.VerificationState = verificationStateTheirQRScanned @@ -57,14 +55,14 @@ func (vh *VerificationHelper) HandleScannedQRData(ctx context.Context, data []by // key, and Key2 is what the other device thinks our device key is. if vh.client.UserID != txn.TheirUser { - return fmt.Errorf("mode %d is only allowed when the other user is the same as the current user", qrCode.Mode) + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "mode %d is only allowed when the other user is the same as the current user", qrCode.Mode) } // Verify the master key is correct if bytes.Equal(ownCrossSigningPublicKeys.MasterKey.Bytes(), qrCode.Key1[:]) { log.Info().Msg("Verified that the other device has the same master key") } else { - return fmt.Errorf("the master key does not match") + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "the master key does not match") } // Verify that the device key that the other device things we have is @@ -73,11 +71,11 @@ func (vh *VerificationHelper) HandleScannedQRData(ctx context.Context, data []by if bytes.Equal(myKeys.SigningKey.Bytes(), qrCode.Key2[:]) { log.Info().Msg("Verified that the other device has the correct key for this device") } else { - return fmt.Errorf("the other device has the wrong key for this device") + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "the other device has the wrong key for this device") } if err := vh.mach.SignOwnMasterKey(ctx); err != nil { - return fmt.Errorf("failed to sign own master key: %w", err) + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "failed to sign own master key: %w", err) } case QRCodeModeSelfVerifyingMasterKeyUntrusted: // The QR was created by a device that does not trust the master key, @@ -89,47 +87,47 @@ func (vh *VerificationHelper) HandleScannedQRData(ctx context.Context, data []by if trusted, err := vh.mach.CryptoStore.IsKeySignedBy(ctx, vh.client.UserID, ownCrossSigningPublicKeys.MasterKey, vh.client.UserID, vh.mach.OwnIdentity().SigningKey); err != nil { return err } else if !trusted { - return fmt.Errorf("the master key is not trusted by this device") + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeMasterKeyNotTrusted, "the master key is not trusted by this device, cannot verify device that does not trust the master key") } if vh.client.UserID != txn.TheirUser { - return fmt.Errorf("mode %d is only allowed when the other user is the same as the current user", qrCode.Mode) + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "mode %d is only allowed when the other user is the same as the current user", qrCode.Mode) } // Get their device theirDevice, err := vh.mach.GetOrFetchDevice(ctx, txn.TheirUser, txn.TheirDevice) if err != nil { - return err + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeInternalError, "failed to get their device: %w", err) } // Verify that the other device's key is what we expect. if bytes.Equal(theirDevice.SigningKey.Bytes(), qrCode.Key1[:]) { log.Info().Msg("Verified that the other device key is what we expected") } else { - return fmt.Errorf("the other device's key is not what we expected") + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "the other device's key is not what we expected") } // Verify that what they think the master key is is correct. if bytes.Equal(vh.mach.GetOwnCrossSigningPublicKeys(ctx).MasterKey.Bytes(), qrCode.Key2[:]) { log.Info().Msg("Verified that the other device has the correct master key") } else { - return fmt.Errorf("the master key does not match") + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "the master key does not match") } // Trust their device theirDevice.Trust = id.TrustStateVerified err = vh.mach.CryptoStore.PutDevice(ctx, txn.TheirUser, theirDevice) if err != nil { - return fmt.Errorf("failed to update device trust state after verifying: %w", err) + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeInternalError, "failed to update device trust state after verifying: %+v", err) } // Cross-sign their device with the self-signing key err = vh.mach.SignOwnDevice(ctx, theirDevice) if err != nil { - return fmt.Errorf("failed to sign their device: %w", err) + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeInternalError, "failed to sign their device: %+v", err) } default: - return fmt.Errorf("unknown QR code mode %d", qrCode.Mode) + return vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "unknown QR code mode %d", qrCode.Mode) } // Send a m.key.verification.start event with the secret @@ -141,17 +139,20 @@ func (vh *VerificationHelper) HandleScannedQRData(ctx context.Context, data []by } err = vh.sendVerificationEvent(ctx, txn, event.InRoomVerificationStart, txn.StartEventContent) if err != nil { - return err + return fmt.Errorf("failed to send m.key.verification.start event: %w", err) } + log.Debug().Msg("Successfully sent the m.key.verification.start event") // Immediately send the m.key.verification.done event, as our side of the // transaction is done. err = vh.sendVerificationEvent(ctx, txn, event.InRoomVerificationDone, &event.VerificationDoneEventContent{}) if err != nil { - return err + return fmt.Errorf("failed to send m.key.verification.done event: %w", err) } + log.Debug().Msg("Successfully sent the m.key.verification.done event") txn.SentOurDone = true if txn.ReceivedTheirDone { + log.Debug().Msg("We already received their done event. Setting verification state to done.") txn.VerificationState = verificationStateDone vh.verificationDone(ctx, txn.TransactionID) } @@ -225,15 +226,17 @@ func (vh *VerificationHelper) generateAndShowQRCode(ctx context.Context, txn *ve Stringer("transaction_id", txn.TransactionID). Logger() if vh.showQRCode == nil { - log.Warn().Msg("Ignoring QR code generation request as showing a QR code is not enabled on this device") + log.Info().Msg("Ignoring QR code generation request as showing a QR code is not enabled on this device") return nil - } - if !slices.Contains(txn.TheirSupportedMethods, event.VerificationMethodQRCodeScan) { - log.Warn().Msg("Ignoring QR code generation request as other device cannot scan QR codes") + } else if !slices.Contains(txn.TheirSupportedMethods, event.VerificationMethodQRCodeScan) { + log.Info().Msg("Ignoring QR code generation request as other device cannot scan QR codes") return nil } ownCrossSigningPublicKeys := vh.mach.GetOwnCrossSigningPublicKeys(ctx) + if ownCrossSigningPublicKeys == nil || len(ownCrossSigningPublicKeys.MasterKey) == 0 { + return fmt.Errorf("failed to get own cross-signing master public key") + } mode := QRCodeModeCrossSigning if vh.client.UserID == txn.TheirUser { @@ -245,6 +248,8 @@ func (vh *VerificationHelper) generateAndShowQRCode(ctx context.Context, txn *ve } else { mode = QRCodeModeSelfVerifyingMasterKeyUntrusted } + } else { + panic("unimplemented") } var key1, key2 []byte @@ -276,7 +281,7 @@ func (vh *VerificationHelper) generateAndShowQRCode(ctx context.Context, txn *ve // Key 2 is the master signing key. key2 = ownCrossSigningPublicKeys.MasterKey.Bytes() default: - log.Fatal().Str("mode", string(mode)).Msg("Unknown QR code mode") + log.Fatal().Int("mode", int(mode)).Msg("Unknown QR code mode") } qrCode := NewQRCode(mode, txn.TransactionID, [32]byte(key1), [32]byte(key2)) diff --git a/crypto/verificationhelper/sas.go b/crypto/verificationhelper/sas.go index 8160a4e1..6e523ba5 100644 --- a/crypto/verificationhelper/sas.go +++ b/crypto/verificationhelper/sas.go @@ -580,7 +580,7 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn *verifi slices.Sort(keyIDs) expectedKeyMAC, err := vh.verificationMACHKDF(txn, txn.TheirUser, txn.TheirDevice, vh.client.UserID, vh.client.DeviceID, "KEY_IDS", strings.Join(keyIDs, ",")) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeSASMismatch, "failed to calculate key list MAC: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeSASMismatch, "failed to calculate key list MAC: %w", err) return } if !bytes.Equal(expectedKeyMAC, macEvt.Keys) { @@ -607,7 +607,7 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn *verifi if kID == txn.TheirDevice.String() { theirDevice, err = vh.mach.GetOrFetchDevice(ctx, txn.TheirUser, txn.TheirDevice) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to fetch their device: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to fetch their device: %w", err) return } key = theirDevice.SigningKey.String() @@ -626,7 +626,7 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn *verifi expectedMAC, err := vh.verificationMACHKDF(txn, txn.TheirUser, txn.TheirDevice, vh.client.UserID, vh.client.DeviceID, keyID.String(), key) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to calculate key MAC: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to calculate key MAC: %w", err) return } if !bytes.Equal(expectedMAC, mac) { @@ -639,7 +639,7 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn *verifi theirDevice.Trust = id.TrustStateVerified err = vh.mach.CryptoStore.PutDevice(ctx, txn.TheirUser, theirDevice) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to update device trust state after verifying: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to update device trust state after verifying: %w", err) return } } @@ -653,7 +653,7 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn *verifi txn.VerificationState = verificationStateSASMACExchanged err := vh.sendVerificationEvent(ctx, txn, event.InRoomVerificationDone, &event.VerificationDoneEventContent{}) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to send verification done event: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to send verification done event: %w", err) return } txn.SentOurDone = true diff --git a/crypto/verificationhelper/verificationhelper.go b/crypto/verificationhelper/verificationhelper.go index 2c7c05f8..e2166a15 100644 --- a/crypto/verificationhelper/verificationhelper.go +++ b/crypto/verificationhelper/verificationhelper.go @@ -178,31 +178,31 @@ func NewVerificationHelper(client *mautrix.Client, mach *crypto.OlmMachine, call } if c, ok := callbacks.(RequiredCallbacks); !ok { - panic("callbacks must implement VerificationRequested") + panic("callbacks must implement RequiredCallbacks") } else { helper.verificationRequested = c.VerificationRequested helper.verificationCancelledCallback = c.VerificationCancelled helper.verificationDone = c.VerificationDone } + supportedMethods := map[event.VerificationMethod]struct{}{} if c, ok := callbacks.(showSASCallbacks); ok { - helper.supportedMethods = append(helper.supportedMethods, event.VerificationMethodSAS) + supportedMethods[event.VerificationMethodSAS] = struct{}{} helper.showSAS = c.ShowSAS } if c, ok := callbacks.(showQRCodeCallbacks); ok { - helper.supportedMethods = append(helper.supportedMethods, - event.VerificationMethodQRCodeShow, event.VerificationMethodReciprocate) + supportedMethods[event.VerificationMethodQRCodeShow] = struct{}{} + supportedMethods[event.VerificationMethodReciprocate] = struct{}{} helper.scanQRCode = c.ScanQRCode helper.showQRCode = c.ShowQRCode helper.qrCodeScaned = c.QRCodeScanned } if supportsScan { - helper.supportedMethods = append(helper.supportedMethods, - event.VerificationMethodQRCodeScan, event.VerificationMethodReciprocate) + supportedMethods[event.VerificationMethodQRCodeScan] = struct{}{} + supportedMethods[event.VerificationMethodReciprocate] = struct{}{} } - slices.Sort(helper.supportedMethods) - helper.supportedMethods = slices.Compact(helper.supportedMethods) + helper.supportedMethods = maps.Keys(supportedMethods) return &helper } @@ -332,6 +332,10 @@ func (vh *VerificationHelper) Init(ctx context.Context) error { // StartVerification starts an interactive verification flow with the given // user via a to-device event. func (vh *VerificationHelper) StartVerification(ctx context.Context, to id.UserID) (id.VerificationTransactionID, error) { + if len(vh.supportedMethods) == 0 { + return "", fmt.Errorf("no supported verification methods") + } + txnID := id.NewVerificationTransactionID() devices, err := vh.mach.CryptoStore.GetDevices(ctx, to) @@ -389,8 +393,8 @@ func (vh *VerificationHelper) StartVerification(ctx context.Context, to id.UserI return txnID, nil } -// StartVerification starts an interactive verification flow with the given -// user in the given room. +// StartInRoomVerification starts an interactive verification flow with the +// given user in the given room. func (vh *VerificationHelper) StartInRoomVerification(ctx context.Context, roomID id.RoomID, to id.UserID) (id.VerificationTransactionID, error) { log := vh.getLog(ctx).With(). Str("verification_action", "start in-room verification"). @@ -441,15 +445,34 @@ func (vh *VerificationHelper) AcceptVerification(ctx context.Context, txnID id.V txn, ok := vh.activeTransactions[txnID] if !ok { return fmt.Errorf("unknown transaction ID") - } - if txn.VerificationState != verificationStateRequested { + } else if txn.VerificationState != verificationStateRequested { return fmt.Errorf("transaction is not in the requested state") } + supportedMethods := map[event.VerificationMethod]struct{}{} + for _, method := range txn.TheirSupportedMethods { + switch method { + case event.VerificationMethodSAS: + if slices.Contains(vh.supportedMethods, event.VerificationMethodSAS) { + supportedMethods[event.VerificationMethodSAS] = struct{}{} + } + case event.VerificationMethodQRCodeShow: + if slices.Contains(vh.supportedMethods, event.VerificationMethodQRCodeScan) { + supportedMethods[event.VerificationMethodQRCodeScan] = struct{}{} + supportedMethods[event.VerificationMethodReciprocate] = struct{}{} + } + case event.VerificationMethodQRCodeScan: + if slices.Contains(vh.supportedMethods, event.VerificationMethodQRCodeShow) { + supportedMethods[event.VerificationMethodQRCodeShow] = struct{}{} + supportedMethods[event.VerificationMethodReciprocate] = struct{}{} + } + } + } + log.Info().Msg("Sending ready event") readyEvt := &event.VerificationReadyEventContent{ FromDevice: vh.client.DeviceID, - Methods: vh.supportedMethods, + Methods: maps.Keys(supportedMethods), } err := vh.sendVerificationEvent(ctx, txn, event.InRoomVerificationReady, readyEvt) if err != nil { @@ -457,7 +480,7 @@ func (vh *VerificationHelper) AcceptVerification(ctx context.Context, txnID id.V } txn.VerificationState = verificationStateReady - if slices.Contains(txn.TheirSupportedMethods, event.VerificationMethodQRCodeShow) { + if vh.scanQRCode != nil && slices.Contains(txn.TheirSupportedMethods, event.VerificationMethodQRCodeShow) { vh.scanQRCode(ctx, txn.TransactionID) } @@ -497,7 +520,7 @@ func (vh *VerificationHelper) sendVerificationEvent(ctx context.Context, txn *ve Parsed: content, }) if err != nil { - return fmt.Errorf("failed to send start event: %w", err) + return fmt.Errorf("failed to send %s event to %s: %w", evtType.String(), txn.RoomID, err) } } else { content.(event.VerificationTransactionable).SetTransactionID(txn.TransactionID) @@ -508,15 +531,19 @@ func (vh *VerificationHelper) sendVerificationEvent(ctx context.Context, txn *ve }} _, err := vh.client.SendToDevice(ctx, evtType, &req) if err != nil { - return fmt.Errorf("failed to send start event: %w", err) + return fmt.Errorf("failed to send %s event to %s: %w", evtType.String(), txn.TheirDevice, err) } } return nil } +// cancelVerificationTxn cancels a verification transaction with the given code +// and reason. It always returns an error, which is the formatted error message +// (this is allows the caller to return the result of this function call +// directly to expose the error to its caller). func (vh *VerificationHelper) cancelVerificationTxn(ctx context.Context, txn *verificationTransaction, code event.VerificationCancelCode, reasonFmtStr string, fmtArgs ...any) error { log := vh.getLog(ctx) - reason := fmt.Sprintf(reasonFmtStr, fmtArgs...) + reason := fmt.Errorf(reasonFmtStr, fmtArgs...).Error() log.Info(). Stringer("transaction_id", txn.TransactionID). Str("code", string(code)). @@ -528,11 +555,11 @@ func (vh *VerificationHelper) cancelVerificationTxn(ctx context.Context, txn *ve } err := vh.sendVerificationEvent(ctx, txn, event.InRoomVerificationCancel, cancelEvt) if err != nil { - return err + return fmt.Errorf("failed to send cancel verification event (code: %s, reason: %s): %w", code, reason, err) } txn.VerificationState = verificationStateCancelled vh.verificationCancelledCallback(ctx, txn.TransactionID, code, reason) - return nil + return fmt.Errorf("verification cancelled (code: %s): %s", code, reason) } func (vh *VerificationHelper) onVerificationRequest(ctx context.Context, evt *event.Event) { @@ -568,7 +595,7 @@ func (vh *VerificationHelper) onVerificationRequest(ctx context.Context, evt *ev return } - if verificationRequest.TransactionID == "" { + if len(verificationRequest.TransactionID) == 0 { log.Warn().Msg("Ignoring verification request without a transaction ID") return } @@ -581,11 +608,33 @@ func (vh *VerificationHelper) onVerificationRequest(ctx context.Context, evt *ev ctx = log.WithContext(ctx) log.Info().Msg("Received verification request") + // Check if we support any of the methods listed + var supportsAnyMethod bool + for _, method := range verificationRequest.Methods { + switch method { + case event.VerificationMethodSAS: + supportsAnyMethod = slices.Contains(vh.supportedMethods, event.VerificationMethodSAS) + case event.VerificationMethodQRCodeScan: + supportsAnyMethod = slices.Contains(vh.supportedMethods, event.VerificationMethodQRCodeShow) && + slices.Contains(verificationRequest.Methods, event.VerificationMethodReciprocate) + case event.VerificationMethodQRCodeShow: + supportsAnyMethod = slices.Contains(vh.supportedMethods, event.VerificationMethodQRCodeScan) && + slices.Contains(verificationRequest.Methods, event.VerificationMethodReciprocate) + } + if supportsAnyMethod { + break + } + } + if !supportsAnyMethod { + log.Warn().Msg("Ignoring verification request that doesn't have any methods we support") + return + } + vh.activeTransactionsLock.Lock() - _, ok := vh.activeTransactions[verificationRequest.TransactionID] + existing, ok := vh.activeTransactions[verificationRequest.TransactionID] if ok { vh.activeTransactionsLock.Unlock() - log.Info().Msg("Ignoring verification request for an already active transaction") + vh.cancelVerificationTxn(ctx, existing, event.VerificationCancelCodeUnexpectedMessage, "received a new verification request for the same transaction ID") return } vh.activeTransactions[verificationRequest.TransactionID] = &verificationTransaction{ @@ -607,7 +656,7 @@ func (vh *VerificationHelper) onVerificationReady(ctx context.Context, txn *veri Logger() if txn.VerificationState != verificationStateRequested { - log.Warn().Msg("Ignoring verification ready event for a transaction that is not in the requested state") + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "verification ready event received for a transaction that is not in the requested state") return } @@ -633,7 +682,7 @@ func (vh *VerificationHelper) onVerificationReady(ctx context.Context, txn *veri } devices, err := vh.mach.CryptoStore.GetDevices(ctx, txn.TheirUser) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to get devices for %s: %v", txn.TheirUser, err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to get devices for %s: %w", txn.TheirUser, err) return } req := mautrix.ReqSendToDevice{Messages: map[id.UserID]map[id.DeviceID]*event.Content{txn.TheirUser: {}}} @@ -653,13 +702,13 @@ func (vh *VerificationHelper) onVerificationReady(ctx context.Context, txn *veri } } - if slices.Contains(txn.TheirSupportedMethods, event.VerificationMethodQRCodeShow) { + if vh.scanQRCode != nil && slices.Contains(txn.TheirSupportedMethods, event.VerificationMethodQRCodeShow) { vh.scanQRCode(ctx, txn.TransactionID) } err := vh.generateAndShowQRCode(ctx, txn) if err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to generate and show QR code: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to generate and show QR code: %w", err) } } @@ -680,8 +729,7 @@ func (vh *VerificationHelper) onVerificationStart(ctx context.Context, txn *veri // We didn't sent a start event yet, so we have gotten ourselves // into a bad state. They've either sent two start events, or we // have gone on to a new state. - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, - "got repeat start event from other user") + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "got repeat start event from other user") return } @@ -713,8 +761,7 @@ func (vh *VerificationHelper) onVerificationStart(ctx context.Context, txn *veri txn.StartEventContent = startEvt } } else if txn.VerificationState != verificationStateReady { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, - "got start event for transaction that is not in ready state") + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "got start event for transaction that is not in ready state") return } @@ -722,7 +769,7 @@ func (vh *VerificationHelper) onVerificationStart(ctx context.Context, txn *veri case event.VerificationMethodSAS: txn.VerificationState = verificationStateSASStarted if err := vh.onVerificationStartSAS(ctx, txn, evt); err != nil { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to handle SAS verification start: %v", err) + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to handle SAS verification start: %w", err) } case event.VerificationMethodReciprocate: log.Info().Msg("Received reciprocate start event") @@ -749,9 +796,10 @@ func (vh *VerificationHelper) onVerificationDone(ctx context.Context, txn *verif vh.activeTransactionsLock.Lock() defer vh.activeTransactionsLock.Unlock() - if txn.VerificationState != verificationStateTheirQRScanned && txn.VerificationState != verificationStateSASMACExchanged { - vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, - "got done event for transaction that is not in QR-scanned or MAC-exchanged state") + if !slices.Contains([]verificationState{ + verificationStateTheirQRScanned, verificationStateOurQRScanned, verificationStateSASMACExchanged, + }, txn.VerificationState) { + vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUnexpectedMessage, "got done event for transaction that is not in QR-scanned or MAC-exchanged state") return } diff --git a/event/verification.go b/event/verification.go index b1851de3..6101896f 100644 --- a/event/verification.go +++ b/event/verification.go @@ -220,6 +220,10 @@ const ( VerificationCancelCodeAccepted VerificationCancelCode = "m.accepted" VerificationCancelCodeSASMismatch VerificationCancelCode = "m.mismatched_sas" VerificationCancelCodeCommitmentMismatch VerificationCancelCode = "m.mismatched_commitment" + + // Non-spec codes + VerificationCancelCodeInternalError VerificationCancelCode = "com.beeper.internal_error" + VerificationCancelCodeMasterKeyNotTrusted VerificationCancelCode = "com.beeper.master_key_not_trusted" // the master key is not trusted by this device, but the QR code that was scanned was from a device that doesn't trust the master key ) // VerificationCancelEventContent represents the content of an