# ov-api / online voting client api ## About This api provides a high level interface to the client-side crypto and the server endpoints of the online voting protocol, with two main interfaces: - **OvApi** is a simple and straightforward api for the full protocol. It basically exposes three simple methods (authentication, send vote and cast vote) and transparently takes care of all the details and intermediate requests. Implementing a full voting client with this api is pretty trivial, so this is the preferred method. - **OvWorker** is a helper interface for precomputing values in background during the session, while the user is interacting with the voting portal. This may significantly speed up the final vote encryption process. This document is a general guide for setting up and using these interfaces. ## Setup The library is distributed as a single bundle file containing all necessary dependencies, including support for web workers for asynchronous processing. ```html ``` Note: './crypto/' is the default location for the script. You may use a different location but the API needs to be aware of this (this same script will be dynamically loaded into web workers) so you will have to pass the new location as a configuration option. ## Basic usage ### Bootstrap As soon as the host page is loaded, the api will automatically start collecting entropy. However, you need a way to know when it is safe to start invoking methods in the api. For this, you call the _ovApi.init()_ method like so: ```JavaScript window.ovApi = new OvApi({ lib: 'my/custom/path/ov-api.js', }); ovApi.init().then(function() { console.log('Online voting ready!'); }); ``` Note that you _can_ invoke the api without doing this, but this is not recommended. If you start making requests before enough entropy has been collected, the api will simply work with lower quality randomness, which you probably don't want. ### Asynchronous process Most API methods are asynchronous and return *promises*. When called they will return immediately a promise object. You can chain 'then', 'fail' and 'progress' event handlers to this promise object which will be invoked as appropiate once the promise is resolved. All these handlers are optional. ```JavaScript ovApi.sendVote(voteoptions) .then(function(result) { // the method has completed ok and 'result' will // contain the return value }) .fail(function(error) { // the method has failed and 'error' will // contain information about the error }) .progress(function(percent) { // the method is still processing and 'percent' will // contain the estimated percentage completed (0-100 integer) // this is informative, and might get called multiple times // (or not at all) }); ``` Note that these promises are implemented with Kris Kowal's "q" library which supports several alternative ways to handle them. More info: https://github.com/kriskowal/q/wiki/API-Reference ### Concurrency Note that the API being asynchronous doesn't mean it is automatically concurrent. You should have only one task running independently from the main thread per instance of the API (i.e. per web worker). It is safe, however, to create several instances of the API and have them running concurrently if you should ever need to do so. # Using OvApi Implementing a vote cycle with this API is quite simple. ## Initializing First create and initialize an instance and wait for the _init_ method to resolve: ```JavaScript window.ovApi = new OvApi({ tenantId: 'someTenantId', electionEventId: $stateParams.eeid }); ovApi.init().then(function() { // ovApi is safe to be used now! }); ``` Note that you can pass configuration options to the constructor. ## Authenticating and getting a ballot Next, once you have asked the voter for his/her _startVotingKey_, just call the _requestBallot_ method like so: ```JavaScript ovApi.requestBallot(startVotingKey) .then(function(ballotResponse) { // if you do want a parsed ballot, you can now call: var ballot = ovApi.parseBallot(ballotResponse); }) ``` This is all it takes to get an authenticated session for the voter and obtain the ballot. ### The ballot The _ballot_ is basically an object tree with the full collection of questions, votations and elections available to the voter, suitable for navigation and rendering it to HTML. The structure will vary depending on the type of elections, but every selectable option will contain a '_prime_' property which is a number representing the actual vote option. This number is the only relevant information for the voting protocol, so you can do anything you want to the ballot object (add properties to track selections, etc ...). Note that the ballot will be in the language configured as default. If you want to present it in a different language, just call _translateBallot_. ```JavaScript ovApi.translateBallot(ballot, 'fr_FR'); ``` The _ballot_ object has two additional properties, _status_ and _correctnessIds_, that you will need to be aware of: ## ballot.status / Checking vote status This property indicates if the voter has already voted on this ballot and wether the vote was fully completed or not. ```JavaScript switch (ballot.status) { case 'SENT_BUT_NOT_CAST': // The voter already sent a vote for this ballot, but left the // portal before actually 'casting' it. You should ask him to do // that now. See OvApi.requestChoiceCodes case 'CAST': // The voter did cast a vote for this ballot. You can now remind // him that he already did so and show him his 'vote cast // code'. See OvApi.requestVoteCastCode case 'NOT_SENT': // The voter never voted on this ballot. You should present it to // him so he can make his choices. ``` ## ballot.correctnessIds / Vote correctness map This is a map of vote correctness identifiers for each representation (_prime_) in the ballot. You will need to supply it as a parameter for the _sendVote_ (see below). ## Sending the vote Once the voter has made all his/her choices, assembling and sending a vote is just a matter of calling _sendVote_, passing the vote options as an array of strings representing (big) integers. ```JavaScript ovApi.sendVote(primesArray, encrypterValues, null, // precomputed partial choice codes, can be null null, // precomputed proof values, can be null ballot.correctnessIds, null, // writein (only present if writeins are enabled, can be null or omitted if later arguments also omitted) null, // verificationCode (only present if an additional verification step is enabled, can be null or omitted if later arguments also omitted) null) // disableOptimizations (intended for testing purposes, can be null or omitted) .then(function(choiceCodes) { // Once this point is reached, you should have the choice codes to show to the voter. The voter should be encouraged to make sure that they match. ``` The write-in options, if present, are informed as an array of strings. ## Casting the vote If everything is ok, the voter will supply the _ballot casting key_ to cast the vote. You just call: ```JavaScript ovApi.castVote(ballotCastingKey) .then(function(voteCastCode) { // Once this point is reached, you have the vote cast code to show to the voter. Again, // they should make sure it matches. ``` That's all, we're done! The voting process is now complete. The process of recovering from an interrupted session ( _requestChoiceCodes_ method) and returning voters ( _requestVoteCastCodes_ method) is not covered in this guide, but it follows the same pattern as above. # Using OvWorker for precomputations ## Initializing First create and initialize an instance and wait for the _init_ method to resolve, just like you do with _OvApi_ : ```JavaScript window.ovApi = new OvApi(config); window.ovWorker = new OvWorker(config); ovApi.init().then(function() { ovWorker.init().then(function() { console.log('Online voting ready!'); }); }); ``` ## Precomputations The vote encryption step is quite compute intensive and can take a while, specially on low end devices. To minimize this several computations can be performed in advance, in a separate thread (i.e., a web worker) while the user is interacting with the voter portal. We call these 'precomputations'. To use precomputations you first invoke the appropiate method, store the result when available and finally supply them to your vote encryption method of choice. There are 3 types of precomputations you can use: - for encryption. - for partial choice code generation - for zero knowledge proofs ### Precomputations for encryption You need the election's _encryption parameters_ for these. The encryption parameters are provided along with the ballot, so this means you must have already called (and resolved) _OvApi.requestBallot_ . Once you did, you can recover the encryption parameters with _ovApi.getSerializedEncryptionParams()_ and feed them to _OvWorker.precomputeEncrypterValues()_ , like so: ```JavaScript ovApi.getSerializedEncryptionParams() .then(function(serializedEncParams) { // start precomputing encrypter values ovWorker.precomputeEncrypterValues(serializedEncParams) .then(function(encrypterValues) { // got our encrypter values, store for later mySessionService.setEncrypterValues(encrypterValues); ``` To actually use these _encrypterValues_ you need to supply them to the encryption method _OvApi.sendVote()_ , like so: ```JavaScript ovApi.sendVote( primes, mySessionService.getEncrypterValues(), ... ``` Note that both methods accept this parameter to be _null_ or omitted, in which case they simply will just not use any precomputed values. ### Precomputations for zero knowledge proofs Once you have the encryption parameters and the precomputed encrypter values you can also perform in advance several computations for the zero knowledge proofs. ```JavaScript // start precomputing proofs ovWorker.precomputeProofValues( at.voterInformation.electionEventId, at.voterInformation.votingCardId, serializedEncParams, encrypterValues) .then(function(proofValues) { // got our proof values, store for later mySessionService.setProofValues(proofValues); ... ``` To use these precomputed proof values just supply them to the encryption method ( _OvApi.sendVote()_ ), like so: ```JavaScript ovApi.sendVote( primes, mySessionService.getEncrypterValues(), ... mySessionService.getProofValues(proofValues) ... ``` ### Precomputations for partial choice code generation For these optimizations you need also a secret key contained in the voter's keystore, which is provided by _OvApi.requestBallot_ ```JavaScript // get verification secret ovApi.getSerializedVerificationCardSecret() .then(function(serializedVerificationCardSecret) { // got our verification secret, store for later mySessionService.setVerificationKey(serializedVerificationCardSecret); ``` The _partial choice codes_ are precomputed individually. This is because there can be a potentially large number of them and computing them all might be considerable overkill or even choke the browser. So the strategy is up to you and depends largely on the size of your election (in terms of possible different vote options). A sensible approach is to precompute just the options the user actually selects since they are likely to be present in the final vote. So, whenever the user makes a choice you can do something like this: ```JavaScript // user just selected 'option' ... ovWorker.precomputePartialChoiceCode( mySessionService.getEncParams(), option.prime, mySessionService.getVerificationKey()); ``` Note that this method is 'fire and forget'. It returns nothing, but will silently compute the partial choice code in background and keep it in a pool. You don't really have to care. When the time comes to actually encrypt the vote you can call _ovWorker.getPrecomputedPartialChoiceCodes()_ to obtain the pool of precomputed partial choice codes up to that point: ```JavaScript ovWorker.getPrecomputedPartialChoiceCodes() .then(function(precomputedPCC) { ovApi.sendVote(primes, sessionService.getEncrypterValues(), precomputedPCC) ``` You then pass these values to _OvApi.sendVote()_ . It will use any partial choice codes present in the pool, and will compute the rest, if any, on the fly. Again, this parameter can be _null_.