Skip to content

Commit

Permalink
Merge pull request #2656 from joshcooper/FACT-3110-more-ecdsa-types
Browse files Browse the repository at this point in the history
(FACT-3110) Support for ecdsa 384 and 521 bit keys
  • Loading branch information
cthorn42 authored Dec 13, 2023
2 parents 69b0978 + 6e3223a commit 0cd73de
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 89 deletions.
8 changes: 1 addition & 7 deletions lib/facter/util/resolvers/fingerprint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@
module Facter
module Util
module Resolvers
class FingerPrint
attr_accessor :sha1, :sha256
def initialize(sha1, sha256)
@sha1 = sha1
@sha256 = sha256
end
end
FingerPrint = Struct.new(:sha1, :sha256)
end
end
end
10 changes: 1 addition & 9 deletions lib/facter/util/resolvers/ssh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
module Facter
module Util
module Resolvers
class Ssh
attr_accessor :fingerprint, :type, :key, :name
def initialize(fingerprint, type, key, name)
@fingerprint = fingerprint
@type = type
@key = key
@name = name
end
end
Ssh = Struct.new(:fingerprint, :type, :key, :name)
end
end
end
2 changes: 2 additions & 0 deletions lib/facter/util/resolvers/ssh_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ module Resolvers
class SshHelper
class << self
SSH_NAME = { 'ssh-dss' => 'dsa', 'ecdsa-sha2-nistp256' => 'ecdsa',
'ecdsa-sha2-nistp384' => 'ecdsa', 'ecdsa-sha2-nistp521' => 'ecdsa',
'ssh-ed25519' => 'ed25519', 'ssh-rsa' => 'rsa' }.freeze
SSH_FINGERPRINT = { 'rsa' => 1, 'dsa' => 2, 'ecdsa' => 3, 'ed25519' => 4 }.freeze

def create_ssh(key_type, key)
key_name = SSH_NAME[key_type]
return unless key_name

# decode64 ignores non-base64 characters including newlines
decoded_key = Base64.decode64(key)
ssh_fp = SSH_FINGERPRINT[key_name]
sha1 = "SSHFP #{ssh_fp} 1 #{Digest::SHA1.new.update(decoded_key)}"
Expand Down
146 changes: 79 additions & 67 deletions spec/facter/resolvers/ssh_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,6 @@

describe Facter::Resolvers::Ssh do
describe '#folders' do
let(:ecdsa_content) { load_fixture('ecdsa').read.strip! }
let(:rsa_content) { load_fixture('rsa').read.strip! }
let(:ed25519_content) { load_fixture('ed25519').read.strip! }

let(:ecdsa_fingerprint) do
Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 3 1 fd92cf867fac0042d491eb1067e4f3cabf54039a',
'SSHFP 3 2 a51271a67987d7bbd685fa6d7cdd2823a30373ab01420b094480523fabff2a05'
)
end

let(:rsa_fingerprint) do
Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 1 1 90134f93fec6ab5e22bdd88fc4d7cd6e9dca4a07',
'SSHFP 1 2 efaa26ff8169f5ffc372ebcad17aef886f4ccaa727169acdd0379b51c6c77e99'
)
end

let(:ed25519_fingerprint) do
Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 4 1 f5780634d4e34c6ef2411ac439b517bfdce43cf1',
'SSHFP 4 2 c1257b3865df22f3349f9ebe19961c8a8edf5fbbe883113e728671b42d2c9723'
)
end

let(:ecdsa_result) do
Facter::Util::Resolvers::Ssh.new(ecdsa_fingerprint, 'ecdsa-sha2-nistp256', ecdsa_content, 'ecdsa')
end

let(:rsa_result) do
Facter::Util::Resolvers::Ssh.new(rsa_fingerprint, 'ssh-rsa', rsa_content, 'rsa')
end
let(:ed25519_result) do
Facter::Util::Resolvers::Ssh.new(ed25519_fingerprint, 'ssh-ed22519', ed25519_content, 'ed25519')
end

let(:paths) { %w[/etc/ssh /usr/local/etc/ssh /etc /usr/local/etc /etc/opt/ssh] }
let(:file_names) { %w[ssh_host_rsa_key.pub ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub] }

Expand All @@ -46,43 +10,95 @@
allow(File).to receive(:directory?).with('/etc').and_return(true)

allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_ecdsa_key.pub', nil).and_return(ecdsa_content)
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_dsa_key.pub', nil).and_return(nil)
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_rsa_key.pub', nil).and_return(rsa_content)
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_ed25519_key.pub', nil).and_return(ed25519_content)

allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
.with('ssh-rsa', load_fixture('rsa_key').read.strip!)
.and_return(rsa_result)
allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
.with('ecdsa-sha2-nistp256', load_fixture('ecdsa_key').read.strip!)
.and_return(ecdsa_result)
allow(Facter::Util::Resolvers::SshHelper).to receive(:create_ssh)
.with('ssh-ed25519', load_fixture('ed25519_key').read.strip!)
.and_return(ed25519_result)
.with(a_string_starting_with('/etc/ssh_host'), nil).and_return(nil)
end

after do
Facter::Resolvers::Ssh.invalidate_cache
end

context 'when ssh_host_dsa_key.pub file is not readable' do
it 'returns resolved ssh' do
expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([rsa_result, ecdsa_result, ed25519_result])
shared_examples 'an ssh key' do
it 'resolves the key' do
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with(path, nil).and_return(content)

expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([result])
end
end

context 'when ssh_host_ecdsa_key.pub file is also not readable' do
before do
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_ecdsa_key.pub', nil).and_return(nil)
context 'when rsa' do
let(:path) { '/etc/ssh_host_rsa_key.pub' }
let(:content) { load_fixture('rsa').read }
let(:result) do
fingerprint = Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 1 1 90134f93fec6ab5e22bdd88fc4d7cd6e9dca4a07',
'SSHFP 1 2 efaa26ff8169f5ffc372ebcad17aef886f4ccaa727169acdd0379b51c6c77e99'
)
Facter::Util::Resolvers::Ssh.new(fingerprint, *content.strip.split(' '), 'rsa')
end

it 'returns resolved ssh' do
expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([rsa_result, ed25519_result])
include_examples 'an ssh key'
end

context 'when ecdsa' do
let(:path) { '/etc/ssh_host_dsa_key.pub' }
let(:content) { load_fixture('ecdsa').read }
let(:result) do
fingerprint = Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 3 1 fd92cf867fac0042d491eb1067e4f3cabf54039a',
'SSHFP 3 2 a51271a67987d7bbd685fa6d7cdd2823a30373ab01420b094480523fabff2a05'
)
Facter::Util::Resolvers::Ssh.new(fingerprint, *content.strip.split(' '), 'ecdsa')
end

include_examples 'an ssh key'
end

context 'when ed25519' do
let(:path) { '/etc/ssh_host_ed25519_key.pub' }
let(:content) { load_fixture('ed25519').read }
let(:result) do
fingerprint = Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 4 1 1c02084d251368b98a3af97820d9fbf2b8dc9558',
'SSHFP 4 2 656bd7aa3f8ad4703bd581888231f822cb8cd4a2a258584469551d2c2c9f6b62'
)
Facter::Util::Resolvers::Ssh.new(fingerprint, *content.strip.split(' '), 'ed25519')
end

include_examples 'an ssh key'
end

context 'when ecdsa 384-bit' do
let(:path) { '/etc/ssh_host_ecdsa_key.pub' }
let(:content) { load_fixture('ecdsa384').read }
let(:result) do
fingerprint = Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 3 1 a3c1dc40a07cd76ea2ffe3f57e96aae146427174',
'SSHFP 3 2 949d92d65c6bb3908727bef5cdafef5b546650d64a081a4f85e7dcaf6b7cb7ab'
)
Facter::Util::Resolvers::Ssh.new(fingerprint, *content.strip.split(' '), 'ecdsa')
end

include_examples 'an ssh key'
end

context 'when ecdsa 521-bit' do
let(:path) { '/etc/ssh_host_ecdsa_key.pub' }
let(:content) { load_fixture('ecdsa521').read }
let(:result) do
fingerprint = Facter::Util::Resolvers::FingerPrint.new(
'SSHFP 3 1 61046cb5f7b38df21fe4511a9280436ce89514ee',
'SSHFP 3 2 b74da480da3411a79abf37d0bcfbbcaa8c1dbfc6a983365276b3c7f0c7a8de3e'
)
Facter::Util::Resolvers::Ssh.new(fingerprint, *content.strip.split(' '), 'ecdsa')
end

include_examples 'an ssh key'
end

context 'when no files are readable' do
it 'returns an empty array' do
expect(Facter::Resolvers::Ssh.resolve(:ssh)).to eq([])
end
end

