PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.4.0
Defender Security – Malware Scanner, Login Security & Firewall v5.4.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / web-auth / webauthn-lib / src / PublicKeyCredentialLoader.php

PublicKeyCredentialLoader.php in Defender Security – Malware Scanner, Login Security & Firewall 5.4.0, at vendor/web-auth/webauthn-lib/src/PublicKeyCredentialLoader.php

182 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 /*
6 * The MIT License (MIT)
7 *
8 * Copyright (c) 2014-2021 Spomky-Labs
9 *
10 * This software may be modified and distributed under the terms
11 * of the MIT license. See the LICENSE file for details.
12 */
13
14 namespace Webauthn;
15
16 use function array_key_exists;
17 use Assert\Assertion;
18 use Base64Url\Base64Url;
19 use CBOR\Decoder;
20 use CBOR\MapObject;
21 use CBOR\OtherObject\OtherObjectManager;
22 use CBOR\Tag\TagObjectManager;
23 use InvalidArgumentException;
24 use function ord;
25 use Psr\Log\LoggerInterface;
26 use Psr\Log\NullLogger;
27 use Ramsey\Uuid\Uuid;
28 use function Safe\json_decode;
29 use function Safe\sprintf;
30 use function Safe\unpack;
31 use Throwable;
32 use Webauthn\AttestationStatement\AttestationObjectLoader;
33 use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientOutputsLoader;
34
35 class PublicKeyCredentialLoader
36 {
37 private const FLAG_AT = 0b01000000;
38 private const FLAG_ED = 0b10000000;
39
40 /**
41 * @var AttestationObjectLoader
42 */
43 private $attestationObjectLoader;
44
45 /**
46 * @var Decoder
47 */
48 private $decoder;
49
50 /**
51 * @var LoggerInterface
52 */
53 private $logger;
54
55 public function __construct(AttestationObjectLoader $attestationObjectLoader, ?LoggerInterface $logger = null)
56 {
57 if (null !== $logger) {
58 @trigger_error('The argument "logger" is deprecated since version 3.3 and will be removed in 4.0. Please use the method "setLogger".', E_USER_DEPRECATED);
59 }
60 $this->decoder = new Decoder(new TagObjectManager(), new OtherObjectManager());
61 $this->attestationObjectLoader = $attestationObjectLoader;
62 $this->logger = $logger ?? new NullLogger();
63 }
64
65 public static function create(AttestationObjectLoader $attestationObjectLoader): self
66 {
67 return new self($attestationObjectLoader);
68 }
69
70 public function setLogger(LoggerInterface $logger): self
71 {
72 $this->logger = $logger;
73
74 return $this;
75 }
76
77 /**
78 * @param mixed[] $json
79 */
80 public function loadArray(array $json): PublicKeyCredential
81 {
82 $this->logger->info('Trying to load data from an array', ['data' => $json]);
83 try {
84 foreach (['id', 'rawId', 'type'] as $key) {
85 Assertion::keyExists($json, $key, sprintf('The parameter "%s" is missing', $key));
86 Assertion::string($json[$key], sprintf('The parameter "%s" shall be a string', $key));
87 }
88 Assertion::keyExists($json, 'response', 'The parameter "response" is missing');
89 Assertion::isArray($json['response'], 'The parameter "response" shall be an array');
90 Assertion::eq($json['type'], 'public-key', sprintf('Unsupported type "%s"', $json['type']));
91
92 $id = Base64Url::decode($json['id']);
93 $rawId = Base64Url::decode($json['rawId']);
94 Assertion::true(hash_equals($id, $rawId));
95
96 $publicKeyCredential = new PublicKeyCredential(
97 $json['id'],
98 $json['type'],
99 $rawId,
100 $this->createResponse($json['response'])
101 );
102 $this->logger->info('The data has been loaded');
103 $this->logger->debug('Public Key Credential', ['publicKeyCredential' => $publicKeyCredential]);
104
105 return $publicKeyCredential;
106 } catch (Throwable $throwable) {
107 $this->logger->error('An error occurred', [
108 'exception' => $throwable,
109 ]);
110 throw $throwable;
111 }
112 }
113
114 public function load(string $data): PublicKeyCredential
115 {
116 $this->logger->info('Trying to load data from a string', ['data' => $data]);
117 try {
118 $json = json_decode($data, true);
119
120 return $this->loadArray($json);
121 } catch (Throwable $throwable) {
122 $this->logger->error('An error occurred', [
123 'exception' => $throwable,
124 ]);
125 throw $throwable;
126 }
127 }
128
129 /**
130 * @param mixed[] $response
131 */
132 private function createResponse(array $response): AuthenticatorResponse
133 {
134 Assertion::keyExists($response, 'clientDataJSON', 'Invalid data. The parameter "clientDataJSON" is missing');
135 Assertion::string($response['clientDataJSON'], 'Invalid data. The parameter "clientDataJSON" is invalid');
136 switch (true) {
137 case array_key_exists('attestationObject', $response):
138 Assertion::string($response['attestationObject'], 'Invalid data. The parameter "attestationObject " is invalid');
139 $attestationObject = $this->attestationObjectLoader->load($response['attestationObject']);
140
141 return new AuthenticatorAttestationResponse(CollectedClientData::createFormJson($response['clientDataJSON']), $attestationObject);
142 case array_key_exists('authenticatorData', $response) && array_key_exists('signature', $response):
143 $authData = Base64Url::decode($response['authenticatorData']);
144
145 $authDataStream = new StringStream($authData);
146 $rp_id_hash = $authDataStream->read(32);
147 $flags = $authDataStream->read(1);
148 $signCount = $authDataStream->read(4);
149 $signCount = unpack('N', $signCount)[1];
150
151 $attestedCredentialData = null;
152 if (0 !== (ord($flags) & self::FLAG_AT)) {
153 $aaguid = Uuid::fromBytes($authDataStream->read(16));
154 $credentialLength = $authDataStream->read(2);
155 $credentialLength = unpack('n', $credentialLength)[1];
156 $credentialId = $authDataStream->read($credentialLength);
157 $credentialPublicKey = $this->decoder->decode($authDataStream);
158 Assertion::isInstanceOf($credentialPublicKey, MapObject::class, 'The data does not contain a valid credential public key.');
159 $attestedCredentialData = new AttestedCredentialData($aaguid, $credentialId, (string) $credentialPublicKey);
160 }
161
162 $extension = null;
163 if (0 !== (ord($flags) & self::FLAG_ED)) {
164 $extension = $this->decoder->decode($authDataStream);
165 $extension = AuthenticationExtensionsClientOutputsLoader::load($extension);
166 }
167 Assertion::true($authDataStream->isEOF(), 'Invalid authentication data. Presence of extra bytes.');
168 $authDataStream->close();
169 $authenticatorData = new AuthenticatorData($authData, $rp_id_hash, $flags, $signCount, $attestedCredentialData, $extension);
170
171 return new AuthenticatorAssertionResponse(
172 CollectedClientData::createFormJson($response['clientDataJSON']),
173 $authenticatorData,
174 Base64Url::decode($response['signature']),
175 $response['userHandle'] ?? null
176 );
177 default:
178 throw new InvalidArgumentException('Unable to create the response object');
179 }
180 }
181 }
182