Skip to content

Commit

Permalink
Improved tests for verificationtool, improved test data to enable moc…
Browse files Browse the repository at this point in the history
…k mode with different data sets
  • Loading branch information
frog711 committed Oct 27, 2023
1 parent 411a3bb commit 0240615
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 78 deletions.
8 changes: 4 additions & 4 deletions src/main/communication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { type SecretProof } from '../classes/ballot'
import { ElectionData, SecondDeviceFinalMessage, SecondDeviceLoginResponse } from '../classes/communication'
import { EnvironmentVariables } from './constants'
import { ErrorType } from './error'
import data from '../mock/extended.json'

type ResponseStatus = 'OK' | 'ERROR'
abstract class ResponseBean<T> { // eslint-disable-line
Expand Down Expand Up @@ -129,18 +128,19 @@ class Comm implements Communication {
}

class CommMock implements Communication {
public constructor(public readonly data) {}

Check failure on line 131 in src/main/communication.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing space before function parentheses
public async electionData (): Promise<ResponseBean<ElectionData>> {
const electionData = ElectionData.fromJson(data.electionData)
const electionData = ElectionData.fromJson(this.data.electionData)
return await Promise.resolve(new ResponseBeanOk<ElectionData>(electionData))
}

public async login (voterId: string, nonce: string, c: string, challenge: string): Promise<ResponseBean<SecondDeviceLoginResponse>> {
const loginResponse = SecondDeviceLoginResponse.fromJson(data.loginResponse.value)
const loginResponse = SecondDeviceLoginResponse.fromJson(this.data.loginResponse.value)
return await Promise.resolve(new ResponseBeanOk<SecondDeviceLoginResponse>(loginResponse))
}

public async challenge (token: string, proof: SecretProof): Promise<ResponseBeanOk<SecondDeviceFinalMessage>> {
const finalMessage = SecondDeviceFinalMessage.fromJson(data.finalMessage.value)
const finalMessage = SecondDeviceFinalMessage.fromJson(this.data.finalMessage.value)
return await Promise.resolve(new ResponseBeanOk<SecondDeviceFinalMessage>(finalMessage))
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type ProofGenerator, ProofGeneratorImpl, ProofGeneratorMock } from '../algorithms/proof'
import dataTest from '../mock/data.json'

Check failure on line 2 in src/main/constants.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'/home/runner/work/polyas-core3-second-device-verification/polyas-core3-second-device-verification/src/mock/data.json' imported multiple times
import dataUI from '../mock/extended.json'
import dataUI from '../mock/data.json'

Check failure on line 3 in src/main/constants.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'/home/runner/work/polyas-core3-second-device-verification/polyas-core3-second-device-verification/src/mock/data.json' imported multiple times
import { Comm, CommMock, type Communication } from './communication'
const k = '0373744f99d31509eb5f8caaabc0cc3fab70e571a5db4d762020723b9cd6ada260'
const g = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
Expand All @@ -22,12 +22,12 @@ class EnvironmentVariables {
this.instance.mode = mode
console.log('mode', mode)
if (mode === 'mock') {
this.instance.proofGen = new ProofGeneratorMock(BigInt(dataUI.challengeRequest.challenge), BigInt(dataUI.challengeRequest.challengeRandomCoin))
this.instance.comm = new CommMock()
this.instance.proofGen = new ProofGeneratorMock(BigInt(dataUI.challenge.challenge), BigInt(dataUI.challenge.challengeRandomCoin))
this.instance.comm = new CommMock(dataUI)
}
if (mode === 'test') {
this.instance.proofGen = new ProofGeneratorMock(BigInt(dataTest.challenge.challenge), BigInt(dataTest.challenge.challengeRandomCoin))
this.instance.comm = new CommMock()
this.instance.comm = new CommMock(dataTest)
} else if (mode === 'dev') {
this.instance.proofGen = new ProofGeneratorImpl()
this.instance.comm = new Comm()
Expand Down
10 changes: 9 additions & 1 deletion src/mock/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"c": "vtWXj-YxxTV2ektefJ5pk7AWc9saoPbu6wJZUZ9R1t8ekU89x7SCYLcg8ODi3fHST4BTmAK97XN3XqWc",
"vid": "voter8",
"nonce": "4bf8cecf3fb4c4b4372005e13a53dce705123fab5b9e9288461e6d8fbf9644ea",
"url": "localhost:5000?c=vtWXj-YxxTV2ektefJ5pk7AWc9saoPbu6wJZUZ9R1t8ekU89x7SCYLcg8ODi3fHST4BTmAK97XN3XqWc&vid=voter8&nonce=4bf8cecf3fb4c4b4372005e13a53dce705123fab5b9e9288461e6d8fbf9644ea",
"password": "196308",
"fingerprint": "b7e8e76c369d6a9ca268e40cde8347ac443040d6c4a1df3035744ace05b94e00849abf083ae5baa8fee462a723823054858387ec35462a49f93c2ea40b2fc876",
"proof": {
"e": "108039209026641834721998202775536164454916176078442584841940316235417705823230",
"r": "44267717001895006656767798790813376597351395807170189462353830054915294464906"
Expand All @@ -21,6 +23,7 @@
"challengeCommitment": "030e1a9be2459151057e9d731b524ca435f1c05bc0a95d3d82b30512d306172b17"
},
"loginResponse": {
"value": {
"token": "MDIwNWJmMmUxNDQ5NmY2OGMwZjg2ZjZiMzEzZjIxMGE5MzkzZWRiMDgzODIxZGNjNGY5OTE0Y2FiOWM1MWM5ZjJl.UjVXYXRxTlRzdk12QWRwOA==",
"ballotVoterId": "0205bf2e14496f68c0f86f6b313f210a9393edb083821dcc4f9914cab9c51c9f2e",
"electionId": "bfced618-34aa-4b78-ba5b-d21dc04a1a7e",
Expand All @@ -43,13 +46,18 @@
"messages": {},
"allowInvalid": true,
"initialMessage": "{\"secondDeviceParametersJson\":\"{\\\"publicKey\\\":\\\"030588c6c80497da9e50bf56a4853c9fd3dd945a5e2ed741ccf783c5538611da26\\\",\\\"verificationKey\\\":\\\"30820122300d06092a864886f70d01010105000382010f003082010a0282010100a52865923e9a08c8e58c0beacd3f40391f980b7db7a87c626d68dbf2a2a28a848402e5adc7ae7d3afef34b697bcf26e5c29b3be55850f2c7a308d90573d6b3788339104fc7579b07b483ccafa11f12ad123f6eaeb3a64a5cdc3f944ed613d5ad1bb6f8cbb704682d16391f731fac0c87dfe84859c9c9fd690a57cbe7a7bdf3a69d3e8457a1afd88112bf44538b6a04809b3e61ef9608c24ef1f02d6796e73bbeff49efca7a9cf443e36791bce307323d1a05f7fd8d8697b820f632eb50b19a2b4f20c958e193ec80b269e4a1b322bbd2a9d27ba91e7e1f5440bf944cdb1658f5d6d612a0b1d838cbbe19640fd4c5d967b03b95c388910c6ce0c3ecd9340af3f90203010001\\\",\\\"ballots\\\":[{\\\"type\\\":\\\"STANDARD_BALLOT\\\",\\\"id\\\":\\\"A\\\",\\\"title\\\":{\\\"default\\\":\\\"Ballot title\\\",\\\"value\\\":{}},\\\"lists\\\":[{\\\"id\\\":\\\"A1\\\",\\\"title\\\":{\\\"default\\\":\\\"First question!\\\",\\\"value\\\":{}},\\\"columnHeaders\\\":[{\\\"default\\\":\\\"\\\",\\\"value\\\":{}}],\\\"candidates\\\":[{\\\"id\\\":\\\"A1-1\\\",\\\"columns\\\":[{\\\"value\\\":{\\\"default\\\":\\\"Yes\\\",\\\"value\\\":{}},\\\"contentType\\\":\\\"TEXT\\\"}],\\\"maxVotes\\\":1,\\\"minVotes\\\":0},{\\\"id\\\":\\\"A1-2\\\",\\\"columns\\\":[{\\\"value\\\":{\\\"default\\\":\\\"No\\\",\\\"value\\\":{}},\\\"contentType\\\":\\\"TEXT\\\"}],\\\"maxVotes\\\":1,\\\"minVotes\\\":0}],\\\"maxVotesOnList\\\":1,\\\"minVotesOnList\\\":1,\\\"maxVotesForList\\\":0,\\\"minVotesForList\\\":0,\\\"voteCandidateXorList\\\":false}],\\\"showInvalidOption\\\":true,\\\"showAbstainOption\\\":false,\\\"maxVotes\\\":1,\\\"minVotes\\\":0,\\\"prohibitMoreVotes\\\":false,\\\"prohibitLessVotes\\\":false,\\\"calculateAvailableVotes\\\":false}]}\",\"comSeed\":\"a240ec46ff7adefb01b5b8d6fade3c96cb50c40f737a3cffbd98a0e9e6415ea2\",\"publicCredential\":\"0205bf2e14496f68c0f86f6b313f210a9393edb083821dcc4f9914cab9c51c9f2e\",\"ballot\":{\"encryptedChoice\":{\"ciphertexts\":[{\"x\":\"03bf956c38e14a6f81ed3621e165fb8c6000c28738f0e279fa28d2254d6b799eb1\",\"y\":\"02e19fbd88d9e1ad760653dde8e7f00fcc0d45e2b38ccc0cb2301f2239d4fcac3f\"}]},\"proofOfKnowledgeOfEncryptionCoins\":[{\"c\":\"79966540728819921955585823592173536360716995948664894735154654897488787881072\",\"f\":\"90388416755735603296616014607154433872748203957820626540975447356971608146868\"}],\"proofOfKnowledgeOfPrivateCredential\":{\"c\":\"4219105992081372606513358125198075081967495840895255912931536426010398533192\",\"f\":\"110464010855198853861051741469261963282081696331616030540127604123885412224008\"}},\"signatureHex\":\"529f3e8c7d1f0e2c8061526d8e1d8000c24ab60b32b3bda0ce959788483f977fb12da70ccb7ac154a698ef925cf7ca52e142f8eb22d23e5ccd42b63da227230bf886b13211f5c1f618a946a64f8566fd36849b46a156d4a35288204fd7b22e15fcdce8884b5d6e5c69b07ca271332ba14eced079402c735db642b82ae7478fe2efe849d8c50ba11b7d6985486607a54ea42c6394dc2060ac58cfa9c69cc750816dad43fb74d113ab7bc014e619649688fdbf96a29c894fa2cfc5d2bac8b897d0c8dbb3b79e5c17a90913dcb4ba583ea90e706891d38278745c1b4856f88d045c38b840d4fd427291187c250b2ed7bc846fa25440e98d3e9832f2047e52bc5207\",\"factorX\":[\"03aacd547442d178a6fd95d949d84ecc17bbf16bb2428b7598f6abce29a1459a5a\"],\"factorY\":[\"0228136a113abad456a2cb690b4a38cea7ef3ba7839b74550aa5bc53a5af88a868\"],\"factorA\":[\"0340abe2067662ca5b3b2d122e4aaf7971db4209763ee8949d506e8c974e6c2ddd\"],\"factorB\":[\"026bcbe81a01c159c9e42045dbded1ca37ac0d664e3fe3e24bac342c4db28c8647\"]}"
},
"status": "OK"
},
"challenge": {
"challenge": "108039209026641834721998202775536164454916176078442584841940316235417705823230",
"challengeRandomCoin": "44267717001895006656767798790813376597351395807170189462353830054915294464906"
},

"finalMessage": "{\"z\":[\"3633826251616834446657553661530373736489206587264246793596555854504147120873052400272122845815239659486740186516083053240689380948861192914781931033170662\"]}",
"finalMessage": {
"value": "{\"z\":[\"3633826251616834446657553661530373736489206587264246793596555854504147120873052400272122845815239659486740186516083053240689380948861192914781931033170662\"]}",
"status": "OK"
},
"decoded": [0,0,0,1],
"receipt": [
"Project ID: bfced618-34aa-4b78-ba5b-d21dc04a1a7e",
Expand Down
4 changes: 3 additions & 1 deletion src/mock/extended.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/mock/testinstanz.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/decryption.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { bufToHex, hexToBuf } from "../src/main/utils"
import crypto from "crypto"
import { EnvironmentVariables } from "../src/main/constants"
import { ProofGeneratorMock } from "../src/algorithms/proof"
const loginResponse = SecondDeviceLoginResponse.fromJson(data.loginResponse)
const loginResponse = SecondDeviceLoginResponse.fromJson(data.loginResponse.value)
const randomCoinSeed = "1e89b5f95deae82f6f823b52709117405f057783eda018d72cbd83141d394fbd"

Object.defineProperty(globalThis, 'crypto', {
Expand Down Expand Up @@ -46,7 +46,7 @@ test("test decrytQRCode", async () => {

test("test checkZKP", async () => {
const init = loginResponse.initialMessageDecoded
const final = SecondDeviceFinalMessage.fromJson(data.finalMessage)
const final = SecondDeviceFinalMessage.fromJson(data.finalMessage.value)
const e = "108039209026641834721998202775536164454916176078442584841940316235417705823230"
const r = "44267717001895006656767798790813376597351395807170189462353830054915294464906"
const proof = (new ProofGeneratorMock(BigInt(e), BigInt(r))).generateProof()
Expand Down
4 changes: 2 additions & 2 deletions test/signature.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ Object.defineProperty(globalThis, 'crypto', {
});

test("test ballot fingerprint", async () => {
const response = SecondDeviceLoginResponse.fromJson(data.loginResponse)
const response = SecondDeviceLoginResponse.fromJson(data.loginResponse.value)
const expectedNormalizedBallot = "000000010000002103bf956c38e14a6f81ed3621e165fb8c6000c28738f0e279fa28d2254d6b799eb10000002102e19fbd88d9e1ad760653dde8e7f00fcc0d45e2b38ccc0cb2301f2239d4fcac3f000000010000002100b0cb75473491d930dfffdf51f65753db9e6d1252720f50532bd6a4ddb5073c700000002100c7d607e9d00ebb3849a3632d1e64bdc726ea3ba0ce564a0de2c578f1d5db83b4000000200953edeaf6598b16e39aab05f7a751a5d68c0190ef6c10b64b602b6a97c1a6480000002100f4386a1cefe2f2ef00aef6b4cc107ec5ec13984f65e1c941fdf49882986f0c08"
const expectedFingerprint = "91dd5f592932c7c681f20310c801e7ea935f116527b65ce6524f14c6ad2f9dac"
expect(bufToHex(getBallotAsNormalizedBytestring(response.initialMessageDecoded.ballot))).toBe(expectedNormalizedBallot)
expect(await computeFingerprint(response)).toBe(expectedFingerprint)
})

test("test checkSignature", async () => {
const response = SecondDeviceLoginResponse.fromJson(data.loginResponse)
const response = SecondDeviceLoginResponse.fromJson(data.loginResponse.value)
const valid = await checkSignature(response)
expect(valid).toBe(true)
})
93 changes: 30 additions & 63 deletions test/verificationtool.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import data from "../src/mock/data.json"
import axios from "axios"
//import * as proof from "../src/algorithms/proof"
import * as decrypt from "../src/algorithms/decryption"
import * as sign from "../src/algorithms/signature"
import { hexToBuf } from "../src/main/utils"
import axios from 'axios'
import {Comm, ResponseBean, ResponseBeanError, ResponseBeanOk} from "../src/main/communication"
import {Verificationtool} from "../src/main/verificationtool"
import { ElectionData, SecondDeviceFinalMessage, SecondDeviceLoginResponse } from "../src/classes/communication"
import { ErrorType } from "../src/main/error"
import { EnvironmentVariables } from "../src/main/constants"
import crypto from "crypto"
import { Proof, SecretProof } from "../src/classes/ballot"

EnvironmentVariables.init("test").fingerprint = "b7e8e76c369d6a9ca268e40cde8347ac443040d6c4a1df3035744ace05b94e00849abf083ae5baa8fee462a723823054858387ec35462a49f93c2ea40b2fc876"
EnvironmentVariables.instance.comm = new Comm()
const mockedAxios = jest.spyOn(axios, 'request')
const mockedDecrypt = jest.spyOn(decrypt, 'decrytQRCode')
const randomCoinSeed = "1e89b5f95deae82f6f823b52709117405f057783eda018d72cbd83141d394fbd"

Expand All @@ -24,51 +22,9 @@ Object.defineProperty(globalThis, 'crypto', {
}
});

async function validAxios(request: any) {
if(request.url == "/electionData") {
if (request.data != undefined) {
return Promise.reject("Invalid request value")
}
return Promise.resolve(
{
status: "OK",
data: data.electionData
})
} else if (request.url == "/login") {
if (JSON.stringify(request.data) != JSON.stringify(data.loginRequest)) {
return Promise.reject("Invalid request value")
}
return Promise.resolve(
{
status: "OK",
data: {
status: "OK",
value: data.loginResponse
}
})
} else if (request.url == "/challenge") {
if (JSON.stringify(request.data) != JSON.stringify(data.challenge)) {
return Promise.reject("Invalid request value")
}
return Promise.resolve(
{
status: "OK",
data: {
status: "OK",
value: data.finalMessage
}
})
}
else {
console.log(request)
return Promise.reject("Invalid url")
}
}


beforeEach(() => {
mockedAxios.mockImplementation(validAxios)
mockedDecrypt.mockResolvedValue(hexToBuf(randomCoinSeed))
//mockedAxios.mockImplementation(validAxios)
//mockedDecrypt.mockResolvedValue(hexToBuf(randomCoinSeed))
})

test("test verificationtool valid", async () => {
Expand All @@ -79,11 +35,11 @@ test("test verificationtool valid", async () => {

const login = await verificationtool.login(data.vid, data.nonce, data.password, data.c)
expect(login.status).toBe("OK")
expect((login as ResponseBeanOk<SecondDeviceLoginResponse>).value).toStrictEqual(SecondDeviceLoginResponse.fromJson(data.loginResponse))
expect((login as ResponseBeanOk<SecondDeviceLoginResponse>).value).toStrictEqual(SecondDeviceLoginResponse.fromJson(data.loginResponse.value))

const finalMessage = await verificationtool.finalMessage()
expect(finalMessage.status).toBe("OK")
expect((finalMessage as ResponseBeanOk<SecondDeviceFinalMessage>).value).toStrictEqual(SecondDeviceFinalMessage.fromJson(data.finalMessage))
expect((finalMessage as ResponseBeanOk<SecondDeviceFinalMessage>).value).toStrictEqual(SecondDeviceFinalMessage.fromJson(data.finalMessage.value))

const decodedBallot = await verificationtool.decodeBallot()
expect(decodedBallot.status).toBe("OK")
Expand All @@ -109,20 +65,21 @@ test("test invalid format", async () => {
status: "OK",
data: {a:1}
}
const verificationtool = new Verificationtool()
const comm = new Comm()
const mockedAxios = jest.spyOn(axios, 'request')
mockedAxios.mockResolvedValueOnce(invalidResponseData)
const electionData = await verificationtool.loadElectionData()
const electionData = await comm.electionData()
expect(electionData.status).toBe("ERROR")
expect((electionData as ResponseBeanError).errorType).toBe(ErrorType.FORMAT)

mockedAxios.mockResolvedValueOnce(invalidResponse)
const login = await verificationtool.login(data.vid, data.nonce, data.password, data.c)
const login = await comm.login(data.vid, data.nonce, data.password, data.c)
expect(login.status).toBe("ERROR")
expect((login as ResponseBeanError).errorType).toBe(ErrorType.FORMAT)

await verificationtool.login(data.vid, data.nonce, data.password, data.c)
await comm.login(data.vid, data.nonce, data.password, data.c)
mockedAxios.mockResolvedValueOnce(invalidResponse)
const final = await verificationtool.finalMessage()
const final = await comm.challenge('', new SecretProof(BigInt(1), BigInt(2), BigInt(3)))
expect(final.status).toBe("ERROR")
expect((final as ResponseBeanError).errorType).toBe(ErrorType.FORMAT)
})
Expand All @@ -135,16 +92,17 @@ test("test backend error", async () => {
value: {a:1}
}
}
const verificationtool = new Verificationtool()
const comm = new Comm()
const mockedAxios = jest.spyOn(axios, 'request')

mockedAxios.mockResolvedValueOnce(invalidResponse)
const login = await verificationtool.login(data.vid, data.nonce, data.password, data.c)
const login = await comm.login(data.vid, data.nonce, data.password, data.c)
expect(login.status).toBe("ERROR")
expect((login as ResponseBeanError).errorType).toBe(ErrorType.EXTERN)

await verificationtool.login(data.vid, data.nonce, data.password, data.c)
await comm.login(data.vid, data.nonce, data.password, data.c)
mockedAxios.mockResolvedValueOnce(invalidResponse)
const final = await verificationtool.finalMessage()
const final = await comm.challenge('', new SecretProof(BigInt(1), BigInt(2), BigInt(3)))
expect(final.status).toBe("ERROR")
expect((final as ResponseBeanError).errorType).toBe(ErrorType.EXTERN)
})
Expand Down Expand Up @@ -233,10 +191,19 @@ test("test error in fullLogin in finalMessage", async () => {
expect((final as ResponseBeanError).message).not.toBeDefined()
})

test("test connection error in fullLogin", async() => {
mockedAxios.mockRejectedValueOnce("No comment")
const verificationtool = new Verificationtool()
const loginFail = await verificationtool.fullLogin(data.vid, data.nonce, data.password, data.c)
test("test connection error in Comm", async() => {
const comm = new Comm()
const mockedAxios = jest.spyOn(axios, 'request')
mockedAxios.mockRejectedValue('no comment')
const electionDataFail = await comm.electionData()
expect(electionDataFail.status).toBe(ResponseBean.errorStatus)
expect((electionDataFail as ResponseBeanError).errorType).toBe(ErrorType.CONNECTION)

const loginFail = await comm.login(data.vid, data.nonce, data.password, data.c)
expect(loginFail.status).toBe(ResponseBean.errorStatus)
expect((loginFail as ResponseBeanError).errorType).toBe(ErrorType.CONNECTION)

const challengeFail = await comm.challenge('', new SecretProof(BigInt(1), BigInt(2), BigInt(3)))
expect(challengeFail.status).toBe(ResponseBean.errorStatus)
expect((challengeFail as ResponseBeanError).errorType).toBe(ErrorType.CONNECTION)
})

0 comments on commit 0240615

Please sign in to comment.