Skip to content

Commit 109b323

Browse files
committed
Merge branch 'main' into dev
2 parents c021743 + 28abd32 commit 109b323

File tree

3 files changed

+143
-134
lines changed

3 files changed

+143
-134
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "Amazon.Extensions.CognitoAuthentication",
5+
"Type": "Patch",
6+
"ChangelogMessages": [
7+
"Fix Auth with device and device confirmation"
8+
]
9+
}
10+
]
11+
}

src/Amazon.Extensions.CognitoAuthentication/CognitoUserAuthentication.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private RespondToAuthChallengeRequest CreateDevicePasswordVerifierAuthRequest(Re
193193

194194
string timeStr = DateTime.UtcNow.ToString("ddd MMM d HH:mm:ss \"UTC\" yyyy", CultureInfo.InvariantCulture);
195195

196-
var claimBytes = AuthenticationHelper.AuthenticateDevice(username, deviceKey, devicePassword, deviceKeyGroup, salt,
196+
var claimBytes = AuthenticationHelper.AuthenticateDevice(deviceKey, devicePassword, deviceKeyGroup, salt,
197197
challenge.ChallengeParameters[CognitoConstants.ChlgParamSrpB], secretBlock, timeStr, tupleAa);
198198

199199

@@ -321,9 +321,9 @@ public virtual async Task<AuthFlowResponse> RespondToCustomAuthAsync(RespondToCu
321321
/// <param name="deviceKey">The DeviceKey for the associated CognitoDevice</param>
322322
/// <param name="devicePass">The random password for the associated CognitoDevice</param>
323323
/// <returns></returns>
324-
public DeviceSecretVerifierConfigType GenerateDeviceVerifier(string deviceGroupKey, string devicePass, string username)
324+
public DeviceSecretVerifierConfigType GenerateDeviceVerifier(string deviceGroupKey, string devicePass, string deviceKey)
325325
{
326-
return AuthenticationHelper.GenerateDeviceVerifier(deviceGroupKey, devicePass, username);
326+
return AuthenticationHelper.GenerateDeviceVerifier(deviceGroupKey, devicePass, deviceKey);
327327
}
328328

329329
/// <summary>

src/Amazon.Extensions.CognitoAuthentication/Util/AuthenticationHelper.cs

Lines changed: 129 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
* express or implied. See the License for the specific language governing
1313
* permissions and limitations under the License.
1414
*/
15-
15+
1616
using System;
17-
using System.Globalization;
18-
using System.Linq;
17+
using System.Globalization;
18+
using System.Linq;
1919
using System.Numerics;
2020
using System.Security.Cryptography;
2121
using System.Text;
@@ -157,136 +157,134 @@ public static byte[] GetPasswordAuthenticationKey(
157157
return hkdfSha256.Expand(Encoding.UTF8.GetBytes(DerivedKeyInfo), DerivedKeySizeBytes);
158158
}
159159

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>
166166
/// <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(":"),
174174
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);
290288
}
291289

292290
/// <summary>

0 commit comments

Comments
 (0)