/// <summary> /// Creates the Password Authentication Key based on the SRP protocol /// </summary> /// <param name="userID"> Username of CognitoUser</param> /// <param name="userPassword">Password of CognitoUser</param> /// <param name="poolName">PoolName of CognitoUserPool (part of poolID after "_")</param> /// <param name="Aa">Returned from TupleAa</param> /// <param name="B">BigInteger SRPB from AWS ChallengeParameters</param> /// <param name="salt">BigInteger salt from AWS ChallengeParameters</param> /// <returns>Returns the password authentication key for the SRP protocol</returns> public static byte[] GetPasswordAuthenticationKey(string userID, string userPassword, string poolName, Tuple <BigInteger, BigInteger> Aa, BigInteger B, BigInteger salt) { // Authenticate the password // u = H(A, B) byte[] contentBytes = CognitoAuthHelper.CombineBytes(new [] { Aa.Item1.ToBigEndianByteArray(), B.ToBigEndianByteArray() }); byte[] digest = CognitoAuthHelper.Sha256.ComputeHash(contentBytes); BigInteger u = BigIntegerExtensions.FromUnsignedBigEndian(digest); if (u.Equals(BigInteger.Zero)) { throw new ArgumentException("Hash of A and B cannot be zero."); } // x = H(salt | H(poolName | userId | ":" | password)) byte[] userIdContent = CognitoAuthHelper.CombineBytes(new byte[][] { Encoding.UTF8.GetBytes(poolName), Encoding.UTF8.GetBytes(userID), Encoding.UTF8.GetBytes(":"), Encoding.UTF8.GetBytes(userPassword) }); byte[] userIdHash = CognitoAuthHelper.Sha256.ComputeHash(userIdContent); byte[] xBytes = CognitoAuthHelper.CombineBytes(new byte[][] { salt.ToBigEndianByteArray(), userIdHash }); byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); // Use HKDF to get final password authentication key var first = (B - k * BigInteger.ModPow(g, x, N)).TrueMod(N); var second = BigInteger.ModPow(first, Aa.Item2 + u * x, N); HkdfSha256 hkdfSha256 = new HkdfSha256(u.ToBigEndianByteArray(), second.ToBigEndianByteArray()); return(hkdfSha256.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySizeBytes)); }
/// <summary> /// Generates the claim for authenticating a user through the SRP protocol /// </summary> /// <param name="username"> Username of CognitoUser</param> /// <param name="password"> Password of CognitoUser</param> /// <param name="poolName"> PoolName of CognitoUserPool (from poolID: <region>_<poolName>)</param> /// <param name="tupleAa"> TupleAa from CreateAaTuple</param> /// <param name="saltString"> salt provided in ChallengeParameters from Cognito </param> /// <param name="srpbString"> srpb provided in ChallengeParameters from Cognito</param> /// <param name="secretBlockBase64">secret block provided in ChallengeParameters from Cognito</param> /// <param name="formattedTimestamp">En-US Culture of Current Time</param> /// <returns>Returns the claim for authenticating the given user</returns> public static byte[] AuthenticateUser( string username, string password, string poolName, Tuple <BigInteger, BigInteger> tupleAa, string saltString, string srpbString, string secretBlockBase64, string formattedTimestamp) { var B = BigIntegerExtensions.FromUnsignedLittleEndianHex(srpbString); if (B.TrueMod(N).Equals(BigInteger.Zero)) { throw new ArgumentException("B mod N cannot be zero.", nameof(srpbString)); } var secretBlockBytes = Convert.FromBase64String(secretBlockBase64); var salt = BigIntegerExtensions.FromUnsignedLittleEndianHex(saltString); // Need to generate the key to hash the response based on our A and what AWS sent back var key = GetPasswordAuthenticationKey(username, password, poolName, tupleAa, B, salt); // HMAC our data with key (HKDF(S)) (the shared secret) var contentBytes = CognitoAuthHelper.CombineBytes(new [] { Encoding.UTF8.GetBytes(poolName), Encoding.UTF8.GetBytes(username), secretBlockBytes, Encoding.UTF8.GetBytes(formattedTimestamp) }); using (var hashAlgorithm = new HMACSHA256(key)) { return(hashAlgorithm.ComputeHash(contentBytes)); } }
/// <summary> /// Generates a DeviceSecretVerifierConfigType object based on a CognitoDevice's Key, Group Key, and Password /// </summary> /// <param name="deviceGroupKey">The Group Key of the CognitoDevice</param> /// <param name="devicePass">A random password for the CognitoDevice (used in the future for logging in via this device)</param> /// <param name="username">The username of the CognitoDevice user</param> /// <returns></returns> public static DeviceSecretVerifierConfigType GenerateDeviceVerifier(string deviceGroupKey, string devicePass, string username) { Random r = new Random(); byte[] userIdContent = CognitoAuthHelper.CombineBytes( Encoding.UTF8.GetBytes(deviceGroupKey), Encoding.UTF8.GetBytes(username), Encoding.UTF8.GetBytes(":"), Encoding.UTF8.GetBytes(devicePass) ); byte[] userIdHash = CognitoAuthHelper.Sha256.ComputeHash(userIdContent); byte[] saltBytes = new byte[16]; RandomNumberGenerator.Create().GetBytes(saltBytes); // setting the initial byte to 0-127 to avoid negative salt or password verifier error saltBytes[0] = (byte)r.Next(sbyte.MaxValue); byte[] xBytes = CognitoAuthHelper.CombineBytes(saltBytes, userIdHash); byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); var v = BigInteger.ModPow(g, x, N); byte[] vBytes = v.ToBigEndianByteArray(); return(new DeviceSecretVerifierConfigType { PasswordVerifier = Convert.ToBase64String(vBytes), Salt = Convert.ToBase64String(saltBytes) }); }
/// <summary> /// Internal method for generating k for the input key material to HKDF /// </summary> /// <returns>Returns the BigInteger k value for the HKDF protocol's ikm</returns> private static BigInteger CreateKForGeneratingIkm() { var content = CognitoAuthHelper.CombineBytes(new[] { N.ToBigEndianByteArray(), g.ToBigEndianByteArray() }); var messageDigest = CognitoAuthHelper.Sha256.ComputeHash(content); return(BigIntegerExtensions.FromBigEndian(messageDigest)); }
static AuthenticationHelper() { // generate k for the input key material to HKDF var content = CognitoAuthHelper.CombineBytes(new[] { N.ToBigEndianByteArray(), g.ToBigEndianByteArray() }); var messageDigest = CognitoAuthHelper.Sha256.ComputeHash(content); k = BigIntegerExtensions.FromUnsignedBigEndian(messageDigest); }
/// <summary> /// Create a cryptographically secure random BigInteger /// </summary> /// <returns></returns> public static BigInteger CreateBigIntegerRandom() { var b = new byte[SecretKeySizeBytes]; using (var cryptoRandom = RandomNumberGenerator.Create()) { cryptoRandom.GetBytes(b); } return(BigIntegerExtensions.FromUnsignedBigEndian(b)); }
/// <summary> /// Creates the Device Password Authentication Key based on the SRP protocol /// </summary> /// <param name="username"> Username of Cognito User</param> /// <param name="devicePass">Password of CognitoDevice</param> /// <param name="deviceGroup">GroupKey of CognitoDevice</param> /// <param name="Aa">Returned from TupleAa</param> /// <param name="B">BigInteger SRPB from AWS ChallengeParameters</param> /// <param name="salt">BigInteger salt from AWS ChallengeParameters</param> /// <returns>Returns the password authentication key for the SRP protocol</returns> public static byte[] GetDeviceAuthenticationKey( string username, string devicePass, string deviceGroup, Tuple <BigInteger, BigInteger> Aa, BigInteger B, BigInteger salt) { // Authenticate the password // u = H(A, B) byte[] contentBytes = CognitoAuthHelper.CombineBytes(Aa.Item1.ToBigEndianByteArray(), B.ToBigEndianByteArray()); byte[] digest = CognitoAuthHelper.Sha256.ComputeHash(contentBytes); BigInteger u = BigIntegerExtensions.FromUnsignedBigEndian(digest); if (u.Equals(BigInteger.Zero)) { throw new ArgumentException("Hash of A and B cannot be zero."); } // x = H(salt | H(deviceGroupKey | deviceKey | ":" | devicePassword)) byte[] deviceContent = CognitoAuthHelper.CombineBytes(Encoding.UTF8.GetBytes(deviceGroup), Encoding.UTF8.GetBytes(username), Encoding.UTF8.GetBytes(":"), Encoding.UTF8.GetBytes(devicePass)); byte[] deviceHash = CognitoAuthHelper.Sha256.ComputeHash(deviceContent); byte[] xBytes = CognitoAuthHelper.CombineBytes(salt.ToBigEndianByteArray(), deviceHash); byte[] xDigest = CognitoAuthHelper.Sha256.ComputeHash(xBytes); BigInteger x = BigIntegerExtensions.FromUnsignedBigEndian(xDigest); var gX = BigInteger.ModPow(g, x, N); // Use HKDF to get final password authentication key var intValue2 = (B - k * gX).TrueMod(N); var s_value = BigInteger.ModPow(intValue2, Aa.Item2 + u * x, N); HkdfSha256 hkdfSha256 = new HkdfSha256(u.ToBigEndianByteArray(), s_value.ToBigEndianByteArray()); return(hkdfSha256.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySizeBytes)); }