diff options
Diffstat (limited to 'jni/ruby/test/rubygems/test_gem_security_policy.rb')
-rw-r--r-- | jni/ruby/test/rubygems/test_gem_security_policy.rb | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/jni/ruby/test/rubygems/test_gem_security_policy.rb b/jni/ruby/test/rubygems/test_gem_security_policy.rb new file mode 100644 index 0000000..d708306 --- /dev/null +++ b/jni/ruby/test/rubygems/test_gem_security_policy.rb @@ -0,0 +1,540 @@ +# coding: UTF-8 + +require 'rubygems/test_case' + +unless defined?(OpenSSL::SSL) then + 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 + + @sha1 = OpenSSL::Digest::SHA1 + @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, @sha1, signature, data) + end + + def test_check_data_invalid + data = digest 'hello' + + signature = sign data + + invalid = digest 'hello!' + + e = assert_raises Gem::Security::Exception do + @almost_no.check_data PUBLIC_KEY, @sha1, 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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_raises 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], @sha1, @trust_dir + end + + def test_check_trust_child + Gem::Security.trust_dir.trust_cert PUBLIC_CERT + + assert @high.check_trust [PUBLIC_CERT, CHILD_CERT], @sha1, @trust_dir + end + + def test_check_trust_empty_chain + e = assert_raises Gem::Security::Exception do + @chain.check_trust [], @sha1, @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_raises Gem::Security::Exception do + @high.check_trust [WRONG_KEY_CERT], @sha1, @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_raises Gem::Security::Exception do + @chain.check_trust nil, @sha1, @trust_dir + end + + assert_equal 'missing signing chain', e.message + end + + def test_check_trust_no_trust + e = assert_raises Gem::Security::Exception do + @high.check_trust [PUBLIC_CERT], @sha1, @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_raises Gem::Security::Exception do + @high.check_trust [PUBLIC_CERT, CHILD_CERT], @sha1, @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/O=JIN.GR.JP/OU=RRR/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_raises 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_raises 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_raises 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 @sha1.new, data.digest + + e = assert_raises 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_raises 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 + + sha512 = OpenSSL::Digest::SHA512 + + data = sha512.new + data << 'hello' + + digests = { 'SHA512' => { 0 => data } } + signature = PRIVATE_KEY.sign sha512.new, data.digest + signatures = { 0 => signature } + + e = assert_raises 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.gzip @spec.to_yaml + + package = Gem::Package.new 'nonexistent.gem' + package.checksums['SHA1'] = {} + + s = StringIO.new metadata_gz + def s.full_name() 'metadata.gz' end + + digests = package.digest s + metadata_gz_digest = digests['SHA1']['metadata.gz'] + + signatures = {} + signatures['metadata.gz'] = + PRIVATE_KEY.sign @sha1.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.gzip @spec.to_yaml + + package = Gem::Package.new 'nonexistent.gem' + package.checksums['SHA1'] = {} + + s = StringIO.new metadata_gz + def s.full_name() 'metadata.gz' end + + digests = package.digest s + digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello' + + metadata_gz_digest = digests['SHA1']['metadata.gz'] + + signatures = {} + signatures['metadata.gz'] = + PRIVATE_KEY.sign @sha1.new, metadata_gz_digest.digest + + e = assert_raises 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.gzip @spec.to_yaml + + package = Gem::Package.new 'nonexistent.gem' + package.checksums['SHA1'] = {} + + s = StringIO.new metadata_gz + def s.full_name() 'metadata.gz' end + + digests = package.digest s + digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello' + + assert_raises Gem::Security::Exception do + @high.verify_signatures @spec, digests, {} + end + end + + def digest data + digester = @sha1.new + digester << data + digester + end + + def sign data, key = PRIVATE_KEY + key.sign @sha1.new, data.digest + end + + def dummy_signatures key = PRIVATE_KEY + data = digest 'hello' + + digests = { 'SHA1' => { 0 => data } } + signatures = { 0 => sign(data, key) } + + return digests, signatures + end + +end if defined?(OpenSSL::SSL) + |