Initialer Commit: Kundenverwaltung (PHP/Smarty)
Bestehende Codebasis als Ausgangsstand aufgenommen. - .gitignore: Kundendaten (uploads_*), Smarty-Compile-Cache (templates_c), Laufzeit-Temp und Datenbank-Dumps ausgeschlossen; Verzeichnisstruktur ueber .gitkeep erhalten - .gitattributes: Zeilenenden normalisiert (LF im Repo), Binaerformate markiert - db-backup.php: MySQL-Zugangsdaten kommen jetzt wie im Rest der Anwendung aus der externen Konfiguration statt hartkodiert aus der Datei; Passwort wird ueber eine temporaere Optionsdatei statt per Kommandozeile uebergeben, Fehler von mysqldump/gzip werden gemeldet, Aufraeumen aelterer Backups berechnet Jahr und Monat konsistent Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,547 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2022 Lukas Buchs
|
||||
license https://github.com/lbuchs/WebAuthn/blob/master/LICENSE MIT
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>lbuchs/WebAuthn Test</title>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
|
||||
/**
|
||||
* creates a new FIDO2 registration
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function createRegistration() {
|
||||
try {
|
||||
|
||||
// check browser support
|
||||
if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
|
||||
throw new Error('Browser not supported.');
|
||||
}
|
||||
|
||||
// get create args
|
||||
let rep = await window.fetch('server.php?fn=getCreateArgs' + getGetParams(), {method:'GET', cache:'no-cache'});
|
||||
const createArgs = await rep.json();
|
||||
|
||||
// error handling
|
||||
if (createArgs.success === false) {
|
||||
throw new Error(createArgs.msg || 'unknown error occured');
|
||||
}
|
||||
|
||||
// replace binary base64 data with ArrayBuffer. a other way to do this
|
||||
// is the reviver function of JSON.parse()
|
||||
recursiveBase64StrToArrayBuffer(createArgs);
|
||||
|
||||
// create credentials
|
||||
const cred = await navigator.credentials.create(createArgs);
|
||||
|
||||
// create object
|
||||
const authenticatorAttestationResponse = {
|
||||
transports: cred.response.getTransports ? cred.response.getTransports() : null,
|
||||
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
||||
attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null
|
||||
};
|
||||
|
||||
// check auth on server side
|
||||
rep = await window.fetch('server.php?fn=processCreate' + getGetParams(), {
|
||||
method : 'POST',
|
||||
body : JSON.stringify(authenticatorAttestationResponse),
|
||||
cache : 'no-cache'
|
||||
});
|
||||
const authenticatorAttestationServerResponse = await rep.json();
|
||||
|
||||
// prompt server response
|
||||
if (authenticatorAttestationServerResponse.success) {
|
||||
reloadServerPreview();
|
||||
window.alert(authenticatorAttestationServerResponse.msg || 'registration success');
|
||||
|
||||
} else {
|
||||
throw new Error(authenticatorAttestationServerResponse.msg);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
reloadServerPreview();
|
||||
window.alert(err.message || 'unknown error occured');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checks a FIDO2 registration
|
||||
* @returns {undefined}
|
||||
*/
|
||||
async function checkRegistration() {
|
||||
try {
|
||||
|
||||
if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
|
||||
throw new Error('Browser not supported.');
|
||||
}
|
||||
|
||||
// get check args
|
||||
let rep = await window.fetch('server.php?fn=getGetArgs' + getGetParams(), {method:'GET',cache:'no-cache'});
|
||||
const getArgs = await rep.json();
|
||||
|
||||
// error handling
|
||||
if (getArgs.success === false) {
|
||||
throw new Error(getArgs.msg);
|
||||
}
|
||||
|
||||
// replace binary base64 data with ArrayBuffer. a other way to do this
|
||||
// is the reviver function of JSON.parse()
|
||||
recursiveBase64StrToArrayBuffer(getArgs);
|
||||
|
||||
// check credentials with hardware
|
||||
const cred = await navigator.credentials.get(getArgs);
|
||||
|
||||
// create object for transmission to server
|
||||
const authenticatorAttestationResponse = {
|
||||
id: cred.rawId ? arrayBufferToBase64(cred.rawId) : null,
|
||||
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
||||
authenticatorData: cred.response.authenticatorData ? arrayBufferToBase64(cred.response.authenticatorData) : null,
|
||||
signature: cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null,
|
||||
userHandle: cred.response.userHandle ? arrayBufferToBase64(cred.response.userHandle) : null
|
||||
};
|
||||
|
||||
// send to server
|
||||
rep = await window.fetch('server.php?fn=processGet' + getGetParams(), {
|
||||
method:'POST',
|
||||
body: JSON.stringify(authenticatorAttestationResponse),
|
||||
cache:'no-cache'
|
||||
});
|
||||
const authenticatorAttestationServerResponse = await rep.json();
|
||||
|
||||
// check server response
|
||||
if (authenticatorAttestationServerResponse.success) {
|
||||
reloadServerPreview();
|
||||
window.alert(authenticatorAttestationServerResponse.msg || 'login success');
|
||||
} else {
|
||||
throw new Error(authenticatorAttestationServerResponse.msg);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
reloadServerPreview();
|
||||
window.alert(err.message || 'unknown error occured');
|
||||
}
|
||||
}
|
||||
|
||||
function clearRegistration() {
|
||||
window.fetch('server.php?fn=clearRegistrations' + getGetParams(), {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
|
||||
}).then(function(json) {
|
||||
if (json.success) {
|
||||
reloadServerPreview();
|
||||
window.alert(json.msg);
|
||||
} else {
|
||||
throw new Error(json.msg);
|
||||
}
|
||||
}).catch(function(err) {
|
||||
reloadServerPreview();
|
||||
window.alert(err.message || 'unknown error occured');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function queryFidoMetaDataService() {
|
||||
window.fetch('server.php?fn=queryFidoMetaDataService' + getGetParams(), {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
|
||||
}).then(function(json) {
|
||||
if (json.success) {
|
||||
window.alert(json.msg);
|
||||
} else {
|
||||
throw new Error(json.msg);
|
||||
}
|
||||
}).catch(function(err) {
|
||||
window.alert(err.message || 'unknown error occured');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* convert RFC 1342-like base64 strings to array buffer
|
||||
* @param {mixed} obj
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function recursiveBase64StrToArrayBuffer(obj) {
|
||||
let prefix = '=?BINARY?B?';
|
||||
let suffix = '?=';
|
||||
if (typeof obj === 'object') {
|
||||
for (let key in obj) {
|
||||
if (typeof obj[key] === 'string') {
|
||||
let str = obj[key];
|
||||
if (str.substring(0, prefix.length) === prefix && str.substring(str.length - suffix.length) === suffix) {
|
||||
str = str.substring(prefix.length, str.length - suffix.length);
|
||||
|
||||
let binary_string = window.atob(str);
|
||||
let len = binary_string.length;
|
||||
let bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
obj[key] = bytes.buffer;
|
||||
}
|
||||
} else {
|
||||
recursiveBase64StrToArrayBuffer(obj[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a ArrayBuffer to Base64
|
||||
* @param {ArrayBuffer} buffer
|
||||
* @returns {String}
|
||||
*/
|
||||
function arrayBufferToBase64(buffer) {
|
||||
let binary = '';
|
||||
let bytes = new Uint8Array(buffer);
|
||||
let len = bytes.byteLength;
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode( bytes[ i ] );
|
||||
}
|
||||
return window.btoa(binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL parameter
|
||||
* @returns {String}
|
||||
*/
|
||||
function getGetParams() {
|
||||
let url = '';
|
||||
|
||||
url += '&apple=' + (document.getElementById('cert_apple').checked ? '1' : '0');
|
||||
url += '&yubico=' + (document.getElementById('cert_yubico').checked ? '1' : '0');
|
||||
url += '&solo=' + (document.getElementById('cert_solo').checked ? '1' : '0');
|
||||
url += '&hypersecu=' + (document.getElementById('cert_hypersecu').checked ? '1' : '0');
|
||||
url += '&google=' + (document.getElementById('cert_google').checked ? '1' : '0');
|
||||
url += 'µsoft=' + (document.getElementById('cert_microsoft').checked ? '1' : '0');
|
||||
url += '&mds=' + (document.getElementById('cert_mds').checked ? '1' : '0');
|
||||
|
||||
url += '&requireResidentKey=' + (document.getElementById('requireResidentKey').checked ? '1' : '0');
|
||||
|
||||
url += '&type_usb=' + (document.getElementById('type_usb').checked ? '1' : '0');
|
||||
url += '&type_nfc=' + (document.getElementById('type_nfc').checked ? '1' : '0');
|
||||
url += '&type_ble=' + (document.getElementById('type_ble').checked ? '1' : '0');
|
||||
url += '&type_int=' + (document.getElementById('type_int').checked ? '1' : '0');
|
||||
url += '&type_hybrid=' + (document.getElementById('type_hybrid').checked ? '1' : '0');
|
||||
|
||||
url += '&fmt_android-key=' + (document.getElementById('fmt_android-key').checked ? '1' : '0');
|
||||
url += '&fmt_android-safetynet=' + (document.getElementById('fmt_android-safetynet').checked ? '1' : '0');
|
||||
url += '&fmt_apple=' + (document.getElementById('fmt_apple').checked ? '1' : '0');
|
||||
url += '&fmt_fido-u2f=' + (document.getElementById('fmt_fido-u2f').checked ? '1' : '0');
|
||||
url += '&fmt_none=' + (document.getElementById('fmt_none').checked ? '1' : '0');
|
||||
url += '&fmt_packed=' + (document.getElementById('fmt_packed').checked ? '1' : '0');
|
||||
url += '&fmt_tpm=' + (document.getElementById('fmt_tpm').checked ? '1' : '0');
|
||||
|
||||
url += '&rpId=' + encodeURIComponent(document.getElementById('rpId').value);
|
||||
|
||||
url += '&userId=' + encodeURIComponent(document.getElementById('userId').value);
|
||||
url += '&userName=' + encodeURIComponent(document.getElementById('userName').value);
|
||||
url += '&userDisplayName=' + encodeURIComponent(document.getElementById('userDisplayName').value);
|
||||
|
||||
if (document.getElementById('userVerification_required').checked) {
|
||||
url += '&userVerification=required';
|
||||
|
||||
} else if (document.getElementById('userVerification_preferred').checked) {
|
||||
url += '&userVerification=preferred';
|
||||
|
||||
} else if (document.getElementById('userVerification_discouraged').checked) {
|
||||
url += '&userVerification=discouraged';
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
function reloadServerPreview() {
|
||||
let iframe = document.getElementById('serverPreview');
|
||||
iframe.src = iframe.src;
|
||||
}
|
||||
|
||||
function setAttestation(attestation) {
|
||||
let inputEls = document.getElementsByTagName('input');
|
||||
for (const inputEl of inputEls) {
|
||||
if (inputEl.id && inputEl.id.match(/^(fmt|cert)\_/)) {
|
||||
inputEl.disabled = !attestation;
|
||||
}
|
||||
if (inputEl.id && inputEl.id.match(/^fmt\_/)) {
|
||||
inputEl.checked = attestation ? inputEl.id !== 'fmt_none' : inputEl.id === 'fmt_none';
|
||||
}
|
||||
if (inputEl.id && inputEl.id.match(/^cert\_/)) {
|
||||
inputEl.checked = attestation ? inputEl.id === 'cert_mds' : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* force https on load
|
||||
* @returns {undefined}
|
||||
*/
|
||||
window.onload = function() {
|
||||
if (!window.isSecureContext && location.protocol !== 'https:') {
|
||||
location.href = location.href.replace('http://', 'https://');
|
||||
}
|
||||
if (!document.getElementById('rpId').value) {
|
||||
document.getElementById('rpId').value = location.hostname;
|
||||
}
|
||||
|
||||
if (!document.getElementById('attestation_yes').checked) {
|
||||
setAttestation(false);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family:sans-serif;
|
||||
margin: 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
.splitter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.splitter > .form {
|
||||
flex: 1;
|
||||
min-width: 600px;
|
||||
}
|
||||
.splitter > .serverPreview {
|
||||
width: 740px;
|
||||
min-height: 700px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid grey;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.splitter > .serverPreview iframe {
|
||||
width: 700px;
|
||||
flex: 1;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="margin: 40px 10px 2px 0;">lbuchs/WebAuthn</h1>
|
||||
<div style="font-style: italic;">A simple PHP WebAuthn (FIDO2) server library.</div>
|
||||
<div class="splitter">
|
||||
<div class="form">
|
||||
<div> </div>
|
||||
<div> </div>
|
||||
<div>Simple working demo for the <a href="https://github.com/lbuchs/WebAuthn">lbuchs/WebAuthn</a> library.</div>
|
||||
<div>
|
||||
<div> </div>
|
||||
<table>
|
||||
<tbody><tr>
|
||||
<td>
|
||||
<button type="button" onclick="createRegistration()">➕ new registration</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" onclick="checkRegistration()">❔ check registration</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" onclick="clearRegistration()">␡ clear all registrations</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div> </div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="requireResidentKey" name="requireResidentKey">
|
||||
<label for="requireResidentKey">Use Client-side discoverable Credentials / Passkeys
|
||||
<i style="font-size: 0.8em;">Warning: most client-side modules and authenticators don't allow to delete single client side credentials.
|
||||
You have to reset the full device to remove the credentials again.</i>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">Relying Party</div>
|
||||
<p style="margin:0 0 5px 0;font-size:0.9em;font-style: italic;">A valid domain string that identifies the
|
||||
WebAuthn Relying Party<br/>on whose behalf a given registration or authentication ceremony is being performed.</p>
|
||||
<div>
|
||||
<label for="rpId">RP ID:</label>
|
||||
<input type="text" id="rpId" name="rpId" value="">
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">User</div>
|
||||
<div style="margin-bottom:12px">
|
||||
<label for="userId">User ID (Hex):</label>
|
||||
<input type="text" id="userId" name="userId" value="64656d6f64656d6f" required pattern="[0-9a-fA-F]{2,}">
|
||||
<i style="font-size: 0.8em;">You get the user ID back when checking registration (as userHandle), if you're using client-side discoverable credentials.
|
||||
You can identify with this ID the user who wants to login.
|
||||
A user handle is an opaque byte sequence with a maximum size of 64 bytes, and is not meant to be displayed to the user.
|
||||
The user handle MUST NOT contain personally identifying information about the user, such as a username or e-mail address.</i>
|
||||
</div>
|
||||
<div style="margin-bottom:12px">
|
||||
<label for="userName">User Name:</label>
|
||||
<input type="text" id="userName" name="userName" value="demo" required pattern="[0-9a-zA-Z]{2,}">
|
||||
<i style="font-size: 0.8em;">only for display, i.e., aiding the user in determining the difference between user accounts with similar display names.</i>
|
||||
</div>
|
||||
<div style="margin-bottom:6px">
|
||||
<label for="userDisplayName">User Display Name:</label>
|
||||
<input type="text" id="userDisplayName" name="userDisplayName" value="Demo Demolin" required>
|
||||
<i style="font-size: 0.8em;">A human-palatable name for the user account, intended only for display.</i>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">user verification</div>
|
||||
<div>
|
||||
<input type="radio" id="userVerification_required" name="userVerification">
|
||||
<label for="userVerification_required">required <i style="font-size: 0.8em;">User verification is required (e.g. by pin), the operation will fail if the response does not have the UV flag.</i></label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="radio" id="userVerification_preferred" name="userVerification">
|
||||
<label for="userVerification_preferred">preferred <i style="font-size: 0.8em;">user verification is prefered, the operation will not fail if the response does not have the UV flag.</i></label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="radio" id="userVerification_discouraged" name="userVerification" checked>
|
||||
<label for="userVerification_discouraged">discouraged <i style="font-size: 0.8em;">user verification should not be employed as to minimize the user interaction during the process.</i></label>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">type of authenticator</div>
|
||||
<div>
|
||||
<input type="checkbox" id="type_usb" name="type_usb" checked>
|
||||
<label for="type_usb">USB</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="type_nfc" name="type_nfc" checked>
|
||||
<label for="type_nfc">NFC</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="type_ble" name="type_ble" checked>
|
||||
<label for="type_ble">BLE</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="type_hybrid" name="type_hybrid" checked>
|
||||
<label for="type_hybrid">hybrid <i style="font-size: 0.8em;">Passkeys via mobile device, ...</i></label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="type_int" name="type_int" checked>
|
||||
<label for="type_int">internal <i style="font-size: 0.8em;">Passkeys on the device, Windows Hello, Android SafetyNet, Apple, ...</i></label>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">attestation</div>
|
||||
<div>
|
||||
<input type="radio" id="attestation_yes" value="yes" name="attestation" onchange="setAttestation(this.value === 'yes')" checked>
|
||||
<label for="attestation_yes">yes</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="attestation_no" value="no" name="attestation" onchange="setAttestation(this.value === 'yes')">
|
||||
<label for="attestation_no">no</label>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">attestation statement format</div>
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_android-key" name="fmt_android-key" checked>
|
||||
<label for="fmt_android-key">android-key</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_android-safetynet" name="fmt_android-safetynet" checked>
|
||||
<label for="fmt_android-safetynet">android-safetynet</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_apple" name="fmt_apple" checked>
|
||||
<label for="fmt_apple">apple</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_fido-u2f" name="fmt_fido-u2f" checked>
|
||||
<label for="fmt_fido-u2f">fido-u2f</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_none" name="fmt_none">
|
||||
<label for="fmt_none">none</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_packed" name="fmt_packed" checked>
|
||||
<label for="fmt_packed">packed</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="fmt_tpm" name="fmt_tpm" checked>
|
||||
<label for="fmt_tpm">tpm</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div> </div>
|
||||
<div style="font-weight: bold">attestation root certificates</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_mds" name="mds" checked>
|
||||
<label for="cert_mds">Accept keys signed by <a href="https://fidoalliance.org/metadata/" target="_blank">FIDO Alliance Metadata Service</a></label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_apple" name="apple">
|
||||
<label for="cert_apple">Accept keys signed by apple root ca</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_yubico" name="yubico">
|
||||
<label for="cert_yubico">Accept keys signed by yubico root ca</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_solo" name="solo">
|
||||
<label for="cert_solo">Accept keys signed by solokeys root ca</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_hypersecu" name="hypersecu">
|
||||
<label for="cert_hypersecu">Accept keys signed by hypersecu root ca</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_google" name="google">
|
||||
<label for="cert_google">Accept keys signed by google root ca</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="cert_microsoft" name="microsoft">
|
||||
<label for="cert_microsoft">Accept keys signed by Microsofts collection of trusted TPM root ca</label>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 0.7em">(Nothing checked = accept all)</div>
|
||||
<div> </div>
|
||||
|
||||
<div>
|
||||
<button type="button" onclick="queryFidoMetaDataService()">🗘 update root certificates from FIDO Alliance Metadata Service (MDS)</button>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
<div>If you select a root ca, direct attestation is required to validate your client with the root.<br>
|
||||
The browser may warn you that he will provide informations about your device.<br>
|
||||
When not checking against any root ca (deselect all certificates),
|
||||
the client may change the assertion from the authenticator (for instance, using an anonymization CA),<br>
|
||||
the browser may not warn about providing informations about your device.
|
||||
</div>
|
||||
<div style="margin-top:20px;font-size: 0.7em;font-style: italic">
|
||||
Copyright © 2023 Lukas Buchs - <a href="https://raw.githubusercontent.com/lbuchs/WebAuthn/master/LICENSE">license therms</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="serverPreview">
|
||||
<p style="margin-left:10px;font-weight: bold;">Here you can see what's saved on the server:</p>
|
||||
<iframe src="server.php?fn=getStoredDataHtml" id="serverPreview"></iframe>
|
||||
</div>
|
||||
<div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number:
|
||||
68:1d:01:6c:7a:3c:e3:02:25:a5:01:94:28:47:57:71
|
||||
|
||||
Signature Algorithm: ecdsa-with-SHA384
|
||||
|
||||
Issuer:
|
||||
stateOrProvinceName = California
|
||||
organizationName = Apple Inc.
|
||||
commonName = Apple WebAuthn Root CA
|
||||
|
||||
Validity
|
||||
Not Before: Mar 18 18:21:32 2020 GMT
|
||||
Not After : Mar 15 00:00:00 2045 GMT
|
||||
|
||||
Subject:
|
||||
stateOrProvinceName = California
|
||||
organizationName = Apple Inc.
|
||||
commonName = Apple WebAuthn Root CA
|
||||
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: id-ecPublicKey
|
||||
ASN1 OID: secp384r1
|
||||
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:TRUE
|
||||
X509v3 Subject Key Identifier:
|
||||
26:D7:64:D9:C5:78:C2:5A:67:D1:A7:DE:6B:12:D0:1B:63:F1:C6:D7
|
||||
X509v3 Key Usage: critical
|
||||
Certificate Sign, CRL Sign
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICEjCCAZmgAwIBAgIQaB0BbHo84wIlpQGUKEdXcTAKBggqhkjOPQQDAzBLMR8w
|
||||
HQYDVQQDDBZBcHBsZSBXZWJBdXRobiBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ
|
||||
bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTIwMDMxODE4MjEzMloXDTQ1MDMx
|
||||
NTAwMDAwMFowSzEfMB0GA1UEAwwWQXBwbGUgV2ViQXV0aG4gUm9vdCBDQTETMBEG
|
||||
A1UECgwKQXBwbGUgSW5jLjETMBEGA1UECAwKQ2FsaWZvcm5pYTB2MBAGByqGSM49
|
||||
AgEGBSuBBAAiA2IABCJCQ2pTVhzjl4Wo6IhHtMSAzO2cv+H9DQKev3//fG59G11k
|
||||
xu9eI0/7o6V5uShBpe1u6l6mS19S1FEh6yGljnZAJ+2GNP1mi/YK2kSXIuTHjxA/
|
||||
pcoRf7XkOtO4o1qlcaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUJtdk
|
||||
2cV4wlpn0afeaxLQG2PxxtcwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2cA
|
||||
MGQCMFrZ+9DsJ1PW9hfNdBywZDsWDbWFp28it1d/5w2RPkRX3Bbn/UbDTNLx7Jr3
|
||||
jAGGiQIwHFj+dJZYUJR786osByBelJYsVZd2GbHQu209b5RCmGQ21gpSAk9QZW4B
|
||||
1bWeT0vT
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,37 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number:
|
||||
04:00:00:00:00:01:0f:86:26:e6:0d
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign
|
||||
Validity
|
||||
Not Before: Dec 15 08:00:00 2006 GMT
|
||||
Not After : Dec 15 08:00:00 2021 GMT
|
||||
Subject: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
|
||||
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
|
||||
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
|
||||
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
|
||||
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
|
||||
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
|
||||
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
|
||||
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
|
||||
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
|
||||
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
|
||||
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
|
||||
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
|
||||
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
|
||||
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
|
||||
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
|
||||
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
|
||||
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
|
||||
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
|
||||
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,130 @@
|
||||
Google Hardware Attestation Root certificate
|
||||
----------------------------------------------
|
||||
|
||||
https://developer.android.com/training/articles/security-key-attestation.html
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number:
|
||||
e8:fa:19:63:14:d2:fa:18
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: serialNumber = f92009e853b6b045
|
||||
Validity
|
||||
Not Before: May 26 16:28:52 2016 GMT
|
||||
Not After : May 24 16:28:52 2026 GMT
|
||||
Subject: serialNumber = f92009e853b6b045
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (4096 bit)
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
36:61:E1:00:7C:88:05:09:51:8B:44:6C:47:FF:1A:4C:C9:EA:4F:12
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:36:61:E1:00:7C:88:05:09:51:8B:44:6C:47:FF:1A:4C:C9:EA:4F:12
|
||||
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:TRUE
|
||||
X509v3 Key Usage: critical
|
||||
Digital Signature, Certificate Sign, CRL Sign
|
||||
X509v3 CRL Distribution Points:
|
||||
|
||||
Full Name:
|
||||
URI:https://android.googleapis.com/attestation/crl/
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
|
||||
BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy
|
||||
ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
|
||||
Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
|
||||
tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
|
||||
nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
|
||||
C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
|
||||
oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
|
||||
JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
|
||||
sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
|
||||
igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
|
||||
RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
|
||||
aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
|
||||
AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD
|
||||
VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO
|
||||
BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk
|
||||
Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD
|
||||
ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB
|
||||
Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m
|
||||
qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY
|
||||
DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm
|
||||
QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u
|
||||
JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD
|
||||
CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy
|
||||
ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD
|
||||
qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic
|
||||
MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1
|
||||
wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 15352756130135856819 (0xd50ff25ba3f2d6b3)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer:
|
||||
serialNumber = f92009e853b6b045
|
||||
Validity
|
||||
Not Before: Nov 22 20:37:58 2019 GMT
|
||||
Not After : Nov 18 20:37:58 2034 GMT
|
||||
Subject:
|
||||
serialNumber = f92009e853b6b045
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (4096 bit)
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
36:61:E1:00:7C:88:05:09:51:8B:44:6C:47:FF:1A:4C:C9:EA:4F:12
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:36:61:E1:00:7C:88:05:09:51:8B:44:6C:47:FF:1A:4C:C9:EA:4F:12
|
||||
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:TRUE
|
||||
X509v3 Key Usage: critical
|
||||
Certificate Sign
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
|
||||
BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
|
||||
NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
|
||||
Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
|
||||
tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
|
||||
nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
|
||||
C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
|
||||
oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
|
||||
JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
|
||||
sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
|
||||
igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
|
||||
RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
|
||||
aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
|
||||
AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
|
||||
IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
|
||||
VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
|
||||
XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
|
||||
h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
|
||||
L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
|
||||
QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
|
||||
D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
|
||||
mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
|
||||
Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
|
||||
oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
|
||||
jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
|
||||
ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
|
||||
ex0SdDrx+tWUDqG8At2JHA==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,56 @@
|
||||
HyperFIDO U2F Security Key Attestation CA
|
||||
https://hypersecu.com/support/downloads/attestation
|
||||
|
||||
Last Update: 2017-01-01
|
||||
|
||||
HyperFIDO U2F Security Key devices which contain attestation certificates signed by a set of CAs.
|
||||
This file contains the CA certificates that Relying Parties (RP) need to configure their software
|
||||
with to be able to verify U2F device certificates.
|
||||
|
||||
The file will be updated as needed when we publish more CA certificates.
|
||||
|
||||
Issuer: CN=FT FIDO 0100
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjTCCATOgAwIBAgIBATAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxGVCBGSURP
|
||||
IDAxMDAwHhcNMTQwNzAxMTUzNjI2WhcNNDQwNzAzMTUzNjI2WjAXMRUwEwYDVQQD
|
||||
EwxGVCBGSURPIDAxMDAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASxdLxJx8ol
|
||||
S3DS5cIHzunPF0gg69d+o8ZVCMJtpRtlfBzGuVL4YhaXk2SC2gptPTgmpZCV2vbN
|
||||
fAPi5gOF0vbZo3AwbjAdBgNVHQ4EFgQUXt4jWlYDgwhaPU+EqLmeM9LoPRMwPwYD
|
||||
VR0jBDgwNoAUXt4jWlYDgwhaPU+EqLmeM9LoPROhG6QZMBcxFTATBgNVBAMTDEZU
|
||||
IEZJRE8gMDEwMIIBATAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQC2
|
||||
D9o9cconKTo8+4GZPyZBJ3amc8F0/kzyidX9dhrAIAIgM9ocs5BW/JfmshVP9Mb+
|
||||
Joa/kgX4dWbZxrk0ioTfJZg=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 4107 (0x100b)
|
||||
Signature Algorithm: ecdsa-with-SHA256
|
||||
Issuer:
|
||||
commonName = HYPERFIDO 0200
|
||||
organizationName = HYPERSECU
|
||||
countryName = CA
|
||||
Validity
|
||||
Not Before: Jan 1 00:00:00 2018 GMT
|
||||
Not After : Dec 31 23:59:59 2047 GMT
|
||||
Subject:
|
||||
commonName = HYPERFIDO 0200
|
||||
organizationName = HYPERSECU
|
||||
countryName = CA
|
||||
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBxzCCAWygAwIBAgICEAswCgYIKoZIzj0EAwIwOjELMAkGA1UEBhMCQ0ExEjAQ
|
||||
BgNVBAoMCUhZUEVSU0VDVTEXMBUGA1UEAwwOSFlQRVJGSURPIDAyMDAwIBcNMTgw
|
||||
MTAxMDAwMDAwWhgPMjA0NzEyMzEyMzU5NTlaMDoxCzAJBgNVBAYTAkNBMRIwEAYD
|
||||
VQQKDAlIWVBFUlNFQ1UxFzAVBgNVBAMMDkhZUEVSRklETyAwMjAwMFkwEwYHKoZI
|
||||
zj0CAQYIKoZIzj0DAQcDQgAErKUI1G0S7a6IOLlmHipLlBuxTYjsEESQvzQh3dB7
|
||||
dvxxWWm7kWL91rq6S7ayZG0gZPR+zYqdFzwAYDcG4+aX66NgMF4wHQYDVR0OBBYE
|
||||
FLZYcfMMwkQAGbt3ryzZFPFypmsIMB8GA1UdIwQYMBaAFLZYcfMMwkQAGbt3ryzZ
|
||||
FPFypmsIMAwGA1UdEwQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMC
|
||||
A0kAMEYCIQCG2/ppMGt7pkcRie5YIohS3uDPIrmiRcTjqDclKVWg0gIhANcPNDZH
|
||||
E2/zZ+uB5ThG9OZus+xSb4knkrbAyXKX2zm/
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
Solokeys FIDO2/U2F Device Attestation CA
|
||||
========================================
|
||||
Data:
|
||||
Version: 1 (0x0)
|
||||
Serial Number: 14143382635911888524 (0xc44763928ff4be8c)
|
||||
Signature Algorithm: ecdsa-with-SHA256
|
||||
|
||||
Issuer:
|
||||
emailAddress = hello@solokeys.com
|
||||
commonName = solokeys.com
|
||||
organizationalUnitName = Root CA
|
||||
organizationName = Solo Keys
|
||||
stateOrProvinceName = Maryland
|
||||
countryName = US
|
||||
|
||||
Validity
|
||||
Not Before: Nov 11 12:51:42 2018 GMT
|
||||
Not After : Oct 29 12:51:42 2068 GMT
|
||||
|
||||
Subject:
|
||||
emailAddress = hello@solokeys.com
|
||||
commonName = solokeys.com
|
||||
organizationalUnitName = Root CA
|
||||
organizationName = Solo Keys
|
||||
stateOrProvinceName = Maryland
|
||||
countryName = US
|
||||
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB9DCCAZoCCQDER2OSj/S+jDAKBggqhkjOPQQDAjCBgDELMAkGA1UEBhMCVVMx
|
||||
ETAPBgNVBAgMCE1hcnlsYW5kMRIwEAYDVQQKDAlTb2xvIEtleXMxEDAOBgNVBAsM
|
||||
B1Jvb3QgQ0ExFTATBgNVBAMMDHNvbG9rZXlzLmNvbTEhMB8GCSqGSIb3DQEJARYS
|
||||
aGVsbG9Ac29sb2tleXMuY29tMCAXDTE4MTExMTEyNTE0MloYDzIwNjgxMDI5MTI1
|
||||
MTQyWjCBgDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1hcnlsYW5kMRIwEAYDVQQK
|
||||
DAlTb2xvIEtleXMxEDAOBgNVBAsMB1Jvb3QgQ0ExFTATBgNVBAMMDHNvbG9rZXlz
|
||||
LmNvbTEhMB8GCSqGSIb3DQEJARYSaGVsbG9Ac29sb2tleXMuY29tMFkwEwYHKoZI
|
||||
zj0CAQYIKoZIzj0DAQcDQgAEWHAN0CCJVZdMs0oktZ5m93uxmB1iyq8ELRLtqVFL
|
||||
SOiHQEab56qRTB/QzrpGAY++Y2mw+vRuQMNhBiU0KzwjBjAKBggqhkjOPQQDAgNI
|
||||
ADBFAiEAz9SlrAXIlEu87vra54rICPs+4b0qhp3PdzcTg7rvnP0CIGjxzlteQQx+
|
||||
jQGd7rwSZuE5RWUPVygYhUstQO9zNUOs
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID6TCCAdGgAwIBAgIUUAjLh3ownidqbj5fboDmufVXiecwDQYJKoZIhvcNAQEL
|
||||
BQAwMjERMA8GA1UECgwIU29sb0tleXMxCzAJBgNVBAYTAkNIMRAwDgYDVQQDDAdS
|
||||
b290IFIxMCAXDTIxMDUyODA4MjQxMloYDzIwNzEwNTE2MDgyNDEyWjAtMREwDwYD
|
||||
VQQKDAhTb2xvS2V5czELMAkGA1UEBhMCQ0gxCzAJBgNVBAMMAkYxMFkwEwYHKoZI
|
||||
zj0CAQYIKoZIzj0DAQcDQgAEPCcSladE9kbZ0XI7jmtceNSHVrC1Rx0d3U8aMvKS
|
||||
CJimYSe7c0Jy7CZpw7TU6N6chNx6Q1jaZ/B3ZjPLGZBOMqOBxDCBwTAdBgNVHQ4E
|
||||
FgQUQWu2S++iGQ3kYl/9KQSWuYIptPgwHwYDVR0jBBgwFoAUVOPVaecSkRBulbOE
|
||||
QMfZOr8x1dcwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwMgYI
|
||||
KwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRwOi8vaS5zMnBraS5uZXQvcjEv
|
||||
MCcGA1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly9jLnMycGtpLm5ldC9yMS8wDQYJKoZI
|
||||
hvcNAQELBQADggIBACVIymBmQwWwIUKnffptCJPeAn4m+Vy7ntr6KeS6aM7NI3cb
|
||||
xzRq5tHOugtjFJsBSGynbF0/Kc+VnGR2lCFuVKeuusFsAhk4F13jaOTPSTWTXK6k
|
||||
2TdoqZ6wIqmQ7bAZVYqcE21ZkM/Bo5Ej+PZacGjlGaEHwjL5CU2scnZeqS8d1ago
|
||||
MCIfvRlYd2vkbPjqQx0t5jzEKZ7hF4y77kh0JArYpgpp0Sq4P96pPDwIZCvVGmGi
|
||||
jhNOie0UnF6trfTD1AAXtlPqYPK9gNpXlN2IhsIpNMf7YA9R1zjVvnfYnFS6Tr73
|
||||
0UzBct6jC9JompqvAo9NIe6cu/Qkc/KUL4JDt9iJWB8RN2aAnVCYQ+xT4evVFQCV
|
||||
F/1pbeSvFfPqCfazkSPIiff7n9Tmk1Wwe3VmkuU7HUmAkYaLazs7DLY/Cp0/1V1K
|
||||
pURMyawlUtv2J4PQqvOnMGUYupxp2l3DjdzHMx8RE+caCM2PxzPsUucLVHdOkBW2
|
||||
h6U5PIWJuZtbiabwFmQXahqSNkRO6kXRvedowqaHNZiIFi4VKqPHrJrEhNhncfJ/
|
||||
jwAAThoo5yyEgh3a+THoZKOFfZhzFJA9MVMwqQB00iSsF5HKC+MFUkHpKaV2DVjS
|
||||
mPXOYy4biL8XkOqQeJUuB5sQ/2LYxaaXj1dBrnR3cklHp2KKacWYdnLdEe8I
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVzCCAz+gAwIBAgIUR9xt5vBCCwFTaFGS3VFH0v49lmUwDQYJKoZIhvcNAQEL
|
||||
BQAwMjERMA8GA1UECgwIU29sb0tleXMxCzAJBgNVBAYTAkNIMRAwDgYDVQQDDAdS
|
||||
b290IFIxMCAXDTIxMDUyMTIyMTA1M1oYDzIwNzEwNTA5MjIxMDUzWjAyMREwDwYD
|
||||
VQQKDAhTb2xvS2V5czELMAkGA1UEBhMCQ0gxEDAOBgNVBAMMB1Jvb3QgUjEwggIi
|
||||
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCBybLOEMbhPyBEaHeqXJQUfRgd
|
||||
xZHFtum2oXknCineXh8NXBL8PJLJEYi4Clv/QQGj+VPGPOcF1nwMX5BsmTYsLPMo
|
||||
Rt7npI/xaPYdW/ByIvQQNqH3NTQjt8j4Wz0XWHL/VuqNFGZAdI4GN00NifKH1FCy
|
||||
wOgwyKHl74XmMJLzzigFoo8m5ZPD+uw7ZRt1Q28jFTJiB2qyMX1rn2wHZLnHHhgs
|
||||
wp0uApnQw5CGmhK3pkW8U9Gr3JH6K0q6NwZ4Pp1DPJFbl4J9cPFP2o25sOsf+jjX
|
||||
4Ko2J97qndoRxHFWjh6HLvbq6/RyySbVGBsBeep92QXidf/3Vgc+6xkgTmlWL+5W
|
||||
6mj7F8zhPAz8l8m3HEv/pVdsJikwrR2FAllw4T2nBYXhdJ4lPdH9HYZrbmyGVOcD
|
||||
1M/hQ2d/7X0H1K40KV1li1nYJHxkfcDKHeI5RcAaLE+n6ctLS5KqyJ0MXmmga6I7
|
||||
rd5cTfQqjYQm/YbfShK6ZsmsGC/KJGmFbx9kqsMbcg7a2qF1RT0WPFJd2F1glaon
|
||||
tsqPwFDi5mR7M46tjLAWJx2Wu5mbACedRoxKPMe8l+b1CQ7k+temvxNK0IvM38St
|
||||
z9UiVpl0VwACNpJ2MobM+XJNQNCbMbBt0n6wPmftqI5taoFxPTNM6t1F2utsjbdn
|
||||
M1/WUKOh5lhheBMDWQIDAQABo2MwYTAdBgNVHQ4EFgQUVOPVaecSkRBulbOEQMfZ
|
||||
Or8x1dcwHwYDVR0jBBgwFoAUVOPVaecSkRBulbOEQMfZOr8x1dcwDwYDVR0TAQH/
|
||||
BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAEh5VMjC
|
||||
YnWzJK8Rolmsbmpa7LQiS+WKk2ihYZRLUYlzET5+MDmue+n4sBuuuXPLaQsdnTSO
|
||||
xqmVts8bneacEckr0y8j5/2o5/5UPjAchXXK6PwHqnajk2kzCIH2tWTyhpKcxF1i
|
||||
fQo9RGA6igOjes8vMmKCFQg3A88wiAtrafgf/I3fVSHJOSeU29nqmVNHGU4tQFu0
|
||||
Frx/d3y5elo5rZpld71DwRA83qLUgJ3liancoSb/icR8igGyLxCcxmKU5fFItiG9
|
||||
4x+egnblyYfuhLbXU0k8LXJVMdDImZHEyI80EHOSpV3wZR5LyfiXu3fygxtOm6tL
|
||||
fi1khkJIfIPMyV1Y4B7y6zOlHbneF1F8P4pTjHTYFHKhNm1wWffrqgLDYdtLAXSV
|
||||
tyDM0zjtoRSHRYWsuwbeWdwoWHQeLkPzMtmFkHrPi0i3iv4GaianTnE1k1lX8Xf4
|
||||
HbWBHXoVJCoAwycJJqX2SPZwFlWGp8IMGLUNzjFtLP+D7pdUSBgkDqz6FumZQGx/
|
||||
KLHugnolEb8ZiOZKbaPOjc9EmXFjA42y4vXAip6ZZ2FfuWDqkPaU1WfM95cE0hf5
|
||||
BSbfxSSQ2V3z4sywzkfQxr7q2mYzJ9C2NhLYXMrc98L9uEd0O7dO0eEF+44gc5O7
|
||||
Bc7NuTA9//IG8nJb0MvD76TxHPlZuVnMdMb6
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,42 @@
|
||||
Yubico U2F Device Attestation CA
|
||||
================================
|
||||
|
||||
Last Update: 2014-09-01
|
||||
|
||||
Yubico manufacturer U2F devices that contains device attestation
|
||||
certificates signed by a set of Yubico CAs. This file contains the CA
|
||||
certificates that Relying Parties (RP) need to configure their
|
||||
software with to be able to verify U2F device certificates.
|
||||
|
||||
This file has been signed with OpenPGP and you should verify the
|
||||
signature and the authenticity of the public key before trusting the
|
||||
content. The signature is located next to the file:
|
||||
|
||||
https://developers.yubico.com/u2f/yubico-u2f-ca-certs.txt
|
||||
https://developers.yubico.com/u2f/yubico-u2f-ca-certs.txt.sig
|
||||
|
||||
We will update this file from time to time when we publish more CA
|
||||
certificates.
|
||||
|
||||
Name: Yubico U2F Root CA Serial 457200631
|
||||
Issued: 2014-08-01
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDHjCCAgagAwIBAgIEG0BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ
|
||||
dWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw
|
||||
MDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290
|
||||
IENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk
|
||||
5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep
|
||||
8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw
|
||||
nebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT
|
||||
9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw
|
||||
LvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ
|
||||
hjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN
|
||||
BgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4
|
||||
MYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt
|
||||
hX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k
|
||||
LVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U
|
||||
sG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc
|
||||
U9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Lukas Buchs
|
||||
* license https://github.com/lbuchs/WebAuthn/blob/master/LICENSE MIT
|
||||
*
|
||||
* Server test script for WebAuthn library. Saves new registrations in session.
|
||||
*
|
||||
* JAVASCRIPT | SERVER
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* REGISTRATION
|
||||
*
|
||||
* window.fetch -----------------> getCreateArgs
|
||||
* |
|
||||
* navigator.credentials.create <-------------'
|
||||
* |
|
||||
* '-------------------------> processCreate
|
||||
* |
|
||||
* alert ok or fail <----------------'
|
||||
*
|
||||
* ------------------------------------------------------------
|
||||
*
|
||||
* VALIDATION
|
||||
*
|
||||
* window.fetch ------------------> getGetArgs
|
||||
* |
|
||||
* navigator.credentials.get <----------------'
|
||||
* |
|
||||
* '-------------------------> processGet
|
||||
* |
|
||||
* alert ok or fail <----------------'
|
||||
*
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
require_once '../src/WebAuthn.php';
|
||||
try {
|
||||
session_start();
|
||||
|
||||
// read get argument and post body
|
||||
$fn = filter_input(INPUT_GET, 'fn');
|
||||
$requireResidentKey = !!filter_input(INPUT_GET, 'requireResidentKey');
|
||||
$userVerification = filter_input(INPUT_GET, 'userVerification', FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
|
||||
$userId = filter_input(INPUT_GET, 'userId', FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
$userName = filter_input(INPUT_GET, 'userName', FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
$userDisplayName = filter_input(INPUT_GET, 'userDisplayName', FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
|
||||
$userId = $userId ? preg_replace('/[^0-9a-f]/i', '', $userId): "";
|
||||
$userName = $userName ? preg_replace('/[^0-9a-z]/i', '', $userName): "";
|
||||
$userDisplayName = $userDisplayName ? preg_replace('/[^0-9a-z öüäéèàÖÜÄÉÈÀÂÊÎÔÛâêîôû]/i', '', $userDisplayName): "";
|
||||
|
||||
$post = trim(file_get_contents('php://input'));
|
||||
if ($post) {
|
||||
$post = json_decode($post, null, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
if ($fn !== 'getStoredDataHtml') {
|
||||
|
||||
// Formats
|
||||
$formats = [];
|
||||
if (filter_input(INPUT_GET, 'fmt_android-key')) {
|
||||
$formats[] = 'android-key';
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'fmt_android-safetynet')) {
|
||||
$formats[] = 'android-safetynet';
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'fmt_apple')) {
|
||||
$formats[] = 'apple';
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'fmt_fido-u2f')) {
|
||||
$formats[] = 'fido-u2f';
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'fmt_none')) {
|
||||
$formats[] = 'none';
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'fmt_packed')) {
|
||||
$formats[] = 'packed';
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'fmt_tpm')) {
|
||||
$formats[] = 'tpm';
|
||||
}
|
||||
|
||||
$rpId = 'localhost';
|
||||
if (filter_input(INPUT_GET, 'rpId')) {
|
||||
$rpId = filter_input(INPUT_GET, 'rpId', FILTER_VALIDATE_DOMAIN);
|
||||
if ($rpId === false) {
|
||||
throw new Exception('invalid relying party ID');
|
||||
}
|
||||
}
|
||||
|
||||
// types selected on front end
|
||||
$typeUsb = !!filter_input(INPUT_GET, 'type_usb');
|
||||
$typeNfc = !!filter_input(INPUT_GET, 'type_nfc');
|
||||
$typeBle = !!filter_input(INPUT_GET, 'type_ble');
|
||||
$typeInt = !!filter_input(INPUT_GET, 'type_int');
|
||||
$typeHyb = !!filter_input(INPUT_GET, 'type_hybrid');
|
||||
|
||||
// cross-platform: true, if type internal is not allowed
|
||||
// false, if only internal is allowed
|
||||
// null, if internal and cross-platform is allowed
|
||||
$crossPlatformAttachment = null;
|
||||
if (($typeUsb || $typeNfc || $typeBle || $typeHyb) && !$typeInt) {
|
||||
$crossPlatformAttachment = true;
|
||||
|
||||
} else if (!$typeUsb && !$typeNfc && !$typeBle && !$typeHyb && $typeInt) {
|
||||
$crossPlatformAttachment = false;
|
||||
}
|
||||
|
||||
|
||||
// new Instance of the server library.
|
||||
// make sure that $rpId is the domain name.
|
||||
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $rpId, $formats);
|
||||
|
||||
// add root certificates to validate new registrations
|
||||
if (filter_input(INPUT_GET, 'solo')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/solo.pem');
|
||||
$WebAuthn->addRootCertificates('rootCertificates/solokey_f1.pem');
|
||||
$WebAuthn->addRootCertificates('rootCertificates/solokey_r1.pem');
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'apple')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/apple.pem');
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'yubico')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/yubico.pem');
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'hypersecu')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/hypersecu.pem');
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'google')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/globalSign.pem');
|
||||
$WebAuthn->addRootCertificates('rootCertificates/googleHardware.pem');
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'microsoft')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/microsoftTpmCollection.pem');
|
||||
}
|
||||
if (filter_input(INPUT_GET, 'mds')) {
|
||||
$WebAuthn->addRootCertificates('rootCertificates/mds');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// request for create arguments
|
||||
// ------------------------------------
|
||||
|
||||
if ($fn === 'getCreateArgs') {
|
||||
$createArgs = $WebAuthn->getCreateArgs(\hex2bin($userId), $userName, $userDisplayName, 60*4, $requireResidentKey, $userVerification, $crossPlatformAttachment);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($createArgs));
|
||||
|
||||
// save challange to session. you have to deliver it to processGet later.
|
||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
|
||||
|
||||
|
||||
// ------------------------------------
|
||||
// request for get arguments
|
||||
// ------------------------------------
|
||||
|
||||
} else if ($fn === 'getGetArgs') {
|
||||
$ids = [];
|
||||
|
||||
if ($requireResidentKey) {
|
||||
if (!isset($_SESSION['registrations']) || !is_array($_SESSION['registrations']) || count($_SESSION['registrations']) === 0) {
|
||||
throw new Exception('we do not have any registrations in session to check the registration');
|
||||
}
|
||||
|
||||
} else {
|
||||
// load registrations from session stored there by processCreate.
|
||||
// normaly you have to load the credential Id's for a username
|
||||
// from the database.
|
||||
if (isset($_SESSION['registrations']) && is_array($_SESSION['registrations'])) {
|
||||
foreach ($_SESSION['registrations'] as $reg) {
|
||||
if ($reg->userId === $userId) {
|
||||
$ids[] = $reg->credentialId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($ids) === 0) {
|
||||
throw new Exception('no registrations in session for userId ' . $userId);
|
||||
}
|
||||
}
|
||||
|
||||
$getArgs = $WebAuthn->getGetArgs($ids, 60*4, $typeUsb, $typeNfc, $typeBle, $typeHyb, $typeInt, $userVerification);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($getArgs));
|
||||
|
||||
// save challange to session. you have to deliver it to processGet later.
|
||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
|
||||
|
||||
|
||||
// ------------------------------------
|
||||
// process create
|
||||
// ------------------------------------
|
||||
|
||||
} else if ($fn === 'processCreate') {
|
||||
$clientDataJSON = base64_decode($post->clientDataJSON);
|
||||
$attestationObject = base64_decode($post->attestationObject);
|
||||
$challenge = $_SESSION['challenge'];
|
||||
|
||||
// processCreate returns data to be stored for future logins.
|
||||
// in this example we store it in the php session.
|
||||
// Normaly you have to store the data in a database connected
|
||||
// with the user name.
|
||||
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $challenge, $userVerification === 'required', true, false);
|
||||
|
||||
// add user infos
|
||||
$data->userId = $userId;
|
||||
$data->userName = $userName;
|
||||
$data->userDisplayName = $userDisplayName;
|
||||
|
||||
if (!isset($_SESSION['registrations']) || !array_key_exists('registrations', $_SESSION) || !is_array($_SESSION['registrations'])) {
|
||||
$_SESSION['registrations'] = [];
|
||||
}
|
||||
$_SESSION['registrations'][] = $data;
|
||||
|
||||
$msg = 'registration success.';
|
||||
if ($data->rootValid === false) {
|
||||
$msg = 'registration ok, but certificate does not match any of the selected root ca.';
|
||||
}
|
||||
|
||||
$return = new stdClass();
|
||||
$return->success = true;
|
||||
$return->msg = $msg;
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($return));
|
||||
|
||||
|
||||
|
||||
// ------------------------------------
|
||||
// proccess get
|
||||
// ------------------------------------
|
||||
|
||||
} else if ($fn === 'processGet') {
|
||||
$clientDataJSON = base64_decode($post->clientDataJSON);
|
||||
$authenticatorData = base64_decode($post->authenticatorData);
|
||||
$signature = base64_decode($post->signature);
|
||||
$userHandle = base64_decode($post->userHandle);
|
||||
$id = base64_decode($post->id);
|
||||
$challenge = $_SESSION['challenge'] ?? '';
|
||||
$credentialPublicKey = null;
|
||||
|
||||
// looking up correspondending public key of the credential id
|
||||
// you should also validate that only ids of the given user name
|
||||
// are taken for the login.
|
||||
if (isset($_SESSION['registrations']) && is_array($_SESSION['registrations'])) {
|
||||
foreach ($_SESSION['registrations'] as $reg) {
|
||||
if ($reg->credentialId === $id) {
|
||||
$credentialPublicKey = $reg->credentialPublicKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($credentialPublicKey === null) {
|
||||
throw new Exception('Public Key for credential ID not found!');
|
||||
}
|
||||
|
||||
// if we have resident key, we have to verify that the userHandle is the provided userId at registration
|
||||
if ($requireResidentKey && $userHandle !== hex2bin($reg->userId)) {
|
||||
throw new \Exception('userId doesnt match (is ' . bin2hex($userHandle) . ' but expect ' . $reg->userId . ')');
|
||||
}
|
||||
|
||||
// process the get request. throws WebAuthnException if it fails
|
||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $credentialPublicKey, $challenge, null, $userVerification === 'required');
|
||||
|
||||
$return = new stdClass();
|
||||
$return->success = true;
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($return));
|
||||
|
||||
// ------------------------------------
|
||||
// proccess clear registrations
|
||||
// ------------------------------------
|
||||
|
||||
} else if ($fn === 'clearRegistrations') {
|
||||
$_SESSION['registrations'] = null;
|
||||
$_SESSION['challenge'] = null;
|
||||
|
||||
$return = new stdClass();
|
||||
$return->success = true;
|
||||
$return->msg = 'all registrations deleted';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($return));
|
||||
|
||||
// ------------------------------------
|
||||
// display stored data as HTML
|
||||
// ------------------------------------
|
||||
|
||||
} else if ($fn === 'getStoredDataHtml') {
|
||||
$html = '<!DOCTYPE html>' . "\n";
|
||||
$html .= '<html><head><style>tr:nth-child(even){background-color: #f2f2f2;}</style></head>';
|
||||
$html .= '<body style="font-family:sans-serif">';
|
||||
if (isset($_SESSION['registrations']) && is_array($_SESSION['registrations'])) {
|
||||
$html .= '<p>There are ' . count($_SESSION['registrations']) . ' registrations in this session:</p>';
|
||||
foreach ($_SESSION['registrations'] as $reg) {
|
||||
$html .= '<table style="border:1px solid black;margin:10px 0;">';
|
||||
foreach ($reg as $key => $value) {
|
||||
|
||||
if (is_bool($value)) {
|
||||
$value = $value ? 'yes' : 'no';
|
||||
|
||||
} else if (is_null($value)) {
|
||||
$value = 'null';
|
||||
|
||||
} else if (is_object($value)) {
|
||||
$value = chunk_split(strval($value), 64);
|
||||
|
||||
} else if (is_string($value) && strlen($value) > 0 && htmlspecialchars($value, ENT_QUOTES) === '') {
|
||||
$value = chunk_split(bin2hex($value), 64);
|
||||
}
|
||||
$html .= '<tr><td>' . htmlspecialchars($key) . '</td><td style="font-family:monospace;">' . nl2br(htmlspecialchars($value)) . '</td>';
|
||||
}
|
||||
$html .= '</table>';
|
||||
}
|
||||
} else {
|
||||
$html .= '<p>There are no registrations in this session.</p>';
|
||||
}
|
||||
$html .= '</body></html>';
|
||||
|
||||
header('Content-Type: text/html');
|
||||
print $html;
|
||||
|
||||
// ------------------------------------
|
||||
// get root certs from FIDO Alliance Metadata Service
|
||||
// ------------------------------------
|
||||
|
||||
} else if ($fn === 'queryFidoMetaDataService') {
|
||||
|
||||
$mdsFolder = 'rootCertificates/mds';
|
||||
$success = false;
|
||||
$msg = null;
|
||||
|
||||
// fetch only 1x / 24h
|
||||
$lastFetch = \is_file($mdsFolder . '/lastMdsFetch.txt') ? \strtotime(\file_get_contents($mdsFolder . '/lastMdsFetch.txt')) : 0;
|
||||
if ($lastFetch + (3600*48) < \time()) {
|
||||
$cnt = $WebAuthn->queryFidoMetaDataService($mdsFolder);
|
||||
$success = true;
|
||||
\file_put_contents($mdsFolder . '/lastMdsFetch.txt', date('r'));
|
||||
$msg = 'successfully queried FIDO Alliance Metadata Service - ' . $cnt . ' certificates downloaded.';
|
||||
|
||||
} else {
|
||||
$msg = 'Fail: last fetch was at ' . date('r', $lastFetch) . ' - fetch only 1x every 48h';
|
||||
}
|
||||
|
||||
$return = new stdClass();
|
||||
$return->success = $success;
|
||||
$return->msg = $msg;
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($return));
|
||||
}
|
||||
|
||||
} catch (Throwable $ex) {
|
||||
$return = new stdClass();
|
||||
$return->success = false;
|
||||
$return->msg = $ex->getMessage();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
print(json_encode($return));
|
||||
}
|
||||
Reference in New Issue
Block a user