Skip to content
Commits on Source (10)
...@@ -1365,19 +1365,23 @@ def get_dek_from_submissionid( submission_id ): ...@@ -1365,19 +1365,23 @@ def get_dek_from_submissionid( submission_id ):
""" """
Get the Data Encryption Key related to a submission id folder. 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: 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" ] return config.db.query_fetchone( sql, ( submission_id, ) )[ "dek" ]
except: except:
return None try:
return session[ "dek_{}".format( submission_id ) ]
except:
return None
def do_decrypt_dek( data, submission_id ): def do_decrypt_dek( data, submission_id ):
""" """
...@@ -1393,6 +1397,116 @@ def do_encrypt_dek( data, submission_id ): ...@@ -1393,6 +1397,116 @@ def do_encrypt_dek( data, submission_id ):
dek = get_dek_from_submissionid( submission_id ) dek = get_dek_from_submissionid( submission_id )
return do_encrypt( data, dek ) 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 # File upload
...@@ -1687,17 +1801,7 @@ def submission_do_new(): ...@@ -1687,17 +1801,7 @@ def submission_do_new():
data = ( username, email_hash, 2 ) data = ( username, email_hash, 2 )
donor_user_id = config.db.query_fetchone( sql, data )[ "id" ] donor_user_id = config.db.query_fetchone( sql, data )[ "id" ]
email = pbkdf2( email, "icnml_user_DEK" ).hash( True ) dek_salt, dek, dek_check = dek_generate( email = email, username = username )
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 )
sql = sql_insert_generate( "donor_dek", [ "donor_name", "salt", "dek", "dek_check", "iterations", "algo", "hash" ], "id" ) 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", ) data = ( username, dek_salt, dek, dek_check, config.DEK_NB_ITERATIONS, "pbkdf2", "sha512", )
...@@ -1728,7 +1832,10 @@ def submission_upload_tplp( submission_id ): ...@@ -1728,7 +1832,10 @@ def submission_upload_tplp( submission_id ):
This page is not accessible if a consent form is not available in the This page is not accessible if a consent form is not available in the
database for this particular donor. database for this particular donor.
""" """
try: try:
dek_check( submission_id )
sql = """ sql = """
SELECT email_aes as email, nickname, created_time, consent_form SELECT email_aes as email, nickname, created_time, consent_form
FROM submissions FROM submissions
...@@ -2701,6 +2808,12 @@ def user_myprofile_tenprint(): ...@@ -2701,6 +2808,12 @@ def user_myprofile_tenprint():
not accessible by the user via this interface. The consent form has been sent the 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. 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 = """ sql = """
SELECT files.id, files.uuid SELECT files.id, files.uuid
...@@ -2713,6 +2826,7 @@ def user_myprofile_tenprint(): ...@@ -2713,6 +2826,7 @@ def user_myprofile_tenprint():
return my_render_template( return my_render_template(
"users/profile/tenprint.html", "users/profile/tenprint.html",
has_dek = has_dek,
tenprint_cards = tenprint_cards tenprint_cards = tenprint_cards
) )
......
...@@ -13,9 +13,96 @@ ...@@ -13,9 +13,96 @@
<script type="text/javascript"> <script type="text/javascript">
baseurl = "{{ baseurl }}"; 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> </script>
<style type="text/css"> <style type="text/css">
.icnml_button {
margin-top: 50px;
}
.icnml_button > div > a {
width: 200px;
margin-bottom: 10px;
padding: 10px;
}
#icnml_donor_list { #icnml_donor_list {
margin: 10px; margin: 10px;
display: grid; display: grid;
...@@ -30,23 +117,50 @@ ...@@ -30,23 +117,50 @@
{% include navigation %} {% include navigation %}
<div class="icnml_content"> <div class="icnml_content">
<div id="icnml_donor_list"> {% if not has_dek %}
{% for tenprint in tenprint_cards %} <div style="margin: 10px;">
<div style="margin-bottom: 20px"> <p style="color: #ff0000; font-size: 30px; font-weight: bolder;">
<div class="ui-widget-header ui-corner-top icnml_box_top" id="tenprint_{{ tenprint[ 'uuid' ] }}_filename">-</div> You don't have anymore a Data Encryption Key (DEK) in ICNML.
<div class="ui-widget-content ui-corner-bottom icnml_box_content"> </p>
<div class="icnml_button">
<a id="edit_{{ tenprint[ 'id' ] }}" href="#"> <p>
<img height="350px" src="{{ url_for( 'image_file_serve', file_id = tenprint[ 'uuid' ] ) }}"> This imply that your data, in particular all biometric information (tenprint and latent marks) are not available as part of the ICNML data.
</a> </p>
</div>
<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>
</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> </div>
<script type="text/javascript"> <script type="text/javascript">
$( "#dek_button" ).on( "click", recreate_dek );
$( "#icnml_navigation_myprofile" ) $( "#icnml_navigation_myprofile" )
.addClass( "activated" ); .addClass( "activated" );
......