Skip to content
This repository has been archived by the owner on Mar 16, 2023. It is now read-only.

Commit

Permalink
integrity-checking feature publication on github
Browse files Browse the repository at this point in the history
  • Loading branch information
yahesh committed Aug 30, 2016
1 parent 2bcd2db commit 734c14c
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 169 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# 0.4b0 (2016-09-30)

* version bump for integrity-checking feature publication on github

# 0.3b1 (2016-08-29)

* moved inline JavaScript code to separate .js files
* introduced subresource integrity for link and style elements
* added asmCrypto.js.map for better debugging support
* switched from server-defined PBKDF2 salt to JavaScript-generated salt
* test implementation of new features within chroot environment

# 0.3b0 (2016-08-25)

* version bump for password-protection feature publication on github
Expand Down
2 changes: 1 addition & 1 deletion index.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

# Shared-Secrets v0.3b0
# Shared-Secrets v0.4b0
#
# Copyright (c) 2016, SysEleven GmbH
# All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion pages/how/get.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
?>

<h2>Short description of the password-protection feature.</h2>
<p>When using the password-protection feature, the secret is encrypted locally using the AES algorithm in GCM mode. The encryption key is derived from the entered password and a server-provided salt using the PBKDF2 algorithm. The server-provided salt is prepended to the encrypted secret. The password-protection feature is implemented using client-side JavaScript. Please beware that a compromised server may serve you JavaScript code that defeats the purpose of the local encryption. If you do not trust the server that provides the secret sharing service, then encrypt your secret with a locally installed application before sharing it.
<p>When using the password-protection feature, the secret is encrypted locally using the AES algorithm in GCM mode. The encryption key is derived from the entered password and a dynamically generated salt using the PBKDF2 algorithm. The dynamically generated salt is prepended to the encrypted secret. The password-protection feature is implemented using client-side JavaScript. Please beware that a compromised server may serve you JavaScript code that defeats the purpose of the local encryption. If you do not trust the server that provides the secret sharing service, then encrypt your secret with a locally installed application before sharing it.

<?php

Expand Down
77 changes: 5 additions & 72 deletions pages/read/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,78 +42,11 @@
<input type="password" autocomplete="off" class="form-control" id="password" maxlength="64" size="32" style="display: inline; visibility: hidden; width: 25%;" />
<input type="button" class="btn btn-default" id="decrypt" style="visibility: hidden;" value="Unprotect!" onclick="decrypt();" />

<script src="/vendors/asmcrypto/asmcrypto.js" type="text/javascript"></script>
<script src="/vendors/buffer/index.js" type="text/javascript"></script>

<script type="text/javascript">
// action happening on local decryption
function decrypt() {
var result = decrypt_secret(document.getElementById("secret").innerHTML,
document.getElementById("password").value);

if (null != result) {
document.getElementById("secret").innerHTML = html_entities(result);

document.getElementById("decrypt").disabled = true;
document.getElementById("decrypt-locally").disabled = true;

document.getElementById("password").readOnly = "readonly";

document.getElementById("decrypt-error").style.display = "none";
} else {
document.getElementById("decrypt-error").style.display = "block";
}
}

// show/hide local decryption
function decrypt_locally(checkbox) {
if (document.getElementById("decrypt-locally").checked) {
document.getElementById("decrypt").style.visibility = "visible";
document.getElementById("password").style.visibility = "visible";
} else {
document.getElementById("decrypt").style.visibility = "hidden";
document.getElementById("password").style.visibility = "hidden";
}
}

// prevent code injection through locally decrypted secret
function html_entities(content) {
return content.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

function decrypt_secret(concatSecret, password) {
// these variables configure the PBKDF2 call
var outputLength = 32;
var workFactor = 1024;

// split concatenation of Base64-encoded salt and Base64-encoded encrypted secret
var base64Salt = concatSecret.substring(0, 44);
var base64Secret = concatSecret.substring(44);

// retrieve plain salt from Base64-encoded salt
var salt = (new buffer.SlowBuffer(base64Salt, "base64")).toArrayBuffer();

// retrieve plain secret from Base64-encoded encrypted secret
var secret = (new buffer.SlowBuffer(base64Secret, "base64")).toArrayBuffer();

// derive decryption key
var pbkdf2Key = asmCrypto.PBKDF2_HMAC_SHA256.bytes(password, salt, workFactor, outputLength);

try {
// decrypt secret with derived decryption key
var aesResult = asmCrypto.AES_GCM.decrypt(secret, pbkdf2Key, new Uint8Array(12));
} catch(err) {
var aesResult = null;
}

if (null != aesResult) {
// return UTF-8-encoded decrypted secret
return (new buffer.SlowBuffer(aesResult)).toString("utf-8");
} else {
return aesResult;
}
}
</script>
<script src="/vendors/asmcrypto/asmcrypto.js" integrity="sha256-+3Ja+u+3rug2giERjvQSkhc1GZ1jG8ebXZ5TbQe2890=" type="text/javascript"></script>
<script src="/vendors/buffer/index.js" integrity="sha256-+fItxTnTLDK8HaHyqiP4cD+RxwDK66DqoTE91HqUfnM=" type="text/javascript"></script>
<script src="/vendors/clipboard/clipboard.min.js" integrity="sha256-YPxFEfHAzLj9n2T+2UXAKGNCRUINk0BexppujiVhRH0=" type="text/javascript"></script>
<script src="/resources/js/copy-to-clipboard.js" integrity="sha256-pdoft+huio0ejiVD+B0WLnFm9Wab+1Yj1nODdPNAZI4=" type="text/javascript"></script>
<script src="/resources/js/decrypt.js" integrity="sha256-rbZOtsEWrdRylOtuKJGpCMFTvrU4AE2jy2fbbDLteqM=" type="text/javascript"></script>

<?php

Expand Down
74 changes: 3 additions & 71 deletions pages/share/get.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,77 +44,9 @@
<input type="password" autocomplete="off" class="form-control" id="password" maxlength="64" size="32" style="display: inline; margin-top: 0.5em; visibility: hidden; width: 25%;" />
<input type="button" class="btn btn-default" id="encrypt" style="visibility: hidden;" value="Protect!" onclick="encrypt();" />

<script src="/vendors/asmcrypto/asmcrypto.js" type="text/javascript"></script>
<script src="/vendors/buffer/index.js" type="text/javascript"></script>

<script type="text/javascript">
// action happening on local encryption
function encrypt() {
var result = encrypt_secret(document.getElementById("secret").value,
document.getElementById("password").value,
"<?php print(base64_encode(openssl_random_pseudo_bytes(32))); ?>");

if (null != result) {
document.getElementById("secret").value = result;

document.getElementById("share-secret-btn").disabled = false;

document.getElementById("encrypt").disabled = true;
document.getElementById("encrypt-locally").disabled = true;

document.getElementById("password").readOnly = "readonly";
document.getElementById("secret").readOnly = "readonly";

document.getElementById("encrypt-error").style.display = "none";
} else {
document.getElementById("encrypt-error").style.display = "block";
}
}

// show/hide local encryption
function encrypt_locally(checkbox) {
if (document.getElementById("encrypt-locally").checked) {
document.getElementById("share-secret-btn").disabled = true;

document.getElementById("encrypt").style.visibility = "visible";
document.getElementById("password").style.visibility = "visible";
} else {
document.getElementById("share-secret-btn").disabled = false;

document.getElementById("encrypt").style.visibility = "hidden";
document.getElementById("password").style.visibility = "hidden";
}
}

function encrypt_secret(secret, password, base64Salt) {
// these variables configure the PBKDF2 call
var outputLength = 32;
var workFactor = 1024;

// retrieve salt from Base64-encoded salt
var salt = (new buffer.SlowBuffer(base64Salt, "base64")).toArrayBuffer();

// derive encryption key
var pbkdf2Key = asmCrypto.PBKDF2_HMAC_SHA256.bytes(password, salt, workFactor, outputLength);

try {
// encrypt secret with derived encryption key
var aesResult = asmCrypto.AES_GCM.encrypt(secret, pbkdf2Key, new Uint8Array(12));
} catch (err) {
var aesResult = null;
}

if (null != aesResult) {
// create Base64-encoded encrypted secret
var base64Secret = (new buffer.SlowBuffer(aesResult)).toString("base64");

// return concatenation of Base64-encoded salt and Base64-encoded encrypted secret
return (base64Salt + base64Secret);
} else {
return aesResult;
}
}
</script>
<script src="/vendors/asmcrypto/asmcrypto.js" integrity="sha256-+3Ja+u+3rug2giERjvQSkhc1GZ1jG8ebXZ5TbQe2890=" type="text/javascript"></script>
<script src="/vendors/buffer/index.js" integrity="sha256-+fItxTnTLDK8HaHyqiP4cD+RxwDK66DqoTE91HqUfnM=" type="text/javascript"></script>
<script src="/resources/js/encrypt.js" integrity="sha256-NlYh0r+uul7mzgrlyr+3IR6L3k/qYDR44WH7j9D4CzQ=" type="text/javascript"></script>

<?php

Expand Down
3 changes: 3 additions & 0 deletions pages/share/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<p><pre id="share-secret"><?php print(share_secret(SECRET_PARAM)); ?></pre>
<button type="btn" class="btn btn-default pull-right" data-clipboard-target="#share-secret" id="copy-to-clipboard">Copy to Clipboard!</button></p>

<script src="/vendors/clipboard/clipboard.min.js" integrity="sha256-YPxFEfHAzLj9n2T+2UXAKGNCRUINk0BexppujiVhRH0=" type="text/javascript"></script>
<script src="/resources/js/copy-to-clipboard.js" integrity="sha256-pdoft+huio0ejiVD+B0WLnFm9Wab+1Yj1nODdPNAZI4=" type="text/javascript"></script>

<?php

# include footer
Expand Down
15 changes: 15 additions & 0 deletions resources/js/copy-to-clipboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// find copy-to-clipboard button
var copy_to_clipboard = document.getElementById("copy-to-clipboard");

if (null != copy_to_clipboard) {
// check if we're confronted with a Safari browser
if ((-1 != navigator.userAgent.indexOf("Safari")) &&
(-1 == navigator.userAgent.indexOf("Android")) &&
(-1 == navigator.userAgent.indexOf("Chrome"))) {
// hide copy-to-clipboard button, because it is not supported
copy_to_clipboard.style.display = "none";
} else {
// initialize clipboard feature
var clipboard = new Clipboard('.btn');
}
}
67 changes: 67 additions & 0 deletions resources/js/decrypt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// action happening on local decryption
function decrypt() {
var result = decrypt_secret(document.getElementById("secret").innerHTML,
document.getElementById("password").value);

if (null != result) {
document.getElementById("secret").innerHTML = html_entities(result);

document.getElementById("decrypt").disabled = true;
document.getElementById("decrypt-locally").disabled = true;

document.getElementById("password").readOnly = "readonly";

document.getElementById("decrypt-error").style.display = "none";
} else {
document.getElementById("decrypt-error").style.display = "block";
}
}

// show/hide local decryption
function decrypt_locally(checkbox) {
if (document.getElementById("decrypt-locally").checked) {
document.getElementById("decrypt").style.visibility = "visible";
document.getElementById("password").style.visibility = "visible";
} else {
document.getElementById("decrypt").style.visibility = "hidden";
document.getElementById("password").style.visibility = "hidden";
}
}

// prevent code injection through locally decrypted secret
function html_entities(content) {
return content.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

function decrypt_secret(concatSecret, password) {
// these variables configure the PBKDF2 call
var outputLength = 32;
var workFactor = 1024;

// split concatenation of Base64-encoded salt and Base64-encoded encrypted secret
var base64Salt = concatSecret.substring(0, 44);
var base64Secret = concatSecret.substring(44);

// retrieve plain salt from Base64-encoded salt
var salt = (new buffer.SlowBuffer(base64Salt, "base64")).toArrayBuffer();

// retrieve plain secret from Base64-encoded encrypted secret
var secret = (new buffer.SlowBuffer(base64Secret, "base64")).toArrayBuffer();

// derive decryption key
var pbkdf2Key = asmCrypto.PBKDF2_HMAC_SHA256.bytes(password, salt, workFactor, outputLength);

try {
// decrypt secret with derived decryption key
var aesResult = asmCrypto.AES_GCM.decrypt(secret, pbkdf2Key, new Uint8Array(12));
} catch(err) {
var aesResult = null;
}

if (null != aesResult) {
// return UTF-8-encoded decrypted secret
return (new buffer.SlowBuffer(aesResult)).toString("utf-8");
} else {
return aesResult;
}
}
72 changes: 72 additions & 0 deletions resources/js/encrypt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// action happening on local encryption
function encrypt() {
var result = encrypt_secret(document.getElementById("secret").value,
document.getElementById("password").value);

if (null != result) {
document.getElementById("secret").value = result;

document.getElementById("share-secret-btn").disabled = false;

document.getElementById("encrypt").disabled = true;
document.getElementById("encrypt-locally").disabled = true;

document.getElementById("password").readOnly = "readonly";
document.getElementById("secret").readOnly = "readonly";

document.getElementById("encrypt-error").style.display = "none";
} else {
document.getElementById("encrypt-error").style.display = "block";
}
}

// show/hide local encryption
function encrypt_locally(checkbox) {
if (document.getElementById("encrypt-locally").checked) {
document.getElementById("share-secret-btn").disabled = true;

document.getElementById("encrypt").style.visibility = "visible";
document.getElementById("password").style.visibility = "visible";
} else {
document.getElementById("share-secret-btn").disabled = false;

document.getElementById("encrypt").style.visibility = "hidden";
document.getElementById("password").style.visibility = "hidden";
}
}

function encrypt_secret(secret, password) {
// these variables configure the PBKDF2 call
var outputLength = 32;
var workFactor = 1024;

// disable asmCrypto warning
asmCrypto.random.skipSystemRNGWarning = true;

// retrieve salt from PRNG
var salt = new Uint8Array(32);
asmCrypto.getRandomValues(salt);

// derive encryption key
var pbkdf2Key = asmCrypto.PBKDF2_HMAC_SHA256.bytes(password, salt, workFactor, outputLength);

try {
// encrypt secret with derived encryption key
var aesResult = asmCrypto.AES_GCM.encrypt(secret, pbkdf2Key, new Uint8Array(12));
} catch (err) {
var aesResult = null;
}

if (null != aesResult) {
// create Base64-encoded salt
var base64Salt = (new buffer.SlowBuffer(salt)).toString("base64");

// create Base64-encoded encrypted secret
var base64Secret = (new buffer.SlowBuffer(aesResult)).toString("base64");

// return concatenation of Base64-encoded salt and Base64-encoded encrypted secret
return (base64Salt + base64Secret);
} else {
return aesResult;
}
}
23 changes: 2 additions & 21 deletions template/footer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,7 @@
<!-- footer -->
</div>

<script src="/vendors/jquery/jquery.min.js" type="text/javascript"></script>
<script src="/vendors/clipboard/clipboard.min.js" type="text/javascript"></script>
<script src="/vendors/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>

<script type="text/javascript">
// find copy-to-clipboard button
var copy_to_clipboard = document.getElementById("copy-to-clipboard");

if (null != copy_to_clipboard) {
// check if we're confronted with a Safari browser
if ((-1 != navigator.userAgent.indexOf("Safari")) &&
(-1 == navigator.userAgent.indexOf("Android")) &&
(-1 == navigator.userAgent.indexOf("Chrome"))) {
// hide copy-to-clipboard button, because it is not supported
copy_to_clipboard.style.display = "none";
} else {
// initialize clipboard feature
var clipboard = new Clipboard('.btn');
}
}
</script>
<script src="/vendors/jquery/jquery.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" type="text/javascript"></script>
<script src="/vendors/bootstrap/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" type="text/javascript"></script>
</body>
</html>
Loading

0 comments on commit 734c14c

Please sign in to comment.