This commit is contained in:
Ben Toews 2019-01-17 10:47:31 -07:00
Коммит f3d4c5435a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
14 изменённых файлов: 412 добавлений и 0 удалений

3
Gemfile Normal file
Просмотреть файл

@ -0,0 +1,3 @@
source "https://rubygems.org"
gemspec

41
Gemfile.lock Normal file
Просмотреть файл

@ -0,0 +1,41 @@
PATH
remote: .
specs:
sshcert (0.0.1)
GEM
remote: https://rubygems.org/
specs:
coderay (1.1.2)
diff-lcs (1.3)
method_source (0.8.2)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.4)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
slop (3.6.0)
PLATFORMS
ruby
DEPENDENCIES
pry (~> 0.10.4)
rspec (~> 3.5.0)
rspec-mocks (~> 3.5.0)
sshcert!
BUNDLED WITH
2.0.1

20
lib/sshcert.rb Normal file
Просмотреть файл

@ -0,0 +1,20 @@
require "sshcert/error"
require "sshcert/encoding"
class SSHCert
TYPE_USER = 1
TYPE_HOST = 2
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"
ECDSA_CERT_TYPES = [
ECDSA_SHA2_NISTP256_CERT_TYPE,
ECDSA_SHA2_NISTP384_CERT_TYPE,
ECDSA_SHA2_NISTP521_CERT_TYPE
]
end

209
lib/sshcert/encoding.rb Normal file
Просмотреть файл

@ -0,0 +1,209 @@
require "openssl"
require "base64"
class SSHCert
module Encoding
# Decode a certificate.
#
# cert - An SSH formatted certificate.
#
# Returns a Hash representing the decoded fields from the certificate.
def decode_cert(cert)
_, cert_b64, _ = cert.split(" ")
if cert_b64.nil?
raise DecodeError
end
cert_raw = Base64.decode64(cert_b64)
type, _ = read_string(cert_raw)
case type
when SSHCert::RSA_CERT_TYPE
decode_all(cert_raw, [
[:key_type, :string],
[:nonce, :string],
[:e, :mpint],
[:n, :mpint],
[: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],
])
when SSHCert::DSA_CERT_TYPE
decode_all(cert_raw, [
[:key_type, :string],
[:nonce, :string],
[:p, :mpint],
[:q, :mpint],
[:g, :mpint],
[:y, :mpint],
[: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],
])
when *SSHCert::ECDSA_CERT_TYPES
decode_all(cert_raw, [
[:key_type, :string],
[:nonce, :string],
[:curve, :string],
[:public_key, :string],
[: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],
])
when SSHCert::ED25519_CERT_TYPE
decode_all(cert_raw, [
[:key_type, :string],
[:nonce, :string],
[:pk, :string],
[: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],
])
else
raise DecodeError, "unknown cert type: #{type}"
end
end
# Decode all of the given fields from data.
#
# data - A binary String.
# fields - An Array of Arrays, each containing a symbol describing the field
# and a Symbol describing the type of the field (:mpint, :string,
# :uint64, or :uint32).
#
# Returns a Hash mapping the provide field keys to the decoded values.
def decode_all(data, fields)
fields.each_with_object({}) do |(key, type), result|
case type
when :string
string, data = read_string(data)
result[key] = string
when :mpint
mpint, data = read_mpint(data)
result[key] = mpint
when :uint64
uint64, data = read_uint64(data)
result[key] = uint64
when :uint32
uint32, data = read_uint32(data)
result[key] = uint32
else
raise SSHCert::DecodeError
end
end
end
# Read a string out of the provided data.
#
# data - A binary String.
#
# Returns an Array including the decoded String and the remaining binary
# String data.
def read_string(data)
if data.bytesize < 4
raise SSHCert::DecodeError, "data too short"
end
size_s, data = data.byteslice(0...4), data.byteslice(4..-1)
size = size_s.unpack("L>").first
if data.bytesize < size
raise SSHCert::DecodeError, "data too short"
end
[data.byteslice(0...size), data.byteslice(size..-1)]
end
# Read a multi-precision integer from the provided data.
#
# data - A binary String.
#
# Returns an Array including the decoded mpint as an OpenSSL::BN and the
# remaining binary String data.
def read_mpint(data)
if data.bytesize < 4
raise SSHCert::DecodeError, "data too short"
end
str_size_s = data.byteslice(0...4)
str_size = str_size_s.unpack("N").first
mpi_size = str_size + 4
if data.bytesize < mpi_size
raise SSHCert::DecodeError, "data too short"
end
mpi_s, data = data.slice(0...mpi_size), data.slice(mpi_size..-1)
# This calls OpenSSL's BN_mpi2bn() function. As far as I can tell, this
# matches up with with MPI type defined in RFC4251 Section 5 with the
# exception that OpenSSL doesn't enforce minimal length. We could enforce
# this ourselves, but it doesn't seem worth the added complexity.
mpi = OpenSSL::BN.new(mpi_s, 0)
[mpi, data]
end
# Read a uint64 from the provided data.
#
# data - A binary String.
#
# Returns an Array including the decoded uint64 as an Integer and the
# remaining binary String data.
def read_uint64(data)
if data.bytesize < 8
raise SSHCert::DecodeError, "data too short"
end
data.unpack("Q>a*")
end
# Read a uint32 from the provided data.
#
# data - A binary String.
#
# Returns an Array including the decoded uint32 as an Integer and the
# remaining binary String data.
def read_uint32(data)
if data.bytesize < 4
raise SSHCert::DecodeError, "data too short"
end
data.unpack("L>a*")
end
extend self
end
end

4
lib/sshcert/error.rb Normal file
Просмотреть файл

@ -0,0 +1,4 @@
class SSHCert
Error = Class.new(StandardError)
DecodeError = Class.new(Error)
end

50
spec/encoding_spec.rb Normal file
Просмотреть файл

@ -0,0 +1,50 @@
require_relative "./spec_helper"
describe SSHCert::Encoding do
let(:rsa_cert_string) { fixture("rsa_leaf_for_rsa_ca-cert.pub") }
let(:rsa_cert) { described_class.decode_cert(rsa_cert_string) }
it "can decode RSA certificates" do
expect(rsa_cert[:key_type]).to be_a(String)
expect(rsa_cert[:key_type]).to eq(SSHCert::RSA_CERT_TYPE)
expect(rsa_cert[:nonce]).to be_a(String)
expect(rsa_cert[:nonce].length).to eq(32)
expect(rsa_cert[:e]).to be_a(OpenSSL::BN)
expect(rsa_cert[:e]).not_to eq(OpenSSL::BN.new(0))
expect(rsa_cert[:n]).to be_a(OpenSSL::BN)
expect(rsa_cert[:n]).not_to eq(OpenSSL::BN.new(0))
expect(rsa_cert[:serial]).to be_a(Integer)
expect(rsa_cert[:serial]).to eq(0)
expect(rsa_cert[:type]).to be_a(Integer)
expect(rsa_cert[:type]).to eq(SSHCert::TYPE_USER)
expect(rsa_cert[:key_id]).to be_a(String)
expect(rsa_cert[:key_id]).to eq("key-id-rsa-leaf-for-rsa-ca")
expect(rsa_cert[:valid_principals]).to be_a(String)
expect(rsa_cert[:valid_principals]).to eq("")
expect(rsa_cert[:valid_after]).to be_a(Integer)
expect(rsa_cert[:valid_after]).to eq(0)
expect(rsa_cert[:valid_before]).to be_a(Integer)
expect(rsa_cert[:valid_before]).to eq((2**64)-1)
expect(rsa_cert[:critical_options]).to be_a(String)
expect(rsa_cert[:critical_options]).to eq("")
expect(rsa_cert[:extensions]).to be_a(String)
expect(rsa_cert[:reserved]).to be_a(String)
expect(rsa_cert[:reserved]).to eq("")
expect(rsa_cert[:signature_key]).to be_a(String)
expect(rsa_cert[:signature]).to be_a(String)
end
end

5
spec/fixtures/gen.sh поставляемый Executable file
Просмотреть файл

@ -0,0 +1,5 @@
#!/bin/bash
ssh-keygen -trsa -b2048 -N "" -f ./rsa_ca
ssh-keygen -trsa -b2048 -N "" -f ./rsa_leaf_for_rsa_ca
ssh-keygen -s rsa_ca -I key-id-rsa-leaf-for-rsa-ca rsa_leaf_for_rsa_ca.pub

28
spec/fixtures/rsa_ca поставляемый Normal file
Просмотреть файл

@ -0,0 +1,28 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAv66klSKoQufmBwNaDomCZntFcTbRrGEN3rkcZZdbGNVgJWDYxMRC
+AoSTCgjYuO8nYzbsGiHp8MBX+xJgvkvst/G+T1/UjR6oXyJDgk5DB4/UtsPZjNc3rcrvW
Uxf2t2lGYpDszaZe6kc7nLwhH3dGP8c/SuxqjVKAQfaT1KlyEd5ubPfQXEMY0uajdYoqxP
dKNTMhKhi9T5B3bOHSChbci+f3NZDrCKhur+ZxNcJbQEZpop/B/eotRApUdadXrIjrN3iB
liw9pB0C6r3gToFyQF56EbC7Orl7gBy9+Paufl32o1FqEmVNCVqd9WD/XGYBcPMBhGG6+q
74+7DtH88QAAA+DeND8L3jQ/CwAAAAdzc2gtcnNhAAABAQC/rqSVIqhC5+YHA1oOiYJme0
VxNtGsYQ3euRxll1sY1WAlYNjExEL4ChJMKCNi47ydjNuwaIenwwFf7EmC+S+y38b5PX9S
NHqhfIkOCTkMHj9S2w9mM1zetyu9ZTF/a3aUZikOzNpl7qRzucvCEfd0Y/xz9K7GqNUoBB
9pPUqXIR3m5s99BcQxjS5qN1iirE90o1MyEqGL1PkHds4dIKFtyL5/c1kOsIqG6v5nE1wl
tARmmin8H96i1EClR1p1esiOs3eIGWLD2kHQLqveBOgXJAXnoRsLs6uXuAHL349q5+Xfaj
UWoSZU0JWp31YP9cZgFw8wGEYbr6rvj7sO0fzxAAAAAwEAAQAAAQB3855jBZEubVhxwxM0
TbEg2LuYIaoMqZ+4ZTb0DEehvsWwHm8Ik5klB4hzyBp84+9A2MkSGBfsUAgoMwG9yJqk4Y
zupCFtHBIxxHEwK21bvAH8o8F7P2E6rn3cw9q51lIag+PlsyRo7XJUSUzNYdjtKTTui4xk
HXtm002k2go3kBiZZNcY5awUh/WP1tHmOO7THHfgenytWIjLnHiYVLE/a5OUXXNz8zHGCd
HX2XZDKlkcn1HPCDXqwR21maZPUQrXG1IBjYB0DJUxtIsSyPdJOxUnZ9/8ZjRBiG2UvGK5
vxdT3Qb3BBuqgMQmJ94N4E2BlgGuPbOrqRU/w/WOhgGRAAAAgGJGA09VB09czQslRVnwKp
6kOqAxkYS1goVf8PLeQzQT0iEUydeznmWBk4Hu+fvyk/rBT4U/PH6Ap7Jdf+2MWasinX4p
vTCEi7AgF9w1YmrJX58eK/YQY/zjvY3IJyxKygqxlovc3oJE7N9klPg5LkTfqMpGPsqO3+
BqMrCcct79AAAAgQDemeeRtVhFOML8V2rLdLA/ToF+I0eDlGNmqZpPiP4jzSCtntW9Pzl0
+m9ysD2VjYEcwOZmwmyKKp8W7ZFnnsiioyVgCKAbt/Qtcu9SDV2q2TCxcKOVTr92iYrw7W
rvLNtLp0u3YnnOfWHCsmYVWZNJk3AOxwZa4e/uzjsiYDqw4wAAAIEA3HEkZB7WVojOhuq/
W3x3n+qJiCyqbSRhB4RawgNV4/800Yh+/6ZgM9PemIM0d6QOEyxnS2NI++nYLZiBHcDNf/
rY3WqNqgLaj7flgpFIkkuX5/ZFtaaLL70f6cLnO9tDZrDwQc3zpFpjkmFjmpzHCeiR6HB2
51jUbYy2Kg6SZxsAAAAmbWFzdGFoeWV0aUBCZW5qYW1pbnMtTWFjQm9vay1Qcm8ubG9jYW
wBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----

1
spec/fixtures/rsa_ca.pub поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/rqSVIqhC5+YHA1oOiYJme0VxNtGsYQ3euRxll1sY1WAlYNjExEL4ChJMKCNi47ydjNuwaIenwwFf7EmC+S+y38b5PX9SNHqhfIkOCTkMHj9S2w9mM1zetyu9ZTF/a3aUZikOzNpl7qRzucvCEfd0Y/xz9K7GqNUoBB9pPUqXIR3m5s99BcQxjS5qN1iirE90o1MyEqGL1PkHds4dIKFtyL5/c1kOsIqG6v5nE1wltARmmin8H96i1EClR1p1esiOs3eIGWLD2kHQLqveBOgXJAXnoRsLs6uXuAHL349q5+XfajUWoSZU0JWp31YP9cZgFw8wGEYbr6rvj7sO0fzx mastahyeti@Benjamins-MacBook-Pro.local

