Permalink
Browse files

More work on the authentication plugin.

  • Loading branch information...
1 parent 989a5cc commit b5879a7e6369fe246f951b4ab86858714757d48e @pwnall committed Oct 24, 2009
@@ -51,17 +51,71 @@ def self.auth_url_calling(callback_name, nonce = nil)
def self.random_nonce
[OpenSSL::Random.random_bytes(16)].pack('m')
end
+
+ # Checks the authentication data.
+ #
+ # Returns the authentication nonce for application-specific verification, or
+ # false if the authentication data shows a failure.
+ def self.verify_data(data)
+ # Step 1: SSL status.
+ return false if data['verify'] != 'SUCCESS'
+
+ # Bulk-check for the keys for the other steps.
+ required_keys = ['cipher', 'dn', 'issuer_dn', 'nonce', 'protocol', 'serial',
+ 'signature', 'ssl_sig', 'valid_from', 'valid_until']
+ return false unless required_keys.all? { |key| data.has_key? key }
+
+ # Step 2: certificate expiration.
+ now = Time.now
+ return false if now < Time.parse(data['valid_from'])
+ return false if now > Time.parse(data['valid_until'])
- # Checks authentication data.
+ # Step 3: issuer DN.
+ dn_prefix =
+ '/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client'
+ return false if data['issuer_dn'][0, dn_prefix.length] != dn_prefix
+
+ # Step 4: signature.
+ return false unless verify_data_signature(data)
+
+ # CRL verification would happen here.
+
+ # Blacklisting checking would happen here.
+
+ return decode_data(data)
+ end
+
+ # URL to MIT's page explaining how to obtain certificates.
+ #
+ # Sites that need for MIT certificates should have a link to this page. The
+ # URL is included here so sites don't have to be updated individually when
+ # the URL changes.
+ def self.mit_certificates_url
+ 'http://ist.mit.edu/services/certificates#content_paragraph_1'
+ end
+
+ # Checks the signature in authentication data.
#
# This method will automatically download the proxy's public key, if
# necessary.
- def self.verify_data(data)
+ def self.verify_data_signature(data)
signature = data['signature'].unpack('m').first
blob = _presign_blob data
signing_key.verify OpenSSL::Digest::SHA1.new, signature, blob
end
+ # Extracts the interesting fields from the authentication data.
+ #
+ # This should not be called directly. verify_data calls it when the
+ # authentication data is valid. Client code should call verify_data to avoid
+ # attacks on the authorization system.
+ #
+ # See verify_data for what the method returns.
+ def self.decode_data(data)
+ dn = Hash[*data['dn'][1..-1].split('/').map { |f| f.split '=' }.flatten]
+ { :name => dn['CN'], :email => dn['emailAddress'], :nonce => data['nonce'] }
+ end
+
# Signs authentication data.
#
# The signature will be added directly to the authentication data.
@@ -45,9 +45,11 @@ def test_sign_and_verify
flexmock(MitCertAuthProxy).should_receive(:signing_key).
and_return(key.public_key)
- assert MitCertAuthProxy.verify_data(data), "Valid signature didn't verify"
+ assert MitCertAuthProxy.verify_data_signature(data),
+ "Valid signature didn't pass verification"
data[:c] = 4
- assert !MitCertAuthProxy.verify_data(data), "Invalid signature verified"
+ assert !MitCertAuthProxy.verify_data_signature(data),
+ 'Invalid signature passed verification'
end
def test_random_nonce
@@ -81,4 +83,10 @@ def test_auth_url_calling
MitCertAuthProxy.auth_url_calling('callbackMethod', '1234'),
'eximplicit nonce'
end
+
+ def test_mit_certificates_url
+ page = Net::HTTP.get URI.parse(MitCertAuthProxy.mit_certificates_url)
+ assert /certificates at mit/i =~ page,
+ 'The linked page doesn\'t contain "certificates at mit"'
+ end
end
@@ -0,0 +1,84 @@
+# Author:: Victor Costan
+# Copyright:: Copyright (C) 2009 Zergling.Net
+# License:: MIT
+
+require 'test_helper'
+require 'flexmock/test_unit'
+
+
+class SignatureVerificationTest < ActiveSupport::TestCase
+ def setup
+ super
+
+ @@key ||= OpenSSL::PKey::RSA.generate 512
+ @key = @@key
+
+ dn = '/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/' +
+ 'OU=Client CA v1/CN=Victor Marius Costan/' +
+ 'emailAddress=costan@MIT.EDU'
+ issuer_dn = '/C=US/ST=Massachusetts/O=Massachusetts Institute of ' +
+ 'Technology/OU=Client CA v1'
+
+ @nonce = 'O9eYWU/t1NSb+ZDmfniqizBSeNhVtsnTHS1H3T2FtRc='
+ @data = {'dn' => dn, 'issuer_dn' => issuer_dn, 'verify' => 'SUCCESS',
+ 'serial' => 'D376EC2AE81A03E10743D175CB659F58',
+ 'nonce' => @nonce, 'valid_from' => (Time.now - 120).utc.to_s,
+ 'valid_until' => (Time.now + 120).utc.to_s,
+ 'cipher' => 'DHE-RSA-CAMELLIA256-SHA', 'protocol' => 'TLSv1',
+ 'ssl_sig' => 'sha1WithRSAEncryption'}
+
+ flexmock(MitCertAuthProxy).should_receive(:signing_key).
+ and_return(@key.public_key)
+ end
+
+ def test_valid
+ golden_result = { :nonce => @nonce, :name => 'Victor Marius Costan',
+ :email => 'costan@MIT.EDU' }
+
+ MitCertAuthProxy.sign_data! @key, @data
+ assert_equal golden_result, MitCertAuthProxy.verify_data(@data)
+ end
+
+ def test_ssl_failure
+ @data['verify'] = 'NONE'
+ MitCertAuthProxy.sign_data! @key, @data
+ assert_equal false, MitCertAuthProxy.verify_data(@data)
+ end
+
+ def test_certificate_not_yet_valid
+ @data['valid_from'] = (Time.now + 15).utc.to_s
+ MitCertAuthProxy.sign_data! @key, @data
+ assert_equal false, MitCertAuthProxy.verify_data(@data)
+ end
+
+ def test_certificate_expired
+ @data['valid_until'] = (Time.now - 1).utc.to_s
+ MitCertAuthProxy.sign_data! @key, @data
+ assert_equal false, MitCertAuthProxy.verify_data(@data)
+ end
+
+ def test_wrong_dn
+ @data['issuer_dn'] = '/C=US/ST=Massachusetts/O=Harvard/OU=Client CA v1'
+ MitCertAuthProxy.sign_data! @key, @data
+ assert_equal false, MitCertAuthProxy.verify_data(@data)
+ end
+
+ def test_wrong_signature
+ MitCertAuthProxy.sign_data! @key, @data
+ @data['serial'] = 'D376EC2AE81A03E10743D175CB659F5'
+ assert_equal false, MitCertAuthProxy.verify_data(@data)
+ end
+
+ def test_required_keys
+ @data.keys.each do |key|
+ dup_data = @data.dup
+ dup_data.delete key
+ MitCertAuthProxy.sign_data! @key, dup_data
+ assert_equal false, MitCertAuthProxy.verify_data(dup_data)
+ end
+ end
+
+ def test_no_signature
+ assert_equal false, MitCertAuthProxy.verify_data(@data)
+ end
+end

0 comments on commit b5879a7

Please sign in to comment.