1 <?php 2 3 namespace lbuchs\WebAuthn\Attestation; 4 use lbuchs\WebAuthn\WebAuthnException; 5 use lbuchs\WebAuthn\CBOR\CborDecoder; 6 use lbuchs\WebAuthn\Binary\ByteBuffer; 7 8 /** 9 * @author Lukas Buchs 10 * @license https://github.com/lbuchs/WebAuthn/blob/master/LICENSE MIT 11 */ 12 class AttestationObject { 13 private $_authenticatorData; 14 private $_attestationFormat; 15 private $_attestationFormatName; 16 17 public function __construct($binary , $allowedFormats) { 18 $enc = CborDecoder::decode($binary); 19 // validation 20 if (!\is_array($enc) || !\array_key_exists('fmt', $enc) || !is_string($enc['fmt'])) { 21 throw new WebAuthnException('invalid attestation format', WebAuthnException::INVALID_DATA); 22 } 23 24 if (!\array_key_exists('attStmt', $enc) || !\is_array($enc['attStmt'])) { 25 throw new WebAuthnException('invalid attestation format (attStmt not available)', WebAuthnException::INVALID_DATA); 26 } 27 28 if (!\array_key_exists('authData', $enc) || !\is_object($enc['authData']) || !($enc['authData'] instanceof ByteBuffer)) { 29 throw new WebAuthnException('invalid attestation format (authData not available)', WebAuthnException::INVALID_DATA); 30 } 31 32 $this->_authenticatorData = new AuthenticatorData($enc['authData']->getBinaryString()); 33 $this->_attestationFormatName = $enc['fmt']; 34 35 // Format ok? 36 if (!in_array($this->_attestationFormatName, $allowedFormats)) { 37 throw new WebAuthnException('invalid atttestation format: ' . $this->_attestationFormatName, WebAuthnException::INVALID_DATA); 38 } 39 40 41 switch ($this->_attestationFormatName) { 42 case 'android-key': $this->_attestationFormat = new Format\AndroidKey($enc, $this->_authenticatorData); break; 43 case 'android-safetynet': $this->_attestationFormat = new Format\AndroidSafetyNet($enc, $this->_authenticatorData); break; 44 case 'apple': $this->_attestationFormat = new Format\Apple($enc, $this->_authenticatorData); break; 45 case 'fido-u2f': $this->_attestationFormat = new Format\U2f($enc, $this->_authenticatorData); break; 46 case 'none': $this->_attestationFormat = new Format\None($enc, $this->_authenticatorData); break; 47 case 'packed': $this->_attestationFormat = new Format\Packed($enc, $this->_authenticatorData); break; 48 case 'tpm': $this->_attestationFormat = new Format\Tpm($enc, $this->_authenticatorData); break; 49 default: throw new WebAuthnException('invalid attestation format: ' . $enc['fmt'], WebAuthnException::INVALID_DATA); 50 } 51 } 52 53 /** 54 * returns the attestation format name 55 * @return string 56 */ 57 public function getAttestationFormatName() { 58 return $this->_attestationFormatName; 59 } 60 61 /** 62 * returns the attestation format class 63 * @return Format\FormatBase 64 */ 65 public function getAttestationFormat() { 66 return $this->_attestationFormat; 67 } 68 69 /** 70 * returns the attestation public key in PEM format 71 * @return AuthenticatorData 72 */ 73 public function getAuthenticatorData() { 74 return $this->_authenticatorData; 75 } 76 77 /** 78 * returns the certificate chain as PEM 79 * @return string|null 80 */ 81 public function getCertificateChain() { 82 return $this->_attestationFormat->getCertificateChain(); 83 } 84 85 /** 86 * return the certificate issuer as string 87 * @return string 88 */ 89 public function getCertificateIssuer() { 90 $pem = $this->getCertificatePem(); 91 $issuer = ''; 92 if ($pem) { 93 $certInfo = \openssl_x509_parse($pem); 94 if (\is_array($certInfo) && \array_key_exists('issuer', $certInfo) && \is_array($certInfo['issuer'])) { 95 96 $cn = $certInfo['issuer']['CN'] ?? ''; 97 $o = $certInfo['issuer']['O'] ?? ''; 98 $ou = $certInfo['issuer']['OU'] ?? ''; 99 100 if ($cn) { 101 $issuer .= $cn; 102 } 103 if ($issuer && ($o || $ou)) { 104 $issuer .= ' (' . trim($o . ' ' . $ou) . ')'; 105 } else { 106 $issuer .= trim($o . ' ' . $ou); 107 } 108 } 109 } 110 111 return $issuer; 112 } 113 114 /** 115 * return the certificate subject as string 116 * @return string 117 */ 118 public function getCertificateSubject() { 119 $pem = $this->getCertificatePem(); 120 $subject = ''; 121 if ($pem) { 122 $certInfo = \openssl_x509_parse($pem); 123 if (\is_array($certInfo) && \array_key_exists('subject', $certInfo) && \is_array($certInfo['subject'])) { 124 125 $cn = $certInfo['subject']['CN'] ?? ''; 126 $o = $certInfo['subject']['O'] ?? ''; 127 $ou = $certInfo['subject']['OU'] ?? ''; 128 129 if ($cn) { 130 $subject .= $cn; 131 } 132 if ($subject && ($o || $ou)) { 133 $subject .= ' (' . trim($o . ' ' . $ou) . ')'; 134 } else { 135 $subject .= trim($o . ' ' . $ou); 136 } 137 } 138 } 139 140 return $subject; 141 } 142 143 /** 144 * returns the key certificate in PEM format 145 * @return string 146 */ 147 public function getCertificatePem() { 148 return $this->_attestationFormat->getCertificatePem(); 149 } 150 151 /** 152 * checks validity of the signature 153 * @param string $clientDataHash 154 * @return bool 155 * @throws WebAuthnException 156 */ 157 public function validateAttestation($clientDataHash) { 158 return $this->_attestationFormat->validateAttestation($clientDataHash); 159 } 160 161 /** 162 * validates the certificate against root certificates 163 * @param array $rootCas 164 * @return boolean 165 * @throws WebAuthnException 166 */ 167 public function validateRootCertificate($rootCas) { 168 return $this->_attestationFormat->validateRootCertificate($rootCas); 169 } 170 171 /** 172 * checks if the RpId-Hash is valid 173 * @param string$rpIdHash 174 * @return bool 175 */ 176 public function validateRpIdHash($rpIdHash) { 177 return $rpIdHash === $this->_authenticatorData->getRpIdHash(); 178 } 179 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body