diff options
| author | Azul <azul@leap.se> | 2012-06-18 12:34:11 +0200 | 
|---|---|---|
| committer | Azul <azul@leap.se> | 2012-06-18 12:34:11 +0200 | 
| commit | 09a7a8c0fb28ff49fac64f282aa136f8a2c20dfe (patch) | |
| tree | 09e0101fb5d02cf0db3648eae562a981aa422b17 | |
initial commit - testing srp auth
* This is lacking a few steps. We confirm the secret is the same but no key is generated from it and it is transfered over the wire in clear.
* this was inspired by https://gist.github.com/790048
* seperated util, client, server and test code
| -rw-r--r-- | lib/srp.rb | 13 | ||||
| -rw-r--r-- | lib/srp/client.rb | 48 | ||||
| -rw-r--r-- | lib/srp/server.rb | 42 | ||||
| -rw-r--r-- | lib/srp/util.rb | 53 | ||||
| -rw-r--r-- | test/auth_test.rb | 25 | ||||
| -rw-r--r-- | test/test_helper.rb | 3 | 
6 files changed, 184 insertions, 0 deletions
diff --git a/lib/srp.rb b/lib/srp.rb new file mode 100644 index 0000000..999f9b6 --- /dev/null +++ b/lib/srp.rb @@ -0,0 +1,13 @@ +# Ruby library for the server side of the Secure Remote Password protocol + +# References +# `The Stanford SRP Homepage', +# http://srp.stanford.edu/ +# `SRP JavaScript Demo', +# http://srp.stanford.edu/demo/demo.html + +$:.unshift File.dirname(__FILE__) +module SRP +  autoload :Client, 'srp/client' +  autoload :Server, 'srp/server' +end diff --git a/lib/srp/client.rb b/lib/srp/client.rb new file mode 100644 index 0000000..f4662e6 --- /dev/null +++ b/lib/srp/client.rb @@ -0,0 +1,48 @@ +require File.expand_path(File.dirname(__FILE__) + '/util') + +module SRP +  class Client + +    include Util + +    attr_reader :salt, :verifier + +    def initialize(username, password) +      @username = username +      @password = password +      @salt = bigrand(10).hex +      @multiplier = multiplier # let's cache it +      calculate_verifier +    end + +    def authenticate(server, username, password) +      x = calculate_x(username, password, salt) +      a = bigrand(32).hex +      aa = modpow(GENERATOR, a, PRIME_N) # A = g^a (mod N) +      bb, u = server.initialize_auth(aa) +      client_s = calculate_client_s(x, a, bb, u) +      server.authenticate(aa, client_s) +    end + +    protected +    def calculate_verifier +      x = calculate_x(@username, @password, @salt) +      @verifier = modpow(GENERATOR, x, PRIME_N) +    end + +    def calculate_x(username, password, salt) +      shex = '%x' % [salt] +      spad = if shex.length.odd? then '0' else '' end +      sha1_hex(spad + shex + sha1_str([username, password].join(':'))).hex +    end + +    def calculate_client_s(x, a, bb, u) +      base = bb +      base += PRIME_N * @multiplier +      base -= modpow(GENERATOR, x, PRIME_N) * @multiplier +      base = base % PRIME_N +      modpow(base, x * u + a, PRIME_N) +    end +  end +end + diff --git a/lib/srp/server.rb b/lib/srp/server.rb new file mode 100644 index 0000000..a1189a1 --- /dev/null +++ b/lib/srp/server.rb @@ -0,0 +1,42 @@ +require File.expand_path(File.dirname(__FILE__) + '/util') + +module SRP +  class Server + +    include Util + +    def initialize(salt, verifier) +      @salt = salt +      @verifier = verifier +    end + +    def initialize_auth(aa) +      @b = bigrand(32).hex +      # B = g^b + k v (mod N) +      @bb = (modpow(GENERATOR, @b, PRIME_N) + multiplier * @verifier) % PRIME_N +      u = calculate_u(aa, @bb, PRIME_N) +      return @bb, u +    end + +    def authenticate(aa, client_s) +      u = calculate_u(aa, @bb, PRIME_N) +      base = (modpow(@verifier, u, PRIME_N) * aa) % PRIME_N +      server_s = modpow(base, @b, PRIME_N) +      return client_s == server_s +    end + + +    protected + +    def calculate_u(aa, bb, n) +      nlen = 2 * ((('%x' % [n]).length * 4 + 7) >> 3) +      aahex = '%x' % [aa] +      bbhex = '%x' % [bb] +      hashin = '0' * (nlen - aahex.length) + aahex \ +        + '0' * (nlen - bbhex.length) + bbhex +      sha1_hex(hashin).hex +    end +  end +end + + diff --git a/lib/srp/util.rb b/lib/srp/util.rb new file mode 100644 index 0000000..6792105 --- /dev/null +++ b/lib/srp/util.rb @@ -0,0 +1,53 @@ +require 'digest' +require 'openssl' + +module SRP +  module Util + +    # constants both sides know +    PRIME_N = <<-EOS.split.join.hex # 1024 bits modulus (N) +eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c25657 +6d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089da +d15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e5 +7ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb +06e3 +    EOS +    GENERATOR = 2 # g + +    # a^n (mod m) +    def modpow(a, n, m) +      r = 1 +      while true +        r = r * a % m if n[0] == 1 +        n >>= 1 +        return r if n == 0 +        a = a * a % m +      end +    end + +    def sha1_hex(h) +      Digest::SHA1.hexdigest([h].pack('H*')) +    end + +    def sha1_str(s) +      Digest::SHA1.hexdigest(s) +    end + +    def bigrand(bytes) +      OpenSSL::Random.random_bytes(bytes).unpack("H*")[0] +    end + +    def multiplier +      n = PRIME_N +      g = GENERATOR +      nhex = '%x' % [n] +      nlen = nhex.length + (nhex.length.odd? ? 1 : 0 ) +      ghex = '%x' % [g] +      hashin = '0' * (nlen - nhex.length) + nhex \ +        + '0' * (nlen - ghex.length) + ghex +      sha1_hex(hashin).hex % n +    end +  end + +end + diff --git a/test/auth_test.rb b/test/auth_test.rb new file mode 100644 index 0000000..e6c4017 --- /dev/null +++ b/test/auth_test.rb @@ -0,0 +1,25 @@ +require File.expand_path(File.dirname(__FILE__) + '/test_helper') + +class AuthTest < Test::Unit::TestCase + +  def setup +    @username = 'user' +    @password = 'opensasemi' +    @client = SRP::Client.new(@username, @password) +    @server = SRP::Server.new(@client.salt, @client.verifier) +  end + +  def test_successful_auth +    assert @client.authenticate(@server, @username, @password) +  end + +  def test_wrong_password +    assert !@client.authenticate(@server, @username, "password") +  end + +  def test_wrong_username +    assert !@client.authenticate(@server, "username", @password) +  end +end + + diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..68d3acf --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,3 @@ +require "rubygems" +require 'test/unit' +require File.expand_path(File.dirname(__FILE__) + '/../lib/srp.rb')  | 
