Skip to content

Instantly share code, notes, and snippets.

@pachacamac
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pachacamac/9d2efa59842d3afa6e5b to your computer and use it in GitHub Desktop.
Save pachacamac/9d2efa59842d3afa6e5b to your computer and use it in GitHub Desktop.
Timecapsule Encryption PoC/WiP
require 'digest/sha2'
require 'securerandom'
require 'time'
def random_seed
Digest::SHA512.hexdigest(SecureRandom.random_bytes(64))
end
def timekey(rounds, seed = random_seed)
# see also: http://www.gwern.net/Self-decrypting%20files
start = Time.now
key = seed.dup
r = rounds
begin
key = Digest::SHA512.hexdigest(key)
r -= 1
end while r > 0
{seconds: Time.now - start, seed_info: {rounds: rounds, seed: seed}, secret_key: key}
end
def benchmark(desired_seconds, bench_rounds = 2**16)
bench_seconds = timekey(bench_rounds)[:seconds]
((bench_rounds / bench_seconds) * desired_seconds).to_i
end
def cpu_count
@cpu_count ||= (`nproc` || `lscpu -e|grep '^[0-9]'|wc -l` || `cat /proc/cpuinfo|grep processor|wc -l`).to_i
end
def xor_crypt(s,k)
s, k = s.bytes, k.bytes.cycle
s.map{|e| (e^k.next).chr}.join
end
def get_password(msg='> ')
require 'io/console'
print msg
STDIN.noecho(&:gets).chomp
end
HOSTS_TO_BLOCK = %w{reddit.com hackaday.com onethingwell.org watch-series-online.li wallhaven.cc alpha.wallhaven.cc
twitter.com gmail.com accounts.google.com mail.google.com bbc.com}
def hosts_file(action=:block, hosts=HOSTS_TO_BLOCK, file='/etc/hosts')
comment = '#BLOCKED' # used for unblocking
lines = hosts.reduce([]){|s,e| s<<"127.0.0.1 #{e} #{comment}"<<"127.0.0.1 www.#{e} #{comment}"}
case action.to_s
when 'block'; `echo '#{lines.join("\n")}' >> #{file}`
when 'unblock'; `echo '#{File.readlines(file).reject{|e| e.match /#{comment}$/}.join}' > #{file}`
else raise 'wrong action! (block or unblock)'
end
end
# has to be run as daemon as root and then logged out
def lockdown!(opts={})
secret_key = opts.delete(:secret_key) || raise('No secret_key given!')
opts[:seed_info] || raise('No seed info given!')
opts[:unlock_time] || raise('No unlock time given!')
opts[:users] ||= [`id -un`.chomp]
opts[:sudo_group] ||= 'wheel'
raise 'Invalid unlock time!' if Time.now >= opts[:unlock_time]
raise 'Unreasonable unlock time!' if Time.now + 3600*6 < opts[:unlock_time] # 6 hour limit
puts "Starting lockdown!"
# 0) create new timecapsule (seed, rounds, key), save seed and rounds for user
puts "Writing seed info"
File.open('seed_info', 'a') do |f|
f.puts "Lockdown started (#{Time.now})"
opts.each{|k,v| f.puts "\t#{k}:\t#{v.inspect}"}
f.chmod(0777)
end
# 1) get current root password, save it in a file only root has access to
puts "Saving original and new root passwords in /root"
File.write '/root/original_password', get_password('Enter root password > ')
File.write '/root/current_password', secret_key
# 2) block sites in /etc/hosts
puts "Blocking hosts"
hosts_file :block
# 3) remove users from sudo group
puts "Removing users from group #{opts[:sudo_group]}"
opts[:users].each{|user| `gpasswd -d #{user} #{opts[:sudo_group]}`}
# 4) change root password to timecapsule key, save it in a file only root has access to
puts "Changing root password"
`echo -e "#{secret_key}\n#{secret_key}" | passwd -q root`
# 5) do nothing for x time
puts "Sleeping until #{opts[:unlock_time]}"
sleep 1 while Time.now < opts[:unlock_time]
# 6) change root password back to original
puts "Restoring root password"
key_orig = File.read('/root/original_password')
`echo -e "#{key_orig}\n#{key_orig}" | passwd -q root`
puts "Deleting password backups from /root"
File.delete '/root/original_password'
File.delete '/root/current_password'
# 7) add user backs to sudo group
puts "Adding users back to group #{opts[:sudo_group]}"
opts[:users].each{|user| `gpasswd -a #{user} #{opts[:sudo_group]}`}
# 8) unblock sites in /etc/hosts
puts "Unblocking hosts"
hosts_file :unblock
puts "Lockdown finished!"
end
tc = case ARGV[1].to_s.downcase.to_sym
when :rounds; timekey(ARGV[0].to_i)
when :seconds; timekey(benchmark(ARGV[0].to_i))
when :minutes; timekey(benchmark(ARGV[0].to_i)*60)
else
puts "Usage: #{__FILE__} <num> <rounds/seconds/minutes> [lockdown unlock_time]"
exit
end
if ARGV[2].to_s.downcase.to_sym == :lockdown
lockdown!(tc.merge(unlock_time: Time.parse(ARGV[3].to_s)))
else
puts tc.inspect
end
# Idea for generating quicker than verifying:
# * Generate N hashes with R rounds each in parallel (a good value for n would be the number of CPU cores)
# * Set up a chain between the n results:
# * The final hash of seed 1 is used to encrypt the seed for hash 2
# * The final hash of seed n is used to encrypt the seed for hash n+1
# * Only release the plain first seed, the n-1 encrypted seeds, and the number of rounds
# * Every block of the chain has to be calculated one after the other, effectively preventing parallelization
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment