1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Change check{User|Device}Trust interfaces

...to return objects with functions rather than a bitmask
This commit is contained in:
David Baker
2019-11-15 12:15:13 +00:00
parent ce2d1d6e2b
commit e541b96a71
5 changed files with 201 additions and 90 deletions

View File

@@ -259,8 +259,17 @@ describe("Cross Signing", function() {
// once ssk is confirmed, device key should be trusted // once ssk is confirmed, device key should be trusted
await keyChangePromise; await keyChangePromise;
await uploadSigsPromise; await uploadSigsPromise;
expect(alice.checkUserTrust("@alice:example.com")).toBe(6);
expect(alice.checkDeviceTrust("@alice:example.com", "Osborne2")).toBe(7); const aliceTrust = alice.checkUserTrust("@alice:example.com");
expect(aliceTrust.isCrossSigningVerified()).toBeTruthy();
expect(aliceTrust.isTofu()).toBeTruthy();
expect(aliceTrust.isVerified()).toBeTruthy();
const aliceDeviceTrust = alice.checkDeviceTrust("@alice:example.com", "Osborne2");
expect(aliceDeviceTrust.isCrossSigningVerified()).toBeTruthy();
expect(aliceDeviceTrust.isLocallyVerified()).toBeTruthy();
expect(aliceDeviceTrust.isTofu()).toBeTruthy();
expect(aliceDeviceTrust.isVerified()).toBeTruthy();
}); });
it("should use trust chain to determine device verification", async function() { it("should use trust chain to determine device verification", async function() {
@@ -324,14 +333,27 @@ describe("Cross Signing", function() {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
// Bob's device key should be TOFU // Bob's device key should be TOFU
expect(alice.checkUserTrust("@bob:example.com")).toBe(2); const bobTrust = alice.checkUserTrust("@bob:example.com");
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(2); expect(bobTrust.isVerified()).toBeFalsy();
expect(bobTrust.isTofu()).toBeTruthy();
const bobDeviceTrust = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust.isVerified()).toBeFalsy();
expect(bobDeviceTrust.isTofu()).toBeTruthy();
// Alice verifies Bob's SSK // Alice verifies Bob's SSK
alice.uploadKeySignatures = () => {}; alice.uploadKeySignatures = () => {};
await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey, true); await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey, true);
// Bob's device key should be trusted // Bob's device key should be trusted
expect(alice.checkUserTrust("@bob:example.com")).toBe(6); const bobTrust2 = alice.checkUserTrust("@bob:example.com");
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(6); expect(bobTrust2.isCrossSigningVerified()).toBeTruthy();
expect(bobTrust2.isTofu()).toBeTruthy();
const bobDeviceTrust2 = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust2.isCrossSigningVerified()).toBeTruthy();
expect(bobDeviceTrust2.isLocallyVerified()).toBeFalsy();
expect(bobDeviceTrust2.isTofu()).toBeTruthy();
}); });
it("should trust signatures received from other devices", async function() { it("should trust signatures received from other devices", async function() {
@@ -487,8 +509,14 @@ describe("Cross Signing", function() {
await keyChangePromise; await keyChangePromise;
// Bob's device key should be trusted // Bob's device key should be trusted
expect(alice.checkUserTrust("@bob:example.com")).toBe(6); const bobTrust = alice.checkUserTrust("@bob:example.com");
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(6); expect(bobTrust.isCrossSigningVerified()).toBeTruthy();
expect(bobTrust.isTofu()).toBeTruthy();
const bobDeviceTrust = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust.isCrossSigningVerified()).toBeTruthy();
expect(bobDeviceTrust.isLocallyVerified()).toBeFalsy();
expect(bobDeviceTrust.isTofu()).toBeTruthy();
}); });
it("should dis-trust an unsigned device", async function() { it("should dis-trust an unsigned device", async function() {
@@ -547,11 +575,17 @@ describe("Cross Signing", function() {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
// Bob's device key should be untrusted // Bob's device key should be untrusted
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(0); const bobDeviceTrust = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust.isVerified()).toBeFalsy();
expect(bobDeviceTrust.isTofu()).toBeFalsy();
// Alice verifies Bob's SSK // Alice verifies Bob's SSK
await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey, true); await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey, true);
// Bob's device key should be untrusted // Bob's device key should be untrusted
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(0); const bobDeviceTrust2 = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust2.isVerified()).toBeFalsy();
expect(bobDeviceTrust2.isTofu()).toBeFalsy();
}); });
it("should dis-trust a user when their ssk changes", async function() { it("should dis-trust a user when their ssk changes", async function() {
@@ -615,8 +649,12 @@ describe("Cross Signing", function() {
// Alice verifies Bob's SSK // Alice verifies Bob's SSK
alice.uploadKeySignatures = () => {}; alice.uploadKeySignatures = () => {};
await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey, true); await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey, true);
// Bob's device key should be trusted // Bob's device key should be trusted
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(6); const bobDeviceTrust = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust.isVerified()).toBeTruthy();
expect(bobDeviceTrust.isTofu()).toBeTruthy();
// Alice downloads new SSK for Bob // Alice downloads new SSK for Bob
const bobMasterSigning2 = new global.Olm.PkSigning(); const bobMasterSigning2 = new global.Olm.PkSigning();
const bobMasterPrivkey2 = bobMasterSigning2.generate_seed(); const bobMasterPrivkey2 = bobMasterSigning2.generate_seed();
@@ -652,23 +690,38 @@ describe("Cross Signing", function() {
unsigned: {}, unsigned: {},
}); });
// Bob's and his device should be untrusted // Bob's and his device should be untrusted
expect(alice.checkUserTrust("@bob:example.com")).toBe(0); const bobTrust = alice.checkUserTrust("@bob:example.com");
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(0); expect(bobTrust.isVerified()).toBeFalsy();
expect(bobTrust.isTofu()).toBeFalsy();
const bobDeviceTrust2 = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust2.isVerified()).toBeFalsy();
expect(bobDeviceTrust2.isTofu()).toBeFalsy();
// Alice verifies Bob's SSK // Alice verifies Bob's SSK
alice.uploadKeySignatures = () => {}; alice.uploadKeySignatures = () => {};
await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey2, true); await alice.setDeviceVerified("@bob:example.com", bobMasterPubkey2, true);
// Bob should be trusted but not his device // Bob should be trusted but not his device
expect(alice.checkUserTrust("@bob:example.com")).toBe(4); const bobTrust2 = alice.checkUserTrust("@bob:example.com");
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(0); expect(bobTrust2.isVerified()).toBeTruthy();
const bobDeviceTrust3 = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust3.isVerified()).toBeFalsy();
// Alice gets new signature for device // Alice gets new signature for device
const sig2 = bobSigning2.sign(bobDeviceString); const sig2 = bobSigning2.sign(bobDeviceString);
bobDevice.signatures["@bob:example.com"]["ed25519:" + bobPubkey2] = sig2; bobDevice.signatures["@bob:example.com"]["ed25519:" + bobPubkey2] = sig2;
alice._crypto._deviceList.storeDevicesForUser("@bob:example.com", { alice._crypto._deviceList.storeDevicesForUser("@bob:example.com", {
Dynabook: bobDevice, Dynabook: bobDevice,
}); });
// Bob's device should be trusted again (but not TOFU) // Bob's device should be trusted again (but not TOFU)
expect(alice.checkUserTrust("@bob:example.com")).toBe(4); const bobTrust3 = alice.checkUserTrust("@bob:example.com");
expect(alice.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(4); expect(bobTrust3.isVerified()).toBeTruthy();
const bobDeviceTrust4 = alice.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bobDeviceTrust4.isCrossSigningVerified()).toBeTruthy();
}); });
it("should offer to upgrade device verifications to cross-signing", async function() { it("should offer to upgrade device verifications to cross-signing", async function() {
@@ -722,13 +775,17 @@ describe("Cross Signing", function() {
await alice.resetCrossSigningKeys(); await alice.resetCrossSigningKeys();
await upgradePromise; await upgradePromise;
expect(alice.checkUserTrust("@bob:example.com")).toBe(6); const bobTrust = alice.checkUserTrust("@bob:example.com");
expect(bobTrust.isCrossSigningVerified()).toBeTruthy();
expect(bobTrust.isTofu()).toBeTruthy();
// "forget" that Bob is trusted // "forget" that Bob is trusted
delete alice._crypto._deviceList._crossSigningInfo["@bob:example.com"] delete alice._crypto._deviceList._crossSigningInfo["@bob:example.com"]
.keys.master.signatures["@alice:example.com"]; .keys.master.signatures["@alice:example.com"];
expect(alice.checkUserTrust("@bob:example.com")).toBe(2); const bobTrust2 = alice.checkUserTrust("@bob:example.com");
expect(bobTrust2.isCrossSigningVerified()).toBeFalsy();
expect(bobTrust2.isTofu()).toBeTruthy();
upgradePromise = new Promise((resolve) => { upgradePromise = new Promise((resolve) => {
upgradeResolveFunc = resolve; upgradeResolveFunc = resolve;
@@ -736,6 +793,8 @@ describe("Cross Signing", function() {
alice._crypto._deviceList.emit("userCrossSigningUpdated", "@bob:example.com"); alice._crypto._deviceList.emit("userCrossSigningUpdated", "@bob:example.com");
await upgradePromise; await upgradePromise;
expect(alice.checkUserTrust("@bob:example.com")).toBe(6); const bobTrust3 = alice.checkUserTrust("@bob:example.com");
expect(bobTrust.isCrossSigningVerified()).toBeTruthy();
expect(bobTrust.isTofu()).toBeTruthy();
}); });
}); });

View File

@@ -318,9 +318,17 @@ describe("SAS verification", function() {
await verifyProm; await verifyProm;
expect(alice.client.checkDeviceTrust("@bob:example.com", "Dynabook")).toBe(1); const bobDeviceTrust = alice.client.checkDeviceTrust("@bob:example.com", "Dynabook");
expect(bob.client.checkUserTrust("@alice:example.com")).toBe(6); expect(bobDeviceTrust.isLocallyVerified()).toBeTruthy();
expect(bob.client.checkDeviceTrust("@alice:example.com", "Osborne2")).toBe(1); expect(bobDeviceTrust.isCrossSigningVerified()).toBeFalsy();
const aliceTrust = bob.client.checkUserTrust("@alice:example.com");
expect(aliceTrust.isCrossSigningVerified()).toBeTruthy();
expect(aliceTrust.isTofu()).toBeTruthy();
const aliceDeviceTrust = bob.client.checkDeviceTrust("@alice:example.com", "Osborne2");
expect(aliceDeviceTrust.isLocallyVerified()).toBeTruthy();
expect(aliceDeviceTrust.isCrossSigningVerified()).toBeFalsy();
}); });
}); });

