1require 'rubygems/test_case'
2
3class TestGemSecuritySigner < Gem::TestCase
4
5  ALTERNATE_KEY  = load_key 'alternate'
6  CHILD_KEY      = load_key 'child'
7  GRANDCHILD_KEY = load_key 'grandchild'
8
9  CHILD_CERT      = load_cert 'child'
10  GRANDCHILD_CERT = load_cert 'grandchild'
11  EXPIRED_CERT    = load_cert 'expired'
12
13  def setup
14    super
15
16    @cert_file = PUBLIC_CERT
17  end
18
19  def test_initialize
20    signer = Gem::Security::Signer.new nil, nil
21
22    assert_nil signer.key
23    assert_nil signer.cert_chain
24  end
25
26  def test_initialize_cert_chain_empty
27    signer = Gem::Security::Signer.new PUBLIC_KEY, []
28
29    assert_empty signer.cert_chain
30  end
31
32  def test_initialize_cert_chain_mixed
33    signer = Gem::Security::Signer.new nil, [@cert_file, CHILD_CERT]
34
35    assert_equal [PUBLIC_CERT, CHILD_CERT].map { |c| c.to_pem },
36                 signer.cert_chain.map { |c| c.to_pem }
37  end
38
39  def test_initialize_cert_chain_invalid
40    assert_raises OpenSSL::X509::CertificateError do
41      Gem::Security::Signer.new nil, ['garbage']
42    end
43  end
44
45  def test_initialize_cert_chain_path
46    signer = Gem::Security::Signer.new nil, [@cert_file]
47
48    assert_equal [PUBLIC_CERT].map { |c| c.to_pem },
49                 signer.cert_chain.map { |c| c.to_pem }
50  end
51
52  def test_initialize_default
53    FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
54
55    private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
56    Gem::Security.write PRIVATE_KEY, private_key_path
57
58    public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
59    Gem::Security.write PUBLIC_CERT, public_cert_path
60
61    signer = Gem::Security::Signer.new nil, nil
62
63    assert_equal PRIVATE_KEY.to_pem, signer.key.to_pem
64    assert_equal [PUBLIC_CERT.to_pem], signer.cert_chain.map { |c| c.to_pem }
65  end
66
67  def test_initialize_key_path
68    key_file = PRIVATE_KEY_PATH
69
70    signer = Gem::Security::Signer.new key_file, nil
71
72    assert_equal PRIVATE_KEY.to_s, signer.key.to_s
73  end
74
75  def test_load_cert_chain
76    Gem::Security.trust_dir.trust_cert PUBLIC_CERT
77
78    signer = Gem::Security::Signer.new nil, []
79    signer.cert_chain.replace [CHILD_CERT]
80
81    signer.load_cert_chain
82
83    assert_equal [PUBLIC_CERT.to_pem, CHILD_CERT.to_pem],
84                 signer.cert_chain.map { |c| c.to_pem }
85  end
86
87  def test_load_cert_chain_broken
88    Gem::Security.trust_dir.trust_cert CHILD_CERT
89
90    signer = Gem::Security::Signer.new nil, []
91    signer.cert_chain.replace [GRANDCHILD_CERT]
92
93    signer.load_cert_chain
94
95    assert_equal [CHILD_CERT.to_pem, GRANDCHILD_CERT.to_pem],
96                 signer.cert_chain.map { |c| c.to_pem }
97  end
98
99  def test_sign
100    signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
101
102    signature = signer.sign 'hello'
103
104    expected = <<-EXPECTED
105pxSf9ScaghbMNmNp8fqSJj7BiIGpbuoOVYCOM3TJNH9STLILA5z3xKp3gM6w
106VJ7aGsh9KCP485ftS3J9Kb/lKJsyoSkkRSQ5QG+LnyZwMuWlThPDR5o7q6al
1070oxE7vvbbqxFqcT4ojWIkwxJxOluFWmt2D8I6QTX2vLAn09y+Kl66AOrT7R5
108UinbXkz04VwcNvkBqJyko3yWxFKiGNpntZQg4jIw4L+h97EOaZp8H96udzQH
109Da3K0YZ6FsqLDFNnWAFhve3kmpE3CludpvDqH0piq0zKqnOiqAcvICIpPaJP
110c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
111    EXPECTED
112
113    assert_equal expected, [signature].pack('m')
114  end
115
116  def test_sign_expired
117    signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
118
119    assert_raises Gem::Security::Exception do
120      signer.sign 'hello'
121    end
122  end
123
124  def test_sign_expired_auto_update
125    FileUtils.mkdir_p File.join(Gem.user_home, '.gem'), :mode => 0700
126
127    private_key_path = File.join(Gem.user_home, '.gem', 'gem-private_key.pem')
128    Gem::Security.write PRIVATE_KEY, private_key_path
129
130    cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
131    Gem::Security.write EXPIRED_CERT, cert_path
132
133    signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
134
135    signer.sign 'hello'
136
137    cert = OpenSSL::X509::Certificate.new File.read cert_path
138
139    refute_equal EXPIRED_CERT.to_pem, cert.to_pem
140    assert_in_delta Time.now,         cert.not_before, 10
141
142    expiry = EXPIRED_CERT.not_after.strftime "%Y%m%d%H%M%S"
143
144    expired_path =
145      File.join Gem.user_home, '.gem', "gem-public_cert.pem.expired.#{expiry}"
146
147    assert_path_exists expired_path
148    assert_equal EXPIRED_CERT.to_pem, File.read(expired_path)
149  end
150
151  def test_sign_expired_auto_update_exists
152    FileUtils.mkdir_p File.join(Gem.user_home, '.gem'), :mode => 0700
153
154    expiry = EXPIRED_CERT.not_after.strftime "%Y%m%d%H%M%S"
155    expired_path =
156      File.join Gem.user_home, "gem-public_cert.pem.expired.#{expiry}"
157
158    Gem::Security.write EXPIRED_CERT, expired_path
159
160    private_key_path = File.join(Gem.user_home, 'gem-private_key.pem')
161    Gem::Security.write PRIVATE_KEY, private_key_path
162
163    cert_path = File.join Gem.user_home, 'gem-public_cert.pem'
164    Gem::Security.write EXPIRED_CERT, cert_path
165
166    signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
167
168    e = assert_raises Gem::Security::Exception do
169      signer.sign 'hello'
170    end
171
172    assert_match %r%certificate /CN=nobody/DC=example not valid%, e.message
173  end
174
175  def test_sign_no_key
176    signer = Gem::Security::Signer.new nil, nil
177
178    assert_nil signer.sign 'stuff'
179  end
180
181  def test_sign_wrong_key
182    signer = Gem::Security::Signer.new ALTERNATE_KEY, [PUBLIC_CERT]
183
184    assert_raises Gem::Security::Exception do
185      signer.sign 'hello'
186    end
187  end
188
189end
190
191