Custom Policy Assertions in WSE 3 - Part 2
In part 1, I described the requirements of an election system using WSE 3 and a high level overview of a design that meets those requirements. In this part, I'll describe the major components of the system. These are:
- the blind signature issuer
- the blind signature client
- the ballot issuer
- the polling place
- the voter client
Blind Signature Issuer
The interface for this service is determined by the protocol and is complicated by the fact the service needs to call back to the client. The following 4 messages need to be exchanged:
- Request Signature (RSig) - initial request that contains N blinded copies of the document
- Request Blinding Factors (RBF) - request from the service N-1 blinding factors
- Request Blinding Factors Response (RBFR) - response from the client with the requested blinding factors
- Request Signature Response (RSigR) - response from the service with the blinded signature
I weighed two design options here:
- The client drives the entire signing transaction. I chose this option since it seemed to be easiest to implement. In this case the RBF is actually a response from the service to the RSig and the RBFR is actually a request from the client and the service returns the RSigR. The RBF also includes an identifier that the client includes in the RBFR so the service knows which RSig it refers to. WS-Addressing might allow something similar that is not application specific.
- The client creates a new endpoint that the service can call. This can be accomplished by adding a SoapService to the SoapReceivers collection on the client side in WSE or in Indigo with a duplex channel. This allows for an intuitive message exchange pattern where requests are requests and responses are responses, but the added complexity of a new client/server pair and securing that message exchange made this implementation more difficult.
The Blind Signature Issuer exposes two methods:
[
SoapMethod("urn:BlindSignatureIssuer/RSig")]
public Rbf RequestSignature(SoapEnvelope env)
and
[
SoapMethod("urn:BlindSignatureIssuer/RBFR")]
public RSigR RequestBlindingFactorResponse(SoapEnvelope env)
RequestSignature first authorizes the user and then randomly selects a document it will sign. It then creates a unique identifier for the request and creates a record consisting of the identifier, the request, and the index of the document it will sign. It stores this record for use by RequestBlindingFactorResponse. Now RequestSignature creates an RBF consisting of the identifier and the index it doesn't need a blinding factor for.
RequestBlindingFactorResponse first looks up the record and makes sure the client included the requested blinding factors. Then it unblinds all the documents it has blinding factors for and verifies they are valid documents. If all this checks out, the remaining document is signed and the signature is returned in the RSigR.
All the messages in this exchange must be signed and should be encrypted as well, except the RBFR which must be encrypted. Since only the client knows the blinding factors, all the messages but the RBFR are already effectively encrypted. It's easiest to apply one policy for the entire exchange, so it's easiest to encrypt all the messages.
Blind Signature Client
The blind signature client is primarily responsible for generating the necessary documents and blinding factors and then blinding them. The client then then sends the service an RSig and receives the RBF. Using the RBF it looks up the necessary blinding factors and adds them along with the id to an RBFR. After sending the RBFR, it receives the RSigR and is able to extract the blinded signature, and unblind it. Now the client has a valid signature and can carry on its merry way.
Ballot Issuer
The ballot issuer is a simple service that issues a schema describing the ballot:
[
SoapMethod("urn:VotingServices/BallotIssuer/IssueBallot")]
public SoapEnvelope IssueBallot()
This can be modified to take a parameter to allow it to issue ballots for multiple elections. Assuming the ballot is publicly available, this service should allow anonymous access with signing only, and encryption is unnecessary. In this case I'll use anonymousForCertificateSecurity with encryptBody="false".
Polling Place
This is another fairly simple service:
[
SoapMethod("urn:VotingServices/PollingPlace/Vote")]
public Int64 Vote(SoapEnvelope env)
It takes a ballot, records it and returns the ballot id from the client as a receipt. Both request and response require signing and encryption, in order to meet requirement #3 from part 1, I'll use anonymousForCertificateSecurity in conjunction with our new blind signature assertion.
Voter Client
Client implementation here is straightforward as well. The client has a ballot issued, allows the user to make some selections and then sends the ballot to the polling place. You'll noticed there's none of that blind signature mumbo jumbo here. That's because the blind signature mechanism has been segregated into a policy assertion. When the client calls proxy.Vote(ballot), the message enters a pipeline and it's first stop is the blind signature assertion. The assertion goes through all the steps of getting a signature and then adds a header containing it. It also modifies the ballot so it has the correct id. Next it passes through any other assertions required by the polling place, like anonymousForCertificateSecurity, and is sent to the service where the whole process happens in reverse. There's a couple extra steps for the client to communicate necessary information to blind signature assertion, but I'll get to that later.
In part 3 I'll look at what all this policy actually looks like.
The other parts: