diff --git a/alternate/readme.md b/alternate/readme.md
new file mode 100644
index 0000000..5229830
--- /dev/null
+++ b/alternate/readme.md
@@ -0,0 +1,22 @@
+#### Here is the idea:
+ 1. This script stores and returns binary blobs, so really, anything.
+ 2. Every blob has extra attributes which can be set by additional parameters on creation, these are:
+ 1. id (required), uniquely identifies each blob when combined with key
+ 2. key (required), used to encrypt/decrypt blob on storage/access
+ 3. file, the blob to encrypt and store, ideally this is already encrypted with a local key that never leaves your computer before sent to this script.
+ 4. time-to-live (HOURS where 1 => time-to-live <= 24), if it hasn't been successfully accessed within X hours, all traces of it will be securely deleted (by a cronjob, not in PHP)
+ 5. tmp (true/false), stores the blob in in-memory storage, with the hope that if the machine is powered off everything disappears
+ 3. Sending in only an id and key will decrypt the blob and send it back to the browser, if nothing exists for that id/key, a new blob will be created from $new_blob_source with sent in parameters or defaults, stored, and sent back.
+ 4. Sending in an id, key, and file will save (and overwrite if id/key was set before) the file to be served back when requested again, with optionally overridden defaults based on the other parameters sent in.
+ 5. Every time a blob is successfully accessed (correct id and key), the time will be saved. This will be used by the secure deleting cronjob.
+
+I am looking for feedback on how *secure* this idea is, if there are flaws in the approach or potential weaknesses I don't see, and ways to improve it.
+
+Any improvements that can be made in the reference implementations will be appreciated as well.
+
+#### In this repo
+
+ 1. secureblob.php - Reference implementation in PHP
+ 2. secureblob_cron.sh - Reference implementation of cleaning cronjob
+ 3. secureblob_up.sh - Upload script to test reference implementations
+ 4. agpl-3.0.txt - License all code is released under
diff --git a/alternate/secureblob.php b/alternate/secureblob.php
new file mode 100644
index 0000000..fec6781
--- /dev/null
+++ b/alternate/secureblob.php
@@ -0,0 +1,258 @@
+.
+*/
+global $blob_path, $tmp_blob_path, $new_blob_source, $default_content_type, $max_size, $min_size;
+$blob_path = '/tmp/secureblob/';
+$tmp_blob_path = '/run/shm/secureblob/';
+$new_blob_source = '/dev/urandom';
+//$new_blob_source = '/dev/zero';
+//$new_blob_source = '/run/shm/bob';
+$default_content_type = 'application/octet-stream';
+//$max_size = 4 * 1024 * 1024; // 4mb
+$max_size = 64 * 1024; // 64kb
+$min_size = 16;
+
+// params for my encrypt/decrypt/stretch_key functions, if you write your own functions, ignore these params
+global $mcrypt_cipher, $mcrypt_mode, $mcrypt_rand_src, $pbkdf2_hash, $pbkdf2_iterations, $salt_length;
+$mcrypt_cipher = MCRYPT_RIJNDAEL_128;
+//$mcrypt_cipher = MCRYPT_RIJNDAEL_256;
+//$mcrypt_cipher = MCRYPT_BLOWFISH;
+$mcrypt_mode = MCRYPT_MODE_CBC;
+$mcrypt_rand_src = MCRYPT_DEV_RANDOM;
+$pbkdf2_hash = 'sha512';
+$pbkdf2_iterations = 65536;
+$salt_length = 32;
+
+// todo: include config file
+
+function encrypt($decrypted, $password) {
+ global $mcrypt_cipher, $mcrypt_mode, $mcrypt_rand_src, $salt_length;
+ //echo "decrypted: '$decrypted'
\n";
+ srand();
+ // first generate a random $salt_length character salt
+ $salt_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZâ"\'~!@#$%^&*(){}[],./?';
+ $salt_chars_len = strlen($salt_chars);
+ $salt = '';
+ for ($i = 0; $i < $salt_length; $i++)
+ $salt .= $salt_chars[rand(0, $salt_chars_len)];
+ //echo "salt: '$salt'
\n";
+ // stretch the key
+ $key = stretch_key($password, $salt, mcrypt_get_key_size($mcrypt_cipher, $mcrypt_mode));
+ // Build $iv and $iv_base64. We use a block size of 128 bits (AES compliant) and CBC mode. (Note: ECB mode is inadequate as IV is not used.)
+ $iv = mcrypt_create_iv(mcrypt_get_iv_size($mcrypt_cipher, $mcrypt_mode), $mcrypt_rand_src);
+ $iv_base64 = base64_encode($iv);
+ //echo "iv_base64: '$iv_base64'
\n";
+ // Encrypt $decrypted plus a random character to be removed so we know the last character isn't \0
+ $encrypted = base64_encode(mcrypt_encrypt($mcrypt_cipher, $key, $decrypted . $salt_chars[rand(0, $salt_chars_len)], $mcrypt_mode, $iv));
+ //echo "encrypted: '$encrypted'
\n";
+ // We're done!
+ return "$salt:$mcrypt_cipher:$mcrypt_mode:$iv_base64:$encrypted";
+}
+
+function decrypt($encrypted, $password, $check_integrity = true) {
+ $encrypted = explode(':', $encrypted); // salt, mcrypt_cipher, mcrypt_mode, iv_base64, encrypted_base64
+ // retrieve $salt from $encrypted
+ $salt = $encrypted[0];
+ $mcrypt_cipher = $encrypted[1];
+ $mcrypt_mode = $encrypted[2];
+ // stretch the key
+ $key = stretch_key($password, $salt, mcrypt_get_key_size($mcrypt_cipher, $mcrypt_mode));
+ // Retrieve $iv which is base64_decoded.
+ $iv = base64_decode($encrypted[3]);
+ // Decrypt the data. rtrim won't corrupt the data because the last 32 characters are the md5 hash; thus any \0 character has to be padding.
+ $decrypted = rtrim(mcrypt_decrypt($mcrypt_cipher, $key, base64_decode($encrypted[4]), $mcrypt_mode, $iv), "\0\4");
+ // Remove the last character from $decrypted.
+ $decrypted = substr($decrypted, 0, -1);
+ // Yay!
+ return $decrypted;
+}
+function stretch_key($key, $salt, $max_key_size){
+ global $pbkdf2_hash, $pbkdf2_iterations;
+ //echo "max_key_size: '$max_key_size'
\n";
+ //return hash('SHA256', $salt . $key, true);
+ return pbkdf2($pbkdf2_hash, $key, $salt, $pbkdf2_iterations, $max_key_size, true);
+}
+function secure_delete_file($filename){
+ $size = filesize($filename);
+
+ $src = fopen('/dev/zero', 'rb');
+ $dest = fopen($filename, 'wb');
+
+ stream_copy_to_stream($src, $dest, $size);
+
+ fclose($src);
+ fclose($dest);
+ unlink($filename);
+}
+function secure_delete_folder($path) {
+ $it = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($path),
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
+ foreach ($it as $file) {
+ if (in_array($file->getBasename(), array('.', '..'))) {
+ continue;
+ } elseif ($file->isDir()) {
+ rmdir($file->getPathname());
+ } elseif ($file->isFile() || $file->isLink()) {
+ secure_delete_file($file->getPathname());
+ }
+ }
+ rmdir($path);
+}
+function write_to_file($fname, $contents, $mode = 'w') {
+ $fh = fopen($fname, $mode);
+ fwrite($fh, $contents);
+ fclose($fh);
+}
+function encrypt_write($folder, $key, $decrypted = NULL){
+ if($decrypted === NULL)
+ $decrypted = create_blob();
+ if(!file_exists($folder)) {
+ // create directory
+ mkdir($folder, 0700, true);
+ // write attributes
+ $time_to_live = setDefaultLimits('time-to-live', 1, 1, 24);
+ write_to_file($folder.'time-to-live', $time_to_live);
+ }
+ write_to_file($folder.'blob', encrypt($decrypted, $key), 'wb');
+ return $decrypted;
+}
+// creates a blob from $new_blob_source
+function create_blob(){
+ global $new_blob_source, $max_size, $min_size;
+ srand();
+ return file_get_contents($new_blob_source, false, NULL, 0, rand( (($max_size-$min_size)/2), $max_size)); // size is some random amount between halfway between max_size and min_size and max_size
+}
+/*
+ * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
+ * $algorithm - The hash algorithm to use. Recommended: SHA256
+ * $password - The password.
+ * $salt - A salt that is unique to the password.
+ * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
+ * $key_length - The length of the derived key in bytes.
+ * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
+ * Returns: A $key_length-byte key derived from the password and salt.
+ *
+ * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
+ *
+ * This implementation of PBKDF2 was originally created by https://defuse.ca
+ * With improvements by http://www.variations-of-shadow.com
+ */
+function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
+{
+ $algorithm = strtolower($algorithm);
+ if(!in_array($algorithm, hash_algos(), true))
+ trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
+ if($count <= 0 || $key_length <= 0)
+ trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
+
+ if (function_exists("hash_pbkdf2")) {
+ // The output length is in NIBBLES (4-bits) if $raw_output is false!
+ if (!$raw_output) {
+ $key_length = $key_length * 2;
+ }
+ return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
+ }
+
+ $hash_length = strlen(hash($algorithm, "", true));
+ $block_count = ceil($key_length / $hash_length);
+
+ $output = "";
+ for($i = 1; $i <= $block_count; $i++) {
+ // $i encoded as 4 bytes, big endian.
+ $last = $salt . pack("N", $i);
+ // first iteration
+ $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
+ // perform the other $count - 1 iterations
+ for ($j = 1; $j < $count; $j++) {
+ $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
+ }
+ $output .= $xorsum;
+ }
+
+ if($raw_output)
+ return substr($output, 0, $key_length);
+ else
+ return bin2hex(substr($output, 0, $key_length));
+}
+
+function setRequired($name){
+ if(!isset($_REQUEST[$name]))
+ die("All parameters must be set."); // intentionally vague
+ return $_REQUEST[$name];
+}
+
+function setDefault($name, $default){
+ return isset($_REQUEST[$name]) ? $_REQUEST[$name] : $default;
+}
+
+// this function does no bounds checking on lower/upper, so don't send in user input
+function setDefaultLimits($name, $default, $lower, $upper){
+ $ret = setDefault($name, $default);
+ if($ret < $lower)
+ return $lower;
+ if($ret > $upper)
+ return $upper;
+ return $ret;
+}
+
+$id = setRequired('id');
+$key = setRequired('key');
+
+$tmp = setDefault('tmp', true) !== 'false'; // default is true
+
+$folder = ($tmp ? $tmp_blob_path : $blob_path).hash('sha512', $id . stretch_key($key, 'hau98grch348rcueoahic34hce.i', 128)).'/';
+
+// I'd like the file to never get to the filesystem at all, any good solutions for this?
+// http://stackoverflow.com/questions/5701508/storing-php-php-fpm-apaches-temporary-from-upload-files-in-ram-rather-than-th
+$file = $_FILES['file'];
+
+$decrypted = '';
+if(isset($file)){
+ // then we want to SET a new file that was sent in
+ if($file['size'] > $max_size)
+ die('Max file size exceeded');
+ if($file['size'] < $min_size)
+ die('Min file size not reached');
+
+ $decrypted = file_get_contents($file['tmp_name']);
+
+ // delete unencrypted file and previous folder if it exists
+ secure_delete_file($file['tmp_name']);
+ secure_delete_folder($folder);
+
+ encrypt_write($folder, $key, $decrypted);
+
+ //echo "serving new real just sent in
\n";
+} else if(!file_exists($folder)) {
+ // then we want to SET a new file we create from $new_blob_source
+ $decrypted = encrypt_write($folder, $key);
+
+ //echo "serving new real just generated
\n";
+} else {
+ // otherwise we want to serve an existing file
+ $fname = $folder.'blob';
+ $decrypted = decrypt(file_get_contents($fname), $key);
+ touch($fname);
+}
+
+header('Content-Type: '.setDefault('content-type', $default_content_type));
+echo $decrypted;
+//echo "decrypted: '$decrypted'
\n";
+?>
\ No newline at end of file
diff --git a/alternate/secureblob_cron.sh b/alternate/secureblob_cron.sh
new file mode 100755
index 0000000..c0c1b09
--- /dev/null
+++ b/alternate/secureblob_cron.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# secureblob.php https://github.com/moparisthebest/secureblob
+# Copyright (C) 2014 moparisthebest (Travis Burtrum)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+file_age_hours(){
+ file="$1"
+ from_date="$2"
+ [ -z "$from_date" ] && from_date="$(date +%s)"
+ seconds_diff="$(($from_date - $(stat -c '%Y' "$file")))"
+ echo "$(($seconds_diff/60/60))"
+}
+
+from_date="$(date +%s)"
+find /tmp/secureblob /run/shm/secureblob -type f -name blob -mmin +60 | while read file
+do
+ dir="$(dirname "$file")"
+ [ "$(file_age_hours "$file" "$from_date")" -lt "$(cat "$dir/time-to-live")" ] 2>/dev/null || {
+ # done this way so if time-to-live isn't a proper number we delete everything
+ find "$dir" -type f -exec shred --force --remove '{}' \;
+ rm -rf "$dir"
+ }
+done
+exit
+
+# set up tests
+rm -rf /run/shm/secureblob /tmp/secureblob
+mkdir -p /run/shm/secureblob /tmp/secureblob /tmp/secureblob/bob /tmp/secureblob/tom
+touch /tmp/secureblob/tom/blob
+echo 1 > /tmp/secureblob/tom/time-to-live
+touch --date='3 hours ago' /tmp/secureblob/bob/blob
+echo 2 > /tmp/secureblob/bob/time-to-live
+# end tests
diff --git a/alternate/secureblob_up.sh b/alternate/secureblob_up.sh
new file mode 100755
index 0000000..b044435
--- /dev/null
+++ b/alternate/secureblob_up.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+url="$1"
+[ -z "$url" ] && echo "Must specify URL" && exit 1
+file="$2"
+[ -z "$file" ] && file="${BASH_SOURCE[0]}"
+echo "uploading '$file' to '$url'"
+sha1sum < "$file"
+curl -s -F "file=@$file" -F 'id=bob' -F 'key=bob' "$url" | sha1sum
+curl -s -F 'id=bob' -F 'key=bob' "$url" | sha1sum