Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the ballot encoding #144

Merged
merged 2 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions docs/ballot_encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ A possible encoding of an answer would be (by string concatenation):
"text:cd13:base64("Noémien"),base64("Pierluca")\n"
```

## Size of the ballot
## Size of the ballot

In order to maintain complete voter anonymity and untraceability of ballots throughout the
election process, it is important that all encrypted ballots have the same size. To this aim,
the election has an attribute called "BallotSize" (multiple of 29) which is the size
the election has an attribute called "BallotSize" which is the size
that all ballots should have before they're encrypted. Smaller ballots should therefore be
padded in order to reach this size. To denote the end of the ballot and the start of the padding,
we use an empty line (\n\n). For a ballot size of 116, our ballot from the previous example
we use an empty line (\n\n). For a ballot size of 117, our ballot from the previous example
would then become:

```
Expand All @@ -63,5 +63,11 @@ would then become:

"text:cd13:base64("Noémien"),base64("Pierluca")\n\n" +

"ndtTx5uxmvnllH1T7NgLOREguUWbN"
"ndtTx5uxmvnllH1T7NgLORuUWbN"
```

## Chunks of ballot

The encoded ballot must then be divided into chunks of 29 or less bytes since the maximum size supported by the kyber library for the encryption is of 29 bytes.

For the previous example we would then have 5 chunks, the first 4 would contain 29 bytes, while the last chunk would contain a single byte.
4 changes: 2 additions & 2 deletions web/frontend/src/pages/ballot/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Ballot: FC = () => {

const { electionId } = useParams();
const UserID = sessionStorage.getItem('id');
const { loading, configObj, electionID, status, pubKey, chunksPerBallot } =
const { loading, configObj, electionID, status, pubKey, ballotSize, chunksPerBallot } =
useElection(electionId);
const { configuration, answers, setAnswers } = useConfiguration(configObj);

Expand Down Expand Up @@ -75,7 +75,7 @@ const Ballot: FC = () => {

const sendBallot = async () => {
try {
const ballotChunks = voteEncode(answers, chunksPerBallot);
const ballotChunks = voteEncode(answers, ballotSize, chunksPerBallot);
const EGPairs = Array<Buffer[]>();
ballotChunks.forEach((chunk) =>
EGPairs.push(encryptVote(chunk, Buffer.from(hexToBytes(pubKey).buffer), edCurve))
Expand Down
21 changes: 13 additions & 8 deletions web/frontend/src/pages/ballot/components/VoteEncode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { Buffer } from 'buffer';
import ShortUniqueId from 'short-unique-id';
import { Answers, RANK, SELECT, TEXT } from 'types/configuration';

export function voteEncode(answers: Answers, chunksPerBallot: number): string[] {
export function voteEncode(
answers: Answers,
ballotSize: number,
chunksPerBallot: number
): string[] {
// contains the special string representation of the result
let encodedBallot = '';

Expand Down Expand Up @@ -34,22 +38,23 @@ export function voteEncode(answers: Answers, chunksPerBallot: number): string[]

encodedBallot += '\n';

const chunkSize = 29;
// ballot size
const ballotSize = chunksPerBallot * chunkSize;
const encodedBallotSize = Buffer.byteLength(encodedBallot);

// add padding if necessary
// add padding if necessary until encodedBallot.length == ballotSize
if (encodedBallotSize < ballotSize) {
const padding = new ShortUniqueId({ length: ballotSize - encodedBallotSize });
encodedBallot += padding();
}

const chunkSize = 29;
const ballotChunks: string[] = [];

// divide into chunks of 29 bytes, where 1 character === 1 byte
for (let i = 0; i < ballotSize; i += chunkSize) {
ballotChunks.push(encodedBallot.substring(i, i + chunkSize));
// divide into chunksPerBallot chunks, where 1 character === 1 byte
for (let i = 0; i < chunksPerBallot; i += 1) {
const start = i * chunkSize;
// substring(start, start + chunkSize), if (start + chunkSize) > string.length
// then (start + chunkSize) is treated as if it was equal to string.length
ballotChunks.push(encodedBallot.substring(start, start + chunkSize));
}

return ballotChunks;
Expand Down