ruby/test/rubygems/test_gem_security_policy.rb

536 строки
14 KiB
Ruby

# frozen_string_literal: true
require_relative "helper"
unless Gem::HAVE_OPENSSL
warn "Skipping Gem::Security::Policy tests. openssl not found."
end
class TestGemSecurityPolicy < Gem::TestCase
ALTERNATE_KEY = load_key "alternate"
INVALID_KEY = load_key "invalid"
CHILD_KEY = load_key "child"
GRANDCHILD_KEY = load_key "grandchild"
INVALIDCHILD_KEY = load_key "invalidchild"
ALTERNATE_CERT = load_cert "alternate"
CA_CERT = load_cert "ca"
CHILD_CERT = load_cert "child"
EXPIRED_CERT = load_cert "expired"
FUTURE_CERT = load_cert "future"
GRANDCHILD_CERT = load_cert "grandchild"
INVALIDCHILD_CERT = load_cert "invalidchild"
INVALID_ISSUER_CERT = load_cert "invalid_issuer"
INVALID_SIGNER_CERT = load_cert "invalid_signer"
WRONG_KEY_CERT = load_cert "wrong_key"
def setup
super
@spec = quick_gem "a" do |s|
s.description = "π"
s.files = %w[lib/code.rb]
end
@digest = OpenSSL::Digest.new Gem::Security::DIGEST_NAME
@trust_dir = Gem::Security.trust_dir.dir # HACK use the object
@no = Gem::Security::NoSecurity
@almost_no = Gem::Security::AlmostNoSecurity
@low = Gem::Security::LowSecurity
@medium = Gem::Security::MediumSecurity
@high = Gem::Security::HighSecurity
@chain = Gem::Security::Policy.new(
"Chain",
:verify_data => true,
:verify_signer => true,
:verify_chain => true,
:verify_root => false,
:only_trusted => false,
:only_signed => false
)
@root = Gem::Security::Policy.new(
"Root",
:verify_data => true,
:verify_signer => true,
:verify_chain => true,
:verify_root => true,
:only_trusted => false,
:only_signed => false
)
end
def test_check_data
data = digest "hello"
signature = sign data
assert @almost_no.check_data(PUBLIC_KEY, @digest, signature, data)
end
def test_check_data_invalid
data = digest "hello"
signature = sign data
invalid = digest "hello!"
e = assert_raise Gem::Security::Exception do
@almost_no.check_data PUBLIC_KEY, @digest, signature, invalid
end
assert_equal "invalid signature", e.message
end
def test_check_chain
chain = [PUBLIC_CERT, CHILD_CERT, GRANDCHILD_CERT]
assert @chain.check_chain chain, Time.now
end
def test_check_chain_empty_chain
e = assert_raise Gem::Security::Exception do
@chain.check_chain [], Time.now
end
assert_equal "empty signing chain", e.message
end
def test_check_chain_invalid
chain = [PUBLIC_CERT, CHILD_CERT, INVALIDCHILD_CERT]
e = assert_raise Gem::Security::Exception do
@chain.check_chain chain, Time.now
end
assert_equal "invalid signing chain: " +
"certificate #{INVALIDCHILD_CERT.subject} " +
"was not issued by #{CHILD_CERT.subject}", e.message
end
def test_check_chain_no_chain
e = assert_raise Gem::Security::Exception do
@chain.check_chain nil, Time.now
end
assert_equal "missing signing chain", e.message
end
def test_check_cert
assert @low.check_cert(PUBLIC_CERT, nil, Time.now)
end
def test_check_cert_expired
e = assert_raise Gem::Security::Exception do
@low.check_cert EXPIRED_CERT, nil, Time.now
end
assert_equal "certificate #{EXPIRED_CERT.subject} " +
"not valid after #{EXPIRED_CERT.not_after}",
e.message
end
def test_check_cert_future
e = assert_raise Gem::Security::Exception do
@low.check_cert FUTURE_CERT, nil, Time.now
end
assert_equal "certificate #{FUTURE_CERT.subject} " +
"not valid before #{FUTURE_CERT.not_before}",
e.message
end
def test_check_cert_invalid_issuer
e = assert_raise Gem::Security::Exception do
@low.check_cert INVALID_ISSUER_CERT, PUBLIC_CERT, Time.now
end
assert_equal "certificate #{INVALID_ISSUER_CERT.subject} " +
"was not issued by #{PUBLIC_CERT.subject}",
e.message
end
def test_check_cert_issuer
assert @low.check_cert(CHILD_CERT, PUBLIC_CERT, Time.now)
end
def test_check_cert_no_signer
e = assert_raise Gem::Security::Exception do
@high.check_cert(nil, nil, Time.now)
end
assert_equal "missing signing certificate", e.message
end
def test_check_key
assert @almost_no.check_key(PUBLIC_CERT, PRIVATE_KEY)
end
def test_check_key_no_signer
assert @almost_no.check_key(nil, nil)
e = assert_raise Gem::Security::Exception do
@high.check_key(nil, nil)
end
assert_equal "missing key or signature", e.message
end
def test_check_key_wrong_key
e = assert_raise Gem::Security::Exception do
@almost_no.check_key(PUBLIC_CERT, ALTERNATE_KEY)
end
assert_equal "certificate #{PUBLIC_CERT.subject} " +
"does not match the signing key", e.message
end
def test_check_root
chain = [PUBLIC_CERT, CHILD_CERT, INVALIDCHILD_CERT]
assert @chain.check_root chain, Time.now
end
def test_check_root_empty_chain
e = assert_raise Gem::Security::Exception do
@chain.check_root [], Time.now
end
assert_equal "missing root certificate", e.message
end
def test_check_root_invalid_signer
chain = [INVALID_SIGNER_CERT]
e = assert_raise Gem::Security::Exception do
@chain.check_root chain, Time.now
end
assert_equal "certificate #{INVALID_SIGNER_CERT.subject} " +
"was not issued by #{INVALID_SIGNER_CERT.issuer}",
e.message
end
def test_check_root_not_self_signed
chain = [INVALID_ISSUER_CERT]
e = assert_raise Gem::Security::Exception do
@chain.check_root chain, Time.now
end
assert_equal "root certificate #{INVALID_ISSUER_CERT.subject} " +
"is not self-signed (issuer #{INVALID_ISSUER_CERT.issuer})",
e.message
end
def test_check_root_no_chain
e = assert_raise Gem::Security::Exception do
@chain.check_root nil, Time.now
end
assert_equal "missing signing chain", e.message
end
def test_check_trust
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
assert @high.check_trust [PUBLIC_CERT], @digest, @trust_dir
end
def test_check_trust_child
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
assert @high.check_trust [PUBLIC_CERT, CHILD_CERT], @digest, @trust_dir
end
def test_check_trust_empty_chain
e = assert_raise Gem::Security::Exception do
@chain.check_trust [], @digest, @trust_dir
end
assert_equal "missing root certificate", e.message
end
def test_check_trust_mismatch
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
e = assert_raise Gem::Security::Exception do
@high.check_trust [WRONG_KEY_CERT], @digest, @trust_dir
end
assert_equal "trusted root certificate #{PUBLIC_CERT.subject} checksum " +
"does not match signing root certificate checksum", e.message
end
def test_check_trust_no_chain
e = assert_raise Gem::Security::Exception do
@chain.check_trust nil, @digest, @trust_dir
end
assert_equal "missing signing chain", e.message
end
def test_check_trust_no_trust
e = assert_raise Gem::Security::Exception do
@high.check_trust [PUBLIC_CERT], @digest, @trust_dir
end
assert_equal "root cert #{PUBLIC_CERT.subject} is not trusted", e.message
end
def test_check_trust_no_trust_child
e = assert_raise Gem::Security::Exception do
@high.check_trust [PUBLIC_CERT, CHILD_CERT], @digest, @trust_dir
end
assert_equal "root cert #{PUBLIC_CERT.subject} is not trusted " +
"(root of signing cert #{CHILD_CERT.subject})", e.message
end
def test_subject
assert_equal "email:nobody@example", @no.subject(PUBLIC_CERT)
assert_equal "/C=JP/ST=Tokyo/O=RubyGemsTest/CN=CA", @no.subject(CA_CERT)
end
def test_verify
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
assert @almost_no.verify [PUBLIC_CERT], nil, *dummy_signatures
end
def test_verify_chain_signatures
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
assert @high.verify [PUBLIC_CERT], nil, *dummy_signatures
end
def test_verify_chain_key
@almost_no.verify [PUBLIC_CERT], PRIVATE_KEY, *dummy_signatures
end
def test_verify_no_digests
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
_, signatures = dummy_signatures
e = assert_raise Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, {}, signatures
end
assert_equal "no digests provided (probable bug)", e.message
end
def test_verify_no_digests_no_security
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
_, signatures = dummy_signatures
e = assert_raise Gem::Security::Exception do
@no.verify [PUBLIC_CERT], nil, {}, signatures
end
assert_equal "missing digest for 0", e.message
end
def test_verify_no_signatures
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
digests, = dummy_signatures
use_ui @ui do
@no.verify [PUBLIC_CERT], nil, digests, {}, "some_gem"
end
assert_match "WARNING: some_gem is not signed\n", @ui.error
assert_raise Gem::Security::Exception do
@high.verify [PUBLIC_CERT], nil, digests, {}
end
end
def test_verify_no_signatures_no_digests
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
use_ui @ui do
@no.verify [PUBLIC_CERT], nil, {}, {}, "some_gem"
end
assert_empty @ui.output
assert_empty @ui.error
end
def test_verify_not_enough_signatures
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
digests, signatures = dummy_signatures
data = digest "goodbye"
signatures[1] = PRIVATE_KEY.sign @digest.new, data.digest
e = assert_raise Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, digests, signatures
end
assert_equal "missing digest for 1", e.message
end
def test_verify_no_trust
digests, signatures = dummy_signatures
use_ui @ui do
@low.verify [PUBLIC_CERT], nil, digests, signatures, "some_gem"
end
assert_equal "WARNING: email:nobody@example is not trusted for some_gem\n",
@ui.error
assert_raise Gem::Security::Exception do
@medium.verify [PUBLIC_CERT], nil, digests, signatures
end
end
def test_verify_wrong_digest_type
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
data = OpenSSL::Digest.new("SHA512")
data << "hello"
digests = { "SHA512" => { 0 => data } }
signature = PRIVATE_KEY.sign "sha512", data.digest
signatures = { 0 => signature }
e = assert_raise Gem::Security::Exception do
@almost_no.verify [PUBLIC_CERT], nil, digests, signatures
end
assert_equal "no digests provided (probable bug)", e.message
end
def test_verify_signatures_chain
@spec.cert_chain = [PUBLIC_CERT, CHILD_CERT]
assert @chain.verify_signatures @spec, *dummy_signatures(CHILD_KEY)
end
def test_verify_signatures_data
@spec.cert_chain = [PUBLIC_CERT]
@almost_no.verify_signatures @spec, *dummy_signatures
end
def test_verify_signatures_root
@spec.cert_chain = [PUBLIC_CERT, CHILD_CERT]
assert @root.verify_signatures @spec, *dummy_signatures(CHILD_KEY)
end
def test_verify_signatures_signer
@spec.cert_chain = [PUBLIC_CERT]
assert @low.verify_signatures @spec, *dummy_signatures
end
def test_verify_signatures_trust
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@spec.cert_chain = [PUBLIC_CERT]
assert @high.verify_signatures @spec, *dummy_signatures
end
def test_verify_signatures
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@spec.cert_chain = [PUBLIC_CERT.to_s]
metadata_gz = Gem::Util.gzip @spec.to_yaml
package = Gem::Package.new "nonexistent.gem"
package.checksums[Gem::Security::DIGEST_NAME] = {}
s = StringIO.new metadata_gz
def s.full_name() "metadata.gz" end
digests = package.digest s
metadata_gz_digest = digests[Gem::Security::DIGEST_NAME]["metadata.gz"]
signatures = {}
signatures["metadata.gz"] =
PRIVATE_KEY.sign @digest.new, metadata_gz_digest.digest
assert @high.verify_signatures @spec, digests, signatures
end
def test_verify_signatures_missing
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@spec.cert_chain = [PUBLIC_CERT.to_s]
metadata_gz = Gem::Util.gzip @spec.to_yaml
package = Gem::Package.new "nonexistent.gem"
package.checksums[Gem::Security::DIGEST_NAME] = {}
s = StringIO.new metadata_gz
def s.full_name() "metadata.gz" end
digests = package.digest s
digests[Gem::Security::DIGEST_NAME]["data.tar.gz"] = @digest.hexdigest "hello"
metadata_gz_digest = digests[Gem::Security::DIGEST_NAME]["metadata.gz"]
signatures = {}
signatures["metadata.gz"] =
PRIVATE_KEY.sign @digest.new, metadata_gz_digest.digest
e = assert_raise Gem::Security::Exception do
@high.verify_signatures @spec, digests, signatures
end
assert_equal "missing signature for data.tar.gz", e.message
end
def test_verify_signatures_none
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@spec.cert_chain = [PUBLIC_CERT.to_s]
metadata_gz = Gem::Util.gzip @spec.to_yaml
package = Gem::Package.new "nonexistent.gem"
package.checksums[Gem::Security::DIGEST_NAME] = {}
s = StringIO.new metadata_gz
def s.full_name() "metadata.gz" end
digests = package.digest s
digests[Gem::Security::DIGEST_NAME]["data.tar.gz"] = @digest.hexdigest "hello"
assert_raise Gem::Security::Exception do
@high.verify_signatures @spec, digests, {}
end
end
def digest(data)
digester = @digest.new
digester << data
digester
end
def sign(data, key = PRIVATE_KEY)
key.sign @digest.new, data.digest
end
def dummy_signatures(key = PRIVATE_KEY)
data = digest "hello"
digests = { Gem::Security::DIGEST_NAME => { 0 => data } }
signatures = { 0 => sign(data, key) }
return digests, signatures
end
end if Gem::HAVE_OPENSSL