Merge pull request #24 from vincentmdealmeida/EventSetupDecrypt

The generic functionality of the event_vote page has been replicated …
This commit is contained in:
Vincent 2018-09-05 11:52:59 +01:00 committed by GitHub
commit 7e601a25e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 311 additions and 178 deletions

View file

@ -253,14 +253,16 @@ def event_trustee_setup(request, event_id):
email_key = event.keys.filter(key=access_key) email_key = event.keys.filter(key=access_key)
if email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists(): if email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists():
if TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists(): if TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists():
messages.add_message(request, messages.WARNING, 'You have already submitted your key for this event') return render(request, "polls/event_setup.html", {"is_trustee": True,
return HttpResponseRedirect(reverse("user_home")) "can_submit": False,
"access_denied_reason": "You have already submitted your public key for this event. Thank you!"
})
if request.method == "POST": if request.method == "POST":
form = EventSetupForm(request.POST) form = EventSetupForm(request.POST)
# If form data is valid, create a TrusteeKey object with the supplied public key # If form data is valid, create a TrusteeKey object with the supplied public key
if form.is_valid(): if form.is_valid():
public_key = request.POST["public_key"] public_key = request.POST.get("public_key")
key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0] key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0]
key.key = public_key key.key = public_key
key.save() key.save()
@ -280,11 +282,18 @@ def event_trustee_setup(request, event_id):
return HttpResponseRedirect(reverse("user_home")) return HttpResponseRedirect(reverse("user_home"))
else: else:
form = EventSetupForm() form = EventSetupForm()
return render(request, "polls/event_setup.html", {"event": event, "form": form, "user_email": email_key[0].user.email}) return render(request, "polls/event_setup.html", {"event": event,
"form": form,
"user_email": email_key[0].user.email,
"is_trustee": True,
"can_submit": True
})
#if no key or is invalid? else:
messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path) return render(request, "polls/event_setup.html", {"is_trustee": False,
return HttpResponseRedirect(reverse("user_home")) "can_submit": False,
"access_denied_reason": "You do not have permission to access this page."
})
def event_end(request, event_id): def event_end(request, event_id):
@ -311,11 +320,10 @@ def event_trustee_decrypt(request, event_id):
if email_key.exists() and event.users_trustees.filter(email=trustee.email).exists(): if email_key.exists() and event.users_trustees.filter(email=trustee.email).exists():
if PartialBallotDecryption.objects.filter(event=event, user=trustee).count() == event.total_num_opts(): if PartialBallotDecryption.objects.filter(event=event, user=trustee).count() == event.total_num_opts():
return render(request, "polls/event_decrypt.html", {"is_trustee": True,
warning_msg = 'You have already provided your decryption key for this event - Thank You' "can_submit": False,
messages.add_message(request, messages.WARNING, warning_msg) "access_denied_reason": "You have already submitted your partial decryptions for this event. Thank you!"
})
return HttpResponseRedirect(reverse("user_home"))
elif request.method == "GET": elif request.method == "GET":
# Get the Trustee's original PK - used in the template for SK validation # Get the Trustee's original PK - used in the template for SK validation
trustee_pk = TrusteeKey.objects.get(event=event, user=trustee).key trustee_pk = TrusteeKey.objects.get(event=event, user=trustee).key
@ -344,7 +352,9 @@ def event_trustee_decrypt(request, event_id):
"event": event, "event": event,
"user_email": trustee.email, "user_email": trustee.email,
"trustee_pk": trustee_pk, "trustee_pk": trustee_pk,
"poll_ciphers": poll_ciphers "poll_ciphers": poll_ciphers,
"is_trustee": True,
"can_submit": True
}) })
elif request.method == "POST": elif request.method == "POST":
@ -359,7 +369,7 @@ def event_trustee_decrypt(request, event_id):
input_name = str("") input_name = str("")
input_name = "poll-" + str(i) + "-cipher-" + str(j) input_name = "poll-" + str(i) + "-cipher-" + str(j)
part_dec = request.POST[input_name] part_dec = request.POST.get(input_name)
PartialBallotDecryption.objects.create(event=event, PartialBallotDecryption.objects.create(event=event,
poll=polls[i], poll=polls[i],
@ -381,8 +391,10 @@ def event_trustee_decrypt(request, event_id):
return HttpResponseRedirect(reverse("user_home")) return HttpResponseRedirect(reverse("user_home"))
# Without an access key, the client does not have permission to access this page # Without an access key, the client does not have permission to access this page
messages.add_message(request, messages.WARNING, 'You do not have permission to decrypt this Event.') return render(request, "polls/event_decrypt.html", {"is_trustee": False,
return HttpResponseRedirect(reverse("user_home")) "can_submit": False,
"access_denied_reason": "You don't have permission to access this page."
})
def manage_questions(request, event_id): def manage_questions(request, event_id):

View file

@ -22,6 +22,7 @@
<script src="{% static 'js/event_vote.js' %}" type="text/javascript"></script> <script src="{% static 'js/event_vote.js' %}" type="text/javascript"></script>
<script src="{% static 'js/vote_audit.js' %}" type="text/javascript"></script> <script src="{% static 'js/vote_audit.js' %}" type="text/javascript"></script>
<script src="{% static 'js/encrypt.js' %}" type="text/javascript"></script> <script src="{% static 'js/encrypt.js' %}" type="text/javascript"></script>
<script src="{% static 'js/event_setup.js' %}" type="text/javascript"></script>
<script type="text/javascript" src="{% static 'js/core/rand.js' %}"></script> <script type="text/javascript" src="{% static 'js/core/rand.js' %}"></script>
<script type="text/javascript" src="{% static 'js/core/rom_curve.js' %}"></script> <script type="text/javascript" src="{% static 'js/core/rom_curve.js' %}"></script>
@ -66,73 +67,6 @@
} }
function getBytes(arr) {
for(var i = 0; i < arr.length; i++) {
arr[i] = parseInt(arr[i]);
}
return new Uint8Array(arr);
}
//new function
demosEncrypt.decryptSubmitCiphers = function() {
};
//new function
demosEncrypt.generateKeys = function() {
var parameter = $("#event-param").val();
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
//the full objects need to be initalised as per the library, then copy the values we need into it
//I follow Bingsheng's code as to what objects are used in the parameter object
var ctx = new CTX("BN254CX"); //new context we can use
var n = new ctx.BIG();
var g1 = new ctx.ECP();
var g2 = new ctx.ECP2();
//copying the values
n.copy(tempParams.n);
g1.copy(tempParams.g1);
g2.copy(tempParams.g2);
var params = {
n:n,
g1:g1,
g2:g2
}
var PKbytes = [];
var SKbytes = [];
var keypair = keyGen(params);
keypair.PK.toBytes(PKbytes);
keypair.SK.toBytes(SKbytes);
var PKB64Encoded = "";
for(let i = 0; i < PKbytes.length; i++) {
PKB64Encoded += btoa(PKbytes[i]);
}
var SKB64Encoded = "";
for(let j = 0; j < SKbytes.length; j++) {
SKB64Encoded += btoa(SKbytes[j]);
}
$('input#public-key').val(PKB64Encoded);
$('input#secret-key').val(SKB64Encoded);
//mostly code from before here
var blob = new Blob([SKB64Encoded], {type : 'text/plain'});
var dlBtn = $('a#download-btn');
var url = URL.createObjectURL(blob);
var fileName = $(dlBtn).attr("href", url);
$(dlBtn).attr("download", "sk-{% block sk-file-name %}{% endblock %}".replace(/[\W]/g, "-"));
$(dlBtn).attr("disabled", false);
$("#public-submit").attr("disabled", false);
}
//these other functions might not be used //these other functions might not be used
//I don't think this is used //I don't think this is used
demosEncrypt.downloadSecretKey = function() { demosEncrypt.downloadSecretKey = function() {

View file

@ -4,77 +4,86 @@
{% block sk-file-name %}{{ event.title|safe }}{% endblock %} {% block sk-file-name %}{{ event.title|safe }}{% endblock %}
{% block content %} {% block content %}
<script type="text/javascript">
// This is what we expect the SK supplied by the trustee to generate
var trustee_pk = "{{ trustee_pk }}";
var tempParams = "{{ event.EID_crypto|escapejs }}"; {% if is_trustee and can_submit %}
tempParams = JSON.parse(tempParams); <script type="text/javascript">
</script> // This is what we expect the SK supplied by the trustee to generate
var trustee_pk = "{{ trustee_pk }}";
<div class="container"> var tempParams = "{{ event.EID_crypto|escapejs }}";
<h2>Trustee Event Decryption for Event '{{ event.title }}'</h2> tempParams = JSON.parse(tempParams);
<hr/> </script>
<div class="panel panel-default">
<div class="panel-heading"><strong>Upload your Secret Key as '{{ user_email }}'</strong></div> <div class="container">
<div class="panel panel-body"> <h2>Trustee Event Decryption for Event '{{ event.title }}'</h2>
<input id="secret-key" name="secret-key" class="textinput textInput form-control" type="text" disabled/> <hr/>
<div class="alert alert-info" role="alert" style="margin-top: 0.75em;"> <div class="panel panel-default">
Your secret key will be used to decrypt the event and get a vote tally for every poll. <div class="panel-heading"><strong>Upload your Secret Key as '{{ user_email }}'</strong></div>
It won't be sent to the server. <div class="panel panel-body">
<input id="secret-key" name="secret-key" class="textinput textInput form-control" type="text" disabled/>
<div class="alert alert-info" role="alert" style="margin-top: 0.75em;">
Your secret key will be used to decrypt the event and get a vote tally for every poll.
It won't be sent to the server.
</div>
<label for="files_sk_upload" class="btn btn-primary">
<span class="glyphicon glyphicon-cloud-upload"></span>
Upload Key
</label>
<input type="file" id="files_sk_upload" name="file" class="btn-info">
</div> </div>
<label for="files_sk_upload" class="btn btn-primary"> <br/>
<span class="glyphicon glyphicon-cloud-upload"></span> <div class="panel-heading"><strong>Encrypted Event Data</strong></div>
Upload Key <div class="panel panel-body">
</label> <form id="cipher-form" method="POST">
<input type="file" id="files_sk_upload" name="file" class="btn-info"> {% csrf_token %}
</div> {% for opts_ciphers in poll_ciphers %}
<br/> {% for cipher in opts_ciphers %}
<div class="panel-heading"><strong>Encrypted Event Data</strong></div> <input id="cipher"
<div class="panel panel-body"> name="poll-{{ forloop.parentloop.counter0 }}-cipher-{{ forloop.counter0 }}"
<form id="cipher-form" method="POST"> class="textinput textInput form-control"
{% csrf_token %} type="text"
{% for opts_ciphers in poll_ciphers %} value="{ &quot;C1&quot;: &quot;{{ cipher.C1 }}&quot;, &quot;C2&quot;: &quot;{{ cipher.C2 }}&quot; }"
{% for cipher in opts_ciphers %} />
<input id="cipher"
name="poll-{{ forloop.parentloop.counter0 }}-cipher-{{ forloop.counter0 }}"
class="textinput textInput form-control"
type="text"
value="{ &quot;C1&quot;: &quot;{{ cipher.C1 }}&quot;, &quot;C2&quot;: &quot;{{ cipher.C2 }}&quot; }"
/>
<br/>
{% endfor %}
<br/> <br/>
{% endfor %} {% endfor %}
<br/> <button id="decrypt-btn"
{% endfor %} type="button"
<button id="decrypt-btn" onclick="decryptSubmit()"
class="btn btn-success">
onclick="decryptSubmitCiphers()" Send Partial Decryptions</button>
class="btn btn-success"> </form>
Send Partial Decryptions</button> </div>
</form>
</div> </div>
</div>
</div>
<!-- Information Dialog called upon request -->
<div class="modal fade" id="modalDialog" role="dialog" tabindex="-1" data-backdrop="static">
<div class="modal-dialog" role="document">
<!-- Dialog content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" style="text-align: center"><strong>Partial Decryption Successfully Received</strong></h4>
</div>
<div class="modal-body">
<p>Thank you! You can now close down this page.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div> </div>
</div>
<!-- Information Dialog called upon request -->
<div class="modal fade" id="EventDecryptionModalDialog" role="dialog" tabindex="-1" data-backdrop="static">
<div class="modal-dialog" role="document">
<!-- Dialog content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" style="text-align: center"><strong>Partial Decryptions Successfully Received</strong></h4>
</div>
<div class="modal-body">
<p>Thank you! You can now close down this page.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{% else %}
<div class="container">
<div class="alert alert-warning" role="alert">
<p>{{ access_denied_reason }}</p>
</div>
</div>
{% endif %}
{% endblock %} {% endblock %}

View file

@ -6,34 +6,66 @@
{% block content %} {% block content %}
<div class="container"> <script type="text/javascript">
<h2>Trustee Event Setup for Event '{{ event.title }}'</h2> var EVENT_TITLE = "{{ event.title|safe }}";
<hr/> </script>
<h4>Key Generation For: {{ user_email }}</h4>
<br/> {% if is_trustee and can_submit %}
<div class="panel panel-default"> <div class="container">
<div class="panel-heading"><strong>Step 1: Generate and Download Your Secret Key</strong></div> <h2>Trustee Event Setup for Event '{{ event.title }}'</h2>
<div class="panel panel-body"> <hr/>
<input id="secret-key" class="textinput textInput form-control" type="text"/> <h4>Key Generation For: {{ user_email }}</h4>
<input id="event-param" type="text" value="{{event.EID}}" hidden/> <br/>
<div class="alert alert-warning" role="alert" style="margin-top: 0.75em;"> <div class="panel panel-default">
<strong>Warning:</strong> This key can <strong>NOT</strong> be recalculated if forgotten or lost! Ensure you back this up. <div class="panel-heading"><strong>Step 1: Generate and Download Your Secret Key</strong></div>
<div class="panel panel-body">
<input id="secret-key" class="textinput textInput form-control" type="text"/>
<input id="event-param" type="text" value="{{event.EID}}" hidden/>
<div class="alert alert-warning" role="alert" style="margin-top: 0.75em;">
<strong>Warning:</strong> This key can <strong>NOT</strong> be recalculated if forgotten or lost! Ensure you back this up.
</div>
<button id="keygen-btn" onclick="generateKeys()" class="btn btn-success">Generate</button>
<a id="download-btn" role="button" href="#" class="btn btn-primary" disabled>Download</a>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Step 2: Submit Your Public Key</strong></div>
<div class="panel panel-body">
{% load crispy_forms_tags %}
<form method="post" action="" class="">
{% crispy form %}
<p>Ensure your secret key is backed up before submitting.</p>
<button id="public-submit" class="btn btn-danger" type="button" onclick="submitPublicKey()" disabled>Submit</button>
</form>
</div>
</div> </div>
<button id="keygen-btn" onclick="demosEncrypt.generateKeys()" class="btn btn-success">Generate</button>
<a id="download-btn" role="button" href="#" class="btn btn-primary" disabled>Download</a>
</div> </div>
</div>
<div class="panel panel-default"> <!-- Information Dialog called upon request -->
<div class="panel-heading"><strong>Step 2: Submit Your Public Key</strong></div> <div class="modal fade" id="EventSetupModalDialog" role="dialog" tabindex="-1" data-backdrop="static">
<div class="panel panel-body"> <div class="modal-dialog" role="document">
{% load crispy_forms_tags %}
<form method="post" action="" class=""> <!-- Dialog content-->
{% crispy form %} <div class="modal-content">
<p>Ensure your secret key is backed up before submitting.</p> <div class="modal-header">
<input id="public-submit" class="btn btn-danger" type="submit" value="Submit" disabled/> <h4 class="modal-title" style="text-align: center"><strong>Public Key Successfully Received</strong></h4>
</form> </div>
<div class="modal-body">
<p>Thank you! You can now close down this page.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div> </div>
</div> {% else %}
</div> <div class="container">
<div class="alert alert-warning" role="alert">
<p>{{ access_denied_reason }}</p>
</div>
</div>
{% endif %}
{% endblock %} {% endblock %}

View file

@ -1,6 +1,7 @@
// -------------- Global vars -------------------- // -------------- Global vars --------------------
var filesHandleSK = document.getElementById('files_sk_upload'); var filesHandleSK = document.getElementById('files_sk_upload');
var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val(); var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
var partialDecryptions = {};
// -------------- Helper fns -------------------- // -------------- Helper fns --------------------
//SK checking algorithm - If PK and SK matches, it returns True; otherwise, it returns false. //SK checking algorithm - If PK and SK matches, it returns True; otherwise, it returns false.
@ -15,6 +16,14 @@ function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
} }
function getBytes(arr) {
for(var i = 0; i < arr.length; i++) {
arr[i] = parseInt(arr[i]);
}
return new Uint8Array(arr);
}
function getKeyBytes(key, byteArray) { function getKeyBytes(key, byteArray) {
for(let i = 0; i < key.length; i += 4) { for(let i = 0; i < key.length; i += 4) {
let B64EncodedByte = key.substring(i, i + 4); let B64EncodedByte = key.substring(i, i + 4);
@ -23,8 +32,8 @@ function getKeyBytes(key, byteArray) {
} }
} }
function showDialog(titleTxt, bodyTxt) { function showDecryptDialog(titleTxt, bodyTxt) {
var modalDialog = $('#modalDialog'); var modalDialog = $('#EventDecryptionModalDialog');
var title = modalDialog.find('.modal-title'); var title = modalDialog.find('.modal-title');
var body = modalDialog.find('.modal-body'); var body = modalDialog.find('.modal-body');
@ -79,7 +88,7 @@ function validateSKFromString(SKStr) {
return skCheck(ctx, params, sk, pk); return skCheck(ctx, params, sk, pk);
} }
function decryptSubmitCiphers() { function decryptSubmit() {
var skString = $('#secret-key').val(); var skString = $('#secret-key').val();
if (!skString) { if (!skString) {
@ -96,7 +105,6 @@ function decryptSubmitCiphers() {
inputs.each(function() { //for each ciphertext to decrypt inputs.each(function() { //for each ciphertext to decrypt
let input = $(this); let input = $(this);
console.log(input.attr('name'));
var ciphertext = { var ciphertext = {
C1: null, C1: null,
@ -116,10 +124,38 @@ function decryptSubmitCiphers() {
var bytes = []; var bytes = [];
partial.D.toBytes(bytes); partial.D.toBytes(bytes);
input.val(bytes.toString()); input.val(bytes.toString());
partialDecryptions[input.attr('name')] = bytes.toString();
}); });
submitPartialDecryptions();
} }
} }
function onAfterPartialDecryptionsSend() {
showDecryptDialog('Partial Decryptions Successfully Received',
'Thank you! You can now close down this page.');
}
function submitPartialDecryptions() {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", CSRF);
}
}
});
$.ajax({
type : "POST",
url : window.location,
data : partialDecryptions,
success : function(){
onAfterPartialDecryptionsSend();
}
});
}
function processFileSKChange(event) { function processFileSKChange(event) {
var files = event.target.files; var files = event.target.files;
@ -154,3 +190,8 @@ function processFileSKChange(event) {
if(filesHandleSK) { if(filesHandleSK) {
filesHandleSK.addEventListener('change', processFileSKChange, false); filesHandleSK.addEventListener('change', processFileSKChange, false);
} }
$('#EventDecryptionModalDialog').on('hide.bs.modal', function (e) {
// Update page to reflect the fact that the PK submission has taken place
location.reload();
});

105
static/js/event_setup.js Normal file
View file

@ -0,0 +1,105 @@
var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function showSetupDialog(titleTxt, bodyTxt) {
var modalDialog = $('#EventSetupModalDialog');
var title = modalDialog.find('.modal-title');
var body = modalDialog.find('.modal-body');
title.text(titleTxt);
var bodyText = bodyTxt;
var p = document.createElement("p");
p.innerHTML = bodyText;
body.empty();
body.append( p );
modalDialog.modal('show');
}
function generateKeys() {
var parameter = $("#event-param").val();
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
//the full objects need to be initalised as per the library, then copy the values we need into it
//I follow Bingsheng's code as to what objects are used in the parameter object
var ctx = new CTX("BN254CX"); //new context we can use
var n = new ctx.BIG();
var g1 = new ctx.ECP();
var g2 = new ctx.ECP2();
//copying the values
n.copy(tempParams.n);
g1.copy(tempParams.g1);
g2.copy(tempParams.g2);
var params = {
n:n,
g1:g1,
g2:g2
}
var PKbytes = [];
var SKbytes = [];
var keypair = keyGen(params);
keypair.PK.toBytes(PKbytes);
keypair.SK.toBytes(SKbytes);
var PKB64Encoded = "";
for(let i = 0; i < PKbytes.length; i++) {
PKB64Encoded += btoa(PKbytes[i]);
}
var SKB64Encoded = "";
for(let j = 0; j < SKbytes.length; j++) {
SKB64Encoded += btoa(SKbytes[j]);
}
$('input#public-key').val(PKB64Encoded);
$('input#secret-key').val(SKB64Encoded);
//mostly code from before here
var blob = new Blob([SKB64Encoded], {type : 'text/plain'});
var dlBtn = $('a#download-btn');
var url = URL.createObjectURL(blob);
$(dlBtn).attr("href", url);
let fileName = "sk-" + EVENT_TITLE.replace(/[\W]/g, "-");
$(dlBtn).attr("download", fileName);
$(dlBtn).attr("disabled", false);
$("#public-submit").attr("disabled", false);
}
function onAfterKeySend() {
showSetupDialog('Public Key Successfully Received',
'Thank you! You can now close down this page.');
}
function submitPublicKey() {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", CSRF);
}
}
});
$.ajax({
type : "POST",
url : window.location,
data : { public_key: $('input#public-key').val() },
success : function(){
onAfterKeySend();
}
});
}
$('#EventSetupModalDialog').on('hide.bs.modal', function (e) {
// Update page to reflect the fact that the PK submission has taken place
location.reload();
});