summaryrefslogtreecommitdiff
path: root/lib/srp/session.rb
blob: 38c97222b2056ea2bf78859d065078363c9d3ecb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
module SRP
  class Session
    include SRP::Util
    attr_accessor :user, :aa, :bb

    def initialize(user, aa=nil)
      @user = user
      aa ? initialize_server(aa) : initialize_client
    end

    # client -> server: I, A = g^a
    def handshake(server)
      @bb = server.handshake(user.username, aa)
      @u = calculate_u
    end

    # client -> server: M = H(H(N) xor H(g), H(I), s, A, B, K)
    def validate(server)
      server.validate(calculate_m(client_secret))
    end

    def authenticate!(m)
      authenticate(m) || raise(SRP::WrongPassword)
    end

    def authenticate(m)
      if(m == calculate_m(server_secret))
        @m2 = calculate_m2
        return @user
      end
    end

    def to_hash
      if @m2
        { :M2 => @m2.to_s(16) }
      else
        { :B => bb.to_s(16),
#         :b => @b.to_s(16),    # only use for debugging
          :salt => @user.salt.to_s(16)
        }
      end
    end

    def to_json(options={})
      to_hash.to_json(options)
    end

    protected


    # only seed b for testing purposes.
    def initialize_server(aa, b = nil)
      @aa = aa
      @b =  b || bigrand(32).hex
      # B = g^b + k v (mod N)
      @bb = (modpow(GENERATOR, @b) + multiplier * @user.verifier) % BIG_PRIME_N
      @u = calculate_u
    end

    def initialize_client
      @a = bigrand(32).hex
      @aa = modpow(GENERATOR, @a) # A = g^a (mod N)
    end

    # client: K = H( (B - kg^x) ^ (a + ux) )
    def client_secret
      base = @bb
      # base += BIG_PRIME_N * @multiplier
      base -= modpow(GENERATOR, @user.private_key) * multiplier
      base = base % BIG_PRIME_N
      modpow(base, @user.private_key * @u + @a)
    end

    # server: K = H( (Av^u) ^ b )
    # do not cache this - it's secret and someone might store the
    # session in a CookieStore
    def server_secret
      base = (modpow(@user.verifier, @u) * @aa) % BIG_PRIME_N
      modpow(base, @b)
    end

    # this is outdated - SRP 6a uses
    # M = H(H(N) xor H(g), H(I), s, A, B, K)
    def calculate_m(secret)
      @k = sha256_int(secret).hex
      n_xor_g_long = hn_xor_hg.bytes.map{|b| "%02x" % b.ord}.join.hex
      username_hash = sha256_str(@user.username).hex
      @m = sha256_int(n_xor_g_long, username_hash, @user.salt, @aa, @bb, @k).hex
    end

    def calculate_m2
      sha256_int(@aa, @m, @k).hex
    end

    def calculate_u
      sha256_int(@aa, @bb).hex
    end

  end
end