Skip to content

Commit a1a8791

Browse files
crypto/verificationhelper: add cross-signing to SAS verification path (#332)
When verifying a new device via SAS / emoji verification, we never cross-sign the new device, which thus never fully finishes verification. This commit refactors a bunch of the key signing code and makes the SAS and QR methods behave more similarly. --------- Signed-off-by: Sumner Evans <sumner.evans@automattic.com> Co-authored-by: Sumner Evans <sumner.evans@automattic.com>
1 parent 513bae7 commit a1a8791

File tree

2 files changed

+78
-31
lines changed

2 files changed

+78
-31
lines changed

crypto/verificationhelper/reciprocate.go

+23-16
Original file line numberDiff line numberDiff line change
@@ -212,27 +212,34 @@ func (vh *VerificationHelper) ConfirmQRCodeScanned(ctx context.Context, txnID id
212212

213213
log.Info().Msg("Confirming QR code scanned")
214214

215-
if txn.TheirUserID == vh.client.UserID {
216-
// Self-signing situation. Trust their device.
217-
218-
// Get their device
219-
theirDevice, err := vh.mach.GetOrFetchDevice(ctx, txn.TheirUserID, txn.TheirDeviceID)
220-
if err != nil {
221-
return err
222-
}
215+
// Get their device
216+
theirDevice, err := vh.mach.GetOrFetchDevice(ctx, txn.TheirUserID, txn.TheirDeviceID)
217+
if err != nil {
218+
return err
219+
}
223220

224-
// Trust their device
225-
theirDevice.Trust = id.TrustStateVerified
226-
err = vh.mach.CryptoStore.PutDevice(ctx, txn.TheirUserID, theirDevice)
227-
if err != nil {
228-
return fmt.Errorf("failed to update device trust state after verifying: %w", err)
229-
}
221+
// Trust their device
222+
theirDevice.Trust = id.TrustStateVerified
223+
err = vh.mach.CryptoStore.PutDevice(ctx, txn.TheirUserID, theirDevice)
224+
if err != nil {
225+
return fmt.Errorf("failed to update device trust state after verifying: %w", err)
226+
}
230227

231-
// Cross-sign their device with the self-signing key
228+
if txn.TheirUserID == vh.client.UserID {
229+
// Self-signing situation.
230+
//
231+
// If we have the cross-signing keys, then we need to sign their device
232+
// using the self-signing key. Otherwise, they have the master private
233+
// key, so we need to trust the master public key.
232234
if vh.mach.CrossSigningKeys != nil {
233235
err = vh.mach.SignOwnDevice(ctx, theirDevice)
234236
if err != nil {
235-
return fmt.Errorf("failed to sign their device: %w", err)
237+
return fmt.Errorf("failed to sign our own new device: %w", err)
238+
}
239+
} else {
240+
err = vh.mach.SignOwnMasterKey(ctx)
241+
if err != nil {
242+
return fmt.Errorf("failed to sign our own master key: %w", err)
236243
}
237244
}
238245
} else {

crypto/verificationhelper/sas.go

+55-15
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"crypto/hmac"
1414
"crypto/rand"
1515
"crypto/sha256"
16+
"crypto/subtle"
1617
"encoding/base64"
1718
"encoding/json"
1819
"errors"
@@ -336,13 +337,7 @@ func (vh *VerificationHelper) onVerificationKey(ctx context.Context, txn Verific
336337
return
337338
}
338339
if !bytes.Equal(commitment, txn.Commitment) {
339-
err := vh.sendVerificationEvent(ctx, txn, event.InRoomVerificationCancel, &event.VerificationCancelEventContent{
340-
Code: event.VerificationCancelCodeKeyMismatch,
341-
Reason: "The key was not the one we expected.",
342-
})
343-
if err != nil {
344-
log.Err(err).Msg("Failed to send cancellation event")
345-
}
340+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "The key was not the one we expected")
346341
return
347342
}
348343
} else {
@@ -593,12 +588,15 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn Verific
593588
// Verifying Keys MAC
594589
log.Info().Msg("Verifying MAC for all sent keys")
595590
var hasTheirDeviceKey bool
591+
var masterKey string
596592
var keyIDs []string
597593
for keyID := range macEvt.MAC {
598594
keyIDs = append(keyIDs, keyID.String())
599595
_, kID := keyID.Parse()
600596
if kID == txn.TheirDeviceID.String() {
601597
hasTheirDeviceKey = true
598+
} else {
599+
masterKey = kID
602600
}
603601
}
604602
slices.Sort(keyIDs)
@@ -617,6 +615,7 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn Verific
617615
}
618616

619617
// Verify the MAC for each key
618+
var theirDevice *id.Device
620619
for keyID, mac := range macEvt.MAC {
621620
log.Info().Str("key_id", keyID.String()).Msg("Received MAC for key")
622621

@@ -627,8 +626,11 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn Verific
627626
}
628627

629628
var key string
630-
var theirDevice *id.Device
631629
if kID == txn.TheirDeviceID.String() {
630+
if theirDevice != nil {
631+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeInvalidMessage, "two keys found for their device ID")
632+
return
633+
}
632634
theirDevice, err = vh.mach.GetOrFetchDevice(ctx, txn.TheirUserID, txn.TheirDeviceID)
633635
if err != nil {
634636
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to fetch their device: %w", err)
@@ -653,22 +655,60 @@ func (vh *VerificationHelper) onVerificationMAC(ctx context.Context, txn Verific
653655
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to calculate key MAC: %w", err)
654656
return
655657
}
656-
if !bytes.Equal(expectedMAC, mac) {
658+
if subtle.ConstantTimeCompare(expectedMAC, mac) == 0 {
657659
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeSASMismatch, "MAC mismatch for key %s", keyID)
658660
return
659661
}
662+
}
663+
log.Info().Msg("All MACs verified")
660664

661-
// Trust their device
662-
if kID == txn.TheirDeviceID.String() {
663-
theirDevice.Trust = id.TrustStateVerified
664-
err = vh.mach.CryptoStore.PutDevice(ctx, txn.TheirUserID, theirDevice)
665+
// Trust their device
666+
theirDevice.Trust = id.TrustStateVerified
667+
err = vh.mach.CryptoStore.PutDevice(ctx, txn.TheirUserID, theirDevice)
668+
if err != nil {
669+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to update device trust state after verifying: %w", err)
670+
return
671+
}
672+
673+
if txn.TheirUserID == vh.client.UserID {
674+
// Self-signing situation.
675+
//
676+
// If we have the cross-signing keys, then we need to sign their device
677+
// using the self-signing key. Otherwise, they have the master private
678+
// key, so we need to trust the master public key.
679+
if vh.mach.CrossSigningKeys != nil {
680+
err = vh.mach.SignOwnDevice(ctx, theirDevice)
665681
if err != nil {
666-
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to update device trust state after verifying: %w", err)
682+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to sign our own new device: %w", err)
667683
return
668684
}
685+
} else {
686+
err = vh.mach.SignOwnMasterKey(ctx)
687+
if err != nil {
688+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeUser, "failed to sign our own master key: %w", err)
689+
return
690+
}
691+
}
692+
} else if masterKey != "" {
693+
// Cross-signing situation.
694+
//
695+
// The master key was included in the list of keys to verify, so verify
696+
// that it matches what we expect and sign their master key using the
697+
// user-signing key.
698+
theirSigningKeys, err := vh.mach.GetCrossSigningPublicKeys(ctx, txn.TheirUserID)
699+
if err != nil {
700+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "couldn't get %s's cross-signing keys: %w", txn.TheirUserID, err)
701+
return
702+
} else if theirSigningKeys.MasterKey.String() != masterKey {
703+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeKeyMismatch, "master keys do not match")
704+
return
705+
}
706+
707+
if err := vh.mach.SignUser(ctx, txn.TheirUserID, theirSigningKeys.MasterKey); err != nil {
708+
vh.cancelVerificationTxn(ctx, txn, event.VerificationCancelCodeInternalError, "failed to sign their master key: %w", err)
709+
return
669710
}
670711
}
671-
log.Info().Msg("All MACs verified")
672712

673713
txn.ReceivedTheirMAC = true
674714
if txn.SentOurMAC {

0 commit comments

Comments
 (0)