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 }