parse extension and critical options from the cert

This commit is contained in:
Ben Toews 2019-01-23 15:22:02 -07:00
Родитель 77e65398b5
Коммит d0dd7c2df6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
4 изменённых файлов: 84 добавлений и 27 удалений

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

@ -41,10 +41,12 @@ class SSHData::Certificate
end
# Parse data into better types, where possible.
data[:valid_after] = Time.at(data[:valid_after])
data[:valid_before] = Time.at(data[:valid_before])
data[:public_key] = SSHData::PublicKey.from_data(data.delete(:key_data))
data[:valid_principals], _ = SSHData::Encoding.decode_strings(data[:valid_principals])
valid_after = Time.at(data.delete(:valid_after))
valid_before = Time.at(data.delete(:valid_before))
public_key = SSHData::PublicKey.from_data(data.delete(:key_data))
valid_principals, _ = SSHData::Encoding.decode_strings(data.delete(:valid_principals))
critical_options, _ = SSHData::Encoding.decode_options(data.delete(:critical_options))
extensions, _ = SSHData::Encoding.decode_options(data.delete(:extensions))
# The signature key is encoded as a string, but we can parse it.
sk_raw = data.delete(:signature_key)
@ -52,7 +54,7 @@ class SSHData::Certificate
if read != sk_raw.bytesize
raise SSHData::DecodeError, "unexpected trailing data"
end
data[:ca_key] = SSHData::PublicKey.from_data(sk_data)
ca_key = SSHData::PublicKey.from_data(sk_data)
unless unsafe_no_verify
# The signature is the last field. The signature is calculated over all
@ -60,12 +62,20 @@ class SSHData::Certificate
signed_data_len = raw.bytesize - data[:signature].bytesize - 4
signed_data = raw.byteslice(0, signed_data_len)
unless data[:ca_key].verify(signed_data, data[:signature])
unless ca_key.verify(signed_data, data[:signature])
raise SSHData::VerifyError
end
end
new(**data)
new(**data.merge(
valid_after: valid_after,
valid_before: valid_before,
public_key: public_key,
valid_principals: valid_principals,
critical_options: critical_options,
extensions: extensions,
ca_key: ca_key,
))
end
# Intialize a new Certificate instance.
@ -80,11 +90,12 @@ class SSHData::Certificate
# type: - The certificate's Integer type field (one of TYPE_USER
# or TYPE_HOST).
# key_id: - The certificate's String key_id field.
# valid_principals: - The certificate's String valid_principals field.
# valid_principals: - The Array of Strings valid_principles field from the
# certificate.
# valid_after: - The certificate's Time valid_after field.
# valid_before: - The certificate's Time valid_before field.
# critical_options: - The certificate's String critical_options field.
# extensions: - The certificate's String extensions field.
# critical_options: - The Hash critical_options field from the certificate.
# extensions: - The Hash extensions field from the certificate.
# reserved: - The certificate's String reserved field.
# ca_key: - The issuing CA's public key as a PublicKey::Base
# subclass instance.

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

@ -214,7 +214,7 @@ module SSHData::Encoding
# Read a series of strings out of the provided data.
#
# data - A binary String.
# data - A binary String.
#
# Returns an Array including the Array of decoded Strings and the Integer
# number of bytes read.
@ -231,6 +231,34 @@ module SSHData::Encoding
[strs, total_read]
end
# Read a series of key/value pairs out of the provided data.
#
# data - A binary String.
#
# Returns an Array including the Hash of decoded keys/values and the Integer
# number of bytes read.
def decode_options(data)
total_read = 0
opts = {}
while data.bytesize > total_read
key, read = decode_string(data, total_read)
total_read += read
value_data, read = decode_string(data, total_read)
total_read += read
value_str, read = decode_string(value_data)
if read != value_data.bytesize
raise SSHData::DecodeError, "bad options data"
end
opts[key] = value_str
end
[opts, total_read]
end
# Read a multi-precision integer from the provided data.
#
# data - A binary String.

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

