Skip to content

Conversation

@douglas-xt
Copy link
Contributor

This PR adds validation of public keys during account recovery to prevent users from restoring their account using a backup file that doesn't belong to them.

When a user performs account recovery, the public keys included in the backup file are now compared against the keys stored in the database. If the keys don't match, the recovery is rejected with a clear error message indicating the backup file doesn't correspond to the account.

This change works in conjunction with updates in the SDK and drive-web that now include public keys in the backup file generation and send them during the recovery request.

@douglas-xt douglas-xt force-pushed the fix/validate-public-keys-on-recovery branch from 212af49 to c635ea3 Compare January 7, 2026 18:26
@sg-gs sg-gs self-requested a review January 8, 2026 08:59
@ApiProperty()
@IsOptional()
kyber?: string;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a line between this class and the next one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why in this class ecc and kyber are optional? Isn't it enough just to have optional public keys field in RecoverAccountDto? Because now we can have {ecc: undefined, kyber: underfined}, {ecc: , kyber: underfined}, {ecc: undefined, kyber: } and I don't think we should accept those as valid input

}

if (!withReset && !publicKeys) {
throw new BadRequestException('Invalid backup file');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'Invalid keys' it's a better naming as you do not know if those come from a backup file or anything else in the future

publicKeys.kyber && existingKeys.kyber !== publicKeys.kyber;

if (eccMismatch || kyberMismatch) {
throw new BadRequestException('Invalid backup file');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. In this case it could be worth it to specify which key is failing.

@sg-gs
Copy link
Member

sg-gs commented Jan 8, 2026

WDYT of this PR @TamaraFinogina ?


const user = await this.userRepository.findByUuid(userUuid);

if (publicKeys) {
Copy link
Contributor

@TamaraFinogina TamaraFinogina Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if withReset = true and mismatching public keys, then backup won't go through?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sg-gs Do we require/can send publicKeys for account reset? Becuase now we check public keys and if they fail we abort even if it's withReset=true


if (publicKeys) {
const existingKeys = await this.keyServerUseCases.getPublicKeys(user.id);
const eccMismatch = publicKeys.ecc && existingKeys.ecc !== publicKeys.ecc;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we send publicKeys = {ecc: underfined, kyber: underfined}, then publicKeys.ecc would be false, right? so, the eccMismatch will be false without even checking existingKeys.ecc !== publicKeys.ecc; part, no?

I think we shouldn't accept those keys. Or at least use something like
! publicKeys.ecc || existingKeys.ecc !== publicKeys.ecc

then undefined, null or similar keeys will give a mismatch

const existingKeys = await this.keyServerUseCases.getPublicKeys(user.id);
const eccMismatch = publicKeys.ecc && existingKeys.ecc !== publicKeys.ecc;
const kyberMismatch =
publicKeys.kyber && existingKeys.kyber !== publicKeys.kyber;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@douglas-xt This check would pass for publicKeys = {ecc: underfined, kyber: underfined} without even checking what existingKeys are. I know that now they are not empty, but if we change that in the future this line becomes an issue. Can we remove publicKeys.kyber && and just do the comparison?

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 9, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants