Custom Policy Assertions in WSE 3 - Part 5
In part 4 I described the implementation of the blind signature assertion in this, in this part I'll go through the system from starting the services up and trace through one vote.
Service Setup
If the ballot issuer and polling place need to handle multiple elections simultaneously, they need a mapping between elections and ballot schemas. In this system, there's only one election so the schema is hardcoded, but this could be accomplished through config or code. The blind signature issuer has a few more requirments. It needs to know the certificate it will be signing requests with. Since this is central to its functionality, policy probably isn't the best place for this. Config or code can accomplish this. Also the issuer needs an instance that implements this interface:
public interface IBlindSignatureIssuerHelper
{
bool IssueSignature(CredentialSet credentials);
bool VerifyMessage(byte[] message);
}
This allows the blind signature issuer to be application agnostic like the blind signature assertion. IssueSignature authorizes the client and allows the application to determine who votes and how many times. VerifyMessage gives the application an unblinded byte[]. In this system, the byte[] is converted to a ballot and is verified against the schema. If this fails no signature is issued. An instance of this interface should be provided through config or code.
One Vote
Now that the services are correctly configured and running, a voter starts the client and votes. First the client sends a request to the ballot issuer for a ballot schema, this could contain an election id but in this case it's just an empty envelope. The ballot issuer returns a schema, maybe something like this:
<soap:Envelope xmlns:wsa="https://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="https://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="https://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<!-- a bunch of addressing and security headers including a signature -->
</soap:Header>
<soap:Body wsu:Id="Id-55e2975e-db0a-4f3a-a15b-264a5bf99601">
<xs:schema xmlns="urn:ballot.xsd" xmlns:mstns="urn:ballot.xsd" xmlns:xs="https://www.w3.org/2001/XMLSchema" targetNamespace="urn:ballot.xsd" elementFormDefault="qualified">
<xs:element name="ballot" type="ballotType" />
<xs:complexType name="ballotType">
<xs:all>
<xs:element name="Id" type="xs:string" />
<xs:element ref="Mayor">
<xs:annotation>
<xs:documentation>Candidates for the Mayor of Springfield</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
</xs:complexType>
<xs:element name="Mayor" type="SpringfieldMayoralCandidatesType" />
<xs:simpleType name="SpringfieldMayoralCandidatesType">
<xs:restriction base="xs:string">
<xs:enumeration value="Sideshow Bob" />
<xs:enumeration value="Diamond Joe" />
</xs:restriction>
</xs:simpleType>
</xs:schema>
</soap:Body>
</soap:Envelope>
Now with this schema the client can determine this is a mayoral election with 2 candidates, Sideshow Bob Terwilliger and Diamond Joe Quimby and put up some UI and allow the voter make a choice and if necessary select their identifying credentials. To prepare to vote the client adds a ballot to body of the envelope then slips a couple items into the OperationState for the BlindSignature assertion:
voteEnvelope.Context.OperationState.Set<CredentialSet>(credentials);
voteEnvelope.Context.OperationState.Set<IMessageConverterClient>(new BallotConverter(ballot, description));
and then it votes:
Int64 i = pollingPlaceProxy.Vote(voteEnvelope);
At this point we enter the pipeline and the first filter is the BlindSignatureClientOutputFilter. After making sure it has what it needs, it asks the client for sets of all possible ballots. In this case the client returns 16 sets and there's 2 possible ballots, so there's 32 ballots total, each converted into a byte[]. Each ballot set contains something like this:
<b:ballot xmlns:b="urn:ballot.xsd">
<b:Id>10694</b:Id>
<b:Mayor>Diamond Joe</b:Mayor>
</b:ballot>
<b:ballot xmlns:b="urn:ballot.xsd">
<b:Id>10694</b:Id>
<b:Mayor>Sideshow Bob</b:Mayor>
</b:ballot>
All ballots within a set contain the same id, the id for each set is randomly selected.
Now the filter creates a random blinding factor for each set and blinds all the byte[] within it. These are packaged up in an RSig and sent to the blind signature issuer. The issuer authorizes the client credentials with IBlindSignatureIssuerHelper.IssueSignature and then creates a unique id for this signature transaction. Now it randomly selects an index in the array of blinded message sets, and packages that and the id in an RBF and returns to the client. The filter gets all the blinding factors, except the one referenced in the RBF and sends them along with the id in an RBFR back to the service. Upon receipt, the service makes sure it has a record for the id. If it does, it compares the indices of the blinding factors to make sure it received the correct ones. Now it unblinds all the ballots it has blinding factors for and calls IBlindSignatureIssuerHelper.VerifyMessage to make sure all the ballots are valid. Next it signs the remaining ballots and returns the RSigR.
Once the client filter receives the RSigR, it can unblind the signatures and it has valid signatures for both Diamond Joe and Sideshow Bob. It calls IMessageConverterClient.SetMessage so the ballot in the envelope has the correct <Id> element and correct signature is selected and placed in the headers. Execution continues through the pipeline, in this case through the anonymousForCertificateSecurity assertion. It signs the body and headers including the blindSignature header as well as encrypting the body.
On the service side the pipeline begins with the anonymousForCertificateSecurity filter. It verifies all the signatures and decrypts the body. The envelope looks like when it left the BlindSignatureClientOutputFilter as it enters the BlindSignatureServiceInputFilter:
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<bsig:blindSignature wsu:Id="Id-72e4d869-69fb-44be-899d-5808cd2e64cc" xmlns:bsig="urn:BlindSignature">
<!-- base64string with signature -->
</bsig:blindSignature>
</soap:Header>
<soap:Body wsu:Id="Id-59019b8b-20c5-41fd-af48-7884bf8ea4fb">
<b:ballot xmlns:b="urn:ballot.xsd">
<b:Id>10694</b:Id>
<b:Mayor>Diamond Joe</b:Mayor>
</b:ballot>
</soap:Body>
</soap:Envelope>
The service filter first finds the blindSignature header and yanks out the signature value. Then it gets the private key of it's certificate and creates a signature for the body of the message and compares it with the blindSignature. If it matches it's off to the polling place service.
The polling place makes sure no ballot with this id has been cast (a valid signature for Sideshow Bob has been issued with this same id). If not, it's recorded as a vote for Diamond Joe. After the election concludes the election authority can publish each ballot id and how it voted. Since only voters know the ballot id of the ballot they cast, they can check it was tallied correctly anonymously. Also outside auditors can make sure the numbers add up.
Going back to part 1, how does this system meet the requirements?
- only authorized voters can vote - The signature issuer authorizes every request received and denies it if the presented credentials are unauthorized. Without a signature, a vote is invalid.
- authorized voters can only vote once (this can be some arbitrary limit instead of one if desired) - Same solution as #1. The authorization routine can be implemented to track the number of signatures issued to any authorized voter (e.g. maybe PlatinumPlus citizens get 2 votes) to verify they don't voter early and often.
- only voters know how they voted (ballots are confidential) - There's two other parties that could know how the voter voted, the signature issuer and the polling place. The signature issuer knows who the voter is but since it issues a signature for every possible ballot it cannot know which one the voter actually uses. The polling place obviously knows how a ballot votes, but because of the blind signature process there's no information about the voter on the ballot only that the signature issuer authorized this ballot to be cast. Even if the polling place and the signature issuer collude with the signature issuer tracking who receives what signatures and the polling place tracks what signature maps to which ballot, the signatures won't match because the signature from the signature issuer still has a blinding factor that only the voter knows.
- voters can verify their ballot was tabulated correctly - Neither the polling place nor the signature issuer can map a ballot id to a voter. The polling place knows how each ballot id voted, but has no information on who the voter was. The signature issuer knows who it issued signatures to, but since it is signing a blinded ballot it doesn't know the ballot id. Only the voter knows his ballot id and can check as describe elsewhere.
The other parts: