This commit is contained in:
Ben Toews 2019-01-22 14:05:28 -07:00
Родитель d970afb4d8
Коммит 212f3fd997
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
7 изменённых файлов: 335 добавлений и 259 удалений

Просмотреть файл

@ -3,4 +3,5 @@ end
require "ssh_data/error"
require "ssh_data/certificate"
require "ssh_data/public_key"
require "ssh_data/encoding"

Просмотреть файл

@ -1,45 +1,62 @@
class SSHData::Certificate
# Integer certificate types (denotes host vs. user)
# Integer certificate types
TYPE_USER = 1
TYPE_HOST = 2
# String certificate types (denotes key type).
RSA_CERT_TYPE = "ssh-rsa-cert-v01@openssh.com"
DSA_CERT_TYPE = "ssh-dss-cert-v01@openssh.com"
ECDSA_SHA2_NISTP256_CERT_TYPE = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
ECDSA_SHA2_NISTP384_CERT_TYPE = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
ECDSA_SHA2_NISTP521_CERT_TYPE = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
ED25519_CERT_TYPE = "ssh-ed25519-cert-v01@openssh.com"
# Certificate algorithm identifiers
ALGO_RSA = "ssh-rsa-cert-v01@openssh.com"
ALGO_DSA = "ssh-dss-cert-v01@openssh.com"
ALGO_ECDSA256 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
ALGO_ECDSA384 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
ALGO_ECDSA521 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
ALGO_ED25519 = "ssh-ed25519-cert-v01@openssh.com"
attr_reader :type_string, :nonce, :key_data, :serial, :type, :key_id,
attr_reader :algo, :nonce, :key_data, :serial, :type, :key_id,
:valid_principals, :valid_after, :valid_before, :critical_options,
:extensions, :reserved, :signature_key, :signature, :signed_data
# Parse an SSH certificate.
#
# cert - An SSH formatted certificate, including key type, encoded key and
# cert - An SSH formatted certificate, including key algo, encoded key and
# optional user/host names.
#
# Returns a Certificate instance.
def self.parse(cert)
data = SSHData::Encoding.parse_certificate(cert)
algo, b64, _ = cert.split(" ", 3)
if algo.nil? || b64.nil?
raise SSHData::DecodeError, "bad certificate format"
end
raw = Base64.decode64(b64)
data, read = SSHData::Encoding.decode_certificate(raw)
if read != raw.bytesize
raise SSHData::DecodeError, "unexpected trailing data"
end
if data[:algo] != algo
raise SSHData::DecodeError, "algo mismatch: #{data[:algo].inspect}!=#{algo.inspect}"
end
# Parse data into better types, where possible.
data[:valid_after] = Time.at(data[:valid_after])
data[:valid_before] = Time.at(data[:valid_before])
# TODO: parse more fields, where possible
# The signature is the last field. The signature is calculated over all
# preceding data.
signed_data_len = raw.bytesize - data[:signature].bytesize
data[:signed_data] = raw.byteslice(0, signed_data_len)
new(**data)
end
# Intialize a new Certificate instance.
#
# type_string: - The certificate's String type (one of RSA_CERT_TYPE,
# DSA_CERT_TYPE, ECDSA_SHA2_NISTP256_CERT_TYPE,
# ECDSA_SHA2_NISTP384_CERT_TYPE,
# ECDSA_SHA2_NISTP521_CERT_TYPE, or ED25519_CERT_TYPE)
# algo: - The certificate's String algorithm id (one of ALGO_RSA,
# ALGO_DSA, ALGO_ECDSA256, ALGO_ECDSA384, ALGO_ECDSA521,
# or ALGO_ED25519)
# nonce: - The certificate's String nonce field.
# key_data: - Hash of key-type-speciric data for public key.
# key_data: - Hash of key-type-specific data for public key.
# serial: - The certificate's Integer serial field.
# type: - The certificate's Integer type field (one of TYPE_USER
# or TYPE_HOST).
@ -57,8 +74,8 @@ class SSHData::Certificate
# calculated during parsing.
#
# Returns nothing.
def initialize(type_string:, nonce:, key_data:, serial:, type:, key_id:, valid_principals:, valid_after:, valid_before:, critical_options:, extensions:, reserved:, signature_key:, signature:, signed_data:)
@type_string = type_string
def initialize(algo:, nonce:, key_data:, serial:, type:, key_id:, valid_principals:, valid_after:, valid_before:, critical_options:, extensions:, reserved:, signature_key:, signature:, signed_data:)
@algo = algo
@nonce = nonce
@key_data = key_data
@serial = serial

Просмотреть файл

@ -2,100 +2,118 @@ require "openssl"
require "base64"
module SSHData::Encoding
# Certificate fields that come before the public key.
CERT_HEADER_FIELDS = [
[:type_string, :string],
[:nonce, :string],
# Fields in an RSA public key
RSA_KEY_FIELDS = [
[:e, :mpint],
[:n, :mpint]
]
# Certificate fields that come after the public key.
CERT_TRAILER_FIELDS = [
[:serial, :uint64],
[:type, :uint32],
[:key_id, :string],
[:valid_principals, :string],
[:valid_after, :uint64],
[:valid_before, :uint64],
[:critical_options, :string],
[:extensions, :string],
[:reserved, :string],
[:signature_key, :string],
# Fields in a DSA public key
DSA_KEY_FIELDS = [
[:p, :mpint],
[:q, :mpint],
[:g, :mpint],
[:y, :mpint]
]
# The fields describing the public key for each type of certificate.
KEY_FIELDS_BY_CERT_TYPE = {
SSHData::Certificate::RSA_CERT_TYPE => [
[:e, :mpint],
[:n, :mpint]
],
SSHData::Certificate::DSA_CERT_TYPE => [
[:p, :mpint],
[:q, :mpint],
[:g, :mpint],
[:y, :mpint]
],
SSHData::Certificate::ECDSA_SHA2_NISTP256_CERT_TYPE => [
[:curve, :string],
[:public_key, :string]
],
SSHData::Certificate::ECDSA_SHA2_NISTP384_CERT_TYPE => [
[:curve, :string],
[:public_key, :string]
],
SSHData::Certificate::ECDSA_SHA2_NISTP521_CERT_TYPE => [
[:curve, :string],
[:public_key, :string]
],
SSHData::Certificate::ED25519_CERT_TYPE => [
[:pk, :string]
]
# Fields in an ECDSA public key
ECDSA_KEY_FIELDS = [
[:curve, :string],
[:public_key, :string]
]
# Fields in a ED25519 public key
ED25519_KEY_FIELDS = [
[:pk, :string]
]
PUBLIC_KEY_ALGO_BY_CERT_ALGO = {
SSHData::Certificate::ALGO_RSA => SSHData::PublicKey::ALGO_RSA,
SSHData::Certificate::ALGO_DSA => SSHData::PublicKey::ALGO_DSA,
SSHData::Certificate::ALGO_ECDSA256 => SSHData::PublicKey::ALGO_ECDSA256,
SSHData::Certificate::ALGO_ECDSA384 => SSHData::PublicKey::ALGO_ECDSA384,
SSHData::Certificate::ALGO_ECDSA521 => SSHData::PublicKey::ALGO_ECDSA521,
SSHData::Certificate::ALGO_ED25519 => SSHData::PublicKey::ALGO_ED25519,
}
KEY_FIELDS_BY_PUBLIC_KEY_ALGO = {
SSHData::PublicKey::ALGO_RSA => RSA_KEY_FIELDS,
SSHData::PublicKey::ALGO_DSA => DSA_KEY_FIELDS,
SSHData::PublicKey::ALGO_ECDSA256 => ECDSA_KEY_FIELDS,
SSHData::PublicKey::ALGO_ECDSA384 => ECDSA_KEY_FIELDS,
SSHData::PublicKey::ALGO_ECDSA521 => ECDSA_KEY_FIELDS,
SSHData::PublicKey::ALGO_ED25519 => ED25519_KEY_FIELDS,
}
# Decode the fields in a public key.
#
# raw - Binary String public key as described by RFC4253 section 6.6.
# algo - String public key algorithm identifier (optional).
# offset - Integer number of bytes into `raw` at which we should start
# reading.
#
# Returns an Array containing a Hash describing the public key and the
# Integer number of bytes read.
def self.decode_public_key(raw, algo=nil, offset=0)
total_read = 0
if algo.nil?
algo, read = read_string(raw, offset + total_read)
total_read += read
end
unless fields = KEY_FIELDS_BY_PUBLIC_KEY_ALGO[algo]
raise SSHData::DecodeError, "unknown key algo: #{algo.inspect}"
end
data, read = decode_all(raw, fields, offset + total_read)
total_read += read
data[:algo] = algo
[data, total_read]
end
# Decode the fields in a certificate.
#
# cert - An SSH formatted certificate, including key type, encoded key and
# optional user/host names.
# raw - Binary String certificate as described by RFC4253 section 6.6.
# offset - Integer number of bytes into `raw` at which we should start
# reading.
#
# Returns a Hash of the certificate's fields.
def self.parse_certificate(cert)
type, cert_b64, _ = cert.split(" ")
if cert_b64.nil?
raise SSHData::DecodeError
elsif !KEY_FIELDS_BY_CERT_TYPE.key?(type)
raise SSHData::DecodeError, "unknown certificate type: #{type.inspect}"
# Returns an Array containing a Hash describing the certificate and the
# Integer number of bytes read.
def self.decode_certificate(raw, offset=0)
total_read = 0
data, read = decode_all(raw, [
[:algo, :string],
[:nonce, :string],
], offset + total_read)
total_read += read
unless key_algo = PUBLIC_KEY_ALGO_BY_CERT_ALGO[data[:algo]]
raise SSHData::DecodeError, "unknown cert algo: #{data[:algo].inspect}"
end
cert_raw = Base64.decode64(cert_b64)
offset = 0
data = {}
data[:key_data], read = decode_public_key(raw, key_algo, offset + total_read)
total_read += read
header_data, read = decode_all(cert_raw, CERT_HEADER_FIELDS, 0)
offset += read
data.merge!(header_data)
trailer, read = decode_all(raw, [
[:serial, :uint64],
[:type, :uint32],
[:key_id, :string],
[:valid_principals, :string],
[:valid_after, :uint64],
[:valid_before, :uint64],
[:critical_options, :string],
[:extensions, :string],
[:reserved, :string],
[:signature_key, :string],
[:signature, :string],
], offset + total_read)
total_read += read
if type != data[:type_string]
raise SSHData::DecodeError, "type mismatch: #{type.inspect}!=#{data[:type_string].inspect}"
end
key_fields = KEY_FIELDS_BY_CERT_TYPE[type]
data[:key_data], read = decode_all(cert_raw, key_fields, offset)
offset += read
trailer_data, read = decode_all(cert_raw, CERT_TRAILER_FIELDS, offset)
offset += read
data.merge!(trailer_data)
# The signature is over all data up to the signature field. This isn't its
# own field, but we parse it out here so we don't have to do it later.
data[:signed_data] = cert_raw.byteslice(0, read)
data[:signature], read = read_string(cert_raw, offset)
offset += read
if cert_raw.bytesize != offset
raise SSHData::DecodeError, "bad data length"
end
data
[data.merge(trailer), total_read]
end
# Decode all of the given fields from data.

Просмотреть файл

@ -0,0 +1,9 @@
class SSHData::PublicKey
# Public key algorithm identifiers
ALGO_RSA = "ssh-rsa"
ALGO_DSA = "ssh-dss"
ALGO_ECDSA256 = "ecdsa-sha2-nistp256"
ALGO_ECDSA384 = "ecdsa-sha2-nistp384"
ALGO_ECDSA521 = "ecdsa-sha2-nistp521"
ALGO_ED25519 = "ssh-ed25519"
end

Просмотреть файл

@ -14,8 +14,38 @@ describe SSHData::Certificate do
let(:min_time) { Time.at(0) }
let(:max_time) { Time.at((2**64)-1) }
it "raises on trailing data" do
algo, b64, host = fixture("rsa_leaf_for_rsa_ca-cert.pub").split(" ", 3)
raw = Base64.decode64(b64)
raw += "foobar"
b64 = Base64.strict_encode64(raw)
cert = [algo, b64, host].join(" ")
expect {
described_class.parse(cert)
}.to raise_error(SSHData::DecodeError)
end
it "raises on type mismatch" do
_, b64, host = fixture("rsa_leaf_for_rsa_ca-cert.pub").split(" ", 3)
cert = [SSHData::Certificate::ALGO_ED25519, b64, host].join(" ")
expect {
described_class.parse(cert)
}.to raise_error(SSHData::DecodeError)
end
it "doesn't require the user/host names" do
type, b64, _ = fixture("rsa_leaf_for_rsa_ca-cert.pub").split(" ", 3)
cert = [type, b64].join(" ")
expect {
described_class.parse(cert)
}.not_to raise_error
end
it "parses RSA certs" do
expect(rsa_cert.type_string).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(rsa_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(rsa_cert.nonce).to be_a(String)
expect(rsa_cert.key_data).to be_a(Hash)
expect(rsa_cert.serial).to eq(123)
@ -29,11 +59,10 @@ describe SSHData::Certificate do
expect(rsa_cert.reserved).to eq("")
expect(rsa_cert.signature_key).to be_a(String)
expect(rsa_cert.signature).to be_a(String)
expect(rsa_cert.signed_data).to be_a(String)
end
it "parses DSA certs" do
expect(dsa_cert.type_string).to eq(SSHData::Certificate::DSA_CERT_TYPE)
expect(dsa_cert.algo).to eq(SSHData::Certificate::ALGO_DSA)
expect(dsa_cert.nonce).to be_a(String)
expect(dsa_cert.key_data).to be_a(Hash)
expect(dsa_cert.serial).to eq(123)
@ -47,11 +76,10 @@ describe SSHData::Certificate do
expect(dsa_cert.reserved).to eq("")
expect(dsa_cert.signature_key).to be_a(String)
expect(dsa_cert.signature).to be_a(String)
expect(dsa_cert.signed_data).to be_a(String)
end
it "parses ECDSA certs" do
expect(ecdsa_cert.type_string).to eq(SSHData::Certificate::ECDSA_SHA2_NISTP256_CERT_TYPE)
expect(ecdsa_cert.algo).to eq(SSHData::Certificate::ALGO_ECDSA256)
expect(ecdsa_cert.nonce).to be_a(String)
expect(ecdsa_cert.key_data).to be_a(Hash)
expect(ecdsa_cert.serial).to eq(123)
@ -65,11 +93,10 @@ describe SSHData::Certificate do
expect(ecdsa_cert.reserved).to eq("")
expect(ecdsa_cert.signature_key).to be_a(String)
expect(ecdsa_cert.signature).to be_a(String)
expect(ecdsa_cert.signed_data).to be_a(String)
end
it "parses ED25519 certs" do
expect(ed25519_cert.type_string).to eq(SSHData::Certificate::ED25519_CERT_TYPE)
expect(ed25519_cert.algo).to eq(SSHData::Certificate::ALGO_ED25519)
expect(ed25519_cert.nonce).to be_a(String)
expect(ed25519_cert.key_data).to be_a(Hash)
expect(ed25519_cert.serial).to eq(123)
@ -83,11 +110,10 @@ describe SSHData::Certificate do
expect(ed25519_cert.reserved).to eq("")
expect(ed25519_cert.signature_key).to be_a(String)
expect(ed25519_cert.signature).to be_a(String)
expect(ed25519_cert.signed_data).to be_a(String)
end
it "parses certs issued by RSA CAs" do
expect(rsa_ca_cert.type_string).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(rsa_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(rsa_ca_cert.nonce).to be_a(String)
expect(rsa_ca_cert.key_data).to be_a(Hash)
expect(rsa_ca_cert.serial).to eq(123)
@ -101,11 +127,10 @@ describe SSHData::Certificate do
expect(rsa_ca_cert.reserved).to eq("")
expect(rsa_ca_cert.signature_key).to be_a(String)
expect(rsa_ca_cert.signature).to be_a(String)
expect(rsa_ca_cert.signed_data).to be_a(String)
end
it "parses certs issued by DSA CAs" do
expect(dsa_ca_cert.type_string).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(dsa_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(dsa_ca_cert.nonce).to be_a(String)
expect(dsa_ca_cert.key_data).to be_a(Hash)
expect(dsa_ca_cert.serial).to eq(123)
@ -119,11 +144,10 @@ describe SSHData::Certificate do
expect(dsa_ca_cert.reserved).to eq("")
expect(dsa_ca_cert.signature_key).to be_a(String)
expect(dsa_ca_cert.signature).to be_a(String)
expect(dsa_ca_cert.signed_data).to be_a(String)
end
it "parses certs issued by ECDSA CAs" do
expect(ecdsa_ca_cert.type_string).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(ecdsa_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(ecdsa_ca_cert.nonce).to be_a(String)
expect(ecdsa_ca_cert.key_data).to be_a(Hash)
expect(ecdsa_ca_cert.serial).to eq(123)
@ -137,11 +161,10 @@ describe SSHData::Certificate do
expect(ecdsa_ca_cert.reserved).to eq("")
expect(ecdsa_ca_cert.signature_key).to be_a(String)
expect(ecdsa_ca_cert.signature).to be_a(String)
expect(ecdsa_ca_cert.signed_data).to be_a(String)
end
it "parses certs issued by ED25519 CAs" do
expect(ed25519_ca_cert.type_string).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(ed25519_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(ed25519_ca_cert.nonce).to be_a(String)
expect(ed25519_ca_cert.key_data).to be_a(Hash)
expect(ed25519_ca_cert.serial).to eq(123)
@ -155,6 +178,5 @@ describe SSHData::Certificate do
expect(ed25519_ca_cert.reserved).to eq("")
expect(ed25519_ca_cert.signature_key).to be_a(String)
expect(ed25519_ca_cert.signature).to be_a(String)
expect(ed25519_ca_cert.signed_data).to be_a(String)
end
end

Просмотреть файл

@ -1,174 +1,192 @@
require_relative "./spec_helper"
describe SSHData::Encoding do
let(:rsa_data) { described_class.parse_certificate(fixture("rsa_leaf_for_rsa_ca-cert.pub")) }
let(:dsa_data) { described_class.parse_certificate(fixture("dsa_leaf_for_rsa_ca-cert.pub")) }
let(:ecdsa_data) { described_class.parse_certificate(fixture("ecdsa_leaf_for_rsa_ca-cert.pub")) }
let(:ed25519_data) { described_class.parse_certificate(fixture("ed25519_leaf_for_rsa_ca-cert.pub")) }
let(:rsa_key_data) { described_class.decode_public_key(fixture("rsa_leaf_for_rsa_ca.pub", binary: true)).first }
let(:dsa_key_data) { described_class.decode_public_key(fixture("dsa_leaf_for_rsa_ca.pub", binary: true)).first }
let(:ecdsa_key_data) { described_class.decode_public_key(fixture("ecdsa_leaf_for_rsa_ca.pub", binary: true)).first }
let(:ed25519_key_data) { described_class.decode_public_key(fixture("ed25519_leaf_for_rsa_ca.pub", binary: true)).first }
let(:rsa_ca_data) { described_class.parse_certificate(fixture("rsa_leaf_for_rsa_ca-cert.pub")) }
let(:dsa_ca_data) { described_class.parse_certificate(fixture("rsa_leaf_for_dsa_ca-cert.pub")) }
let(:ecdsa_ca_data) { described_class.parse_certificate(fixture("rsa_leaf_for_ecdsa_ca-cert.pub")) }
let(:ed25519_ca_data) { described_class.parse_certificate(fixture("rsa_leaf_for_ed25519_ca-cert.pub")) }
let(:rsa_cert_data) { described_class.decode_certificate(fixture("rsa_leaf_for_rsa_ca-cert.pub", binary: true)).first }
let(:dsa_cert_data) { described_class.decode_certificate(fixture("dsa_leaf_for_rsa_ca-cert.pub", binary: true)).first }
let(:ecdsa_cert_data) { described_class.decode_certificate(fixture("ecdsa_leaf_for_rsa_ca-cert.pub", binary: true)).first }
let(:ed25519_cert_data) { described_class.decode_certificate(fixture("ed25519_leaf_for_rsa_ca-cert.pub", binary: true)).first }
it "raises on type mismatch" do
_, cert, host = fixture("rsa_leaf_for_rsa_ca-cert.pub").split(" ", 3)
bad_type_cert = [SSHData::Certificate::ED25519_CERT_TYPE, cert, host].join(" ")
expect {
described_class.parse_certificate(bad_type_cert)
}.to raise_error(SSHData::DecodeError)
let(:rsa_ca_data) { described_class.decode_certificate(fixture("rsa_leaf_for_rsa_ca-cert.pub", binary: true)).first }
let(:dsa_ca_data) { described_class.decode_certificate(fixture("rsa_leaf_for_dsa_ca-cert.pub", binary: true)).first }
let(:ecdsa_ca_data) { described_class.decode_certificate(fixture("rsa_leaf_for_ecdsa_ca-cert.pub", binary: true)).first }
let(:ed25519_ca_data) { described_class.decode_certificate(fixture("rsa_leaf_for_ed25519_ca-cert.pub", binary: true)).first }
it "can decode a public key at an offset" do
prefix = "foobar"
raw = fixture("rsa_leaf_for_rsa_ca.pub", binary: true)
with_prefix = [prefix, raw].join
data, _ = described_class.decode_public_key(with_prefix, nil, prefix.bytesize).first
expect(data).to eq(rsa_key_data)
end
it "doesn't require the user/host names" do
type, cert, _ = fixture("rsa_leaf_for_rsa_ca-cert.pub").split(" ", 3)
no_host_cert = [type, cert].join(" ")
expect {
described_class.parse_certificate(no_host_cert)
}.not_to raise_error
it "can skip the algo when decoding a public key" do
raw = fixture("rsa_leaf_for_rsa_ca.pub", binary: true)
algo, offset = described_class.read_string(raw)
data, _ = described_class.decode_public_key(raw, algo, offset)
expect(data).to eq(rsa_key_data)
end
it "can decode an RSA public key" do
expect(rsa_key_data[:algo]).to eq(SSHData::PublicKey::ALGO_RSA)
expect(rsa_key_data[:e]).to be_a(OpenSSL::BN)
expect(rsa_key_data[:n]).to be_a(OpenSSL::BN)
end
it "can decode an DSA public key" do
expect(dsa_key_data[:algo]).to eq(SSHData::PublicKey::ALGO_DSA)
expect(dsa_key_data[:p]).to be_a(OpenSSL::BN)
expect(dsa_key_data[:q]).to be_a(OpenSSL::BN)
expect(dsa_key_data[:g]).to be_a(OpenSSL::BN)
expect(dsa_key_data[:y]).to be_a(OpenSSL::BN)
end
it "can decode an ECDSA public key" do
expect(ecdsa_key_data[:algo]).to eq(SSHData::PublicKey::ALGO_ECDSA256)
expect(ecdsa_key_data[:curve]).to eq("nistp256")
expect(ecdsa_key_data[:public_key]).to be_a(String)
end
it "can decode an ED25519 public key" do
expect(ed25519_key_data[:algo]).to eq(SSHData::PublicKey::ALGO_ED25519)
expect(ed25519_key_data[:pk]).to be_a(String)
end
it "can decode a certificate at an offset" do
prefix = "foobar"
raw = fixture("rsa_leaf_for_rsa_ca-cert.pub", binary: true)
with_prefix = [prefix, raw].join
data = described_class.decode_certificate(with_prefix, prefix.bytesize).first
expect(data).to eq(rsa_cert_data)
end
it "can decode RSA certificates" do
expect(rsa_data[:type_string]).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(rsa_cert_data[:algo]).to eq(SSHData::Certificate::ALGO_RSA)
expect(rsa_data[:nonce]).to be_a(String)
expect(rsa_data[:nonce].length).to eq(32)
expect(rsa_cert_data[:nonce]).to be_a(String)
expect(rsa_cert_data[:nonce].length).to eq(32)
expect(rsa_data[:key_data][:e]).to be_a(OpenSSL::BN)
expect(rsa_data[:key_data][:e]).not_to eq(OpenSSL::BN.new(0))
expect(rsa_cert_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_RSA)
expect(rsa_cert_data[:key_data][:e]).to be_a(OpenSSL::BN)
expect(rsa_cert_data[:key_data][:n]).to be_a(OpenSSL::BN)
expect(rsa_data[:key_data][:n]).to be_a(OpenSSL::BN)
expect(rsa_data[:key_data][:n]).not_to eq(OpenSSL::BN.new(0))
expect(rsa_cert_data[:serial]).to eq(123)
expect(rsa_cert_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(rsa_cert_data[:key_id]).to eq("my-ident")
expect(rsa_cert_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(rsa_cert_data[:valid_after]).to eq(0)
expect(rsa_cert_data[:valid_before]).to eq((2**64)-1)
expect(rsa_cert_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(rsa_cert_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(rsa_cert_data[:reserved]).to eq("")
expect(rsa_data[:serial]).to eq(123)
expect(rsa_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(rsa_data[:key_id]).to eq("my-ident")
expect(rsa_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(rsa_data[:valid_after]).to eq(0)
expect(rsa_data[:valid_before]).to eq((2**64)-1)
expect(rsa_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(rsa_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(rsa_data[:reserved]).to eq("")
expect(rsa_cert_data[:signature_key]).to be_a(String)
expect(rsa_cert_data[:signature_key].bytesize).to eq(279)
expect(rsa_data[:signature_key]).to be_a(String)
expect(rsa_data[:signature_key].bytesize).to eq(279)
expect(rsa_data[:signed_data]).to be_a(String)
expect(rsa_data[:signed_data].bytesize).to eq(392)
expect(rsa_data[:signature]).to be_a(String)
expect(rsa_data[:signature].bytesize).to eq(271)
expect(rsa_cert_data[:signature]).to be_a(String)
expect(rsa_cert_data[:signature].bytesize).to eq(271)
end
it "can decode DSA certificates" do
expect(dsa_data[:type_string]).to eq(SSHData::Certificate::DSA_CERT_TYPE)
expect(dsa_cert_data[:algo]).to eq(SSHData::Certificate::ALGO_DSA)
expect(dsa_data[:nonce]).to be_a(String)
expect(dsa_data[:nonce].length).to eq(32)
expect(dsa_cert_data[:nonce]).to be_a(String)
expect(dsa_cert_data[:nonce].length).to eq(32)
expect(dsa_data[:key_data][:p]).to be_a(OpenSSL::BN)
expect(dsa_data[:key_data][:p]).not_to eq(OpenSSL::BN.new(0))
expect(dsa_cert_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_DSA)
expect(dsa_cert_data[:key_data][:p]).to be_a(OpenSSL::BN)
expect(dsa_cert_data[:key_data][:q]).to be_a(OpenSSL::BN)
expect(dsa_cert_data[:key_data][:g]).to be_a(OpenSSL::BN)
expect(dsa_cert_data[:key_data][:y]).to be_a(OpenSSL::BN)
expect(dsa_data[:key_data][:q]).to be_a(OpenSSL::BN)
expect(dsa_data[:key_data][:q]).not_to eq(OpenSSL::BN.new(0))
expect(dsa_cert_data[:serial]).to eq(123)
expect(dsa_cert_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(dsa_cert_data[:key_id]).to eq("my-ident")
expect(dsa_cert_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(dsa_cert_data[:valid_after]).to eq(0)
expect(dsa_cert_data[:valid_before]).to eq((2**64)-1)
expect(dsa_cert_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(dsa_cert_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(dsa_cert_data[:reserved]).to eq("")
expect(dsa_data[:key_data][:g]).to be_a(OpenSSL::BN)
expect(dsa_data[:key_data][:g]).not_to eq(OpenSSL::BN.new(0))
expect(dsa_cert_data[:signature_key]).to be_a(String)
expect(dsa_cert_data[:signature_key].bytesize).to eq(279)
expect(dsa_data[:key_data][:y]).to be_a(OpenSSL::BN)
expect(dsa_data[:key_data][:y]).not_to eq(OpenSSL::BN.new(0))
expect(dsa_data[:serial]).to eq(123)
expect(dsa_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(dsa_data[:key_id]).to eq("my-ident")
expect(dsa_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(dsa_data[:valid_after]).to eq(0)
expect(dsa_data[:valid_before]).to eq((2**64)-1)
expect(dsa_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(dsa_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(dsa_data[:reserved]).to eq("")
expect(dsa_data[:signature_key]).to be_a(String)
expect(dsa_data[:signature_key].bytesize).to eq(279)
expect(dsa_data[:signed_data]).to be_a(String)
expect(dsa_data[:signed_data].bytesize).to eq(392)
expect(dsa_data[:signature]).to be_a(String)
expect(dsa_data[:signature].bytesize).to eq(271)
expect(dsa_cert_data[:signature]).to be_a(String)
expect(dsa_cert_data[:signature].bytesize).to eq(271)
end
it "can decode ECDSA certificates" do
expect(ecdsa_data[:type_string]).to eq(SSHData::Certificate::ECDSA_SHA2_NISTP256_CERT_TYPE)
expect(ecdsa_cert_data[:algo]).to eq(SSHData::Certificate::ALGO_ECDSA256)
expect(ecdsa_data[:nonce]).to be_a(String)
expect(ecdsa_data[:nonce].length).to eq(32)
expect(ecdsa_cert_data[:nonce]).to be_a(String)
expect(ecdsa_cert_data[:nonce].length).to eq(32)
expect(ecdsa_data[:key_data][:curve]).to be_a(String)
expect(ecdsa_data[:key_data][:curve]).to eq("nistp256")
expect(ecdsa_cert_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_ECDSA256)
expect(ecdsa_cert_data[:key_data][:curve]).to eq("nistp256")
expect(ecdsa_cert_data[:key_data][:public_key]).to be_a(String)
expect(ecdsa_data[:key_data][:public_key]).to be_a(String)
expect(ecdsa_data[:key_data][:public_key].length).to eq(65)
expect(ecdsa_cert_data[:serial]).to eq(123)
expect(ecdsa_cert_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(ecdsa_cert_data[:key_id]).to eq("my-ident")
expect(ecdsa_cert_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(ecdsa_cert_data[:valid_after]).to eq(0)
expect(ecdsa_cert_data[:valid_before]).to eq((2**64)-1)
expect(ecdsa_cert_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ecdsa_cert_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ecdsa_cert_data[:reserved]).to eq("")
expect(ecdsa_data[:serial]).to eq(123)
expect(ecdsa_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(ecdsa_data[:key_id]).to eq("my-ident")
expect(ecdsa_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(ecdsa_data[:valid_after]).to eq(0)
expect(ecdsa_data[:valid_before]).to eq((2**64)-1)
expect(ecdsa_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ecdsa_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ecdsa_data[:reserved]).to eq("")
expect(ecdsa_cert_data[:signature_key]).to be_a(String)
expect(ecdsa_cert_data[:signature_key].bytesize).to eq(279)
expect(ecdsa_data[:signature_key]).to be_a(String)
expect(ecdsa_data[:signature_key].bytesize).to eq(279)
expect(ecdsa_data[:signed_data]).to be_a(String)
expect(ecdsa_data[:signed_data].bytesize).to eq(392)
expect(ecdsa_data[:signature]).to be_a(String)
expect(ecdsa_data[:signature].bytesize).to eq(271)
expect(ecdsa_cert_data[:signature]).to be_a(String)
expect(ecdsa_cert_data[:signature].bytesize).to eq(271)
end
it "can decode ED25519 certificates" do
expect(ed25519_data[:type_string]).to eq(SSHData::Certificate::ED25519_CERT_TYPE)
expect(ed25519_cert_data[:algo]).to eq(SSHData::Certificate::ALGO_ED25519)
expect(ed25519_data[:nonce]).to be_a(String)
expect(ed25519_data[:nonce].length).to eq(32)
expect(ed25519_cert_data[:nonce]).to be_a(String)
expect(ed25519_cert_data[:nonce].length).to eq(32)
expect(ed25519_data[:key_data][:pk]).to be_a(String)
expect(ed25519_data[:key_data][:pk].length).to eq(32)
expect(ed25519_cert_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_ED25519)
expect(ed25519_cert_data[:key_data][:pk]).to be_a(String)
expect(ed25519_data[:serial]).to eq(123)
expect(ed25519_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(ed25519_data[:key_id]).to eq("my-ident")
expect(ed25519_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(ed25519_data[:valid_after]).to eq(0)
expect(ed25519_data[:valid_before]).to eq((2**64)-1)
expect(ed25519_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ed25519_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ed25519_data[:reserved]).to eq("")
expect(ed25519_cert_data[:serial]).to eq(123)
expect(ed25519_cert_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
expect(ed25519_cert_data[:key_id]).to eq("my-ident")
expect(ed25519_cert_data[:valid_principals]).to eq("\x00\x00\x00\x0Cmy-principal")
expect(ed25519_cert_data[:valid_after]).to eq(0)
expect(ed25519_cert_data[:valid_before]).to eq((2**64)-1)
expect(ed25519_cert_data[:critical_options]).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ed25519_cert_data[:extensions]).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ed25519_cert_data[:reserved]).to eq("")
expect(ed25519_data[:signature_key]).to be_a(String)
expect(ed25519_data[:signature_key].bytesize).to eq(279)
expect(ed25519_cert_data[:signature_key]).to be_a(String)
expect(ed25519_cert_data[:signature_key].bytesize).to eq(279)
expect(ed25519_data[:signed_data]).to be_a(String)
expect(ed25519_data[:signed_data].bytesize).to eq(392)
expect(ed25519_data[:signature]).to be_a(String)
expect(ed25519_data[:signature].bytesize).to eq(271)
expect(ed25519_cert_data[:signature]).to be_a(String)
expect(ed25519_cert_data[:signature].bytesize).to eq(271)
end
it "can decode certs from RSA CAs" do
expect(rsa_ca_data[:type_string]).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(rsa_ca_data[:algo]).to eq(SSHData::Certificate::ALGO_RSA)
expect(rsa_ca_data[:nonce]).to be_a(String)
expect(rsa_ca_data[:nonce].length).to eq(32)
expect(rsa_ca_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_RSA)
expect(rsa_ca_data[:key_data][:e]).to be_a(OpenSSL::BN)
expect(rsa_ca_data[:key_data][:e]).not_to eq(OpenSSL::BN.new(0))
expect(rsa_ca_data[:key_data][:n]).to be_a(OpenSSL::BN)
expect(rsa_ca_data[:key_data][:n]).not_to eq(OpenSSL::BN.new(0))
expect(rsa_ca_data[:serial]).to eq(123)
expect(rsa_ca_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
@ -183,24 +201,19 @@ describe SSHData::Encoding do
expect(rsa_ca_data[:signature_key]).to be_a(String)
expect(rsa_ca_data[:signature_key].bytesize).to eq(279)
expect(rsa_ca_data[:signed_data]).to be_a(String)
expect(rsa_ca_data[:signed_data].bytesize).to eq(392)
expect(rsa_ca_data[:signature]).to be_a(String)
expect(rsa_ca_data[:signature].bytesize).to eq(271)
end
it "can decode certs from DSA CAs" do
expect(dsa_ca_data[:type_string]).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(dsa_ca_data[:algo]).to eq(SSHData::Certificate::ALGO_RSA)
expect(dsa_ca_data[:nonce]).to be_a(String)
expect(dsa_ca_data[:nonce].length).to eq(32)
expect(dsa_ca_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_RSA)
expect(dsa_ca_data[:key_data][:e]).to be_a(OpenSSL::BN)
expect(dsa_ca_data[:key_data][:e]).not_to eq(OpenSSL::BN.new(0))
expect(dsa_ca_data[:key_data][:n]).to be_a(OpenSSL::BN)
expect(dsa_ca_data[:key_data][:n]).not_to eq(OpenSSL::BN.new(0))
expect(dsa_ca_data[:serial]).to eq(123)
expect(dsa_ca_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
@ -213,21 +226,18 @@ describe SSHData::Encoding do
expect(dsa_ca_data[:reserved]).to eq("")
expect(dsa_ca_data[:signature_key]).to be_a(String)
expect(dsa_ca_data[:signed_data]).to be_a(String)
expect(dsa_ca_data[:signature]).to be_a(String)
end
it "can decode certs from ECDSA CAs" do
expect(ecdsa_ca_data[:type_string]).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(ecdsa_ca_data[:algo]).to eq(SSHData::Certificate::ALGO_RSA)
expect(ecdsa_ca_data[:nonce]).to be_a(String)
expect(ecdsa_ca_data[:nonce].length).to eq(32)
expect(ecdsa_ca_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_RSA)
expect(ecdsa_ca_data[:key_data][:e]).to be_a(OpenSSL::BN)
expect(ecdsa_ca_data[:key_data][:e]).not_to eq(OpenSSL::BN.new(0))
expect(ecdsa_ca_data[:key_data][:n]).to be_a(OpenSSL::BN)
expect(ecdsa_ca_data[:key_data][:n]).not_to eq(OpenSSL::BN.new(0))
expect(ecdsa_ca_data[:serial]).to eq(123)
expect(ecdsa_ca_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
@ -240,21 +250,18 @@ describe SSHData::Encoding do
expect(ecdsa_ca_data[:reserved]).to eq("")
expect(ecdsa_ca_data[:signature_key]).to be_a(String)
expect(ecdsa_ca_data[:signed_data]).to be_a(String)
expect(ecdsa_ca_data[:signature]).to be_a(String)
end
it "can decode certs from ED25519 CAs" do
expect(ed25519_ca_data[:type_string]).to eq(SSHData::Certificate::RSA_CERT_TYPE)
expect(ed25519_ca_data[:algo]).to eq(SSHData::Certificate::ALGO_RSA)
expect(ed25519_ca_data[:nonce]).to be_a(String)
expect(ed25519_ca_data[:nonce].length).to eq(32)
expect(ed25519_ca_data[:key_data][:algo]).to eq(SSHData::PublicKey::ALGO_RSA)
expect(ed25519_ca_data[:key_data][:e]).to be_a(OpenSSL::BN)
expect(ed25519_ca_data[:key_data][:e]).not_to eq(OpenSSL::BN.new(0))
expect(ed25519_ca_data[:key_data][:n]).to be_a(OpenSSL::BN)
expect(ed25519_ca_data[:key_data][:n]).not_to eq(OpenSSL::BN.new(0))
expect(ed25519_ca_data[:serial]).to eq(123)
expect(ed25519_ca_data[:type]).to eq(SSHData::Certificate::TYPE_USER)
@ -267,7 +274,6 @@ describe SSHData::Encoding do
expect(ed25519_ca_data[:reserved]).to eq("")
expect(ed25519_ca_data[:signature_key]).to be_a(String)
expect(ed25519_ca_data[:signed_data]).to be_a(String)
expect(ed25519_ca_data[:signature]).to be_a(String)
end
end

Просмотреть файл

@ -3,6 +3,9 @@ require "ssh_data"
REPO_PATH = File.expand_path(File.join(__FILE__, "..", ".."))
FIXTURE_PATH = File.expand_path(File.join(REPO_PATH, "spec", "fixtures"))
def fixture(name)
File.read(File.join(FIXTURE_PATH, name))
def fixture(name, binary: false)
data = File.read(File.join(FIXTURE_PATH, name))
return data unless binary
_, b64, _ = data.split(" ", 3)
Base64.decode64(b64)
end