28
spec/fixtures/rsa_leaf_for_rsa_ca поставляемый Normal file
Просмотреть файл

@ -0,0 +1,28 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEA6uxOJIc2j0lDSY05KxB/Y1yUyHl9Bf8WtNVn2h+CC6C4oEJfgTwM
q5sxpQP/CT+PwX20unHlEtXYI9z91yVzGbQuOaUfpZkNX54WTRpVPLFWa9rCH1zyROYqHI
NcFpjfGtDsf4vrQ0DWMDo3HyroGONqsCY2Pogdb6ctrQK1nIpUV8F4vJOyzzsjCtuZ9djt
vZ26tvYf0IkgJ8Bn9oc3aeHTnmogjcUemOvjr8UzIqBktZMrrtuhPwzDK+NEfYfcIk9igB
mgsVPInSDPvkaxUGKdatxXJSgOrzlGZHpVp6clzCX71NCPuAySa4lm3UjZBNBMGJPQGhm5
AmkbW9sn6QAAA+AIhV11CIVddQAAAAdzc2gtcnNhAAABAQDq7E4khzaPSUNJjTkrEH9jXJ
TIeX0F/xa01WfaH4ILoLigQl+BPAyrmzGlA/8JP4/BfbS6ceUS1dgj3P3XJXMZtC45pR+l
mQ1fnhZNGlU8sVZr2sIfXPJE5iocg1wWmN8a0Ox/i+tDQNYwOjcfKugY42qwJjY+iB1vpy
2tArWcilRXwXi8k7LPOyMK25n12O29nbq29h/QiSAnwGf2hzdp4dOeaiCNxR6Y6+OvxTMi
oGS1kyuu26E/DMMr40R9h9wiT2KAGaCxU8idIM++RrFQYp1q3FclKA6vOUZkelWnpyXMJf
vU0I+4DJJriWbdSNkE0EwYk9AaGbkCaRtb2yfpAAAAAwEAAQAAAQAhQf72EOZadqa9/O5A
+H7wVCUEFJ7sgGZ4h1XCfX9tof1BQuQ+aX/ps3jdwakTaBoz7FXvpzwk5H1K5nhRQw3+b5
Gzu3q1eOt5w+moVLgIdZqkfj3JLuFle6gujTI2SMIdC8LPc8GEreMkxLkVAoAFNfzenypC
xAZWzjYbLi0D0hQXGzq9VF8W9ti/xDL/A+p+6khAvMBcOpUAFqhQi2Dkoo1AcrTtXimWzf
9eX+3L8CC9jTWD0u63GdAD+sNzqOc2Ub/gJDKTGn//m3FkrcUIykk5Te18gQ1yxxInz1d3
57OMUeFNdN5wRJewQiZncyMnbdm5wvwm4dQNUyxiXRJZAAAAgEmPs5K9zpDwq+KwUJ7hj1
pmXSq1KpctJreIcWltkQmDl9fheAxsgu1sa9A67uLeRFS6YW4dW/0PXOxUxaIkn2X+8KY3
ZOH/guSiY+xzKpPWTGtvpKKmKrfXBeAj3f+swQVvQ86y7kP+A7hkO2Fh0x1KUuhokaQDP8
M6iiyDpIGNAAAAgQD6ZpwxX32KIBshGXz20j67okOU2sDZ3Fs34qUeMs8Yi+XqMZvAYLMh
DS15WhuP9odKpwueY3Ane1DirlWbqKj8kh516pgZSSYBMzlnIOfP77mzW5KOc1QwTmCwhD
OTZrvfqk+XdhYngZ1LcIdu3SnkV0Em12J2dtmol1DDlygmTwAAAIEA8C0YNE9Cg/otbN+E
2+Pk++SWfL9RqbMzjq/z7jxcTzVNCS8AOW9C6Zj/2/Lm8UMAamn4pnw+3zFCmy9Hz9XMhy
3vvwE5Rd4F2a0+hF57zgww/5fITL0gInr9M7vsf0fXCVGIURLmcnIPen/JHFKiVTwgsJsR
gt0xvuEZuwCk+EcAAAAmbWFzdGFoeWV0aUBCZW5qYW1pbnMtTWFjQm9vay1Qcm8ubG9jYW
wBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----

1
spec/fixtures/rsa_leaf_for_rsa_ca-cert.pub поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgaT58aLCcercY7UCAgDrldyZni0U0uxorzMiS9A8FkbQAAAADAQABAAABAQDq7E4khzaPSUNJjTkrEH9jXJTIeX0F/xa01WfaH4ILoLigQl+BPAyrmzGlA/8JP4/BfbS6ceUS1dgj3P3XJXMZtC45pR+lmQ1fnhZNGlU8sVZr2sIfXPJE5iocg1wWmN8a0Ox/i+tDQNYwOjcfKugY42qwJjY+iB1vpy2tArWcilRXwXi8k7LPOyMK25n12O29nbq29h/QiSAnwGf2hzdp4dOeaiCNxR6Y6+OvxTMioGS1kyuu26E/DMMr40R9h9wiT2KAGaCxU8idIM++RrFQYp1q3FclKA6vOUZkelWnpyXMJfvU0I+4DJJriWbdSNkE0EwYk9AaGbkCaRtb2yfpAAAAAAAAAAAAAAABAAAAGmtleS1pZC1yc2EtbGVhZi1mb3ItcnNhLWNhAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAv66klSKoQufmBwNaDomCZntFcTbRrGEN3rkcZZdbGNVgJWDYxMRC+AoSTCgjYuO8nYzbsGiHp8MBX+xJgvkvst/G+T1/UjR6oXyJDgk5DB4/UtsPZjNc3rcrvWUxf2t2lGYpDszaZe6kc7nLwhH3dGP8c/SuxqjVKAQfaT1KlyEd5ubPfQXEMY0uajdYoqxPdKNTMhKhi9T5B3bOHSChbci+f3NZDrCKhur+ZxNcJbQEZpop/B/eotRApUdadXrIjrN3iBliw9pB0C6r3gToFyQF56EbC7Orl7gBy9+Paufl32o1FqEmVNCVqd9WD/XGYBcPMBhGG6+q74+7DtH88QAAAQ8AAAAHc3NoLXJzYQAAAQC9wy38jUvI7JppNupdVAxVyCLaUwHjznl8oapINBctp/SGdB1v6peCWwDECWi+ULIcPGUx/XdSyJB1ubLlqG/CvCFRRpA+ICMBrVeGtZc8EN9zTKPtkmSnPFN0qg8+1aTRfesxKjY/84YwsEDPYs6q1TYVodw4+1WB/mFfEkw7ChlMqVAARDBoZvP8AvKE9nNQKrbDUSNu4KN5JKTRrF3lX3S15en6XtK38RldCLJU0Z/XEZ7qktoDOzvKLdb+/G4i9pG2x/YlGzn6PH7v2na7vSTym/e/Gi+aBfQkICmZ9TyfuF5zL6nQE1juVxh/SOL08Kx7EvRqheHIsV2siZRJ mastahyeti@Benjamins-MacBook-Pro.local

1
spec/fixtures/rsa_leaf_for_rsa_ca.pub поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDq7E4khzaPSUNJjTkrEH9jXJTIeX0F/xa01WfaH4ILoLigQl+BPAyrmzGlA/8JP4/BfbS6ceUS1dgj3P3XJXMZtC45pR+lmQ1fnhZNGlU8sVZr2sIfXPJE5iocg1wWmN8a0Ox/i+tDQNYwOjcfKugY42qwJjY+iB1vpy2tArWcilRXwXi8k7LPOyMK25n12O29nbq29h/QiSAnwGf2hzdp4dOeaiCNxR6Y6+OvxTMioGS1kyuu26E/DMMr40R9h9wiT2KAGaCxU8idIM++RrFQYp1q3FclKA6vOUZkelWnpyXMJfvU0I+4DJJriWbdSNkE0EwYk9AaGbkCaRtb2yfp mastahyeti@Benjamins-MacBook-Pro.local

8
spec/spec_helper.rb Normal file
Просмотреть файл

@ -0,0 +1,8 @@
require "sshcert"
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))
end

13
sshcert.gemspec Normal file
Просмотреть файл

@ -0,0 +1,13 @@
Gem::Specification.new do |s|
s.name = "sshcert"
s.summary = "Library for parsing SSH certificates"
s.version = "0.0.1"
s.authors = ["mastahyeti"]
s.required_ruby_version = "~> 2.3"
s.files = Dir["./lib/**/*.rb"]
s.add_development_dependency "pry", "~> 0.10.4"
s.add_development_dependency "rspec", "~> 3.5.0"
s.add_development_dependency "rspec-mocks", "~> 3.5.0"
end