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(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     BigInt a = BigInt(rng, 256);    
68     BigInt A = powerMod(g, &a, p);    
69     BigInt u = hashSeq(hash_id, p_bytes, &A, &B);    
70     BigInt x = computeX(hash_id, identifier, password, salt);    
71     BigInt ref_1 = (B - (k * powerMod(g, &x, p))) % (*p);
72     auto ref_2_2 = (u * x);
73     BigInt ref_2 = (a + ref_2_2);
74     BigInt S = powerMod(&ref_1, &ref_2, p);
75     
76     SymmetricKey Sk = SymmetricKey(BigInt.encode1363(&S, p_bytes));
77     
78     return SRP6KeyPair(&A, Sk);
79 }
80 
81 
82 /**
83 * Generate a new SRP-6 verifier
84 * Params:
85 *  identifier = a username or other client identifier
86 *  password = the secret used to authenticate user
87 *  salt = a randomly chosen value, at least 128 bits long
88 *  group_id = specifies the shared SRP group
89 *  hash_id = specifies a secure hash function
90 */
91 BigInt generateSrp6Verifier(in string identifier,
92                               in string password,
93                               const ref Vector!ubyte salt,
94                               in string group_id,
95                               in string hash_id)
96 {
97     BigInt x = computeX(hash_id, identifier, password, salt);
98     
99     DLGroup group = DLGroup(group_id);
100     return powerMod(&group.getG(), &x, &group.getP());
101 }
102 
103 
104 /**
105 * Return the group id for this SRP param set, or else thrown an
106 * exception
107 * Params:
108 *  N = the group modulus
109 *  g = the group generator
110 * Returns: group identifier
111 */
112 string srp6GroupIdentifier(const ref BigInt N, const ref BigInt g)
113 {
114     /*
115     This function assumes that only one 'standard' SRP parameter set has
116     been defined for a particular bitsize. As of this writing that is the case.
117     */
118     try
119     {
120         const string group_name = "modp/srp/" ~ to!string(N.bits());
121         
122         DLGroup group = DLGroup(group_name);
123         
124         if (group.getP() == N && group.getG() == g)
125             return group_name;
126         
127         throw new Exception("Unknown SRP params");
128     }
129     catch (Exception)
130     {
131         throw new InvalidArgument("Bad SRP group parameters");
132     }
133 }
134 
135 /**
136 * Represents a SRP-6a server session
137 */
138 final class SRP6ServerSession
139 {
140 public:
141     /**
142     * Server side step 1
143     * Params:
144     *  v = the verification value saved from client registration
145     *  group_id = the SRP group id
146     *  hash_id = the SRP hash in use
147     *  rng = a random number generator
148     * Returns: SRP-6 B value
149     */
150     ref const(BigInt) step1(const ref BigInt v,
151                             in string group_id,
152                             in string hash_id,
153                             RandomNumberGenerator rng)
154     {
155         DLGroup group = DLGroup(group_id);
156         const BigInt* g = &group.getG();
157         const BigInt* p = &group.getP();
158         
159         m_p_bytes = p.bytes();
160         
161         BigInt k = hashSeq(hash_id, m_p_bytes, p, g);
162         
163         BigInt b = BigInt(rng, 256);
164         
165         auto m_B0 = powerMod(g, &b, p);
166         m_B = (v*k + m_B0) % (*p);
167         
168         m_v = v.clone;
169         m_b = b.move();
170         m_p = p.clone;
171         m_hash_id = hash_id;
172         
173         return m_B;
174     }
175 
176     /**
177     * Server side step 2
178     * Params:
179     *  A = the client's value
180     * Returns: shared symmetric key
181     */
182     SymmetricKey step2(const(BigInt)* A)
183     {
184         if (*A <= 0 || *A >= m_p)
185             throw new Exception("Invalid SRP parameter from client");
186         
187         BigInt u = hashSeq(m_hash_id, m_p_bytes, A, &m_B);
188         auto ref_1 = (*A * powerMod(&m_v, &u, &m_p));
189         BigInt S = powerMod(&ref_1, &m_b, &m_p);
190         
191         return SymmetricKey(BigInt.encode1363(S, m_p_bytes));
192     }
193 
194 private:
195     string m_hash_id;
196     BigInt m_B, m_b, m_v, m_p; // m_S
197     size_t m_p_bytes;
198 }
199 
200 private:
201     
202 BigInt hashSeq(in string hash_id,
203                  size_t pad_to,
204                  const(BigInt)* in1,
205                  const(BigInt)* in2)
206 {
207     Unique!HashFunction hash_fn = globalState().algorithmFactory().makeHashFunction(hash_id);
208     
209     hash_fn.update(BigInt.encode1363(in1, pad_to));
210     hash_fn.update(BigInt.encode1363(in2, pad_to));
211     
212     return BigInt.decode(hash_fn.finished());
213 }
214 
215 BigInt computeX(in string hash_id,
216                 in string identifier,
217                 in string password,
218                 const ref Vector!ubyte salt)
219 {
220     Unique!HashFunction hash_fn = globalState().algorithmFactory().makeHashFunction(hash_id);
221     
222     hash_fn.update(identifier);
223     hash_fn.update(":");
224     hash_fn.update(password);
225     
226     SecureVector!ubyte inner_h = hash_fn.finished();
227     
228     hash_fn.update(salt);
229     hash_fn.update(inner_h);
230     
231     SecureVector!ubyte outer_h = hash_fn.finished();
232     
233     return BigInt.decode(outer_h);
234 }