|
12 | 12 | * express or implied. See the License for the specific language governing |
13 | 13 | * permissions and limitations under the License. |
14 | 14 | */ |
15 | | - |
| 15 | + |
16 | 16 | using System; |
17 | | -using System.Globalization; |
18 | | -using System.Linq; |
| 17 | +using System.Globalization; |
| 18 | +using System.Linq; |
19 | 19 | using System.Numerics; |
20 | 20 | using System.Security.Cryptography; |
21 | 21 | using System.Text; |
@@ -157,136 +157,134 @@ public static byte[] GetPasswordAuthenticationKey( |
157 | 157 | return hkdfSha256.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySizeBytes); |
158 | 158 | } |
159 | 159 |
|
160 | | - /// <summary> |
161 | | - /// Generates a DeviceSecretVerifierConfigType object based on a CognitoDevice's Key, Group Key, and Password |
162 | | - /// </summary> |
163 | | - /// <param name="deviceGroupKey">The Group Key of the CognitoDevice</param> |
164 | | - /// <param name="devicePass">A random password for the CognitoDevice (used in the future for logging in via this device)</param> |
165 | | - /// <param name="username">The username of the CognitoDevice user</param> |
| 160 | + /// <summary> |
| 161 | + /// Generates a DeviceSecretVerifierConfigType object based on a CognitoDevice's Key, Group Key, and Password |
| 162 | + /// </summary> |
| 163 | + /// <param name="deviceGroupKey">The Group Key of the CognitoDevice</param> |
| 164 | + /// <param name="devicePass">A random password for the CognitoDevice (used in the future for logging in via this device)</param> |
| 165 | + /// <param name="deviceKey">The DeviceKey for the associated CognitoDevice</param> |
166 | 166 | /// <returns></returns> |
167 | | - public static DeviceSecretVerifierConfigType GenerateDeviceVerifier(string deviceGroupKey, string devicePass, string username) |
168 | | - { |
169 | | - Random r = new Random(); |
170 | | - byte[] userIdContent = CognitoAuthHelper.CombineBytes( |
171 | | - Encoding.UTF8.GetBytes(deviceGroupKey), |
172 | | - Encoding.UTF8.GetBytes(username), |
173 | | - Encoding.UTF8.GetBytes(":"), |
| 167 | + public static DeviceSecretVerifierConfigType GenerateDeviceVerifier(string deviceGroupKey, string devicePass, string deviceKey) |
| 168 | + { |
| 169 | + Random r = new Random(); |
| 170 | + byte[] userIdContent = CognitoAuthHelper.CombineBytes( |
| 171 | + Encoding.UTF8.GetBytes(deviceGroupKey), |
| 172 | + Encoding.UTF8.GetBytes(deviceKey), |
| 173 | + Encoding.UTF8.GetBytes(":"), |
174 | 174 | Encoding.UTF8.GetBytes(devicePass) |
175 | | - ); |
176 | | - |
177 | | - byte[] userIdHash = CognitoAuthHelper.Sha256.ComputeHash(userIdContent); |
178 | | - |
179 | | - byte[] saltBytes = new byte[16]; |
180 | | - RandomNumberGenerator.Create().GetBytes(saltBytes); |
181 | | - // setting the initial byte to 0-127 to avoid negative salt or password verifier error |
182 | | - saltBytes[0] = (byte) r.Next(sbyte.MaxValue); |
183 | | - |
184 | | - byte[] xBytes = CognitoAuthHelper.CombineBytes(saltBytes, userIdHash); |
185 | | - byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); |
186 | | - BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); |
187 | | - |
188 | | - var v = BigInteger.ModPow(g, x, N); |
189 | | - byte[] vBytes = v.ToBigEndianByteArray(); |
190 | | - |
191 | | - return new DeviceSecretVerifierConfigType |
192 | | - { |
193 | | - PasswordVerifier = Convert.ToBase64String(vBytes), |
194 | | - Salt = Convert.ToBase64String(saltBytes) |
195 | | - }; |
196 | | - } |
197 | | - |
198 | | - /// <summary> |
199 | | - /// Generates the claim for authenticating a device through the SRP protocol |
200 | | - /// </summary> |
201 | | - /// <param name="username"> Username of Cognito User</param> |
202 | | - /// <param name="deviceKey"> Key of CognitoDevice</param> |
203 | | - /// <param name="devicePassword"> Password of CognitoDevice</param> |
204 | | - /// <param name="deviceGroupKey"> GroupKey of CognitoDevice</param> |
205 | | - /// <param name="saltString"> salt provided in ChallengeParameters from Cognito </param> |
206 | | - /// <param name="srpbString"> srpb provided in ChallengeParameters from Cognito</param> |
207 | | - /// <param name="secretBlockBase64">secret block provided in ChallengeParameters from Cognito</param> |
208 | | - /// <param name="formattedTimestamp">En-US Culture of Current Time</param> |
209 | | - /// <param name="tupleAa"> TupleAa from CreateAaTuple</param> |
210 | | - /// <returns>Returns the claim for authenticating the given user</returns> |
211 | | - public static byte[] AuthenticateDevice( |
212 | | - string username, |
213 | | - string deviceKey, |
214 | | - string devicePassword, |
215 | | - string deviceGroupKey, |
216 | | - string saltString, |
217 | | - string srpbString, |
218 | | - string secretBlockBase64, |
219 | | - string formattedTimestamp, |
220 | | - Tuple<BigInteger, BigInteger> tupleAa) |
221 | | - |
222 | | - { |
223 | | - var B = BigIntegerExtensions.FromUnsignedLittleEndianHex(srpbString); |
224 | | - if (B.TrueMod(N).Equals(BigInteger.Zero)) throw new ArgumentException("B mod N cannot be zero.", nameof(srpbString)); |
225 | | - |
226 | | - var salt = BigIntegerExtensions.FromUnsignedLittleEndianHex(saltString); |
227 | | - var secretBlockBytes = Convert.FromBase64String(secretBlockBase64); |
228 | | - // Need to generate the key to hash the response based on our A and what AWS sent back |
229 | | - var key = GetDeviceAuthenticationKey(username, devicePassword, deviceGroupKey, tupleAa, B, salt); |
230 | | - |
231 | | - // HMAC our data with key (HKDF(S)) (the shared secret) |
232 | | - var msg = CognitoAuthHelper.CombineBytes( |
233 | | - Encoding.UTF8.GetBytes(deviceGroupKey), |
234 | | - Encoding.UTF8.GetBytes(deviceKey), |
235 | | - secretBlockBytes, |
236 | | - Encoding.UTF8.GetBytes(formattedTimestamp) |
237 | | - ); |
238 | | - |
239 | | - using (var hashAlgorithm = new HMACSHA256(key)) |
240 | | - { |
241 | | - return hashAlgorithm.ComputeHash(msg); |
242 | | - } |
243 | | - } |
244 | | - |
245 | | - /// <summary> |
246 | | - /// Creates the Device Password Authentication Key based on the SRP protocol |
247 | | - /// </summary> |
248 | | - /// <param name="username"> Username of Cognito User</param> |
249 | | - /// <param name="devicePass">Password of CognitoDevice</param> |
250 | | - /// <param name="deviceGroup">GroupKey of CognitoDevice</param> |
251 | | - /// <param name="Aa">Returned from TupleAa</param> |
252 | | - /// <param name="B">BigInteger SRPB from AWS ChallengeParameters</param> |
253 | | - /// <param name="salt">BigInteger salt from AWS ChallengeParameters</param> |
254 | | - /// <returns>Returns the password authentication key for the SRP protocol</returns> |
255 | | - public static byte[] GetDeviceAuthenticationKey( |
256 | | - string username, |
257 | | - string devicePass, |
258 | | - string deviceGroup, |
259 | | - Tuple<BigInteger, BigInteger> Aa, |
260 | | - BigInteger B, |
261 | | - BigInteger salt) |
262 | | - { |
263 | | - // Authenticate the password |
264 | | - // u = H(A, B) |
265 | | - byte[] contentBytes = CognitoAuthHelper.CombineBytes(Aa.Item1.ToBigEndianByteArray(), B.ToBigEndianByteArray()); |
266 | | - byte[] digest = CognitoAuthHelper.Sha256.ComputeHash(contentBytes); |
267 | | - |
268 | | - BigInteger u = BigIntegerExtensions.FromUnsignedBigEndian(digest); |
269 | | - if (u.Equals(BigInteger.Zero)) |
270 | | - { |
271 | | - throw new ArgumentException("Hash of A and B cannot be zero."); |
272 | | - } |
273 | | - |
274 | | - // x = H(salt | H(deviceGroupKey | deviceKey | ":" | devicePassword)) |
275 | | - byte[] deviceContent = CognitoAuthHelper.CombineBytes(Encoding.UTF8.GetBytes(deviceGroup), Encoding.UTF8.GetBytes(username), |
276 | | - Encoding.UTF8.GetBytes(":"), Encoding.UTF8.GetBytes(devicePass)); |
277 | | - byte[] deviceHash = CognitoAuthHelper.Sha256.ComputeHash(deviceContent); |
278 | | - byte[] xBytes = CognitoAuthHelper.CombineBytes(salt.ToBigEndianByteArray(), deviceHash); |
279 | | - |
280 | | - byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); |
281 | | - BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); |
282 | | - |
283 | | - var gX = BigInteger.ModPow(g, x, N); |
284 | | - // Use HKDF to get final password authentication key |
285 | | - var intValue2 = (B - k * gX).TrueMod(N); |
286 | | - var s_value = BigInteger.ModPow(intValue2, Aa.Item2 + u * x, N); |
287 | | - |
288 | | - HkdfSha256 hkdfSha256 = new HkdfSha256(u.ToBigEndianByteArray(), s_value.ToBigEndianByteArray()); |
289 | | - return hkdfSha256.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySizeBytes); |
| 175 | + ); |
| 176 | + |
| 177 | + byte[] userIdHash = CognitoAuthHelper.Sha256.ComputeHash(userIdContent); |
| 178 | + |
| 179 | + byte[] saltBytes = new byte[16]; |
| 180 | + RandomNumberGenerator.Create().GetBytes(saltBytes); |
| 181 | + // setting the initial byte to 0-127 to avoid negative salt or password verifier error |
| 182 | + saltBytes[0] = (byte) r.Next(sbyte.MaxValue); |
| 183 | + |
| 184 | + byte[] xBytes = CognitoAuthHelper.CombineBytes(saltBytes, userIdHash); |
| 185 | + byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); |
| 186 | + BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); |
| 187 | + |
| 188 | + var v = BigInteger.ModPow(g, x, N); |
| 189 | + byte[] vBytes = v.ToBigEndianByteArray(); |
| 190 | + |
| 191 | + return new DeviceSecretVerifierConfigType |
| 192 | + { |
| 193 | + PasswordVerifier = Convert.ToBase64String(vBytes), |
| 194 | + Salt = Convert.ToBase64String(saltBytes) |
| 195 | + }; |
| 196 | + } |
| 197 | + |
| 198 | + /// <summary> |
| 199 | + /// Generates the claim for authenticating a device through the SRP protocol |
| 200 | + /// </summary> |
| 201 | + /// <param name="deviceKey"> Key of CognitoDevice</param> |
| 202 | + /// <param name="devicePassword"> Password of CognitoDevice</param> |
| 203 | + /// <param name="deviceGroupKey"> GroupKey of CognitoDevice</param> |
| 204 | + /// <param name="saltString"> salt provided in ChallengeParameters from Cognito </param> |
| 205 | + /// <param name="srpbString"> srpb provided in ChallengeParameters from Cognito</param> |
| 206 | + /// <param name="secretBlockBase64">secret block provided in ChallengeParameters from Cognito</param> |
| 207 | + /// <param name="formattedTimestamp">En-US Culture of Current Time</param> |
| 208 | + /// <param name="tupleAa"> TupleAa from CreateAaTuple</param> |
| 209 | + /// <returns>Returns the claim for authenticating the given user</returns> |
| 210 | + public static byte[] AuthenticateDevice( |
| 211 | + string deviceKey, |
| 212 | + string devicePassword, |
| 213 | + string deviceGroupKey, |
| 214 | + string saltString, |
| 215 | + string srpbString, |
| 216 | + string secretBlockBase64, |
| 217 | + string formattedTimestamp, |
| 218 | + Tuple<BigInteger, BigInteger> tupleAa) |
| 219 | + |
| 220 | + { |
| 221 | + var B = BigIntegerExtensions.FromUnsignedLittleEndianHex(srpbString); |
| 222 | + if (B.TrueMod(N).Equals(BigInteger.Zero)) throw new ArgumentException("B mod N cannot be zero.", nameof(srpbString)); |
| 223 | + |
| 224 | + var salt = BigIntegerExtensions.FromUnsignedLittleEndianHex(saltString); |
| 225 | + var secretBlockBytes = Convert.FromBase64String(secretBlockBase64); |
| 226 | + // Need to generate the key to hash the response based on our A and what AWS sent back |
| 227 | + var key = GetDeviceAuthenticationKey(deviceKey, devicePassword, deviceGroupKey, tupleAa, B, salt); |
| 228 | + |
| 229 | + // HMAC our data with key (HKDF(S)) (the shared secret) |
| 230 | + var msg = CognitoAuthHelper.CombineBytes( |
| 231 | + Encoding.UTF8.GetBytes(deviceGroupKey), |
| 232 | + Encoding.UTF8.GetBytes(deviceKey), |
| 233 | + secretBlockBytes, |
| 234 | + Encoding.UTF8.GetBytes(formattedTimestamp) |
| 235 | + ); |
| 236 | + |
| 237 | + using (var hashAlgorithm = new HMACSHA256(key)) |
| 238 | + { |
| 239 | + return hashAlgorithm.ComputeHash(msg); |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + /// <summary> |
| 244 | + /// Creates the Device Password Authentication Key based on the SRP protocol |
| 245 | + /// </summary> |
| 246 | + /// <param name="deviceKey">The DeviceKey for the associated CognitoDevice</param> |
| 247 | + /// <param name="devicePass">Password of CognitoDevice</param> |
| 248 | + /// <param name="deviceGroup">GroupKey of CognitoDevice</param> |
| 249 | + /// <param name="Aa">Returned from TupleAa</param> |
| 250 | + /// <param name="B">BigInteger SRPB from AWS ChallengeParameters</param> |
| 251 | + /// <param name="salt">BigInteger salt from AWS ChallengeParameters</param> |
| 252 | + /// <returns>Returns the password authentication key for the SRP protocol</returns> |
| 253 | + public static byte[] GetDeviceAuthenticationKey( |
| 254 | + string deviceKey, |
| 255 | + string devicePass, |
| 256 | + string deviceGroup, |
| 257 | + Tuple<BigInteger, BigInteger> Aa, |
| 258 | + BigInteger B, |
| 259 | + BigInteger salt) |
| 260 | + { |
| 261 | + // Authenticate the password |
| 262 | + // u = H(A, B) |
| 263 | + byte[] contentBytes = CognitoAuthHelper.CombineBytes(Aa.Item1.ToBigEndianByteArray(), B.ToBigEndianByteArray()); |
| 264 | + byte[] digest = CognitoAuthHelper.Sha256.ComputeHash(contentBytes); |
| 265 | + |
| 266 | + BigInteger u = BigIntegerExtensions.FromUnsignedBigEndian(digest); |
| 267 | + if (u.Equals(BigInteger.Zero)) |
| 268 | + { |
| 269 | + throw new ArgumentException("Hash of A and B cannot be zero."); |
| 270 | + } |
| 271 | + |
| 272 | + // x = H(salt | H(deviceGroupKey | deviceKey | ":" | devicePassword)) |
| 273 | + byte[] deviceContent = CognitoAuthHelper.CombineBytes(Encoding.UTF8.GetBytes(deviceGroup), Encoding.UTF8.GetBytes(deviceKey), |
| 274 | + Encoding.UTF8.GetBytes(":"), Encoding.UTF8.GetBytes(devicePass)); |
| 275 | + byte[] deviceHash = CognitoAuthHelper.Sha256.ComputeHash(deviceContent); |
| 276 | + byte[] xBytes = CognitoAuthHelper.CombineBytes(salt.ToBigEndianByteArray(), deviceHash); |
| 277 | + |
| 278 | + byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); |
| 279 | + BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); |
| 280 | + |
| 281 | + var gX = BigInteger.ModPow(g, x, N); |
| 282 | + // Use HKDF to get final password authentication key |
| 283 | + var intValue2 = (B - k * gX).TrueMod(N); |
| 284 | + var s_value = BigInteger.ModPow(intValue2, Aa.Item2 + u * x, N); |
| 285 | + |
| 286 | + HkdfSha256 hkdfSha256 = new HkdfSha256(u.ToBigEndianByteArray(), s_value.ToBigEndianByteArray()); |
| 287 | + return hkdfSha256.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySizeBytes); |
290 | 288 | } |
291 | 289 |
|
292 | 290 | /// <summary> |
|
0 commit comments