I've been trying to get this working for a while now. There is some code that I was able to use as a reference, but unfortunately, I'm still facing some problems and it is now unclear as to why it is occurring.
Before I get started with the issue, I want to point out the references I mentioned earlier. Here are the key links that helped me:
- https://gist.github.com/jenky/a4465f73adf90206b3e98c3d36a3be4f (excellent starting point)
- https://github.com/alexrudd/cognito-srp/blob/master/cognitosrp.go
- https://github.com/aws/aws-sdk-net-extensions-cognito/tree/ab97c0cb0b8fc3c87573d31b2dc9fb9f7c2e5573/src/Amazon.Extensions.CognitoAuthentication
- DEVICE_PASSWORD_VERIFIER challenge response in Amazon Cognito using boto3 and warrant
Here is a blog post that I used for help as well and aligned me with the links provided above: https://aws.amazon.com/premiumsupport/knowledge-center/cognito-user-pool-remembered-devices/
Let me refer to that article so I can explain my issue better.
So, I was able to get past all the steps except for that last one that keeps failing. Let's assume some basic variables:
$device_group_key = "-hdk698";
$device_key = "us-east-1-dsdsdsds-sdsds-sdsd";
$username = $poolId.$userId;
I first need to get the device["PasswordVerifier"]. To make that happen, I will call this function:
$device = $this->getDeviceSecretVerifierConfig($device_group_key, $username);
// reference to the function
public function getDeviceSecretVerifierConfig(string $deviceGroupKey, string $username): array
{
$salt = $this->bytes(16);
$randomPassword = $this->bytes(40);
$fullPassword = $this->hash(sprintf('%s%s:%s', $deviceGroupKey, $username, $randomPassword));
$passwordVerifier = $this->g->modPow(
new BigInteger($this->hexHash($salt->toHex() . $fullPassword), 16),
$this->N
);
return [
'Salt' => base64_encode($salt->toString()),
'PasswordVerifier' => base64_encode($passwordVerifier->toString())
];
}
Now, I need to get the Device Password Authentication Key.
$hkdf = $this->getDevicePasswordAuthenticationKey(
$username,
$device["PasswordVerifier"],
$device_group_key,
$challengeParameters['SRP_B'],
$challengeParameters['SALT']
);
$msg = $device_group_key.$device_key.$secretBlock.$time;
$signature = hash_hmac('sha256', $msg, $hkdf, true);
// reference to the function
protected function getDevicePasswordAuthenticationKey(string $username, string $devicePassword, string $deviceGroup, string $server, string $salt): string
{
$u = $this->calculateU($this->largeA(), $serverB = new BigInteger($server, 16));
if ($u->equals(new BigInteger(0))) {
throw new \RuntimeException('U cannot be zero.');
}
$fullPassword = $this->hash(sprintf('%s%s:%s', $deviceGroupKey, $username, $devicePassword));
$x = new BigInteger($this->hexHash($this->padHex($salt).$fullPassword), 16);
$gModPowXN = $this->g->modPow($x, $this->N);
$intValue2 = $serverB->subtract($this->k->multiply($gModPowXN));
$s = $intValue2->modPow($this->smallA()->add($u->multiply($x)), $this->N);
return $this->computeHkdf(
hex2bin($this->padHex($s)),
hex2bin($this->padHex($u))
);
}
With all this information, I can finally return what is needed to pass this last challenge.
return [
"TIMESTAMP" => $time,
"USERNAME" => $userId,
"PASSWORD_CLAIM_SECRET_BLOCK" => $secret_block,
"PASSWORD_CLAIM_SIGNATURE" => base64_encode($signature),
"DEVICE_KEY" => $device_key
];
Please note that this is not the whole code, I'm just trying to show the essential bits, but I might be wrong so let me know.
For some reason, it isn't working as expected, and I've tried countless options with no luck. If anyone can help, it would be great!
Thanks a lot! Mark