You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-07 23:02:56 +03:00
Add crypto mode setting for invisible crypto, and apply it to decrypting events (#4407)
Adds a global "crypto mode" setting to the crypto API (only works with Rust crypto), and changes the decryption settings based on that.
This commit is contained in:
@@ -40,6 +40,13 @@ export interface CryptoApi {
|
||||
*/
|
||||
globalBlacklistUnverifiedDevices: boolean;
|
||||
|
||||
/**
|
||||
* The cryptography mode to use.
|
||||
*
|
||||
* @see CryptoMode
|
||||
*/
|
||||
setCryptoMode(cryptoMode: CryptoMode): void;
|
||||
|
||||
/**
|
||||
* Return the current version of the crypto module.
|
||||
* For example: `Rust SDK ${versions.matrix_sdk_crypto} (${versions.git_sha}), Vodozemac ${versions.vodozemac}`.
|
||||
@@ -589,6 +596,24 @@ export enum DecryptionFailureCode {
|
||||
*/
|
||||
HISTORICAL_MESSAGE_USER_NOT_JOINED = "HISTORICAL_MESSAGE_USER_NOT_JOINED",
|
||||
|
||||
/**
|
||||
* The sender's identity is not verified, but was previously verified.
|
||||
*/
|
||||
SENDER_IDENTITY_PREVIOUSLY_VERIFIED = "SENDER_IDENTITY_PREVIOUSLY_VERIFIED",
|
||||
|
||||
/**
|
||||
* The sender device is not cross-signed. This will only be used if the
|
||||
* crypto mode is set to `CryptoMode.Invisible` or `CryptoMode.Transition`.
|
||||
*/
|
||||
UNSIGNED_SENDER_DEVICE = "UNSIGNED_SENDER_DEVICE",
|
||||
|
||||
/**
|
||||
* We weren't able to link the message back to any known device. This will
|
||||
* only be used if the crypto mode is set to `CryptoMode.Invisible` or
|
||||
* `CryptoMode.Transition`.
|
||||
*/
|
||||
UNKNOWN_SENDER_DEVICE = "UNKNOWN_SENDER_DEVICE",
|
||||
|
||||
/** Unknown or unclassified error. */
|
||||
UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
||||
|
||||
@@ -632,6 +657,38 @@ export enum DecryptionFailureCode {
|
||||
UNKNOWN_ENCRYPTION_ALGORITHM = "UNKNOWN_ENCRYPTION_ALGORITHM",
|
||||
}
|
||||
|
||||
/**
|
||||
* The cryptography mode. Affects how messages are encrypted and decrypted.
|
||||
* Only supported by Rust crypto.
|
||||
*/
|
||||
export enum CryptoMode {
|
||||
/**
|
||||
* Message encryption keys are shared with all devices in the room, except for
|
||||
* blacklisted devices, or unverified devices if
|
||||
* `globalBlacklistUnverifiedDevices` is set. Events from all senders are
|
||||
* decrypted.
|
||||
*/
|
||||
Legacy,
|
||||
|
||||
/**
|
||||
* Events are encrypted as with `Legacy` mode, but encryption will throw an error if a
|
||||
* verified user has an unsigned device, or if a verified user replaces
|
||||
* their identity. Events are decrypted only if they come from cross-signed
|
||||
* devices, or devices that existed before the Rust crypto SDK started
|
||||
* tracking device trust: other events will result in a decryption failure. (To access the failure
|
||||
* reason, see {@link MatrixEvent.decryptionFailureReason}.)
|
||||
*/
|
||||
Transition,
|
||||
|
||||
/**
|
||||
* Message encryption keys are only shared with devices that have been cross-signed by their owner.
|
||||
* Encryption will throw an error if a verified user replaces their identity. Events are
|
||||
* decrypted only if they come from a cross-signed device other events will result in a decryption
|
||||
* failure. (To access the failure reason, see {@link MatrixEvent.decryptionFailureReason}.)
|
||||
*/
|
||||
Invisible,
|
||||
}
|
||||
|
||||
/**
|
||||
* Options object for `CryptoApi.bootstrapCrossSigning`.
|
||||
*/
|
||||
|
@@ -88,6 +88,7 @@ import {
|
||||
BootstrapCrossSigningOpts,
|
||||
CrossSigningKeyInfo,
|
||||
CrossSigningStatus,
|
||||
CryptoMode,
|
||||
decodeRecoveryKey,
|
||||
DecryptionFailureCode,
|
||||
DeviceVerificationStatus,
|
||||
@@ -648,6 +649,13 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
this.backupManager.checkAndStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Crypto.CryptoApi#setCryptoMode}.
|
||||
*/
|
||||
public setCryptoMode(cryptoMode: CryptoMode): void {
|
||||
throw new Error("Not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Crypto.CryptoApi#getVersion}.
|
||||
*/
|
||||
|
@@ -45,6 +45,7 @@ import {
|
||||
CrossSigningStatus,
|
||||
CryptoApi,
|
||||
CryptoCallbacks,
|
||||
CryptoMode,
|
||||
Curve25519AuthData,
|
||||
DecryptionFailureCode,
|
||||
DeviceVerificationStatus,
|
||||
@@ -106,6 +107,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
private readonly RECOVERY_KEY_DERIVATION_ITERATIONS = 500000;
|
||||
|
||||
private _trustCrossSignedDevices = true;
|
||||
private cryptoMode = CryptoMode.Legacy;
|
||||
|
||||
/** whether {@link stop} has been called */
|
||||
private stopped = false;
|
||||
@@ -257,7 +259,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
// through decryptEvent and hence get rid of this case.
|
||||
throw new Error("to-device event was not decrypted in preprocessToDeviceMessages");
|
||||
}
|
||||
return await this.eventDecryptor.attemptEventDecryption(event);
|
||||
return await this.eventDecryptor.attemptEventDecryption(event, this.cryptoMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,6 +369,13 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
return `Rust SDK ${versions.matrix_sdk_crypto} (${versions.git_sha}), Vodozemac ${versions.vodozemac}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link Crypto.CryptoApi#setCryptoMode}.
|
||||
*/
|
||||
public setCryptoMode(cryptoMode: CryptoMode): void {
|
||||
this.cryptoMode = cryptoMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link CryptoApi#isEncryptionEnabledInRoom}.
|
||||
*/
|
||||
@@ -1742,18 +1751,31 @@ class EventDecryptor {
|
||||
private readonly perSessionBackupDownloader: PerSessionKeyBackupDownloader,
|
||||
) {}
|
||||
|
||||
public async attemptEventDecryption(event: MatrixEvent): Promise<IEventDecryptionResult> {
|
||||
public async attemptEventDecryption(event: MatrixEvent, cryptoMode: CryptoMode): Promise<IEventDecryptionResult> {
|
||||
// add the event to the pending list *before* attempting to decrypt.
|
||||
// then, if the key turns up while decryption is in progress (and
|
||||
// decryption fails), we will schedule a retry.
|
||||
// (fixes https://github.com/vector-im/element-web/issues/5001)
|
||||
this.addEventToPendingList(event);
|
||||
|
||||
let trustRequirement;
|
||||
switch (cryptoMode) {
|
||||
case CryptoMode.Legacy:
|
||||
trustRequirement = RustSdkCryptoJs.TrustRequirement.Untrusted;
|
||||
break;
|
||||
case CryptoMode.Transition:
|
||||
trustRequirement = RustSdkCryptoJs.TrustRequirement.CrossSignedOrLegacy;
|
||||
break;
|
||||
case CryptoMode.Invisible:
|
||||
trustRequirement = RustSdkCryptoJs.TrustRequirement.CrossSigned;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = (await this.olmMachine.decryptRoomEvent(
|
||||
stringifyEvent(event),
|
||||
new RustSdkCryptoJs.RoomId(event.getRoomId()!),
|
||||
new RustSdkCryptoJs.DecryptionSettings(RustSdkCryptoJs.TrustRequirement.Untrusted),
|
||||
new RustSdkCryptoJs.DecryptionSettings(trustRequirement),
|
||||
)) as RustSdkCryptoJs.DecryptedRoomEvent;
|
||||
|
||||
// Success. We can remove the event from the pending list, if
|
||||
@@ -1861,6 +1883,36 @@ class EventDecryptor {
|
||||
errorDetails,
|
||||
);
|
||||
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.SenderIdentityPreviouslyVerified:
|
||||
// We're refusing to decrypt due to not trusting the sender,
|
||||
// rather than failing to decrypt due to lack of keys, so we
|
||||
// don't need to keep it on the pending list.
|
||||
this.removeEventFromPendingList(event);
|
||||
throw new DecryptionError(
|
||||
DecryptionFailureCode.SENDER_IDENTITY_PREVIOUSLY_VERIFIED,
|
||||
"The sender identity is unverified, but was previously verified.",
|
||||
);
|
||||
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.UnknownSenderDevice:
|
||||
// We're refusing to decrypt due to not trusting the sender,
|
||||
// rather than failing to decrypt due to lack of keys, so we
|
||||
// don't need to keep it on the pending list.
|
||||
this.removeEventFromPendingList(event);
|
||||
throw new DecryptionError(
|
||||
DecryptionFailureCode.UNKNOWN_SENDER_DEVICE,
|
||||
"The sender device is not known.",
|
||||
);
|
||||
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.UnsignedSenderDevice:
|
||||
// We're refusing to decrypt due to not trusting the sender,
|
||||
// rather than failing to decrypt due to lack of keys, so we
|
||||
// don't need to keep it on the pending list.
|
||||
this.removeEventFromPendingList(event);
|
||||
throw new DecryptionError(
|
||||
DecryptionFailureCode.UNSIGNED_SENDER_DEVICE,
|
||||
"The sender identity is not cross-signed.",
|
||||
);
|
||||
|
||||
// We don't map MismatchedIdentityKeys for now, as there is no equivalent in legacy.
|
||||
// Just put it on the `UNKNOWN_ERROR` bucket.
|
||||
default:
|
||||
|
Reference in New Issue
Block a user