summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAzul <azul@leap.se>2012-06-18 12:34:11 +0200
committerAzul <azul@leap.se>2012-06-18 12:34:11 +0200
commit09a7a8c0fb28ff49fac64f282aa136f8a2c20dfe (patch)
tree09e0101fb5d02cf0db3648eae562a981aa422b17 /lib
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
Diffstat (limited to 'lib')
-rw-r--r--lib/srp.rb13
-rw-r--r--lib/srp/client.rb48
-rw-r--r--lib/srp/server.rb42
-rw-r--r--lib/srp/util.rb53
4 files changed, 156 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
+