askSetupCrossSigning method

Future<void> askSetupCrossSigning({
  1. bool setupMasterKey = false,
  2. bool setupSelfSigningKey = false,
  3. bool setupUserSigningKey = false,
})

Implementation

Future<void> askSetupCrossSigning(
    {bool setupMasterKey = false,
    bool setupSelfSigningKey = false,
    bool setupUserSigningKey = false}) async {
  if (state != BootstrapState.askSetupCrossSigning) {
    throw BootstrapBadStateException();
  }
  if (!setupMasterKey && !setupSelfSigningKey && !setupUserSigningKey) {
    await client.dehydratedDeviceSetup(newSsssKey!);
    checkOnlineKeyBackup();
    return;
  }
  final userID = client.userID!;
  try {
    Uint8List masterSigningKey;
    final secretsToStore = <String, String>{};
    SDNCrossSigningKey? masterKey;
    SDNCrossSigningKey? selfSigningKey;
    SDNCrossSigningKey? userSigningKey;
    String? masterPub;
    if (setupMasterKey) {
      final master = olm.PkSigning();
      try {
        masterSigningKey = master.generate_seed();
        masterPub = master.init_with_seed(masterSigningKey);
        final json = <String, dynamic>{
          'user_id': userID,
          'usage': ['master'],
          'keys': <String, dynamic>{
            'ed25519:$masterPub': masterPub,
          },
        };
        masterKey = SDNCrossSigningKey.fromJson(json);
        secretsToStore[EventTypes.CrossSigningMasterKey] =
            base64.encode(masterSigningKey);
      } finally {
        master.free();
      }
    } else {
      Logs().v('Get stored key...');
      masterSigningKey = base64decodeUnpadded(
          await newSsssKey?.getStored(EventTypes.CrossSigningMasterKey) ??
              '');
      if (masterSigningKey.isEmpty) {
        // no master signing key :(
        throw BootstrapBadStateException('No master key');
      }
      final master = olm.PkSigning();
      try {
        masterPub = master.init_with_seed(masterSigningKey);
      } finally {
        master.free();
      }
    }
    String? sign(Map<String, dynamic> object) {
      final keyObj = olm.PkSigning();
      try {
        keyObj.init_with_seed(masterSigningKey);
        return keyObj
            .sign(String.fromCharCodes(canonicalJson.encode(object)));
      } finally {
        keyObj.free();
      }
    }

    if (setupSelfSigningKey) {
      final selfSigning = olm.PkSigning();
      try {
        final selfSigningPriv = selfSigning.generate_seed();
        final selfSigningPub = selfSigning.init_with_seed(selfSigningPriv);
        final json = <String, dynamic>{
          'user_id': userID,
          'usage': ['self_signing'],
          'keys': <String, dynamic>{
            'ed25519:$selfSigningPub': selfSigningPub,
          },
        };
        final signature = sign(json);
        json['signatures'] = <String, dynamic>{
          userID: <String, dynamic>{
            'ed25519:$masterPub': signature,
          },
        };
        selfSigningKey = SDNCrossSigningKey.fromJson(json);
        secretsToStore[EventTypes.CrossSigningSelfSigning] =
            base64.encode(selfSigningPriv);
      } finally {
        selfSigning.free();
      }
    }
    if (setupUserSigningKey) {
      final userSigning = olm.PkSigning();
      try {
        final userSigningPriv = userSigning.generate_seed();
        final userSigningPub = userSigning.init_with_seed(userSigningPriv);
        final json = <String, dynamic>{
          'user_id': userID,
          'usage': ['user_signing'],
          'keys': <String, dynamic>{
            'ed25519:$userSigningPub': userSigningPub,
          },
        };
        final signature = sign(json);
        json['signatures'] = <String, dynamic>{
          userID: <String, dynamic>{
            'ed25519:$masterPub': signature,
          },
        };
        userSigningKey = SDNCrossSigningKey.fromJson(json);
        secretsToStore[EventTypes.CrossSigningUserSigning] =
            base64.encode(userSigningPriv);
      } finally {
        userSigning.free();
      }
    }
    // upload the keys!
    state = BootstrapState.loading;
    Logs().v('Upload device signing keys.');
    await client.uiaRequestBackground(
        (AuthenticationData? auth) => client.uploadCrossSigningKeys(
              masterKey: masterKey,
              selfSigningKey: selfSigningKey,
              userSigningKey: userSigningKey,
              auth: auth,
            ));
    Logs().v('Device signing keys have been uploaded.');
    // aaaand set the SSSS secrets
    final futures = <Future<void>>[];
    if (masterKey != null) {
      futures.add(
        client.onSync.stream
            .firstWhere((syncUpdate) =>
                masterKey?.publicKey != null &&
                client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key ==
                    masterKey?.publicKey)
            .then((_) => Logs().v('New Master Key was created')),
      );
    }
    for (final entry in secretsToStore.entries) {
      futures.add(
        client.onSync.stream
            .firstWhere((syncUpdate) =>
                syncUpdate.accountData != null &&
                syncUpdate.accountData!
                    .any((accountData) => accountData.type == entry.key))
            .then((_) =>
                Logs().v('New Key with type ${entry.key} was created')),
      );
      Logs().v('Store new SSSS key ${entry.key}...');
      await newSsssKey?.store(entry.key, entry.value);
    }
    Logs().v(
        'Wait for MasterKey and ${secretsToStore.entries.length} keys to be created');
    await Future.wait<void>(futures);
    final keysToSign = <SignableKey>[];
    if (masterKey != null) {
      if (client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key !=
          masterKey.publicKey) {
        throw BootstrapBadStateException(
            'ERROR: New master key does not match up!');
      }
      Logs().v('Set own master key to verified...');
      await client.userDeviceKeys[client.userID]!.masterKey!
          .setVerified(true, false);
      keysToSign.add(client.userDeviceKeys[client.userID]!.masterKey!);
    }
    if (selfSigningKey != null) {
      keysToSign.add(
          client.userDeviceKeys[client.userID]!.deviceKeys[client.deviceID]!);
    }
    Logs().v('Sign ourself...');
    await encryption.crossSigning.sign(keysToSign);
  } catch (e, s) {
    Logs().e('[Bootstrapping] Error setting up cross signing', e, s);
    state = BootstrapState.error;
    return;
  }

  await client.dehydratedDeviceSetup(newSsssKey!);
  checkOnlineKeyBackup();
}