II. Lista de Tablas 8
3. Ensayos 19
3.4. Proceso de recanteo y taladrado manual de piezas 30
Proving a vote is either “yes” or “no” comes free by only accepting a single bit from the voter (vote ∈ {0,1}); however, there are other aspects of voting that may need verifying. With multiple candidates, there could be a need to guarantee only one vote has been cast, as discussed in Section 3.3.4 (this could also be seen as answers to a question in a survey). A sticky adder can be used to verify that at most one candidate receives a“yes” vote, by keeping the second bit high if it is ever set. Algorithm 6 is an example of a ballot consisting of xcandidates, where each voter only gets a single vote. If votera casts [0,1,0], the function will return [0,1,0], because the variable sticky does not get set. However, whenvoterb casts [1,0,1] the sticky bit is set, meaning the resulting array is [0,0,0]. This nullifies the three votes from voterb, because multiple candidates received a“yes”. The same nullification would happen with paper voting if multiple candidates are chosen. Another example for a ballot is where each voter gets three votes to use however they like. Now, each vote is multiple bits, instead of a single bit. In this case, the sticky bit would represent the third bit, so would only be set with four or more votes.
for example, three available options, where the voter must pick an order.1 In this example, there would be three separate ballots: first choice, second choice and third choice, giving nine tallies in total. For each choice, the votes are verified to only contain a single “yes” vote. Then, each option (option As first, second and third choice) has the votes verified, meaning for the 2- dimensional array (3x3), a high bit can only have one bit set per column and one per row. The only change to the function is the valid bit gets applied to the whole 2-dimensional array. This does not stop a voter only choosing a first choice, but even with paper based voting this can still occur. If this was a requirement, then the logic for the valid bit on Line 7 of Algorithm 6 could be valid← ¬sticky∧add, meaning that add must be set.
To record who has voted, the valid bit can also be stored. For each voter, their voter identification number would return 0, where the valid bit can be XORed with it to keep records of who has voted. This value can be combined with Line 7 of Algorithm 6, such that the vote is only valid if the voter has not submitted a valid vote previously. The voter identification number would be visible to each fragment server, but if a secure search could be implemented, the identification number could be fragmented and, therefore, kept private. The verification process was not tested in practice, because the performance of the addition is what is being compared to Chapter 3. However, all of the verification examples given can be easily implemented with FRIBs, where the XOR andNOT operations can be free (no reduction requests required). With the remaining operations being single bit functions, parallelisation for many voters could yield usable performance. A result LUT could even be generated to get the sticky bit of an array of votes (for each candidate), where the result is fragmented and therefore unknown.
Another requirement for a voting scheme would be that the voter can verify if their vote was cast correctly. This is out-of-scope of this use case, but worth discussing. A common technique is to use public bulletin boards [170][171], and that can be used here. When a voter sends their vote fragments to each fragment server, they can also send a random string (for each set of fragment). The voter also sends the random string to each public bulletin board and
1This type of approach would have allowed the New Zealand flag referendum to be imple-
Algorithm 6 Verifying a single vote is cast
1: function verifyVote(votes)
2: sticky←0
3: add←0
4: for v invotesdo
5: sticky ←sticky|(add∧v)
6: add←add⊕v
7: valid← ¬sticky
8: for iinvotes.lengthdo
9: votes[i]←votes[i]∧valid
10: return votes
receives back an index value. After the ballot (or periodically) the fragment servers can send their public bulletin board (one for each) the fragments with random strings. Note that the public bulletin boards should be hosted by different entities to the fragment servers as well. The public bulletin boards secretly decided together the index for each random string, which was sent back to the voter; therefore, after the ballot the voter can then verify their vote was cast correctly, by checking the index in the list of votes on the public bulletin boards. This also allows all the votes to be tallied to confirm the result. The votes and voter identification numbers are therefore kept separate. This is purely an idea and has not been explored for this thesis.
7.2.3 Enhanced Privacy Model
The implementation structures for the enhanced privacy models both with and without redundancy are nearly identical, with only a few differences for dealing with the different number of fragment servers. Both will be described in this section, but only the code for without redundancy will be given as examples, where both implementations can be found in Appendix D. Imple- menting FRIBs can be divided into three threads/processes: (1) performing the addition and reduction, (2) handling requests for another fragment server’s result LUT, and (3) handling requests for correcting the obfuscation through indexes.
Performing the Addition and Reduction
The first stage of this thread is getting the next vote from the queue and setting it to be the first carry bit, where the remaining carry bits were set during the last reduction. In Listing 7.7, the vote_window_len will be six, as four bits plus the carry bit are reduced at once for the 24-bit tally. The carry bits are then appended to the list of states to be reduced.
Listing 7.7:Stage 1 for adding a new vote by getting it from the queue
c a r r y _ w i n d o w [0] = s e l f . v o t e a d d Q u e u e . get () s t a t e s = [] for i in r a n g e ( v o t e _ w i n d o w _ l e n ) : c a r r y = c a r r y _ w i n d o w [ i ] if c a r r y == N o n e : c o n t i n u e s t a t e s . a p p e n d ( t a l l y _ w i n d o w [ i ] + ( c a r r y < < 4) )
The second stage involves obfuscating the fragment server’s state for itself to use in the vector, and for handling requests from the other two fragment servers. Listing 7.8 gives an example of this process, where qosb and qosc are queues between the other two threads, as each thread needs to know the current state. Once the fragment server has obfuscated its own state (in osa) for use in the vector request, it generates three other random states to add to the vector, before shuffling it. Now that the vector is shuffled, the index of the correct obfuscated state is found and sent to fragment server C (via
sendc). Note that each fragment server considers itself as fragment server A, so fragment server C is always the one to send the index too and to receive the new index and flip bit back from. The vector is sent to fragment server B (sendb) at the same time. This implementation is reducing the states in parallel and tries to fill the packets to improve performance. The only variable not covered so far is vis, which will be used later to recover the current index into the vector for that state.
Listing 7.8: Stage 2 for adding a new vote is to obfuscate the state
vis = [] s e n d b = s e n d c = " " for s t a t e in s t a t e s : osa = s e l f . a . o b f u s c a t e ( s t a t e ) s e l f . q o s b . put ( s t r u c t . u n p a c k (" < B ", s e l f . b . o b f u s c a t e ( s t a t e ) ) [ 0 ] ) s e l f . q o s c . put ( s t r u c t . u n p a c k (" < B ", s e l f . c . o b f u s c a t e ( s t a t e ) ) [ 0 ] )
vec = [ osa ] i = 0 w h i l e i < 3: r = s t r u c t . p a c k (" < B ", r a n d o m . r a n d i n t (0 , 31) ) if r not in vec : vec . a p p e n d ( r ) i += 1 r a n d o m . s h u f f l e ( vec ) vi = vec . i n d e x ( osa ) vis . a p p e n d ( vi )
s e n d b += vec [ 0 ] + vec [ 1 ] + vec [ 2 ] + vec [3] s e n d c += s t r u c t . p a c k (" < B ", vi )
s e l f . b . s s l _ s o c k . s e n d a l l ( s e n d b ) s e l f . c . s s l _ s o c k . s e n d a l l ( s e n d c )
The thread waits for the small obfuscated LUT from fragment server B, and the index and flip bit from fragment server C. Listing 7.9 shows the states being retrieved by selecting the correct row (usingvis from earlier) and using the index and flip bit to get the result state.
Listing 7.9: Stage 3 for adding a new vote is to process the obfuscated LUT with the received index and flip bit
r s t a t e s = []
for si in r a n g e ( len ( s t a t e s ) ) : s m a l l _ l u t = []
r o w s = s e l f . b . s s l _ s o c k . r e c v ( 1 2 8 )
row = r o w s [32 * (3 - vis [ si ]) : 32 * (3 - vis [ si ]) + 32]
for by in row : s m a l l _ l u t . a p p e n d ( by ) i n d e x = s t r u c t . u n p a c k (" < B ", s e l f . c . s s l _ s o c k . r e c v (1) ) [0] f l i p = s t r u c t . u n p a c k (" < B ", s e l f . c . s s l _ s o c k . r e c v (1) ) [0] r s t a t e s . a p p e n d ( s t r u c t . u n p a c k (" < B ", s m a l l _ l u t [ i n d e x ]) [0] ^ f l i p )
The final step is to modify thecarry_window for the next vote and update the tally (as shown in Listing 7.10). The thread then goes back to the first stage in order to add the next vote.
Listing 7.10: The final stage for adding a new vote is to set up the windows for the next vote r e s u l t s = [] for i in r a n g e ( v o t e _ w i n d o w _ l e n ) : if c a r r y _ w i n d o w [ i ] == N o n e : c o n t i n u e c a r r y _ w i n d o w [ i ] = ( r s t a t e s [ rsi ] & 16) > > 4 t a l l y _ w i n d o w [ i ] = ( r s t a t e s [ rsi ] & 15) for i in r a n g e ( v o t e _ w i n d o w _ l e n -1 , 0 , -1) : c a r r y _ w i n d o w [ i ] = c a r r y _ w i n d o w [ i -1]
Handling Requests for Another Fragment Server’s Result LUT
This thread is responsible for generating the small obfuscated result LUT for another fragment server. Note that for all the implementations and expla- nations in this thesis, the result LUT is for the lower fragment server; for example, fragment server B generates the LUT for fragment server A. With the three-server enhanced privacy model, this thread receives the randomised vector of states from the lower fragment server, and gets the possible results for each index in the vector, whereas the redundancy model will receive multi- ple vectors; in this implementation, it is five vectors (from all other fragment servers), in order to keep the data transferred to a minimum.
The simplicity of this thread can be seen in Listing 7.11. The thread re- ceives four possible states from the lower fragment server. It then gets its own obfuscated state (osc) in order to get the result vector for each of the four states received. The result vector is shuffle, before a random value is XORed with it to hide the values. Once all four result vectors are obfuscated, they are sent back to the lower fragment server.
Listing 7.11: Loop which generates the obfuscated result LUT
w h i l e T r u e : b4 = c o n n . r e c v (4) if len ( b4 ) == 0: b r e a k osc = s e l f . q o s c . get () * 32 rbs = ’ ’ for i in r a n g e (4) : i = ( s t r u c t . u n p a c k (" < B ", b4 [ i ]) [0] * 32 * 32) + osc r o w _ b y t e s = s e l f . r e s u l t _ l u t [ i : i + 3 2 ] s e l f . c . r a n d o m . s h u f f l e ( r o w _ b y t e s ) for rb in r o w _ b y t e s : t m p _ r = s e l f . c . r a n d o m . r a n d i n t (0 ,31) rbs += s t r u c t . p a c k (" < B ", s t r u c t . u n p a c k ( " < B ", rb ) [0] ^ t m p _ r ) c o n n . s e n d a l l ( rbs )
Handling Requests for Correcting the Obfuscation through Indexes
The final thread provides the upper fragment server (for example, at fragment server B, the upper fragment server is C) with the index for the small ob- fuscated result LUT that is generated by the previous thread. The thread receives the index for the vector sent by the upper server, where the index is
for the position of the correct state (as only one state in the vector is correct). Using this index and its own obfuscated state, it can generate the index for the new state of the upper fragment server. Again, the redundancy model would receive more indexes, as five vectors are sent to generate the obfuscated LUT. To further explain, Listing 7.12 contains the loop which is responsible for generating the index value. It waits to receive the index value, then gets its own obfuscated state value. The thread must handle the random shuffle and numbers generated by the previous thread to keep the pseudo-random number generators aligned. In this implementation, the approach to solve this is very basic; it first shuffles an empty array of the same size, then for each state in the result vector it generates a random number. Recall that this thread shares the same seed for the pseudo-random number generator as the thread generating the small obfuscated result LUT. Once the correct index is reached, the index and the random value is saved to send back to the upper fragment server. Note that it cannot be sent once it is found to prevent timing attacks [172].2 The random value is the flip bit mentioned previously, to reveal the new state.
Listing 7.12: Loop which generates the obfuscated index
w h i l e T r u e : b1 = c o n n . r e c v (1) if len ( b1 ) == 0: b r e a k ri = s t r u c t . u n p a c k (" < B ", b1 ) [0] osb = s e l f . q o s b . get () o s b f = 0 for i in r a n g e (4) : r o w _ b y t e s = [] for j in r a n g e ( 3 2 ) : r o w _ b y t e s . a p p e n d ( j ) s e l f . b . r a n d o m . s h u f f l e ( r o w _ b y t e s ) o s b _ n e w = r o w _ b y t e s . i n d e x ( osb ) for j in r a n g e ( 3 2 ) : t m p _ r = s e l f . b . r a n d o m . r a n d i n t (0 ,31) if i == ri and j == o s b _ n e w : o s b f = t m p _ r o s b i = o s b _ n e w c o n n . s e n d a l l ( s t r u c t . p a c k (" < B ", o s b i ) + s t r u c t . p a c k (" < B " , o s b f ) )
2Timing attacks are where a function takes a varying amount of time for different in-