class Hiera::Backend::Eyaml::Encryptors::SSHAgent

Constants

SSH2_AGENTC_REQUEST_IDENTITIES
SSH2_AGENTC_SIGN_REQUEST
SSH2_AGENT_IDENTITIES_ANSWER
SSH2_AGENT_SIGN_RESPONSE
VERSION

Public Class Methods

create_keys() click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 149
def self.create_keys
  STDERR.puts 'This encryptor does not support creation of keys'
end
decrypt(ciphertext) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 145
def self.decrypt(ciphertext)
  decrypt_contents(keyid, ciphertext)
end
decrypt_contents(keyid, contents) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 118
def self.decrypt_contents(keyid, contents)
  enc = Encrypted.from_dct(JSON.parse(contents))
  key = get_key(keyid, enc.challenge, enc.salt)
  Fernet.verifier(key, enc.payload, enforce_ttl: false).message
end
encode_s(str) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 28
def self.encode_s(str)
  [str.size].pack('I>') + str
end
encrypt(plaintext) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 140
def self.encrypt(plaintext)
  enc = encrypt_contents(keyid, plaintext)
  JSON.generate(enc.to_dct)
end
encrypt_contents(keyid, contents) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 111
def self.encrypt_contents(keyid, contents)
  challenge = Random.urandom(64)
  salt = Random.urandom(16)
  key = get_key(keyid, challenge, salt)
  Encrypted.new(challenge, salt, Fernet.generate(key, contents))
end
get_key(keyid, challenge, salt) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 95
def self.get_key(keyid, challenge, salt)
  signature_blob = Socket.unix(ENV['SSH_AUTH_SOCK']) do |sock|
    key_blob = get_key_blob(sock, keyid)
    break sign(sock, key_blob, challenge)
  end

  kdf = OpenSSL::PKCS5.pbkdf2_hmac(
    signature_blob,
    salt,
    100_000,
    32,
    OpenSSL::Digest::SHA256.new
  )
  Base64.encode64(kdf)
end
get_key_blob(sock, keyid) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 52
def self.get_key_blob(sock, keyid)
  sock.write(encode_s(SSH2_AGENTC_REQUEST_IDENTITIES))

  sio = StringIO.new(read_s(sock))
  if sio.read(1) != SSH2_AGENT_IDENTITIES_ANSWER
    raise 'expected SSH2_AGENT_IDENTITIES_ANSWER'
  end

  (0...read_u32(sio)).each do
    key_blob = read_s(sio)
    key_comment = read_s(sio)
    break key_blob if key_comment == keyid
  end
end
keyid() click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 131
def self.keyid
  keyid = option :keyid
  if keyid.nil? || keyid.empty?
    raise ArgumentError, 'No keyid configured!'
  end

  keyid
end
read_s(sock) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 24
def self.read_s(sock)
  sock.read(read_u32(sock))
end
read_u32(sock) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 20
def self.read_u32(sock)
  sock.read(4).unpack('L>')[0]
end
sign(sock, key_blob, challenge) click to toggle source
# File lib/hiera/backend/eyaml/encryptors/sshagent.rb, line 32
def self.sign(sock, key_blob, challenge)
  request = (
    SSH2_AGENTC_SIGN_REQUEST +
    encode_s(key_blob) +
    encode_s(challenge) +
    [0].pack('L>')
  )
  sock.write(encode_s(request))

  sio = StringIO.new(read_s(sock))
  if sio.read(1) != SSH2_AGENT_SIGN_RESPONSE
    raise 'Expected SSH2_AGENT_SIGN_RESPONSE'
  end

  sio = StringIO.new(read_s(sio))
  raise 'Expected ssh-rsa' if read_s(sio) != 'ssh-rsa'

  read_s(sio)
end