Expand All @@ -108,11 +124,7 @@
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_ecdsa_key.pub', nil).and_return('invalid key')
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_dsa_key.pub', nil).and_return(nil)
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_rsa_key.pub', nil).and_return(nil)
allow(Facter::Util::FileHelper).to receive(:safe_read)
.with('/etc/ssh_host_ed25519_key.pub', nil).and_return(nil)
.with(a_string_starting_with('/etc/ssh_host'), nil)
end

after do
Expand Down
28 changes: 22 additions & 6 deletions spec/facter/util/resolvers/ssh_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,32 @@
subject(:ssh_helper) { Facter::Util::Resolvers::SshHelper }

describe '#create_ssh' do
let(:fingerprint) { instance_spy(Facter::Util::Resolvers::FingerPrint) }
let(:key) { load_fixture('rsa_key').read.strip }

before do
allow(Facter::Util::Resolvers::FingerPrint).to receive(:new).and_return(fingerprint)
it 'returns an RSA ssh object' do
expect(ssh_helper.create_ssh('ssh-rsa', key)).to \
be_an_instance_of(Facter::Util::Resolvers::Ssh).and \
have_attributes(name: 'rsa', type: 'ssh-rsa')
end

it 'returns a ssh object' do
expect(ssh_helper.create_ssh('ssh-rsa', key)).to be_an_instance_of(Facter::Util::Resolvers::Ssh).and \
have_attributes(name: 'rsa', type: 'ssh-rsa', fingerprint: fingerprint)
it 'returns sha1 fingerprint' do
expect(ssh_helper.create_ssh('ssh-rsa', key).fingerprint.sha1).to \
eq('SSHFP 1 1 90134f93fec6ab5e22bdd88fc4d7cd6e9dca4a07')
end

it 'returns sha256 fingerprint' do
expect(ssh_helper.create_ssh('ssh-rsa', key).fingerprint.sha256).to \
eq('SSHFP 1 2 efaa26ff8169f5ffc372ebcad17aef886f4ccaa727169acdd0379b51c6c77e99')
end

it 'ignores non-base64 characters' do
nonbase64_key = "\x00\n-_#{key}"
expect(ssh_helper.create_ssh('ssh-rsa', nonbase64_key).fingerprint.sha1).to \
eq('SSHFP 1 1 90134f93fec6ab5e22bdd88fc4d7cd6e9dca4a07')
end

it 'implements value semantics' do
expect(ssh_helper.create_ssh('ssh-rsa', key)).to eq(ssh_helper.create_ssh('ssh-rsa', key))
end
end
end
1 change: 1 addition & 0 deletions spec/fixtures/ecdsa384
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBI+YmNHUdvMtZSEdCDJLruZjtUGsi59cf/TNkmRKFcVGgaWO54NUXT/PlTwjm7g9uS1FKbZY4+MKP0Q4KsgfGJAwn9MLsdSeUGY2UIrhQ0UM6KUUZCDot0G7Xm2pAdy/Qw==
1 change: 1 addition & 0 deletions spec/fixtures/ecdsa384_key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBI+YmNHUdvMtZSEdCDJLruZjtUGsi59cf/TNkmRKFcVGgaWO54NUXT/PlTwjm7g9uS1FKbZY4+MKP0Q4KsgfGJAwn9MLsdSeUGY2UIrhQ0UM6KUUZCDot0G7Xm2pAdy/Qw==
1 change: 1 addition & 0 deletions spec/fixtures/ecdsa521
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHKr7fO2HGs84ihV9+Z4Dkk4rX+FqhtKV4vGEIwnwR3r0GUIER1aIk+shXzOhCEPNqTiik5CRdE9sDhXkYDJa35+QFIBvo1i2qCNEQ1EowBbYZYBAhk3CPAhIUIYe+Achz+PCqBhqkPC+vHhqHpECAzOI0qjFuoT17rbEb4stl3n8yHfQ==
1 change: 1 addition & 0 deletions spec/fixtures/ecdsa521_key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHKr7fO2HGs84ihV9+Z4Dkk4rX+FqhtKV4vGEIwnwR3r0GUIER1aIk+shXzOhCEPNqTiik5CRdE9sDhXkYDJa35+QFIBvo1i2qCNEQ1EowBbYZYBAhk3CPAhIUIYe+Achz+PCqBhqkPC+vHhqHpECAzOI0qjFuoT17rbEb4stl3n8yHfQ==

0 comments on commit 0cd73de

Please sign in to comment.