Skip to content
Commits on Source (10)
......@@ -1365,19 +1365,23 @@ def get_dek_from_submissionid( submission_id ):
"""
Get the Data Encryption Key related to a submission id folder.
"""
sql = """
SELECT donor_dek.dek
FROM donor_dek
LEFT JOIN users ON users.username = donor_dek.donor_name
LEFT JOIN submissions ON submissions.donor_id = users.id
WHERE submissions.uuid = %s
LIMIT 1
"""
try:
sql = """
SELECT donor_dek.dek
FROM donor_dek
LEFT JOIN users ON users.username = donor_dek.donor_name
LEFT JOIN submissions ON submissions.donor_id = users.id
WHERE submissions.uuid = %s
LIMIT 1
"""
return config.db.query_fetchone( sql, ( submission_id, ) )[ "dek" ]
except:
return None
try:
return session[ "dek_{}".format( submission_id ) ]
except:
return None
def do_decrypt_dek( data, submission_id ):
"""
......@@ -1393,6 +1397,116 @@ def do_encrypt_dek( data, submission_id ):
dek = get_dek_from_submissionid( submission_id )
return do_encrypt( data, dek )
def dek_check( submission_id ):
if dek_exists( submission_id ):
return True
else:
try:
return dek_submitte_recreate_session( submission_id )
except:
return False
def dek_exists( submission_id ):
if get_dek_from_submissionid( submission_id ) == None:
return False
else:
return True
def dek_submitte_recreate_session( submission_id ):
sql = """
SELECT
donor_dek.salt,
donor_dek.dek_check,
donor_dek.donor_name as username,
submissions.email_aes as email
FROM donor_dek
LEFT JOIN users ON users.username = donor_dek.donor_name
LEFT JOIN submissions ON submissions.donor_id = users.id
WHERE submissions.uuid = %s
LIMIT 1
"""
user = config.db.query_fetchone( sql, ( submission_id, ) )
username = user[ "username" ]
email = do_decrypt_user_session( user[ "email" ] )
dek_salt = user[ "salt" ]
_, dek, _ = dek_generate( username = username, email = email, salt = dek_salt )
to_check = do_decrypt( user[ "dek_check" ], dek )
to_check = json.loads( to_check )
if to_check[ "value" ] == "ok":
session[ "dek_{}".format( submission_id ) ] = dek
return True
else:
return False
def dek_generate( **kwargs ):
if "email" in kwargs:
email = kwargs[ "email" ]
email = pbkdf2( email, "icnml_user_DEK" ).hash( True )
elif "email_hash" in kwargs:
email = kwargs[ "email_hash" ]
else:
raise Exception( "need the email or hashed_email" )
if "username" in kwargs:
username = kwargs[ "username" ]
else:
raise Exception( "need the username" )
dek_salt = kwargs.get( "salt", random_data( 100 ) )
dek = pbkdf2(
"{}:{}".format( username, email, ),
dek_salt,
iterations = config.DEK_NB_ITERATIONS,
hash_name = "sha512"
).hash( True )
check = {
"value": "ok",
"time": int( time.time() * 1000 ),
"random": random_data( 10 )
}
check = json.dumps( check )
check = do_encrypt( check, dek )
return dek_salt, dek, check
@app.route( baseurl + "/dek/reconstruct", methods = [ "POST" ] )
@login_required
def dek_regenerate():
try:
email_hash = request.form.get( "email_hash" )
username = session.get( "username" )
sql = "SELECT * FROM donor_dek WHERE donor_name = %s"
user = config.db.query_fetchone( sql, ( username, ) )
_, dek, _ = dek_generate( username = username, email_hash = email_hash, salt = user[ "salt" ] )
check = do_decrypt( user[ "dek_check" ], dek )
check = json.loads( check )
if check[ "value" ] != "ok":
raise
sql = "UPDATE donor_dek SET dek = %s WHERE id = %s AND donor_name = %s"
config.db.query( sql, ( dek, user[ "id" ], username, ) )
config.db.commit()
return jsonify( {
"error": False
} )
except:
return jsonify( {
"error": True
} )
################################################################################
# File upload
......@@ -1687,17 +1801,7 @@ def submission_do_new():
data = ( username, email_hash, 2 )
donor_user_id = config.db.query_fetchone( sql, data )[ "id" ]
email = pbkdf2( email, "icnml_user_DEK" ).hash( True )
dek_salt = random_data( 100 )
dek = pbkdf2( "{}:{}".format( username, email, ), dek_salt, iterations = config.DEK_NB_ITERATIONS, hash_name = "sha512" ).hash( True )
dek_check = {
"value": "ok",
"time": int( time.time() * 1000 ),
"random": random_data( 10 )
}
dek_check = json.dumps( dek_check )
dek_check = do_encrypt( dek_check, dek )
dek_salt, dek, dek_check = dek_generate( email = email, username = username )
sql = sql_insert_generate( "donor_dek", [ "donor_name", "salt", "dek", "dek_check", "iterations", "algo", "hash" ], "id" )
data = ( username, dek_salt, dek, dek_check, config.DEK_NB_ITERATIONS, "pbkdf2", "sha512", )
......@@ -1728,7 +1832,10 @@ def submission_upload_tplp( submission_id ):
This page is not accessible if a consent form is not available in the
database for this particular donor.
"""
try:
dek_check( submission_id )
sql = """
SELECT email_aes as email, nickname, created_time, consent_form
FROM submissions
......@@ -2701,6 +2808,12 @@ def user_myprofile_tenprint():
not accessible by the user via this interface. The consent form has been sent the
the donor by email anyways before uploading any of the images.
"""
sql = """
SELECT dek
FROM donor_dek
WHERE donor_name = %s
"""
has_dek = config.db.query_fetchone( sql, ( session[ "username" ], ) )[ "dek" ] != None
sql = """
SELECT files.id, files.uuid
......@@ -2713,6 +2826,7 @@ def user_myprofile_tenprint():
return my_render_template(
"users/profile/tenprint.html",
has_dek = has_dek,
tenprint_cards = tenprint_cards
)
......
......@@ -13,9 +13,96 @@
<script type="text/javascript">
baseurl = "{{ baseurl }}";
var recreate_dek_ajax = async function()
{
var email = $( "#email_input" ).val();
email = await generateKey( email, "icnml_user_DEK", 20000 );
email = email.substring( 0, 128 );
$.ajax( {
url: "{{ url_for( 'dek_regenerate' ) }}",
method: "POST",
data: {
"email_hash": email
},
dataType: "json",
success: function( data )
{
if( ! data.error )
{
$( "#dek_create_confirmation" ).remove();
window.location = "{{ url_for( 'user_myprofile_tenprint' ) }}";
} else {
toastr.error( "Error while reconstructing the DEK" );
}
},
error: function( data )
{
$( "#dek_create_confirmation" ).remove();
toastr.error( "Network error" );
}
} );
}
var recreate_dek = function()
{
$( "<div />" )
.attr( "id", "dek_create_confirmation" )
.text( "Do you really want to recreate the Data Encryption Key, making your biometric data available to the users of ICNML?" )
.append(
$( "<div />" )
.attr( "class", "icnml_box_fields" )
.css( "margin-top", "10px" )
.append(
$( "<label />" )
.attr( "for", "email_input" )
.text( "Email" )
)
.append(
$( "<input />" )
.attr( "id", "email_input" )
)
)
.dialog( {
title: "Confirm Data Encryption Key generation",
modal: true,
width: "550px",
buttons: {
"Re-generate": function(){
recreate_dek_ajax();
},
"Cancel": function()
{
$( this ).dialog( "close" );
},
},
close: function()
{
$( this ).remove();
}
} );
$( "#email_input" ).on( "keypress", function( event )
{
if( event.keyCode == 13 )
{
recreate_dek_ajax();
}
} );
}
</script>
<style type="text/css">
.icnml_button {
margin-top: 50px;
}
.icnml_button > div > a {
width: 200px;
margin-bottom: 10px;
padding: 10px;
}
#icnml_donor_list {
margin: 10px;
display: grid;
......@@ -30,23 +117,50 @@
{% include navigation %}
<div class="icnml_content">
<div id="icnml_donor_list">
{% for tenprint in tenprint_cards %}
<div style="margin-bottom: 20px">
<div class="ui-widget-header ui-corner-top icnml_box_top" id="tenprint_{{ tenprint[ 'uuid' ] }}_filename">-</div>
<div class="ui-widget-content ui-corner-bottom icnml_box_content">
<div class="icnml_button">
<a id="edit_{{ tenprint[ 'id' ] }}" href="#">
<img height="350px" src="{{ url_for( 'image_file_serve', file_id = tenprint[ 'uuid' ] ) }}">
</a>
</div>
{% if not has_dek %}
<div style="margin: 10px;">
<p style="color: #ff0000; font-size: 30px; font-weight: bolder;">
You don't have anymore a Data Encryption Key (DEK) in ICNML.
</p>
<p>
This imply that your data, in particular all biometric information (tenprint and latent marks) are not available as part of the ICNML data.
</p>
<p>
For privacy purpose, the original submitter of your biometric data can still see the uploaded data.
This is done to prevent the submitter knowing that you have requested a deletion of ICNML.
</p>
<div class="icnml_button">
<div id="create_dek_button_div">
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" id="dek_button" role="button" aria-disabled="false">
<span class="ui-button-text" id="dek_button_span_text">Reconstruct DEK</span>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
<div id="icnml_donor_list">
{% for tenprint in tenprint_cards %}
<div style="margin-bottom: 20px">
<div class="ui-widget-header ui-corner-top icnml_box_top" id="tenprint_{{ tenprint[ 'uuid' ] }}_filename">-</div>
<div class="ui-widget-content ui-corner-bottom icnml_box_content">
<div class="icnml_button">
<a id="edit_{{ tenprint[ 'id' ] }}" href="#">
<img height="350px" src="{{ url_for( 'image_file_serve', file_id = tenprint[ 'uuid' ] ) }}">
</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
<script type="text/javascript">
$( "#dek_button" ).on( "click", recreate_dek );
$( "#icnml_navigation_myprofile" )
.addClass( "activated" );
......