summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/srp/src/srp_session.js
blob: 88f19d5edd46b3e20de933a928f13a9fa8fd4c9c (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
srp.Session = function(account, calculate) {

  // default for injected dependency
  account = account || new srp.Account();
  calculate = calculate || new srp.Calculate();

  var a = calculate.randomEphemeral();
  var A = calculate.A(a);
  var S = null;
  var K = null;
  var M = null;
  var M2 = null;
  var authenticated = false;

  // *** Accessor methods ***

  // allows setting the random number A for testing

  this.calculateAndSetA = function(_a) {
    a = _a;
    A = calculate.A(_a);
    return A;
  };

  this.update = function() {
    var salt = calculate.randomSalt();
    var x = calculate.X(account.login(), account.password(), salt);
    return {
      login: account.login(),
      password_salt: salt,
      password_verifier: calculate.V(x)
    };
  }

  this.signup = function() {
    var loginParams = this.update();

    if (account.loginParams) {
      var extraParams = account.loginParams();
      for (var attr in extraParams) {
        loginParams[attr] = extraParams[attr];
      }
    }

    return loginParams;
  };

  this.handshake = function() {
    return {
      login: account.login(),
      A: this.getA()
    };
  };

  this.getA = function() {
    return A;
  }

  // Delegate login & id so they can be used when talking to the remote
  this.login = account.login;
  this.id = account.id;

  // Calculate S, M, and M2
  // This is the client side of the SRP specification
  this.calculations = function(salt, ephemeral)
  {
    //S -> C: s | B
    var B = calculate.zeroPrefix(ephemeral);
    salt = calculate.zeroPrefix(salt);
    var x = calculate.X(account.login(), account.password(), salt);
    S = calculate.S(a, A, B, x);
    K = calculate.K(S);

    // M = H(H(N) xor H(g), H(I), s, A, B, K)
    var xor = calculate.nXorG();
    var hash_i = calculate.hash(account.login())
    M = calculate.hashHex(xor + hash_i + salt + A + B + K);
    //M2 = H(A, M, K)
    M2 = calculate.hashHex(A + M + K);
  };


  this.getS = function() {
    return S;
  }

  this.getM = function() {
    return M;
  }

  this.validate = function(serverM2) {
    authenticated = (serverM2 && serverM2 == M2)
    return authenticated;
  }

  // If someone wants to use the session key for encrypting traffic, they can
  // access the key with this function.
  this.key = function()
  {
    if(K) {
      return K;
    } else {
      this.onError("User has not been authenticated.");
    }
  };

  // Encrypt plaintext using slowAES
  this.encrypt = function(plaintext)
  {
    var key = cryptoHelpers.toNumbers(session.key());
    var byteMessage = cryptoHelpers.convertStringToByteArray(plaintext);
    var iv = new Array(16);
    rng.nextBytes(iv);
    var paddedByteMessage = slowAES.getPaddedBlock(byteMessage, 0, byteMessage.length, slowAES.modeOfOperation.CFB);
    var ciphertext = slowAES.encrypt(paddedByteMessage, slowAES.modeOfOperation.CFB, key, key.length, iv).cipher;
    var retstring = cryptoHelpers.base64.encode(iv.concat(ciphertext));
    while(retstring.indexOf("+",0) > -1)
      retstring = retstring.replace("+", "_");
    return retstring;
  };
};