1 /**
2 * SRP-6a (RFC 5054 compatatible)
3 * 
4 * Copyright:
5 * (C) 2011,2012 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.constructs.srp6;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_SRP6):
15 import botan.math.bigint.bigint;
16 import botan.hash.hash;
17 import botan.rng.rng;
18 import botan.algo_base.symkey;
19 import botan.pubkey.algo.dl_group;
20 import botan.libstate.libstate;
21 import botan.libstate.global_state;
22 import botan.math.numbertheory.numthry;
23 import botan.utils.types;
24 
25 struct SRP6KeyPair {
26     BigInt privkey;
27     SymmetricKey pubkey;
28 
29     this()(auto ref BigInt priv, SymmetricKey pub) {
30         privkey = priv.move();
31         pubkey = pub;
32     }
33 }
34 
35 /**
36 * SRP6a Client side
37 * Params:
38 *  identifier = the username we are attempting login for
39 *  password = the password we are attempting to use
40 *  group_id = specifies the shared SRP group
41 *  hash_id = specifies a secure hash function
42 *  salt = is the salt value sent by the server
43 *  B = is the server's public value
44 *  rng = is a random number generator
45 *
46 * Returns: (A,K) the client public key and the shared secret key
47 */
48 SRP6KeyPair
49     srp6ClientAgree(in string identifier,
50                     in string password,
51                     in string group_id,
52                     in string hash_id,
53                     const ref Vector!ubyte salt,
54                     const ref BigInt B,
55                     RandomNumberGenerator rng)
56 {
57     DLGroup group = DLGroup(group_id);
58     const BigInt* g = &group.getG();
59     const BigInt* p = &group.getP();
60     
61     const size_t p_bytes = p.bytes();
62     
63     if (B <= 0 || B >= *p)
64         throw new Exception("Invalid SRP parameter from server");
65     
66     BigInt k = hashSeq(hash_id, p_bytes, *p, *g);
67     
68     BigInt a = BigInt(rng, 256);
69     
70     BigInt A = powerMod(*g, a, *p);
71     
72     BigInt u = hashSeq(hash_id, p_bytes, A, B);
73     
74     BigInt x = computeX(hash_id, identifier, password, salt);
75     
76     BigInt S = powerMod((B - (k * powerMod(*g, x, *p))) % (*p), (a + (u * x)), *p);
77     
78     SymmetricKey Sk = SymmetricKey(BigInt.encode1363(S, p_bytes));
79     
80     return SRP6KeyPair(A, Sk);
81 }
82 
83 
84 /**
85 * Generate a new SRP-6 verifier
86 * Params:
87 *  identifier = a username or other client identifier
88 *  password = the secret used to authenticate user
89 *  salt = a randomly chosen value, at least 128 bits long
90 *  group_id = specifies the shared SRP group
91 *  hash_id = specifies a secure hash function
92 */
93 BigInt generateSrp6Verifier(in string identifier,
94                               in string password,
95                               const ref Vector!ubyte salt,
96                               in string group_id,
97                               in string hash_id)
98 {
99     BigInt x = computeX(hash_id, identifier, password, salt);
100     
101     DLGroup group = DLGroup(group_id);
102     return powerMod(group.getG(), x, group.getP());
103 }
104 
105 
106 /**
107 * Return the group id for this SRP param set, or else thrown an
108 * exception
109 * Params:
110 *  N = the group modulus
111 *  g = the group generator
112 * Returns: group identifier
113 */
114 string srp6GroupIdentifier(const ref BigInt N, const ref BigInt g)
115 {
116     /*
117     This function assumes that only one 'standard' SRP parameter set has
118     been defined for a particular bitsize. As of this writing that is the case.
119     */
120     try
121     {
122         const string group_name = "modp/srp/" ~ to!string(N.bits());
123         
124         DLGroup group = DLGroup(group_name);
125         
126         if (group.getP() == N && group.getG() == g)
127             return group_name;
128         
129         throw new Exception("Unknown SRP params");
130     }
131     catch (Exception)
132     {
133         throw new InvalidArgument("Bad SRP group parameters");
134     }
135 }
136 
137 /**
138 * Represents a SRP-6a server session
139 */
140 final class SRP6ServerSession
141 {
142 public:
143     /**
144     * Server side step 1
145     * Params:
146     *  v = the verification value saved from client registration
147     *  group_id = the SRP group id
148     *  hash_id = the SRP hash in use
149     *  rng = a random number generator
150     * Returns: SRP-6 B value
151     */
152     ref const(BigInt) step1(const ref BigInt v,
153                             in string group_id,
154                             in string hash_id,
155                             RandomNumberGenerator rng)
156     {
157         DLGroup group = DLGroup(group_id);
158         const BigInt* g = &group.getG();
159         const BigInt* p = &group.getP();
160         
161         m_p_bytes = p.bytes();
162         
163         BigInt k = hashSeq(hash_id, m_p_bytes, *p, *g);
164         
165         BigInt b = BigInt(rng, 256);
166         
167         m_B = (v*k + powerMod(*g, b, *p)) % (*p);
168         
169         m_v = v.dup;
170         m_b = b.move();
171         m_p = p.dup;
172         m_hash_id = hash_id;
173         
174         return m_B;
175     }
176 
177     /**
178     * Server side step 2
179     * Params:
180     *  A = the client's value
181     * Returns: shared symmetric key
182     */
183     SymmetricKey step2()(auto const ref BigInt A)
184     {
185         if (A <= 0 || A >= m_p)
186             throw new Exception("Invalid SRP parameter from client");
187         
188         BigInt u = hashSeq(m_hash_id, m_p_bytes, A, m_B);
189         
190         BigInt S = powerMod(A * powerMod(m_v, u, m_p), m_b, m_p);
191         
192         return SymmetricKey(BigInt.encode1363(S, m_p_bytes));
193     }
194 
195 private:
196     string m_hash_id;
197     BigInt m_B, m_b, m_v, m_p; // m_S
198     size_t m_p_bytes;
199 }
200 
201 private:
202     
203 BigInt hashSeq()(in string hash_id,
204                  size_t pad_to,
205                  auto const ref BigInt in1,
206                  auto const ref BigInt in2)
207 {
208     Unique!HashFunction hash_fn = globalState().algorithmFactory().makeHashFunction(hash_id);
209     
210     hash_fn.update(BigInt.encode1363(in1, pad_to));
211     hash_fn.update(BigInt.encode1363(in2, pad_to));
212     
213     return BigInt.decode(hash_fn.finished());
214 }
215 
216 BigInt computeX(in string hash_id,
217                 in string identifier,
218                 in string password,
219                 const ref Vector!ubyte salt)
220 {
221     Unique!HashFunction hash_fn = globalState().algorithmFactory().makeHashFunction(hash_id);
222     
223     hash_fn.update(identifier);
224     hash_fn.update(":");
225     hash_fn.update(password);
226     
227     SecureVector!ubyte inner_h = hash_fn.finished();
228     
229     hash_fn.update(salt);
230     hash_fn.update(inner_h);
231     
232     SecureVector!ubyte outer_h = hash_fn.finished();
233     
234     return BigInt.decode(outer_h);
235 }