Commit a0d0fa41 authored by Lukas Buchs's avatar Lukas Buchs
Browse files

fixes & improvements

requireUserVerification may be a string
added more informations to the response class of processCreate()
No related merge requests found
Showing with 102 additions and 11 deletions
+102 -11
......@@ -53,6 +53,66 @@ class AttestationObject {
return $this->_authenticatorData;
}
/**
* returns the certificate chain as PEM
* @return string|null
*/
public function getCertificateChain() {
return $this->_attestationFormat->getCertificateChain();
}
/**
* return the certificate issuer as string
* @return string
*/
public function getCertificateIssuer() {
$pem = $this->getCertificatePem();
$issuer = '';
if ($pem) {
$certInfo = \openssl_x509_parse($pem);
if (\is_array($certInfo) && \is_array($certInfo['issuer'])) {
if ($certInfo['issuer']['CN']) {
$issuer .= \trim($certInfo['issuer']['CN']);
}
if ($certInfo['issuer']['O'] || $certInfo['issuer']['OU']) {
if ($issuer) {
$issuer .= ' (' . \trim($certInfo['issuer']['O'] . ' ' . $certInfo['issuer']['OU']) . ')';
} else {
$issuer .= \trim($certInfo['issuer']['O'] . ' ' . $certInfo['issuer']['OU']);
}
}
}
}
return $issuer;
}
/**
* return the certificate subject as string
* @return string
*/
public function getCertificateSubject() {
$pem = $this->getCertificatePem();
$subject = '';
if ($pem) {
$certInfo = \openssl_x509_parse($pem);
if (\is_array($certInfo) && \is_array($certInfo['subject'])) {
if ($certInfo['subject']['CN']) {
$subject .= \trim($certInfo['subject']['CN']);
}
if ($certInfo['subject']['O'] || $certInfo['subject']['OU']) {
if ($subject) {
$subject .= ' (' . \trim($certInfo['subject']['O'] . ' ' . $certInfo['subject']['OU']) . ')';
} else {
$subject .= \trim($certInfo['subject']['O'] . ' ' . $certInfo['subject']['OU']);
}
}
}
}
return $subject;
}
/**
* returns the key certificate in PEM format
* @return string
......
......@@ -69,6 +69,8 @@ class AuthenticatorData {
* Authenticator Attestation Globally Unique Identifier, a unique number
* that identifies the model of the authenticator (not the specific instance
* of the authenticator)
* The aaguid may be 0 if the user is using a old u2f device and/or if
* the browser is using the fido-u2f format.
* @return string
* @throws WebAuthnException
*/
......@@ -203,15 +205,15 @@ class AuthenticatorData {
*/
private function _readAttestData($binary, &$endOffset) {
$attestedCData = new \stdClass();
if (strlen($binary) <= 55) {
if (\strlen($binary) <= 55) {
throw new WebAuthnException('Attested data should be present but is missing', WebAuthnException::INVALID_DATA);
}
// The AAGUID of the authenticator
$attestedCData->aaguid = \substr($binary,37, 16);
$attestedCData->aaguid = \substr($binary, 37, 16);
//Byte length L of Credential ID, 16-bit unsigned big-endian integer.
$length = unpack('nlength', \substr($binary, 53, 2))['length'];
$length = \unpack('nlength', \substr($binary, 53, 2))['length'];
$attestedCData->credentialId = \substr($binary, 55, $length);
// set end offset
......@@ -274,7 +276,7 @@ class AuthenticatorData {
*/
private function _readExtensionData($binary) {
$ext = CborDecoder::decode($binary);
if (!is_array($ext)) {
if (!\is_array($ext)) {
throw new WebAuthnException('invalid extension data', WebAuthnException::INVALID_DATA);
}
......
......@@ -22,7 +22,7 @@ abstract class FormatBase {
}
/**
*
*
*/
public function __destruct() {
// delete X.509 chain certificate file after use
......@@ -31,7 +31,18 @@ abstract class FormatBase {
}
}
/**
/**
* returns the certificate chain in PEM format
* @return string|null
*/
public function getCertificateChain() {
if (\is_file($this->_x5c_tempFile)) {
return \file_get_contents($this->_x5c_tempFile);
}
return null;
}
/**
* returns the key X.509 certificate in PEM format
* @return string
*/
......
......@@ -97,7 +97,12 @@ class WebAuthn {
* @param string $userDisplayName
* @param int $timeout timeout in seconds
* @param bool $requireResidentKey true, if the key should be stored by the authentication device
* @param bool $requireUserVerification indicates that you require user verificationand will fail the operation if the response does not have the UV flag set.
* @param bool|string $requireUserVerification indicates that you require user verification and will fail the operation
* if the response does not have the UV flag set.
* Valid values:
* true = required
* false = preferred
* string 'required' 'preferred' 'discouraged'
* @param array $excludeCredentialIds a array of ids, which are already registered, to prevent re-registration
* @return \stdClass
*/
......@@ -110,8 +115,17 @@ class WebAuthn {
$args->publicKey->rp->name = $this->_rpName;
$args->publicKey->rp->id = $this->_rpId;
// validate User Verification Requirement
if (\is_bool($requireUserVerification)) {
$requireUserVerification = $requireUserVerification ? 'required' : 'preferred';
} else if (\is_string($requireUserVerification) && \in_array(\strtolower($requireUserVerification), ['required', 'preferred', 'discouraged'])) {
$requireUserVerification = \strtolower($requireUserVerification);
} else {
$requireUserVerification = 'preferred';
}
$args->publicKey->authenticatorSelection = new \stdClass();
$args->publicKey->authenticatorSelection->userVerification = $requireUserVerification ? 'required' : 'preferred';
$args->publicKey->authenticatorSelection->userVerification = $requireUserVerification;
if ($requireResidentKey) {
$args->publicKey->authenticatorSelection->requireResidentKey = true;
}
......@@ -128,7 +142,7 @@ class WebAuthn {
$tmp->alg = -7; // ES256
$args->publicKey->pubKeyCredParams[] = $tmp;
$args->publicKey->attestation = count($this->_formats) === 1 && in_array('none', $this->_formats) ? 'none' : 'direct';
$args->publicKey->attestation = \count($this->_formats) === 1 && \in_array('none', $this->_formats) ? 'none' : 'direct';
$args->publicKey->extensions = new \stdClass();
$args->publicKey->extensions->exts = true;
$args->publicKey->timeout = $timeout * 1000; // microseconds
......@@ -168,7 +182,7 @@ class WebAuthn {
$args->publicKey->timeout = $timeout * 1000; // microseconds
$args->publicKey->challenge = $this->_createChallenge(); // binary
if (is_array($credentialIds) && count($credentialIds) > 0) {
if (\is_array($credentialIds) && \count($credentialIds) > 0) {
$args->publicKey->allowCredentials = array();
foreach ($credentialIds as $id) {
......@@ -204,7 +218,7 @@ class WebAuthn {
* @return ?int
*/
public function getSignatureCounter() {
return is_int($this->_signatureCounter) ? $this->_signatureCounter : null;
return \is_int($this->_signatureCounter) ? $this->_signatureCounter : null;
}
/**
......@@ -280,9 +294,13 @@ class WebAuthn {
// prepare data to store for future logins
$data = new \stdClass();
$data->rpId = $this->_rpId;
$data->credentialId = $attestationObject->getAuthenticatorData()->getCredentialId();
$data->credentialPublicKey = $attestationObject->getAuthenticatorData()->getPublicKeyPem();
$data->certificateChain = $attestationObject->getCertificateChain();
$data->certificate = $attestationObject->getCertificatePem();
$data->certificateIssuer = $attestationObject->getCertificateIssuer();
$data->certificateSubject = $attestationObject->getCertificateSubject();
$data->signatureCounter = $this->_signatureCounter;
$data->AAGUID = $attestationObject->getAuthenticatorData()->getAAGUID();
return $data;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment