parse extension and critical options from the cert
This commit is contained in:
Родитель
77e65398b5
Коммит
d0dd7c2df6
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче