1 <?php 2 3 4 namespace lbuchs\WebAuthn\Attestation\Format; 5 use lbuchs\WebAuthn\Attestation\AuthenticatorData; 6 use lbuchs\WebAuthn\WebAuthnException; 7 use lbuchs\WebAuthn\Binary\ByteBuffer; 8 9 class Packed extends FormatBase { 10 private $_alg; 11 private $_signature; 12 private $_x5c; 13 14 public function __construct($AttestionObject, AuthenticatorData $authenticatorData) { 15 parent::__construct($AttestionObject, $authenticatorData); 16 17 // check packed data 18 $attStmt = $this->_attestationObject['attStmt']; 19 20 if (!\array_key_exists('alg', $attStmt) || $this->_getCoseAlgorithm($attStmt['alg']) === null) { 21 throw new WebAuthnException('unsupported alg: ' . $attStmt['alg'], WebAuthnException::INVALID_DATA); 22 } 23 24 if (!\array_key_exists('sig', $attStmt) || !\is_object($attStmt['sig']) || !($attStmt['sig'] instanceof ByteBuffer)) { 25 throw new WebAuthnException('no signature found', WebAuthnException::INVALID_DATA); 26 } 27 28 $this->_alg = $attStmt['alg']; 29 $this->_signature = $attStmt['sig']->getBinaryString(); 30 31 // certificate for validation 32 if (\array_key_exists('x5c', $attStmt) && \is_array($attStmt['x5c']) && \count($attStmt['x5c']) > 0) { 33 34 // The attestation certificate attestnCert MUST be the first element in the array 35 $attestnCert = array_shift($attStmt['x5c']); 36 37 if (!($attestnCert instanceof ByteBuffer)) { 38 throw new WebAuthnException('invalid x5c certificate', WebAuthnException::INVALID_DATA); 39 } 40 41 $this->_x5c = $attestnCert->getBinaryString(); 42 43 // certificate chain 44 foreach ($attStmt['x5c'] as $chain) { 45 if ($chain instanceof ByteBuffer) { 46 $this->_x5c_chain[] = $chain->getBinaryString(); 47 } 48 } 49 } 50 } 51 52 53 /* 54 * returns the key certificate in PEM format 55 * @return string|null 56 */ 57 public function getCertificatePem() { 58 if (!$this->_x5c) { 59 return null; 60 } 61 return $this->_createCertificatePem($this->_x5c); 62 } 63 64 /** 65 * @param string $clientDataHash 66 */ 67 public function validateAttestation($clientDataHash) { 68 if ($this->_x5c) { 69 return $this->_validateOverX5c($clientDataHash); 70 } else { 71 return $this->_validateSelfAttestation($clientDataHash); 72 } 73 } 74 75 /** 76 * validates the certificate against root certificates 77 * @param array $rootCas 78 * @return boolean 79 * @throws WebAuthnException 80 */ 81 public function validateRootCertificate($rootCas) { 82 if (!$this->_x5c) { 83 return false; 84 } 85 86 $chainC = $this->_createX5cChainFile(); 87 if ($chainC) { 88 $rootCas[] = $chainC; 89 } 90 91 $v = \openssl_x509_checkpurpose($this->getCertificatePem(), -1, $rootCas); 92 if ($v === -1) { 93 throw new WebAuthnException('error on validating root certificate: ' . \openssl_error_string(), WebAuthnException::CERTIFICATE_NOT_TRUSTED); 94 } 95 return $v; 96 } 97 98 /** 99 * validate if x5c is present 100 * @param string $clientDataHash 101 * @return bool 102 * @throws WebAuthnException 103 */ 104 protected function _validateOverX5c($clientDataHash) { 105 $publicKey = \openssl_pkey_get_public($this->getCertificatePem()); 106 107 if ($publicKey === false) { 108 throw new WebAuthnException('invalid public key: ' . \openssl_error_string(), WebAuthnException::INVALID_PUBLIC_KEY); 109 } 110 111 // Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash 112 // using the attestation public key in attestnCert with the algorithm specified in alg. 113 $dataToVerify = $this->_authenticatorData->getBinary(); 114 $dataToVerify .= $clientDataHash; 115 116 $coseAlgorithm = $this->_getCoseAlgorithm($this->_alg); 117 118 // check certificate 119 return \openssl_verify($dataToVerify, $this->_signature, $publicKey, $coseAlgorithm->openssl) === 1; 120 } 121 122 /** 123 * validate if self attestation is in use 124 * @param string $clientDataHash 125 * @return bool 126 */ 127 protected function _validateSelfAttestation($clientDataHash) { 128 // Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash 129 // using the credential public key with alg. 130 $dataToVerify = $this->_authenticatorData->getBinary(); 131 $dataToVerify .= $clientDataHash; 132 133 $publicKey = $this->_authenticatorData->getPublicKeyPem(); 134 135 // check certificate 136 return \openssl_verify($dataToVerify, $this->_signature, $publicKey, OPENSSL_ALGO_SHA256) === 1; 137 } 138 } 139
title
Description
Body
title
Description
Body
title
Description
Body
title
Body