Files
2026-11-05 21:21:55 +00:00

68 lines
3.7 KiB
PHP

<?php
/** Nachgebauter Authenticator für die Passkey-Tests (ES256 und RS256). Erwartet $GLOBALS['fa_rp'] (Domain) und $GLOBALS['fa_origin']. */
function t_cbor($v): string
{
if (is_int($v)) return $v >= 0 ? t_cbor_head(0, $v) : t_cbor_head(1, -1 - $v);
if (is_string($v) && !empty($v) && str_starts_with($v, "\0BIN")) { $b = substr($v, 4); return t_cbor_head(2, strlen($b)) . $b; }
if (is_string($v)) return t_cbor_head(3, strlen($v)) . $v;
if (is_array($v)) { $o = t_cbor_head(5, count($v)); foreach ($v as $k => $x) $o .= t_cbor($k) . t_cbor($x); return $o; }
throw new RuntimeException('x');
}
function t_cbor_head(int $m, int $n): string
{
if ($n < 24) return chr(($m << 5) | $n);
if ($n < 256) return chr(($m << 5) | 24) . chr($n);
if ($n < 65536) return chr(($m << 5) | 25) . pack('n', $n);
return chr(($m << 5) | 26) . pack('N', $n);
}
class FakeKey
{
public $res; public string $kind; public string $credId; public int $count = 0;
public function __construct(string $kind)
{
$this->kind = $kind;
static $cnf = null;
if ($cnf === null) { $cnf = sys_get_temp_dir() . '/deskhub-pk-' . bin2hex(random_bytes(4)) . '.cnf'; file_put_contents($cnf, "[req]
distinguished_name=d
[d]
"); }
$this->res = $kind === 'ec' ? openssl_pkey_new(['config' => $cnf, 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1'])
: openssl_pkey_new(['config' => $cnf, 'private_key_type' => OPENSSL_KEYTYPE_RSA, 'private_key_bits' => 2048]);
$this->credId = random_bytes(32);
}
public function cose(): string
{
$d = openssl_pkey_get_details($this->res);
if ($this->kind === 'ec') return t_cbor([1 => 2, 3 => -7, -1 => 1, -2 => "\0BIN" . $d['ec']['x'], -3 => "\0BIN" . $d['ec']['y']]);
return t_cbor([1 => 3, 3 => -257, -1 => "\0BIN" . $d['rsa']['n'], -2 => "\0BIN" . $d['rsa']['e']]);
}
}
function t_client(string $type, string $challenge, ?string $origin = null): string
{
return json_encode(['type' => $type, 'challenge' => $challenge, 'origin' => $origin ?? $GLOBALS['fa_origin'], 'crossOrigin' => false]);
}
function t_register(FakeKey $k, array $opt, array $o = []): array
{
$rpId = $o['rpId'] ?? $GLOBALS['fa_rp'];
$flags = $o['flags'] ?? 0x45;
$ad = hash('sha256', $rpId, true) . chr($flags) . pack('N', $k->count) . str_repeat("\0", 16) . pack('n', strlen($k->credId)) . $k->credId . $k->cose();
$att = t_cbor(['fmt' => 'none', 'attStmt' => [], 'authData' => "\0BIN" . $ad]);
$cd = t_client($o['type'] ?? 'webauthn.create', $o['challenge'] ?? $opt['challenge'], $o['origin'] ?? null);
return ['id' => webauthn_b64u_encode($k->credId), 'response' => ['clientDataJSON' => webauthn_b64u_encode($cd), 'attestationObject' => webauthn_b64u_encode($att)]];
}
function t_login(FakeKey $k, array $opt, int $staffId, array $o = []): array
{
$flags = $o['flags'] ?? 0x05;
$k->count = $o['count'] ?? $k->count + 1;
$ad = hash('sha256', $o['rpId'] ?? $GLOBALS['fa_rp'], true) . chr($flags) . pack('N', $k->count);
$cd = t_client($o['type'] ?? 'webauthn.get', $o['challenge'] ?? $opt['challenge'], $o['origin'] ?? null);
$sig = '';
openssl_sign($ad . hash('sha256', $cd, true), $sig, $o['signWith'] ?? $k->res, OPENSSL_ALGO_SHA256);
return ['id' => webauthn_b64u_encode($k->credId), 'response' => [
'clientDataJSON' => webauthn_b64u_encode($cd), 'authenticatorData' => webauthn_b64u_encode($ad), 'signature' => webauthn_b64u_encode($sig),
'userHandle' => $o['userHandle'] ?? webauthn_b64u_encode(webauthn_user_handle($staffId)),
]];
}