Skip to content
GitLab
Explore
Projects
Groups
Topics
Snippets
Projects
Groups
Topics
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
fusiondirectory
WebAuthn
Commits
e281e2fb
Commit
e281e2fb
authored
4 years ago
by
Lukas Buchs
Browse files
Options
Download
Patches
Plain Diff
TPM
untested
parent
ce78983b
master
v2.0.0-beta
v1.1.3
v1.1.2
v1.1.1
v1.1.0
v1.0.0
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
Attestation/AttestationObject.php
+4
-3
Attestation/AttestationObject.php
Attestation/Format/Packed.php
+0
-1
Attestation/Format/Packed.php
Attestation/Format/Tpm.php
+173
-0
Attestation/Format/Tpm.php
WebAuthn.php
+1
-0
WebAuthn.php
with
178 additions
and
4 deletions
+178
-4
Attestation/AttestationObject.php
+
4
−
3
View file @
e281e2fb
...
@@ -36,12 +36,13 @@ class AttestationObject {
...
@@ -36,12 +36,13 @@ class AttestationObject {
}
}
switch
(
$enc
[
'fmt'
])
{
switch
(
$enc
[
'fmt'
])
{
case
'android-key'
:
$this
->
_attestationFormat
=
new
Format\AndroidKey
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'android-safetynet'
:
$this
->
_attestationFormat
=
new
Format\AndroidSafetyNet
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'fido-u2f'
:
$this
->
_attestationFormat
=
new
Format\U2f
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'fido-u2f'
:
$this
->
_attestationFormat
=
new
Format\U2f
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'none'
:
$this
->
_attestationFormat
=
new
Format\None
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'none'
:
$this
->
_attestationFormat
=
new
Format\None
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'packed'
:
$this
->
_attestationFormat
=
new
Format\Packed
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'packed'
:
$this
->
_attestationFormat
=
new
Format\Packed
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'android-key'
:
$this
->
_attestationFormat
=
new
Format\AndroidKey
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'tpm'
:
$this
->
_attestationFormat
=
new
Format\Tpm
(
$enc
,
$this
->
_authenticatorData
);
break
;
case
'android-safetynet'
:
$this
->
_attestationFormat
=
new
Format\AndroidSafetyNet
(
$enc
,
$this
->
_authenticatorData
);
break
;
default
:
throw
new
WebAuthnException
(
'invalid attestation format: '
.
$enc
[
'fmt'
],
WebAuthnException
::
INVALID_DATA
);
default
:
throw
new
WebAuthnException
(
'invalid atttestation format: '
.
$enc
[
'fmt'
],
WebAuthnException
::
INVALID_DATA
);
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
Attestation/Format/Packed.php
+
0
−
1
View file @
e281e2fb
...
@@ -47,7 +47,6 @@ class Packed extends FormatBase {
...
@@ -47,7 +47,6 @@ class Packed extends FormatBase {
$this
->
_x5c_chain
[]
=
$chain
->
getBinaryString
();
$this
->
_x5c_chain
[]
=
$chain
->
getBinaryString
();
}
}
}
}
unset
(
$i
);
}
}
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
Attestation/Format/Tpm.php
0 → 100644
+
173
−
0
View file @
e281e2fb
<?php
namespace
WebAuthn\Attestation\Format
;
use
WebAuthn\WebAuthnException
;
use
WebAuthn\Binary\ByteBuffer
;
class
Tpm
extends
FormatBase
{
private
static
$_ES256
=
-
7
;
// ES256
private
static
$_RS256
=
-
257
;
// RS256
private
$_TPM_GENERATED_VALUE
=
"
\xFF\x54\x43\x47
"
;
private
$_TPM_ST_ATTEST_CERTIFY
=
"
\x80\x17
"
;
/**
* @var ByteBuffer
*/
private
$_certInfo
;
private
$_signature
;
private
$_pubArea
;
private
$_x5c
;
public
function
__construct
(
$AttestionObject
,
\
WebAuthn\Attestation\AuthenticatorData
$authenticatorData
)
{
parent
::
__construct
(
$AttestionObject
,
$authenticatorData
);
// check packed data
$attStmt
=
$this
->
_attestationObject
[
'attStmt'
];
if
(
!
\
array_key_exists
(
'ver'
,
$attStmt
)
||
$attStmt
[
'ver'
]
!==
'2.0'
)
{
throw
new
WebAuthnException
(
'invalid tpm version: '
.
$attStmt
[
'ver'
],
WebAuthnException
::
INVALID_DATA
);
}
if
(
!
\
array_key_exists
(
'alg'
,
$attStmt
)
||
(
$attStmt
[
'alg'
]
!==
self
::
$_ES256
&&
$attStmt
[
'alg'
]
!==
self
::
$_RS256
))
{
// SHA256
throw
new
WebAuthnException
(
'only ES256/RS256 acceptable but got: '
.
$attStmt
[
'alg'
],
WebAuthnException
::
INVALID_DATA
);
}
if
(
!
\
array_key_exists
(
'sig'
,
$attStmt
)
||
!
\
is_object
(
$attStmt
[
'sig'
])
||
!
(
$attStmt
[
'sig'
]
instanceof
ByteBuffer
))
{
throw
new
WebAuthnException
(
'signature not found'
,
WebAuthnException
::
INVALID_DATA
);
}
if
(
!
\
array_key_exists
(
'certInfo'
,
$attStmt
)
||
!
\
is_object
(
$attStmt
[
'certInfo'
])
||
!
(
$attStmt
[
'certInfo'
]
instanceof
ByteBuffer
))
{
throw
new
WebAuthnException
(
'certInfo not found'
,
WebAuthnException
::
INVALID_DATA
);
}
if
(
!
\
array_key_exists
(
'pubArea'
,
$attStmt
)
||
!
\
is_object
(
$attStmt
[
'pubArea'
])
||
!
(
$attStmt
[
'pubArea'
]
instanceof
ByteBuffer
))
{
throw
new
WebAuthnException
(
'pubArea not found'
,
WebAuthnException
::
INVALID_DATA
);
}
$this
->
_signature
=
$attStmt
[
'sig'
]
->
getBinaryString
();
$this
->
_certInfo
=
$attStmt
[
'certInfo'
];
$this
->
_pubArea
=
$attStmt
[
'pubArea'
];
// certificate for validation
if
(
\
array_key_exists
(
'x5c'
,
$attStmt
)
&&
\
is_array
(
$attStmt
[
'x5c'
])
&&
\
count
(
$attStmt
[
'x5c'
])
>
0
)
{
// The attestation certificate attestnCert MUST be the first element in the array
$attestnCert
=
array_shift
(
$attStmt
[
'x5c'
]);
if
(
!
(
$attestnCert
instanceof
ByteBuffer
))
{
throw
new
WebAuthnException
(
'invalid x5c certificate'
,
WebAuthnException
::
INVALID_DATA
);
}
$this
->
_x5c
=
$attestnCert
->
getBinaryString
();
// certificate chain
if
(
count
(
$attStmt
[
'x5c'
])
>
1
)
{
foreach
(
$attStmt
[
'x5c'
]
as
$chain
)
{
if
(
$chain
instanceof
ByteBuffer
)
{
$this
->
_x5c_chain
[]
=
$chain
->
getBinaryString
();
}
}
}
}
else
{
throw
new
WebAuthnException
(
'no x5c certificate found'
,
WebAuthnException
::
INVALID_DATA
);
}
}
/*
* returns the key certificate in PEM format
* @return string|null
*/
public
function
getCertificatePem
()
{
if
(
!
$this
->
_x5c
)
{
return
null
;
}
return
$this
->
_createCertificatePem
(
$this
->
_x5c
);
}
/**
* @param string $clientDataHash
*/
public
function
validateAttestation
(
$clientDataHash
)
{
return
$this
->
_validateOverX5c
(
$clientDataHash
);
}
/**
* validates the certificate against root certificates
* @param array $rootCas
* @return boolean
* @throws WebAuthnException
*/
public
function
validateRootCertificate
(
$rootCas
)
{
if
(
!
$this
->
_x5c
)
{
return
false
;
}
$chainC
=
$this
->
_createX5cChainFile
();
if
(
$chainC
)
{
$rootCas
[]
=
$chainC
;
}
$v
=
\
openssl_x509_checkpurpose
(
$this
->
getCertificatePem
(),
-
1
,
$rootCas
);
if
(
$v
===
-
1
)
{
throw
new
WebAuthnException
(
'error on validating root certificate: '
.
\
openssl_error_string
(),
WebAuthnException
::
CERTIFICATE_NOT_TRUSTED
);
}
return
$v
;
}
/**
* validate if x5c is present
* @param string $clientDataHash
* @return bool
* @throws WebAuthnException
*/
protected
function
_validateOverX5c
(
$clientDataHash
)
{
$publicKey
=
\
openssl_pkey_get_public
(
$this
->
getCertificatePem
());
if
(
$publicKey
===
false
)
{
throw
new
WebAuthnException
(
'invalid public key: '
.
\
openssl_error_string
(),
WebAuthnException
::
INVALID_PUBLIC_KEY
);
}
// Concatenate authenticatorData and clientDataHash to form attToBeSigned.
$attToBeSigned
=
$this
->
_authenticatorData
->
getBinary
();
$attToBeSigned
.
=
$clientDataHash
;
// Validate that certInfo is valid:
// Verify that magic is set to TPM_GENERATED_VALUE.
if
(
$this
->
_certInfo
->
getBytes
(
0
,
4
)
!==
$this
->
_TPM_GENERATED_VALUE
)
{
throw
new
WebAuthnException
(
'tpm magic not TPM_GENERATED_VALUE'
,
WebAuthnException
::
INVALID_DATA
);
}
// Verify that type is set to TPM_ST_ATTEST_CERTIFY.
if
(
$this
->
_certInfo
->
getBytes
(
4
,
2
)
!==
$this
->
_TPM_ST_ATTEST_CERTIFY
)
{
throw
new
WebAuthnException
(
'tpm type not TPM_ST_ATTEST_CERTIFY'
,
WebAuthnException
::
INVALID_DATA
);
}
$offset
=
6
;
$qualifiedSigner
=
$this
->
_tpmReadLengthPrefixed
(
$this
->
_certInfo
,
$offset
);
$extraData
=
$this
->
_tpmReadLengthPrefixed
(
$this
->
_certInfo
,
$offset
);
// Verify that extraData is set to the hash of attToBeSigned using the hash algorithm employed in "alg".
if
(
$extraData
->
getBinaryString
()
!==
hash
(
'SHA256'
,
$attToBeSigned
,
true
))
{
throw
new
WebAuthnException
(
'certInfo:extraData not hash of attToBeSigned'
,
WebAuthnException
::
INVALID_DATA
);
}
// Verify the sig is a valid signature over certInfo using the attestation
// public key in aikCert with the algorithm specified in alg.
return
\
openssl_verify
(
$this
->
_certInfo
,
$this
->
_signature
,
$publicKey
,
OPENSSL_ALGO_SHA256
)
===
1
;
}
protected
function
_tpmReadLengthPrefixed
(
ByteBuffer
$buffer
,
&
$offset
)
{
$len
=
$buffer
->
getUint16Val
(
$offset
);
$data
=
$buffer
->
getBytes
(
$offset
+
2
,
$len
);
$offset
+=
(
2
+
$len
);
return
new
ByteBuffer
(
$data
);
}
}
This diff is collapsed.
Click to expand it.
WebAuthn.php
+
1
−
0
View file @
e281e2fb
...
@@ -11,6 +11,7 @@ require_once 'Attestation/Format/None.php';
...
@@ -11,6 +11,7 @@ require_once 'Attestation/Format/None.php';
require_once
'Attestation/Format/AndroidKey.php'
;
require_once
'Attestation/Format/AndroidKey.php'
;
require_once
'Attestation/Format/AndroidSafetyNet.php'
;
require_once
'Attestation/Format/AndroidSafetyNet.php'
;
require_once
'Attestation/Format/Packed.php'
;
require_once
'Attestation/Format/Packed.php'
;
require_once
'Attestation/Format/Tpm.php'
;
require_once
'Attestation/Format/U2f.php'
;
require_once
'Attestation/Format/U2f.php'
;
require_once
'CBOR/CborDecoder.php'
;
require_once
'CBOR/CborDecoder.php'
;
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Topics
Snippets