Skip to content

BUG? 'invalid challenge' ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString() !== $challenge->getBinaryString() #110

@russmenum

Description

@russmenum

OK, with a virtual authenticator the demo seems to work, my code is basically that code, but somehow I am getting a challenge mismatch case that prevents registration

My ERROR case:

if ($fn === 'getCreateArgs') {
				$createArgs = $WebAuthn->getCreateArgs(\hex2bin($userId), $userName, $userDisplayName, 60*4, $requireResidentKey, $userVerification, $crossPlatformAttachment);
				$thisChallenge = $WebAuthn->getChallenge();
		
				// header('Content-Type: application/json');
				// print(json_encode($createArgs));
				// REFACTORED FOR USE CASE
				$this->response->type('application/json');
				echo json_encode($createArgs);
		
				$this->log('AUDIT challenge getCreateArgs: '.$thisChallenge,'error');
				$this->log('AUDIT $createArgs->publicKey->challenge: '.$createArgs->publicKey->challenge,'error');

				// save challange to session. you have to deliver it to processGet later.
				// $_SESSION['challenge'] = $WebAuthn->getChallenge();
				// REFACTORED FOR USE CASE
				$this->loadModel('Crypto');
				$this->Crypto->create();
				$cryptoKey = $this->genKey();
				$crypto['Crypto']['key'] = $cryptoKey;
				$crypto['Crypto']['value'] = $WebAuthn->getChallenge();
				$this->Crypto->save($crypto,false);
				$this->Session->write('User.challengeKey',$cryptoKey); 
				unset($crypto,$cryptoKey);

		
			// ------------------------------------
			// request for get arguments
			// ------------------------------------
		
			} 

used $thisChallenge and $WebAuthn->getChallenge() to sanity verify no one behind but as expect both values MATCH. THE LOG:

2025-08-06 08:24:20 Error: AUDIT challenge getCreateArgs: 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
2025-08-06 08:24:20 Error: AUDIT $createArgs->publicKey->challenge: 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44

in the processCreate validating MATCH test values

else if ($fn === 'processCreate') {
				$clientDataJSON = !empty($post->clientDataJSON) ? base64_decode($post->clientDataJSON) : null;
				$crypto = $this->Crypto->find('first',array('recursive'=> -1,'conditions'=>array('Crypto.key'=>$cryptoKey)));
				$this->log('AUDIT challenge VALUE crypto: ','error');
				$this->log( $crypto,'error');
				$this->log( 'client JSON DUMP','error');
				$this->log( json_decode($clientDataJSON),'error');

LOGGED VALUES:

2025-08-06 08:24:20 Error: AUDIT challenge VALUE crypto: 
2025-08-06 08:24:20 Error: Array
(
    [Crypto] => Array
        (
            [id] => 51
            [key] => hn756je7b6
            [value] => 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
        )

)
2025-08-06 08:24:20 Error: client JSON DUMP
2025-08-06 08:24:20 Error: stdClass Object
(
    [type] => webauthn.create
    [challenge] => RjXw6VKmqajvMCoAIuPfKxZmuTlUY8Z3QlM9Dn83Cxw
    [origin] => [THE ORIGIN URL]
    [crossOrigin] => 
)

To check what it is getting modified my local WebAuthn.php for this error case to return the values and logged

WebAuthn.php

 // 4. Verify that the value of C.challenge matches the challenge that was sent to the authenticator in the create() call.
        if (!\property_exists($clientData, 'challenge') || ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString() !== $challenge->getBinaryString()) {
            // throw new WebAuthnException('invalid challenge', WebAuthnException::INVALID_CHALLENGE);
            
            // DEBUG THIS MIS MATCH
            $DEBUGTHISVALUE = ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString();//trying to show this "STRING" JSON 
            $debug = array(
                "server" => $challenge->getBinaryString(),
                "client" => $DEBUGTHISVALUE,
            );
            return $debug;
        }

lse if ($fn === 'processCreate') {

$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $challenge, $userVerification === 'required', true, false);
$this->log( 'DATA DUMP','error');
$this->log( $data,'error');

RESULTING LOG

2025-08-06 08:24:20 Error: DATA DUMP
2025-08-06 08:24:20 Error: Array
(
    [server] => 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
    [client] => F5��R����0*�"��+�f�9Tc�wBS=��7��
)

So yes, the challenge do not match, but why? 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44 is the same across all, this client-side JS works on the demo, what is going on here, what do I need to change to keep the MATCH?

Client side the JSON starts as

{
    "rp": {
        "name": "WebAuthn Library",
        "id": "[the origin URL missing the https:// value from the form]"
    },
    "authenticatorSelection": {
        "userVerification": "discouraged"
    },
    "user": {
        "id": "=?BINARY?B?ZGVtb2RlbW8=?=",
        "name": "demo",
        "displayName": "Demo Demolin"
    },
    "pubKeyCredParams": [
        {
            "type": "public-key",
            "alg": -8
        },
        {
            "type": "public-key",
            "alg": -7
        },
        {
            "type": "public-key",
            "alg": -257
        }
    ],
    "attestation": "direct",
    "extensions": {
        "exts": true
    },
    "timeout": 240000,
    "challenge": "=?BINARY?B?CFEem8RcJKhOAl2iecXTa5PpWpV1bMfuVqsCSJyoXkQ=?=",
    "excludeCredentials": []
}

Which matched the CFEem8RcJKhOAl2iecXTa5PpWpV1bMfuVqsCSJyoXkQ part of logged CLIENT CFEem8RcJKhOAl2iecXTa5PpWpV1bMfuVqsCSJyoXkQ I understand JSON messes this up, and the reason for recursiveBase64StrToArrayBuffer() is to handle the restore to ArrayBuffer, which seems to be happening

challenge
: 
ArrayBuffer(32)
byteLength
: 
32
detached
: 
false
maxByteLength
: 
32
resizable
: 
false
[[Prototype]]
: 
ArrayBuffer
[[Int8Array]]
: 
Int8Array(32)
[[Uint8Array]]
: 
Uint8Array(32)
[[Int16Array]]
: 
Int16Array(16)
[[Int32Array]]
: 
Int32Array(8)
[[ArrayBufferByteLength]]
: 
32
[[ArrayBufferData]]
: 
603

But, yet this mismatch?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions