python-fido2/tests/test_attestation.py

352 lines
31 KiB
Python

# Copyright (c) 2013 Yubico AB
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from fido2.webauthn import AuthenticatorData
from fido2.attestation import (
Attestation,
AttestationType,
UnsupportedAttestation,
FidoU2FAttestation,
PackedAttestation,
TpmAttestation,
NoneAttestation,
AndroidSafetynetAttestation,
AppleAttestation,
InvalidData,
InvalidSignature,
UnsupportedType,
verify_x509_chain,
)
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
import unittest
# GS Root R2 (https://pki.goog/)
_GSR2_DER = bytes.fromhex(
"308203ba308202a2a003020102020b0400000000010f8626e60d300d06092a864886f70d0101050500304c3120301e060355040b1317476c6f62616c5369676e20526f6f74204341202d20523231133011060355040a130a476c6f62616c5369676e311330110603550403130a476c6f62616c5369676e301e170d3036313231353038303030305a170d3231313231353038303030305a304c3120301e060355040b1317476c6f62616c5369676e20526f6f74204341202d20523231133011060355040a130a476c6f62616c5369676e311330110603550403130a476c6f62616c5369676e30820122300d06092a864886f70d01010105000382010f003082010a0282010100a6cf240ebe2e6f28994542c4ab3e21549b0bd37f8470fa12b3cbbf875fc67f86d3b2305cd6fdadf17bdce5f86096099210f5d053defb7b7e7388ac52887b4aa6ca49a65ea8a78c5a11bc7a82ebbe8ce9b3ac962507974a992a072fb41e77bf8a0fb5027c1b96b8c5b93a2cbcd612b9eb597de2d006865f5e496ab5395e8834ecbc780c0898846ca8cd4bb4a07d0c794df0b82dcb21cad56c5b7de1a02984a1f9d39449cb24629120bcdd0bd5d9ccf9ea270a2b7391c69d1bacc8cbe8e0a0f42f908b4dfbb0361bf6197a85e06df26113885c9fe0930a51978a5aceafabd5f7aa09aa60bddcd95fdf72a960135e0001c94afa3fa4ea070321028e82ca03c29b8f0203010001a3819c308199300e0603551d0f0101ff040403020106300f0603551d130101ff040530030101ff301d0603551d0e041604149be20757671c1ec06a06de59b49a2ddfdc19862e30360603551d1f042f302d302ba029a0278625687474703a2f2f63726c2e676c6f62616c7369676e2e6e65742f726f6f742d72322e63726c301f0603551d230418301680149be20757671c1ec06a06de59b49a2ddfdc19862e300d06092a864886f70d01010505000382010100998153871c68978691ece04ab8440bab81ac274fd6c1b81c4378b30c9afcea2c3c6e611b4d4b29f59f051d26c1b8e983006245b6a90893b9a9334b189ac2f887884edbdd71341ac154da463fe0d32aab6d5422f53a62cd206fba2989d7dd91eed35ca23ea15b41f5dfe564432de9d539abd2a2dfb78bd0c080191c45c02d8ce8f82da4745649c505b54f15de6e44783987a87ebbf3791891bbf46f9dc1f08c358c5d01fbc36db9ef446d7946317e0afea982c1ffefab6e20c450c95f9d4d9b178c0ce501c9a0416a7353faa550b46e250ffb4c18f4fd52d98e69b1e8110fde88d8fb1d49f7aade95cf2078c26012db25408c6afc7e4238406412f79e81e1932e" # noqa E501
)
class TestAttestationObject(unittest.TestCase):
def test_unsupported_attestation(self):
attestation = Attestation.for_type("__unsupported__")()
self.assertIsInstance(attestation, UnsupportedAttestation)
with self.assertRaises(UnsupportedType) as ctx:
attestation.verify({}, 0, b"")
self.assertEqual(ctx.exception.fmt, "__unsupported__")
def test_none_attestation(self):
attestation = Attestation.for_type("none")()
self.assertIsInstance(attestation, NoneAttestation)
auth_data = AuthenticatorData(
bytes.fromhex(
"0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE12410000002BF8A011F38C0A4D15800617111F9EDC7D0040A17370D9C1759005700C8DE77E7DFD3A0A5300E0A26E5213AA40D6DF10EE4028B58B5F34167035D840BEBAE0C5CE8FD05AD9BD33E3BE7D1C558D81AB4803570BA5010203262001215820A5FD5CE1B1C458C530A54FA61B31BF6B04BE8B97AFDE54DD8CBB69275A8A1BE1225820FA3A3231DD9DEED9D1897BE5A6228C59501E4BCD12975D3DFF730F01278EA61C" # noqa E501
)
)
res = attestation.verify({}, auth_data, b"deadbeef" * 8)
self.assertEqual(res.attestation_type, AttestationType.NONE)
self.assertEqual(res.trust_path, [])
with self.assertRaises(InvalidData):
attestation.verify({"not": "empty"}, auth_data, b"deadbeef" * 8)
def test_none_windows_hello_attestation(self):
attestation = Attestation.for_type("none")()
self.assertIsInstance(attestation, NoneAttestation)
auth_data = AuthenticatorData(
bytes.fromhex(
"54ce651ed715b4aaa755eecebd4ea0950815b334bd07d109893e963018cddbd945000000006028b017b1d44c02b4b3afcdafc96bb200201decfcd6d6a05c2826d52348afdc70a9800df007845047b1a23706aa6e2f315ca401030339010020590100af59f4ad4f71da800bb91045b267e240e06317f7b2b1d76f78e239a433811faeca58a1869fb00225eb2727f81b6b20cbc18c0ad8d38fa450e8df11b4ad3bc3ee5d13c77ed172fa3af0195ec6ac0c4bac8c950115dfce6d38737cbafefbe117d8401cd56c638043a0d585131bc48a153b17a8dcb96671e15a90ba1b4ff810b138b77ac0a050b039b87b6089dd8dfa45611b992109d554aad3e6b72ac82d801496e4d2d230aa466090bbbf4f5632fe4b588e4f571462378fa6f514a536a5945b223c8d98f730b7cf85de86b98c217090f9e9ebf9643cf3feceeacb837d7a18542e03271cd8c70cf81186cdb63e4cbf4efc0cbbd3c93231b06f19580d0a980264d12143010001" # noqa
)
) # noqa
res = attestation.verify({}, auth_data, b"deadbeef" * 8)
self.assertEqual(res.attestation_type, AttestationType.NONE)
self.assertEqual(res.trust_path, [])
with self.assertRaises(InvalidData):
attestation.verify({"not": "empty"}, auth_data, b"deadbeef" * 8)
def test_tpm_windows_hello_attestation(self):
attestation = Attestation.for_type("tpm")()
self.assertIsInstance(attestation, TpmAttestation)
statement = {
"alg": -65535,
"sig": bytes.fromhex(
"""80e564d8cbb236577de68d2e68ecae200a8eaf6992889b5
fdc24624a4cb69caaab18df965058fbac39df9714b9c80b9a12d715cfc4dd15ed3a6e191a6d26e
7206fd402b0733c2c8b91f62ad44e4d41c940e2e914253b1d1a1c8889b1cdaf668b5449245dc33
1fab12e0b0dcdfc530cbe1f370e1f2b06c163fbd6177925a1a8998edd2e726989246a1980fa34e
6d65d3ca284944cb10254d85db0d8948294fb8174a41206c6b5e36406bae447343f8c9f97420e3
9f361815dfb268b33ccde5f29e4348a70f95abc30754c839fa7126e5bd882377d6abe3c0c95ba5
c21190a5e4fff5380b2c23cc1655e593244019e172ba8284618471d95b92c231c1ffe98ff23
""".replace(
"\n", ""
)
),
"x5c": [
bytes.fromhex(
"""308204b23082039aa0030201020210789e1a3657344c52bad2
2ed1ceb1bfaf300d06092a864886f70d01010b05003041313f303d060355040313364e43552d4e
54432d4b455949442d394642423739414130463532363237384245443135303932394137313731
45393641333542454637301e170d3139303430313038353934305a170d32393034303130383539
34305a300030820122300d06092a864886f70d01010105000382010f003082010a0282010100a6
60d1fe41564c26f943c70ff89fbd9ed6d957191d5ecaf727393d73cfff85e3ccfb9830027fe84b
171cc4b0b13811df4d9deff2bce4d8a8f9797169f7b8fc25016d9ba687c003083693716180c8f1
eccaa4410a9a7fe07b198ad7ba94ecf744e9bef0273a5e0723a4ac197994ca1ac0e5f595433970
0cf14ead419ae7cde8c3e81389771d5fa3d339f8d0856e918fd3746fa9a944d3c1f1c6a4e0ce3f
99b5ac5ba05166b3b8695405ae7d3777f9cc8e3ab8570f2191ba4f2cfc4c544337596f48d3d5f5
f9ae80575bf9eb81d5c477e99c58854645d587dd0ccdea2b0e3d482e69b326b289e65741e6b214
3fc2bca35ca7dd60e554affdcb85000762ff09b0410203010001a38201e5308201e1300e060355
1d0f0101ff040403020780300c0603551d130101ff04023000306d0603551d200101ff04633061
305f06092b060104018237151f3052305006082b0601050507020230441e420054004300500041
002000200054007200750073007400650064002000200050006c006100740066006f0072006d00
200020004900640065006e007400690074007930100603551d250409300706056781050803304a
0603551d110101ff0440303ea43c303a3138300e060567810502030c0569643a31333010060567
810502020c074e5043543678783014060567810502010c0b69643a3445353434333030301f0603
551d23041830168014c799ef2371327cb2e9e03838d0a9009fe9ed29e7301d0603551d0e041604
1429fb5f05c6187d8463b8b250b8f0ff128fd3a0713081b306082b060105050701010481a63081
a33081a006082b0601050507300286819368747470733a2f2f617a637370726f646e637561696b
7075626c6973682e626c6f622e636f72652e77696e646f77732e6e65742f6e63752d6e74632d6b
657969642d39666262373961613066353236323738626564313530393239613731373165393661
3335626566372f66383530353438392d303235612d343235322d383239302d3934646532633633
643039362e636572300d06092a864886f70d01010b0500038201010084bc4b9ac3ab6c2438bdec
dd3d99e6179bfc465995481d856683602bdcf0c26327b8ab77f7b695c8c6aab5f283b079c29369
29727b839e5bf08c687a33fc59bf281ebf28e9d04e78fd626573028014028badca038e68361017
a4501b18d56a6a73e35f00e043d8febb7a4c719c837bc5cb801efe23570d6c8b40699ba411fe66
f6fe5558f7d1c56a7646ba483cd601690a9323caba9257ae561781b13c658083ad1281047d94d4
c1ab9759d90a16fbe167cec388e7b67027a20dbc1b88986dbb636107ef91ffec22c413ac5fbfec
3de9ee4aa1c6e4c173e43246193890c8b024587fcc8028eb379f515de3c678b11dfb81aef3547c
3c6e790577d52f775f9148""".replace(
"\n", ""
)
),
bytes.fromhex(
"""308205e8308203d0a003020102021333000000a5304bb34bf0
bee43e0000000000a5300d06092a864886f70d01010b050030818c310b30090603550406130255
53311330110603550408130a57617368696e67746f6e3110300e060355040713075265646d6f6e
64311e301c060355040a13154d6963726f736f667420436f72706f726174696f6e313630340603
550403132d4d6963726f736f66742054504d20526f6f7420436572746966696361746520417574
686f726974792032303134301e170d3136303831383230323032305a170d323931323331323032
3032305a3041313f303d060355040313364e43552d4e54432d4b455949442d3946424237394141
304635323632373842454431353039323941373137314539364133354245463730820122300d06
092a864886f70d01010105000382010f003082010a0282010100e0b963203494ff3b8b93855f4d
0086aabf9f5038fe2a2c04311609074565097dd16de61ae1e6086f5d16997dc7ee5342bf9988f6
bb73ca614f3f5d8ea084fd047112892ae22db792e2efbe24bcb07fd01af124666db7ad53677e45
6a95e972a659c04fe3569e882afbf019c3c5890c52d2e81d175f97234fbe341406cbf834cafa76
184c077c9bd058fbe14b4032039142128fe985ee6041819eee86a62a43491d11af9d78f08e722a
28c0e9b522fed12f172dddfd032a634a6eba2fc90c332997d3ba5f297230cd7d666b6925c0e6ea
79b2459f68fc283cd7a09e09973a610fb88eb63bb1cc29e0dc5e033ace6b966c78038c1adc049e
f5360ae28696825ed10203010001a382018b30820187300b0603551d0f040403020186301b0603
551d250414301206092b06010401823715240605678105080330160603551d20040f300d300b06
092b060104018237151f30120603551d130101ff040830060101ff020100301d0603551d0e0416
0414c799ef2371327cb2e9e03838d0a9009fe9ed29e7301f0603551d230418301680147a8c0ace
2f486217e294d1ae55c152ec7174a45630700603551d1f046930673065a063a061865f68747470
3a2f2f7777772e6d6963726f736f66742e636f6d2f706b696f70732f63726c2f4d6963726f736f
667425323054504d253230526f6f742532304365727469666963617465253230417574686f7269
7479253230323031342e63726c307d06082b060105050701010471306f306d06082b0601050507
30028661687474703a2f2f7777772e6d6963726f736f66742e636f6d2f706b696f70732f636572
74732f4d6963726f736f667425323054504d253230526f6f742532304365727469666963617465
253230417574686f72697479253230323031342e637274300d06092a864886f70d01010b050003
820201003e91d074e6d6b9719bf13ffd7cd16b733938c092edaa580136ac7d8bb5295242e432f6
7c3ca5b1c8968b994665e99796a39d579a85cbe6eab02dfce1d08a4ce802b41bf6b00a01533c7c
f3b96c7d0b9c0f3a5d2e04350037aea5140a5cc781ca73f370998110bd1031cfa427760920574a
5d7709a1765921d61cb36d91d2ce9d3301f0798ae4b23592b080e70bb535cdf57403f96fe6f0ff
4c0f0363f785a918a1fd3debfaaaebe6b08724a216b491e95e6e300e3d43e4e156fe3c036afba1
7ad2b442f904568af1cc3fd9ad1888cbbd9ec98d42e55af5b26fa8790b6b7da677a585fff6ae90
18e492742d4e9c5ca1a06990a3abff76c6bc4b1e22d8c226d09a96fdcc12801345b647e15850d1
0d0cdb609160b1a7a7c2c6f0eb3dbc2fcd42b765fd22a5672b26009b9a83b44388b62cb89e9169
a455ff5be5ce8f7bde0420b5d7d24ec254affdc2e7e946c961ec159b6dfc703e3934f9445b0072
8e137e11a7c66f76709ca2177b39159fc08593aaa83724b159abb93e535aef53d7d6066a317f92
d42d17888534fee9daf844260de901c3b18b49ccb2a5f81f0f4639f2e2cfa1ce1d7c791cef6f48
5d10df989aac02b1e9afd1094603f5307133f5f59ce105a5910700f98fea5a5fcf8f5cf4c797bd
79d440cc4f9161f5cc61e0e8f06592050cd1f0f0fd066bd1d6335710fdf8159b75281ee1082bff
1da2fc0b631bd346ac""".replace(
"\n", ""
)
),
],
"certInfo": bytes.fromhex(
"""ff54434780170022000b68cec627cc6411099a1f80
9fde4379f649aa170c7072d1adf230de439efc80810014f7c8b0cdeb31328648130a19733d6fff
16e76e1300000003ef605603446ed8c56aa7608d01a6ea5651ee67a8a20022000bdf681917e185
29c61e1b85a1e7952f3201eb59c609ed5d8e217e5de76b228bbd0022000b0a10d216b0c3ab82bf
dc1f0a016ab9493384c7aee1937ee8800f76b30c9b71a7""".replace(
"\n", ""
)
),
"pubArea": bytes.fromhex(
"""0001000b0006047200209dffcbf36c383ae699fb986
8dc6dcb89d7153884be2803922c124158bfad22ae001000100800000000000100c706586c7f46c
dffede0ee0c5ebc8b7a08b36555c8091669e9ef2cb4fd858134a01e9522d3ef924069aeeec2271
823fe9879b5079eb3123be2eb39a7e954f8b83b5ebefefda25aed01bd19eab6db1962a3713985b
7a2dd1aa7770b5c1567fb0d18521e14abebbccc16832ef10bb05dcc818bbb70c91c224475928ad
a6f6181ed64f1cfb40db5e01687454cfacafa8318bdc6a677550baa6e24f8af864fa5324e9d930
a97cdeb1995b476f21a017b33ab7fe4139f2524c784fcb04cf5241c89f0c145eb23da914ad1722
d47a843692a0b2a567d94dd808c13678a51c5a0583dc042dcbba1b9ceff12b159d0539248b0994
ee18128ed50dd7a855e54d2459db005""".replace(
"\n", ""
)
),
}
auth_data = AuthenticatorData(
bytes.fromhex(
"54ce651ed715b4aaa755eecebd4ea0950815b334bd07d109893e963018cddbd9450000000008987058cadc4b81b6e130de50dcbe9600206053b7b599d16fb3fb11ea17a344850ebd0d18183a5b7ca6dfbd20c63cdb462aa401030339010020590100c706586c7f46cdffede0ee0c5ebc8b7a08b36555c8091669e9ef2cb4fd858134a01e9522d3ef924069aeeec2271823fe9879b5079eb3123be2eb39a7e954f8b83b5ebefefda25aed01bd19eab6db1962a3713985b7a2dd1aa7770b5c1567fb0d18521e14abebbccc16832ef10bb05dcc818bbb70c91c224475928ada6f6181ed64f1cfb40db5e01687454cfacafa8318bdc6a677550baa6e24f8af864fa5324e9d930a97cdeb1995b476f21a017b33ab7fe4139f2524c784fcb04cf5241c89f0c145eb23da914ad1722d47a843692a0b2a567d94dd808c13678a51c5a0583dc042dcbba1b9ceff12b159d0539248b0994ee18128ed50dd7a855e54d2459db0052143010001" # noqa
)
)
client_param = bytes.fromhex(
"057a0ecbe7e3e99e8926941614f6af078c802b110be89eb221d69be2e17a1ba4"
)
try:
res = attestation.verify(statement, auth_data, client_param)
except UnsupportedAlgorithm as e:
if e._reason is _Reasons.UNSUPPORTED_HASH:
self.skipTest(
"SHA1 signature verification not supported on this machine"
)
res = attestation.verify(statement, auth_data, client_param)
self.assertEqual(res.attestation_type, AttestationType.ATT_CA)
verify_x509_chain(res.trust_path)
def test_fido_u2f_attestation(self):
attestation = Attestation.for_type("fido-u2f")()
self.assertIsInstance(attestation, FidoU2FAttestation)
statement = {
"sig": bytes.fromhex(
"30450220324779C68F3380288A1197B6095F7A6EB9B1B1C127F66AE12A99FE8532EC23B9022100E39516AC4D61EE64044D50B415A6A4D4D84BA6D895CB5AB7A1AA7D081DE341FA" # noqa E501
),
"x5c": [
bytes.fromhex(
"3082024A30820132A0030201020204046C8822300D06092A864886F70D01010B0500302E312C302A0603550403132359756269636F2055324620526F6F742043412053657269616C203435373230303633313020170D3134303830313030303030305A180F32303530303930343030303030305A302C312A302806035504030C2159756269636F205532462045452053657269616C203234393138323332343737303059301306072A8648CE3D020106082A8648CE3D030107034200043CCAB92CCB97287EE8E639437E21FCD6B6F165B2D5A3F3DB131D31C16B742BB476D8D1E99080EB546C9BBDF556E6210FD42785899E78CC589EBE310F6CDB9FF4A33B3039302206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013060B2B0601040182E51C020101040403020430300D06092A864886F70D01010B050003820101009F9B052248BC4CF42CC5991FCAABAC9B651BBE5BDCDC8EF0AD2C1C1FFB36D18715D42E78B249224F92C7E6E7A05C49F0E7E4C881BF2E94F45E4A21833D7456851D0F6C145A29540C874F3092C934B43D222B8962C0F410CEF1DB75892AF116B44A96F5D35ADEA3822FC7146F6004385BCB69B65C99E7EB6919786703C0D8CD41E8F75CCA44AA8AB725AD8E799FF3A8696A6F1B2656E631B1E40183C08FDA53FA4A8F85A05693944AE179A1339D002D15CABD810090EC722EF5DEF9965A371D415D624B68A2707CAD97BCDD1785AF97E258F33DF56A031AA0356D8E8D5EBCADC74E071636C6B110ACE5CC9B90DFEACAE640FF1BB0F1FE5DB4EFF7A95F060733F5" # noqa E501
)
],
}
auth_data = AuthenticatorData(
bytes.fromhex(
"1194228DA8FDBDEEFD261BD7B6595CFD70A50D70C6407BCF013DE96D4EFB17DE41000000000000000000000000000000000000000000403EBD89BF77EC509755EE9C2635EFAAAC7B2B9C5CEF1736C3717DA48534C8C6B654D7FF945F50B5CC4E78055BDD396B64F78DA2C5F96200CCD415CD08FE420038A5010203262001215820E87625896EE4E46DC032766E8087962F36DF9DFE8B567F3763015B1990A60E1422582027DE612D66418BDA1950581EBC5C8C1DAD710CB14C22F8C97045F4612FB20C91" # noqa E501
)
)
client_param = bytes.fromhex(
"687134968222EC17202E42505F8ED2B16AE22F16BB05B88C25DB9E602645F141"
)
res = attestation.verify(statement, auth_data, client_param)
self.assertEqual(res.attestation_type, AttestationType.BASIC)
self.assertEqual(len(res.trust_path), 1)
statement["sig"] = b"a" * len(statement["sig"])
with self.assertRaises(InvalidSignature):
attestation.verify(statement, auth_data, client_param)
def test_packed_attestation(self):
attestation = Attestation.for_type("packed")()
self.assertIsInstance(attestation, PackedAttestation)
statement = {
"alg": -7,
"sig": bytes.fromhex(
"304502200D15DAF337D727AB4719B4027114A2AC43CD565D394CED62C3D9D1D90825F0B3022100989615E7394C87F4AD91F8FDAE86F7A3326DF332B3633DB088AAC76BFFB9A46B" # noqa E501
),
"x5c": [
bytes.fromhex(
"308202B73082019FA00302010202041D31330D300D06092A864886F70D01010B0500302A3128302606035504030C1F59756269636F2050726576696577204649444F204174746573746174696F6E301E170D3138303332383036333932345A170D3139303332383036333932345A306E310B300906035504061302534531123010060355040A0C0959756269636F20414231223020060355040B0C1941757468656E74696361746F72204174746573746174696F6E3127302506035504030C1E59756269636F205532462045452053657269616C203438393736333539373059301306072A8648CE3D020106082A8648CE3D030107034200047D71E8367CAFD0EA6CF0D61E4C6A416BA5BB6D8FAD52DB2389AD07969F0F463BFDDDDDC29D39D3199163EE49575A3336C04B3309D607F6160C81E023373E0197A36C306A302206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013060B2B0601040182E51C0201010404030204303021060B2B0601040182E51C01010404120410F8A011F38C0A4D15800617111F9EDC7D300C0603551D130101FF04023000300D06092A864886F70D01010B050003820101009B904CEADBE1F1985486FEAD02BAEAA77E5AB4E6E52B7E6A2666A4DC06E241578169193B63DADEC5B2B78605A128B2E03F7FE2A98EAEB4219F52220995F400CE15D630CF0598BA662D7162459F1AD1FC623067376D4E4091BE65AC1A33D8561B9996C0529EC1816D1710786384D5E8783AA1F7474CB99FE8F5A63A79FF454380361C299D67CB5CC7C79F0D8C09F8849B0500F6D625408C77CBBC26DDEE11CB581BEB7947137AD4F05AAF38BD98DA10042DDCAC277604A395A5B3EAA88A5C8BB27AB59C8127D59D6BBBA5F11506BF7B75FDA7561A0837C46F025FD54DCF1014FC8D17C859507AC57D4B1DEA99485DF0BA8F34D00103C3EEF2EF3BBFEC7A6613DE" # noqa E501
)
],
}
auth_data = AuthenticatorData(
bytes.fromhex(
"0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE124100000003F8A011F38C0A4D15800617111F9EDC7D004060A386206A3AACECBDBB22D601853D955FDC5D11ADFBD1AA6A950D966B348C7663D40173714A9F987DF6461BEADFB9CD6419FFDFE4D4CF2EEC1AA605A4F59BDAA50102032620012158200EDB27580389494D74D2373B8F8C2E8B76FA135946D4F30D0E187E120B423349225820E03400D189E85A55DE9AB0F538ED60736EB750F5F0306A80060FE1B13010560D" # noqa E501
)
)
client_param = bytes.fromhex(
"985B6187D042FB1258892ED637CEC88617DDF5F6632351A545617AA2B75261BF"
)
res = attestation.verify(statement, auth_data, client_param)
self.assertEqual(res.attestation_type, AttestationType.BASIC)
self.assertEqual(len(res.trust_path), 1)
statement["sig"] = b"a" * len(statement["sig"])
with self.assertRaises(InvalidSignature):
attestation.verify(statement, auth_data, client_param)
def test_android_safetynet_attestation(self):
attestation = Attestation.for_type("android-safetynet")()
self.assertIsInstance(attestation, AndroidSafetynetAttestation)
statement = {
"ver": "14574037",
"response": b"eyJhbGciOiJSUzI1NiIsIng1YyI6WyJNSUlGa2pDQ0JIcWdBd0lCQWdJUVJYcm9OMFpPZFJrQkFBQUFBQVB1bnpBTkJna3Foa2lHOXcwQkFRc0ZBREJDTVFzd0NRWURWUVFHRXdKVlV6RWVNQndHQTFVRUNoTVZSMjl2WjJ4bElGUnlkWE4wSUZObGNuWnBZMlZ6TVJNd0VRWURWUVFERXdwSFZGTWdRMEVnTVU4eE1CNFhEVEU0TVRBeE1EQTNNVGswTlZvWERURTVNVEF3T1RBM01UazBOVm93YkRFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ1RDa05oYkdsbWIzSnVhV0V4RmpBVUJnTlZCQWNURFUxdmRXNTBZV2x1SUZacFpYY3hFekFSQmdOVkJBb1RDa2R2YjJkc1pTQk1URU14R3pBWkJnTlZCQU1URW1GMGRHVnpkQzVoYm1SeWIybGtMbU52YlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTmpYa3owZUsxU0U0bSsvRzV3T28rWEdTRUNycWRuODhzQ3BSN2ZzMTRmSzBSaDNaQ1laTEZIcUJrNkFtWlZ3Mks5RkcwTzlyUlBlUURJVlJ5RTMwUXVuUzl1Z0hDNGVnOW92dk9tK1FkWjJwOTNYaHp1blFFaFVXWEN4QURJRUdKSzNTMmFBZnplOTlQTFMyOWhMY1F1WVhIRGFDN09acU5ub3NpT0dpZnM4djFqaTZIL3hobHRDWmUybEorN0d1dHpleEtweHZwRS90WlNmYlk5MDVxU2xCaDlmcGowMTVjam5RRmtVc0FVd21LVkFVdWVVejR0S2NGSzRwZXZOTGF4RUFsK09raWxNdElZRGFjRDVuZWw0eEppeXM0MTNoYWdxVzBXaGg1RlAzOWhHazlFL0J3UVRqYXpTeEdkdlgwbTZ4RlloaC8yVk15WmpUNEt6UEpFQ0F3RUFBYU9DQWxnd2dnSlVNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBVEFNQmdOVkhSTUJBZjhFQWpBQU1CMEdBMVVkRGdRV0JCUXFCUXdHV29KQmExb1RLcXVwbzRXNnhUNmoyREFmQmdOVkhTTUVHREFXZ0JTWTBmaHVFT3ZQbSt4Z254aVFHNkRyZlFuOUt6QmtCZ2dyQmdFRkJRY0JBUVJZTUZZd0p3WUlLd1lCQlFVSE1BR0dHMmgwZEhBNkx5OXZZM053TG5CcmFTNW5iMjluTDJkMGN6RnZNVEFyQmdnckJnRUZCUWN3QW9ZZmFIUjBjRG92TDNCcmFTNW5iMjluTDJkemNqSXZSMVJUTVU4eExtTnlkREFkQmdOVkhSRUVGakFVZ2hKaGRIUmxjM1F1WVc1a2NtOXBaQzVqYjIwd0lRWURWUjBnQkJvd0dEQUlCZ1puZ1F3QkFnSXdEQVlLS3dZQkJBSFdlUUlGQXpBdkJnTlZIUjhFS0RBbU1DU2dJcUFnaGg1b2RIUndPaTh2WTNKc0xuQnJhUzVuYjI5bkwwZFVVekZQTVM1amNtd3dnZ0VFQmdvckJnRUVBZFo1QWdRQ0JJSDFCSUh5QVBBQWR3Q2t1UW1RdEJoWUZJZTdFNkxNWjNBS1BEV1lCUGtiMzdqamQ4ME95QTNjRUFBQUFXWmREM1BMQUFBRUF3QklNRVlDSVFDU1pDV2VMSnZzaVZXNkNnK2dqLzl3WVRKUnp1NEhpcWU0ZVk0Yy9teXpqZ0loQUxTYmkvVGh6Y3pxdGlqM2RrM3ZiTGNJVzNMbDJCMG83NUdRZGhNaWdiQmdBSFVBVmhRR21pL1h3dXpUOWVHOVJMSSt4MFoydWJ5WkVWekE3NVNZVmRhSjBOMEFBQUZtWFE5ejVBQUFCQU1BUmpCRUFpQmNDd0E5ajdOVEdYUDI3OHo0aHIvdUNIaUFGTHlvQ3EySzAreUxSd0pVYmdJZ2Y4Z0hqdnB3Mm1CMUVTanEyT2YzQTBBRUF3Q2tuQ2FFS0ZVeVo3Zi9RdEl3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUk5blRmUktJV2d0bFdsM3dCTDU1RVRWNmthenNwaFcxeUFjNUR1bTZYTzQxa1p6d0o2MXdKbWRSUlQvVXNDSXkxS0V0MmMwRWpnbG5KQ0YyZWF3Y0VXbExRWTJYUEx5RmprV1FOYlNoQjFpNFcyTlJHelBodDNtMWI0OWhic3R1WE02dFg1Q3lFSG5UaDhCb200L1dsRmloemhnbjgxRGxkb2d6L0syVXdNNlM2Q0IvU0V4a2lWZnYremJKMHJqdmc5NEFsZGpVZlV3a0k5Vk5NakVQNWU4eWRCM29MbDZnbHBDZUY1ZGdmU1g0VTl4MzVvai9JSWQzVUUvZFBwYi9xZ0d2c2tmZGV6dG1VdGUvS1Ntcml3Y2dVV1dlWGZUYkkzenNpa3daYmtwbVJZS21qUG1odjRybGl6R0NHdDhQbjhwcThNMktEZi9QM2tWb3QzZTE4UT0iLCJNSUlFU2pDQ0F6S2dBd0lCQWdJTkFlTzBtcUdOaXFtQkpXbFF1REFOQmdrcWhraUc5dzBCQVFzRkFEQk1NU0F3SGdZRFZRUUxFeGRIYkc5aVlXeFRhV2R1SUZKdmIzUWdRMEVnTFNCU01qRVRNQkVHQTFVRUNoTUtSMnh2WW1Gc1UybG5iakVUTUJFR0ExVUVBeE1LUjJ4dlltRnNVMmxuYmpBZUZ3MHhOekEyTVRVd01EQXdOREphRncweU1URXlNVFV3TURBd05ESmFNRUl4Q3pBSkJnTlZCQVlUQWxWVE1SNHdIQVlEVlFRS0V4VkhiMjluYkdVZ1ZISjFjM1FnVTJWeWRtbGpaWE14RXpBUkJnTlZCQU1UQ2tkVVV5QkRRU0F4VHpFd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURRR005RjFJdk4wNXprUU85K3ROMXBJUnZKenp5T1RIVzVEekVaaEQyZVBDbnZVQTBRazI4RmdJQ2ZLcUM5RWtzQzRUMmZXQllrL2pDZkMzUjNWWk1kUy9kTjRaS0NFUFpSckF6RHNpS1VEelJybUJCSjV3dWRnem5kSU1ZY0xlL1JHR0ZsNXlPRElLZ2pFdi9TSkgvVUwrZEVhbHROMTFCbXNLK2VRbU1GKytBY3hHTmhyNTlxTS85aWw3MUkyZE44RkdmY2Rkd3VhZWo0YlhocDBMY1FCYmp4TWNJN0pQMGFNM1Q0SStEc2F4bUtGc2JqemFUTkM5dXpwRmxnT0lnN3JSMjV4b3luVXh2OHZObWtxN3pkUEdIWGt4V1k3b0c5aitKa1J5QkFCazdYckpmb3VjQlpFcUZKSlNQazdYQTBMS1cwWTN6NW96MkQwYzF0Skt3SEFnTUJBQUdqZ2dFek1JSUJMekFPQmdOVkhROEJBZjhFQkFNQ0FZWXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDQVFBd0hRWURWUjBPQkJZRUZKalIrRzRRNjgrYjdHQ2ZHSkFib090OUNmMHJNQjhHQTFVZEl3UVlNQmFBRkp2aUIxZG5IQjdBYWdiZVdiU2FMZC9jR1lZdU1EVUdDQ3NHQVFVRkJ3RUJCQ2t3SnpBbEJnZ3JCZ0VGQlFjd0FZWVphSFIwY0RvdkwyOWpjM0F1Y0d0cExtZHZiMmN2WjNOeU1qQXlCZ05WSFI4RUt6QXBNQ2VnSmFBamhpRm9kSFJ3T2k4dlkzSnNMbkJyYVM1bmIyOW5MMmR6Y2pJdlozTnlNaTVqY213d1B3WURWUjBnQkRnd05qQTBCZ1puZ1F3QkFnSXdLakFvQmdnckJnRUZCUWNDQVJZY2FIUjBjSE02THk5d2Eya3VaMjl2Wnk5eVpYQnZjMmwwYjNKNUx6QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFHb0ErTm5uNzh5NnBSamQ5WGxRV05hN0hUZ2laL3IzUk5Ha21VbVlIUFFxNlNjdGk5UEVhanZ3UlQyaVdUSFFyMDJmZXNxT3FCWTJFVFV3Z1pRK2xsdG9ORnZoc085dHZCQ09JYXpwc3dXQzlhSjl4anU0dFdEUUg4TlZVNllaWi9YdGVEU0dVOVl6SnFQalk4cTNNRHhyem1xZXBCQ2Y1bzhtdy93SjRhMkc2eHpVcjZGYjZUOE1jRE8yMlBMUkw2dTNNNFR6czNBMk0xajZieWtKWWk4d1dJUmRBdktMV1p1L2F4QlZielltcW13a201ekxTRFc1bklBSmJFTENRQ1p3TUg1NnQyRHZxb2Z4czZCQmNDRklaVVNweHU2eDZ0ZDBWN1N2SkNDb3NpclNtSWF0ai85ZFNTVkRRaWJldDhxLzdVSzR2NFpVTjgwYXRuWnoxeWc9PSJdfQ.eyJub25jZSI6InpiNVE5NFVPaHFOWnRVUWEraWY0NnF1UDRwZWZQN2JnQWRpQ3hraDFZRGs9IiwidGltZXN0YW1wTXMiOjE1NDM0ODI1Njg4NTgsImFwa1BhY2thZ2VOYW1lIjoiY29tLmdvb2dsZS5hbmRyb2lkLmdtcyIsImFwa0RpZ2VzdFNoYTI1NiI6InIxYzZiTkJmQ0hjZHcvZWpKSE1NWjhoakIrU0xXa1BSM0lreTZjV1dhNE09IiwiY3RzUHJvZmlsZU1hdGNoIjp0cnVlLCJhcGtDZXJ0aWZpY2F0ZURpZ2VzdFNoYTI1NiI6WyI4UDFzVzBFUEpjc2x3N1V6UnNpWEw2NHcrTzUwRWQrUkJJQ3RheTFnMjRNPSJdLCJiYXNpY0ludGVncml0eSI6dHJ1ZX0.Lq9WpOJ_GilocvPCTbIN2K5FtppXW2fTQzCW2pvb1Bo5qOZnJ0oOYBUqMgxx-zghlluSkkIIfPTvYl2zZUQsY-SNlBx7JASqDbksMyRsdU9r1Jn8D2zEtipFgjmZUkozi7AngnHoA5d0Yp-NF6slmr_FLMpAOnLZY9lREw8Cxnmso3Ph7zYUu7O5SxaRGwj8eMKydYJYHa23h2C8acuQKgSWL2YlG9T-oKT0CJ8jOSrKnHr39eMo7PFX0464diUvXUsv_M9kRIIQqCP0LzilGMdJVUrvFU7kg8csnFP6KMDfY70RGZ5ey3eNqs_D5-pjPfC4XPsPsksmy_wf-3UOmw", # noqa E501
}
auth_data = AuthenticatorData(
bytes.fromhex(
"720c20fde835785e0f5ebcad8ef6a7bd88804a91612a2e820e0059b8d5358797450000000000000000000000000000000000000000004101c8fd9b533d6adacf6710ebcfb39f6361c4d7e8787db47dc0a75ae0e7c862198c9c83b81ef2547bb5669314095fc846af4ecac6875f7b230cac7359c76b0c20f7a5010203262001215820a28851e2d411b5b2c289da50d41cc41be88498941fc256dab500b21c8dafe8d1225820d289dd467715be06a622771a7b21e1bbe2372f8713d20dd7888a6e7ae1845ca8" # noqa E501
)
)
client_param = bytes.fromhex(
"8422c80f3428e4e6465f76ebc8a4a93759a0a2e1fb845ee5eea7a02027408520"
)
res = attestation.verify(statement, auth_data, client_param)
self.assertEqual(res.attestation_type, AttestationType.BASIC)
verify_x509_chain(res.trust_path + [_GSR2_DER])
def test_apple_attestation(self):
attestation = Attestation.for_type("apple")()
self.assertIsInstance(attestation, AppleAttestation)
statement = {
"alg": -7,
"x5c": [
bytes.fromhex(
"30820242308201c9a00302010202060176af5359ff300a06082a8648ce3d0403023048311c301a06035504030c134170706c6520576562417574686e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230313232383136323732345a170d3230313233313136323732345a3081913149304706035504030c4038303966626331313065613835663233613862323435616563363136333530663337646665393632313232373336653431663862646365663334366138306439311a3018060355040b0c114141412043657274696669636174696f6e31133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613059301306072a8648ce3d020106082a8648ce3d030107034200041f46a2f159fde354598cdd47e005f1b6e7c9f00ed2a941ec7a88d222f5bcf55d6b078bc5b0be9552d85a974921f5bb848e2bbc3aecd6f71a386d2c87d6eafd37a3553053300c0603551d130101ff04023000300e0603551d0f0101ff0404030204f0303306092a864886f76364080204263024a1220420e56fb6212b3aae885294464fb10184b7fea62c48a6d78e61194e07ae6dacc132300a06082a8648ce3d040302036700306402301de8f0f238eee4f5ae80c59290b51e8c3f79397bf198e444ba162d4fccaab8558b072cf00a7c662f9058ff2a98af61ae0230149403b9643066e73a98d3659563dc4da49bf84e82b2b5bbeaf57755646fa243f36344d44b80a5798203bca023e030c7" # noqa E501
),
bytes.fromhex(
"30820234308201baa003020102021056255395c7a7fb40ebe228d8260853b6300a06082a8648ce3d040303304b311f301d06035504030c164170706c6520576562417574686e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333830315a170d3330303331333030303030305a3048311c301a06035504030c134170706c6520576562417574686e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004832e872f261491810225b9f5fcd6bb6378b5f55f3fcb045bc735993475fd549044df9bfe19211765c69a1dda050b38d45083401a434fb24d112d56c3e1cfbfcb9891fec0696081bef96cbc77c88dddaf46a5aee1dd515b5afaab93be9c0b2691a366306430120603551d130101ff040830060101ff020100301f0603551d2304183016801426d764d9c578c25a67d1a7de6b12d01b63f1c6d7301d0603551d0e04160414ebae82c4ffa1ac5b51d4cf24610500be63bd7788300e0603551d0f0101ff040403020106300a06082a8648ce3d0403030368003065023100dd8b1a3481a5fad9dbb4e7657b841e144c27b75b876a4186c2b1475750337227efe554457ef648950c632e5c483e70c102302c8a6044dc201fcfe59bc34d2930c1487851d960ed6a75f1eb4acabe38cd25b897d0c805bef0c7f78b07a571c6e80e07" # noqa E501
),
],
}
auth_data = AuthenticatorData(
bytes.fromhex(
"c46cef82ad1b546477591d008b08759ec3e6d2ecb4f39474bfea6969925d03b7450000000000000000000000000000000000000000001473d9429f4052d84debd035eb5bb7e716e3b81863a50102032620012158201f46a2f159fde354598cdd47e005f1b6e7c9f00ed2a941ec7a88d222f5bcf55d2258206b078bc5b0be9552d85a974921f5bb848e2bbc3aecd6f71a386d2c87d6eafd37" # noqa E501
)
)
client_param = bytes.fromhex(
"0d3ce80fabbc3adb9dd891deabb8db84603ea1fe2da8b5d4b46d6591aab342f3"
)
res = attestation.verify(statement, auth_data, client_param)
self.assertEqual(res.attestation_type, AttestationType.ANON_CA)
self.assertEqual(len(res.trust_path), 2)
verify_x509_chain(res.trust_path)