diff --git a/.gitignore b/.gitignore
index 41bfdb2..a8ed72b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# do not publish live config file
-config.php
+config/*
+!config/config.php.default
# do not publish macOS blergh
._*
diff --git a/.htaccess b/.htaccess
index f59395a..d615954 100644
--- a/.htaccess
+++ b/.htaccess
@@ -2,6 +2,22 @@
RewriteEngine On
RewriteBase /
+ # prevent access to certain locations
+ RewriteRule ^\.git(\/.*)?$ - [R=404,L]
+ RewriteRule ^\.gitattributes$ - [R=404,L]
+ RewriteRule ^\.gitignore$ - [R=404,L]
+ RewriteRule ^\.htaccess$ - [R=404,L]
+ RewriteRule ^actions(\/.*)?$ - [R=404,L]
+ RewriteRule ^CHANGELOG\.md$ - [R=404,L]
+ RewriteRule ^config(\/.*)?$ - [R=404,L]
+ RewriteRule ^ENCRYPTION\.md$ - [R=404,L]
+ RewriteRule ^lib(\/.*)?$ - [R=404,L]
+ RewriteRule ^LICENSE$ - [R=404,L]
+ RewriteRule ^pages(\/)?$ - [R=404,L]
+ RewriteRule ^README\.md$ - [R=404,L]
+ RewriteRule ^template(\/.*)?$ - [R=404,L]
+
+ # single entrypoint
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php [L]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e4a6fb..0c3edd7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+# 0.20b0 (2019-10-18)
+
+* rewrote the application to use OpenSSL instead of GPG fixing indirect integrity vulnerabilities
+* rewrote the client-side encryption to be based on the [Web Cryptography API](https://www.w3.org/TR/WebCryptoAPI/)
+* added `encryption.md` to describe the details of the newly implemented encryption scheme
+* moved configuration file into the subfolder `config/`
+* renamed folder `libs/` to `lib/`
+* introduced the possibility to have key roll-overs (encrypt with new key but decrypt with new and old key)
+* updated Bootstrap to version 3.4.1
+* updated jQuery to version 3.4.1
+* **Beware:** Decrypting old GPG-based URLs is not possible with this version. Due to the new encryption scheme v01 the first 48 bytes of a URL will be the same for all generated URLs therefore it is possible to distinguish with high certainty between old and new URLs so that it possible for you to redirect old URLs to a read-only instance.
+
# 0.14b0 (2019-10-28)
* introduced a read-only mode to simplify the migration to the upcoming release
diff --git a/ENCRYPTION.md b/ENCRYPTION.md
new file mode 100644
index 0000000..b6d98ff
--- /dev/null
+++ b/ENCRYPTION.md
@@ -0,0 +1,138 @@
+# Shared-Secrets Encryption Schemes
+
+Shared-Secrets uses two encryption schemes - one for the client-side encryption within the browser, one for the server-side encryption. The client-side encryption prevents the server from learning the actual secret while the server-side encryption prevents anyone else from being able to decrypt the secret. This way the read-once property of the secret sharing links can be enforced.
+
+The encryption schemes are named after their version field (which is the first byte of every encrypted message). Currently schemes **v00** (client-side encryption) and **v01** (server-side encryption) are defined.
+
+## Encryption Scheme v00
+
+Encryption scheme v00 is a password-based Encrypt-then-MAC (EtM) scheme which uses AES-256-CTR as the encryption algorithm and HMAC-SHA-256 as the MAC algorithm. The key to derive the encryption key and the message authentication key is derived via a 10.000 rounds PBKDF2-SHA-256 on the password and a 256 bits long random salt.
+
+### Message Format
+
+Messages in the v00 format have the following format:
+
+```
+[version:01][salt:32][nonce:16][message:nn][mac:32]
+```
+
+### Message Fields
+
+Messages in the v00 format have the following fields:
+
+* **version** is 1 byte in size and **MUST** have the value `00h`
+* **salt** is 32 bytes in size and **SHOULD** contain a cryptographically secure random number
+* **nonce** is 16 bytes in size and **SHOULD** contain the UNIX timestamp as the first 8 bytes and zero bytes as the second 8 bytes
+* **message** is the AES-256-CTR encrypted message
+* **mac** is 32 bytes in size and **MUST** contain the HMAC-SHA-256 MAC of all previous fields in their given order
+
+### Key Derivation
+
+Messages in the v00 format use the following keys:
+
+* **salt** is a cryptographically secure random number
+* **key** is derived from the given password and **salt** using a 10.000 rounds PBKDF2-SHA-256
+* **enckey** is derived from **key** as the key and the string `enc` as the message using HMAC-SHA-256
+* **mackey** is derived from **key** as the key and the string `mac` as the message using HMAC-SHA-256
+
+### Key Usage
+
+Keys in the v00 format have the following purposes:
+
+* **enckey** in combination with **nonce** are used to encrypt the message using AES-256-CTR
+* **mackey** is used as the key to calculate the MAC of the message `[version:01][salt:32][nonce:16][message:nn]` using HMAC-SHA-256
+
+### Example
+
+The following Bash command encrypts a message with a given password using the above encryption scheme v00. A current version of OpenSSL/LibreSSL and the tool [nettle-pbkdf2](http://manpages.ubuntu.com/manpages/en/man1/nettle-pbkdf2.1.html) are needed:
+
+```
+# version 00 symmetric encryption
+MESSAGE="message to encrypt" &&
+PASSWORD="password" &&
+VERSION="00" &&
+NONCE=$(printf "%016x0000000000000000" "$(date +%s)") &&
+SALT=$(openssl rand -hex 32) &&
+KEY=$(echo -n "$PASSWORD" | nettle-pbkdf2 -i 10000 -l 32 --raw --hex-salt "$SALT" | xxd -p | tr -d "\n") &&
+ENCKEY=$(echo -n "enc" | openssl dgst -sha256 -mac "HMAC" -macopt "hexkey:$KEY" -binary | xxd -p | tr -d "\n") &&
+MACKEY=$(echo -n "mac" | openssl dgst -sha256 -mac "HMAC" -macopt "hexkey:$KEY" -binary | xxd -p | tr -d "\n") &&
+ENCMESSAGE=$(echo -n "$MESSAGE" | openssl enc -aes-256-ctr -K "$ENCKEY" -iv "$NONCE" -nopad | xxd -p | tr -d "\n") &&
+MACMESSAGE="$VERSION$SALT$NONCE$ENCMESSAGE" &&
+MAC=$(echo -n "$MACMESSAGE" | xxd -r -p | openssl dgst -sha256 -mac "HMAC" -macopt "hexkey:$MACKEY" -binary | xxd -p | tr -d "\n") &&
+FULLMESSAGE="$MACMESSAGE$MAC" &&
+echo "$FULLMESSAGE"
+```
+
+## Encryption Scheme v01
+
+Encryption scheme v01 is a key-based Encrypt-then-MAC (EtM) scheme which uses AES-256-CTR as the encryption algorithm and HMAC-SHA-256 as the MAC algorithm. The key to derive the encryption key and the message authentication key is randomly generated.
+
+### Message Format
+
+Messages in the v01 format have the following format:
+
+```
+[version:01][rsakeycount:02][rsakeyid:32][rsakeylength:02][rsakey:mm][...][rsakeyid:32][rsakeylength:02][rsakey:mm][nonce:16][message:nn][mac:32]
+```
+
+### Message Fields
+
+Messages in the v01 format have the following fields:
+
+* **version** is 1 byte in size and **MUST** have the value `01h`
+* **rsakeycount** is 2 bytes in size and **MUST** denote the number of upcoming RSA key blocks
+* **rsakeyid** is 32 bytes in size and **MUST** contain the SHA-256 hash of the DER-encoded RSA public key that was used to encrypt the upcoming RSA key
+* **rsakeylength** is 2 bytes in size and **MUST** denote the length of the upcoming RSA key
+* **rsakey** has the length of the previous **rsakeylength** field and **MUST** contain the RSA-encrypted key that was used to derive the encryption and message autentication key for the RSA key denoted by the previous **rsakeyid** field
+* **nonce** is 16 bytes in size and **SHOULD** contain the UNIX timestamp as the first 8 bytes and zero bytes as the second 8 bytes
+* **message** is the AES-256-CTR encrypted message
+* **mac** is 32 bytes in size and **MUST** contain the HMAC-SHA-256 MAC of all previous fields in their given order
+
+### Key Derivation
+
+Messages in the v01 format use the following keys:
+
+* **key** is cryptographically secure random number
+* **enckey** is derived from **key** as the key and the string `enc` as the message using HMAC-SHA-256
+* **mackey** is derived from **key** as the key and the string `mac` as the message using HMAC-SHA-256
+* **rsakey** is derived by RSA-encrypting **key** with an RSA public key
+
+The required RSA public key can be generated as follows:
+
+```
+openssl genrsa -out ./rsa.priv 2048
+openssl rsa -in ./rsa.priv -pubout -outform PEM > ./rsa.pub
+```
+
+### Key Usage
+
+Keys in the v00 format have the following purposes:
+
+* **enckey** in combination with **nonce** are used to encrypt the message using AES-256-CTR
+* **mackey** is used as the key to calculate the MAC of the message `[version:01][rsakeycount:02][rsakeyid:32][rsakeylength:02][rsakey:mm][...][rsakeyid:32][rsakeylength:02][rsakey:mm][nonce:16][message:nn]` using HMAC-SHA-256
+
+### Example
+
+The following Bash command encrypts a message with a given password using the above encryption scheme v01. A current version of OpenSSL/LibreSSL is needed:
+
+```
+# version 01 hybrid encryption
+MESSAGE="message to encrypt" &&
+RSAKEYFILE="./rsa.pub" &&
+URLPREFIX="https://secrets.syseleven.de/" &&
+RSAKEYCOUNT="0001" &&
+VERSION="01" &&
+NONCE=$(printf "%016x0000000000000000" "$(date +%s)") &&
+KEY=$(openssl rand -hex 32) &&
+ENCKEY=$(echo -n "enc" | openssl dgst -sha256 -mac "HMAC" -macopt "hexkey:$KEY" -binary | xxd -p | tr -d "\n") &&
+MACKEY=$(echo -n "mac" | openssl dgst -sha256 -mac "HMAC" -macopt "hexkey:$KEY" -binary | xxd -p | tr -d "\n") &&
+RSAKEY=$(echo -n "$KEY" | xxd -r -p | openssl rsautl -encrypt -oaep -pubin -inkey "$RSAKEYFILE" -keyform PEM | xxd -p | tr -d "\n") &&
+RSAKEYID=$(openssl rsa -pubin -in "$RSAKEYFILE" -pubout -outform DER 2>/dev/null | openssl dgst -sha256 -binary | xxd -p | tr -d "\n") &&
+RSAKEYLENGTH=$(echo -n "$RSAKEY" | xxd -r -p | wc -c) &&
+RSAKEYLENGTH=$(printf "%04x" "$RSAKEYLENGTH") &&
+ENCMESSAGE=$(echo -n "$MESSAGE" | openssl enc -aes-256-ctr -K "$ENCKEY" -iv "$NONCE" -nopad | xxd -p | tr -d "\n") &&
+MACMESSAGE="$VERSION$RSAKEYCOUNT$RSAKEYID$RSAKEYLENGTH$RSAKEY$NONCE$ENCMESSAGE" &&
+MAC=$(echo -n "$MACMESSAGE" | xxd -r -p | openssl dgst -sha256 -mac "HMAC" -macopt "hexkey:$MACKEY" -binary | xxd -p | tr -d "\n") &&
+FULLMESSAGE="$MACMESSAGE$MAC" &&
+echo "FULLMESSAGE"
+```
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index b525959..de25011 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016, SysEleven GmbH
+Copyright (c) 2016-2019, SysEleven GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
index 57916a3..a46be25 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ To protect your secret from getting known by the server or an attacker, you can
### Share a Secret
-Simply enter your secret on the default page of the Shared-Secrets service. You can decide to password-protect the entered secret before sending it to the server by checking the "Password-protected:" box, entering your password and pressing the "Protect!" button. After that, press the "Share the Secret!" button. The secret will be GPG-encrypted and converted into a secret sharing link.
+Simply enter your secret on the default page of the Shared-Secrets service. You can decide to password-protect the entered secret before sending it to the server by checking the "Password-protected:" box, entering your password and pressing the "Protect!" button. After that, press the "Share the Secret!" button. The secret will be encrypted and converted into a secret sharing link.
Secret sharing links can also be created by using a simple POST request:
```
@@ -33,7 +33,7 @@ curl -X POST -d "plain"
### Requirements
-Shared-Secrets is based on MariaDB 10.0, Nginx 1.10 and PHP 7.0, but should also work with MySQL, Apache and earlier versions of PHP. GPG encryption is supported by directly calling the gpg binary. Previously, using the [GnuPG PECL package](https://pecl.php.net/package/gnupg) has been prefered mechanism due to its cleaner interface - however this interface is currently forcefully deactivated due to security concerns.
+Shared-Secrets is based on MariaDB 10.0, Nginx 1.10 and PHP 7.0, but should also work with MySQL, Apache and earlier versions of PHP. Encrypted is done via the OpenSSL integration of PHP.
### Nginx Setup
@@ -61,6 +61,21 @@ server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
+ # prevent access to certain locations
+ location ~ ^\/\.git(\/.*)?$ { return 404; }
+ location ~ ^\/\.gitattributes$ { return 404; }
+ location ~ ^\/\.gitignore$ { return 404; }
+ location ~ ^\/\.htaccess$ { return 404; }
+ location ~ ^\/actions(\/.*)?$ { return 404; }
+ location ~ ^\/CHANGELOG\.md$ { return 404; }
+ location ~ ^\/config(\/.*)?$ { return 404; }
+ location ~ ^\/ENCRYPTION\.md$ { return 404; }
+ location ~ ^\/lib(\/.*)?$ { return 404; }
+ location ~ ^\/LICENSE$ { return 404; }
+ location ~ ^\/pages(\/.*)?$ { return 404; }
+ location ~ ^\/README\.md$ { return 404; }
+ location ~ ^\/template(\/.*)?$ { return 404; }
+
# Your configuration comes here:
# ...
}
@@ -82,37 +97,18 @@ add_header X-XSS-Protection "1; mode=block";
Shared-Secrets uses a single-table database to store who did retrieve which secret at what point in time. No actual secret content is stored. (The logging of IP addresses is disabled through the configuration parameter LOG_IP_ADDRESS by default.):
```
-CREATE TABLE secrets ( fingerprint VARCHAR(64) PRIMARY KEY, ip VARCHAR(46), time TIMESTAMP );
-```
-
-### GPG Setup
-
-You have to have a somewhat recent GPG version installed on the server. Furthermore you have to generate and import the private and public key of the service. It is recommended to generate the GPG keypair locally and only upload the key material that is really necessary. You can use a so-called notebook keyring (which does not include the certifying/primary private key) to reduce the risk of a full compromise of the used GPG keypair:
+CREATE TABLE secrets ( fingerprint VARCHAR(64) PRIMARY KEY, time TIMESTAMP );
```
-# generate a GPG keypair, set a passphrase so that you can export the private encryption subkey later
-gpg --gen-key
-# read the fingerprint of the generated key, replace with the e-mail address that has been used for the key,
-# the fingerprint will be shown with spaces for better readability - these have to be removed when configuring the software
-gpg --with-fingerprint --list-keys
+### Encryption Setup
-# export the private encryption subkey and the public key
-gpg --export-secret-subkeys --armor >./private.asc
-gpg --export --armor >./public.asc
+You should generate a fresh RSA key pair with a minimum key size of 2048 bits:
-# import the GPG keys on the server with the user that will execute GPG
-sudo -u www-data -H gpg --import ./private.asc
-sudo -u www-data -H gpg --import ./public.asc
```
-
-Newer GPG versions disable stdin for key passphrases.
-
+openssl genrsa -out ./rsa.key 2048
```
-#in ~/.gnupg/gpg.conf (home of the user that will execute GPG) add:
-use-agent
-pinentry-mode loopback
-```
+**Beware:** You should place this file in a location so that it is not accessible through the webserver.
### Service Setup
@@ -124,11 +120,7 @@ It is strongly recommended to use TLS to protect the connection between the serv
## Attributions
-* [asmCrypto](https://github.com/vibornoff/asmcrypto.js): for providing PBKDF2 and AES functions
* [Bootstrap](https://getbootstrap.com): for providing an easy-to-use framework to build nice-looking applications
-* [buffer](https://github.com/feross/buffer): for providing Base64 encoding and array conversion functions
-* [GnuPG](https://www.gnupg.org): for providing a reliable tool for secure communication
-* [GnuPG PECL package](https://pecl.php.net/package/gnupg): for providing a clean interface to GnuPG (*currently not supported*)
* [html5shiv](https://github.com/aFarkas/html5shiv): for handling Internet Explorer compatibility stuff
* [jQuery](https://jquery.com): for just existing
* [Katharina Franz](https://www.katharinafranz.com): for suggesting Bootstrap as an easy-to-use framework to build nice-looking applications
@@ -137,7 +129,6 @@ It is strongly recommended to use TLS to protect the connection between the serv
## ToDo
* switch to a more personalized design (current design is taken from [here](https://github.com/twbs/bootstrap/tree/master/docs/examples/starter-template))
-* implement an alternative encryption scheme based on AES instead of GPG (fewer dependencies)
* implement an expiry date functionality
## License
diff --git a/actions/how.php b/actions/how.php
index 35d6c84..2be29b9 100644
--- a/actions/how.php
+++ b/actions/how.php
@@ -5,4 +5,3 @@
# dummy script without any content
-?>
diff --git a/actions/imprint.php b/actions/imprint.php
index 1e4da59..9e8d5c4 100644
--- a/actions/imprint.php
+++ b/actions/imprint.php
@@ -11,4 +11,3 @@ function redirect_page($target_url) {
header("Location: ".$target_url);
}
-?>
diff --git a/actions/pub.php b/actions/pub.php
new file mode 100644
index 0000000..cee7ec5
--- /dev/null
+++ b/actions/pub.php
@@ -0,0 +1,22 @@
+ERROR: SECRET HAS ALREADY BEEN RETRIEVED.";
- }
- }
+ if (null !== $decrypted_secret) {
+ if ($link = mysqli_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, MYSQL_PORT)) {
+ try {
+ if ($statement = mysqli_prepare($link, MYSQL_WRITE)) {
+ $fingerprint = bin2hex($fingerprint);
+
+ if (mysqli_stmt_bind_param($statement, "s", $fingerprint)) {
+ if (mysqli_stmt_execute($statement)) {
+ if (1 === mysqli_affected_rows($link)) {
+ $result = $decrypted_secret;
+ } else {
+ $error = "Secret has already been retrieved.";
+ }
+ } else {
+ if (DEBUG_MODE) {
+ $error = "Insert statement could not be executed";
}
}
+ } else {
+ if (DEBUG_MODE) {
+ $error = "Insert statement parameters could not be bound.";
+ }
}
-
-
- # close select statement
- if (null !== $select) {
- mysqli_stmt_close($select);
+ } else {
+ if (DEBUG_MODE) {
+ $error = "Insert statement could not be prepared.";
}
}
-
- # close mysql connection
- mysqli_close($mysql);
+ } finally {
+ mysqli_close($link);
+ }
+ } else {
+ if (DEBUG_MODE) {
+ $error = "Database connection could not be established.";
}
}
+ } else {
+ if (DEBUG_MODE) {
+ $error = "Decryption failed: $decrypt_error";
+ }
+ }
+ } else {
+ if (DEBUG_MODE) {
+ $error = "The secret must not be empty.";
}
}
- # set default result if non is given
- if (null === $result) {
- $result = "ERROR: AN UNKNOWN ERROR OCCURED.";
+ # set default error if non is given
+ if ((null === $result) && (false === $error)) {
+ $error = "An unknown error occured.";
}
return $result;
}
-?>
diff --git a/actions/share.php b/actions/share.php
index e6236c6..ed3b48c 100644
--- a/actions/share.php
+++ b/actions/share.php
@@ -3,41 +3,54 @@
# prevent direct access
if (!defined("SYS11_SECRETS")) { die(""); }
- function share_secret($secret) {
+ function share_secret($secret, &$error = null) {
$result = null;
+ $error = false;
- # only proceed when the read-only mode is not set
- if ((!defined("READ_ONLY")) || (!READ_ONLY)) {
- # only proceed when the secret is not empty
- if (!empty($secret)) {
- # only proceed when the secret is not too long
- if (MAX_PARAM_SIZE >= strlen($secret)) {
- if (GNUPG_PECL) {
- $encrypted_secret = encrypt_pecl($secret, GPG_KEY_FINGERPRINT, GPG_HOME_DIR);
- } else {
- $encrypted_secret = encrypt($secret, GPG_KEY_FINGERPRINT, GPG_HOME_DIR);
- }
+ # only proceed when the secret is not empty
+ if (!empty($secret)) {
+ # only proceed when the secret is not too long
+ if (MAX_PARAM_SIZE >= strlen($secret)) {
+ # for shared-secrets we only support encryption with one key
+ $keys = array_keys(RSA_PRIVATE_KEYS);
+ $pubkey = open_pubkey(RSA_PRIVATE_KEYS[$keys[count($keys)-1]]);
+ if (null !== $pubkey) {
+ try {
+ $recipients = [$pubkey];
+ try {
+ $encrypted_secret = encrypt_v01($secret, $recipients, $encrypt_error);
+ } finally {
+ zeroize_array($recipients);
+ }
- if (null !== $encrypted_secret) {
- # return the secret sharing URL
- $result = htmlentities(SECRET_SHARING_URL.apache_bugfix_encode(url_base64_encode(base64_encode($encrypted_secret))));
+ if (null !== $encrypted_secret) {
+ # return the secret sharing URL
+ $result = get_secret_url($encrypted_secret);
+ } else {
+ if (DEBUG_MODE) {
+ $error = "Encryption failed: $encrypt_error";
+ }
+ }
+ } finally {
+ openssl_pkey_free($pubkey);
}
} else {
- $result = "ERROR: THE SECRET MUST BE SMALLER THAN ".MAX_PARAM_SIZE." CHARACTERS.";
+ if (DEBUG_MODE) {
+ $error = "Public key could not be read.";
+ }
}
} else {
- $result = "ERROR: THE SECRET MUST NOT BE EMPTY.";
+ $error = "The secret must at most be ".MAX_PARAM_SIZE." characters long.";
}
} else {
- $result = "ERROR: THE CREATION OF SECRET URLS IS DISABLED.";
+ $error = "The secret must not be empty.";
}
- # set default result if non is given
- if (null === $result) {
- $result = "ERROR: AN UNKNOWN ERROR OCCURED.";
+ # set default error if non is given
+ if ((null === $result) && (false === $error)) {
+ $error = "An unknown error occured.";
}
return $result;
}
-?>
diff --git a/config.php.default b/config.php.default
deleted file mode 100644
index e6be318..0000000
--- a/config.php.default
+++ /dev/null
@@ -1,61 +0,0 @@
-");
-
- # this is the GPG home dir that contains the GPG keyring,
- # this has to be set in order for the GnuPG PECL to be used
- define("GPG_HOME_DIR", null);
-
- # this is the file that contains the passphrase for the
- # GPG private encryption key, the file should only contain
- # a single line with the passphrase
- define("GPG_PASSPHRASE_FILE", null);
-
- # this is the full path to the secret sharing service,
- # the encrypted secret will be appended to this string,
- # do NOT forget the trailing slash
- define("SECRET_SHARING_URL", "https://localhost.local/");
-
- # this is the title of the service, it is shown in header
- # of all pages
- define("SERVICE_TITLE", "Shared-Secrets");
-
- # this is the default timezone for the execution of the script
- define("DEFAULT_TIMEZONE", "Europe/Berlin");
-
- # this is the MySQL configuration, do not forget to create
- # the corresponding database and the following table:
- # > CREATE TABLE secrets ( fingerprint VARCHAR(64) PRIMARY KEY, ip VARCHAR(46), time TIMESTAMP );
- define("MYSQL_HOST", "localhost");
- define("MYSQL_PORT", 3306);
- define("MYSQL_USER", "");
- define("MYSQL_PASS", "");
- define("MYSQL_DB", "");
-
- # this is the URL the imprint link shall forward to
- define("IMPRINT_URL", "https://localhost.local/");
-
- # this is the configuration to either enable or disable the
- # password-protection of secrets on top of the server-side
- # encryption, set this configuration value to true to enable
- # the optional password-protection
- define("ENABLE_PASSWORD_PROTECTION", true);
-
- # this is the configuration to either enable or disable the
- # logging of IP addresses for retrieved secrets, set this
- # configuration value to false to disable the logging of
- # IP addresses
- define("LOG_IP_ADDRESS", false);
-
- # this is the configuration to either enable or disable the
- # read-only mode of the instance, set this to simplify the
- # migration to the new GPG-less version
- define("READ_ONLY", false);
-
-?>
diff --git a/config/config.php.default b/config/config.php.default
new file mode 100644
index 0000000..cecdc50
--- /dev/null
+++ b/config/config.php.default
@@ -0,0 +1,38 @@
+ CREATE TABLE secrets ( fingerprint VARCHAR(64) PRIMARY KEY, time TIMESTAMP );
+ define("MYSQL_HOST", "localhost");
+ define("MYSQL_PORT", 3306);
+ define("MYSQL_USER", "");
+ define("MYSQL_PASS", "");
+ define("MYSQL_DB", "");
+
+ # this enables or disables the debug mode of the instance
+ define("DEBUG_MODE", true);
+
+ # this is the default timezone for the execution of the script
+ define("DEFAULT_TIMEZONE", "Europe/Berlin");
+
+ # this enables or disables the read-only mode of the instance
+ define("READ_ONLY", false);
+
diff --git a/index.php b/index.php
index 9a66a68..43a7619 100644
--- a/index.php
+++ b/index.php
@@ -1,6 +1,6 @@
diff --git a/lib/shared-secrets.def.php b/lib/shared-secrets.def.php
new file mode 100644
index 0000000..e10c15e
--- /dev/null
+++ b/lib/shared-secrets.def.php
@@ -0,0 +1,52 @@
+
diff --git a/libs/shared-secrets.exec.php b/libs/shared-secrets.exec.php
deleted file mode 100644
index 692c685..0000000
--- a/libs/shared-secrets.exec.php
+++ /dev/null
@@ -1,235 +0,0 @@
- array("pipe", "r"), # STDIN
- 1 => array("pipe", "w"), # STDOUT
- 2 => array("pipe", "w")); # STDERR
-
- # open the process handle
- $handle = proc_open($command, $descriptors, $pipes);
- if (false !== $handle) {
- # write stdin content and close the pipe
- if (null !== $stdin) {
- # prevent stdio from blocking
- #stream_set_blocking($pipes[0], false);
-
- # write stdin in 1kb chunks to prevent blocking
- $counter = 0;
- while ($counter < strlen($stdin)) {
- $write_size = strlen($stdin)-$counter;
- if ($write_size > STREAM_BUFFER) {
- $write_size = STREAM_BUFFER;
- }
-
- $bytes_written = fwrite($pipes[0], substr($stdin, $counter, $write_size));
- fflush($pipes[0]);
-
- $counter += $bytes_written;
- }
- }
- fclose($pipes[0]);
-
- # read stdout content and close the pipe
- $stdout = stream_get_contents($pipes[1]);
- fclose($pipes[1]);
-
- # read stderr content and close the pipe
- $stderr = stream_get_contents($pipes[2]);
- fclose($pipes[2]);
-
- # close the process and get the return value
- $result = proc_close($handle);
- }
-
- return $result;
- }
-
- ########## GPG FUNCTIONS ##########
-
- # decrypts the $content
- function decrypt($content, $homedir, $passphrase_file) {
- $result = null;
-
- if (is_string($content)) {
- # append homedir and passphrase file if they are given
- $cmd_append = "";
- if (is_dir($homedir)) {
- $cmd_append .= " --homedir ".escapeshellarg($homedir);
- }
- if (is_file($passphrase_file)) {
- $cmd_append .= " --batch --passphrase-file ".escapeshellarg($passphrase_file);
- }
-
- $ret = execute_with_stdio("LANG=en gpg --quiet --keyid-format LONG --no-tty ".$cmd_append." --output - --decrypt -",
- $content,
- $stdout,
- $stderr);
- if (0 === $ret) {
- # check that the decrypted message has been integrity-protected,
- # older versions of GnuPG set the return code to 0 when this warning occurs
- if (false === stripos($stderr, GPG_MDC_ERROR)) {
- $result = $stdout;
- }
- }
- }
-
- return $result;
- }
-
- # decrypts the $content using the GnuPG PECL
- function decrypt_pecl($content, $recipient, $homedir, $passphrase_file) {
- $result = null;
- if (is_string($content)) {
- # set the GnuPG home dir
- if (is_dir($homedir)) {
- putenv("GNUPGHOME=".$homedir);
- }
- # load the GnuPG passphrase from file
- $passphrase = null;
- if (is_file($passphrase_file)) {
- $passphrase = file_get_contents($passphrase_file);
- # reset the passphrase to null if it could not be read
- if (false === $passphrase) {
- $passphrase = null;
- }
- }
- # initialize GnuPG
- $gnupg = gnupg_init();
- # only proceed when the GnuPG init worked
- if ($gnupg) {
- # set error mode so that we handle them ourselves
- gnupg_seterrormode($gnupg, GNUPG_ERROR_SILENT);
- # disable ASCII armoring
- if (gnupg_setarmor($gnupg, 0)) {
- # make the key known that we use for decryption
- if (gnupg_adddecryptkey($gnupg, $recipient, $passphrase)) {
- # decrypt the $content
- $ret = gnupg_decrypt($gnupg, $content);
- if (false !== $ret) {
- $result = $ret;
- }
- }
- }
- }
- }
- return $result;
- }
-
- # encrypts the $content for the $recipient
- function encrypt($content, $recipient, $homedir) {
- $result = null;
-
- if (is_string($content) && (is_string($recipient))) {
- # append homedir if it is given
- $cmd_append = "";
- if (is_dir($homedir)) {
- $cmd_append .= " --homedir ".escapeshellarg($homedir);
- }
-
- $ret = execute_with_stdio("LANG=en gpg --quiet --keyid-format LONG --no-tty --recipient ".escapeshellarg($recipient)." --trust-model always --yes ".$cmd_append." --output - --encrypt -",
- $content,
- $stdout,
- $stderr);
- if (0 === $ret) {
- $result = $stdout;
- }
- }
-
- return $result;
- }
-
- # encrypts the $content for the $recipient using the GnuPG PECL
- function encrypt_pecl($content, $recipient, $homedir) {
- $result = null;
-
- if (is_string($content) && is_string($recipient)) {
- # set the GnuPG home dir
- if (is_dir($homedir)) {
- putenv("GNUPGHOME=".$homedir);
- }
-
- # initialize GnuPG
- $gnupg = gnupg_init();
-
- # only proceed when the GnuPG init worked
- if ($gnupg) {
- # set error mode so that we handle them ourselves
- gnupg_seterrormode($gnupg, GNUPG_ERROR_SILENT);
-
- # disable ASCII armoring
- if (gnupg_setarmor($gnupg, 0)) {
- # make the key known that we use for encryption
- if (gnupg_addencryptkey($gnupg, $recipient)) {
- # encrypt the $content
- $ret = gnupg_encrypt($gnupg, $content);
-
- if (false !== $ret) {
- $result = $ret;
- }
- }
- }
- }
- }
-
- return $result;
- }
-
-?>
diff --git a/pages/how/get.php b/pages/how/get.php
index 8d61bd8..4820bc9 100644
--- a/pages/how/get.php
+++ b/pages/how/get.php
@@ -12,29 +12,23 @@
# prevents cache hits with wrong CSS
$cache_value = md5_file(__FILE__);
- # handle indentation within shell command
- $indentation = "";
- $space_count = 64-strlen(SECRET_SHARING_URL);
- if (0 < $space_count) {
- $indentation = str_repeat(" ", $space_count);
- }
-
?>
Short description of this service.
-
This secret sharing service is based on GPG encryption. When creating a new secret sharing link, the secret itself is encrypted via GPG. The result of the GPG encryption is URL-safe Base64 encoded and prepended with the URL of this website. When the secret sharing link is called, the URL-safe Base64 encoded GPG message is decrypted and the result of the decryption is displayed on the website. Additionally, the fingerprint of the URL-safe Base64 encoded GPG message is stored in a database to prevent it from being displayed more than once.
-
- You can build your own secret sharing link by following some basic steps.
+
This secret sharing service is based on AES and RSA encryption. When creating a new secret sharing link, a random key is generated which is used to encrypt the secret using AES. The key itself is then encrypted using RSA. The result of the encryption is URL-safe Base64 encoded and prepended with the URL of this website. When the secret sharing link is called, the URL-safe Base64 encoded message is decrypted and the result of the decryption is displayed on the website. Additionally, the fingerprint of the encrypted message is stored in a database to prevent it from being displayed more than once.
Get the correct public key.
First of all you have to retrieve the correct public key to encrypt your secret:
-
Short description of the password-protection feature.
-
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.
-
+
When using the password-protection feature, the secret is encrypted locally in your browser using AES-256-CTR. The encryption key is derived from the entered password and a dynamically generated salt using the PBKDF2-SHA-256 algorithm. 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.
+
+
Shell example.
+
You can use the following shell command to encrypt a message and by compatible with the browser-based encryption. You will need the additional tool nettle-pbkdf2 for this:
+