@ -68,8 +68,8 @@ describe SSHData::Certificate do
expect(rsa_cert.valid_principals).to eq(["my-principal"])
expect(rsa_cert.valid_after).to eq(min_time)
expect(rsa_cert.valid_before).to eq(max_time)
expect(rsa_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(rsa_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(rsa_cert.critical_options).to eq({"foo" => "bar"})
expect(rsa_cert.extensions).to eq({"baz" => "qwer"})
expect(rsa_cert.reserved).to eq("")
expect(rsa_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(rsa_cert.signature).to be_a(String)
@ -85,8 +85,8 @@ describe SSHData::Certificate do
expect(dsa_cert.valid_principals).to eq(["my-principal"])
expect(dsa_cert.valid_after).to eq(min_time)
expect(dsa_cert.valid_before).to eq(max_time)
expect(dsa_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(dsa_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(dsa_cert.critical_options).to eq({"foo" => "bar"})
expect(dsa_cert.extensions).to eq({"baz" => "qwer"})
expect(dsa_cert.reserved).to eq("")
expect(dsa_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(dsa_cert.signature).to be_a(String)
@ -102,8 +102,8 @@ describe SSHData::Certificate do
expect(ecdsa_cert.valid_principals).to eq(["my-principal"])
expect(ecdsa_cert.valid_after).to eq(min_time)
expect(ecdsa_cert.valid_before).to eq(max_time)
expect(ecdsa_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ecdsa_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ecdsa_cert.critical_options).to eq({"foo" => "bar"})
expect(ecdsa_cert.extensions).to eq({"baz" => "qwer"})
expect(ecdsa_cert.reserved).to eq("")
expect(ecdsa_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(ecdsa_cert.signature).to be_a(String)
@ -119,8 +119,8 @@ describe SSHData::Certificate do
expect(ed25519_cert.valid_principals).to eq(["my-principal"])
expect(ed25519_cert.valid_after).to eq(min_time)
expect(ed25519_cert.valid_before).to eq(max_time)
expect(ed25519_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ed25519_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ed25519_cert.critical_options).to eq({"foo" => "bar"})
expect(ed25519_cert.extensions).to eq({"baz" => "qwer"})
expect(ed25519_cert.reserved).to eq("")
expect(ed25519_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(ed25519_cert.signature).to be_a(String)
@ -136,8 +136,8 @@ describe SSHData::Certificate do
expect(rsa_ca_cert.valid_principals).to eq(["my-principal"])
expect(rsa_ca_cert.valid_after).to eq(min_time)
expect(rsa_ca_cert.valid_before).to eq(max_time)
expect(rsa_ca_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(rsa_ca_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(rsa_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(rsa_ca_cert.extensions).to eq({"baz" => "qwer"})
expect(rsa_ca_cert.reserved).to eq("")
expect(rsa_ca_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(rsa_ca_cert.signature).to be_a(String)
@ -153,8 +153,8 @@ describe SSHData::Certificate do
expect(dsa_ca_cert.valid_principals).to eq(["my-principal"])
expect(dsa_ca_cert.valid_after).to eq(min_time)
expect(dsa_ca_cert.valid_before).to eq(max_time)
expect(dsa_ca_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(dsa_ca_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(dsa_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(dsa_ca_cert.extensions).to eq({"baz" => "qwer"})
expect(dsa_ca_cert.reserved).to eq("")
expect(dsa_ca_cert.ca_key).to be_a(SSHData::PublicKey::DSA)
expect(dsa_ca_cert.signature).to be_a(String)
@ -170,8 +170,8 @@ describe SSHData::Certificate do
expect(ecdsa_ca_cert.valid_principals).to eq(["my-principal"])
expect(ecdsa_ca_cert.valid_after).to eq(min_time)
expect(ecdsa_ca_cert.valid_before).to eq(max_time)
expect(ecdsa_ca_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ecdsa_ca_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ecdsa_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(ecdsa_ca_cert.extensions).to eq({"baz" => "qwer"})
expect(ecdsa_ca_cert.reserved).to eq("")
expect(ecdsa_ca_cert.ca_key).to be_a(SSHData::PublicKey::ECDSA)
expect(ecdsa_ca_cert.signature).to be_a(String)
@ -187,8 +187,8 @@ describe SSHData::Certificate do
expect(ed25519_ca_cert.valid_principals).to eq(["my-principal"])
expect(ed25519_ca_cert.valid_after).to eq(min_time)
expect(ed25519_ca_cert.valid_before).to eq(max_time)
expect(ed25519_ca_cert.critical_options).to eq("\x00\x00\x00\x03foo\x00\x00\x00\x07\x00\x00\x00\x03bar")
expect(ed25519_ca_cert.extensions).to eq("\x00\x00\x00\x03baz\x00\x00\x00\x08\x00\x00\x00\x04qwer")
expect(ed25519_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(ed25519_ca_cert.extensions).to eq({"baz" => "qwer"})
expect(ed25519_ca_cert.reserved).to eq("")
expect(ed25519_ca_cert.ca_key).to be_a(SSHData::PublicKey::ED25519)
expect(ed25519_ca_cert.signature).to be_a(String)

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

@ -17,6 +17,24 @@ describe SSHData::Encoding do
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 options" do
opts = {"k1" => "v1", "k2" => "v2"}
encoded = opts.reduce("") do |cum, (k, v)|
cum + [
described_class.encode_string(k),
described_class.encode_string(described_class.encode_string(v))
].join
end
decoded, read = described_class.decode_options(encoded)
expect(decoded).to eq(opts)
expect(read).to eq(encoded.bytesize)
decoded, read = described_class.decode_options("")
expect(decoded).to eq({})
expect(read).to eq(0)
end
it "can decode a series of strings" do
strs = %w(one two three)
encoded = strs.map { |s| described_class.encode_string(s) }.join