1# Copyright (c) 2018 Yubico AB
2# All rights reserved.
3#
4#   Redistribution and use in source and binary forms, with or
5#   without modification, are permitted provided that the following
6#   conditions are met:
7#
8#    1. Redistributions of source code must retain the above copyright
9#       notice, this list of conditions and the following disclaimer.
10#    2. Redistributions in binary form must reproduce the above
11#       copyright notice, this list of conditions and the following
12#       disclaimer in the documentation and/or other materials provided
13#       with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26# POSSIBILITY OF SUCH DAMAGE.
27
28from __future__ import absolute_import, unicode_literals
29
30from .base import (
31    Attestation,
32    AttestationType,
33    AttestationResult,
34    InvalidSignature,
35    catch_builtins,
36)
37from ..cose import ES256
38
39from cryptography import x509
40from cryptography.hazmat.backends import default_backend
41from cryptography.exceptions import InvalidSignature as _InvalidSignature
42
43
44class FidoU2FAttestation(Attestation):
45    FORMAT = "fido-u2f"
46
47    @catch_builtins
48    def verify(self, statement, auth_data, client_data_hash):
49        cd = auth_data.credential_data
50        pk = b"\x04" + cd.public_key[-2] + cd.public_key[-3]
51        x5c = statement["x5c"]
52        FidoU2FAttestation.verify_signature(
53            auth_data.rp_id_hash,
54            client_data_hash,
55            cd.credential_id,
56            pk,
57            x5c[0],
58            statement["sig"],
59        )
60        return AttestationResult(AttestationType.BASIC, x5c)
61
62    @staticmethod
63    def verify_signature(
64        app_param, client_param, key_handle, public_key, cert_bytes, signature
65    ):
66        m = b"\0" + app_param + client_param + key_handle + public_key
67        cert = x509.load_der_x509_certificate(cert_bytes, default_backend())
68        try:
69            ES256.from_cryptography_key(cert.public_key()).verify(m, signature)
70        except _InvalidSignature:
71            raise InvalidSignature()
72