Skip to content

Commit

Permalink
More work on the authentication plugin.
Browse files Browse the repository at this point in the history
  • Loading branch information
pwnall committed Oct 24, 2009
1 parent 989a5cc commit b5879a7
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 4 deletions.
58 changes: 56 additions & 2 deletions vendor/plugins/mit_cert_auth_proxy/lib/mit_cert_auth_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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.