View File

@@ -235,9 +235,8 @@ function keyFromRecoverySession(session, decryptionKey) {
* {string} request_id The ID of the request. Used to match a * {string} request_id The ID of the request. Used to match a
* corresponding `crypto.secrets.request_cancelled`. The request ID will be * corresponding `crypto.secrets.request_cancelled`. The request ID will be
* unique per sender, device pair. * unique per sender, device pair.
* {int} device_trust: The trust status of the device requesting * {DeviceTrustLevel} device_trust: The trust status of the device requesting
* the secret. Will be a bit mask in the same form as returned by {@link * the secret as returned by {@link module:client~MatrixClient#checkDeviceTrust}.
* module:client~MatrixClient#checkDeviceTrust}.
*/ */
function MatrixClient(opts) { function MatrixClient(opts) {
opts.baseUrl = utils.ensureNoTrailingSlash(opts.baseUrl); opts.baseUrl = utils.ensureNoTrailingSlash(opts.baseUrl);
@@ -1004,16 +1003,7 @@ function wrapCryptoFuncs(MatrixClient, names) {
* @function module:client~MatrixClient#checkUserTrust * @function module:client~MatrixClient#checkUserTrust
* @param {string} userId The ID of the user to check. * @param {string} userId The ID of the user to check.
* *
* @returns {integer} a bit mask indicating how the user is trusted (if at all) * @returns {UserTrustLevel}
* - returnValue & 1: unused
* - returnValue & 2: trust-on-first-use cross-signing key
* - returnValue & 4: user's cross-signing key is verified
*
* TODO: is this a good way of representing it? Or we could return an object
* with different keys, or a set? The advantage of doing it this way is that
* you can define which methods you want to use, "&" with the appopriate mask,
* then test for truthiness. Or if you want to just trust everything, then use
* the value alone. However, I wonder if bit masks are too obscure...
*/ */
/** /**
@@ -1024,12 +1014,7 @@ function wrapCryptoFuncs(MatrixClient, names) {
* @param {string} userId The ID of the user whose devices is to be checked. * @param {string} userId The ID of the user whose devices is to be checked.
* @param {string} deviceId The ID of the device to check * @param {string} deviceId The ID of the device to check
* *
* @returns {integer} a bit mask indicating how the user is trusted (if at all) * @returns {DeviuceTrustLevel}
* - returnValue & 1: device marked as verified
* - returnValue & 2: trust-on-first-use cross-signing key
* - returnValue & 4: user's cross-signing key is verified and device is signed
*
* TODO: see checkUserTrust
*/ */
wrapCryptoFuncs(MatrixClient, [ wrapCryptoFuncs(MatrixClient, [

View File

@@ -313,15 +313,15 @@ export class CrossSigningInfo extends EventEmitter {
if (this.userId === userCrossSigning.userId if (this.userId === userCrossSigning.userId
&& this.getId() && this.getId() === userCrossSigning.getId() && this.getId() && this.getId() === userCrossSigning.getId()
&& this.getId("self_signing") && this.getId("self_signing")
&& this.getId("self_signing") === userCrossSigning.getId("self_signing")) { && this.getId("self_signing") === userCrossSigning.getId("self_signing")
return CrossSigningVerification.VERIFIED ) {
| (this.firstUse ? CrossSigningVerification.TOFU return new UserTrustLevel(true, this.firstUse);
: CrossSigningVerification.UNVERIFIED);
} }
if (!this.keys.user_signing) { if (!this.keys.user_signing) {
return (userCrossSigning.firstUse ? CrossSigningVerification.TOFU // If there's no user signing key, they can't possibly be verified.
: CrossSigningVerification.UNVERIFIED); // They may be TOFU trusted though.
return new UserTrustLevel(false, userCrossSigning.firstUse);
} }
let userTrusted; let userTrusted;
@@ -333,28 +333,31 @@ export class CrossSigningInfo extends EventEmitter {
} catch (e) { } catch (e) {
userTrusted = false; userTrusted = false;
} }
return (userTrusted ? CrossSigningVerification.VERIFIED return new UserTrustLevel(userTrusted, userCrossSigning.firstUse);
: CrossSigningVerification.UNVERIFIED)
| (userCrossSigning.firstUse ? CrossSigningVerification.TOFU
: CrossSigningVerification.UNVERIFIED);
} }
checkDeviceTrust(userCrossSigning, device) { checkDeviceTrust(userCrossSigning, device, localTrust) {
const userTrust = this.checkUserTrust(userCrossSigning); const userTrust = this.checkUserTrust(userCrossSigning);
const userSSK = userCrossSigning.keys.self_signing; const userSSK = userCrossSigning.keys.self_signing;
if (!userSSK) { if (!userSSK) {
return 0; // if the user has no self-signing key then we cannot make any
// trust assertions about this device from cross-signing
return new DeviceTrustLevel(false, false, localTrust);
} }
const deviceObj = deviceToObject(device, userCrossSigning.userId); const deviceObj = deviceToObject(device, userCrossSigning.userId);
try { try {
// if we can verify the user's SSK from their master key...
pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId); pkVerify(userSSK, userCrossSigning.getId(), userCrossSigning.userId);
// ...and this device's key from their SSK...
pkVerify( pkVerify(
deviceObj, publicKeyFromKeyInfo(userSSK)[1], userCrossSigning.userId, deviceObj, publicKeyFromKeyInfo(userSSK)[1], userCrossSigning.userId,
); );
return userTrust; // ...then we trust this device as much as far as we trust the user
return DeviceTrustLevel.fromUserTrustLevel(userTrust, localTrust);
} catch (e) { } catch (e) {
return 0; return new DeviceTrustLevel(false, false, localTrust);
} }
} }
} }
@@ -377,8 +380,80 @@ export const CrossSigningLevel = {
USER_SIGNING: 2, USER_SIGNING: 2,
}; };
export const CrossSigningVerification = { /**
UNVERIFIED: 0, * Represents the ways in which we trust a user
TOFU: 1, */
VERIFIED: 2, export class UserTrustLevel {
constructor(crossSigningVerified, tofu) {
this._crossSigningVerified = crossSigningVerified;
this._tofu = tofu;
}
/**
* @returns {bool} true if this user is verified via any means
*/
isVerified() {
return this.isCrossSigningVerified();
}
/**
* @returns {bool} true if this user is verified via cross signing
*/
isCrossSigningVerified() {
return this._crossSigningVerified;
}
/**
* @returns {bool} true if this user's key is trusted on first use
*/
isTofu() {
return this._tofu;
}
};
/**
* Represents the ways in which we trust a device
*/
export class DeviceTrustLevel {
constructor(crossSigningVerified, tofu, localVerified) {
this._crossSigningVerified = crossSigningVerified;
this._tofu = tofu;
this._localVerified = localVerified;
}
static fromUserTrustLevel(userTrustLevel, localVerified) {
return new DeviceTrustLevel(
userTrustLevel._crossSigningVerified,
userTrustLevel._tofu,
localVerified,
);
}
/**
* @returns {bool} true if this user is verified via any means
*/
isVerified() {
return this.isCrossSigningVerified() || this.isLocallyVerified();
}
/**
* @returns {bool} true if this user is verified via cross signing
*/
isCrossSigningVerified() {
return this._crossSigningVerified;
}
/**
* @returns {bool} true if this user is verified via cross signing
*/
isLocallyVerified() {
return this._localVerified;
}
/**
* @returns {bool} true if this user's key is trusted on first use
*/
isTofu() {
return this._tofu;
}
}; };

View File

@@ -36,7 +36,7 @@ const DeviceInfo = require("./deviceinfo");
const DeviceVerification = DeviceInfo.DeviceVerification; const DeviceVerification = DeviceInfo.DeviceVerification;
const DeviceList = require('./DeviceList').default; const DeviceList = require('./DeviceList').default;
import { randomString } from '../randomstring'; import { randomString } from '../randomstring';
import { CrossSigningInfo } from './CrossSigning'; import { CrossSigningInfo, UserTrustLevel, DeviceTrustLevel } from './CrossSigning';
import SecretStorage from './Secrets'; import SecretStorage from './Secrets';
import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager'; import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager';
@@ -411,8 +411,8 @@ Crypto.prototype._checkForDeviceVerificationUpgrade = async function(
) { ) {
// only upgrade if this is the first cross-signing key that we've seen for // only upgrade if this is the first cross-signing key that we've seen for
// them, and if their cross-signing key isn't already verified // them, and if their cross-signing key isn't already verified
if (crossSigningInfo.firstUse const trustLevel = this._crossSigningInfo.checkUserTrust(crossSigningInfo);
&& !(this._crossSigningInfo.checkUserTrust(crossSigningInfo) & 2)) { if (crossSigningInfo.firstUse && !trustLevel.verified) {
const devices = this._deviceList.getRawStoredDevicesForUser(userId); const devices = this._deviceList.getRawStoredDevicesForUser(userId);
const deviceIds = await this._checkForValidDeviceSignature( const deviceIds = await this._checkForValidDeviceSignature(
userId, crossSigningInfo.keys.master, devices, userId, crossSigningInfo.keys.master, devices,
@@ -487,25 +487,14 @@ Crypto.prototype.getStoredCrossSigningForUser = function(userId) {
* *
* @param {string} userId The ID of the user to check. * @param {string} userId The ID of the user to check.
* *
* @returns {integer} a bit mask indicating how the user is trusted (if at all) * @returns {UserTrustLevel}
* - returnValue & 1: unused
* - returnValue & 2: trust-on-first-use cross-signing key
* - returnValue & 4: user's cross-signing key is verified
*
* TODO: is this a good way of representing it? Or we could return an object
* with different keys, or a set? The advantage of doing it this way is that
* you can define which methods you want to use, "&" with the appopriate mask,
* then test for truthiness. Or if you want to just trust everything, then use
* the value alone. However, I wonder if bit masks are too obscure...
*/ */
Crypto.prototype.checkUserTrust = function(userId) { Crypto.prototype.checkUserTrust = function(userId) {
const userCrossSigning = this._deviceList.getStoredCrossSigningForUser(userId); const userCrossSigning = this._deviceList.getStoredCrossSigningForUser(userId);
if (!userCrossSigning) { if (!userCrossSigning) {
return 0; return new UserTrustLevel(false, false);
} }
// We shift the result from CrossSigningInfo.checkUserTrust so this return this._crossSigningInfo.checkUserTrust(userCrossSigning);
// function's return is consistent with checkDeviceTrust
return this._crossSigningInfo.checkUserTrust(userCrossSigning) << 1;
}; };
/** /**
@@ -514,25 +503,20 @@ Crypto.prototype.checkUserTrust = function(userId) {
* @param {string} userId The ID of the user whose devices is to be checked. * @param {string} userId The ID of the user whose devices is to be checked.
* @param {string} deviceId The ID of the device to check * @param {string} deviceId The ID of the device to check
* *
* @returns {integer} a bit mask indicating how the user is trusted (if at all) * @returns {DeviceTrustLevel}
* - returnValue & 1: device marked as verified
* - returnValue & 2: trust-on-first-use cross-signing key
* - returnValue & 4: user's cross-signing key is verified and device is signed
*
* TODO: see checkUserTrust
*/ */
Crypto.prototype.checkDeviceTrust = function(userId, deviceId) { Crypto.prototype.checkDeviceTrust = function(userId, deviceId) {
let rv = 0;
const device = this._deviceList.getStoredDevice(userId, deviceId); const device = this._deviceList.getStoredDevice(userId, deviceId);
if (device && device.isVerified()) { const trustedLocally = device && device.isVerified();
rv |= 1;
}
const userCrossSigning = this._deviceList.getStoredCrossSigningForUser(userId); const userCrossSigning = this._deviceList.getStoredCrossSigningForUser(userId);
if (device && userCrossSigning) { if (device && userCrossSigning) {
rv |= this._crossSigningInfo.checkDeviceTrust(userCrossSigning, device) << 1; return this._crossSigningInfo.checkDeviceTrust(
userCrossSigning, device, trustedLocally,
);
} else {
return new DeviceTrustLevel(false, false, trustedLocally);
} }
return rv;
}; };
/* /*