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.dup; 169 m_b = b.move(); 170 m_p = p.dup; 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 }