diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/facter/tor_hidden_services.rb | 19 | ||||
-rw-r--r-- | lib/puppet/parser/functions/generate_onion_key.rb | 48 | ||||
-rw-r--r-- | lib/puppet/parser/functions/onion_address.rb | 30 |
3 files changed, 97 insertions, 0 deletions
diff --git a/lib/facter/tor_hidden_services.rb b/lib/facter/tor_hidden_services.rb new file mode 100644 index 0000000..c2a6cca --- /dev/null +++ b/lib/facter/tor_hidden_services.rb @@ -0,0 +1,19 @@ +Facter.add(:tor_hidden_services) do + confine :kernel => "Linux" + setcode do + config_file = '/etc/tor/torrc' + if File.exists?(config_file) + dirs = File.read(config_file).split("\n").select{|l| + l =~ /^HiddenServiceDir/ + }.collect{|l| l.sub(/^HiddenServiceDir /,'') } + dirs.inject({}) { |res,d| + if File.exists?(h=File.join(d,'hostname')) + res[File.basename(d)] = File.read(h).chomp + end + res + } + else + {} + end + end +end diff --git a/lib/puppet/parser/functions/generate_onion_key.rb b/lib/puppet/parser/functions/generate_onion_key.rb new file mode 100644 index 0000000..9ee5351 --- /dev/null +++ b/lib/puppet/parser/functions/generate_onion_key.rb @@ -0,0 +1,48 @@ +module Puppet::Parser::Functions + newfunction(:generate_onion_key, :type => :rvalue, :doc => <<-EOS +Generates or loads a rsa private key for an onion service, returns they onion +onion address and the private key content. + +Requires a location to load and store the private key, as well an identifier, which will be used as a filename in the location. + +Example: + + res = generate_onion_key('/tmp','my_secret_key') + notice "Onion Address: \${res[0]" + notice "Priavte Key: \${res[1]" + + +It will also store the onion address under /tmp/my_secret_key.hostname. +If /tmp/my_secret_key.key exists, but not the hostname file. Then the function will be loaded and the onion address will be generated from it. + + EOS + ) do |args| + location = args.shift + identifier = args.shift + + raise(Puppet::ParseError, "generate_onion_key(): requires 2 arguments") unless [location,identifier].all?{|i| !i.nil? } + + raise(Puppet::ParseError, "generate_onion_key(): requires location (#{location}) to be a directory") unless File.directory?(location) + path = File.join(location,identifier) + + private_key = if File.exists?(kf="#{path}.key") + pk = OpenSSL::PKey::RSA.new(File.read(kf)) + raise(Puppet::ParseError, "generate_onion_key(): key in path #{kf} must have a length of 1024bit") unless (pk.n.num_bytes * 8) == 1024 + pk + else + # 1024 is hardcoded by tor + pk = OpenSSL::PKey::RSA.generate(1024) + File.open(kf,'w'){|f| f << pk.to_s } + pk + end + onion_address = if File.exists?(hf="#{path}.hostname") + File.read(hf) + else + oa = function_onion_address([private_key]) + File.open(hf,'w'){|f| f << oa.to_s } + oa + end + + [ onion_address, private_key.to_s ] + end +end diff --git a/lib/puppet/parser/functions/onion_address.rb b/lib/puppet/parser/functions/onion_address.rb new file mode 100644 index 0000000..a6f9755 --- /dev/null +++ b/lib/puppet/parser/functions/onion_address.rb @@ -0,0 +1,30 @@ +require 'base32' +module Puppet::Parser::Functions + newfunction(:onion_address, :type => :rvalue, :doc => <<-EOS +Generates an onion address from a 1024-bit RSA private key. + +Example: + + onion_address("-----BEGIN RSA PRIVATE KEY----- +MII.... +-----END RSA PRIVATE KEY-----") + +Returns the onionadress for that key, *without* the .onion suffix. + EOS + ) do |args| + key = args.shift + raise(Puppet::ParseError, "onion_address(): requires 1 argument") unless key && args.empty? + private_key = key.is_a?(OpenSSL::PKey::RSA) ? key : OpenSSL::PKey::RSA.new(key) + + # the onion address are a base32 encoded string of the first half of the sha1 over the + # der format of the public key + # https://trac.torproject.org/projects/tor/wiki/doc/HiddenServiceNames#Howare.onionnamescreated + # We can skip the first 22 bits of the der format as they are ignored by tor + # https://timtaubert.de/blog/2014/11/using-the-webcrypto-api-to-generate-onion-names-for-tor-hidden-services/ + # https://gitweb.torproject.org/torspec.git/tree/rend-spec.txt#n525 + # Except for Ruby 1.8.7 where the first 22 are not present at all + start = RUBY_VERSION.to_f < 1.9 ? 0 : 22 + public_key_der = private_key.public_key.to_der + Base32.encode(Digest::SHA1.digest(public_key_der[start..-1]))[0..15].downcase + end +end |