Skip to content

Commit

Permalink
Merge branch 'feature/vote-decrypt' into 3398-other-curves-vote-decrypt
Browse files Browse the repository at this point in the history
  • Loading branch information
bastianjoel committed Apr 19, 2024
2 parents 0e0a02d + 5d947f4 commit 800b6e7
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class OrganizationRepositoryService extends BaseRepository<ViewOrganizati
public getVerboseName = (plural = false) => this.translate.instant(plural ? `Organizations` : `Organization`);

public override getFieldsets(): Fieldsets<Organization> {
const coreFieldset: (keyof Organization)[] = [`name`, `description`, `vote_decrypt_public_main_key`];
const coreFieldset: (keyof Organization)[] = [`name`, `description`];
const settingsFieldset: (keyof (OrganizationSetting & Organization))[] = coreFieldset.concat(
`url`,
`legal_notice`,
Expand Down
42 changes: 32 additions & 10 deletions client/src/app/gateways/vote-decrypt-gateway.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ import { curve25519, utils, verify } from '@noble/ed25519';
import { distinctUntilChanged, map, Subscription } from 'rxjs';

import { PollType, VoteValue } from '../domain/models/poll';
import { Deferred } from '../infrastructure/utils/promises';
import { ViewPoll } from '../site/pages/meetings/pages/polls';
import {
getVotePublicKeySubscriptionConfig,
ORGANIZATION_VOTE_PUBLIC_KEY_SUBSCRIPTION
} from '../site/pages/organization/organization.subscription';
import { ORGANIZATION_ID } from '../site/pages/organization/services/organization.service';
import { OrganizationControllerService } from '../site/pages/organization/services/organization-controller.service';
import { ModelData } from '../site/services/autoupdate/utils';
import { ModelRequestService } from '../site/services/model-request.service';
import { PollRepositoryService } from './repositories/polls/poll-repository.service';

interface VoteVerificationData {
Expand Down Expand Up @@ -52,15 +57,16 @@ export class VoteDecryptGatewayService {
private _lastVoteVerificationData: VoteVerificationData[] = [];
private _verificationSubscription: Subscription;

private hasPublicMainKey = new Deferred();

public constructor(private orgaController: OrganizationControllerService, private injector: Injector) {
public constructor(
private orgaController: OrganizationControllerService,
private injector: Injector,
private modelRequestService: ModelRequestService
) {
this.orgaController.getViewModelObservable(ORGANIZATION_ID).subscribe(organization => {
if (organization?.vote_decrypt_public_main_key) {
this._publicMainKey = uInt8Enc(organization?.vote_decrypt_public_main_key);
console.log(`New public main key is loaded.`);
this._publicMainKeyString = organization?.vote_decrypt_public_main_key;
this.hasPublicMainKey.resolve();
}
});
}
Expand All @@ -69,7 +75,6 @@ export class VoteDecryptGatewayService {
* Must be called once after the PollRepositoryService is initialized to ensure that the cryptographic poll results get verified.
*/
public async initialize(): Promise<void> {
await this.hasPublicMainKey;
if (this._verificationSubscription) {
return;
}
Expand Down Expand Up @@ -112,7 +117,6 @@ export class VoteDecryptGatewayService {

public async encryptVote(poll: ViewPoll, vote: string): Promise<string> {
console.log(`Encrypting vote...`);
await this.hasPublicMainKey;
if (!poll.crypt_key || !poll.crypt_signature) {
throw new Error(`Voting failed: Keys not fully loaded.`);
}
Expand All @@ -122,7 +126,7 @@ export class VoteDecryptGatewayService {
const cryptSignature = uInt8Enc(poll.crypt_signature);

try {
if (!(await verify(cryptSignature, cryptKey, this._publicMainKey))) {
if (!(await verify(cryptSignature, cryptKey, (await this.getPublicMainKey()).raw))) {
throw new Error(`Voting failed: Cryptography keys could not be verified.`);
}
} catch (e) {
Expand All @@ -146,7 +150,6 @@ export class VoteDecryptGatewayService {
const verificationData: VoteVerificationData[] = [
{ id: poll.id, signature: poll.votes_signature, raws: poll.votes_raw }
];
await this.hasPublicMainKey;
await this.verify(verificationData);
}

Expand Down Expand Up @@ -190,7 +193,7 @@ export class VoteDecryptGatewayService {
const raws = uInt8Enc(date.raws, false);
let resultVerificationErrors: string[] = [];
try {
const verified = await verify(signature, raws, this._publicMainKey);
const verified = await verify(signature, raws, (await this.getPublicMainKey()).raw);
resultVerificationErrors = verified ? resultVerificationErrors : [`Signature verification failed`];
} catch (e) {
resultVerificationErrors.push(`Signature verification failed with error: "${e.message}"`);
Expand Down Expand Up @@ -277,6 +280,25 @@ export class VoteDecryptGatewayService {
return { raw, cooked, comparison: raw?.token.localeCompare(cooked?.token) };
}

private async getPublicMainKey(): Promise<{ raw: Uint8Array; str: string }> {
this.modelRequestService.subscribeTo(getVotePublicKeySubscriptionConfig());
const data = await this.modelRequestService.subscriptionGotData(ORGANIZATION_VOTE_PUBLIC_KEY_SUBSCRIPTION);
if (data) {
if (data !== true && (<ModelData>data)[`organization`]) {
const key = (<ModelData>data)[`organization`][ORGANIZATION_ID][`vote_decrypt_public_main_key`];
this._publicMainKey = uInt8Enc(key);
this._publicMainKeyString = key;
}

return {
raw: this._publicMainKey,
str: this._publicMainKeyString
};
}

throw new Error(`Failed to retrieve public key`);
}

private getSortedVoteArrays(poll: ViewPoll): { rawsArray: CryptoVoteData[]; modelResultsArray: CryptoVoteData[] } {
if (!poll.votes_raw) {
throw new Error(`Couldn't find raw votes.`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ViewOrganization } from 'src/app/site/pages/organization/view-models/vi
import { DEFAULT_FIELDSET } from '../../services/model-request-builder';

export const ORGANIZATION_SUBSCRIPTION = `organization_detail`;
export const ORGANIZATION_VOTE_PUBLIC_KEY_SUBSCRIPTION = `organization_vote_public_key`;

export const getMeetingListSubscriptionConfig: SubscriptionConfigGenerator = () => ({
modelRequest: {
Expand Down Expand Up @@ -39,6 +40,15 @@ export const getOrganizationSubscriptionConfig: SubscriptionConfigGenerator = ()
isDelayed: false
});

export const getVotePublicKeySubscriptionConfig: SubscriptionConfigGenerator = () => ({
modelRequest: {
viewModelCtor: ViewOrganization,
ids: [ORGANIZATION_ID],
fieldset: [`vote_decrypt_public_main_key`]
},
subscriptionName: ORGANIZATION_VOTE_PUBLIC_KEY_SUBSCRIPTION
});

export const getMeetingCreateSubscriptionConfig: SubscriptionConfigGenerator = () => ({
modelRequest: {
viewModelCtor: ViewOrganization,
Expand Down

0 comments on commit 800b6e7

Please sign in to comment.