0

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:

  1. https://gist.github.com/jenky/a4465f73adf90206b3e98c3d36a3be4f (excellent starting point)
  2. https://github.com/alexrudd/cognito-srp/blob/master/cognitosrp.go
  3. https://github.com/aws/aws-sdk-net-extensions-cognito/tree/ab97c0cb0b8fc3c87573d31b2dc9fb9f7c2e5573/src/Amazon.Extensions.CognitoAuthentication
  4. 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

Mark
  • 1,603
  • 3
  • 13
  • 18

1 Answers1

1

I did reach out on this post to see if you had managed to get this working... and could therefore potentially help me. Unfortunately this isn't allowed it would seem so instead I had to sort it myself.

If you look at the key links listed by the OP and follow 1. you will see that much of this is ported from Python code, literally by checking inputs and outputs from functions ... which is what I ended up doing over the past number of days, including familiarisation with Python.

Anyway, for those of you who come across this issue in PHP this should hopefully fix it for you.

The first problem was with the getDeviceSecretVerifierConfig function which is used to confirm you want your device tracked and establish your own password verifier.

public function getDeviceSecretVerifierConfig(string $deviceGroupKey, string $deviceKey): array
{

    $randomPassword = $this->bytes(40);
    $fullPassword = $this->hash(sprintf('%s%s:%s', $deviceGroupKey, $deviceKey, $randomPassword));

    $salt = $this->bytes(16);
    $SaltToHashDevices = $this->padHex(new BigInteger($salt->toHex(),16));

    $x = new BigInteger($this->hexHash($SaltToHashDevices.$fullPassword), 16);
    $gModPowXN = $this->g->modPow($x, $this->N);
    $passwordVerifier = $this->padHex($gModPowXN->toHex());

    return [
        'Salt' => base64_encode(hex2bin($SaltToHashDevices)),
        'PasswordVerifier' => base64_encode(hex2bin($passwordVerifier)),
        'rndPass' => $randomPassword
    ];
}

With that fixed I also had to make a couple of changes for the device password authentication:

protected function getDevicePasswordAuthenticationKey(string $deviceGroupKey, string $deviceKey, string $devicePassword, 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.');
  }

  $usernamePassword = sprintf('%s%s:%s', $deviceGroupKey, $deviceKey, $devicePassword);
  $fullPassword = $this->hash($usernamePassword);
 
  $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))
  );
}

This was called by my final function which I used as it made more sense with the code structure used in the first key link.

protected function deviceChallenge(Result $result, string $rndPassword, string $deviceGroupKey, string $deviceK, string $username): array
    {
        $challengeParameters = $result->get('ChallengeParameters');
        $time = Carbon::now('UTC')->format('D M j H:i:s e Y');
        $secretBlock = base64_decode($challengeParameters['SECRET_BLOCK']);
        $userId = $challengeParameters['USERNAME'];
        $deviceKey = $challengeParameters['DEVICE_KEY'];

        $hkdf = $this->getDevicePasswordAuthenticationKey(
             $deviceGroupKey, 
             $deviceK,
             $rndPassword,
             $challengeParameters['SRP_B'],
             $challengeParameters['SALT'],
        );

        $msg = $deviceGroupKey.$deviceKey.$secretBlock.$time;
        $signature = hash_hmac('sha256', $msg, $hkdf, true);

        return [
            'TIMESTAMP' => $time,
            'USERNAME' => $userId,
            'PASSWORD_CLAIM_SECRET_BLOCK' => $challengeParameters['SECRET_BLOCK'],
            'PASSWORD_CLAIM_SIGNATURE' => base64_encode($signature),
            'DEVICE_KEY' => $deviceK,
        ];
    }

Anyway, hopefully the above may help someone out at some point..... not that you'll be able to pm me to let me know ;-). Enjoy.

James A
  • 11
  • 3
  • Sorry, I didn't mean to ignore you in any way. I have not received a notification from StackOverflow regarding a PM or maybe I did not notice it. We can talk offline if you want to. I'm glad you figured it out. Cheers – Mark Jul 19 '23 at 16:30
  • Hi Mark, that's no problem. I don't believe you would have seen my post as it was rejected as being more of a question rather than an answer. That was my frustration as there is no way to reach out to someone who potentially already has the answer. Anyway, your original post was very helpful, pointing me in the right directions. Given my solution does work it's all good for now but thanks for the offer. Hopefully this all helps someone else. – James A Jul 27 '23 at 12:25