challenge = base64_encode(generateRandomString($length, "raw")); $_SESSION["challenge"] = $this->challenge; } public static function create(int $challengeLength = 32): KeyBasedTwoFactorToken { $token = new KeyBasedTwoFactorToken(); $token->generateChallenge($challengeLength); return $token; } public function hasChallenge(): bool { return isset($this->challenge); } public function getChallenge(): string { return $this->challenge; } protected function readData(string $data) { if (!$this->isConfirmed()) { $this->challenge = $data; $this->credentialID = null; $this->publicKey = null; } else { $jsonData = json_decode($data, true); $this->challenge = $_SESSION["challenge"] ?? ""; $this->credentialID = base64_decode($jsonData["credentialID"]); $this->publicKey = PublicKey::fromJson($jsonData["publicKey"]); } } public function getData(): string { if (!$this->isConfirmed()) { return $this->challenge; } else { return json_encode([ "credentialID" => $this->credentialID, "publicKey" => $this->publicKey->jsonSerialize() ]); } } public function confirmKeyBased(SQL $sql, string $credentialID, PublicKey $publicKey): bool { $this->credentialID = $credentialID; $this->publicKey = $publicKey; return parent::confirm($sql); } public function getPublicKey(): ?PublicKey { return $this->publicKey; } public function getCredentialId(): ?string { return $this->credentialID; } public function jsonSerialize(?array $propertyNames = null): array { $jsonData = parent::jsonSerialize(); if (!$this->isAuthenticated()) { if (!empty($this->challenge) && ($propertyNames === null || in_array("challenge", $propertyNames))) { $jsonData["challenge"] = $this->challenge; } if (!empty($this->credentialID) && ($propertyNames === null || in_array("credentialID", $propertyNames))) { $jsonData["credentialID"] = base64_encode($this->credentialID); } } return $jsonData; } // TODO: algorithms, hardcoded values, ... public function verify(string $signature, string $data): bool { switch ($this->publicKey->getUsedAlgorithm()) { case -7: // EC2 if (strlen($signature) !== 64) { $signature = \Cose\Algorithm\Signature\ECDSA\ECSignature::fromAsn1($signature, 64); } $coseKey = new \Cose\Key\Key($this->publicKey->getNormalizedData()); $ec2key = new \Cose\Key\Ec2Key($coseKey->getData()); $publicKey = $ec2key->toPublic(); $signature = ECSignature::toAsn1($signature, 64); return openssl_verify($data, $signature, $publicKey->asPEM(), "sha256") === 1; default: // Not implemented :( return false; } } }