readiness($nonce); return is_string($raw)&&self::verify($nonce,$raw,$keys,$revoked); } catch(Throwable $e){return false;} } public static function request(string $nonce): string { if(!preg_match('/^[a-f0-9]{64}$/D',$nonce)){throw new InvalidArgumentException('readiness_nonce');} return json_encode(['version'=>'og-native-readiness-v1','nonce'=>$nonce],JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR); } public static function verify(string $nonce,string $raw,array $keys,array $revoked=[]): bool { try { self::request($nonce); if(strlen($raw)>2048||!function_exists('sodium_crypto_sign_verify_detached')){return false;} $body=json_decode($raw,true,8,JSON_THROW_ON_ERROR); if(!is_array($body)||array_keys($body)!==['version','nonce','environment','key_id','capability','schema','signature']||json_encode($body,JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR)!==$raw){return false;} if($body['version']!=='og-native-readiness-v1'||$body['nonce']!==$nonce||$body['environment']!=='production'||$body['capability']!=='native-v4'||$body['schema']!=='native-v4-2'){return false;} $id=$body['key_id']; if(!is_string($id)||!preg_match('/^prod-[a-z0-9-]{1,48}$/D',$id)||array_key_exists($id,$revoked)||!isset($keys[$id])||!is_string($keys[$id])||!preg_match('/^[a-f0-9]{64}$/D',$keys[$id])||!is_string($body['signature'])){return false;} $sig=base64_decode($body['signature'],true); if($sig===false||strlen($sig)!==64||base64_encode($sig)!==$body['signature']){return false;} unset($body['signature']); return sodium_crypto_sign_verify_detached($sig,"og-native-readiness-signature-v1\n".json_encode($body,JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR),hex2bin($keys[$id])); } catch(Throwable $e){return false;} } }