[
  {
    "path": ".gitattributes",
    "content": "# Auto-detect text files\n* text=auto\n\n# Always treat these as LF\ngradlew text eol=lf\n*.sh text eol=lf\n\n# Always treat these as CRLF\n*.bat text eol=crlf\n"
  },
  {
    "path": ".github/workflows/scan.yml",
    "content": "name: static code analysis\n# Documentation: https://github.com/Yubico/yes-static-code-analysis\n\non:\n  push:\n  schedule:\n    - cron: '0 0 * * 1'\n\nenv:\n  SCAN_IMG:\n    yubico-yes-docker-local.jfrog.io/static-code-analysis/java:v1\n  SECRET: ${{ secrets.ARTIFACTORY_READER_TOKEN }}\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@master\n\n    - name: Scan and fail on warnings\n      run: |\n        if [ \"${SECRET}\" != \"\" ]; then\n          docker login yubico-yes-docker-local.jfrog.io/ \\\n            -u svc-static-code-analysis-reader -p ${SECRET}\n          docker pull ${SCAN_IMG}\n          docker run -v${PWD}:/k -e PROJECT_NAME=${GITHUB_REPOSITORY#Yubico/} \\\n            -t ${SCAN_IMG}\n        else\n          echo \"No docker registry credentials, not scanning\"\n        fi\n\n    - uses: actions/upload-artifact@master\n      if: failure()\n      with:\n        name: suppression_files\n        path: suppression_files\n"
  },
  {
    "path": ".gitignore",
    "content": "# Eclipse\n.classpath\n.project\n.settings/\n\n# Intellij\n.idea/\nout/\n*.iml\n*.iws\n\n# Mac\n.DS_Store\n\n# Maven\nlog/\ntarget/\n*.versionsBackup\n\n# Gradle\n.gradle/\n/build/\n/*/build/\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: java\njdk:\n  - openjdk8\nafter_success:\n  - ./gradlew coveralls\n\naddons:\n  hosts:\n    - travis-issue-5227-workaround\n  hostname: travis-issue-5227-workaround\n"
  },
  {
    "path": "COPYING",
    "content": "Copyright (c) 2014, Yubico AB\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions 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.\nTHIS 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 OWNER 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.\n\n-------------------------------\n\nCopyright (c) 2014, Google Inc.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\nRedistributions 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.\nNeither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\nTHIS 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 OWNER 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."
  },
  {
    "path": "NEWS",
    "content": "== Version 0.19.12 ==\n\n* Deprecated all classes with note that the library is obsolete. This is planned\n  to be the final release of java-u2flib-server.\n\n\n== Version 0.19.11 ==\n\n* Bumped Guava dependency to version [24.1.1,30) in response to CVE-2018-10237\n\n\n== Version 0.19.10 ==\n\n* Bumped Jackson dependency to version 2.11.0 in response to CVEs:\n ** CVE-2020-9546\n ** CVE-2020-10672\n ** CVE-2020-10969\n ** CVE-2020-11620\n\n\n== Version 0.19.9 ==\n\n* Bumped Jackson dependency to version 2.9.10.3 in response to CVE-2019-20330 and CVE-2020-8840\n\n\n== Version 0.19.8 ==\n\n* Bumped Jackson dependency to version 2.9.10.1 which has patched CVE-2019-16942\n\n\n== Version 0.19.7 ==\n\n* Bumped Jackson dependency to version 2.9.10 which has patched CVE-2019-16335, CVE-2019-14540\n\n\n== Version 0.19.6 ==\n\n* Bumped Jackson dependency to version 2.9.9.3 which fixes a regression in 2.9.9.2\n\n\n== Version 0.19.5 ==\n\n* Bumped Jackson dependency to version 2.9.9.2 which has patched CVE-2019-12814, CVE-2019-14439, CVE-2019-14379\n\n\n== Version 0.19.4 ==\n\n* Bumped Jackson dependency to version 2.9.9 which has patched CVE-2019-12086\n\n\n== Version 0.19.3 ==\n\nBug fixes:\n\n* Use BouncyCastle security provider explicitly\n\n\n== Version 0.19.2 ==\n\n* Bumped Jackson dependency version to 2.9.8 which has patched CVE-2018-19360, CVE-2018-19362 and CVE-2018-19361\n\n\n== Version 0.19.1 ==\n\n* Bumped Jackson dependency version to 2.9.7 which has patched CVE-2018-7489 and CVE-2017-7525\n\n\n== Version 0.19.0 ==\n\nBreaking changes:\n\n* Overhauled exception hierarchy\n ** New exception class: `U2fCeremonyException`\n ** New exception class: `U2fRegistrationException extends U2fCeremonyException`\n ** New exception class: `U2fAuthenticationException extends U2fCeremonyException`\n ** The following exception classes now extend `U2fAuthenticationException`:\n  *** `DeviceCompromisedException`\n  *** `InvalidDeviceCounterException`\n  *** `NoEligableDevicesException`\n  *** `NoEligibleDevicesException`\n ** `U2fBadConfigurationException` is now a checked exception\n ** `U2fBadInputException` is now a checked exception, and is no longer thrown directly by the methods of `U2F`.\n  *** Methods of `U2F` now catch this exception and wrap it in a `U2fRegistrationException` or ``U2fAuthenticationException`.\n* `DeviceRegistration.getAttestationCertificate()` now returns `null` instead of throwing `NoSuchFieldException`\n* `static ClientData.getString(JsonNode, String)` now throws `U2fBadInputException` instead of `NullPointerException`, or if the returned field is not a `String` value\n* Some `AssertionError`s and `IllegalArgumentException`s are now `U2fBadInputException`s instead\n\n\nImprovements:\n\n* `BouncyCastleCrypto` now throws more descriptive exceptions\n\n\nBug fixes:\n\n* Improved error handling in client data input validation\n ** Thanks to Nicholas Wilson for the contribution, see https://github.com/Yubico/java-u2flib-server/pull/25\n\n\n== Version 0.18.1 ==\n\n* Lombok now longer leaks into runtime dependencies\n\n\n== Version 0.18.0 ==\n\n=== u2flib-server-core ===\n\nBreaking changes:\n\n* \"Authenticate\" renamed to \"sign\" everywhere in `u2flib-server-core`\n** Classes `AuthenticateRequest` renamed to `SignRequest`\n** Class `AuthenticateRequestData` renamed to `SignRequestData`\n** Class `AuthenticateResponse` renamed to `SignResponse`\n** Method `Client.authenticate` renamed to `sign`\n** Class `RawAuthenticateResponse` renamed to `RawSignResponse`\n** Method `SoftKey.authenticate` renamed to `sign`\n** Method `U2F.finishAuthentication` renamed to `finishSignature`\n** Method `U2F.startAuthentication` renamed to `startSignature`\n** Method `U2fPrimitives.finishAuthentication` renamed to `finishSignature`\n** Method `U2fPrimitives.startAuthenticateion` renamed to `startSignature`\n* Constants `AUTHENTICATE_TYP` and `REGISTER_TYPE` in `U2fPrimitives` are\n  now private\n\n== Version 0.17.1 ==\n\n* u2flib-server-attestation module now uses SLF4J logging instead of\n  `Throwable.printStackTrace`\n\n\n== Version 0.17.0 ==\n\n=== u2flib-server-core ===\n\nBreaking changes:\n\n* Field `RegisterRequestData.authenticateRequests: List<AuthenticateRequest>`\n replaced by field `registeredKeys: List<RegisteredKey>`\n\nAdditions:\n\n* Fields added to class `AuthenticateRequestData`:\n  * `challenge: String`\n  * `appId: String`\n* New class `RegisteredKey`\n* Field `appId: String` added to `RegisterRequestData`\n\n=== u2flib-server-demo ===\n\n* `u2f-api.js` upgraded from version 1.0 to 1.1\n* JS calls in views updated to work with version 1.1 of the JS API\n* All views except `loginIndex` and `registerIndex` are now rendered via\n  templates\n* Navigation links added to all views\n* Error feedback improved\n\n\n== Version 0.13.1 (unreleased) ==\n\n* Changed demo server URL to `localhost:8080`.\n* Added the method `ClientData.getString` to get arbitrary clientData fields.\n* Added u2flib-server-attestation for device attestation and metadata.\n\n\n== Version 0.13.0 ==\n\n* Added built-in support for multiple devices per user.\n* Fixed demo server bug when running from jar. Thanks to axianx.\n"
  },
  {
    "path": "README",
    "content": "== java-u2flib-server\n\nNOTE: _OBSOLETE: This project is no longer maintained.\nU2F has been superseded by https://www.w3.org/TR/webauthn/[Web Authentication],\nand this project is superseded by https://github.com/Yubico/java-webauthn-server/[java-webauthn-server].\nWe recommend using WebAuthn instead._\n\nimage:https://travis-ci.org/Yubico/java-u2flib-server.svg?branch=master[\"Build Status\", link=\"https://travis-ci.org/Yubico/java-u2flib-server\"]\nimage:https://coveralls.io/repos/github/Yubico/java-u2flib-server/badge.svg[\"Coverage Status\", link=\"https://coveralls.io/github/Yubico/java-u2flib-server\"]\n\nServer-side https://developers.yubico.com/U2F[U2F] library for Java. Provides functionality for registering\nU2F devices and authenticating with said devices.\n\n== Migrating to WebAuthn\n\nSee the https://github.com/Yubico/java-webauthn-server#migrating-from-u2f[Migrating from U2F] section\nin the https://github.com/Yubico/java-webauthn-server/[java-webauthn-server] documentation.\n\n=== Dependency\n\nMaven:\n[source, xml]\n <dependency>\n   <groupId>com.yubico</groupId>\n   <artifactId>u2flib-server-core</artifactId>\n   <version>0.19.12</version>\n </dependency>\n\nGradle:\n[source, groovy]\n repositories{ mavenCentral() }\n dependencies {\n   compile 'com.yubico:u2flib-server-core:0.19.12'\n }\n\n=== Example Usage\nNOTE: Make sure that you have read https://developers.yubico.com/U2F/Libraries/Using_a_library.html[Using a U2F library] before continuing.\n\n[source, java]\n----\n\nprivate abstract Iterable<DeviceRegistration> getRegistrations(String username);\n\n@GET\npublic View startAuthentication(String username) throws NoEligibleDevicesException {\n\n    // Generate a challenge for each U2F device that this user has registered\n    SignRequestData requestData\n        = u2f.startSignature(SERVER_ADDRESS, getRegistrations(username));\n\n    // Store the challenges for future reference\n    requestStorage.put(requestData.getRequestId(), requestData.toJson());\n\n    // Return an HTML page containing the challenges\n    return new AuthenticationView(requestData.toJson(), username);\n}\n\n@POST\npublic String finishAuthentication(SignResponse response, String username) throws\n        DeviceCompromisedException {\n\n    // Get the challenges that we stored when starting the authentication\n    SignRequestData signRequest\n        = requestStorage.remove(response.getRequestId());\n\n    // Verify the that the given response is valid for one of the registered devices\n    u2f.finishSignature(signRequest,\n                             response,\n                             getRegistrations(username));\n\n    return \"Successfully authenticated!\";\n}\n----\n\nIn the above example `getRegistrations()` will return the U2F devices currently associated with a given user.\nThis is most likely stored in a database. \nSee link:u2flib-server-demo[`u2flib-server-demo`] for a complete demo server (including registration and storage of U2F devices).\n\n=== JavaDoc\nJavaDoc can be found at https://developers.yubico.com/java-u2flib-server[developers.yubico.com/java-u2flib-server].\n\n\n=== Attestation\nThe attestation module (`u2flib-server-attestation`) enables you to restrict registrations to certain U2F devices (e.g. devices made by a specific vendor). It can also provide metadata for devices.\n\n=== Serialization\nAll relevant classes implement `Serializable`, so instead of using `toJson()`, you can use Java's built in serialization mechanism.\nInternally the classes use Jackson to serialize to and from JSON, and the ObjectMapper from Jackson can be used.\n"
  },
  {
    "path": "buildSrc/build.gradle",
    "content": "apply plugin: 'groovy'\n\nrepositories {\n  mavenCentral()\n}\n\ndependencies {\n  compile(\n    'commons-io:commons-io:2.5',\n    'info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.1.11',\n  )\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/com/yubico/gradle/pitest/tasks/PitestMergeTask.groovy",
    "content": "package com.yubico.gradle.pitest.tasks\n\nimport groovy.xml.XmlUtil\n\nimport info.solidsoft.gradle.pitest.PitestTask\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.tasks.OutputFile\nimport org.gradle.api.tasks.TaskAction\n\n/**\n * Merges PIT <code>mutations.xml</code> reports from all subprojects into one\n * report in the parent project.\n */\nclass PitestMergeTask extends DefaultTask {\n\n  @OutputFile\n  def File destinationFile = project.file(\"${project.buildDir}/reports/pitest/mutations.xml\")\n\n  PitestMergeTask() {\n    project.subprojects.each { subproject ->\n      subproject.tasks.withType(PitestTask).each { pitestTask ->\n        inputs.files pitestTask.outputs.files\n      }\n    }\n  }\n\n  def Set<File> findMutationsXmlFiles(File f, Set<File> found) {\n    if (f.isDirectory()) {\n      Set<File> result = found\n      for (File child : f.listFiles()) {\n        result = findMutationsXmlFiles(child, result)\n      }\n      return result\n    } else if (f.getName().endsWith(\".xml\")) {\n      return found.plus(f)\n    } else {\n      return found\n    }\n  }\n\n  def getMutations(File mutationsXmlFile) {\n    return new XmlParser().parseText(mutationsXmlFile.text).children()\n  }\n\n  @TaskAction\n  void merge() {\n    Set<File> mutationsXmlFiles = new HashSet<File>()\n    inputs.files.each { File f ->\n      mutationsXmlFiles = findMutationsXmlFiles(f, mutationsXmlFiles)\n    }\n\n    def rootNode = new XmlParser().createNode(null, 'mutations', [:])\n\n    mutationsXmlFiles.each {\n      getMutations(it).each { mutation ->\n        rootNode.append(mutation)\n      }\n    }\n\n    if (!destinationFile.exists()) {\n      destinationFile.createNewFile()\n    }\n    def os = destinationFile.newOutputStream()\n    XmlUtil.serialize(rootNode, os)\n    os.close()\n  }\n\n}\n"
  },
  {
    "path": "dev-util/example-authentication.py",
    "content": "#!/usr/bin/python3\n#\n# Scratchpad for working with raw U2F messages, useful for creating raw messages as test data.\n# Example keys from secion 8.2 of\n# https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#authentication-response-message-success\n\nfrom binascii import hexlify, unhexlify\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\n\nsig_alg = ec.ECDSA(hashes.SHA256())\n\nprivate_key_hex = 'ffa1e110dde5a2f8d93c4df71e2d4337b7bf5ddb60c75dc2b6b81433b54dd3c0'\npublic_key_hex = '04d368f1b665bade3c33a20f1e429c7750d5033660c019119d29aa4ba7abc04aa7c80a46bbe11ca8cb5674d74f31f8a903f6bad105fb6ab74aefef4db8b0025e1d'\n\nexample_payload_hex = '4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca0100000001ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57'\nexample_signature_hex = '304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f'\n\ns = int(private_key_hex, 16)\nx = int(public_key_hex[2:66], 16)\ny = int(public_key_hex[66:], 16)\n\nkeynums = ec.EllipticCurvePrivateNumbers(s, ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()))\nprivate_key = keynums.private_key(default_backend())\npublic_key = private_key.public_key()\n\n# Just ensure that we can successfully verify the example signature against the example key\npublic_key.verify(unhexlify(example_signature_hex), unhexlify(example_payload_hex), sig_alg)\n\n\n# Successful authentication message, but with invalid user presence byte\npayload_hex = '4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca0000000001ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57'\npayload_signature = private_key.sign(unhexlify(payload_hex), sig_alg)\n\nprint(\"Private key:\", private_key_hex)\nprint(\"Public key:\", public_key_hex)\nprint(\"Signing payload:\", payload_hex)\nprint(\"Signature:\", hexlify(payload_signature))\n"
  },
  {
    "path": "dev-util/example-registration.py",
    "content": "#!/usr/bin/python3\n#\n# Scratchpad for working with raw U2F messages, useful for creating raw messages as test data.\n# Example keys from secion 8.1 of\n# https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#registration-example\n\nfrom base64 import urlsafe_b64encode as b64encode\nfrom binascii import hexlify, unhexlify\nfrom cryptography.hazmat.backends import default_backend\nfrom cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives.asymmetric import ec\n\ndef hextokeys(private_key_hex, public_key_hex):\n  s = int(private_key_hex, 16)\n  x = int(public_key_hex[2:66], 16)\n  y = int(public_key_hex[66:], 16)\n\n  keynums = ec.EllipticCurvePrivateNumbers(s, ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()))\n  private_key = keynums.private_key(default_backend())\n  return (private_key, private_key.public_key())\n\ndef make_signature_base(application_parameter_hex, client_data_json, key_handle_hex, user_public_key_hex):\n  challenge_parameter_hex = make_challenge_parameter_hex(client_data_json)\n  return '00' + application_parameter_hex + challenge_parameter_hex + key_handle_hex + user_public_key_hex\n\ndef sign(private_key, message_hex):\n  return private_key.sign(message_hex, sig_alg)\n\ndef make_challenge_parameter_hex(client_data_json):\n  return sha256(bytes(client_data_json, 'UTF-8')).hex()\n\ndef make_registration_request_message(user_public_key_hex, key_handle_hex, attestation_certificate_hex, signature_hex):\n  return '05' + user_public_key_hex + hex(len(key_handle_hex) // 2)[2:] + key_handle_hex + attestation_certificate_hex + signature_hex\n\ndef sha256(data):\n  hasher = hashes.Hash(hashes.SHA256(), default_backend())\n  hasher.update(data)\n  return hasher.finalize()\n\ndef byteStringToDecimalBytes(data):\n  return [int(b) for b in data]\n\ndef byteStringToDecimalSignedBytes(data):\n  return [i if i < 128 else i - 256 for i in byteStringToDecimalBytes(data)]\n\nsig_alg = ec.ECDSA(hashes.SHA256())\n\nprivate_attestation_key_hex = 'f3fccc0d00d8031954f90864d43c247f4bf5f0665c6b50cc17749a27d1cf7664'\npublic_attestation_key_hex = '048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101'\nattestation_certificate_hex = '3082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df'\n\nprivate_user_key_hex = '9a9684b127c5e3a706d618c86401c7cf6fd827fd0bc18d24b0eb842e36d16df1'\npublic_user_key_hex = '04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9'\nkey_handle_hex = '2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25'\n\nclient_data = '{\"typ\":\"navigator.id.finishEnrollment\",\"challenge\":\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\",\"cid_pubkey\":{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8\",\"y\":\"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4\"},\"origin\":\"http://example.com\"}'\n\napplication_parameter_hex = 'f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1c4'\n\n# 1 byte RFU = 0x00\n# 32 bytes application parameter\n# 32 bytes challenge parameter\n# L bytes key handle\n# 65 bytes user public key\nexample_signature_base_hex = make_signature_base(application_parameter_hex, client_data, key_handle_hex, public_user_key_hex)\nexample_signature_hex = '304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871'\n\n# 1 reserved byte = 0x05\n# 65 bytes user public key\n# 1 byte key handle length = L\n# L bytes key handle\n# X.509 attestation certificate\n# X bytes = above signature\nexample_response_message_hex = '05' + public_user_key_hex + '40' + key_handle_hex + attestation_certificate_hex + example_signature_hex\n\n\n\n(private_attestation_key, public_attestation_key) = hextokeys(private_attestation_key_hex, public_attestation_key_hex)\n\n(private_user_key, public_user_key) = hextokeys(private_user_key_hex, public_user_key_hex)\n\n# Just ensure that we can successfully verify the example signature against the example key\npublic_attestation_key.verify(unhexlify(example_signature_hex), unhexlify(example_signature_base_hex), sig_alg)\n\n\ndifferent_client_data = '{\"typ\":\"navigator.id.launchNukes\",\"challenge\":\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\",\"origin\":\"http://example.com\"}'\ndifferent_challenge_parameter_hex = sha256(bytes(different_client_data, 'UTF-8')).hex()\n\npublic_attestation_key.verify(private_attestation_key.sign(b'Hello, World!', sig_alg), b'Hello, World!', sig_alg)\n\ndef print_results(private_user_key_hex, public_user_key_hex, private_attestation_key, application_parameter_hex, client_data_json, key_handle_hex, user_public_key_hex):\n  signature_base_hex = make_signature_base(application_parameter_hex, client_data_json, key_handle_hex, user_public_key_hex)\n  signature = sign(private_attestation_key, unhexlify(signature_base_hex))\n  message = make_registration_request_message(public_user_key_hex, key_handle_hex, attestation_certificate_hex, signature.hex())\n\n  public_attestation_key.verify(signature, unhexlify(signature_base_hex), sig_alg)\n\n  print(\"Private key:\\n \", private_user_key_hex)\n  print(\"Public key:\\n \", public_user_key_hex)\n  print(\"App ID:\\n \", application_parameter_hex)\n  print(\"Client data:\\n \", client_data_json)\n  print(\"Data to sign:\\n \", signature_base_hex)\n  print(\"Signature:\\n \", signature.hex())\n  print(\"Registration request message:\\n \", message)\n  print(\"Registration request message Base64:\\n \", b64encode(unhexlify(message)))\n\nprint_results(\n  private_user_key_hex,\n  public_user_key_hex,\n  private_attestation_key,\n  application_parameter_hex,\n  different_client_data,\n  key_handle_hex,\n  public_user_key_hex\n)\n"
  },
  {
    "path": "lombok.config",
    "content": "config.stopBubbling = true\nlombok.addLombokGeneratedAnnotation = true\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n    <groupId>com.yubico</groupId>\n    <artifactId>u2flib-server-parent</artifactId>\n    <packaging>pom</packaging>\n    <version>0.19.12</version>\n    <name>U2F parent</name>\n    <description>Java server-side library for U2F</description>\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    </properties>\n    <organization>\n        <name>Yubico</name>\n        <url>https://www.yubico.com/</url>\n    </organization>\n    <url>https://developers.yubico.com/</url>\n    <developers>\n        <developer>\n            <id>dain</id>\n            <name>Dain Nilsson</name>\n            <email>dain@yubico.com</email>\n        </developer>\n        <developer>\n            <id>emil</id>\n            <name>Emil Lundberg</name>\n            <email>emil@yubico.com</email>\n        </developer>\n    </developers>\n    <licenses>\n        <license>\n            <name>BSD-license</name>\n            <comments>Revised 2-clause BSD license</comments>\n        </license>\n    </licenses>\n    <scm>\n        <url>scm:git:git://github.com/Yubico/java-u2flib-server.git</url>\n        <connection>scm:git:git://github.com/Yubico/java-u2flib-server.git</connection>\n        <developerConnection>scm:git:ssh://git@github.com/Yubico/java-u2flib-server.git</developerConnection>\n        <tag>HEAD</tag>\n    </scm>\n\n    <modules>\n        <module>u2flib-server-core</module>\n        <module>u2flib-server-demo</module>\n        <module>u2flib-server-attestation</module>\n    </modules>\n\n    <distributionManagement>\n        <repository>\n            <id>sonatype-nexus-staging</id>\n            <name>Nexus Staging Repo</name>\n            <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n        </repository>\n        <snapshotRepository>\n            <id>sonatype-nexus-snapshots</id>\n            <name>Nexus Snapshot Repo</name>\n            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n        </snapshotRepository>\n    </distributionManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <version>1.7.25</version>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-simple</artifactId>\n            <version>1.7.25</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.13.1</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <version>2.8.47</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.16.18</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>2.0.2</version>\n                <configuration>\n                    <source>1.6</source>\n                    <target>1.6</target>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-release-plugin</artifactId>\n                <version>2.5.1</version>\n                <configuration>\n                    <localCheckout>true</localCheckout>\n                    <pushChanges>false</pushChanges>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-source-plugin</artifactId>\n                <version>2.1.2</version>\n                <executions>\n                    <execution>\n                        <id>attach-sources</id>\n                        <phase>verify</phase>\n                        <goals>\n                            <goal>jar-no-fork</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <version>2.8.1</version>\n                <configuration>\n                    <excludePackageNames>*.crypto;*.json;*.key</excludePackageNames>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>attach-javadoc</id>\n                        <phase>verify</phase>\n                        <goals>\n                            <goal>jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.sonatype.plugins</groupId>\n                <artifactId>nexus-staging-maven-plugin</artifactId>\n                <version>1.6.3</version>\n                <extensions>true</extensions>\n                <configuration>\n                    <serverId>ossrh</serverId>\n                    <nexusUrl>https://oss.sonatype.org/</nexusUrl>\n                    <autoReleaseAfterClose>true</autoReleaseAfterClose>\n                </configuration>\n            </plugin>\n\n            <plugin>\n                <groupId>org.pitest</groupId>\n                <artifactId>pitest-maven</artifactId>\n                <version>1.2.2</version>\n                <configuration>\n                    <timestampedReports>false</timestampedReports>\n                </configuration>\n            </plugin>\n\n        </plugins>\n    </build>\n    <profiles>\n        <profile>\n            <id>release-sign-artifacts</id>\n            <activation>\n                <property>\n                    <name>performRelease</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <version>1.6</version>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>test-coveralls</id>\n            <activation>\n                <property>\n                    <name>env.COVERALLS</name>\n                    <value>true</value>\n                </property>\n            </activation>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.jacoco</groupId>\n                        <artifactId>jacoco-maven-plugin</artifactId>\n                        <version>0.7.5.201505241946</version>\n                        <executions>\n                            <execution>\n                                <id>prepare-agent</id>\n                                <goals>\n                                    <goal>prepare-agent</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.eluder.coveralls</groupId>\n                        <artifactId>coveralls-maven-plugin</artifactId>\n                        <version>3.0.1</version>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n\n</project>\n"
  },
  {
    "path": "u2flib-server-attestation/README.adoc",
    "content": "== Device attestation  module\nModule for verifying attestation certificates and providing additional device metadata.\n\nThis is useful if you want to:\n\n - Restrict which types of devices that can be registered with your server\n - Give users rich data about their registered devices, such as:\n   * The vendor and model name\n   * An image of the device model\n\n\n=== Dependency\n\nMaven:\n[source, xml]\n <dependency>\n   <groupId>com.yubico</groupId>\n   <artifactId>u2flib-server-attestation</artifactId>\n   <version>0.19.1</version>\n </dependency>\n\nGradle:\n[source, groovy]\n repositories{ mavenCentral() }\n dependencies {\n   compile 'com.yubico:u2flib-server-attestation:0.19.1'\n }\n\n\n=== Usage\nThe Attestation class lets you know if the attestation certificate from the registration is trusted, and provides you with metadata about the vendor as well as the device itself.\n\n[source,java]\n----\nDeviceRegistration registration = U2F.finishRegistration(request, response);\nAttestation attestation = metadataService.getAttestation(\n    registration.getAttestationCertificate()\n);\n\n// Check if the device is trusted\nassert attestation.isTrusted();\n\n// Check that the vendor is Yubico\nassert attestation.getVendorProperties().get(\"name\").equals(\"Yubico\");\n\n// Get device name and image\nString deviceName = attestation.getDeviceProperties().get(\"displayName\");\nString imageUrl = attestation.getDeviceProperties().get(\"imageUrl\");\n----\n"
  },
  {
    "path": "u2flib-server-attestation/build.gradle",
    "content": "description = 'U2F attestation'\n\ndependencies {\n\n  compile(\n    project(':u2flib-server-core'),\n  )\n\n}\n"
  },
  {
    "path": "u2flib-server-attestation/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "u2flib-server-attestation/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "u2flib-server-attestation/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "u2flib-server-attestation/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>u2flib-server-parent</artifactId>\n        <groupId>com.yubico</groupId>\n        <version>0.19.12</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>u2flib-server-attestation</artifactId>\n    <name>U2F attestation</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.yubico</groupId>\n            <artifactId>u2flib-server-core</artifactId>\n            <version>0.19.12</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/Attestation.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.ImmutableSet;\nimport com.google.common.collect.Sets;\n\nimport java.io.Serializable;\nimport java.util.EnumSet;\nimport java.util.Map;\nimport java.util.Set;\nimport lombok.Getter;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@Getter\npublic class Attestation implements Serializable {\n    private final String metadataIdentifier;\n    private final Map<String, String> vendorProperties;\n    private final Map<String, String> deviceProperties;\n    private final Set<Transport> transports;\n\n    private Attestation() {\n        metadataIdentifier = null;\n        vendorProperties = null;\n        deviceProperties = null;\n        transports = Sets.immutableEnumSet(null);\n    }\n\n    public Attestation(String metadataIdentifier, Map<String, String> vendorProperties, Map<String, String> deviceProperties, Set<Transport> transports) {\n        this.metadataIdentifier = metadataIdentifier;\n        this.vendorProperties = vendorProperties == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(vendorProperties);\n        this.deviceProperties = deviceProperties == null ? ImmutableMap.<String, String>of() : ImmutableMap.copyOf(deviceProperties);\n        this.transports = Sets.immutableEnumSet(transports == null ? ImmutableSet.<Transport>of() : transports);\n    }\n\n    public boolean isTrusted() {\n        return metadataIdentifier != null;\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/DeviceMatcher.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation;\n\nimport com.fasterxml.jackson.databind.JsonNode;\n\nimport java.security.cert.X509Certificate;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic interface DeviceMatcher {\n    public boolean matches(X509Certificate attestationCertificate, JsonNode parameters);\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/MetadataObject.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.core.type.TypeReference;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.google.common.base.MoreObjects;\nimport com.google.common.collect.ImmutableList;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport lombok.EqualsAndHashCode;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@JsonIgnoreProperties(ignoreUnknown = true)\n@EqualsAndHashCode(of = { \"data\" })\npublic class MetadataObject extends JsonSerializable {\n    private static final TypeReference<Map<String, String>> MAP_STRING_STRING_TYPE = new TypeReference<Map<String, String>>() {\n    };\n    private static final TypeReference<List<String>> LIST_STRING_TYPE = new TypeReference<List<String>>() {\n    };\n    private static final TypeReference<List<JsonNode>> LIST_JSONNODE_TYPE = new TypeReference<List<JsonNode>>() {\n    };\n\n    private final transient JsonNode data;\n\n    private final String identifier;\n    private final long version;\n    private final Map<String, String> vendorInfo;\n    private final List<String> trustedCertificates;\n    private final List<JsonNode> devices;\n\n    @JsonCreator\n    public MetadataObject(JsonNode data) throws U2fBadInputException {\n        this.data = data;\n        try {\n            vendorInfo = OBJECT_MAPPER.readValue(data.get(\"vendorInfo\").traverse(), MAP_STRING_STRING_TYPE);\n            trustedCertificates = OBJECT_MAPPER.readValue(data.get(\"trustedCertificates\").traverse(), LIST_STRING_TYPE);\n            devices = OBJECT_MAPPER.readValue(data.get(\"devices\").traverse(), LIST_JSONNODE_TYPE);\n        } catch (JsonMappingException e) {\n            throw new U2fBadInputException(\"Invalid JSON data\", e);\n        } catch (JsonParseException e) {\n            throw new U2fBadInputException(\"Invalid JSON data\", e);\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Invalid JSON data\", e);\n        }\n\n        identifier = data.get(\"identifier\").asText();\n        version = data.get(\"version\").asLong();\n    }\n\n    @Override\n    public String toJson() {\n        return data.toString();\n    }\n\n    public String getIdentifier() {\n        return identifier;\n    }\n\n    public long getVersion() {\n        return version;\n    }\n\n    public Map<String, String> getVendorInfo() {\n        return vendorInfo;\n    }\n\n    public List<String> getTrustedCertificates() {\n        return trustedCertificates;\n    }\n\n    public List<JsonNode> getDevices() {\n        return MoreObjects.firstNonNull(devices, ImmutableList.<JsonNode>of());\n    }\n\n    public static List<MetadataObject> parseFromJson(String jsonData) throws U2fBadInputException {\n        JsonNode items;\n        try {\n            items = OBJECT_MAPPER.readValue(jsonData, JsonNode.class);\n            if (!items.isArray()) {\n                items = OBJECT_MAPPER.createArrayNode().add(items);\n            }\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Malformed data\", e);\n        }\n\n        ImmutableList.Builder<MetadataObject> objects = ImmutableList.builder();\n        for (JsonNode item : items) {\n            objects.add(MetadataObject.fromJson(item.toString()));\n        }\n        return objects.build();\n    }\n\n    public static MetadataObject fromJson(String json) throws U2fBadInputException {\n        try {\n            return new MetadataObject(OBJECT_MAPPER.readTree(json));\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Malformed data\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/MetadataResolver.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation;\n\nimport java.security.cert.X509Certificate;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic interface MetadataResolver {\n    MetadataObject resolve(X509Certificate attestationCertificate);\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/MetadataService.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.google.common.base.Charsets;\nimport com.google.common.base.Predicates;\nimport com.google.common.cache.Cache;\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.common.hash.Hashing;\nimport com.google.common.io.CharStreams;\nimport com.google.common.io.Closeables;\nimport com.yubico.u2f.attestation.matchers.ExtensionMatcher;\nimport com.yubico.u2f.attestation.matchers.FingerprintMatcher;\nimport com.yubico.u2f.attestation.resolvers.SimpleResolver;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class MetadataService {\n    private static final Logger logger = LoggerFactory.getLogger(MetadataService.class);\n\n    public static final String SELECTORS = \"selectors\";\n    private static final String SELECTOR_TYPE = \"type\";\n    private static final String SELECTOR_PARAMETERS = \"parameters\";\n\n    private static final String TRANSPORTS = \"transports\";\n    private static final String TRANSPORTS_EXT_OID = \"1.3.6.1.4.1.45724.2.1.1\";\n\n    public static final Map<String, DeviceMatcher> DEFAULT_DEVICE_MATCHERS = ImmutableMap.of(\n            ExtensionMatcher.SELECTOR_TYPE, new ExtensionMatcher(),\n            FingerprintMatcher.SELECTOR_TYPE, new FingerprintMatcher()\n    );\n\n    private static MetadataResolver createDefaultMetadataResolver() {\n        SimpleResolver resolver = new SimpleResolver();\n        InputStream is = null;\n        try {\n            is = MetadataService.class.getResourceAsStream(\"/metadata.json\");\n            resolver.addMetadata(CharStreams.toString(new InputStreamReader(is, Charsets.UTF_8)));\n        } catch (IOException e) {\n            logger.error(\"createDefaultMetadataResolver failed\", e);\n        } catch (CertificateException e) {\n            logger.error(\"createDefaultMetadataResolver failed\", e);\n        } catch (U2fBadInputException e) {\n            logger.error(\"createDefaultMetadataResolver failed\", e);\n        } finally {\n            Closeables.closeQuietly(is);\n        }\n        return resolver;\n    }\n\n    private final Attestation unknownAttestation = new Attestation(null, null, null, null);\n    private final MetadataResolver resolver;\n    private final Map<String, DeviceMatcher> matchers = new HashMap<String, DeviceMatcher>();\n    private final Cache<String, Attestation> cache;\n\n    public MetadataService(MetadataResolver resolver, Cache<String, Attestation> cache, Map<String, ? extends DeviceMatcher> matchers) {\n        this.resolver = resolver != null ? resolver : createDefaultMetadataResolver();\n        this.cache = cache != null ? cache : CacheBuilder.newBuilder().<String, Attestation>build();\n        if (matchers == null) {\n            matchers = DEFAULT_DEVICE_MATCHERS;\n        }\n        this.matchers.putAll(matchers);\n    }\n\n    public MetadataService() {\n        this(null, null, null);\n    }\n\n    public MetadataService(MetadataResolver resolver) {\n        this(resolver, null, null);\n    }\n\n    public MetadataService(MetadataResolver resolver, Map<String, ? extends DeviceMatcher> matchers) {\n        this(resolver, null, matchers);\n    }\n\n    public MetadataService(MetadataResolver resolver, Cache<String, Attestation> cache) {\n        this(resolver, cache, null);\n    }\n\n    public void registerDeviceMatcher(String matcherType, DeviceMatcher matcher) {\n        matchers.put(matcherType, matcher);\n    }\n\n    private boolean deviceMatches(JsonNode selectors, X509Certificate attestationCertificate) {\n        if (selectors != null && !selectors.isNull()) {\n            for (JsonNode selector : selectors) {\n                DeviceMatcher matcher = matchers.get(selector.get(SELECTOR_TYPE).asText());\n                if (matcher != null && matcher.matches(attestationCertificate, selector.get(SELECTOR_PARAMETERS))) {\n                    return true;\n                }\n            }\n            return false;\n        }\n        return true; //Match if selectors is null or missing.\n    }\n\n    public Attestation getCachedAttestation(String attestationCertificateFingerprint) {\n        return cache.getIfPresent(attestationCertificateFingerprint);\n    }\n\n    public Attestation getAttestation(final X509Certificate attestationCertificate) {\n        if (attestationCertificate == null) {\n            return unknownAttestation;\n        }\n        try {\n            String fingerprint = Hashing.sha1().hashBytes(attestationCertificate.getEncoded()).toString();\n            return cache.get(fingerprint, new Callable<Attestation>() {\n                @Override\n                public Attestation call() throws Exception {\n                    return lookupAttestation(attestationCertificate);\n                }\n            });\n        } catch (Exception e) {\n            return unknownAttestation;\n        }\n    }\n\n    private Attestation lookupAttestation(X509Certificate attestationCertificate) {\n        MetadataObject metadata = resolver.resolve(attestationCertificate);\n        Map<String, String> vendorProperties = null;\n        Map<String, String> deviceProperties = null;\n        String identifier = null;\n        int transports = 0;\n        if (metadata != null) {\n            identifier = metadata.getIdentifier();\n            vendorProperties = Maps.filterValues(metadata.getVendorInfo(), Predicates.notNull());\n            for (JsonNode device : metadata.getDevices()) {\n                if (deviceMatches(device.get(SELECTORS), attestationCertificate)) {\n                    JsonNode transportNode = device.get(TRANSPORTS);\n                    if(transportNode != null) {\n                        transports = transportNode.asInt(0);\n                    }\n                    ImmutableMap.Builder<String, String> devicePropertiesBuilder = ImmutableMap.builder();\n                    for (Map.Entry<String, JsonNode> deviceEntry : Lists.newArrayList(device.fields())) {\n                        JsonNode value = deviceEntry.getValue();\n                        if (value.isTextual()) {\n                            devicePropertiesBuilder.put(deviceEntry.getKey(), value.asText());\n                        }\n                    }\n                    deviceProperties = devicePropertiesBuilder.build();\n                    break;\n                }\n            }\n        }\n\n        transports |= get_transports(attestationCertificate.getExtensionValue(TRANSPORTS_EXT_OID));\n\n        return new Attestation(identifier, vendorProperties, deviceProperties, Transport.fromInt(transports));\n    }\n\n    private int get_transports(byte[] extensionValue) {\n        if(extensionValue == null) {\n            return 0;\n        }\n\n        // Mask out unused bits (shouldn't be needed as they should already be 0).\n        int unusedBitMask = 0xff;\n        for(int i=0; i < extensionValue[3]; i++) {\n            unusedBitMask <<= 1;\n        }\n        extensionValue[extensionValue.length-1] &= unusedBitMask;\n\n        int transports = 0;\n        for(int i=extensionValue.length - 1; i >= 5; i--) {\n            byte b = extensionValue[i];\n            for(int bi=0; bi < 8; bi++) {\n                transports = (transports << 1) | (b & 1);\n                b >>= 1;\n            }\n        }\n\n        return transports;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/Transport.java",
    "content": "package com.yubico.u2f.attestation;\n\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.Set;\n\n/**\n * Created by Dain on 2016-02-18.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic enum Transport {\n    BT_CLASSIC(1),\n    BLE(2),\n    USB(4),\n    NFC(8);\n\n    private final int bitpos;\n\n    Transport(int bitpos) {\n        this.bitpos = bitpos;\n    }\n\n    public static Set<Transport> fromInt(int bits) {\n        EnumSet<Transport> transports = EnumSet.noneOf(Transport.class);\n        for(Transport transport : Transport.values()) {\n            if((transport.bitpos & bits) != 0) {\n                transports.add(transport);\n            }\n        }\n\n        return transports;\n    }\n\n    public static int toInt(Iterable<Transport> transports) {\n        int transportsInt = 0;\n        for(Transport transport : transports) {\n            transportsInt |= transport.bitpos;\n        }\n        return transportsInt;\n    }\n\n    public static int toInt(Transport...transports) {\n        return toInt(Arrays.asList(transports));\n    }\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/matchers/ExtensionMatcher.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation.matchers;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.yubico.u2f.attestation.DeviceMatcher;\nimport org.bouncycastle.util.Strings;\n\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class ExtensionMatcher implements DeviceMatcher {\n    public static final String SELECTOR_TYPE = \"x509Extension\";\n\n    private static final String EXTENSION_KEY = \"key\";\n    private static final String EXTENSION_VALUE = \"value\";\n\n    @Override\n    public boolean matches(X509Certificate attestationCertificate, JsonNode parameters) {\n        String matchKey = parameters.get(EXTENSION_KEY).asText();\n        JsonNode matchValue = parameters.get(EXTENSION_VALUE);\n        byte[] extensionValue = attestationCertificate.getExtensionValue(matchKey);\n        if (extensionValue != null) {\n            if (matchValue == null) {\n                return true;\n            } else {\n                //TODO: Handle long lengths? Verify length?\n                String readValue = Strings.fromByteArray(Arrays.copyOfRange(extensionValue, 2, extensionValue.length));\n                if (matchValue.asText().equals(readValue)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/matchers/FingerprintMatcher.java",
    "content": "package com.yubico.u2f.attestation.matchers;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.google.common.hash.Hashing;\nimport com.yubico.u2f.attestation.DeviceMatcher;\n\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.X509Certificate;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class FingerprintMatcher implements DeviceMatcher {\n    public static final String SELECTOR_TYPE = \"fingerprint\";\n\n    private static final String FINGERPRINTS_KEY = \"fingerprints\";\n\n    @Override\n    public boolean matches(X509Certificate attestationCertificate, JsonNode parameters) {\n        JsonNode fingerprints = parameters.get(FINGERPRINTS_KEY);\n        if(fingerprints.isArray()) {\n            try {\n                String fingerprint = Hashing.sha1().hashBytes(attestationCertificate.getEncoded()).toString().toLowerCase();\n                for(JsonNode candidate : fingerprints) {\n                    if(fingerprint.equals(candidate.asText().toLowerCase())) {\n                        return true;\n                    }\n                }\n            } catch (CertificateEncodingException e) {\n                //Fall through to return false.\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/java/com/yubico/u2f/attestation/resolvers/SimpleResolver.java",
    "content": "/* Copyright 2015 Yubico */\n\npackage com.yubico.u2f.attestation.resolvers;\n\nimport com.google.common.collect.ArrayListMultimap;\nimport com.google.common.collect.Multimap;\nimport com.yubico.u2f.attestation.MetadataObject;\nimport com.yubico.u2f.attestation.MetadataResolver;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.NoSuchProviderException;\nimport java.security.SignatureException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class SimpleResolver implements MetadataResolver {\n    private static final Logger logger = LoggerFactory.getLogger(SimpleResolver.class);\n\n    private final Multimap<String, X509Certificate> certs = ArrayListMultimap.create();\n    private final Map<X509Certificate, MetadataObject> metadata = new HashMap<X509Certificate, MetadataObject>();\n\n    public void addMetadata(String jsonData) throws CertificateException, U2fBadInputException {\n        for (MetadataObject object : MetadataObject.parseFromJson(jsonData)) {\n            addMetadata(object);\n        }\n    }\n\n    public void addMetadata(MetadataObject object) throws CertificateException {\n        for (String caPem : object.getTrustedCertificates()) {\n            X509Certificate caCert = CertificateParser.parsePem(caPem);\n            certs.put(caCert.getSubjectDN().getName(), caCert);\n            metadata.put(caCert, object);\n        }\n    }\n\n    @Override\n    public MetadataObject resolve(X509Certificate attestationCertificate) {\n        String issuer = attestationCertificate.getIssuerDN().getName();\n        for (X509Certificate cert : certs.get(issuer)) {\n            try {\n                attestationCertificate.verify(cert.getPublicKey());\n                return metadata.get(cert);\n            } catch (CertificateException e) {\n                logger.error(\"resolve failed\", e);\n            } catch (NoSuchAlgorithmException e) {\n                logger.error(\"resolve failed\", e);\n            } catch (InvalidKeyException e) {\n                logger.error(\"resolve failed\", e);\n            } catch (NoSuchProviderException e) {\n                logger.error(\"resolve failed\", e);\n            } catch (SignatureException e) {\n                logger.error(\"resolve failed\", e);\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/main/resources/metadata.json",
    "content": "{\n  \"identifier\": \"2fb54029-7613-4f1d-94f1-fb876c14a6fe\",\n  \"version\": 4,\n  \"vendorInfo\": {\n    \"url\": \"https://yubico.com\",\n    \"imageUrl\": \"https://developers.yubico.com/U2F/Images/yubico.png\",\n    \"name\": \"Yubico\"\n  },\n  \"trustedCertificates\": [\n    \"-----BEGIN CERTIFICATE-----\\nMIIDHjCCAgagAwIBAgIEG1BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ\\ndWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw\\nMDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290\\nIENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\\nAoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk\\n5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep\\n8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw\\nnebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT\\n9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw\\nLvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ\\nhjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN\\nBgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4\\nMYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt\\nhX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k\\nLVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U\\nsG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc\\nU9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==\\n-----END CERTIFICATE-----\"\n  ],\n  \"devices\": [\n    {\n      \"deviceId\": \"1.3.6.1.4.1.41482.1.1\",\n      \"displayName\": \"Security Key by Yubico\",\n      \"transports\": 4,\n      \"deviceUrl\": \"https://www.yubico.com/products/yubikey-hardware/fido-u2f-security-key/\",\n      \"imageUrl\": \"https://developers.yubico.com/U2F/Images/SKY.png\",\n      \"selectors\": [\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"key\": \"1.3.6.1.4.1.41482.1.1\"\n          }\n        },\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"value\": \"1.3.6.1.4.1.41482.1.1\",\n            \"key\": \"1.3.6.1.4.1.41482.2\"\n          }\n        }\n      ]\n    },\n    {\n      \"deviceId\": \"1.3.6.1.4.1.41482.1.2\",\n      \"displayName\": \"YubiKey NEO/NEO-n\",\n      \"transports\": 4,\n      \"deviceUrl\": \"https://www.yubico.com/products/yubikey-hardware/yubikey-neo/\",\n      \"imageUrl\": \"https://developers.yubico.com/U2F/Images/NEO.png\",\n      \"selectors\": [\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"key\": \"1.3.6.1.4.1.41482.1.2\"\n          }\n        },\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"value\": \"1.3.6.1.4.1.41482.1.2\",\n            \"key\": \"1.3.6.1.4.1.41482.2\"\n          }\n        }\n      ]\n    },\n    {\n      \"deviceId\": \"1.3.6.1.4.1.41482.1.3\",\n      \"displayName\": \"YubiKey Plus\",\n      \"transports\": 4,\n      \"deviceUrl\": \"https://www.yubico.com/products/yubikey-hardware/\",\n      \"imageUrl\": \"https://developers.yubico.com/U2F/Images/PLS.png\",\n      \"selectors\": [\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"key\": \"1.3.6.1.4.1.41482.1.3\"\n          }\n        },\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"value\": \"1.3.6.1.4.1.41482.1.3\",\n            \"key\": \"1.3.6.1.4.1.41482.2\"\n          }\n        }\n      ]\n    },\n    {\n      \"deviceId\": \"1.3.6.1.4.1.41482.1.4\",\n      \"displayName\": \"YubiKey Edge\",\n      \"transports\": 4,\n      \"deviceUrl\": \"https://www.yubico.com/products/yubikey-hardware/\",\n      \"imageUrl\": \"https://developers.yubico.com/U2F/Images/YKE.png\",\n      \"selectors\": [\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"value\": \"1.3.6.1.4.1.41482.1.4\",\n            \"key\": \"1.3.6.1.4.1.41482.2\"\n          }\n        }\n      ]\n    },\n    {\n      \"deviceId\": \"1.3.6.1.4.1.41482.1.5\",\n      \"displayName\": \"YubiKey 4/YubiKey 4 Nano\",\n      \"transports\": 4,\n      \"deviceUrl\": \"https://www.yubico.com/products/yubikey-hardware/yubikey4/\",\n      \"imageUrl\": \"https://developers.yubico.com/U2F/Images/YK4.png\",\n      \"selectors\": [\n        {\n          \"type\": \"x509Extension\",\n          \"parameters\": {\n            \"value\": \"1.3.6.1.4.1.41482.1.5\",\n            \"key\": \"1.3.6.1.4.1.41482.2\"\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "u2flib-server-attestation/src/test/java/com/yubico/u2f/attestation/MetadataObjectTest.java",
    "content": "package com.yubico.u2f.attestation;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.collect.Iterables;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class MetadataObjectTest {\n    public static final String JSON = \"{\\\"identifier\\\":\\\"foobar\\\",\\\"version\\\":1,\\\"trustedCertificates\\\":[\\\"-----BEGIN CERTIFICATE-----\\\\nMIIDHjCCAgagAwIBAgIEG1BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ\\\\ndWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw\\\\nMDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290\\\\nIENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\\\\nAoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk\\\\n5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep\\\\n8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw\\\\nnebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT\\\\n9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw\\\\nLvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ\\\\nhjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN\\\\nBgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4\\\\nMYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt\\\\nhX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k\\\\nLVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U\\\\nsG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc\\\\nU9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==\\\\n-----END CERTIFICATE-----\\\"],\\\"vendorInfo\\\":{\\\"name\\\":\\\"Yubico\\\",\\\"url\\\":\\\"https://yubico.com\\\",\\\"imageUrl\\\":\\\"https://developers.yubico.com/U2F/Images/yubico.png\\\"},\\\"devices\\\":[{\\\"displayName\\\":\\\"YubiKey NEO/NEO-n\\\",\\\"deviceId\\\":\\\"1.3.6.1.4.1.41482.1.2\\\",\\\"deviceUrl\\\":\\\"https://www.yubico.com/products/yubikey-hardware/yubikey-neo/\\\",\\\"imageUrl\\\":\\\"https://developers.yubico.com/U2F/Images/NEO.png\\\",\\\"selectors\\\":[{\\\"type\\\":\\\"x509Extension\\\",\\\"parameters\\\":{\\\"key\\\":\\\"1.3.6.1.4.1.41482.1.2\\\"}}]}]}\";\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        MetadataObject metadata = MetadataObject.fromJson(JSON);\n        ObjectMapper objectMapper = new ObjectMapper();\n        MetadataObject metadata2 = objectMapper.readValue(objectMapper.writeValueAsString(metadata), MetadataObject.class);\n\n        assertEquals(\"foobar\", metadata.getIdentifier());\n        assertEquals(1, metadata.getVersion());\n        assertEquals(1, metadata.getTrustedCertificates().size());\n\n        assertEquals(\"Yubico\", metadata.getVendorInfo().get(\"name\"));\n        assertEquals(\"1.3.6.1.4.1.41482.1.2\", metadata.getDevices().get(0).get(\"deviceId\").asText());\n\n        assertEquals(metadata, metadata2);\n        assertEquals(JSON, metadata.toJson());\n    }\n\n    @Test\n    public void testParseFromJson() throws U2fBadInputException {\n        MetadataObject parsed1 = Iterables.getOnlyElement(MetadataObject.parseFromJson(JSON));\n        MetadataObject parsed2 = Iterables.getOnlyElement(MetadataObject.parseFromJson(\"[\" + JSON + \"]\"));\n        MetadataObject expected = MetadataObject.fromJson(JSON);\n\n        assertEquals(expected.toJson(), parsed1.toJson());\n        assertEquals(expected.toJson(), parsed2.toJson());\n    }\n}"
  },
  {
    "path": "u2flib-server-attestation/src/test/java/com/yubico/u2f/attestation/MetadataServiceTest.java",
    "content": "package com.yubico.u2f.attestation;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.hash.Hashing;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.util.EnumSet;\nimport org.junit.Test;\nimport org.mockito.Matchers;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\npublic class MetadataServiceTest {\n    private static final String ATTESTATION_CERT = \"MIICGzCCAQWgAwIBAgIEdaP2dTALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCoxKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NzM2Nzk3MzMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQZo35Damtpl81YdmcbhEuXKAr7xDcQzAy5n3ftAAhtBbu8EeGU4ynfSgLonckqX6J2uXLBppTNE3v2bt+Yf8MLoxIwEDAOBgorBgEEAYLECgECBAAwCwYJKoZIhvcNAQELA4IBAQG9LbiNPgs0sQYOHAJcg+lMk+HCsiWRlYVnbT4I/5lnqU907vY17XYAORd432bU3Nnhsbkvjz76kQJGXeNAF4DPANGGlz8JU+LNEVE2PWPGgEM0GXgB7mZN5Sinfy1AoOdO+3c3bfdJQuXlUxHbo+nDpxxKpzq9gr++RbokF1+0JBkMbaA/qLYL4WdhY5NvaOyMvYpO3sBxlzn6FcP67hlotGH1wU7qhCeh+uur7zDeAWVh7c4QtJOXHkLJQfV3Z7ZMvhkIA6jZJAX99hisABU/SSa5DtgX7AfsHwa04h69AAAWDUzSk3HgOXbUd1FaSOPdlVFkG2N2JllFHykyO3zO\";\n    private static final String ATTESTATION_CERT2 = \"MIICLzCCARmgAwIBAgIEQvUaTTALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCoxKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDExMjMzNTkzMDkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQphQ+PJYiZjZEVHtrx5QGE3/LE1+OytZPTwzrpWBKywji/3qmg22mwmVFl32PO269TxY+yVN4jbfVf5uX0EWJWoyYwJDAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuNDALBgkqhkiG9w0BAQsDggEBALSc3YwTRbLwXhePj/imdBOhWiqh6ssS2ONgp5tphJCHR5Agjg2VstLBRsJzyJnLgy7bGZ0QbPOyh/J0hsvgBfvjByXOu1AwCW+tcoJ+pfxESojDLDn8hrFph6eWZoCtBsWMDh6vMqPENeP6grEAECWx4fTpBL9Bm7F+0Rp/d1/l66g4IhF/ZvuRFhY+BUK94BfivuBHpEkMwxKENTas7VkxvlVstUvPqhPHGYOq7RdF1D/THsbNY8+tgCTgvTziEG+bfDeY6zIz5h7bxb1rpajNVTpUDWtVYL7/w44e1KCoErqdS+kEbmmkmm7KvDE8kuyg42Fmb5DTMsbY2jxMlMU=\";\n    private static final String ATTESTATION_CERT_WITH_TRANSPORTS = \"MIICIjCCAQygAwIBAgIEIHHwozALBgkqhkiG9w0BAQswDzENMAsGA1UEAxMEdGVzdDAeFw0xNTA4MTEwOTAwMzNaFw0xNjA4MTAwOTAwMzNaMCkxJzAlBgNVBAMTHll1YmljbyBVMkYgRUUgU2VyaWFsIDU0NDMzODA4MzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPdFG1pBjBBQVhLrD39Qg1vKjuR2kRdBZnwLI/zgzztQpf4ffpkrkB/3E0TXj5zg8gN9sgMkX48geBe+tBEpvMmjOzA5MCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4yMBMGCysGAQQBguUcAgEBBAQDAgQwMAsGCSqGSIb3DQEBCwOCAQEAb3YpnmHHduNuWEXlLqlnww9034ZeZaojhPAYSLR8d5NPk9gc0hkjQKmIaaBM7DsaHbcHMKpXoMGTQSC++NCZTcKvZ0Lt12mp5HRnM1NNBPol8Hte5fLmvW4tQ9EzLl4gkz7LSlORxTuwTbae1eQqNdxdeB+0ilMFCEUc+3NGCNM0RWd+sP5+gzMXBDQAI1Sc9XaPIg8t3du5JChAl1ifpu/uERZ2WQgtxeBDO6z1Xoa5qz4svf5oURjPZjxS0WUKht48Z2rIjk5lZzERSaY3RrX3UtrnZEIzCmInXOrcRPeAD4ZutpiwuHe62ABsjuMRnKbATbOUiLdknNyPYYQz2g==\";\n\n    @Test\n    public void testGetAttestation_x509extension_key() throws Exception {\n        MetadataService service = new MetadataService();\n\n        X509Certificate attestationCert = CertificateParser.parsePem(ATTESTATION_CERT);\n        Attestation attestation = service.getAttestation(attestationCert);\n\n        assertTrue(attestation.isTrusted());\n        assertEquals(\"Yubico\", attestation.getVendorProperties().get(\"name\"));\n        assertEquals(\"1.3.6.1.4.1.41482.1.2\", attestation.getDeviceProperties().get(\"deviceId\"));\n    }\n\n    @Test\n    public void testGetAttestation_x509extension_key_value() throws Exception {\n        MetadataService service = new MetadataService();\n\n        X509Certificate attestationCert = CertificateParser.parsePem(ATTESTATION_CERT2);\n        Attestation attestation = service.getAttestation(attestationCert);\n\n        assertTrue(attestation.isTrusted());\n        assertEquals(\"Yubico\", attestation.getVendorProperties().get(\"name\"));\n        assertEquals(\"1.3.6.1.4.1.41482.1.4\", attestation.getDeviceProperties().get(\"deviceId\"));\n    }\n\n    @Test\n    public void testGetTransportsFromCertificate() throws CertificateException {\n        MetadataService service = new MetadataService(mock(MetadataResolver.class));\n\n        X509Certificate attestationCert = CertificateParser.parsePem(ATTESTATION_CERT_WITH_TRANSPORTS);\n        Attestation attestation = service.getAttestation(attestationCert);\n\n        assertEquals(EnumSet.of(Transport.USB, Transport.NFC), attestation.getTransports());\n    }\n\n    @Test\n    public void testGetTransportsFromMetadata() throws CertificateException {\n        MetadataService service = new MetadataService();\n\n        X509Certificate attestationCert = CertificateParser.parsePem(ATTESTATION_CERT2);\n        Attestation attestation = service.getAttestation(attestationCert);\n\n        assertEquals(EnumSet.of(Transport.USB), attestation.getTransports());\n    }\n\n    @Test\n    public void getCachedAttestationReturnsCertIfPresent() throws Exception {\n        MetadataService service = new MetadataService();\n\n        final X509Certificate attestationCert = CertificateParser.parsePem(ATTESTATION_CERT);\n        final String certFingerprint = Hashing.sha1().hashBytes(attestationCert.getEncoded()).toString();\n\n        assertNull(service.getCachedAttestation(certFingerprint));\n\n        service.getAttestation(attestationCert);\n\n        Attestation attestation = service.getCachedAttestation(certFingerprint);\n\n        assertTrue(attestation.isTrusted());\n        assertEquals(\"Yubico\", attestation.getVendorProperties().get(\"name\"));\n        assertEquals(\"1.3.6.1.4.1.41482.1.2\", attestation.getDeviceProperties().get(\"deviceId\"));\n    }\n\n    @Test\n    public void getAttestationReturnsUnknownIfFingerprintEncodingFails() throws Exception {\n        MetadataService service = new MetadataService();\n\n        final X509Certificate attestationCert = mock(X509Certificate.class);\n        when(attestationCert.getEncoded()).thenThrow(new CertificateEncodingException(\"Forced failure\"));\n\n        Attestation attestation = service.getAttestation(attestationCert);\n\n        assertFalse(attestation.isTrusted());\n    }\n\n    @Test\n    public void deviceMatchesReturnsTrueIfNoSelectorsAreGiven() throws Exception {\n        MetadataResolver resolver = mock(MetadataResolver.class);\n        JsonNode device = mock(JsonNode.class);\n        MetadataObject metadata = mock(MetadataObject.class);\n        when(metadata.getDevices()).thenReturn(ImmutableList.of(device));\n        when(resolver.resolve(Matchers.<X509Certificate>any())).thenReturn(metadata);\n\n        MetadataService service = new MetadataService(resolver);\n\n        final X509Certificate attestationCert = CertificateParser.parsePem(ATTESTATION_CERT);\n\n        Attestation attestation = service.getAttestation(attestationCert);\n\n        verify(device, times(1)).get(\"transports\");\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/test/java/com/yubico/u2f/attestation/TransportTest.java",
    "content": "package com.yubico.u2f.attestation;\n\nimport org.junit.Test;\n\nimport java.util.EnumSet;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Created by Dain on 2016-02-18.\n */\npublic class TransportTest {\n\n    @Test\n    public void testParsingSingleValuesFromInt() {\n        assertEquals(EnumSet.of(Transport.BT_CLASSIC), Transport.fromInt(1));\n        assertEquals(EnumSet.of(Transport.BLE), Transport.fromInt(2));\n        assertEquals(EnumSet.of(Transport.USB), Transport.fromInt(4));\n        assertEquals(EnumSet.of(Transport.NFC), Transport.fromInt(8));\n    }\n\n    @Test\n    public void testParsingSetsFromInt() {\n        assertEquals(EnumSet.noneOf(Transport.class), Transport.fromInt(0));\n        assertEquals(EnumSet.of(Transport.BLE, Transport.NFC), Transport.fromInt(10));\n        assertEquals(EnumSet.of(Transport.USB, Transport.BT_CLASSIC), Transport.fromInt(5));\n        assertEquals(EnumSet.of(Transport.BT_CLASSIC, Transport.BLE, Transport.USB, Transport.NFC),\n                Transport.fromInt(15));\n    }\n\n    @Test\n    public void testEncodingSingleValuesToInt() {\n        assertEquals(1, Transport.toInt(Transport.BT_CLASSIC));\n        assertEquals(2, Transport.toInt(Transport.BLE));\n        assertEquals(4, Transport.toInt(Transport.USB));\n        assertEquals(8, Transport.toInt(Transport.NFC));\n    }\n\n    @Test\n    public void testEncodingSetsToInt() {\n        assertEquals(0, Transport.toInt());\n        assertEquals(10, Transport.toInt(Transport.BLE, Transport.NFC));\n        assertEquals(5, Transport.toInt(Transport.USB, Transport.BT_CLASSIC));\n        assertEquals(15, Transport.toInt(Transport.BT_CLASSIC, Transport.BLE, Transport.USB, Transport.NFC));\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/test/java/com/yubico/u2f/attestation/matchers/FingerprintMatcherTest.java",
    "content": "package com.yubico.u2f.attestation.matchers;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.node.ArrayNode;\nimport com.fasterxml.jackson.databind.node.BooleanNode;\nimport com.fasterxml.jackson.databind.node.JsonNodeFactory;\nimport com.fasterxml.jackson.databind.node.TextNode;\nimport com.google.common.hash.Hashing;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class FingerprintMatcherTest {\n\n    private static final String ATTESTATION_CERT = \"MIICGzCCAQWgAwIBAgIEdaP2dTALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCoxKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NzM2Nzk3MzMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQZo35Damtpl81YdmcbhEuXKAr7xDcQzAy5n3ftAAhtBbu8EeGU4ynfSgLonckqX6J2uXLBppTNE3v2bt+Yf8MLoxIwEDAOBgorBgEEAYLECgECBAAwCwYJKoZIhvcNAQELA4IBAQG9LbiNPgs0sQYOHAJcg+lMk+HCsiWRlYVnbT4I/5lnqU907vY17XYAORd432bU3Nnhsbkvjz76kQJGXeNAF4DPANGGlz8JU+LNEVE2PWPGgEM0GXgB7mZN5Sinfy1AoOdO+3c3bfdJQuXlUxHbo+nDpxxKpzq9gr++RbokF1+0JBkMbaA/qLYL4WdhY5NvaOyMvYpO3sBxlzn6FcP67hlotGH1wU7qhCeh+uur7zDeAWVh7c4QtJOXHkLJQfV3Z7ZMvhkIA6jZJAX99hisABU/SSa5DtgX7AfsHwa04h69AAAWDUzSk3HgOXbUd1FaSOPdlVFkG2N2JllFHykyO3zO\";\n\n    @Test\n    public void matchesIsFalseForNonArrayFingerprints() {\n        JsonNode parameters = mock(JsonNode.class);\n        when(parameters.get(\"fingerprints\")).thenReturn(BooleanNode.TRUE);\n\n        assertFalse(new FingerprintMatcher().matches(mock(X509Certificate.class), parameters));\n    }\n\n    @Test\n    public void matchesIsFalseIfNoFingerprintMatches() throws CertificateException {\n        final X509Certificate cert = CertificateParser.parsePem(ATTESTATION_CERT);\n        final String fingerprint = Hashing.sha1().hashBytes(cert.getEncoded()).toString().toLowerCase();\n\n        ArrayNode fingerprints = new ArrayNode(JsonNodeFactory.instance);\n        fingerprints.add(new TextNode(\"foo\"));\n        fingerprints.add(new TextNode(\"bar\"));\n\n        JsonNode parameters = mock(JsonNode.class);\n        when(parameters.get(\"fingerprints\")).thenReturn(fingerprints);\n\n        assertFalse(new FingerprintMatcher().matches(cert, parameters));\n    }\n\n    @Test\n    public void matchesIsTrueIfSomeFingerprintMatches() throws CertificateException {\n        final X509Certificate cert = CertificateParser.parsePem(ATTESTATION_CERT);\n        final String fingerprint = Hashing.sha1().hashBytes(cert.getEncoded()).toString().toLowerCase();\n\n        ArrayNode fingerprints = new ArrayNode(JsonNodeFactory.instance);\n        fingerprints.add(new TextNode(\"foo\"));\n        fingerprints.add(new TextNode(fingerprint));\n\n        JsonNode parameters = mock(JsonNode.class);\n        when(parameters.get(\"fingerprints\")).thenReturn(fingerprints);\n\n        assertTrue(new FingerprintMatcher().matches(cert, parameters));\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-attestation/src/test/java/com/yubico/u2f/attestation/resolvers/SimpleResolverTest.java",
    "content": "package com.yubico.u2f.attestation.resolvers;\n\nimport com.yubico.u2f.attestation.MetadataObject;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.NoSuchProviderException;\nimport java.security.Principal;\nimport java.security.PublicKey;\nimport java.security.SignatureException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport org.junit.Test;\nimport org.mockito.Matchers;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class SimpleResolverTest {\n\n    private static final String METADATA_JSON = \"{\\\"identifier\\\":\\\"foobar\\\",\\\"version\\\":1,\\\"trustedCertificates\\\":[\\\"-----BEGIN CERTIFICATE-----\\\\nMIIDHjCCAgagAwIBAgIEG1BT9zANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNZ\\\\ndWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAw\\\\nMDBaGA8yMDUwMDkwNDAwMDAwMFowLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290\\\\nIENBIFNlcmlhbCA0NTcyMDA2MzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\\\\nAoIBAQC/jwYuhBVlqaiYWEMsrWFisgJ+PtM91eSrpI4TK7U53mwCIawSDHy8vUmk\\\\n5N2KAj9abvT9NP5SMS1hQi3usxoYGonXQgfO6ZXyUA9a+KAkqdFnBnlyugSeCOep\\\\n8EdZFfsaRFtMjkwz5Gcz2Py4vIYvCdMHPtwaz0bVuzneueIEz6TnQjE63Rdt2zbw\\\\nnebwTG5ZybeWSwbzy+BJ34ZHcUhPAY89yJQXuE0IzMZFcEBbPNRbWECRKgjq//qT\\\\n9nmDOFVlSRCt2wiqPSzluwn+v+suQEBsUjTGMEd25tKXXTkNW21wIWbxeSyUoTXw\\\\nLvGS6xlwQSgNpk2qXYwf8iXg7VWZAgMBAAGjQjBAMB0GA1UdDgQWBBQgIvz0bNGJ\\\\nhjgpToksyKpP9xv9oDAPBgNVHRMECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAN\\\\nBgkqhkiG9w0BAQsFAAOCAQEAjvjuOMDSa+JXFCLyBKsycXtBVZsJ4Ue3LbaEsPY4\\\\nMYN/hIQ5ZM5p7EjfcnMG4CtYkNsfNHc0AhBLdq45rnT87q/6O3vUEtNMafbhU6kt\\\\nhX7Y+9XFN9NpmYxr+ekVY5xOxi8h9JDIgoMP4VB1uS0aunL1IGqrNooL9mmFnL2k\\\\nLVVee6/VR6C5+KSTCMCWppMuJIZII2v9o4dkoZ8Y7QRjQlLfYzd3qGtKbw7xaF1U\\\\nsG/5xUb/Btwb2X2g4InpiB/yt/3CpQXpiWX/K4mBvUKiGn05ZsqeY1gx4g0xLBqc\\\\nU9psmyPzK+Vsgw2jeRQ5JlKDyqE0hebfC1tvFu0CCrJFcw==\\\\n-----END CERTIFICATE-----\\\"],\\\"vendorInfo\\\":{\\\"name\\\":\\\"Yubico\\\",\\\"url\\\":\\\"https://yubico.com\\\",\\\"imageUrl\\\":\\\"https://developers.yubico.com/U2F/Images/yubico.png\\\"},\\\"devices\\\":[{\\\"displayName\\\":\\\"YubiKey NEO/NEO-n\\\",\\\"deviceId\\\":\\\"1.3.6.1.4.1.41482.1.2\\\",\\\"deviceUrl\\\":\\\"https://www.yubico.com/products/yubikey-hardware/yubikey-neo/\\\",\\\"imageUrl\\\":\\\"https://developers.yubico.com/U2F/Images/NEO.png\\\",\\\"selectors\\\":[{\\\"type\\\":\\\"x509Extension\\\",\\\"parameters\\\":{\\\"key\\\":\\\"1.3.6.1.4.1.41482.1.2\\\"}}]}]  }\";\n    private static final String ATTESTATION_CERT = \"MIICGzCCAQWgAwIBAgIEdaP2dTALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCoxKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NzM2Nzk3MzMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQZo35Damtpl81YdmcbhEuXKAr7xDcQzAy5n3ftAAhtBbu8EeGU4ynfSgLonckqX6J2uXLBppTNE3v2bt+Yf8MLoxIwEDAOBgorBgEEAYLECgECBAAwCwYJKoZIhvcNAQELA4IBAQG9LbiNPgs0sQYOHAJcg+lMk+HCsiWRlYVnbT4I/5lnqU907vY17XYAORd432bU3Nnhsbkvjz76kQJGXeNAF4DPANGGlz8JU+LNEVE2PWPGgEM0GXgB7mZN5Sinfy1AoOdO+3c3bfdJQuXlUxHbo+nDpxxKpzq9gr++RbokF1+0JBkMbaA/qLYL4WdhY5NvaOyMvYpO3sBxlzn6FcP67hlotGH1wU7qhCeh+uur7zDeAWVh7c4QtJOXHkLJQfV3Z7ZMvhkIA6jZJAX99hisABU/SSa5DtgX7AfsHwa04h69AAAWDUzSk3HgOXbUd1FaSOPdlVFkG2N2JllFHykyO3zO\";\n\n    @Test\n    public void testResolve() throws Exception {\n        SimpleResolver resolver = new SimpleResolver();\n\n        resolver.addMetadata(METADATA_JSON);\n        X509Certificate certificate = CertificateParser.parseDer(ATTESTATION_CERT);\n\n        MetadataObject metadata = resolver.resolve(certificate);\n\n        assertNotNull(metadata);\n        assertEquals(\"foobar\", metadata.getIdentifier());\n    }\n\n    @Test\n    public void resolveReturnsNullOnUntrustedSignature() throws Exception {\n        SimpleResolver resolver = new SimpleResolver();\n        resolver.addMetadata(METADATA_JSON);\n\n        X509Certificate cert = mock(X509Certificate.class);\n        doThrow(\n            new CertificateException(\"Forced failure\"),\n            new NoSuchAlgorithmException(\"Forced failure\"),\n            new InvalidKeyException(\"Forced failure\"),\n            new NoSuchProviderException(\"Forced failure\"),\n            new SignatureException(\"Forced failure\")\n        ).when(cert).verify(Matchers.<PublicKey>any());\n        Principal issuerDN = mock(Principal.class);\n        when(issuerDN.getName()).thenReturn(\"CN=Yubico U2F Root CA Serial 457200631\");\n        when(cert.getIssuerDN()).thenReturn(issuerDN);\n\n        assertNull(resolver.resolve(cert));\n        assertNull(resolver.resolve(cert));\n        assertNull(resolver.resolve(cert));\n        assertNull(resolver.resolve(cert));\n        assertNull(resolver.resolve(cert));\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/build.gradle",
    "content": "description = 'U2F core'\n\ndependencies {\n\n  compile(\n    [group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version:'1.54'],\n    [group: 'com.google.guava', name: 'guava', version:'19.0'],\n    [group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version:'2.11.0'],\n  )\n\n}\n"
  },
  {
    "path": "u2flib-server-core/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "u2flib-server-core/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "u2flib-server-core/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "u2flib-server-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>u2flib-server-parent</artifactId>\n        <groupId>com.yubico</groupId>\n        <version>0.19.12</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>u2flib-server-core</artifactId>\n    <name>U2F core</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>bcpkix-jdk15on</artifactId>\n            <version>1.54</version>\n        </dependency>\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <version>[24.1.1,30)</version>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n            <version>2.11.0</version>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/AppId.java",
    "content": "package com.yubico.u2f;\n\nimport com.google.common.net.InetAddresses;\nimport com.yubico.u2f.exceptions.U2fBadConfigurationException;\n\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class AppId {\n\n    public static final String DISABLE_INSTRUCTIONS = \"To disable this check, instantiate the U2F object using U2F.withoutAppIdValidation()\";\n\n    /**\n     * Throws {@link U2fBadConfigurationException} if the given App ID is found to be incompatible with the U2F specification or any major\n     * U2F Client implementation.\n     *\n     * @param appId the App ID to be validated\n     */\n    public static void checkIsValid(String appId) throws U2fBadConfigurationException {\n        if(!appId.contains(\":\")) {\n            throw new U2fBadConfigurationException(\"App ID does not look like a valid facet or URL. Web facets must start with 'https://'. \" + DISABLE_INSTRUCTIONS);\n        }\n        if(appId.startsWith(\"http:\")) {\n            throw new U2fBadConfigurationException(\"HTTP is not supported for App IDs (by Chrome). Use HTTPS instead. \" + DISABLE_INSTRUCTIONS);\n        }\n        if(appId.startsWith(\"https://\")) {\n            URI url = checkValidUrl(appId);\n            checkPathIsNotSlash(url);\n            checkNotIpAddress(url);\n        }\n    }\n\n    private static void checkPathIsNotSlash(URI url) throws U2fBadConfigurationException {\n        if(\"/\".equals(url.getPath())) {\n            throw new U2fBadConfigurationException(\"The path of the URL set as App ID is '/'. This is probably not what you want -- remove the trailing slash of the App ID URL. \" + DISABLE_INSTRUCTIONS);\n        }\n    }\n\n    private static URI checkValidUrl(String appId) throws U2fBadConfigurationException {\n        URI url = null;\n        try {\n            url = new URI(appId);\n        } catch (URISyntaxException e) {\n            throw new U2fBadConfigurationException(\"App ID looks like a HTTPS URL, but has syntax errors.\", e);\n        }\n        return url;\n    }\n\n    private static void checkNotIpAddress(URI url) throws U2fBadConfigurationException {\n        if (InetAddresses.isInetAddress(url.getAuthority()) || (url.getHost() != null && InetAddresses.isInetAddress(url.getHost()))) {\n            throw new U2fBadConfigurationException(\"App ID must not be an IP-address, since it is not supported (by Chrome). Use a host name instead. \" + DISABLE_INSTRUCTIONS);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/U2F.java",
    "content": "/* Copyright 2014 Yubico */\n\npackage com.yubico.u2f;\n\nimport com.google.common.base.Objects;\nimport com.google.common.base.Predicate;\nimport com.google.common.collect.Iterables;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.ChallengeGenerator;\nimport com.yubico.u2f.crypto.RandomChallengeGenerator;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.*;\nimport com.yubico.u2f.exceptions.DeviceCompromisedException;\nimport com.yubico.u2f.exceptions.InvalidDeviceCounterException;\nimport com.yubico.u2f.exceptions.NoEligibleDevicesException;\nimport com.yubico.u2f.exceptions.U2fAuthenticationException;\nimport com.yubico.u2f.exceptions.U2fBadConfigurationException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport com.yubico.u2f.exceptions.U2fRegistrationException;\nimport java.util.Set;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2F {\n\n    private final ChallengeGenerator challengeGenerator;\n    private final U2fPrimitives primitives;\n    private final boolean validateAppId;\n\n\n\n    public U2F() {\n        this(true);\n    }\n\n    public static U2F withoutAppIdValidation() {\n        return new U2F(false);\n    }\n\n    private U2F(boolean validateAppId) {\n        this.challengeGenerator = new RandomChallengeGenerator();\n        primitives = new U2fPrimitives(new BouncyCastleCrypto(), challengeGenerator);\n        this.validateAppId = validateAppId;\n    }\n\n    /**\n     * Initiates a high-level registration of a device, given a set of already registered devices.\n     *\n     * @param appId   the U2F AppID. Set this to the Web Origin of the login page, unless you need to\n     *                support logging in from multiple Web Origins.\n     * @param devices the devices currently registered to the user.\n     * @return a RegisterRequestData, which should be sent to the client and temporarily saved by the server.\n     */\n    public RegisterRequestData startRegistration(String appId, Iterable<? extends DeviceRegistration> devices) throws U2fBadConfigurationException {\n        if(validateAppId) {\n            AppId.checkIsValid(appId);\n        }\n        return new RegisterRequestData(appId, devices, primitives, challengeGenerator);\n    }\n\n    public SignRequestData startSignature(String appId, Iterable<? extends DeviceRegistration> devices) throws U2fBadConfigurationException, NoEligibleDevicesException {\n        if(validateAppId) {\n            AppId.checkIsValid(appId);\n        }\n        return new SignRequestData(appId, devices, primitives, challengeGenerator);\n    }\n\n    /***\n     *\n     */\n    public DeviceRegistration finishRegistration(RegisterRequestData registerRequestData, RegisterResponse response) throws U2fRegistrationException {\n        return finishRegistration(registerRequestData, response, null);\n    }\n\n    /**\n     * Finishes a previously started high-level registration.\n     *\n     * @param registerRequestData the RegisterResponseData created by calling startRegistration\n     * @param response            The response from the device/client.\n     * @param facets              A list of valid facets to verify against.\n     * @return a DeviceRegistration object, holding information about the registered device. Servers should\n     * persist this.\n     * @throws U2fRegistrationException if parsing or verification of the response fails\n     */\n    public DeviceRegistration finishRegistration(RegisterRequestData registerRequestData, RegisterResponse response, Set<String> facets) throws U2fRegistrationException {\n        return primitives.finishRegistration(registerRequestData.getRegisterRequest(response), response, facets);\n    }\n\n    /**\n     * @see U2F#finishSignature(SignRequestData, SignResponse, Iterable, java.util.Set)\n     */\n    public DeviceRegistration finishSignature(SignRequestData signRequestData, SignResponse response, Iterable<? extends DeviceRegistration> devices) throws U2fAuthenticationException {\n        return finishSignature(signRequestData, response, devices, null);\n    }\n\n    /**\n     * Finishes a previously started high-level signing action.\n     *\n     * @param signRequestData the SignRequestData created by calling startSignature\n     * @param response                the response from the device/client.\n     * @param devices                 the devices currently registered to the user.\n     * @param facets                  A list of valid facets to verify against.\n     * @return The (updated) DeviceRegistration that signed the challenge.\n     *\n     * @throws InvalidDeviceCounterException if the response is valid but the signature counter has not increased.\n     * @throws DeviceCompromisedException if the device with the response's key handle is marked as compromised.\n     * @throws U2fAuthenticationException if parsing or verification of the response fails.\n     */\n    public DeviceRegistration finishSignature(SignRequestData signRequestData, SignResponse response, Iterable<? extends DeviceRegistration> devices, Set<String> facets) throws U2fAuthenticationException {\n        try {\n            final SignRequest request = signRequestData.getSignRequest(response);\n            DeviceRegistration device = Iterables.find(devices, new Predicate<DeviceRegistration>() {\n                @Override\n                public boolean apply(DeviceRegistration input) {\n                    return Objects.equal(request.getKeyHandle(), input.getKeyHandle());\n                }\n\n                @Override\n                public boolean test(DeviceRegistration input) {\n                    return apply(input);\n                }\n            });\n\n            if (device.isCompromised()) {\n                throw new DeviceCompromisedException(device, \"The device is marked as possibly compromised, and cannot make trusted signatures.\");\n            }\n\n            primitives.finishSignature(request, response, device, facets);\n            return device;\n        } catch (U2fBadInputException e) {\n            throw new U2fAuthenticationException(\"finishSignature failed\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/U2fPrimitives.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f;\n\nimport com.google.common.base.Optional;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.ChallengeGenerator;\nimport com.yubico.u2f.crypto.Crypto;\nimport com.yubico.u2f.crypto.RandomChallengeGenerator;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.*;\nimport com.yubico.u2f.data.messages.key.RawSignResponse;\nimport com.yubico.u2f.data.messages.key.RawRegisterResponse;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.DeviceCompromisedException;\nimport com.yubico.u2f.exceptions.InvalidDeviceCounterException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport com.yubico.u2f.exceptions.U2fAuthenticationException;\nimport com.yubico.u2f.exceptions.U2fRegistrationException;\nimport java.util.Set;\n\nimport static com.google.common.base.Preconditions.checkArgument;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2fPrimitives {\n\n    private static final String SIGN_TYPE = \"navigator.id.getAssertion\";\n    private static final String REGISTER_TYPE = \"navigator.id.finishEnrollment\";\n    public static final String U2F_VERSION = \"U2F_V2\";\n\n    private final Crypto crypto;\n    private final ChallengeGenerator challengeGenerator;\n\n    public U2fPrimitives(Crypto crypto, ChallengeGenerator challengeGenerator) {\n        this.crypto = crypto;\n        this.challengeGenerator = challengeGenerator;\n    }\n\n    public U2fPrimitives() {\n        this(new BouncyCastleCrypto(), new RandomChallengeGenerator());\n    }\n\n    /**\n     * @see U2fPrimitives#startRegistration(String, byte[])\n     */\n    public RegisterRequest startRegistration(String appId) {\n        return startRegistration(appId, challengeGenerator.generateChallenge());\n    }\n\n    /**\n     * Initiates the registration of a device.\n     *\n     * @param appId     the U2F AppID. Set this to the Web Origin of the login page, unless you need to\n     *                  support logging in from multiple Web Origins.\n     * @param challenge the challenge to use\n     * @return a RegisterRequest, which should be sent to the client and temporary saved by the\n     * server.\n     */\n    public RegisterRequest startRegistration(String appId, byte[] challenge) {\n        return new RegisterRequest(U2fB64Encoding.encode(challenge), appId);\n    }\n\n    /**\n     * @see U2fPrimitives#finishRegistration(com.yubico.u2f.data.messages.RegisterRequest, com.yubico.u2f.data.messages.RegisterResponse, java.util.Set)\n     */\n    public DeviceRegistration finishRegistration(RegisterRequest registerRequest, RegisterResponse response) throws U2fRegistrationException {\n        return finishRegistration(registerRequest, response, null);\n    }\n\n    /**\n     * Finishes a previously started registration.\n     *\n     * @param registerRequest\n     * @param response        the response from the device/client.\n     * @return a DeviceRegistration object, holding information about the registered device. Servers should\n     * persist this.\n     */\n    public DeviceRegistration finishRegistration(RegisterRequest registerRequest,\n                                                 RegisterResponse response,\n                                                 Set<String> facets) throws U2fRegistrationException {\n        try {\n            ClientData clientData = response.getClientData();\n            clientData.checkContent(REGISTER_TYPE, registerRequest.getChallenge(), Optional.fromNullable(facets));\n\n            RawRegisterResponse rawRegisterResponse = RawRegisterResponse.fromBase64(response.getRegistrationData(), crypto);\n            rawRegisterResponse.checkSignature(registerRequest.getAppId(), clientData.asJson());\n            return rawRegisterResponse.createDevice();\n        } catch (U2fBadInputException e) {\n            throw new U2fRegistrationException(\"finishRegistration failed\", e);\n        }\n    }\n\n    /**\n     * Initiates the signing process.\n     *\n     * @see U2fPrimitives#startSignature(String, com.yubico.u2f.data.DeviceRegistration, byte[])\n     */\n    public SignRequest startSignature(String appId, DeviceRegistration deviceRegistration) {\n        return startSignature(appId, deviceRegistration, challengeGenerator.generateChallenge());\n    }\n\n    /**\n     * Initiates the signing process.\n     *\n     * @param appId              the U2F AppID. Set this to the Web Origin of the login page, unless you need to\n     *                           support logging in from multiple Web Origins.\n     * @param deviceRegistration the DeviceRegistration for which to initiate signing.\n     * @param challenge          the challenge to use\n     * @return an SignRequest which should be sent to the client and temporary saved by\n     * the server.\n     */\n    public SignRequest startSignature(String appId, DeviceRegistration deviceRegistration, byte[] challenge) {\n        checkArgument(!deviceRegistration.isCompromised(), \"Device has been marked as compromised, cannot sign.\");\n\n        return SignRequest.builder()\n            .appId(appId)\n            .challenge(U2fB64Encoding.encode(challenge))\n            .keyHandle(deviceRegistration.getKeyHandle())\n            .build();\n    }\n\n    /**\n     * @see U2fPrimitives#finishSignature(SignRequest, SignResponse, com.yubico.u2f.data.DeviceRegistration, java.util.Set)\n     */\n    public void finishSignature(SignRequest signRequest,\n                                SignResponse response,\n                                DeviceRegistration deviceRegistration) throws U2fAuthenticationException {\n        finishSignature(signRequest, response, deviceRegistration, null);\n    }\n\n    /**\n     * Finishes a previously started signature.\n     *\n     * @param signRequest\n     * @param response            the response from the device/client.\n     */\n    public void finishSignature(SignRequest signRequest,\n                                SignResponse response,\n                                DeviceRegistration deviceRegistration,\n                                Set<String> facets) throws U2fAuthenticationException {\n        checkArgument(!deviceRegistration.isCompromised(), \"Device has been marked as compromised, cannot sign.\");\n        checkArgument(signRequest.getKeyHandle().equals(deviceRegistration.getKeyHandle()), \"Wrong DeviceRegistration for the given SignRequest\");\n        if (!deviceRegistration.getKeyHandle().equals(response.getKeyHandle())) {\n            throw new U2fAuthenticationException(\"KeyHandle of SignResponse does not match\");\n        }\n\n        try {\n            ClientData clientData = response.getClientData();\n            clientData.checkContent(SIGN_TYPE, signRequest.getChallenge(), Optional.fromNullable(facets));\n\n            RawSignResponse rawSignResponse = RawSignResponse.fromBase64(\n                response.getSignatureData(), crypto\n            );\n            rawSignResponse.checkSignature(\n                signRequest.getAppId(),\n                clientData.asJson(),\n                U2fB64Encoding.decode(deviceRegistration.getPublicKey())\n            );\n            rawSignResponse.checkUserPresence();\n            deviceRegistration.checkAndUpdateCounter(rawSignResponse.getCounter());\n        } catch (U2fBadInputException e) {\n            throw new U2fAuthenticationException(\"finishSignature failed\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/crypto/BouncyCastleCrypto.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.crypto;\n\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport org.bouncycastle.asn1.sec.SECNamedCurves;\nimport org.bouncycastle.asn1.x9.X9ECParameters;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.jce.spec.ECParameterSpec;\nimport org.bouncycastle.jce.spec.ECPublicKeySpec;\nimport org.bouncycastle.math.ec.ECPoint;\n\nimport java.security.*;\nimport java.security.cert.X509Certificate;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class BouncyCastleCrypto implements Crypto {\n\n    private static final Provider provider = new BouncyCastleProvider();\n\n    public Provider getProvider() {\n        return provider;\n    }\n\n    @Override\n    public void checkSignature(X509Certificate attestationCertificate, byte[] signedBytes, byte[] signature)\n            throws U2fBadInputException {\n        checkSignature(attestationCertificate.getPublicKey(), signedBytes, signature);\n    }\n\n    @Override\n    public void checkSignature(PublicKey publicKey, byte[] signedBytes, byte[] signature)\n            throws U2fBadInputException {\n        try {\n            Signature ecdsaSignature = Signature.getInstance(\"SHA256withECDSA\", provider);\n            ecdsaSignature.initVerify(publicKey);\n            ecdsaSignature.update(signedBytes);\n            if (!ecdsaSignature.verify(signature)) {\n                throw new U2fBadInputException(String.format(\n                    \"Signature is invalid. Public key: %s, signed data: %s , signature: %s\",\n                    publicKey,\n                    U2fB64Encoding.encode(signedBytes),\n                    U2fB64Encoding.encode(signature)\n                ));\n            }\n        } catch (GeneralSecurityException e) {\n            throw new RuntimeException(\n                String.format(\n                    \"Failed to verify signature. This could be a problem with your JVM environment, or a bug in u2flib-server-core. Public key: %s, signed data: %s , signature: %s\",\n                    publicKey,\n                    U2fB64Encoding.encode(signedBytes),\n                    U2fB64Encoding.encode(signature)\n                ),\n                e\n            );\n        }\n    }\n\n    @Override\n    public PublicKey decodePublicKey(byte[] encodedPublicKey) throws U2fBadInputException {\n        try {\n            X9ECParameters curve = SECNamedCurves.getByName(\"secp256r1\");\n            ECPoint point;\n            try {\n                point = curve.getCurve().decodePoint(encodedPublicKey);\n            } catch (RuntimeException e) {\n                throw new U2fBadInputException(\"Could not parse user public key\", e);\n            }\n\n            return KeyFactory.getInstance(\"ECDSA\", provider).generatePublic(\n                    new ECPublicKeySpec(point,\n                            new ECParameterSpec(\n                                    curve.getCurve(),\n                                    curve.getG(),\n                                    curve.getN(),\n                                    curve.getH()\n                            )\n                    )\n            );\n        } catch (GeneralSecurityException e) { //This should not happen\n            throw new RuntimeException(\n                \"Failed to decode public key: \" + U2fB64Encoding.encode(encodedPublicKey),\n                e\n            );\n        }\n    }\n\n    @Override\n    public byte[] hash(byte[] bytes) {\n        try {\n            return MessageDigest.getInstance(\"SHA-256\", provider).digest(bytes);\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public byte[] hash(String str) {\n        return hash(str.getBytes());\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/crypto/ChallengeGenerator.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.crypto;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic interface ChallengeGenerator {\n\n    byte[] generateChallenge();\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/crypto/Crypto.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.crypto;\n\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.security.PublicKey;\nimport java.security.cert.X509Certificate;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic interface Crypto {\n    void checkSignature(X509Certificate attestationCertificate, byte[] signedBytes,\n                        byte[] signature) throws U2fBadInputException;\n\n    void checkSignature(PublicKey publicKey, byte[] signedBytes,\n                        byte[] signature) throws U2fBadInputException;\n\n    PublicKey decodePublicKey(byte[] encodedPublicKey) throws U2fBadInputException;\n\n    byte[] hash(byte[] bytes);\n\n    byte[] hash(String str);\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/crypto/RandomChallengeGenerator.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.crypto;\n\nimport java.security.SecureRandom;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class RandomChallengeGenerator implements ChallengeGenerator {\n\n    private final SecureRandom random = new SecureRandom();\n\n    @Override\n    public byte[] generateChallenge() {\n        byte[] randomBytes = new byte[32];\n        random.nextBytes(randomBytes);\n        return randomBytes;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/DeviceRegistration.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.google.common.base.MoreObjects;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.InvalidDeviceCounterException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.io.Serializable;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport lombok.EqualsAndHashCode;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@EqualsAndHashCode(of = { \"keyHandle\", \"publicKey\", \"attestationCert\" })\npublic class DeviceRegistration extends JsonSerializable implements Serializable {\n    private static final long serialVersionUID = -142942195464329902L;\n    public static final long INITIAL_COUNTER_VALUE = -1;\n\n    @JsonProperty\n    private final String keyHandle;\n    @JsonProperty\n    private final String publicKey;\n    @JsonProperty\n    private final String attestationCert;\n    @JsonProperty\n    private long counter;\n    @JsonProperty\n    private boolean compromised;\n\n    @JsonCreator\n    public DeviceRegistration(@JsonProperty(\"keyHandle\") String keyHandle, @JsonProperty(\"publicKey\") String publicKey, @JsonProperty(\"attestationCert\") String attestationCert, @JsonProperty(\"counter\") long counter, @JsonProperty(\"compromised\") boolean compromised) {\n        this.keyHandle = keyHandle;\n        this.publicKey = publicKey;\n        this.attestationCert = attestationCert;\n        this.counter = counter;\n        this.compromised = compromised;\n    }\n\n    public DeviceRegistration(String keyHandle, String publicKey, X509Certificate attestationCert, long counter)\n            throws U2fBadInputException {\n        this.keyHandle = keyHandle;\n        this.publicKey = publicKey;\n        try {\n            this.attestationCert = U2fB64Encoding.encode(attestationCert.getEncoded());\n        } catch (CertificateEncodingException e) {\n            throw new U2fBadInputException(\"Malformed attestation certificate\", e);\n        }\n        this.counter = counter;\n    }\n\n    public String getKeyHandle() {\n        return keyHandle;\n    }\n\n    public String getPublicKey() {\n        return publicKey;\n    }\n\n    @JsonIgnore\n    public X509Certificate getAttestationCertificate() throws U2fBadInputException, CertificateException {\n        if (attestationCert == null) {\n            return null;\n        } else {\n            return CertificateParser.parseDer(U2fB64Encoding.decode(attestationCert));\n        }\n    }\n\n    public long getCounter() {\n        return counter;\n    }\n\n    public boolean isCompromised() {\n        return compromised;\n    }\n\n    public void markCompromised() {\n        compromised = true;\n    }\n\n    @Override\n    public String toString() {\n        X509Certificate certificate = null;\n        try {\n            certificate = getAttestationCertificate();\n        } catch (CertificateException e) {\n            // do nothing\n        } catch (U2fBadInputException e) {\n            // do nothing\n        }\n        return MoreObjects.toStringHelper(this)\n                .omitNullValues()\n                .add(\"Key handle\", keyHandle)\n                .add(\"Public key\", publicKey)\n                .add(\"Counter\", counter)\n                .add(\"Attestation certificate\", certificate)\n                .toString();\n    }\n\n    public static DeviceRegistration fromJson(String json) throws U2fBadInputException {\n        return fromJson(json, DeviceRegistration.class);\n    }\n\n    @Override\n    public String toJson() {\n        try {\n            return OBJECT_MAPPER.writeValueAsString(new DeviceWithoutCertificate(keyHandle, publicKey, counter, compromised));\n        } catch (JsonProcessingException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    public String toJsonWithAttestationCert() {\n        return super.toJson();\n    }\n\n    public void checkAndUpdateCounter(long clientCounter) throws InvalidDeviceCounterException {\n        if (clientCounter <= getCounter()) {\n            markCompromised();\n            throw new InvalidDeviceCounterException(this);\n        }\n        counter = clientCounter;\n    }\n\n    private static class DeviceWithoutCertificate {\n        @JsonProperty\n        private final String keyHandle;\n        @JsonProperty\n        private final String publicKey;\n        @JsonProperty\n        private final long counter;\n        @JsonProperty\n        private final boolean compromised;\n\n        private DeviceWithoutCertificate(String keyHandle, String publicKey, long counter, boolean compromised) {\n            this.keyHandle = keyHandle;\n            this.publicKey = publicKey;\n            this.counter = counter;\n            this.compromised = compromised;\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/ClientData.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.base.Optional;\nimport com.google.common.collect.ImmutableSet;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Set;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class ClientData {\n\n    private static final String TYPE_PARAM = \"typ\";\n    private static final String CHALLENGE_PARAM = \"challenge\";\n    private static final String ORIGIN_PARAM = \"origin\";\n\n    private final String type;\n    private final String challenge;\n    private final String origin;\n    private final String rawClientData;\n\n    public String asJson() {\n        return rawClientData;\n    }\n\n    public ClientData(String clientData) throws U2fBadInputException {\n        rawClientData = new String(U2fB64Encoding.decode(clientData));\n        try {\n            JsonNode data = new ObjectMapper().readTree(rawClientData);\n            type = getString(data, TYPE_PARAM);\n            challenge = getString(data, CHALLENGE_PARAM);\n            origin = getString(data, ORIGIN_PARAM);\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Malformed ClientData\", e);\n        }\n    }\n\n    @Override\n    public String toString() {\n        return rawClientData;\n    }\n\n    public String getChallenge() {\n        return challenge;\n    }\n\n    private static String getString(JsonNode data, String key) throws U2fBadInputException {\n        JsonNode node = data.get(key);\n        if (node == null) {\n            throw new U2fBadInputException(\"Bad clientData: missing field \" + key);\n        }\n        if (!node.isTextual()) {\n            throw new U2fBadInputException(\"Bad clientData: field \" + key + \" not a string\");\n        }\n        return node.asText();\n    }\n\n    public void checkContent(String type, String challenge, Optional<Set<String>> facets) throws U2fBadInputException {\n        if (!type.equals(this.type)) {\n            throw new U2fBadInputException(\"Bad clientData: wrong type \" + this.type);\n        }\n        if (!challenge.equals(this.challenge)) {\n            throw new U2fBadInputException(\"Bad clientData: wrong challenge\");\n        }\n        if (facets.isPresent()) {\n            Set<String> allowedFacets = canonicalizeOrigins(facets.get());\n            String canonicalOrigin;\n            try {\n                canonicalOrigin = canonicalizeOrigin(origin);\n            } catch (RuntimeException e) {\n                throw new U2fBadInputException(\"Bad clientData: Malformed origin\", e);\n            }\n            verifyOrigin(canonicalOrigin, allowedFacets);\n        }\n    }\n\n    private static void verifyOrigin(String origin, Set<String> allowedOrigins) throws U2fBadInputException {\n        if (!allowedOrigins.contains(origin)) {\n            throw new U2fBadInputException(origin +\n                    \" is not a recognized facet for this application\");\n        }\n    }\n\n    public static Set<String> canonicalizeOrigins(Set<String> origins) {\n        ImmutableSet.Builder<String> result = ImmutableSet.builder();\n        for (String origin : origins) {\n            result.add(canonicalizeOrigin(origin));\n        }\n        return result.build();\n    }\n\n    public static String canonicalizeOrigin(String url) {\n        try {\n            URI uri = new URI(url);\n            if (uri.getAuthority() == null) {\n                return url;\n            }\n            return uri.getScheme() + \"://\" + uri.getAuthority();\n        } catch (URISyntaxException e) {\n            throw new IllegalArgumentException(\"specified bad origin\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/RegisterRequest.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.json.Persistable;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.NonNull;\nimport lombok.Value;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@Value\n@Builder\n@AllArgsConstructor\n@JsonDeserialize(builder = RegisterRequest.RegisterRequestBuilder.class)\npublic class RegisterRequest extends JsonSerializable implements Persistable {\n\n    private static final long serialVersionUID = 24349091760814188L;\n\n    /**\n     * Version of the protocol that the to-be-registered U2F token must speak. For\n     * the version of the protocol described herein, must be \"U2F_V2\"\n     */\n    @NonNull String version;\n\n    /**\n     * The websafe-base64-encoded challenge.\n     */\n    @NonNull String challenge;\n\n    /**\n     * The application id that the RP would like to assert. The U2F token will\n     * enforce that the key handle provided above is associated with this\n     * application id. The browser enforces that the calling origin belongs to the\n     * application identified by the application id.\n     */\n    @NonNull String appId;\n\n    public RegisterRequest(String challenge, String appId) {\n        this(U2fPrimitives.U2F_VERSION, challenge, appId);\n    }\n\n    @Override\n    public String getRequestId() {\n        return getChallenge();\n    }\n\n    public static RegisterRequest fromJson(String json) throws U2fBadInputException {\n        return fromJson(json, RegisterRequest.class);\n    }\n\n    @JsonPOJOBuilder(withPrefix = \"\")\n    static class RegisterRequestBuilder {}\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/RegisterRequestData.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Iterables;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.crypto.ChallengeGenerator;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.json.Persistable;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.util.List;\nimport lombok.EqualsAndHashCode;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@EqualsAndHashCode\npublic class RegisterRequestData extends JsonSerializable implements Persistable {\n\n    private static final long serialVersionUID = 60855174227617680L;\n\n    @JsonProperty\n    private final String appId;\n    @JsonProperty\n    private final List<RegisteredKey> registeredKeys;\n    @JsonProperty\n    private final List<RegisterRequest> registerRequests;\n\n    public RegisterRequestData(@JsonProperty(\"appId\") String appId, @JsonProperty(\"registeredKeys\") List<RegisteredKey> registeredKeys, @JsonProperty(\"registerRequests\") List<RegisterRequest> registerRequests) {\n        this.appId = appId;\n        this.registeredKeys = registeredKeys;\n        this.registerRequests = registerRequests;\n    }\n\n    public RegisterRequestData(String appId, Iterable<? extends DeviceRegistration> devices, U2fPrimitives u2f, ChallengeGenerator challengeGenerator) {\n        this.appId = appId;\n\n        ImmutableList.Builder<RegisteredKey> registeredKeys = ImmutableList.builder();\n        for (DeviceRegistration device : devices) {\n            if(!device.isCompromised()) {\n                registeredKeys.add(new RegisteredKey(device.getKeyHandle()));\n            }\n        }\n\n        this.registeredKeys = registeredKeys.build();\n        this.registerRequests = ImmutableList.of(u2f.startRegistration(appId, challengeGenerator.generateChallenge()));\n    }\n\n    public List<RegisteredKey> getRegisteredKeys() {\n        return ImmutableList.copyOf(registeredKeys);\n    }\n\n    public List<RegisterRequest> getRegisterRequests() {\n        return ImmutableList.copyOf(registerRequests);\n    }\n\n    public RegisterRequest getRegisterRequest(RegisterResponse response) {\n        return Iterables.getOnlyElement(registerRequests);\n    }\n\n    public String getRequestId() {\n        return Iterables.getOnlyElement(registerRequests).getChallenge();\n    }\n\n    public static RegisterRequestData fromJson(String json) throws U2fBadInputException {\n        return fromJson(json, RegisterRequestData.class);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/RegisterResponse.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.json.Persistable;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport lombok.EqualsAndHashCode;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@EqualsAndHashCode\n@JsonIgnoreProperties(ignoreUnknown = true)\npublic class RegisterResponse extends JsonSerializable implements Persistable {\n    private static final int MAX_SIZE = 20000;\n\n    /**\n     * base64(raw registration response message)\n     */\n    @JsonProperty\n    private final String registrationData;\n\n    /**\n     * base64(UTF8(client data))\n     */\n    @JsonProperty(\"clientData\")\n    private final String clientDataRaw;\n\n    private transient ClientData clientDataRef;\n\n    @JsonCreator\n    public RegisterResponse(@JsonProperty(\"registrationData\") String registrationData, @JsonProperty(\"clientData\") String clientData) throws U2fBadInputException {\n        this.registrationData = checkNotNull(registrationData);\n        this.clientDataRaw = checkNotNull(clientData);\n        this.clientDataRef = new ClientData(clientData);\n    }\n\n    public String getRegistrationData() {\n        return registrationData;\n    }\n\n    @JsonIgnore\n    public ClientData getClientData() {\n        return clientDataRef;\n    }\n\n    public String getRequestId() {\n        return getClientData().getChallenge();\n    }\n\n    public static RegisterResponse fromJson(String json) throws U2fBadInputException {\n        checkArgument(json.length() < MAX_SIZE, \"Client response bigger than allowed\");\n        return JsonSerializable.fromJson(json, RegisterResponse.class);\n    }\n\n    private void writeObject(ObjectOutputStream out) throws IOException {\n        out.defaultWriteObject();\n    }\n\n    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {\n        in.defaultReadObject();\n        try {\n            clientDataRef = new ClientData(clientDataRaw);\n        } catch (U2fBadInputException e) {\n            throw new IOException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/RegisteredKey.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.annotation.JsonInclude;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport java.io.Serializable;\nimport java.util.Set;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.NonNull;\nimport lombok.Value;\n\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@Value\n@Builder\n@AllArgsConstructor\n@JsonInclude(JsonInclude.Include.NON_NULL)\n@JsonDeserialize(builder = RegisteredKey.RegisteredKeyBuilder.class)\npublic class RegisteredKey extends JsonSerializable implements Serializable {\n\n    private static final long serialVersionUID = -5509788965855488374L;\n\n    /**\n     * Version of the protocol that the to-be-registered U2F token must speak. For\n     * the version of the protocol described herein, must be \"U2F_V2\"\n     */\n    @NonNull\n    String version;\n\n    /**\n     * websafe-base64 encoding of the key handle obtained from the U2F token\n     * during registration.\n     */\n    @NonNull String keyHandle;\n\n    String appId;\n    Set<String> transports;\n\n    public RegisteredKey(String keyHandle) {\n        this(U2fPrimitives.U2F_VERSION, keyHandle, null, null);\n    }\n\n    @JsonPOJOBuilder(withPrefix = \"\")\n    public static class RegisteredKeyBuilder {}\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/SignRequest.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.fasterxml.jackson.databind.annotation.JsonDeserialize;\nimport com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.json.Persistable;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport lombok.Builder;\nimport lombok.NonNull;\nimport lombok.Value;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@Value\n@Builder\n@JsonDeserialize(builder = SignRequest.SignRequestBuilder.class)\npublic class SignRequest extends JsonSerializable implements Persistable {\n\n    private static final long serialVersionUID = -27808961388655010L;\n\n    /**\n     * Version of the protocol that the to-be-registered U2F token must speak. For\n     * the version of the protocol described herein, must be \"U2F_V2\"\n     */\n    @JsonProperty\n    @NonNull @Builder.Default String version = U2fPrimitives.U2F_VERSION;\n\n    /**\n     * The websafe-base64-encoded challenge.\n     */\n    @JsonProperty\n    @NonNull String challenge;\n\n    /**\n     * The application id that the RP would like to assert. The U2F token will\n     * enforce that the key handle provided above is associated with this\n     * application id. The browser enforces that the calling origin belongs to the\n     * application identified by the application id.\n     */\n    @JsonProperty\n    @NonNull String appId;\n\n    /**\n     * websafe-base64 encoding of the key handle obtained from the U2F token\n     * during registration.\n     */\n    @JsonProperty\n    @NonNull String keyHandle;\n\n    public String getRequestId() {\n        return challenge;\n    }\n\n    public static SignRequest fromJson(String json) throws U2fBadInputException {\n        return fromJson(json, SignRequest.class);\n    }\n\n    @JsonPOJOBuilder(withPrefix = \"\")\n    public static class SignRequestBuilder {}\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/SignRequestData.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.google.common.base.Objects;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Iterables;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.crypto.ChallengeGenerator;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.json.Persistable;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.NoEligibleDevicesException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.util.List;\nimport lombok.EqualsAndHashCode;\n\nimport static com.google.common.base.Preconditions.checkArgument;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@EqualsAndHashCode\npublic class SignRequestData extends JsonSerializable implements Persistable {\n\n    private static final long serialVersionUID = 35378338769078256L;\n\n    @JsonProperty\n    private final String appId;\n\n    /**\n     * The websafe-base64-encoded challenge.\n     */\n    @JsonProperty\n    private final String challenge;\n\n    @JsonProperty\n    private final List<SignRequest> signRequests;\n\n    @JsonCreator\n    public SignRequestData(@JsonProperty(\"appId\") String appId, @JsonProperty(\"challenge\") String challenge, @JsonProperty(\"signRequests\") List<SignRequest> signRequests) {\n        this.appId = appId;\n        this.challenge = challenge;\n        this.signRequests = signRequests;\n    }\n\n    public SignRequestData(String appId, Iterable<? extends DeviceRegistration> devices, U2fPrimitives u2f, ChallengeGenerator challengeGenerator) throws NoEligibleDevicesException {\n        this.appId = appId;\n\n        byte[] challenge = challengeGenerator.generateChallenge();\n        this.challenge = U2fB64Encoding.encode(challenge);\n\n        ImmutableList.Builder<SignRequest> requestBuilder = ImmutableList.builder();\n        for (DeviceRegistration device : devices) {\n            if(!device.isCompromised()) {\n                requestBuilder.add(u2f.startSignature(appId, device, challenge));\n            }\n        }\n        signRequests = requestBuilder.build();\n\n        if (signRequests.isEmpty()) {\n            if(Iterables.isEmpty(devices)) {\n                throw new NoEligibleDevicesException(ImmutableList.<DeviceRegistration>of(), \"No devices registrered\");\n            } else {\n                throw new NoEligibleDevicesException(devices, \"All devices compromised\");\n            }\n        }\n    }\n\n    public List<SignRequest> getSignRequests() {\n        return ImmutableList.copyOf(signRequests);\n    }\n\n    public SignRequest getSignRequest(SignResponse response) throws U2fBadInputException {\n        checkArgument(Objects.equal(getRequestId(), response.getRequestId()), \"Wrong request for response data\");\n\n        for (SignRequest request : signRequests) {\n            if (Objects.equal(request.getKeyHandle(), response.getKeyHandle())) {\n                return request;\n            }\n        }\n        throw new U2fBadInputException(\"Responses keyHandle does not match any contained request\");\n    }\n\n    public String getRequestId() {\n        return signRequests.get(0).getChallenge();\n    }\n\n    public static SignRequestData fromJson(String json) throws U2fBadInputException {\n        return fromJson(json, SignRequestData.class);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/SignResponse.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.annotation.JsonIgnoreProperties;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport com.yubico.u2f.data.messages.json.JsonSerializable;\nimport com.yubico.u2f.data.messages.json.Persistable;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport lombok.EqualsAndHashCode;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@JsonIgnoreProperties(ignoreUnknown = true)\n@EqualsAndHashCode\npublic class SignResponse extends JsonSerializable implements Persistable {\n    private static final int MAX_SIZE = 20000;\n\n    /* base64(client data) */\n    @JsonProperty(\"clientData\")\n    private final String clientDataRaw;\n\n    @JsonIgnore\n    private transient ClientData clientDataRef;\n\n    /* base64(raw response from U2F device) */\n    @JsonProperty\n    private final String signatureData;\n\n    /* keyHandle originally passed */\n    @JsonProperty\n    private final String keyHandle;\n\n    @JsonCreator\n    public SignResponse(@JsonProperty(\"clientData\") String clientData, @JsonProperty(\"signatureData\") String signatureData, @JsonProperty(\"keyHandle\") String keyHandle) throws U2fBadInputException {\n        this.clientDataRaw = checkNotNull(clientData);\n        this.signatureData = checkNotNull(signatureData);\n        this.keyHandle = checkNotNull(keyHandle);\n        clientDataRef = new ClientData(clientData);\n    }\n\n    @JsonIgnore\n    public ClientData getClientData() {\n        return clientDataRef;\n    }\n\n    public String getSignatureData() {\n        return signatureData;\n    }\n\n    public String getKeyHandle() {\n        return keyHandle;\n    }\n\n    public String getRequestId() {\n        return getClientData().getChallenge();\n    }\n\n    public static SignResponse fromJson(String json) throws U2fBadInputException {\n        checkArgument(json.length() < MAX_SIZE, \"Client response bigger than allowed\");\n        return fromJson(json, SignResponse.class);\n    }\n\n    private void writeObject(ObjectOutputStream out) throws IOException {\n        out.defaultWriteObject();\n    }\n\n    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {\n        in.defaultReadObject();\n        try {\n            clientDataRef = new ClientData(clientDataRaw);\n        } catch (U2fBadInputException e) {\n            throw new IOException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/json/JsonSerializable.java",
    "content": "package com.yubico.u2f.data.messages.json;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.fasterxml.jackson.core.JsonParseException;\nimport com.fasterxml.jackson.databind.JsonMappingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.io.IOException;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic abstract class JsonSerializable {\n    protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n\n    @JsonIgnore\n    public String toJson() {\n        try {\n            return OBJECT_MAPPER.writeValueAsString(this);\n        } catch (IOException e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n\n    @Override\n    public String toString() {\n        return toJson();\n    }\n\n    public static <T extends JsonSerializable> T fromJson(String json, Class<T> cls) throws U2fBadInputException {\n        try {\n            return OBJECT_MAPPER.readValue(json, cls);\n        } catch (JsonMappingException e) {\n            throw new U2fBadInputException(\"Invalid JSON data\", e);\n        } catch (JsonParseException e) {\n            throw new U2fBadInputException(\"Invalid JSON data\", e);\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Invalid JSON data\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/json/Persistable.java",
    "content": "package com.yubico.u2f.data.messages.json;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\n\nimport java.io.Serializable;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic interface Persistable extends Serializable {\n    @JsonIgnore\n    public String getRequestId();\n\n    public String toJson();\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/key/RawRegisterResponse.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages.key;\n\nimport com.google.common.io.ByteArrayDataOutput;\nimport com.google.common.io.ByteStreams;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.Crypto;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.key.util.ByteInputStream;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.io.IOException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport lombok.EqualsAndHashCode;\n\n/**\n * The register response produced by the token/key, which is transformed by the client into an RegisterResponse\n * and sent to the server.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@EqualsAndHashCode\npublic class RawRegisterResponse {\n    public static final byte REGISTRATION_RESERVED_BYTE_VALUE = (byte) 0x05;\n    public static final byte REGISTRATION_SIGNED_RESERVED_BYTE_VALUE = (byte) 0x00;\n\n    private transient final Crypto crypto;\n\n    /**\n     * The (uncompressed) x,y-representation of a curve point on the P-256\n     * NIST elliptic curve.\n     */\n    final byte[] userPublicKey;\n\n    /**\n     * A handle that allows the U2F token to identify the generated key pair.\n     */\n    final byte[] keyHandle;\n    final X509Certificate attestationCertificate;\n\n    /**\n     * A ECDSA signature (on P-256)\n     */\n    final byte[] signature;\n\n    public RawRegisterResponse(byte[] userPublicKey,\n                               byte[] keyHandle,\n                               X509Certificate attestationCertificate,\n                               byte[] signature) {\n        this(userPublicKey, keyHandle, attestationCertificate, signature, new BouncyCastleCrypto());\n    }\n\n    public RawRegisterResponse(byte[] userPublicKey,\n                               byte[] keyHandle,\n                               X509Certificate attestationCertificate,\n                               byte[] signature,\n                               Crypto crypto) {\n        this.userPublicKey = userPublicKey;\n        this.keyHandle = keyHandle;\n        this.attestationCertificate = attestationCertificate;\n        this.signature = signature;\n        this.crypto = crypto;\n    }\n\n    public static RawRegisterResponse fromBase64(String rawDataBase64, Crypto crypto) throws U2fBadInputException {\n        ByteInputStream bytes = new ByteInputStream(U2fB64Encoding.decode(rawDataBase64));\n        try {\n            byte reservedByte = bytes.readSigned();\n            if (reservedByte != REGISTRATION_RESERVED_BYTE_VALUE) {\n                throw new U2fBadInputException(\n                        \"Incorrect value of reserved byte. Expected: \" + REGISTRATION_RESERVED_BYTE_VALUE +\n                                \". Was: \" + reservedByte\n                );\n            }\n\n            return new RawRegisterResponse(\n                    bytes.read(65),\n                    bytes.read(bytes.readUnsigned()),\n                    CertificateParser.parseDer(bytes),\n                    bytes.readAll(),\n                    crypto\n            );\n        } catch (CertificateException e) {\n            throw new U2fBadInputException(\"Malformed attestation certificate\", e);\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Truncated registration data\", e);\n        }\n    }\n\n    public void checkSignature(String appId, String clientData) throws U2fBadInputException {\n        byte[] signedBytes = packBytesToSign(crypto.hash(appId), crypto.hash(clientData), keyHandle, userPublicKey);\n        crypto.checkSignature(attestationCertificate, signedBytes, signature);\n    }\n\n    public static byte[] packBytesToSign(byte[] appIdHash, byte[] clientDataHash, byte[] keyHandle, byte[] userPublicKey) {\n        ByteArrayDataOutput encoded = ByteStreams.newDataOutput();\n        encoded.write(REGISTRATION_SIGNED_RESERVED_BYTE_VALUE);\n        encoded.write(appIdHash);\n        encoded.write(clientDataHash);\n        encoded.write(keyHandle);\n        encoded.write(userPublicKey);\n        return encoded.toByteArray();\n    }\n\n    public DeviceRegistration createDevice() throws U2fBadInputException {\n        return new DeviceRegistration(\n                U2fB64Encoding.encode(keyHandle),\n                U2fB64Encoding.encode(userPublicKey),\n                attestationCertificate,\n                DeviceRegistration.INITIAL_COUNTER_VALUE\n        );\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/key/RawSignResponse.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages.key;\n\nimport com.google.common.io.ByteArrayDataOutput;\nimport com.google.common.io.ByteStreams;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.Crypto;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.key.util.ByteInputStream;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport java.io.IOException;\nimport lombok.EqualsAndHashCode;\n\n/**\n * The sign response produced by the token/key, which is transformed by the client into an\n * {@link SignResponse} and sent to the server.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@EqualsAndHashCode(of = { \"userPresence\", \"counter\", \"signature\" })\npublic class RawSignResponse {\n    public static final byte USER_PRESENT_FLAG = 0x01;\n\n    private final byte userPresence;\n    private final long counter;\n    private final byte[] signature;\n    private final Crypto crypto;\n\n    public RawSignResponse(byte userPresence, long counter, byte[] signature) {\n        this(userPresence, counter, signature, new BouncyCastleCrypto());\n    }\n\n    public RawSignResponse(byte userPresence, long counter, byte[] signature, Crypto crypto) {\n        this.userPresence = userPresence;\n        this.counter = counter;\n        this.signature = signature;\n        this.crypto = crypto;\n    }\n\n    public static RawSignResponse fromBase64(String rawDataBase64, Crypto crypto) throws U2fBadInputException {\n        ByteInputStream bytes = new ByteInputStream(U2fB64Encoding.decode(rawDataBase64));\n        try {\n            return new RawSignResponse(\n                    bytes.readSigned(),\n                    bytes.readInteger(),\n                    bytes.readAll(),\n                    crypto\n            );\n        } catch (IOException e) {\n            throw new U2fBadInputException(\"Truncated authentication data\", e);\n        }\n    }\n\n    public void checkSignature(String appId, String clientData, byte[] publicKey) throws U2fBadInputException {\n        byte[] signedBytes = packBytesToSign(\n                crypto.hash(appId),\n                userPresence,\n                counter,\n                crypto.hash(clientData)\n        );\n        crypto.checkSignature(\n                crypto.decodePublicKey(publicKey),\n                signedBytes,\n                signature\n        );\n    }\n\n    public static byte[] packBytesToSign(byte[] appIdHash, byte userPresence, long counter, byte[] challengeHash) {\n        ByteArrayDataOutput encoded = ByteStreams.newDataOutput();\n        encoded.write(appIdHash);\n        encoded.write(userPresence);\n        encoded.writeInt((int) counter);\n        encoded.write(challengeHash);\n        return encoded.toByteArray();\n    }\n\n    /**\n     * Bit 0 is set to 1, which means that user presence was verified. (This version of the protocol doesn't specify a\n     * way to request sign responses without requiring user presence.) A different value of bit 0, as well as bits 1\n     * through 7, are reserved for future use. The values of bit 1 through 7 SHOULD be 0\n     */\n    public byte getUserPresence() {\n        return userPresence;\n    }\n\n    /**\n     * This is the big-endian representation of a counter value that the U2F device\n     * increments every time it performs a sign operation.\n     */\n    public long getCounter() {\n        return counter;\n    }\n\n    /**\n     * This is a ECDSA signature (on P-256)\n     */\n    public byte[] getSignature() {\n        return signature;\n    }\n\n    public void checkUserPresence() throws U2fBadInputException {\n        if (userPresence != USER_PRESENT_FLAG) {\n            throw new U2fBadInputException(\"User presence invalid during signing\");\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/key/util/ByteInputStream.java",
    "content": "/*\n * Copyright 2014 Yubico.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\npackage com.yubico.u2f.data.messages.key.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.DataInputStream;\nimport java.io.IOException;\n\n/**\n * Provides an easy way to read a byte array in chunks.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class ByteInputStream extends DataInputStream {\n\n    public ByteInputStream(byte[] data) {\n        super(new ByteArrayInputStream(data));\n    }\n\n    public byte[] read(int numberOfBytes) throws IOException {\n        byte[] readBytes = new byte[numberOfBytes];\n        readFully(readBytes);\n        return readBytes;\n    }\n\n    public byte[] readAll() throws IOException {\n        byte[] readBytes = new byte[available()];\n        readFully(readBytes);\n        return readBytes;\n    }\n\n    public int readInteger() throws IOException {\n        return readInt();\n    }\n\n    public byte readSigned() throws IOException {\n        return readByte();\n    }\n\n    public int readUnsigned() throws IOException {\n        return readUnsignedByte();\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/key/util/CertificateParser.java",
    "content": "package com.yubico.u2f.data.messages.key.util;\n\nimport com.google.common.io.BaseEncoding;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.io.StringReader;\nimport java.security.Provider;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class CertificateParser {\n    private static final Provider BC_PROVIDER = new BouncyCastleCrypto().getProvider();\n\n    private final static List<String> FIXSIG = Arrays.asList(\n            \"CN=Yubico U2F EE Serial 776137165\",\n            \"CN=Yubico U2F EE Serial 1086591525\",\n            \"CN=Yubico U2F EE Serial 1973679733\",\n            \"CN=Yubico U2F EE Serial 13503277888\",\n            \"CN=Yubico U2F EE Serial 13831167861\",\n            \"CN=Yubico U2F EE Serial 14803321578\"\n    );\n\n\n    public static X509Certificate parsePem(String pemEncodedCert) throws CertificateException {\n        return parseDer(pemEncodedCert.replaceAll(\"-----BEGIN CERTIFICATE-----\", \"\").replaceAll(\"-----END CERTIFICATE-----\", \"\").replaceAll(\"\\n\", \"\"));\n    }\n\n    public static X509Certificate parseDer(String base64DerEncodedCert) throws CertificateException {\n        return parseDer(BaseEncoding.base64().decodingStream(new StringReader(base64DerEncodedCert)));\n    }\n\n    public static X509Certificate parseDer(byte[] derEncodedCert) throws CertificateException {\n        return parseDer(new ByteArrayInputStream(derEncodedCert));\n    }\n\n    public static X509Certificate parseDer(InputStream is) throws CertificateException {\n        X509Certificate cert = (X509Certificate) CertificateFactory.getInstance(\"X.509\", BC_PROVIDER).generateCertificate(is);\n        //Some known certs have an incorrect \"unused bits\" value, which causes problems on newer versions of BouncyCastle.\n        if(FIXSIG.contains(cert.getSubjectDN().getName())) {\n            byte[] encoded = cert.getEncoded();\n            encoded[encoded.length-257] = 0;  // Fix the \"unused bits\" field (should always be 0).\n            cert = (X509Certificate) CertificateFactory.getInstance(\"X.509\", BC_PROVIDER).generateCertificate(new ByteArrayInputStream(encoded));\n        }\n        return cert;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/data/messages/key/util/U2fB64Encoding.java",
    "content": "package com.yubico.u2f.data.messages.key.util;\n\nimport com.google.common.io.BaseEncoding;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2fB64Encoding {\n    private final static BaseEncoding BASE64_ENCODER = BaseEncoding.base64Url().omitPadding();\n    private final static BaseEncoding BASE64_DECODER = BaseEncoding.base64Url();\n\n    public static String encode(byte[] decoded) {\n        return BASE64_ENCODER.encode(decoded);\n    }\n\n    public static byte[] decode(String encoded) throws U2fBadInputException {\n        try {\n            return BASE64_DECODER.decode(encoded);\n        } catch (IllegalArgumentException e) {\n            throw new U2fBadInputException(\"Bad base64 encoding\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/DeviceCompromisedException.java",
    "content": "package com.yubico.u2f.exceptions;\n\nimport com.yubico.u2f.data.DeviceRegistration;\nimport lombok.Getter;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@Getter\npublic class DeviceCompromisedException extends U2fAuthenticationException {\n    private final DeviceRegistration deviceRegistration;\n\n    public DeviceCompromisedException(DeviceRegistration deviceRegistration, String message, Throwable cause) {\n        super(message, cause);\n        this.deviceRegistration = deviceRegistration;\n    }\n\n    public DeviceCompromisedException(DeviceRegistration deviceRegistration, String message) {\n        super(message);\n        this.deviceRegistration = deviceRegistration;\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/InvalidDeviceCounterException.java",
    "content": "package com.yubico.u2f.exceptions;\n\nimport com.yubico.u2f.data.DeviceRegistration;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class InvalidDeviceCounterException extends DeviceCompromisedException {\n    public InvalidDeviceCounterException(DeviceRegistration registration) {\n        super(registration, \"The device's internal counter was was smaller than expected.\" +\n                \"It's possible that the device has been cloned!\");\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/NoEligableDevicesException.java",
    "content": "package com.yubico.u2f.exceptions;\n\nimport com.google.common.collect.ImmutableList;\nimport com.yubico.u2f.data.DeviceRegistration;\n\nimport java.util.List;\nimport lombok.Getter;\n\n/**\n * @deprecated use {@link NoEligibleDevicesException}\n */\n@Deprecated\n@Getter\npublic class NoEligableDevicesException extends U2fAuthenticationException {\n    private final List<DeviceRegistration> devices;\n\n    public NoEligableDevicesException(Iterable<? extends DeviceRegistration> devices, String message, Throwable cause) {\n        super(message, cause);\n        this.devices = ImmutableList.copyOf(devices);\n    }\n\n    public NoEligableDevicesException(Iterable<? extends DeviceRegistration> devices, String message) {\n        super(message);\n        this.devices = ImmutableList.copyOf(devices);\n    }\n\n    public boolean hasDevices() {\n        return !devices.isEmpty();\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/NoEligibleDevicesException.java",
    "content": "package com.yubico.u2f.exceptions;\n\nimport com.yubico.u2f.data.DeviceRegistration;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@SuppressWarnings(\"deprecation\")\npublic class NoEligibleDevicesException extends NoEligableDevicesException {\n\n    public NoEligibleDevicesException(Iterable<? extends DeviceRegistration> devices, String message, Throwable cause) {\n        super(devices, message, cause);\n    }\n\n    public NoEligibleDevicesException(Iterable<? extends DeviceRegistration> devices, String message) {\n        super(devices, message);\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/U2fAuthenticationException.java",
    "content": "package com.yubico.u2f.exceptions;\n\n/**\n * Base class for exceptions thrown when a U2F authentication ceremony fails.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2fAuthenticationException extends U2fCeremonyException {\n    public U2fAuthenticationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public U2fAuthenticationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/U2fBadConfigurationException.java",
    "content": "package com.yubico.u2f.exceptions;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2fBadConfigurationException extends Exception {\n    public U2fBadConfigurationException(String message) {\n        super(message);\n    }\n\n    public U2fBadConfigurationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/U2fBadInputException.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.exceptions;\n\n/**\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\n@SuppressWarnings(\"serial\")\npublic class U2fBadInputException extends Exception {\n\n    public U2fBadInputException(String message) {\n        super(message);\n    }\n\n    public U2fBadInputException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/U2fCeremonyException.java",
    "content": "package com.yubico.u2f.exceptions;\n\n/**\n * Base class for exceptions thrown when a U2F registration or authentication ceremony fails.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2fCeremonyException extends Exception {\n    public U2fCeremonyException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public U2fCeremonyException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/main/java/com/yubico/u2f/exceptions/U2fRegistrationException.java",
    "content": "package com.yubico.u2f.exceptions;\n\n/**\n * Base class for exceptions thrown when a U2F registration ceremony fails.\n *\n * @deprecated The java-u2flib-server library is obsolete. Use <a\n * href=\"https://developers.yubico.com/java-webauthn-server/\">java-webauthn-server</a> instead.\n */\n@Deprecated\npublic class U2fRegistrationException extends U2fCeremonyException {\n    public U2fRegistrationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public U2fRegistrationException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/AppIdTest.java",
    "content": "package com.yubico.u2f;\n\nimport com.yubico.u2f.exceptions.U2fBadConfigurationException;\nimport org.junit.Test;\n\nimport static junit.framework.TestCase.assertFalse;\nimport static junit.framework.TestCase.assertTrue;\n\npublic class AppIdTest {\n\n    @Test\n    public void validUrls() {\n        assertTrue(isValid(\"https://www.example.com\"));\n        assertTrue(isValid(\"https://internal-server\"));\n        assertTrue(isValid(\"https://åäö.se:8443\"));\n        assertTrue(isValid(\"https://localhost:8443/myAppId.json\"));\n    }\n\n    @Test\n    public void validUris() {\n        assertTrue(isValid(\"android:apk-key-hash:585215fd5153209a7e246f53286035838a0be227\"));\n        assertTrue(isValid(\"ios:bundle-id:com.example.Example\"));\n    }\n\n    @Test\n    public void disallowHttp() {\n        assertFalse(isValid(\"http://www.example.com\"));\n    }\n\n    @Test\n    public void disallowSlashAsPath() {\n        assertFalse(isValid(\"https://www.example.com/\"));\n    }\n\n    @Test\n    public void disallowIP() {\n        assertFalse(isValid(\"https://127.0.0.1:8443\"));\n        assertFalse(isValid(\"https://127.0.0.1\"));\n        assertFalse(isValid(\"https://127.0.0.1/foo\"));\n        assertFalse(isValid(\"https://2001:0db8:0000:0000:0000:ff00:0042:8329\"));\n        assertFalse(isValid(\"https://2001:0db8:0000:0000:0000:ff00:0042:8329/åäö\"));\n    }\n\n    @Test\n    public void badSyntax() {\n        assertFalse(isValid(\"https://bad[syntax]\"));\n    }\n\n    private static boolean isValid(String appId) {\n        try {\n            AppId.checkIsValid(appId);\n            return true;\n        } catch (U2fBadConfigurationException e) {\n            return false;\n        }\n\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/SystemTest.java",
    "content": "package com.yubico.u2f;\n\nimport com.google.common.collect.ImmutableSet;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.SignRequest;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.RegisterRequest;\nimport com.yubico.u2f.data.messages.RegisterResponse;\nimport org.junit.Ignore;\n\nimport java.util.Scanner;\n\n@Ignore(\"Includes manual steps\")\npublic class SystemTest {\n\n    public static final ImmutableSet<String> TRUSTED_DOMAINS = ImmutableSet.of(\"http://example.com\");\n    public static final String APP_ID = \"my-app\";\n    private static Scanner scan = new Scanner(System.in);\n    private static final U2fPrimitives u2f = new U2fPrimitives();\n\n    /*\n      For manual testing with physical keys. Can e.g. be combined with these libu2f-host commands:\n\n        u2f-host -aregister -o http://example.com\n        u2f-host -aauthenticate -o http://example.com\n     */\n    public static void main(String... args) throws Exception {\n        String startedRegistration = u2f.startRegistration(APP_ID).toJson();\n        System.out.println(\"Registration data:\");\n        System.out.println(startedRegistration);\n\n        System.out.println();\n        System.out.println(\"Enter token response:\");\n\n        String json = scan.nextLine();\n        RegisterResponse registerResponse = RegisterResponse.fromJson(json);\n        registerResponse.getClientData().getChallenge();\n        DeviceRegistration deviceRegistration = u2f.finishRegistration(\n                RegisterRequest.fromJson(startedRegistration),\n                registerResponse,\n                TRUSTED_DOMAINS\n        );\n\n        System.out.println(deviceRegistration);\n\n        String startedSignature = u2f.startSignature(APP_ID, deviceRegistration).toJson();\n        System.out.println(\"Signature data:\");\n        System.out.println(startedSignature);\n\n        System.out.println();\n        System.out.println(\"Enter token response:\");\n\n        u2f.finishSignature(\n                SignRequest.fromJson(startedSignature),\n                SignResponse.fromJson(scan.nextLine()),\n                deviceRegistration,\n                TRUSTED_DOMAINS\n        );\n        System.out.println(\"Device counter: \" + deviceRegistration.getCounter());\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/TestUtils.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f;\n\nimport com.google.common.io.BaseEncoding;\nimport com.google.common.io.ByteArrayDataOutput;\nimport com.yubico.u2f.data.messages.key.util.CertificateParser;\nimport org.bouncycastle.asn1.sec.SECNamedCurves;\nimport org.bouncycastle.asn1.x9.X9ECParameters;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\nimport org.bouncycastle.jce.spec.ECParameterSpec;\nimport org.bouncycastle.jce.spec.ECPrivateKeySpec;\nimport org.bouncycastle.jce.spec.ECPublicKeySpec;\nimport org.bouncycastle.math.ec.ECPoint;\n\nimport java.io.*;\nimport java.math.BigInteger;\nimport java.security.*;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.security.spec.InvalidKeySpecException;\nimport java.util.Scanner;\n\npublic class TestUtils {\n\n    static {\n        Security.addProvider(new BouncyCastleProvider());\n    }\n\n    public static final BaseEncoding HEX = BaseEncoding.base16().lowerCase();\n    public static final BaseEncoding BASE64 = BaseEncoding.base64();\n\n    public static X509Certificate fetchCertificate(InputStream resourceAsStream) {\n        Scanner in = new Scanner(resourceAsStream);\n        String base64String = in.nextLine();\n        return parseCertificate(BASE64.decode(base64String));\n    }\n\n    public static X509Certificate parseCertificate(byte[] encodedDerCertificate) {\n        try {\n            return CertificateParser.parseDer(encodedDerCertificate);\n        } catch (CertificateException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static PrivateKey parsePrivateKey(InputStream is) {\n        String keyBytesHex = new Scanner(is).nextLine();\n        return parsePrivateKey(keyBytesHex);\n    }\n\n    public static PrivateKey parsePrivateKey(String keyBytesHex) {\n        try {\n            KeyFactory fac = KeyFactory.getInstance(\"ECDSA\");\n            X9ECParameters curve = SECNamedCurves.getByName(\"secp256r1\");\n            ECParameterSpec curveSpec = new ECParameterSpec(\n                    curve.getCurve(), curve.getG(), curve.getN(), curve.getH());\n            ECPrivateKeySpec keySpec = new ECPrivateKeySpec(\n                    new BigInteger(keyBytesHex, 16),\n                    curveSpec);\n            return fac.generatePrivate(keySpec);\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);\n        } catch (InvalidKeySpecException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static PublicKey parsePublicKey(byte[] keyBytes) {\n        try {\n            X9ECParameters curve = SECNamedCurves.getByName(\"secp256r1\");\n            ECParameterSpec curveSpec = new ECParameterSpec(curve.getCurve(), curve.getG(), curve.getN(),\n                    curve.getH());\n            ECPoint point = curve.getCurve().decodePoint(keyBytes);\n            return KeyFactory.getInstance(\"ECDSA\").generatePublic(\n                    new ECPublicKeySpec(point, curveSpec));\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);\n        } catch (InvalidKeySpecException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public static byte[] serialize(Object o) throws IOException {\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        ObjectOutputStream objectOut = new ObjectOutputStream(out);\n        objectOut.writeObject(o);\n        objectOut.close();\n        return out.toByteArray();\n    }\n\n    public static <T> T deserialize(byte[] serialized) throws IOException, ClassNotFoundException {\n        ByteArrayInputStream is = new ByteArrayInputStream(serialized);\n        ObjectInputStream objectIn = new ObjectInputStream(is);\n        T object = (T) objectIn.readObject();\n        objectIn.close();\n        return  object;\n    }\n\n    public static <T> T clone(T input) throws IOException, ClassNotFoundException {\n        return deserialize(serialize(input));\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/U2FTest.java",
    "content": "package com.yubico.u2f;\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableSet;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.SignRequest;\nimport com.yubico.u2f.data.messages.SignRequestData;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.RegisterRequest;\nimport com.yubico.u2f.data.messages.RegisterRequestData;\nimport com.yubico.u2f.data.messages.RegisterResponse;\nimport com.yubico.u2f.data.messages.RegisteredKey;\nimport com.yubico.u2f.exceptions.DeviceCompromisedException;\nimport com.yubico.u2f.exceptions.NoEligibleDevicesException;\nimport com.yubico.u2f.exceptions.U2fBadConfigurationException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\nimport static com.yubico.u2f.testdata.GnubbyKey.ATTESTATION_CERTIFICATE;\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.hamcrest.core.Is.isA;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class U2FTest {\n    U2F u2f = U2F.withoutAppIdValidation();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void startRegistration_compromisedDevice() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        deviceRegistration.markCompromised();\n        u2f.startRegistration(APP_ID_ENROLL, ImmutableList.of(deviceRegistration));\n    }\n\n    @Test(expected = NoEligibleDevicesException.class)\n    public void startSignature_compromisedDevices() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        deviceRegistration.markCompromised();\n        u2f.startSignature(APP_ID_ENROLL, ImmutableList.of(deviceRegistration));\n    }\n\n    @Test(expected = U2fBadConfigurationException.class)\n    public void defaultConstructedU2FstartRegistrationShouldRefuseInvalidAppId() throws U2fBadInputException, U2fBadConfigurationException {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        deviceRegistration.markCompromised();\n        new U2F().startRegistration(\"example.com\", ImmutableList.of(deviceRegistration));\n\n        fail(\"startRegistration did not refuse an invalid app ID.\");\n    }\n\n    @Test\n    public void startRegistrationShouldReturnARandomChallenge() throws U2fBadInputException, U2fBadConfigurationException {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        RegisterRequestData data = u2f.startRegistration(\"example.com\", ImmutableList.of(deviceRegistration));\n        RegisterRequestData data2 = u2f.startRegistration(\"example.com\", ImmutableList.of(deviceRegistration));\n\n        assertEquals(1, data.getRegisterRequests().size());\n        assertEquals(1, data2.getRegisterRequests().size());\n        assertNotEquals(\n            \"startRegistration must not return the same challenge twice in a row.\",\n            data.getRegisterRequests().get(0).getChallenge(),\n            data2.getRegisterRequests().get(0).getChallenge()\n        );\n    }\n\n    @Test(expected = U2fBadConfigurationException.class)\n    public void defaultConstructedU2FstartSignatureShouldRefuseInvalidAppId() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        deviceRegistration.markCompromised();\n        new U2F().startSignature(\"example.com\", ImmutableList.of(deviceRegistration));\n\n        fail(\"startRegistration did not refuse an invalid app ID.\");\n    }\n\n    @Test\n    public void startSignatureShouldReturnARandomChallenge() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        SignRequestData data = u2f.startSignature(\"example.com\", ImmutableList.of(deviceRegistration));\n        SignRequestData data2 = u2f.startSignature(\"example.com\", ImmutableList.of(deviceRegistration));\n\n        assertEquals(1, data.getSignRequests().size());\n        assertNotNull(data.getSignRequests().get(0).getChallenge());\n        assertNotEquals(\n            \"startSignature must not return the same challenge twice in a row.\",\n            data.getSignRequests().get(0).getChallenge(),\n            data2.getSignRequests().get(0).getChallenge()\n        );\n    }\n\n    @Test(expected = DeviceCompromisedException.class)\n    public void finishSignature_compromisedDevice() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n\n        SignRequest request = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse tokenResponse = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n                SIGN_RESPONSE_DATA_BASE64, KEY_HANDLE_BASE64);\n\n        SignRequestData requestData = mock(SignRequestData.class);\n        when(requestData.getSignRequest(tokenResponse)).thenReturn(request);\n\n        deviceRegistration.markCompromised();\n        u2f.finishSignature(requestData, tokenResponse, ImmutableList.of(deviceRegistration));\n    }\n\n    @Test\n    public void finishSignature_invalidFacet() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n\n        SignRequest request = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse tokenResponse = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n                SIGN_RESPONSE_DATA_BASE64, KEY_HANDLE_BASE64);\n\n        SignRequestData requestData = mock(SignRequestData.class);\n        when(requestData.getSignRequest(tokenResponse)).thenReturn(request);\n\n        u2f.finishSignature(requestData, tokenResponse, ImmutableList.of(deviceRegistration), ImmutableSet.of(\"https://wrongfacet.com\"));\n    }\n\n\n    @Test\n    public void finishRegistrationShouldReturnAMatchedDevice() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        DeviceRegistration deviceRegistration2 = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n\n        RegisterRequest request = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        RegisterResponse tokenResponse = new RegisterResponse(\n            REGISTRATION_DATA_BASE64,\n            CLIENT_DATA_REGISTRATION_BASE64\n        );\n\n        RegisterRequestData registerRequest = new RegisterRequestData(\n            APP_ID_ENROLL,\n            ImmutableList.<RegisteredKey>of(),\n            ImmutableList.of(request)\n        );\n\n        DeviceRegistration device = u2f.finishRegistration(registerRequest, tokenResponse, ImmutableSet.of(APP_ID_ENROLL));\n        DeviceRegistration overloadDevice = u2f.finishRegistration(registerRequest, tokenResponse);\n\n        assertEquals(KEY_HANDLE_BASE64, device.getKeyHandle());\n        assertEquals(device, overloadDevice);\n    }\n\n    @Test\n    public void finishSignatureShouldReturnAMatchedDevice() throws Exception {\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        DeviceRegistration deviceRegistration2 = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n\n        SignRequest request = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse tokenResponse = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n            SIGN_RESPONSE_DATA_BASE64, KEY_HANDLE_BASE64);\n\n        SignRequestData requestData = new SignRequestData(\n            APP_ID_SIGN,\n            SERVER_CHALLENGE_SIGN_BASE64,\n            ImmutableList.of(request)\n        );\n\n        DeviceRegistration device = u2f.finishSignature(requestData, tokenResponse, ImmutableList.of(deviceRegistration), ImmutableSet.of(APP_ID_ENROLL));\n        DeviceRegistration overloadDevice = u2f.finishSignature(requestData, tokenResponse, ImmutableList.of(deviceRegistration2));\n\n        assertEquals(KEY_HANDLE_BASE64, device.getKeyHandle());\n        assertEquals(device, overloadDevice);\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/U2fPrimitivesTest.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f;\n\nimport com.google.common.collect.ImmutableSet;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.SignRequest;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.RegisterRequest;\nimport com.yubico.u2f.data.messages.RegisterResponse;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.U2fAuthenticationException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport com.yubico.u2f.testdata.AcmeKey;\nimport com.yubico.u2f.testdata.TestVectors;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport org.junit.rules.ExpectedException;\n\nimport static com.yubico.u2f.testdata.GnubbyKey.ATTESTATION_CERTIFICATE;\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.hamcrest.core.Is.isA;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\npublic class U2fPrimitivesTest {\n    final HashSet<String> allowedOrigins = new HashSet<String>();\n    U2fPrimitives u2f = new U2fPrimitives();\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void setup() throws Exception {\n        allowedOrigins.add(\"http://example.com\");\n    }\n\n    @Test\n    public void finishRegistration() throws Exception {\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        DeviceRegistration response = u2f.finishRegistration(registerRequest, new RegisterResponse(TestVectors.REGISTRATION_DATA_BASE64, CLIENT_DATA_REGISTRATION_BASE64), TRUSTED_DOMAINS);\n        assertEquals(KEY_HANDLE_BASE64, response.getKeyHandle());\n    }\n\n    @Test\n    public void finishRegistration2() throws Exception {\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        DeviceRegistration deviceRegistration = u2f.finishRegistration(registerRequest, new RegisterResponse(AcmeKey.REGISTRATION_DATA_BASE64, AcmeKey.CLIENT_DATA_BASE64), TRUSTED_DOMAINS);\n\n        assertEquals(new DeviceRegistration(AcmeKey.KEY_HANDLE, AcmeKey.USER_PUBLIC_KEY_B64, AcmeKey.ATTESTATION_CERTIFICATE, 0), deviceRegistration);\n\n    }\n\n    @Test\n    public void finishRegistrationWithoutAllowedAppIds() throws Exception {\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        DeviceRegistration response = u2f.finishRegistration(\n            registerRequest,\n            new RegisterResponse(\n                TestVectors.REGISTRATION_DATA_BASE64,\n                CLIENT_DATA_REGISTRATION_BASE64\n            )\n        );\n\n        assertEquals(KEY_HANDLE_BASE64, response.getKeyHandle());\n    }\n\n    @Test\n    public void finishRegistrationShouldDetectIncorrectAppId() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        DeviceRegistration response = u2f.finishRegistration(\n            registerRequest,\n            new RegisterResponse(\n                TestVectors.REGISTRATION_DATA_WITH_DIFFERENT_APP_ID_BASE64,\n                CLIENT_DATA_REGISTRATION_BASE64\n            )\n        );\n\n        fail(\"finishRegistration did not detect incorrect app ID\");\n    }\n\n    @Test\n    public void finishRegistrationShouldDetectIncorrectChallenge() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        String clientDataBase64 = U2fB64Encoding.encode(\"{\\\"typ\\\":\\\"navigator.id.finishEnrollment\\\",\\\"challenge\\\":\\\"ARGHABLARGHLER\\\",\\\"origin\\\":\\\"http://example.com\\\"}\".getBytes(\"UTF-8\"));\n\n        u2f.finishRegistration(\n            registerRequest,\n            new RegisterResponse(\n                TestVectors.REGISTRATION_DATA_BASE64,\n                clientDataBase64\n            )\n        );\n\n        fail(\"finishRegistration did not detect incorrect challenge\");\n    }\n\n    @Test\n    public void finishRegistrationShouldDetectIncorrectClientDataType() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        String clientDataBase64 = U2fB64Encoding.encode(\"{\\\"typ\\\":\\\"navigator.id.launchNukes\\\",\\\"challenge\\\":\\\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\\\",\\\"origin\\\":\\\"http://example.com\\\"}\".getBytes(\"UTF-8\"));\n\n        u2f.finishRegistration(\n            registerRequest,\n            new RegisterResponse(\n                TestVectors.REGISTRATION_DATA_WITH_DIFFERENT_CLIENT_DATA_TYPE_BASE64,\n                clientDataBase64\n            )\n        );\n\n        fail(\"finishRegistration did not detect incorrect type in client data\");\n    }\n\n    @Test\n    public void finishRegistrationShouldDetectIncorrectClientDataOrigin() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n\n        String clientDataBase64 = U2fB64Encoding.encode(\"{\\\"typ\\\":\\\"navigator.id.finishEnrollment\\\",\\\"challenge\\\":\\\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\\\",\\\"origin\\\":\\\"http://evil.com\\\"}\".getBytes(\"UTF-8\"));\n\n        u2f.finishRegistration(\n            registerRequest,\n            new RegisterResponse(\n                TestVectors.REGISTRATION_DATA_BASE64,\n                clientDataBase64\n            )\n        );\n\n        fail(\"finishRegistration did not detect incorrect origin in client data\");\n    }\n\n    @Test\n    public void finishSignature() throws Exception {\n        SignRequest signRequest = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse tokenResponse = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n                SIGN_RESPONSE_DATA_BASE64, KEY_HANDLE_BASE64);\n\n        u2f.finishSignature(signRequest, tokenResponse, new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0), allowedOrigins);\n    }\n\n\n    @Test(expected = U2fAuthenticationException.class)\n    public void finishSignature_badOrigin() throws Exception {\n        Set<String> allowedOrigins = ImmutableSet.of(\"some-other-domain.com\");\n        SignRequest request = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse response = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n                SIGN_RESPONSE_DATA_BASE64, SERVER_CHALLENGE_SIGN_BASE64);\n\n        u2f.finishSignature(request, response, new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0), allowedOrigins);\n    }\n\n    @Test(expected = U2fBadInputException.class)\n    public void finishAuthentication_badBase64() throws Exception {\n        SignRequest authentication = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse response = new SignResponse(\"****\", \"****\", \"****\");\n\n        u2f.finishSignature(authentication, response, new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0));\n    }\n\n    @Test(expected = U2fBadInputException.class)\n    public void finishAuthentication_clientDataMissingField() throws Exception {\n        SignRequest authentication = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse response = new SignResponse(U2fB64Encoding.encode(\"{}\".getBytes()), \"\", \"\");\n\n        u2f.finishSignature(authentication, response, new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0));\n    }\n\n    @Test\n    public void finishAuthentication_truncatedData() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n        SignRequest authentication = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse response = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n                \"\", KEY_HANDLE_BASE64);\n\n        u2f.finishSignature(authentication, response, new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0));\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void startSignature_compromisedDevice() throws Exception {\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n        DeviceRegistration deviceRegistration = u2f.finishRegistration(registerRequest, new RegisterResponse(AcmeKey.REGISTRATION_DATA_BASE64, AcmeKey.CLIENT_DATA_BASE64), TRUSTED_DOMAINS);\n        deviceRegistration.markCompromised();\n\n        u2f.startSignature(APP_ID_ENROLL, deviceRegistration);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void finishSignature_compromisedDevice() throws Exception {\n        SignRequest signRequest = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse tokenResponse = new SignResponse(CLIENT_DATA_SIGN_BASE64,\n                SIGN_RESPONSE_DATA_BASE64, KEY_HANDLE_BASE64);\n\n        DeviceRegistration deviceRegistration = new DeviceRegistration(KEY_HANDLE_BASE64, USER_PUBLIC_KEY_SIGN_HEX, ATTESTATION_CERTIFICATE, 0);\n        deviceRegistration.markCompromised();\n\n        u2f.finishSignature(signRequest, tokenResponse, deviceRegistration, allowedOrigins);\n    }\n\n    @Test\n    public void finishSignatureShouldDetectInvalidUserPresence() throws Exception {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        SignRequest signRequest = SignRequest.builder()\n                .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n                .appId(APP_ID_SIGN)\n                .keyHandle(KEY_HANDLE_BASE64)\n                .build();\n\n        SignResponse tokenResponse = new SignResponse(\n            CLIENT_DATA_SIGN_BASE64,\n            SIGN_RESPONSE_INVALID_USER_PRESENCE_BASE64,\n            KEY_HANDLE_BASE64\n        );\n\n        u2f.finishSignature(\n            signRequest,\n            tokenResponse,\n            new DeviceRegistration(\n                KEY_HANDLE_BASE64,\n                USER_PUBLIC_KEY_SIGN_HEX,\n                ATTESTATION_CERTIFICATE,\n                0\n            ),\n            allowedOrigins\n        );\n\n        fail(\"finishSignature did not detect a non-0x01 user presence byte in the sign response.\");\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void finishSignatureShouldDetectIncorrectDeviceRegistration() throws Exception {\n        SignRequest signRequest = SignRequest.builder()\n            .challenge(SERVER_CHALLENGE_SIGN_BASE64)\n            .appId(APP_ID_SIGN)\n            .keyHandle(KEY_HANDLE_BASE64)\n            .build();\n\n        SignResponse tokenResponse = new SignResponse(\n            CLIENT_DATA_SIGN_BASE64,\n            SIGN_RESPONSE_DATA_BASE64,\n            KEY_HANDLE_BASE64\n        );\n\n        u2f.finishSignature(\n            signRequest,\n            tokenResponse,\n            new DeviceRegistration(\n                \"ARGHABLARGHLER\",\n                USER_PUBLIC_KEY_SIGN_HEX,\n                ATTESTATION_CERTIFICATE,\n                0\n            ),\n            allowedOrigins\n        );\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/codec/RawCodecTest.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.codec;\n\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.Crypto;\nimport com.yubico.u2f.data.messages.key.RawSignResponse;\nimport com.yubico.u2f.data.messages.key.RawRegisterResponse;\nimport com.yubico.u2f.testdata.TestVectors;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.data.messages.key.CodecTestUtils.encodeSignResponse;\nimport static com.yubico.u2f.data.messages.key.CodecTestUtils.encodeRegisterResponse;\nimport static com.yubico.u2f.testdata.GnubbyKey.ATTESTATION_CERTIFICATE;\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\n\npublic class RawCodecTest {\n\n    Crypto crypto = new BouncyCastleCrypto();\n\n    @Test\n    public void testEncodeRegisterResponse() throws Exception {\n        RawRegisterResponse rawRegisterResponse = new RawRegisterResponse(USER_PUBLIC_KEY_REGISTER_HEX,\n                KEY_HANDLE, ATTESTATION_CERTIFICATE, SIGNATURE_REGISTER);\n        byte[] encodedBytes = encodeRegisterResponse(rawRegisterResponse);\n        assertArrayEquals(TestVectors.REGISTRATION_RESPONSE_DATA, encodedBytes);\n    }\n\n    @Test\n    public void testEncodeRegisterSignedBytes() throws Exception {\n        byte[] encodedBytes = RawRegisterResponse.packBytesToSign(APP_ID_ENROLL_SHA256,\n                CLIENT_DATA_ENROLL_SHA256, KEY_HANDLE, USER_PUBLIC_KEY_REGISTER_HEX);\n        assertArrayEquals(EXPECTED_REGISTER_SIGNED_BYTES, encodedBytes);\n    }\n\n    @Test\n    public void testDecodeRegisterResponse() throws Exception {\n        RawRegisterResponse rawRegisterResponse =\n                RawRegisterResponse.fromBase64(TestVectors.REGISTRATION_DATA_BASE64, crypto);\n\n        assertEquals(new RawRegisterResponse(USER_PUBLIC_KEY_REGISTER_HEX,\n                KEY_HANDLE, ATTESTATION_CERTIFICATE, SIGNATURE_REGISTER), rawRegisterResponse);\n    }\n\n    @Test\n    public void testEncodeSignResponse() throws Exception {\n        RawSignResponse rawSignResponse = new RawSignResponse(\n                RawSignResponse.USER_PRESENT_FLAG, COUNTER_VALUE, SIGNATURE_SIGN);\n\n        byte[] encodedBytes = encodeSignResponse(rawSignResponse);\n\n        assertArrayEquals(SIGN_RESPONSE_DATA, encodedBytes);\n    }\n\n    @Test\n    public void testDecodeSignResponse() throws Exception {\n        RawSignResponse rawSignResponse =\n                RawSignResponse.fromBase64(SIGN_RESPONSE_DATA_BASE64, crypto);\n\n        assertEquals(new RawSignResponse(RawSignResponse.USER_PRESENT_FLAG, COUNTER_VALUE,\n            SIGNATURE_SIGN), rawSignResponse);\n    }\n\n    @Test\n    public void testEncodeSignedBytes() throws Exception {\n        byte[] encodedBytes = RawSignResponse.packBytesToSign(APP_ID_SIGN_SHA256,\n                RawSignResponse.USER_PRESENT_FLAG, COUNTER_VALUE, CLIENT_DATA_SIGN_SHA256);\n\n        assertArrayEquals(EXPECTED_SIGN_SIGNED_BYTES, encodedBytes);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/codec/SerialCodecTest.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.codec;\n\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.Crypto;\nimport com.yubico.u2f.data.messages.key.CodecTestUtils;\nimport com.yubico.u2f.data.messages.key.RawSignResponse;\nimport com.yubico.u2f.data.messages.key.RawRegisterResponse;\nimport com.yubico.u2f.testdata.TestVectors;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.GnubbyKey.ATTESTATION_CERTIFICATE;\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\n\npublic class SerialCodecTest {\n\n    private static final Crypto crypto = new BouncyCastleCrypto();\n\n    @Test\n    public void testEncodeRegisterResponse() throws Exception {\n        RawRegisterResponse rawRegisterResponse = new RawRegisterResponse(USER_PUBLIC_KEY_REGISTER_HEX,\n                KEY_HANDLE, ATTESTATION_CERTIFICATE, SIGNATURE_REGISTER);\n\n        byte[] encodedBytes = CodecTestUtils.encodeRegisterResponse(rawRegisterResponse);\n\n        assertArrayEquals(TestVectors.REGISTRATION_RESPONSE_DATA, encodedBytes);\n    }\n\n    @Test\n    public void testEncodeRegisterSignedBytes() throws Exception {\n        byte[] encodedBytes = RawRegisterResponse.packBytesToSign(APP_ID_ENROLL_SHA256,\n                CLIENT_DATA_ENROLL_SHA256, KEY_HANDLE, USER_PUBLIC_KEY_REGISTER_HEX);\n\n        assertArrayEquals(EXPECTED_REGISTER_SIGNED_BYTES, encodedBytes);\n    }\n\n    @Test\n    public void testDecodeRegisterResponse() throws Exception {\n        RawRegisterResponse rawRegisterResponse =\n                RawRegisterResponse.fromBase64(TestVectors.REGISTRATION_DATA_BASE64, crypto);\n\n        assertEquals(new RawRegisterResponse(USER_PUBLIC_KEY_REGISTER_HEX,\n                KEY_HANDLE, ATTESTATION_CERTIFICATE, SIGNATURE_REGISTER), rawRegisterResponse);\n    }\n\n    @Test\n    public void testEncodeSignResponse() throws Exception {\n        RawSignResponse rawSignResponse = new RawSignResponse(\n                RawSignResponse.USER_PRESENT_FLAG, COUNTER_VALUE, SIGNATURE_SIGN);\n\n        byte[] encodedBytes = CodecTestUtils.encodeSignResponse(rawSignResponse);\n\n        assertArrayEquals(SIGN_RESPONSE_DATA, encodedBytes);\n    }\n\n    @Test\n    public void testDecodeSignResponse() throws Exception {\n        RawSignResponse rawSignResponse =\n                RawSignResponse.fromBase64(SIGN_RESPONSE_DATA_BASE64, crypto);\n\n        assertEquals(new RawSignResponse(RawSignResponse.USER_PRESENT_FLAG, COUNTER_VALUE,\n            SIGNATURE_SIGN), rawSignResponse);\n    }\n\n    @Test\n    public void testEncodeSignedBytes() throws Exception {\n        byte[] encodedBytes = RawSignResponse.packBytesToSign(APP_ID_SIGN_SHA256,\n                RawSignResponse.USER_PRESENT_FLAG, COUNTER_VALUE, CLIENT_DATA_SIGN_SHA256);\n\n        assertArrayEquals(EXPECTED_SIGN_SIGNED_BYTES, encodedBytes);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/DeviceRegistrationTest.java",
    "content": "package com.yubico.u2f.data;\n\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.SerializationFeature;\nimport com.yubico.u2f.data.messages.key.Client;\nimport com.yubico.u2f.exceptions.InvalidDeviceCounterException;\nimport com.yubico.u2f.softkey.SoftKey;\n\nimport org.junit.Test;\n\nimport static junit.framework.Assert.fail;\nimport static junit.framework.TestCase.assertFalse;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\npublic class DeviceRegistrationTest {\n\n    @Test\n    public void shouldSerialize() throws Exception {\n        DeviceRegistration deviceRegistration = getDeviceRegistration();\n\n        String json = deviceRegistration.toJson();\n\n        DeviceRegistration deserializedDeviceRegistration = DeviceRegistration.fromJson(json);\n        assertEquals(deviceRegistration.getKeyHandle(), deserializedDeviceRegistration.getKeyHandle());\n        assertEquals(deviceRegistration.getPublicKey(), deserializedDeviceRegistration.getPublicKey());\n        assertEquals(deviceRegistration.getCounter(), deserializedDeviceRegistration.getCounter());\n        assertNotEquals(deviceRegistration, deserializedDeviceRegistration);  // Cert should be missing\n    }\n\n    @Test\n    public void shouldSerializeWithAttestationCertificate() throws Exception {\n        DeviceRegistration deviceRegistration = getDeviceRegistration();\n\n        String json = deviceRegistration.toJsonWithAttestationCert();\n\n        DeviceRegistration deserializedDeviceRegistration = DeviceRegistration.fromJson(json);\n        assertEquals(deviceRegistration, deserializedDeviceRegistration);\n    }\n\n    @Test\n    public void serializationRoundTripWithJackson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        objectMapper.disable(DeserializationFeature.UNWRAP_ROOT_VALUE);\n        objectMapper.disable(SerializationFeature.WRAP_ROOT_VALUE);\n\n        DeviceRegistration input = getDeviceRegistration();\n        String json = objectMapper.writeValueAsString(input);\n        DeviceRegistration output = objectMapper.readValue(json, DeviceRegistration.class);\n\n        assertEquals(input, output);\n    }\n\n    @Test\n    public void shouldAcceptValidCounters() throws Exception {\n        DeviceRegistration deviceRegistration = getDeviceRegistration();\n\n        deviceRegistration.checkAndUpdateCounter(3);\n        deviceRegistration.checkAndUpdateCounter(10);\n        deviceRegistration.checkAndUpdateCounter(97);\n\n        assertFalse(deviceRegistration.isCompromised());\n    }\n\n    @Test\n    public void shouldDetectInvalidCounters() throws Exception {\n        DeviceRegistration deviceRegistration = getDeviceRegistration();\n\n        deviceRegistration.checkAndUpdateCounter(9);\n\n        try {\n            deviceRegistration.checkAndUpdateCounter(9);\n            fail();\n        } catch (InvalidDeviceCounterException e) {\n            assertTrue(deviceRegistration.isCompromised());\n        }\n    }\n\n    @Test\n    public void equalsAndHashCodeIgnoreCounter() {\n        DeviceRegistration dr1 = new DeviceRegistration(\"A\", \"B\", \"C\", 0, false);\n        DeviceRegistration dr2 = new DeviceRegistration(\"A\", \"B\", \"C\", 1, false);\n\n        assertEquals(dr1, dr2);\n        assertEquals(dr1.hashCode(), dr2.hashCode());\n    }\n\n    @Test\n    public void equalsAndHashCodeIgnoreCompromisedFlag() {\n        DeviceRegistration dr1 = new DeviceRegistration(\"A\", \"B\", \"C\", 0, false);\n        DeviceRegistration dr2 = new DeviceRegistration(\"A\", \"B\", \"C\", 1, true);\n\n        assertEquals(dr1, dr2);\n        assertEquals(dr1.hashCode(), dr2.hashCode());\n    }\n\n    @Test\n    public void equalsAndHashCodeDoNotIgnoreKeyHandle() {\n        DeviceRegistration dr1 = new DeviceRegistration(\"A\", \"B\", \"C\", 0, false);\n        DeviceRegistration dr2 = new DeviceRegistration(\"D\", \"B\", \"C\", 0, false);\n\n        assertNotEquals(dr1, dr2);\n        assertNotEquals(dr1.hashCode(), dr2.hashCode());\n    }\n\n    @Test\n    public void equalsAndHashCodeDoNotIgnorePublicKey() {\n        DeviceRegistration dr1 = new DeviceRegistration(\"A\", \"B\", \"C\", 0, false);\n        DeviceRegistration dr2 = new DeviceRegistration(\"A\", \"D\", \"C\", 0, false);\n\n        assertNotEquals(dr1, dr2);\n        assertNotEquals(dr1.hashCode(), dr2.hashCode());\n    }\n\n    @Test\n    public void equalsAndHashCodeDoNotIgnoreAttestationCert() {\n        DeviceRegistration dr1 = new DeviceRegistration(\"A\", \"B\", \"C\", 0, false);\n        DeviceRegistration dr2 = new DeviceRegistration(\"A\", \"B\", \"D\", 0, false);\n\n        assertNotEquals(dr1, dr2);\n        assertNotEquals(dr1.hashCode(), dr2.hashCode());\n    }\n\n    @Test\n    public void toStringDoesNotReturnNull() {\n        assertNotNull(new DeviceRegistration(\"A\", \"B\", null, 0, false).toString());\n    }\n\n    private DeviceRegistration getDeviceRegistration() throws Exception {\n        Client client = new Client(new SoftKey());\n        return client.register();\n    }\n}"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/ClientDataTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.google.common.base.Optional;\nimport com.google.common.collect.ImmutableSet;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport org.junit.Test;\n\nimport java.util.Set;\n\nimport static com.yubico.u2f.data.messages.ClientData.canonicalizeOrigin;\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.junit.Assert.assertEquals;\n\npublic class ClientDataTest {\n\n    @Test\n    public void shouldCanonicalizeOrigin() throws U2fBadInputException {\n        assertEquals(\"http://example.com\", canonicalizeOrigin(\"http://example.com\"));\n        assertEquals(\"http://example.com\", canonicalizeOrigin(\"http://example.com/\"));\n        assertEquals(\"http://example.com\", canonicalizeOrigin(\"http://example.com/foo\"));\n        assertEquals(\"http://example.com\", canonicalizeOrigin(\"http://example.com/foo?bar=b\"));\n        assertEquals(\"http://example.com\", canonicalizeOrigin(\"http://example.com/foo#fragment\"));\n        assertEquals(\"https://example.com\", canonicalizeOrigin(\"https://example.com\"));\n        assertEquals(\"https://example.com\", canonicalizeOrigin(\"https://example.com/foo\"));\n        assertEquals(\"android:apk-key-hash:2jmj7l5rSw0yVb/vlWAYkK/YBwk\",\n                canonicalizeOrigin(\"android:apk-key-hash:2jmj7l5rSw0yVb/vlWAYkK/YBwk\"));\n    }\n\n    @Test\n    public void shouldCheckContent() throws U2fBadInputException {\n        ClientData clientData = new ClientData(CLIENT_DATA_REGISTRATION_BASE64);\n        clientData.checkContent(\"navigator.id.finishEnrollment\", SERVER_CHALLENGE_REGISTER_BASE64, Optional.<Set<String>>of(ImmutableSet.of(APP_ID_ENROLL)));\n    }\n}"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/RegisterRequestDataTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Iterables;\nimport com.yubico.u2f.TestUtils;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.crypto.ChallengeGenerator;\nimport com.yubico.u2f.crypto.RandomChallengeGenerator;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.TestVectors.APP_ID_ENROLL;\nimport static com.yubico.u2f.testdata.TestVectors.SERVER_CHALLENGE_REGISTER_BASE64;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class RegisterRequestDataTest {\n    public static final String KEY_HANDLE = \"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\";\n    public static final String JSON = \"{\\\"registeredKeys\\\":[{\\\"keyHandle\\\":\\\"\" + KEY_HANDLE + \"\\\",\\\"version\\\":\\\"U2F_V2\\\"}],\\\"registerRequests\\\":[{\\\"challenge\\\":\\\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\\\",\\\"appId\\\":\\\"http://example.com\\\",\\\"version\\\":\\\"U2F_V2\\\"}]}\";\n\n    @Test\n    public void testGetters() throws Exception {\n        DeviceRegistration device = mock(DeviceRegistration.class);\n        when(device.getKeyHandle()).thenReturn(KEY_HANDLE);\n\n        U2fPrimitives primitives = mock(U2fPrimitives.class);\n        ChallengeGenerator challengeGenerator = mock(ChallengeGenerator.class);\n\n        byte[] challenge = U2fB64Encoding.decode(SERVER_CHALLENGE_REGISTER_BASE64);\n        when(challengeGenerator.generateChallenge()).thenReturn(challenge);\n        SignRequest signRequest = SignRequest.fromJson(SignRequestTest.JSON);\n        when(primitives.startSignature(APP_ID_ENROLL, device)).thenReturn(signRequest);\n        RegisterRequest registerRequest = RegisterRequest.fromJson(RegisterRequestTest.JSON);\n        when(primitives.startRegistration(APP_ID_ENROLL, challenge)).thenReturn(registerRequest);\n\n        RegisterRequestData requestData = new RegisterRequestData(APP_ID_ENROLL, ImmutableList.of(device), primitives, challengeGenerator);\n\n        assertEquals(SERVER_CHALLENGE_REGISTER_BASE64, requestData.getRequestId());\n        RegisterRequest registerRequest2 = Iterables.getOnlyElement(requestData.getRegisterRequests());\n        assertEquals(registerRequest, registerRequest2);\n    }\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        RegisterRequestData requestData = RegisterRequestData.fromJson(JSON);\n        RegisterRequestData requestData2 = objectMapper.readValue(requestData.toJson(), RegisterRequestData.class);\n\n        assertEquals(requestData, requestData2);\n        assertEquals(requestData.getRequestId(), requestData2.getRequestId());\n        assertEquals(requestData.toJson(), objectMapper.writeValueAsString(requestData));\n        assertEquals(KEY_HANDLE, requestData.getRegisteredKeys().get(0).getKeyHandle());\n    }\n\n    @Test\n    public void testJavaSerializer() throws Exception {\n        RegisterRequestData requestData = RegisterRequestData.fromJson(JSON);\n        RegisterRequestData requestData2 = TestUtils.clone(requestData);\n\n        assertEquals(requestData, requestData2);\n        assertEquals(requestData.getRequestId(), requestData2.getRequestId());\n    }\n\n    private DeviceRegistration mockDevice(final String keyHandle, boolean compromised) {\n        DeviceRegistration device = mock(DeviceRegistration.class);\n        when(device.getKeyHandle()).thenReturn(keyHandle);\n        when(device.isCompromised()).thenReturn(compromised);\n        return device;\n    }\n\n    @Test\n    public void testConstructorAddsOneRegisteredKeyForEachGivenNonCompromisedDeviceRegistration() {\n        DeviceRegistration good1 = mockDevice(\"A\", false);\n        DeviceRegistration good2 = mockDevice(\"B\", false);\n        DeviceRegistration bad1 = mockDevice(\"C\", true);\n        DeviceRegistration bad2 = mockDevice(\"D\", true);\n\n        RegisterRequestData result = new RegisterRequestData(\n            \"AppId\",\n            ImmutableList.of(good1, bad1, bad2, good2),\n            new U2fPrimitives(),\n            new RandomChallengeGenerator()\n        );\n\n        assertEquals(2, result.getRegisteredKeys().size());\n        assertTrue(result.getRegisteredKeys().contains(new RegisteredKey(U2fPrimitives.U2F_VERSION, \"A\", null, null)));\n        assertTrue(result.getRegisteredKeys().contains(new RegisteredKey(U2fPrimitives.U2F_VERSION, \"B\", null, null)));\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/RegisterRequestTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.yubico.u2f.TestUtils;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.TestVectors.APP_ID_ENROLL;\nimport static com.yubico.u2f.testdata.TestVectors.SERVER_CHALLENGE_REGISTER_BASE64;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\npublic class RegisterRequestTest {\n\n    public static final String JSON = \"{\\\"challenge\\\":\\\"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\\\",\\\"appId\\\":\\\"http://example.com\\\",\\\"version\\\":\\\"U2F_V2\\\"}\";\n\n    @Test\n    public void testGetters() throws Exception {\n        RegisterRequest registerRequest = new RegisterRequest(SERVER_CHALLENGE_REGISTER_BASE64, APP_ID_ENROLL);\n        assertEquals(SERVER_CHALLENGE_REGISTER_BASE64, registerRequest.getChallenge());\n        assertEquals(APP_ID_ENROLL, registerRequest.getAppId());\n        assertNotNull(SERVER_CHALLENGE_REGISTER_BASE64, registerRequest.getRequestId());\n    }\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        RegisterRequest registerRequest = RegisterRequest.fromJson(JSON);\n        RegisterRequest registerRequest2 = objectMapper.readValue(registerRequest.toJson(), RegisterRequest.class);\n\n        assertEquals(registerRequest.getRequestId(), registerRequest2.getRequestId());\n        assertEquals(registerRequest.getChallenge(), registerRequest2.getChallenge());\n        assertEquals(registerRequest.getAppId(), registerRequest2.getAppId());\n        assertEquals(registerRequest.toJson(), objectMapper.writeValueAsString(registerRequest));\n    }\n\n    @Test\n    public void testJavaSerializer() throws Exception {\n        RegisterRequest registerRequest = RegisterRequest.fromJson(JSON);\n        RegisterRequest registerRequest2 = TestUtils.clone(registerRequest);\n\n        assertEquals(registerRequest, registerRequest2);\n    }\n}"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/RegisterResponseTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.yubico.u2f.TestUtils;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.fail;\n\npublic class RegisterResponseTest {\n    public static final String JSON = \"{\\\"registrationData\\\":\\\"BQSxdLxJx8olS3DS5cIHzunPF0gg69d-o8ZVCMJtpRtlfBzGuVL4YhaXk2SC2gptPTgmpZCV2vbNfAPi5gOF0vbZQCpVLf23R37WX9hBM_hhlgELIhW1faddMVt7no_i45JaYBlVG6th0WWRZZy68AtJUPer_mZg4uAG92hot3LXDCUwggE8MIHkoAMCAQICCkeQEoAAEVWVc1IwCgYIKoZIzj0EAwIwFzEVMBMGA1UEAxMMR251YmJ5IFBpbG90MB4XDTEyMDgxNDE4MjkzMloXDTEzMDgxNDE4MjkzMlowMTEvMC0GA1UEAxMmUGlsb3RHbnViYnktMC40LjEtNDc5MDEyODAwMDExNTU5NTczNTIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASNYX5lyVCOZLzFZzrIKmeZ2jwURmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEBMAoGCCqGSM49BAMCA0cAMEQCIGDNtgYenCImLRqsHZbYxwgpsjZlMd2iaIMsuDa80w36AiBjGxRZ8J5jMAVXIsjYm39IiDuQibiNYNHZeVkCswQQ3zBFAiAUcYmbzDmH5i6CAsmznDPBkDP3NANS26gPyrAX25Iw5AIhAIJnfWc9iRkzreb2F-Xb3i4kfnBCP9WteASm09OWHvhx\\\",\\\"clientData\\\":\\\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoib3BzWHFVaWZEcmlBQW1XY2xpbmZiUzBlLVVTWTBDZ3lKSGVfT3RkN3o4byIsImNpZF9wdWJrZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJIelF3bGZYWDdRNFM1TXRDQ25aVU5CdzNSTXpQTzl0T3lXakJxUmw0dEo4IiwieSI6IlhWZ3VHRkxJWngxZlhnM3dOcWZkYm43NWhpNC1fNy1CeGhNbGp3NDJIdDQifSwib3JpZ2luIjoiaHR0cDovL2V4YW1wbGUuY29tIn0\\\"}\";\n\n    @Test\n    public void testGetters() throws Exception {\n        RegisterResponse registerResponse = new RegisterResponse(REGISTRATION_DATA_BASE64, CLIENT_DATA_SIGN_BASE64);\n\n        assertEquals(CLIENT_DATA_SIGN, registerResponse.getClientData().toString());\n        assertEquals(SERVER_CHALLENGE_SIGN_BASE64, registerResponse.getRequestId());\n        assertEquals(REGISTRATION_DATA_BASE64, registerResponse.getRegistrationData());\n    }\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        RegisterResponse registerResponse = RegisterResponse.fromJson(JSON);\n        RegisterResponse registerResponse2 = objectMapper.readValue(registerResponse.toJson(), RegisterResponse.class);\n\n        assertEquals(registerResponse, registerResponse2);\n        assertEquals(registerResponse.getRequestId(), registerResponse2.getRequestId());\n        assertEquals(registerResponse.toJson(), objectMapper.writeValueAsString(registerResponse));\n    }\n\n    @Test\n    public void testJavaSerializer() throws Exception {\n        RegisterResponse registerResponse = RegisterResponse.fromJson(JSON);\n        RegisterResponse registerResponse2 = TestUtils.clone(registerResponse);\n\n        assertEquals(registerResponse, registerResponse2);\n        assertEquals(registerResponse.getRequestId(), registerResponse2.getRequestId());\n    }\n\n\n    @Test(expected = IllegalArgumentException.class)\n    public void fromJsonDetectsTooLongJsonContent() throws U2fBadInputException {\n        RegisterResponse.fromJson(makeLongJson(20000));\n        fail(\"fromJson did not detect too long JSON content.\");\n    }\n\n    @Test\n    public void fromJsonAllowsShortJsonContent() throws U2fBadInputException {\n        assertNotNull(RegisterResponse.fromJson(makeLongJson(19999)));\n    }\n\n    private String makeLongJson(int totalLength) {\n        final String jsonPrefix = \"{\\\"registrationData\\\":\\\"\";\n        final String jsonSuffix = \"\\\",\\\"clientData\\\":\\\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoib3BzWHFVaWZEcmlBQW1XY2xpbmZiUzBlLVVTWTBDZ3lKSGVfT3RkN3o4byIsImNpZF9wdWJrZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJIelF3bGZYWDdRNFM1TXRDQ25aVU5CdzNSTXpQTzl0T3lXakJxUmw0dEo4IiwieSI6IlhWZ3VHRkxJWngxZlhnM3dOcWZkYm43NWhpNC1fNy1CeGhNbGp3NDJIdDQifSwib3JpZ2luIjoiaHR0cDovL2V4YW1wbGUuY29tIn0\\\"}\";\n        final int infixLength = totalLength - jsonPrefix.length() - jsonSuffix.length();\n        return jsonPrefix + String.format(\"%0\" + infixLength + \"d\", 0).replace(\"0\", \"a\") + jsonSuffix;\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/SignRequestDataTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.Iterables;\nimport com.yubico.u2f.TestUtils;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.crypto.ChallengeGenerator;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.NoEligibleDevicesException;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.TestVectors.APP_ID_SIGN;\nimport static com.yubico.u2f.testdata.TestVectors.SERVER_CHALLENGE_SIGN_BASE64;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\npublic class SignRequestDataTest {\n    public static final String JSON = \"{\\\"signRequests\\\":[{\\\"challenge\\\":\\\"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o\\\",\\\"appId\\\":\\\"https://gstatic.com/securitykey/a/example.com\\\",\\\"keyHandle\\\":\\\"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\\\",\\\"version\\\":\\\"U2F_V2\\\"}]}\";\n\n    @Test\n    public void testGetters() throws Exception {\n        DeviceRegistration device = mock(DeviceRegistration.class);\n        U2fPrimitives primitives = mock(U2fPrimitives.class);\n        ChallengeGenerator challengeGenerator = mock(ChallengeGenerator.class);\n\n        byte[] challenge = U2fB64Encoding.decode(SERVER_CHALLENGE_SIGN_BASE64);\n        when(challengeGenerator.generateChallenge()).thenReturn(challenge);\n        SignRequest signRequest = SignRequest.fromJson(SignRequestTest.JSON);\n        when(primitives.startSignature(APP_ID_SIGN, device, challenge)).thenReturn(signRequest);\n\n        SignRequestData requestData = new SignRequestData(APP_ID_SIGN, ImmutableList.of(device), primitives, challengeGenerator);\n\n        assertEquals(SERVER_CHALLENGE_SIGN_BASE64, requestData.getRequestId());\n        SignRequest signRequest2 = Iterables.getOnlyElement(requestData.getSignRequests());\n        assertEquals(signRequest, signRequest2);\n    }\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        SignRequestData requestData = SignRequestData.fromJson(JSON);\n        SignRequestData requestData2 = objectMapper.readValue(requestData.toJson(), SignRequestData.class);\n\n        assertEquals(requestData.getRequestId(), requestData2.getRequestId());\n        assertEquals(requestData, requestData2);\n        assertEquals(requestData.toJson(), objectMapper.writeValueAsString(requestData));\n    }\n\n    @Test\n    public void testJavaSerializer() throws Exception {\n        SignRequestData requestData = SignRequestData.fromJson(JSON);\n        SignRequestData requestData2 = TestUtils.clone(requestData);\n\n        assertEquals(requestData.getRequestId(), requestData2.getRequestId());\n        assertEquals(requestData, requestData2);\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void getSignRequestChecksResponseId() throws Exception {\n        SignRequestData requestData = SignRequestData.fromJson(JSON);\n\n        final String clientDataJson = \"{\\\"typ\\\":\\\"navigator.id.getAssertion\\\",\\\"challenge\\\":\\\"OpsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o\\\",\\\"cid_pubkey\\\":{\\\"kty\\\":\\\"EC\\\",\\\"crv\\\":\\\"P-256\\\",\\\"x\\\":\\\"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8\\\",\\\"y\\\":\\\"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4\\\"},\\\"origin\\\":\\\"http://example.com\\\"}\";\n        final String signResponseJson = \"{\\\"clientData\\\":\\\"\" + U2fB64Encoding.encode(clientDataJson.getBytes(\"UTF-8\")) + \"\\\",\\\"signatureData\\\":\\\"\\\",\\\"keyHandle\\\":\\\"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\\\"}\";\n        requestData.getSignRequest(SignResponse.fromJson(signResponseJson));\n\n        fail(\"getSignRequest did not detect wrong request ID.\");\n    }\n\n    @Test\n    public void testFailureModesAreIdentifiable() throws Exception {\n\n        byte[] challenge = U2fB64Encoding.decode(SERVER_CHALLENGE_SIGN_BASE64);\n        ChallengeGenerator challengeGenerator = mock(ChallengeGenerator.class);\n        when(challengeGenerator.generateChallenge()).thenReturn(challenge);\n\n        try {\n            new SignRequestData(APP_ID_SIGN, ImmutableList.<DeviceRegistration>of(), mock(U2fPrimitives.class), challengeGenerator);\n        } catch (NoEligibleDevicesException e) {\n            assertFalse(e.hasDevices());\n        }\n\n        DeviceRegistration compromisedDevice = mock(DeviceRegistration.class);\n        when(compromisedDevice.isCompromised()).thenReturn(true);\n\n        try {\n            new SignRequestData(APP_ID_SIGN, ImmutableList.of(compromisedDevice), mock(U2fPrimitives.class), challengeGenerator);\n        } catch (NoEligibleDevicesException e) {\n            assertTrue(e.hasDevices());\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/SignRequestTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.yubico.u2f.TestUtils;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.junit.Assert.assertEquals;\n\npublic class SignRequestTest {\n\n    public static final String JSON = \"{\\\"challenge\\\":\\\"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o\\\",\\\"appId\\\":\\\"https://gstatic.com/securitykey/a/example.com\\\",\\\"keyHandle\\\":\\\"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\\\",\\\"version\\\":\\\"U2F_V2\\\"}\";\n\n    @Test\n    public void testGetters() throws Exception {\n        SignRequest signRequest = SignRequest.builder().challenge(SERVER_CHALLENGE_SIGN_BASE64).appId(APP_ID_SIGN).keyHandle(KEY_HANDLE_BASE64).build();\n        assertEquals(SERVER_CHALLENGE_SIGN_BASE64, signRequest.getChallenge());\n        assertEquals(SERVER_CHALLENGE_SIGN_BASE64, signRequest.getRequestId());\n        assertEquals(APP_ID_SIGN, signRequest.getAppId());\n        assertEquals(KEY_HANDLE_BASE64, signRequest.getKeyHandle());\n    }\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        SignRequest signRequest = SignRequest.fromJson(JSON);\n        SignRequest signRequest2 = objectMapper.readValue(signRequest.toJson(), SignRequest.class);\n\n        assertEquals(signRequest.getRequestId(), signRequest2.getRequestId());\n        assertEquals(signRequest, signRequest2);\n        assertEquals(signRequest.toJson(), objectMapper.writeValueAsString(signRequest));\n    }\n\n    @Test\n    public void testJavaSerializer() throws Exception {\n        SignRequest signRequest = SignRequest.fromJson(JSON);\n        SignRequest signRequest2 = TestUtils.clone(signRequest);\n\n        assertEquals(signRequest.getRequestId(), signRequest2.getRequestId());\n        assertEquals(signRequest, signRequest2);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/SignResponseTest.java",
    "content": "package com.yubico.u2f.data.messages;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.yubico.u2f.TestUtils;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport org.junit.Test;\n\nimport static com.yubico.u2f.testdata.TestVectors.*;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.fail;\n\npublic class SignResponseTest {\n    public static final String JSON = \"{\\\"clientData\\\":\\\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoib3BzWHFVaWZEcmlBQW1XY2xpbmZiUzBlLVVTWTBDZ3lKSGVfT3RkN3o4byIsImNpZF9wdWJrZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJIelF3bGZYWDdRNFM1TXRDQ25aVU5CdzNSTXpQTzl0T3lXakJxUmw0dEo4IiwieSI6IlhWZ3VHRkxJWngxZlhnM3dOcWZkYm43NWhpNC1fNy1CeGhNbGp3NDJIdDQifSwib3JpZ2luIjoiaHR0cDovL2V4YW1wbGUuY29tIn0\\\",\\\"signatureData\\\":\\\"\\\",\\\"keyHandle\\\":\\\"KlUt_bdHftZf2EEz-GGWAQsiFbV9p10xW3uej-LjklpgGVUbq2HRZZFlnLrwC0lQ96v-ZmDi4Ab3aGi3ctcMJQ\\\"}\";\n\n    @Test\n    public void testGetters() throws Exception {\n        SignResponse signResponse = new SignResponse(CLIENT_DATA_SIGN_BASE64, \"\", KEY_HANDLE_BASE64);\n\n        assertEquals(CLIENT_DATA_SIGN, signResponse.getClientData().toString());\n        assertEquals(KEY_HANDLE_BASE64, signResponse.getKeyHandle());\n    }\n\n    @Test\n    public void testToAndFromJson() throws Exception {\n        ObjectMapper objectMapper = new ObjectMapper();\n        SignResponse signResponse = SignResponse.fromJson(JSON);\n        SignResponse signResponse2 = objectMapper.readValue(signResponse.toJson(), SignResponse.class);\n\n        assertEquals(signResponse, signResponse2);\n        assertEquals(signResponse.getRequestId(), signResponse2.getRequestId());\n        assertEquals(signResponse.toJson(), objectMapper.writeValueAsString(signResponse));\n    }\n\n    @Test\n    public void testJavaSerializer() throws Exception {\n        SignResponse signResponse = SignResponse.fromJson(JSON);\n        SignResponse signResponse2 = TestUtils.clone(signResponse);\n\n        assertEquals(signResponse, signResponse2);\n        assertEquals(signResponse.getRequestId(), signResponse2.getRequestId());\n    }\n\n    @Test(expected = IllegalArgumentException.class)\n    public void fromJsonDetectsTooLongJsonContent() throws U2fBadInputException {\n        SignResponse.fromJson(makeLongJson(20000));\n        fail(\"fromJson did not detect too long JSON content.\");\n    }\n\n    @Test\n    public void fromJsonAllowsShortJsonContent() throws U2fBadInputException {\n        assertNotNull(SignResponse.fromJson(makeLongJson(19999)));\n    }\n\n    private String makeLongJson(int totalLength) {\n        final String jsonPrefix = \"{\\\"clientData\\\":\\\"eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoib3BzWHFVaWZEcmlBQW1XY2xpbmZiUzBlLVVTWTBDZ3lKSGVfT3RkN3o4byIsImNpZF9wdWJrZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJIelF3bGZYWDdRNFM1TXRDQ25aVU5CdzNSTXpQTzl0T3lXakJxUmw0dEo4IiwieSI6IlhWZ3VHRkxJWngxZlhnM3dOcWZkYm43NWhpNC1fNy1CeGhNbGp3NDJIdDQifSwib3JpZ2luIjoiaHR0cDovL2V4YW1wbGUuY29tIn0\\\",\\\"signatureData\\\":\\\"\\\",\\\"keyHandle\\\":\\\"\";\n        final String jsonSuffix = \"\\\"}\";\n        final int infixLength = totalLength - jsonPrefix.length() - jsonSuffix.length();\n        return jsonPrefix + String.format(\"%0\" + infixLength + \"d\", 0).replace(\"0\", \"a\") + jsonSuffix;\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/json/JsonSerializableTest.java",
    "content": "package com.yubico.u2f.data.messages.json;\n\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class JsonSerializableTest {\n\n    private static class Thing extends JsonSerializable {\n        @JsonProperty final String foo;\n        private Thing(@JsonProperty(\"foo\") String foo) {\n            this.foo = foo;\n        }\n    }\n\n    @Test\n    public void toStringReturnsJson() {\n        assertEquals(\"{\\\"foo\\\":\\\"bar\\\"}\", new Thing(\"bar\").toString());\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/key/Client.java",
    "content": "package com.yubico.u2f.data.messages.key;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.common.collect.ImmutableSet;\nimport com.google.common.io.ByteArrayDataOutput;\nimport com.google.common.io.ByteStreams;\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.SignRequest;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.RegisterRequest;\nimport com.yubico.u2f.data.messages.RegisterResponse;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport com.yubico.u2f.softkey.SoftKey;\n\nimport java.nio.ByteBuffer;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.X509Certificate;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class Client {\n    public static final byte REGISTRATION_RESERVED_BYTE_VALUE = (byte) 0x05;\n    public static final ImmutableSet<String> TRUSTED_DOMAINS = ImmutableSet.of(\"http://example.com\");\n    public static final String APP_ID = \"my-app\";\n\n    private final BouncyCastleCrypto crypto = new BouncyCastleCrypto();\n    private final SoftKey key;\n    private final U2fPrimitives u2f = new U2fPrimitives();\n    private final ObjectMapper objectMapper = new ObjectMapper();\n\n    public Client(SoftKey key) {\n        this.key = key;\n    }\n\n    public static byte[] encodeRegisterResponse(RawRegisterResponse rawRegisterResponse)\n            throws U2fBadInputException {\n        byte[] userPublicKey = rawRegisterResponse.userPublicKey;\n        byte[] keyHandle = rawRegisterResponse.keyHandle;\n        X509Certificate attestationCertificate = rawRegisterResponse.attestationCertificate;\n        byte[] signature = rawRegisterResponse.signature;\n\n        byte[] attestationCertificateBytes;\n        try {\n            attestationCertificateBytes = attestationCertificate.getEncoded();\n        } catch (CertificateEncodingException e) {\n            throw new U2fBadInputException(\"Error when encoding attestation certificate.\", e);\n        }\n\n        if (keyHandle.length > 255) {\n            throw new U2fBadInputException(\"keyHandle length cannot be longer than 255 bytes!\");\n        }\n\n        byte[] result = new byte[1 + userPublicKey.length + 1 + keyHandle.length\n                + attestationCertificateBytes.length + signature.length];\n        ByteBuffer.wrap(result)\n                .put(REGISTRATION_RESERVED_BYTE_VALUE)\n                .put(userPublicKey)\n                .put((byte) keyHandle.length)\n                .put(keyHandle)\n                .put(attestationCertificateBytes)\n                .put(signature);\n        return result;\n    }\n\n    public static RegisterResponse encodeTokenRegistrationResponse(String clientDataJson, RawRegisterResponse registerResponse) throws U2fBadInputException {\n        byte[] rawRegisterResponse = Client.encodeRegisterResponse(registerResponse);\n        String rawRegisterResponseBase64 = U2fB64Encoding.encode(rawRegisterResponse);\n        String clientDataBase64 = U2fB64Encoding.encode(clientDataJson.getBytes());\n        return new RegisterResponse(rawRegisterResponseBase64, clientDataBase64);\n    }\n\n    public DeviceRegistration register() throws Exception {\n        RegisterRequest registerRequest = u2f.startRegistration(APP_ID);\n\n        Map<String, String> clientData = new HashMap<String, String>();\n        clientData.put(\"typ\", \"navigator.id.finishEnrollment\");\n        clientData.put(\"challenge\", registerRequest.getChallenge());\n        clientData.put(\"origin\", \"http://example.com\");\n        String clientDataJson = objectMapper.writeValueAsString(clientData);\n\n        byte[] clientParam = crypto.hash(clientDataJson);\n        byte[] appParam = crypto.hash(registerRequest.getAppId());\n\n        RawRegisterResponse rawRegisterResponse = key.register(new com.yubico.u2f.softkey.messages.RegisterRequest(appParam, clientParam));\n\n        // client encodes data\n        RegisterResponse tokenResponse = Client.encodeTokenRegistrationResponse(clientDataJson, rawRegisterResponse);\n\n        return u2f.finishRegistration(registerRequest, tokenResponse, TRUSTED_DOMAINS);\n    }\n\n    public SignResponse sign(DeviceRegistration registeredDevice, SignRequest startedSignature) throws Exception {\n        Map<String, String> clientData = new HashMap<String, String>();\n        clientData.put(\"typ\", \"navigator.id.getAssertion\");\n        clientData.put(\"challenge\", startedSignature.getChallenge());\n        clientData.put(\"origin\", \"http://example.com\");\n        String clientDataJson = objectMapper.writeValueAsString(clientData);\n\n\n        byte[] clientParam = crypto.hash(clientDataJson);\n        byte[] appParam = crypto.hash(startedSignature.getAppId());\n        com.yubico.u2f.softkey.messages.SignRequest signRequest = new com.yubico.u2f.softkey.messages.SignRequest((byte) 0x01, clientParam, appParam, U2fB64Encoding.decode(registeredDevice.getKeyHandle()));\n\n        RawSignResponse rawSignResponse = key.sign(signRequest);\n\n        String clientDataBase64 = U2fB64Encoding.encode(clientDataJson.getBytes());\n        ByteArrayDataOutput authData = ByteStreams.newDataOutput();\n        authData.write(rawSignResponse.getUserPresence());\n        authData.writeInt((int) rawSignResponse.getCounter());\n        authData.write(rawSignResponse.getSignature());\n\n        return new SignResponse(\n                clientDataBase64,\n                U2fB64Encoding.encode(authData.toByteArray()),\n                startedSignature.getKeyHandle()\n        );\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/key/CodecTestUtils.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.data.messages.key;\n\nimport com.google.common.io.ByteArrayDataOutput;\nimport com.google.common.io.ByteStreams;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport java.security.cert.CertificateEncodingException;\n\npublic class CodecTestUtils {\n    public static byte[] encodeSignResponse(RawSignResponse rawSignResponse) {\n        ByteArrayDataOutput encoded = ByteStreams.newDataOutput();\n        encoded.write(rawSignResponse.getUserPresence());\n        encoded.writeInt((int) rawSignResponse.getCounter());\n        encoded.write(rawSignResponse.getSignature());\n        return encoded.toByteArray();\n    }\n\n    public static byte[] encodeRegisterResponse(RawRegisterResponse rawRegisterResponse) throws U2fBadInputException {\n        byte[] keyHandle = rawRegisterResponse.keyHandle;\n        if (keyHandle.length > 255) {\n            throw new U2fBadInputException(\"keyHandle length cannot be longer than 255 bytes!\");\n        }\n\n        try {\n            ByteArrayDataOutput encoded = ByteStreams.newDataOutput();\n            encoded.write(RawRegisterResponse.REGISTRATION_RESERVED_BYTE_VALUE);\n            encoded.write(rawRegisterResponse.userPublicKey);\n            encoded.write((byte) keyHandle.length);\n            encoded.write(keyHandle);\n            encoded.write(rawRegisterResponse.attestationCertificate.getEncoded());\n            encoded.write(rawRegisterResponse.signature);\n            return encoded.toByteArray();\n        } catch (CertificateEncodingException e) {\n            throw new U2fBadInputException(\"Error when encoding attestation certificate.\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/key/util/CertificateParserTest.java",
    "content": "package com.yubico.u2f.data.messages.key.util;\n\nimport java.security.cert.CertificateException;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertNotNull;\n\npublic class CertificateParserTest {\n\n    private static final String ATTESTATION_CERT = \"MIICGzCCAQWgAwIBAgIEdaP2dTALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCoxKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NzM2Nzk3MzMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQZo35Damtpl81YdmcbhEuXKAr7xDcQzAy5n3ftAAhtBbu8EeGU4ynfSgLonckqX6J2uXLBppTNE3v2bt+Yf8MLoxIwEDAOBgorBgEEAYLECgECBAAwCwYJKoZIhvcNAQELA4IBAQG9LbiNPgs0sQYOHAJcg+lMk+HCsiWRlYVnbT4I/5lnqU907vY17XYAORd432bU3Nnhsbkvjz76kQJGXeNAF4DPANGGlz8JU+LNEVE2PWPGgEM0GXgB7mZN5Sinfy1AoOdO+3c3bfdJQuXlUxHbo+nDpxxKpzq9gr++RbokF1+0JBkMbaA/qLYL4WdhY5NvaOyMvYpO3sBxlzn6FcP67hlotGH1wU7qhCeh+uur7zDeAWVh7c4QtJOXHkLJQfV3Z7ZMvhkIA6jZJAX99hisABU/SSa5DtgX7AfsHwa04h69AAAWDUzSk3HgOXbUd1FaSOPdlVFkG2N2JllFHykyO3zO\";\n    private static final String PEM_ATTESTATION_CERT = \"-----BEGIN CERTIFICATE-----\\n\" + ATTESTATION_CERT + \"\\n-----END CERTIFICATE-----\\n\";\n\n    @Test\n    public void parsePemDoesNotReturnNull() throws CertificateException {\n        assertNotNull(CertificateParser.parsePem(PEM_ATTESTATION_CERT));\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/data/messages/key/util/U2fB64EncodingTest.java",
    "content": "package com.yubico.u2f.data.messages.key.util;\n\nimport org.junit.Test;\n\nimport com.yubico.u2f.exceptions.U2fBadInputException;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class U2fB64EncodingTest {\n    @Test\n    public void encodeTest() {\n        byte[] input = \"Test\".getBytes();\n        String base64Data = U2fB64Encoding.encode(input);\n\n        // No padding.\n        assertEquals(\"VGVzdA\", base64Data);\n    }\n\n    @Test\n    public void decodeTest() throws U2fBadInputException {\n        String base64Data = \"VGVzdA\";\n        String base64DataWithPadding = \"VGVzdA==\";\n        String base64DataEmpty = \"\";\n\n        // Verify that Base64 data with and without padding ('=') are decoded correctly.\n        String out1 = new String(U2fB64Encoding.decode(base64Data));\n        String out2 = new String(U2fB64Encoding.decode(base64DataWithPadding));\n        String out3 = new String(U2fB64Encoding.decode(base64DataEmpty));\n\n        assertEquals(out1, out2);\n        assertEquals(out1, \"Test\");\n        assertEquals(out3, \"\");\n    }\n\n    @Test(expected = U2fBadInputException.class)\n    public void decodeBadAlphabetTest() throws U2fBadInputException {\n        U2fB64Encoding.decode(\"****\");\n    }\n\n    @Test(expected = U2fBadInputException.class)\n    public void decodeBadPaddingTest() throws U2fBadInputException {\n        U2fB64Encoding.decode(\"A===\");\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/json/SerializationTest.java",
    "content": "package com.yubico.u2f.json;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport org.junit.Test;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\npublic class SerializationTest {\n\n    @Test\n    public void tokenSignResponse() throws Exception {\n        String response = \"{ \\\"signatureData\\\": \\\"AQAAAAUwRAIgB1Q5iWRzC4zkZE2eIqoJZsXXCcg_6FVbZk-sMtLXcz4CIHxWaQsjLc-vD_kZLeg-p7IQ1HAmAFgiTk_dq6Q6iGcu\\\", \\\"clientData\\\": \\\"eyAiY2hhbGxlbmdlIjogIkQ1VG1CaEQzbTg0c3BRd3FfVm81VWZFSm8xV2JXTnBnRHdvZ0dWcmtBd00iLCAib3JpZ2luIjogImh0dHA6XC9cL2V4YW1wbGUuY29tIiwgInR5cCI6ICJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiB9\\\", \\\"keyHandle\\\": \\\"fSgg0l0JefF0GAFGAi9cOf5iL1nnzSswSmgpathyRRhsZ8QTzxPH1WAu8TqTbadfnNHOnINoF0UkMjKrxKVZLA\\\" }\";\n        SignResponse ar = new ObjectMapper().readValue(response, SignResponse.class);\n        checkNotNull(ar.getKeyHandle());\n        checkNotNull(ar.getClientData());\n        checkNotNull(ar.getSignatureData());\n\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/softkey/SoftKey.java",
    "content": "// Copyright 2014 Google Inc. All rights reserved.\n//\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file or at\n// https://developers.google.com/open-source/licenses/bsd\n\npackage com.yubico.u2f.softkey;\n\nimport com.yubico.u2f.data.messages.key.RawSignResponse;\nimport com.yubico.u2f.data.messages.key.RawRegisterResponse;\nimport com.yubico.u2f.data.messages.key.util.ByteInputStream;\nimport com.yubico.u2f.softkey.messages.SignRequest;\nimport com.yubico.u2f.softkey.messages.RegisterRequest;\nimport com.yubico.u2f.testdata.GnubbyKey;\nimport org.bouncycastle.jce.ECNamedCurveTable;\nimport org.bouncycastle.jce.spec.ECParameterSpec;\n\nimport java.io.IOException;\nimport java.security.*;\nimport java.security.cert.X509Certificate;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\npublic final class SoftKey implements Cloneable {\n\n    private final X509Certificate attestationCertificate;\n    private final PrivateKey certificatePrivateKey;\n    private final Map<String, KeyPair> dataStore;\n    private long deviceCounter = 0;\n\n    public SoftKey() {\n        this(\n                new HashMap<String, KeyPair>(),\n                0,\n                GnubbyKey.ATTESTATION_CERTIFICATE,\n                GnubbyKey.ATTESTATION_CERTIFICATE_PRIVATE_KEY\n        );\n    }\n\n    public SoftKey(\n            Map<String, KeyPair> dataStore,\n            long deviceCounter,\n            X509Certificate attestationCertificate,\n            PrivateKey certificatePrivateKey\n    ) {\n        this.dataStore = dataStore;\n        this.deviceCounter = deviceCounter;\n        this.attestationCertificate = attestationCertificate;\n        this.certificatePrivateKey = certificatePrivateKey;\n    }\n\n    @Override\n    public SoftKey clone() {\n        return new SoftKey(\n                this.dataStore,\n                this.deviceCounter,\n                this.attestationCertificate,\n                this.certificatePrivateKey\n        );\n    }\n\n    public RawRegisterResponse register(RegisterRequest registerRequest) throws Exception {\n\n        byte[] applicationSha256 = registerRequest.getApplicationSha256();\n        byte[] challengeSha256 = registerRequest.getChallengeSha256();\n\n        // generate ECC key\n        SecureRandom random = new SecureRandom();\n        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(\"secp256r1\");\n        KeyPairGenerator g = KeyPairGenerator.getInstance(\"ECDSA\");\n        g.initialize(ecSpec, random);\n        KeyPair keyPair = g.generateKeyPair();\n\n        byte[] keyHandle = new byte[64];\n        random.nextBytes(keyHandle);\n        dataStore.put(new String(keyHandle), keyPair);\n\n        byte[] userPublicKey = stripMetaData(keyPair.getPublic().getEncoded());\n\n        byte[] signedData = RawRegisterResponse.packBytesToSign(applicationSha256, challengeSha256,\n                keyHandle, userPublicKey);\n\n        byte[] signature = sign(signedData, certificatePrivateKey);\n\n        return new RawRegisterResponse(userPublicKey, keyHandle, attestationCertificate, signature);\n    }\n\n    private byte[] stripMetaData(byte[] a) {\n        ByteInputStream bis = new ByteInputStream(a);\n        try {\n            bis.read(3);\n            bis.read(bis.readUnsigned() + 1);\n            int keyLength = bis.readUnsigned();\n            bis.read(1);\n            return bis.read(keyLength - 1);\n        } catch (IOException e) {\n            throw new AssertionError(e);\n        }\n    }\n\n    public RawSignResponse sign(SignRequest signRequest) throws Exception {\n\n        byte[] applicationSha256 = signRequest.getApplicationSha256();\n        byte[] challengeSha256 = signRequest.getChallengeSha256();\n        byte[] keyHandle = signRequest.getKeyHandle();\n\n        KeyPair keyPair = checkNotNull(dataStore.get(new String(keyHandle)));\n        long counter = ++deviceCounter;\n        byte[] signedData = RawSignResponse.packBytesToSign(applicationSha256, RawSignResponse.USER_PRESENT_FLAG,\n                counter, challengeSha256);\n\n        byte[] signature = sign(signedData, keyPair.getPrivate());\n\n        return new RawSignResponse(RawSignResponse.USER_PRESENT_FLAG, counter, signature);\n    }\n\n    private byte[] sign(byte[] signedData, PrivateKey privateKey) throws Exception {\n        Signature signature = Signature.getInstance(\"SHA256withECDSA\");\n        signature.initSign(privateKey);\n        signature.update(signedData);\n        return signature.sign();\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/softkey/SoftKeyTest.java",
    "content": "package com.yubico.u2f.softkey;\n\nimport com.yubico.u2f.U2fPrimitives;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.ClientData;\nimport com.yubico.u2f.data.messages.SignRequest;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.key.Client;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\nimport com.yubico.u2f.exceptions.InvalidDeviceCounterException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport com.yubico.u2f.exceptions.U2fRegistrationException;\nimport com.yubico.u2f.testdata.AcmeKey;\nimport com.yubico.u2f.testdata.GnubbyKey;\nimport java.security.KeyPair;\nimport java.util.HashMap;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\nimport static org.hamcrest.core.Is.isA;\nimport static org.junit.Assert.assertEquals;\n\npublic class SoftKeyTest {\n\n    public static final String APP_ID = \"my-app\";\n\n    private U2fPrimitives u2f;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void setup() {\n        u2f = new U2fPrimitives();\n    }\n\n    @Test\n    public void shouldRegister() throws Exception {\n        Client client = createClient();\n        client.register();\n    }\n\n    @Test\n    public void shouldSign() throws Exception {\n        Client client = createClient();\n        DeviceRegistration registeredDevice = client.register();\n        signUsing(client, registeredDevice);\n    }\n\n    // Tests FIDO Security Measure [SM-3]\n    @Test\n    public void shouldProvideAttestationCert() throws Exception {\n        Client client = createClient();\n        DeviceRegistration deviceRegistration = client.register();\n        assertEquals(\"CN=Gnubby Pilot\", deviceRegistration.getAttestationCertificate().getIssuerDN().getName());\n    }\n\n    @Test\n    public void shouldVerifyAttestationCert() throws Throwable {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        SoftKey key = new SoftKey(\n                new HashMap<String, KeyPair>(),\n                0,\n                AcmeKey.ATTESTATION_CERTIFICATE,\n                GnubbyKey.ATTESTATION_CERTIFICATE_PRIVATE_KEY\n        );\n        new Client(key).register();\n    }\n\n    // Tests FIDO Security Measure [SM-15]\n    @Test(expected = InvalidDeviceCounterException.class)\n    public void shouldProtectAgainstClonedDevices() throws Exception {\n        SoftKey key = new SoftKey();\n        Client client = new Client(key);\n\n        SoftKey clonedKey = key.clone();\n        Client clientUsingClone = new Client(clonedKey);\n\n        DeviceRegistration registeredDevice = client.register();\n\n        signUsing(client, registeredDevice);\n        signUsing(clientUsingClone, registeredDevice);\n    }\n\n    @Test\n     public void shouldVerifyChallenge() throws Throwable {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        Client client = createClient();\n\n        DeviceRegistration registeredDevice = client.register();\n\n        SignRequest signRequest = u2f.startSignature(APP_ID, registeredDevice);\n        SignResponse originalResponse = client.sign(registeredDevice, signRequest);\n        SignResponse tamperedResponse = new SignResponse(\n                tamperChallenge(originalResponse.getClientData()),\n                originalResponse.getSignatureData(),\n                originalResponse.getKeyHandle()\n        );\n        u2f.finishSignature(signRequest, tamperedResponse, registeredDevice);\n    }\n\n    private String tamperChallenge(ClientData clientData) {\n        byte[] rawClientData = clientData.asJson().getBytes();\n        rawClientData[50] += 1;\n        return U2fB64Encoding.encode(rawClientData);\n    }\n\n    @Test\n    public void shouldVerifySignature() throws Throwable {\n        expectedException.expectCause(isA(U2fBadInputException.class));\n\n        Client client = createClient();\n\n        DeviceRegistration registeredDevice = client.register();\n\n        SignRequest signRequest = u2f.startSignature(APP_ID, registeredDevice);\n        SignResponse originalResponse = client.sign(registeredDevice, signRequest);\n        SignResponse tamperedResponse = new SignResponse(\n                U2fB64Encoding.encode(originalResponse.getClientData().asJson().getBytes()),\n                tamperSignature(originalResponse.getSignatureData()),\n                originalResponse.getKeyHandle()\n        );\n        u2f.finishSignature(signRequest, tamperedResponse, registeredDevice);\n    }\n\n\n    @Test(expected = RuntimeException.class)\n    public void shouldThrowSeparateExceptionForMalformedSignature() throws Exception {\n\n        Client client = createClient();\n\n        DeviceRegistration registeredDevice = client.register();\n\n        SignRequest signRequest = u2f.startSignature(APP_ID, registeredDevice);\n        SignResponse originalResponse = client.sign(registeredDevice, signRequest);\n        SignResponse tamperedResponse = new SignResponse(\n            U2fB64Encoding.encode(originalResponse.getClientData().asJson().getBytes()),\n            makeSignatureMalformed(originalResponse.getSignatureData()),\n            originalResponse.getKeyHandle()\n        );\n        u2f.finishSignature(signRequest, tamperedResponse, registeredDevice);\n    }\n\n    private String makeSignatureMalformed(String signature) {\n        return signature.substring(0, 5) + \"47\" + signature.substring(7);\n    }\n\n    private String tamperSignature(String signature) {\n        return signature.substring(0, 24) + \"47\" + signature.substring(26);\n    }\n\n    private Client createClient() {\n        return new Client(new SoftKey());\n    }\n\n    private void signUsing(Client client, DeviceRegistration registeredDevice) throws Exception {\n        SignRequest signRequest = u2f.startSignature(APP_ID, registeredDevice);\n        SignResponse signResponse = client.sign(registeredDevice, signRequest);\n        u2f.finishSignature(signRequest, signResponse, registeredDevice);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/softkey/messages/RegisterRequest.java",
    "content": "package com.yubico.u2f.softkey.messages;\n\npublic class RegisterRequest {\n    private final byte[] challengeSha256;\n    private final byte[] applicationSha256;\n\n    public RegisterRequest(byte[] applicationSha256, byte[] challengeSha256) {\n        this.challengeSha256 = challengeSha256;\n        this.applicationSha256 = applicationSha256;\n    }\n\n    /**\n     * The challenge parameter is the SHA-256 hash of the Client Data, a\n     * stringified JSON datastructure that the FIDO Client prepares. Among other\n     * things, the Client Data contains the challenge from the relying party\n     * (hence the name of the parameter). See below for a detailed explanation of\n     * Client Data.\n     */\n    public byte[] getChallengeSha256() {\n        return challengeSha256;\n    }\n\n    /**\n     * The application parameter is the SHA-256 hash of the application identity\n     * of the application requesting the registration\n     */\n    public byte[] getApplicationSha256() {\n        return applicationSha256;\n    }\n}"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/softkey/messages/SignRequest.java",
    "content": "package com.yubico.u2f.softkey.messages;\n\npublic class SignRequest {\n\n    private final byte control;\n    private final byte[] challengeSha256;\n    private final byte[] applicationSha256;\n    private final byte[] keyHandle;\n\n    public SignRequest(byte control, byte[] challengeSha256, byte[] applicationSha256,\n                       byte[] keyHandle) {\n        this.control = control;\n        this.challengeSha256 = challengeSha256;\n        this.applicationSha256 = applicationSha256;\n        this.keyHandle = keyHandle;\n    }\n\n    /**\n     * The FIDO Client will set the control byte to one of the following values:\n     * 0x07 (\"check-only\")\n     * 0x03 (\"enforce-user-presence-and-sign\")\n     */\n    public byte getControl() {\n        return control;\n    }\n\n    /**\n     * The challenge parameter is the SHA-256 hash of the Client Data, a\n     * stringified JSON datastructure that the FIDO Client prepares. Among other\n     * things, the Client Data contains the challenge from the relying party\n     * (hence the name of the parameter). See below for a detailed explanation of\n     * Client Data.\n     */\n    public byte[] getChallengeSha256() {\n        return challengeSha256;\n    }\n\n    /**\n     * The application parameter is the SHA-256 hash of the application identity\n     * of the application requesting the registration\n     */\n    public byte[] getApplicationSha256() {\n        return applicationSha256;\n    }\n\n    /**\n     * The key handle obtained during registration.\n     */\n    public byte[] getKeyHandle() {\n        return keyHandle;\n    }\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/testdata/AcmeKey.java",
    "content": "package com.yubico.u2f.testdata;\n\nimport com.yubico.u2f.TestUtils;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\n\nimport java.security.cert.X509Certificate;\n\nimport static com.yubico.u2f.TestUtils.fetchCertificate;\n\npublic class AcmeKey {\n\n    public static final X509Certificate ATTESTATION_CERTIFICATE =\n            fetchCertificate(GnubbyKey.class.getResourceAsStream(\"acme/attestation-certificate.der\"));\n\n    public static final String CLIENT_DATA_BASE64 = TestVectors.CLIENT_DATA_REGISTRATION_BASE64;\n    public static final byte[] REGISTRATION_DATA = TestUtils.HEX.decode(\n            \"0504478e16bbdbbb741a660a000314a8b6bd63095196ed704c52eebc0fa02a61\"\n                    + \"8f19ff59df18451a11cee43defd9a29b5710f63dfc671f752b1b0c6ca76c8427\"\n                    + \"af2d403c2415e1760d1108105720c6069a9039c99d09f76909c36d9efc350937\"\n                    + \"31f85f55ac6d73ea69de7d9005ae9507b95e149e19676272fc202d949a3ab151\"\n                    + \"b96870308201443081eaa0030201020209019189ffffffff5183300a06082a86\"\n                    + \"48ce3d040302301b3119301706035504031310476e756262792048534d204341\"\n                    + \"2030303022180f32303132303630313030303030305a180f3230363230353331\"\n                    + \"3233353935395a30303119301706035504031310476f6f676c6520476e756262\"\n                    + \"7920763031133011060355042d030a00019189ffffffff51833059301306072a\"\n                    + \"8648ce3d020106082a8648ce3d030107034200041f1302f12173a9cbea83d06d\"\n                    + \"755411e582a87fbb5850eddcf3607ec759a4a12c3cb392235e8d5b17caee1b34\"\n                    + \"e5b5eb548649696257f0ea8efb90846f88ad5f72300a06082a8648ce3d040302\"\n                    + \"0349003046022100b4caea5dc60fbf9f004ed84fc4f18522981c1c303155c082\"\n                    + \"74e889f3f10c5b23022100faafb4f10b92f4754e3b08b5af353f78485bc903ec\"\n                    + \"e7ea911264fc1673b6598f3046022100f3be1bf12cbf0be7eab5ea32f3664edb\"\n                    + \"18a24d4999aac5aa40ff39cf6f34c9ed022100ce72631767367467dfe2aecf6a\"\n                    + \"5a4eba9779fac65f5ca8a2c325b174ee4769ac\");\n    public static final String REGISTRATION_DATA_BASE64 = U2fB64Encoding\n            .encode(REGISTRATION_DATA);\n    public static final String KEY_HANDLE = \"PCQV4XYNEQgQVyDGBpqQOcmdCfdpCcNtnvw1CTcx-F9VrG1z6mnefZAFrpUHuV4UnhlnYnL8IC2UmjqxUblocA\";\n    public static final String USER_PUBLIC_KEY_B64 = \"BEeOFrvbu3QaZgoAAxSotr1jCVGW7XBMUu68D6AqYY8Z_1nfGEUaEc7kPe_ZoptXEPY9_GcfdSsbDGynbIQnry0\";\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/testdata/GnubbyKey.java",
    "content": "package com.yubico.u2f.testdata;\n\nimport java.security.PrivateKey;\nimport java.security.cert.X509Certificate;\n\nimport static com.yubico.u2f.TestUtils.fetchCertificate;\nimport static com.yubico.u2f.TestUtils.parsePrivateKey;\n\npublic class GnubbyKey {\n\n    public static final X509Certificate ATTESTATION_CERTIFICATE =\n            fetchCertificate(GnubbyKey.class.getResourceAsStream(\"gnubby/attestation-certificate.der\"));\n\n    public static final PrivateKey ATTESTATION_CERTIFICATE_PRIVATE_KEY =\n            parsePrivateKey(GnubbyKey.class.getResourceAsStream(\"gnubby/attestation-certificate-private-key.hex\"));\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/java/com/yubico/u2f/testdata/TestVectors.java",
    "content": "/*\n * Copyright 2014 Yubico.\n * Copyright 2014 Google Inc. All rights reserved.\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n\npackage com.yubico.u2f.testdata;\n\nimport com.google.common.collect.ImmutableSet;\nimport com.yubico.u2f.TestUtils;\nimport com.yubico.u2f.crypto.BouncyCastleCrypto;\nimport com.yubico.u2f.crypto.Crypto;\nimport com.yubico.u2f.data.messages.key.util.U2fB64Encoding;\n\nimport java.util.Set;\n\nfinal public class TestVectors {\n    private final static Crypto crypto = new BouncyCastleCrypto();\n\n    //Test vectors from FIDO U2F: Raw Message Formats - Draft 4\n    public static final int COUNTER_VALUE = 1;\n    public static final Set<String> TRUSTED_DOMAINS = ImmutableSet.of(\"http://example.com\");\n    public static final String APP_ID_ENROLL = \"http://example.com\";\n    public static final byte[] APP_ID_ENROLL_SHA256 = crypto.hash(APP_ID_ENROLL);\n    public static final String APP_ID_SIGN = \"https://gstatic.com/securitykey/a/example.com\";\n    public static final byte[] APP_ID_SIGN_SHA256 = crypto.hash(APP_ID_SIGN);\n    public static final String ORIGIN = \"http://example.com\";\n    public static final String SERVER_CHALLENGE_REGISTER_BASE64 =\n            \"vqrS6WXDe1JUs5_c3i4-LkKIHRr-3XVb3azuA5TifHo\";\n    public static final String SERVER_CHALLENGE_SIGN_BASE64 = \"opsXqUifDriAAmWclinfbS0e-USY0CgyJHe_Otd7z8o\";\n\n    public static final String CHANNEL_ID_STRING =\n            \"{\"\n                    + \"\\\"kty\\\":\\\"EC\\\",\"\n                    + \"\\\"crv\\\":\\\"P-256\\\",\"\n                    + \"\\\"x\\\":\\\"HzQwlfXX7Q4S5MtCCnZUNBw3RMzPO9tOyWjBqRl4tJ8\\\",\"\n                    + \"\\\"y\\\":\\\"XVguGFLIZx1fXg3wNqfdbn75hi4-_7-BxhMljw42Ht4\\\"\"\n                    + \"}\";\n    public static final String CLIENT_DATA_REGISTER = String.format(\n            \"{\"\n                    + \"\\\"typ\\\":\\\"navigator.id.finishEnrollment\\\",\"\n                    + \"\\\"challenge\\\":\\\"%s\\\",\"\n                    + \"\\\"cid_pubkey\\\":%s,\"\n                    + \"\\\"origin\\\":\\\"%s\\\"}\",\n            SERVER_CHALLENGE_REGISTER_BASE64,\n            CHANNEL_ID_STRING,\n            ORIGIN);\n    public static final String CLIENT_DATA_REGISTRATION_BASE64 = TestUtils.BASE64.encode(CLIENT_DATA_REGISTER.getBytes());\n    public static final byte[] CLIENT_DATA_ENROLL_SHA256 = crypto.hash(CLIENT_DATA_REGISTER\n            .getBytes());\n    public static final String CLIENT_DATA_SIGN = String.format(\n            \"{\"\n                    + \"\\\"typ\\\":\\\"navigator.id.getAssertion\\\",\"\n                    + \"\\\"challenge\\\":\\\"%s\\\",\"\n                    + \"\\\"cid_pubkey\\\":%s,\"\n                    + \"\\\"origin\\\":\\\"%s\\\"}\",\n            SERVER_CHALLENGE_SIGN_BASE64,\n            CHANNEL_ID_STRING,\n            ORIGIN);\n    public static final String CLIENT_DATA_SIGN_BASE64 = U2fB64Encoding.encode(CLIENT_DATA_SIGN.getBytes());\n    public static final byte[] CLIENT_DATA_SIGN_SHA256 = TestUtils.HEX.decode(\n            \"ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57\");\n    public static final byte[] REGISTRATION_REQUEST_DATA = TestUtils.HEX.decode(\n            \"4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb\"\n                    + \"f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1c4\");\n    public static final byte[] REGISTRATION_RESPONSE_DATA = TestUtils.HEX.decode(\n            \"0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b\"\n                    + \"657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2\"\n                    + \"f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2\"\n                    + \"e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772\"\n                    + \"d70c253082013c3081e4a003020102020a47901280001155957352300a06082a\"\n                    + \"8648ce3d0403023017311530130603550403130c476e756262792050696c6f74\"\n                    + \"301e170d3132303831343138323933325a170d3133303831343138323933325a\"\n                    + \"3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34\"\n                    + \"373930313238303030313135353935373335323059301306072a8648ce3d0201\"\n                    + \"06082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c\"\n                    + \"1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23\"\n                    + \"abaf0203b4b8911ba0569994e101300a06082a8648ce3d040302034700304402\"\n                    + \"2060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30d\"\n                    + \"fa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b3\"\n                    + \"0410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80f\"\n                    + \"cab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5\"\n                    + \"ad7804a6d3d3961ef871\");\n\n    public static final String REGISTRATION_DATA_BASE64 = U2fB64Encoding.encode(REGISTRATION_RESPONSE_DATA);\n\n    public static final byte[] REGISTRATION_RESPONSE_DATA_WITH_DIFFERENT_APP_ID = TestUtils.HEX.decode(\n                \"0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b\"\n                    + \"657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2\"\n                    + \"f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2\"\n                    + \"e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772\"\n                    + \"d70c253082013c3081e4a003020102020a47901280001155957352300a06082a\"\n                    + \"8648ce3d0403023017311530130603550403130c476e756262792050696c6f74\"\n                    + \"301e170d3132303831343138323933325a170d3133303831343138323933325a\"\n                    + \"3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34\"\n                    + \"373930313238303030313135353935373335323059301306072a8648ce3d0201\"\n                    + \"06082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c\"\n                    + \"1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23\"\n                    + \"abaf0203b4b8911ba0569994e101300a06082a8648ce3d040302034700304402\"\n                    + \"2060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30d\"\n                    + \"fa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b3\"\n                    + \"0410df3046022100d2b4702fea46b322c5addd921b3f4f0fb15c69737fe7441e\"\n                    + \"b764c03dc8f49d09022100eef7dcdf6070d8e5a45ed6be18dfc036ebf8b4faaa\"\n                    + \"ce7287b56e7fac1d2cb552\");\n    public static final String REGISTRATION_DATA_WITH_DIFFERENT_APP_ID_BASE64 = U2fB64Encoding.encode(REGISTRATION_RESPONSE_DATA_WITH_DIFFERENT_APP_ID);\n\n    public static final byte[] REGISTRATION_RESPONSE_DATA_WITH_DIFFERENT_CLIENT_DATA_TYPE = TestUtils.HEX.decode(\n        \"0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b\"\n            + \"657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2\"\n            + \"f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2\"\n            + \"e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772\"\n            + \"d70c253082013c3081e4a003020102020a47901280001155957352300a06082a\"\n            + \"8648ce3d0403023017311530130603550403130c476e756262792050696c6f74\"\n            + \"301e170d3132303831343138323933325a170d3133303831343138323933325a\"\n            + \"3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34\"\n            + \"373930313238303030313135353935373335323059301306072a8648ce3d0201\"\n            + \"06082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c\"\n            + \"1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23\"\n            + \"abaf0203b4b8911ba0569994e101300a06082a8648ce3d040302034700304402\"\n            + \"2060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30d\"\n            + \"fa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b3\"\n            + \"0410df30450220176386c89021f4335d953c56a0c831f98380dc198c95794a85\"\n            + \"b08f0c4ba849ff022100a10114749d0c28e13a9ffe6dde6e622c33163b249ac1\"\n            + \"ffb1c8e25b3cc4907e3c\");\n    public static final String REGISTRATION_DATA_WITH_DIFFERENT_CLIENT_DATA_TYPE_BASE64 = U2fB64Encoding.encode(REGISTRATION_RESPONSE_DATA_WITH_DIFFERENT_CLIENT_DATA_TYPE);\n\n    public static final byte[] KEY_HANDLE = TestUtils.HEX.decode(\n            \"2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a\"\n                    + \"6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25\");\n    public static final String KEY_HANDLE_BASE64 = U2fB64Encoding.encode(KEY_HANDLE);\n    public static final byte[] USER_PUBLIC_KEY_REGISTER_HEX = TestUtils.HEX.decode(\n            \"04b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b65\"\n                    + \"7c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6\"\n                    + \"d9\");\n    public static final String USER_PUBLIC_KEY_SIGN_HEX = \"BNNo8bZlut48M6IPHkKcd1DVAzZgwBkRnSmqS6erwEqnyApGu-EcqMtWdNdPMfipA_a60QX7ardK7-9NuLACXh0\";\n    public static final byte[] SIGN_RESPONSE_DATA = TestUtils.HEX.decode(\n            \"0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030c\"\n                    + \"e43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f\"\n                    + \"53c7b22272ec10047a923f\");\n    public static final String SIGN_RESPONSE_DATA_BASE64 = U2fB64Encoding.encode(SIGN_RESPONSE_DATA);\n    public static final byte[] EXPECTED_REGISTER_SIGNED_BYTES = TestUtils.HEX.decode(\n            \"00f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1\"\n                    + \"c44142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfa\"\n                    + \"cb2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e392\"\n                    + \"5a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c\"\n                    + \"2504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b\"\n                    + \"657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2\"\n                    + \"f6d9\");\n    public static final byte[] EXPECTED_SIGN_SIGNED_BYTES = TestUtils.HEX.decode(\n            \"4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca\"\n                    + \"0100000001ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c482\"\n                    + \"1b3b9dbc57\");\n    public static final byte[] SIGNATURE_REGISTER = TestUtils.HEX.decode(\n            \"304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017\"\n                    + \"db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804\"\n                    + \"a6d3d3961ef871\");\n    public static final byte[] SIGNATURE_SIGN = TestUtils.HEX.decode(\n            \"304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de8\"\n                    + \"70b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272\"\n                    + \"ec10047a923f\");\n\n    public static final byte[] SIGN_RESPONSE_INVALID_USER_PRESENCE = TestUtils.HEX.decode(\n            \"00000000013045022100adf3521ceb4e143fb3966d3017510bfbc9085a44ff13c6945aadd8\"\n                    + \"e26ec5cc00022004916d120830f2ee44ab3c6c58c80a3dd6f5a09b01599e686d\"\n                    + \"ea2e7288903cae\");\n    public static final String SIGN_RESPONSE_INVALID_USER_PRESENCE_BASE64 = U2fB64Encoding.encode(SIGN_RESPONSE_INVALID_USER_PRESENCE);\n}\n"
  },
  {
    "path": "u2flib-server-core/src/test/resources/com/yubico/u2f/testdata/acme/attestation-certificate.der",
    "content": "MIIBRDCB6qADAgECAgkBkYn/////UYMwCgYIKoZIzj0EAwIwGzEZMBcGA1UEAxMQR251YmJ5IEhTTSBDQSAwMDAiGA8yMDEyMDYwMTAwMDAwMFoYDzIwNjIwNTMxMjM1OTU5WjAwMRkwFwYDVQQDExBHb29nbGUgR251YmJ5IHYwMRMwEQYDVQQtAwoAAZGJ/////1GDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHxMC8SFzqcvqg9BtdVQR5YKof7tYUO3c82B+x1mkoSw8s5IjXo1bF8ruGzTltetUhklpYlfw6o77kIRviK1fcjAKBggqhkjOPQQDAgNJADBGAiEAtMrqXcYPv58ATthPxPGFIpgcHDAxVcCCdOiJ8/EMWyMCIQD6r7TxC5L0dU47CLWvNT94SFvJA+zn6pESZPwWc7ZZjw=="
  },
  {
    "path": "u2flib-server-core/src/test/resources/com/yubico/u2f/testdata/gnubby/attestation-certificate-private-key.hex",
    "content": "f3fccc0d00d8031954f90864d43c247f4bf5f0665c6b50cc17749a27d1cf7664"
  },
  {
    "path": "u2flib-server-core/src/test/resources/com/yubico/u2f/testdata/gnubby/attestation-certificate.der",
    "content": "MIIBPDCB5KADAgECAgpHkBKAABFVlXNSMAoGCCqGSM49BAMCMBcxFTATBgNVBAMTDEdudWJieSBQaWxvdDAeFw0xMjA4MTQxODI5MzJaFw0xMzA4MTQxODI5MzJaMDExLzAtBgNVBAMTJlBpbG90R251YmJ5LTAuNC4xLTQ3OTAxMjgwMDAxMTU1OTU3MzUyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjWF+ZclQjmS8xWc6yCpnmdo8FEZoLCWMRj//31jf0vo+bDeLU9eVxKTf+0GZ7deGLyOrrwIDtLiRG6BWmZThATAKBggqhkjOPQQDAgNHADBEAiBgzbYGHpwiJi0arB2W2McIKbI2ZTHdomiDLLg2vNMN+gIgYxsUWfCeYzAFVyLI2Jt/SIg7kIm4jWDR2XlZArMEEN8="
  },
  {
    "path": "u2flib-server-demo/README",
    "content": "== u2flib-server-demo\nA simple self-contained demo server supporting multiple devices per user. The central part is the\nhttps://github.com/Yubico/java-u2flib-server/blob/master/u2flib-server-demo/src/main/java/demo/Resource.java[Resource]\nclass.\n\n=== 1. Clone\n git clone https://github.com/Yubico/java-u2flib-server.git\n\n=== 2. Run\n ./gradlew run\n\n=== 3. Try it out\nThen point a\nhttps://www.yubico.com/support/knowledge-base/categories/articles/browsers-support-u2f/[U2F compatible web browser]\nto link:https://localhost:8443/assets/registerIndex.html[https://localhost:8443/assets/registerIndex.html].\n\nNOTE: Since U2F requires a HTTPS connection, this demo server uses a dummy certificate.\nThis will cause your browser to show a warning, which it is safe to bypass.\n\n=== Troubleshooting\n[qanda]\n.Q&A\nWhy do I get https://developers.yubico.com/U2F/Libraries/Client_error_codes.html[error code 2]?::\n    You are accessing the server using a URL that does not match the server's\n\thttps://developers.yubico.com/U2F/App_ID.html[App ID].\n\tThe App ID is set to `https://localhost:8443` by default, but you can change this in the\n    https://github.com/Yubico/java-u2flib-server/blob/master/u2flib-server-demo/src/main/java/demo/Resource.java[Resource]\n    class.\n"
  },
  {
    "path": "u2flib-server-demo/build.gradle",
    "content": "description = 'U2F demo'\n\napply plugin: 'application'\n\nmainClassName = 'demo.App'\n\ndependencies {\n\n  compile(\n    project(':u2flib-server-core'),\n    project(':u2flib-server-attestation'),\n    [group: 'io.dropwizard', name: 'dropwizard-core', version:'1.3.7'],\n    [group: 'io.dropwizard', name: 'dropwizard-assets', version:'1.3.7'],\n    [group: 'io.dropwizard', name: 'dropwizard-views', version:'1.3.7'],\n    [group: 'io.dropwizard', name: 'dropwizard-views-freemarker', version:'1.3.7'],\n    [group: 'org.freemarker', name: 'freemarker', version:'2.3.28'],\n  )\n\n}\n\nproject.ext.serverConfigFile = file('config.yml')\n\nrun {\n  inputs.file serverConfigFile\n  args 'server', project.serverConfigFile.path\n}\n"
  },
  {
    "path": "u2flib-server-demo/config.yml",
    "content": "server:\n  applicationConnectors:\n    - type: https\n      port: 8443\n      keyStorePath: keystore.jks\n      keyStorePassword: p@ssw0rd\n      validateCerts: false\n  adminConnectors:\n      - type: http\n        port: 8081\n\nlogging:\n  level: INFO\n  loggers:\n    javax.ws.rs.ext.MessageBodyWriter: DEBUG\n  appenders:\n    - type: console\n"
  },
  {
    "path": "u2flib-server-demo/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "u2flib-server-demo/gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "u2flib-server-demo/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "u2flib-server-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <artifactId>u2flib-server-parent</artifactId>\n        <groupId>com.yubico</groupId>\n        <version>0.19.12</version>\n    </parent>\n    <artifactId>u2flib-server-demo</artifactId>\n    <name>U2F demo</name>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.yubico</groupId>\n            <artifactId>u2flib-server-core</artifactId>\n            <version>0.19.12</version>\n        </dependency>\n        <dependency>\n            <groupId>com.yubico</groupId>\n            <artifactId>u2flib-server-attestation</artifactId>\n            <version>0.19.12</version>\n        </dependency>\n        <dependency>\n            <groupId>io.dropwizard</groupId>\n            <artifactId>dropwizard-core</artifactId>\n            <version>1.3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>io.dropwizard</groupId>\n            <artifactId>dropwizard-assets</artifactId>\n            <version>1.3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>io.dropwizard</groupId>\n            <artifactId>dropwizard-views</artifactId>\n            <version>1.3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>io.dropwizard</groupId>\n            <artifactId>dropwizard-views-freemarker</artifactId>\n            <version>1.3.7</version>\n        </dependency>\n        <dependency>\n            <groupId>org.freemarker</groupId>\n            <artifactId>freemarker</artifactId>\n            <version>2.3.28</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-shade-plugin</artifactId>\n                <version>1.6</version>\n                <configuration>\n                    <createDependencyReducedPom>true</createDependencyReducedPom>\n                    <filters>\n                        <filter>\n                            <artifact>*:*</artifact>\n                            <excludes>\n                                <exclude>META-INF/*.SF</exclude>\n                                <exclude>META-INF/*.DSA</exclude>\n                                <exclude>META-INF/*.RSA</exclude>\n                            </excludes>\n                        </filter>\n                    </filters>\n                </configuration>\n                <executions>\n                    <execution>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>shade</goal>\n                        </goals>\n                        <configuration>\n                            <finalName>${project.artifactId}</finalName>\n                            <transformers>\n                                <transformer implementation=\"org.apache.maven.plugins.shade.resource.ServicesResourceTransformer\" />\n                                <transformer implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n                                    <mainClass>demo.App</mainClass>\n                                </transformer>\n                            </transformers>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/App.java",
    "content": "package demo;\n\nimport io.dropwizard.Application;\nimport io.dropwizard.assets.AssetsBundle;\nimport io.dropwizard.setup.Bootstrap;\nimport io.dropwizard.setup.Environment;\nimport io.dropwizard.views.ViewBundle;\n\npublic class App extends Application<Config> {\n    @Override\n    public void initialize(Bootstrap<Config> bootstrap) {\n        bootstrap.addBundle(new ViewBundle());\n        bootstrap.addBundle(new AssetsBundle());\n    }\n\n    @Override\n    public void run(Config config, Environment environment) throws Exception {\n        environment.jersey().register(new Resource());\n    }\n\n    public static void main(String... args) throws Exception {\n        new App().run(args);\n    }\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/Config.java",
    "content": "package demo;\n\nimport io.dropwizard.Configuration;\n\npublic class Config extends Configuration {\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/Resource.java",
    "content": "package demo;\n\nimport javax.ws.rs.FormParam;\nimport javax.ws.rs.GET;\nimport javax.ws.rs.POST;\nimport javax.ws.rs.Path;\nimport javax.ws.rs.Produces;\nimport javax.ws.rs.QueryParam;\nimport javax.ws.rs.core.MediaType;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\nimport com.yubico.u2f.U2F;\nimport com.yubico.u2f.attestation.Attestation;\nimport com.yubico.u2f.attestation.MetadataService;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport com.yubico.u2f.data.messages.SignRequest;\nimport com.yubico.u2f.data.messages.SignRequestData;\nimport com.yubico.u2f.data.messages.SignResponse;\nimport com.yubico.u2f.data.messages.RegisterRequestData;\nimport com.yubico.u2f.data.messages.RegisterResponse;\nimport com.yubico.u2f.exceptions.DeviceCompromisedException;\nimport com.yubico.u2f.exceptions.NoEligibleDevicesException;\nimport com.yubico.u2f.exceptions.U2fAuthenticationException;\nimport com.yubico.u2f.exceptions.U2fBadConfigurationException;\nimport com.yubico.u2f.exceptions.U2fBadInputException;\nimport com.yubico.u2f.exceptions.U2fRegistrationException;\nimport demo.view.AuthenticationView;\nimport demo.view.FinishAuthenticationView;\nimport demo.view.FinishRegistrationView;\nimport demo.view.RegistrationView;\nimport io.dropwizard.views.View;\nimport java.security.cert.CertificateException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Path(\"/\")\n@Produces(MediaType.TEXT_HTML)\npublic class Resource {\n\n    public static final String APP_ID = \"https://localhost:8443\";\n\n    private final Map<String, String> requestStorage = new HashMap<String, String>();\n    private final LoadingCache<String, Map<String, String>> userStorage = CacheBuilder.newBuilder().build(new CacheLoader<String, Map<String, String>>() {\n        @Override\n        public Map<String, String> load(String key) throws Exception {\n            return new HashMap<String, String>();\n        }\n    });\n    private final U2F u2f = new U2F();\n    private final MetadataService metadataService = new MetadataService();\n\n    @Path(\"startRegistration\")\n    @GET\n    public View startRegistration(@QueryParam(\"username\") String username) throws U2fBadConfigurationException, U2fBadInputException {\n        RegisterRequestData registerRequestData = u2f.startRegistration(APP_ID, getRegistrations(username));\n        requestStorage.put(registerRequestData.getRequestId(), registerRequestData.toJson());\n        return new RegistrationView(registerRequestData.toJson(), username);\n    }\n\n    @Path(\"finishRegistration\")\n    @POST\n    public View finishRegistration(@FormParam(\"tokenResponse\") String response, @FormParam(\"username\") String username) throws CertificateException, U2fBadInputException, U2fRegistrationException {\n        RegisterResponse registerResponse = RegisterResponse.fromJson(response);\n        RegisterRequestData registerRequestData = RegisterRequestData.fromJson(requestStorage.remove(registerResponse.getRequestId()));\n        DeviceRegistration registration = u2f.finishRegistration(registerRequestData, registerResponse);\n\n        Attestation attestation = metadataService.getAttestation(registration.getAttestationCertificate());\n\n        addRegistration(username, registration);\n\n        return new FinishRegistrationView(attestation, registration);\n    }\n\n    @Path(\"startAuthentication\")\n    @GET\n    public View startAuthentication(@QueryParam(\"username\") String username) throws U2fBadConfigurationException, U2fBadInputException {\n        try {\n            SignRequestData signRequestData = u2f.startSignature(APP_ID, getRegistrations(username));\n            requestStorage.put(signRequestData.getRequestId(), signRequestData.toJson());\n            return new AuthenticationView(signRequestData, username);\n        } catch (NoEligibleDevicesException e) {\n            return new AuthenticationView(new SignRequestData(APP_ID, \"\", Collections.<SignRequest>emptyList()), username);\n        }\n    }\n\n    @Path(\"finishAuthentication\")\n    @POST\n    public View finishAuthentication(@FormParam(\"tokenResponse\") String response,\n                                       @FormParam(\"username\") String username) throws U2fBadInputException {\n        SignResponse signResponse = SignResponse.fromJson(response);\n        SignRequestData authenticateRequest = SignRequestData.fromJson(requestStorage.remove(signResponse.getRequestId()));\n        DeviceRegistration registration = null;\n        try {\n            registration = u2f.finishSignature(authenticateRequest, signResponse, getRegistrations(username));\n        } catch (DeviceCompromisedException e) {\n            registration = e.getDeviceRegistration();\n            return new FinishAuthenticationView(false, \"Device possibly compromised and therefore blocked: \" + e.getMessage());\n        } catch (U2fAuthenticationException e) {\n            return new FinishAuthenticationView(false, \"Authentication failed: \" + e.getCause().getMessage());\n        } finally {\n            userStorage.getUnchecked(username).put(registration.getKeyHandle(), registration.toJson());\n        }\n        return new FinishAuthenticationView(true);\n    }\n\n    private Iterable<DeviceRegistration> getRegistrations(String username) throws U2fBadInputException {\n        List<DeviceRegistration> registrations = new ArrayList<DeviceRegistration>();\n        for (String serialized : userStorage.getUnchecked(username).values()) {\n            registrations.add(DeviceRegistration.fromJson(serialized));\n        }\n        return registrations;\n    }\n\n    private void addRegistration(String username, DeviceRegistration registration) {\n        userStorage.getUnchecked(username).put(registration.getKeyHandle(), registration.toJson());\n    }\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/U2fDemoException.java",
    "content": "package demo;\n\nimport javax.ws.rs.WebApplicationException;\nimport javax.ws.rs.core.Response;\n\npublic class U2fDemoException extends WebApplicationException {\n    public U2fDemoException() {\n        super(Response.status(Response.Status.NOT_FOUND).build());\n    }\n\n    public U2fDemoException(String message) {\n        super(Response.status(Response.Status.NOT_FOUND).\n                entity(message).type(\"text/plain\").build());\n    }\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/view/AuthenticationView.java",
    "content": "package demo.view;\n\nimport com.yubico.u2f.data.messages.SignRequestData;\nimport io.dropwizard.views.View;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\npublic class AuthenticationView extends View {\n\n    private final SignRequestData data;\n    private final String username;\n\n    public SignRequestData getData() {\n        return data;\n    }\n\n    public String getDataJson() {\n        return data.toJson();\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public AuthenticationView(SignRequestData data, String username) {\n        super(\"authenticate.ftl\");\n        this.data = checkNotNull(data);\n        this.username = checkNotNull(username);\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/view/FinishAuthenticationView.java",
    "content": "package demo.view;\n\nimport io.dropwizard.views.View;\nimport java.util.Collections;\nimport java.util.List;\nimport lombok.Getter;\n\n@Getter\npublic class FinishAuthenticationView extends View {\n\n    private final boolean success;\n    private final List<String> messages;\n\n    public FinishAuthenticationView(boolean success, List<String> messages) {\n        super(\"finishAuthentication.ftl\");\n        this.success = success;\n        this.messages = messages;\n    }\n\n    public FinishAuthenticationView(boolean success, String message) {\n        this(success, Collections.singletonList(message));\n    }\n\n    public FinishAuthenticationView(boolean success) {\n        this(success, Collections.<String>emptyList());\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/view/FinishRegistrationView.java",
    "content": "package demo.view;\n\nimport com.yubico.u2f.attestation.Attestation;\nimport com.yubico.u2f.data.DeviceRegistration;\nimport io.dropwizard.views.View;\nimport lombok.Getter;\n\n@Getter\npublic class FinishRegistrationView extends View {\n\n    private Attestation attestation;\n    private DeviceRegistration registration;\n\n    public FinishRegistrationView(Attestation attestation, DeviceRegistration registration) {\n        super(\"finishRegistration.ftl\");\n        this.attestation = attestation;\n        this.registration = registration;\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/java/demo/view/RegistrationView.java",
    "content": "package demo.view;\n\nimport io.dropwizard.views.View;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\npublic class RegistrationView extends View {\n\n    private final String username;\n    private final String data;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public String getData() {\n        return data;\n    }\n\n    public RegistrationView(String data, String username) {\n        super(\"register.ftl\");\n        this.data = checkNotNull(data);\n        this.username = checkNotNull(username);\n    }\n\n}\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/assets/loginIndex.html",
    "content": "<html>\n<body>\n<h2>Login</h2>\n\n<form method=\"GET\" action=\"/startAuthentication\" id=\"form\">\n    <label for=\"username\">Username</label>\n    <input name=\"username\" id=\"username\" autofocus/>\n    <button type=\"Submit\">Login</button>\n</form>\n\n<h2>Navigation</h2><ul><li><a href='/assets/registerIndex.html'>Register</a></li><li><a href='/assets/loginIndex.html'>Login</a></li></ul>\n</body>\n</html>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/assets/registerIndex.html",
    "content": "<html>\n<body>\n<h2>Register</h2>\n\n<form method=\"GET\" action=\"/startRegistration\" id=\"form\">\n    <label for=\"username\">Username</label>\n    <input name=\"username\" id=\"username\" autofocus/>\n    <button type=\"Submit\">Register</button>\n</form>\n\n<h2>Navigation</h2><ul><li><a href='/assets/registerIndex.html'>Register</a></li><li><a href='/assets/loginIndex.html'>Login</a></li></ul>\n</body>\n</html>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/assets/u2f-api-1.1.js",
    "content": "//Copyright 2014-2015 Google Inc. All rights reserved.\n\n//Use of this source code is governed by a BSD-style\n//license that can be found in the LICENSE file or at\n//https://developers.google.com/open-source/licenses/bsd\n\n/**\n * @fileoverview The U2F api.\n */\n'use strict';\n\n\n/**\n * Namespace for the U2F api.\n * @type {Object}\n */\nvar u2f = u2f || {};\n\n/**\n * FIDO U2F Javascript API Version\n * @number\n */\nvar js_api_version;\n\n/**\n * The U2F extension id\n * @const {string}\n */\n// The Chrome packaged app extension ID.\n// Uncomment this if you want to deploy a server instance that uses\n// the package Chrome app and does not require installing the U2F Chrome extension.\n u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';\n// The U2F Chrome extension ID.\n// Uncomment this if you want to deploy a server instance that uses\n// the U2F Chrome extension to authenticate.\n// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';\n\n\n/**\n * Message types for messsages to/from the extension\n * @const\n * @enum {string}\n */\nu2f.MessageTypes = {\n    'U2F_REGISTER_REQUEST': 'u2f_register_request',\n    'U2F_REGISTER_RESPONSE': 'u2f_register_response',\n    'U2F_SIGN_REQUEST': 'u2f_sign_request',\n    'U2F_SIGN_RESPONSE': 'u2f_sign_response',\n    'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',\n    'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'\n};\n\n\n/**\n * Response status codes\n * @const\n * @enum {number}\n */\nu2f.ErrorCodes = {\n    'OK': 0,\n    'OTHER_ERROR': 1,\n    'BAD_REQUEST': 2,\n    'CONFIGURATION_UNSUPPORTED': 3,\n    'DEVICE_INELIGIBLE': 4,\n    'TIMEOUT': 5\n};\n\n\n/**\n * A message for registration requests\n * @typedef {{\n *   type: u2f.MessageTypes,\n *   appId: ?string,\n *   timeoutSeconds: ?number,\n *   requestId: ?number\n * }}\n */\nu2f.U2fRequest;\n\n\n/**\n * A message for registration responses\n * @typedef {{\n *   type: u2f.MessageTypes,\n *   responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),\n *   requestId: ?number\n * }}\n */\nu2f.U2fResponse;\n\n\n/**\n * An error object for responses\n * @typedef {{\n *   errorCode: u2f.ErrorCodes,\n *   errorMessage: ?string\n * }}\n */\nu2f.Error;\n\n/**\n * Data object for a single sign request.\n * @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}\n */\nu2f.Transport;\n\n\n/**\n * Data object for a single sign request.\n * @typedef {Array<u2f.Transport>}\n */\nu2f.Transports;\n\n/**\n * Data object for a single sign request.\n * @typedef {{\n *   version: string,\n *   challenge: string,\n *   keyHandle: string,\n *   appId: string\n * }}\n */\nu2f.SignRequest;\n\n\n/**\n * Data object for a sign response.\n * @typedef {{\n *   keyHandle: string,\n *   signatureData: string,\n *   clientData: string\n * }}\n */\nu2f.SignResponse;\n\n\n/**\n * Data object for a registration request.\n * @typedef {{\n *   version: string,\n *   challenge: string\n * }}\n */\nu2f.RegisterRequest;\n\n\n/**\n * Data object for a registration response.\n * @typedef {{\n *   version: string,\n *   keyHandle: string,\n *   transports: Transports,\n *   appId: string\n * }}\n */\nu2f.RegisterResponse;\n\n\n/**\n * Data object for a registered key.\n * @typedef {{\n *   version: string,\n *   keyHandle: string,\n *   transports: ?Transports,\n *   appId: ?string\n * }}\n */\nu2f.RegisteredKey;\n\n\n/**\n * Data object for a get API register response.\n * @typedef {{\n *   js_api_version: number\n * }}\n */\nu2f.GetJsApiVersionResponse;\n\n\n//Low level MessagePort API support\n\n/**\n * Sets up a MessagePort to the U2F extension using the\n * available mechanisms.\n * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback\n */\nu2f.getMessagePort = function(callback) {\n  if (typeof chrome != 'undefined' && chrome.runtime) {\n    // The actual message here does not matter, but we need to get a reply\n    // for the callback to run. Thus, send an empty signature request\n    // in order to get a failure response.\n    var msg = {\n        type: u2f.MessageTypes.U2F_SIGN_REQUEST,\n        signRequests: []\n    };\n    chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {\n      if (!chrome.runtime.lastError) {\n        // We are on a whitelisted origin and can talk directly\n        // with the extension.\n        u2f.getChromeRuntimePort_(callback);\n      } else {\n        // chrome.runtime was available, but we couldn't message\n        // the extension directly, use iframe\n        u2f.getIframePort_(callback);\n      }\n    });\n  } else if (u2f.isAndroidChrome_()) {\n    u2f.getAuthenticatorPort_(callback);\n  } else if (u2f.isIosChrome_()) {\n    u2f.getIosPort_(callback);\n  } else {\n    // chrome.runtime was not available at all, which is normal\n    // when this origin doesn't have access to any extensions.\n    u2f.getIframePort_(callback);\n  }\n};\n\n/**\n * Detect chrome running on android based on the browser's useragent.\n * @private\n */\nu2f.isAndroidChrome_ = function() {\n  var userAgent = navigator.userAgent;\n  return userAgent.indexOf('Chrome') != -1 &&\n  userAgent.indexOf('Android') != -1;\n};\n\n/**\n * Detect chrome running on iOS based on the browser's platform.\n * @private\n */\nu2f.isIosChrome_ = function() {\n  return [\"iPhone\", \"iPad\", \"iPod\"].indexOf(navigator.platform) > -1;\n};\n\n/**\n * Connects directly to the extension via chrome.runtime.connect.\n * @param {function(u2f.WrappedChromeRuntimePort_)} callback\n * @private\n */\nu2f.getChromeRuntimePort_ = function(callback) {\n  var port = chrome.runtime.connect(u2f.EXTENSION_ID,\n      {'includeTlsChannelId': true});\n  setTimeout(function() {\n    callback(new u2f.WrappedChromeRuntimePort_(port));\n  }, 0);\n};\n\n/**\n * Return a 'port' abstraction to the Authenticator app.\n * @param {function(u2f.WrappedAuthenticatorPort_)} callback\n * @private\n */\nu2f.getAuthenticatorPort_ = function(callback) {\n  setTimeout(function() {\n    callback(new u2f.WrappedAuthenticatorPort_());\n  }, 0);\n};\n\n/**\n * Return a 'port' abstraction to the iOS client app.\n * @param {function(u2f.WrappedIosPort_)} callback\n * @private\n */\nu2f.getIosPort_ = function(callback) {\n  setTimeout(function() {\n    callback(new u2f.WrappedIosPort_());\n  }, 0);\n};\n\n/**\n * A wrapper for chrome.runtime.Port that is compatible with MessagePort.\n * @param {Port} port\n * @constructor\n * @private\n */\nu2f.WrappedChromeRuntimePort_ = function(port) {\n  this.port_ = port;\n};\n\n/**\n * Format and return a sign request compliant with the JS API version supported by the extension.\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {number} timeoutSeconds\n * @param {number} reqId\n * @return {Object}\n */\nu2f.formatSignRequest_ =\n  function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {\n  if (js_api_version === undefined || js_api_version < 1.1) {\n    // Adapt request to the 1.0 JS API\n    var signRequests = [];\n    for (var i = 0; i < registeredKeys.length; i++) {\n      signRequests[i] = {\n          version: registeredKeys[i].version,\n          challenge: challenge,\n          keyHandle: registeredKeys[i].keyHandle,\n          appId: appId\n      };\n    }\n    return {\n      type: u2f.MessageTypes.U2F_SIGN_REQUEST,\n      signRequests: signRequests,\n      timeoutSeconds: timeoutSeconds,\n      requestId: reqId\n    };\n  }\n  // JS 1.1 API\n  return {\n    type: u2f.MessageTypes.U2F_SIGN_REQUEST,\n    appId: appId,\n    challenge: challenge,\n    registeredKeys: registeredKeys,\n    timeoutSeconds: timeoutSeconds,\n    requestId: reqId\n  };\n};\n\n/**\n * Format and return a register request compliant with the JS API version supported by the extension..\n * @param {Array<u2f.SignRequest>} signRequests\n * @param {Array<u2f.RegisterRequest>} signRequests\n * @param {number} timeoutSeconds\n * @param {number} reqId\n * @return {Object}\n */\nu2f.formatRegisterRequest_ =\n  function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {\n  if (js_api_version === undefined || js_api_version < 1.1) {\n    // Adapt request to the 1.0 JS API\n    for (var i = 0; i < registerRequests.length; i++) {\n      registerRequests[i].appId = appId;\n    }\n    var signRequests = [];\n    for (var i = 0; i < registeredKeys.length; i++) {\n      signRequests[i] = {\n          version: registeredKeys[i].version,\n          challenge: registerRequests[0],\n          keyHandle: registeredKeys[i].keyHandle,\n          appId: appId\n      };\n    }\n    return {\n      type: u2f.MessageTypes.U2F_REGISTER_REQUEST,\n      signRequests: signRequests,\n      registerRequests: registerRequests,\n      timeoutSeconds: timeoutSeconds,\n      requestId: reqId\n    };\n  }\n  // JS 1.1 API\n  return {\n    type: u2f.MessageTypes.U2F_REGISTER_REQUEST,\n    appId: appId,\n    registerRequests: registerRequests,\n    registeredKeys: registeredKeys,\n    timeoutSeconds: timeoutSeconds,\n    requestId: reqId\n  };\n};\n\n\n/**\n * Posts a message on the underlying channel.\n * @param {Object} message\n */\nu2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {\n  this.port_.postMessage(message);\n};\n\n\n/**\n * Emulates the HTML 5 addEventListener interface. Works only for the\n * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.\n * @param {string} eventName\n * @param {function({data: Object})} handler\n */\nu2f.WrappedChromeRuntimePort_.prototype.addEventListener =\n    function(eventName, handler) {\n  var name = eventName.toLowerCase();\n  if (name == 'message' || name == 'onmessage') {\n    this.port_.onMessage.addListener(function(message) {\n      // Emulate a minimal MessageEvent object\n      handler({'data': message});\n    });\n  } else {\n    console.error('WrappedChromeRuntimePort only supports onMessage');\n  }\n};\n\n/**\n * Wrap the Authenticator app with a MessagePort interface.\n * @constructor\n * @private\n */\nu2f.WrappedAuthenticatorPort_ = function() {\n  this.requestId_ = -1;\n  this.requestObject_ = null;\n}\n\n/**\n * Launch the Authenticator intent.\n * @param {Object} message\n */\nu2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {\n  var intentUrl =\n    u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +\n    ';S.request=' + encodeURIComponent(JSON.stringify(message)) +\n    ';end';\n  document.location = intentUrl;\n};\n\n/**\n * Tells what type of port this is.\n * @return {String} port type\n */\nu2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {\n  return \"WrappedAuthenticatorPort_\";\n};\n\n\n/**\n * Emulates the HTML 5 addEventListener interface.\n * @param {string} eventName\n * @param {function({data: Object})} handler\n */\nu2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {\n  var name = eventName.toLowerCase();\n  if (name == 'message') {\n    var self = this;\n    /* Register a callback to that executes when\n     * chrome injects the response. */\n    window.addEventListener(\n        'message', self.onRequestUpdate_.bind(self, handler), false);\n  } else {\n    console.error('WrappedAuthenticatorPort only supports message');\n  }\n};\n\n/**\n * Callback invoked  when a response is received from the Authenticator.\n * @param function({data: Object}) callback\n * @param {Object} message message Object\n */\nu2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =\n    function(callback, message) {\n  var messageObject = JSON.parse(message.data);\n  var intentUrl = messageObject['intentURL'];\n\n  var errorCode = messageObject['errorCode'];\n  var responseObject = null;\n  if (messageObject.hasOwnProperty('data')) {\n    responseObject = /** @type {Object} */ (\n        JSON.parse(messageObject['data']));\n  }\n\n  callback({'data': responseObject});\n};\n\n/**\n * Base URL for intents to Authenticator.\n * @const\n * @private\n */\nu2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =\n  'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';\n\n/**\n * Wrap the iOS client app with a MessagePort interface.\n * @constructor\n * @private\n */\nu2f.WrappedIosPort_ = function() {};\n\n/**\n * Launch the iOS client app request\n * @param {Object} message\n */\nu2f.WrappedIosPort_.prototype.postMessage = function(message) {\n  var str = JSON.stringify(message);\n  var url = \"u2f://auth?\" + encodeURI(str);\n  location.replace(url);\n};\n\n/**\n * Tells what type of port this is.\n * @return {String} port type\n */\nu2f.WrappedIosPort_.prototype.getPortType = function() {\n  return \"WrappedIosPort_\";\n};\n\n/**\n * Emulates the HTML 5 addEventListener interface.\n * @param {string} eventName\n * @param {function({data: Object})} handler\n */\nu2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {\n  var name = eventName.toLowerCase();\n  if (name !== 'message') {\n    console.error('WrappedIosPort only supports message');\n  }\n};\n\n/**\n * Sets up an embedded trampoline iframe, sourced from the extension.\n * @param {function(MessagePort)} callback\n * @private\n */\nu2f.getIframePort_ = function(callback) {\n  // Create the iframe\n  var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;\n  var iframe = document.createElement('iframe');\n  iframe.src = iframeOrigin + '/u2f-comms.html';\n  iframe.setAttribute('style', 'display:none');\n  document.body.appendChild(iframe);\n\n  var channel = new MessageChannel();\n  var ready = function(message) {\n    if (message.data == 'ready') {\n      channel.port1.removeEventListener('message', ready);\n      callback(channel.port1);\n    } else {\n      console.error('First event on iframe port was not \"ready\"');\n    }\n  };\n  channel.port1.addEventListener('message', ready);\n  channel.port1.start();\n\n  iframe.addEventListener('load', function() {\n    // Deliver the port to the iframe and initialize\n    iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);\n  });\n};\n\n\n//High-level JS API\n\n/**\n * Default extension response timeout in seconds.\n * @const\n */\nu2f.EXTENSION_TIMEOUT_SEC = 30;\n\n/**\n * A singleton instance for a MessagePort to the extension.\n * @type {MessagePort|u2f.WrappedChromeRuntimePort_}\n * @private\n */\nu2f.port_ = null;\n\n/**\n * Callbacks waiting for a port\n * @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}\n * @private\n */\nu2f.waitingForPort_ = [];\n\n/**\n * A counter for requestIds.\n * @type {number}\n * @private\n */\nu2f.reqCounter_ = 0;\n\n/**\n * A map from requestIds to client callbacks\n * @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))\n *                       |function((u2f.Error|u2f.SignResponse)))>}\n * @private\n */\nu2f.callbackMap_ = {};\n\n/**\n * Creates or retrieves the MessagePort singleton to use.\n * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback\n * @private\n */\nu2f.getPortSingleton_ = function(callback) {\n  if (u2f.port_) {\n    callback(u2f.port_);\n  } else {\n    if (u2f.waitingForPort_.length == 0) {\n      u2f.getMessagePort(function(port) {\n        u2f.port_ = port;\n        u2f.port_.addEventListener('message',\n            /** @type {function(Event)} */ (u2f.responseHandler_));\n\n        // Careful, here be async callbacks. Maybe.\n        while (u2f.waitingForPort_.length)\n          u2f.waitingForPort_.shift()(u2f.port_);\n      });\n    }\n    u2f.waitingForPort_.push(callback);\n  }\n};\n\n/**\n * Handles response messages from the extension.\n * @param {MessageEvent.<u2f.Response>} message\n * @private\n */\nu2f.responseHandler_ = function(message) {\n  var response = message.data;\n  var reqId = response['requestId'];\n  if (!reqId || !u2f.callbackMap_[reqId]) {\n    console.error('Unknown or missing requestId in response.');\n    return;\n  }\n  var cb = u2f.callbackMap_[reqId];\n  delete u2f.callbackMap_[reqId];\n  cb(response['responseData']);\n};\n\n/**\n * Dispatches an array of sign requests to available U2F tokens.\n * If the JS API version supported by the extension is unknown, it first sends a\n * message to the extension to find out the supported API version and then it sends\n * the sign request.\n * @param {string=} appId\n * @param {string=} challenge\n * @param {Array<u2f.RegisteredKey>} registeredKeys\n * @param {function((u2f.Error|u2f.SignResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {\n  if (js_api_version === undefined) {\n    // Send a message to get the extension to JS API version, then send the actual sign request.\n    u2f.getApiVersion(\n        function (response) {\n          js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];\n          console.log(\"Extension JS API Version: \", js_api_version);\n          u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);\n        });\n  } else {\n    // We know the JS API version. Send the actual sign request in the supported API version.\n    u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);\n  }\n};\n\n/**\n * Dispatches an array of sign requests to available U2F tokens.\n * @param {string=} appId\n * @param {string=} challenge\n * @param {Array<u2f.RegisteredKey>} registeredKeys\n * @param {function((u2f.Error|u2f.SignResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {\n  u2f.getPortSingleton_(function(port) {\n    var reqId = ++u2f.reqCounter_;\n    u2f.callbackMap_[reqId] = callback;\n    var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?\n        opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);\n    var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);\n    port.postMessage(req);\n  });\n};\n\n/**\n * Dispatches register requests to available U2F tokens. An array of sign\n * requests identifies already registered tokens.\n * If the JS API version supported by the extension is unknown, it first sends a\n * message to the extension to find out the supported API version and then it sends\n * the register request.\n * @param {string=} appId\n * @param {Array<u2f.RegisterRequest>} registerRequests\n * @param {Array<u2f.RegisteredKey>} registeredKeys\n * @param {function((u2f.Error|u2f.RegisterResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {\n  if (js_api_version === undefined) {\n    // Send a message to get the extension to JS API version, then send the actual register request.\n    u2f.getApiVersion(\n        function (response) {\n          js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];\n          console.log(\"Extension JS API Version: \", js_api_version);\n          u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,\n              callback, opt_timeoutSeconds);\n        });\n  } else {\n    // We know the JS API version. Send the actual register request in the supported API version.\n    u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,\n        callback, opt_timeoutSeconds);\n  }\n};\n\n/**\n * Dispatches register requests to available U2F tokens. An array of sign\n * requests identifies already registered tokens.\n * @param {string=} appId\n * @param {Array<u2f.RegisterRequest>} registerRequests\n * @param {Array<u2f.RegisteredKey>} registeredKeys\n * @param {function((u2f.Error|u2f.RegisterResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {\n  u2f.getPortSingleton_(function(port) {\n    var reqId = ++u2f.reqCounter_;\n    u2f.callbackMap_[reqId] = callback;\n    var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?\n        opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);\n    var req = u2f.formatRegisterRequest_(\n        appId, registeredKeys, registerRequests, timeoutSeconds, reqId);\n    port.postMessage(req);\n  });\n};\n\n\n/**\n * Dispatches a message to the extension to find out the supported\n * JS API version.\n * If the user is on a mobile phone and is thus using Google Authenticator instead\n * of the Chrome extension, don't send the request and simply return 0.\n * @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback\n * @param {number=} opt_timeoutSeconds\n */\nu2f.getApiVersion = function(callback, opt_timeoutSeconds) {\n u2f.getPortSingleton_(function(port) {\n   // If we are using Android Google Authenticator or iOS client app,\n   // do not fire an intent to ask which JS API version to use.\n   if (port.getPortType) {\n     var apiVersion;\n     switch (port.getPortType()) {\n       case 'WrappedIosPort_':\n       case 'WrappedAuthenticatorPort_':\n         apiVersion = 1.1;\n         break;\n\n       default:\n         apiVersion = 0;\n         break;\n     }\n     callback({ 'js_api_version': apiVersion });\n     return;\n   }\n    var reqId = ++u2f.reqCounter_;\n    u2f.callbackMap_[reqId] = callback;\n    var req = {\n      type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST,\n      timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?\n          opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),\n      requestId: reqId\n    };\n    port.postMessage(req);\n  });\n};\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/demo/view/authenticate.ftl",
    "content": "<html>\n<head>\n<title>Java U2F Demo</title>\n\n<script src=\"/assets/u2f-api-1.1.js\"></script>\n\n<script>\nvar request = JSON.parse(\"${dataJson?json_string?no_esc}\");\nsetTimeout(function() {\n\n    if (request.signRequests.length > 0) {\n        u2f.sign(\n            request.appId,\n            request.challenge,\n            request.signRequests,\n            function(data) {\n                if(data.errorCode) {\n                    switch (data.errorCode) {\n                        case 4:\n                            alert(\"This device is not registered for this account.\");\n                            break;\n\n                        default:\n                            alert(\"U2F failed with error code: \" + data.errorCode);\n                    }\n                    return;\n                } else {\n                    document.getElementById('tokenResponse').value = JSON.stringify(data);\n                    document.getElementById('form').submit();\n                }\n            }\n        );\n    }\n}, 1000);\n</script>\n\n</head>\n    <body>\n\n    <#list data.getSignRequests() as signRequests>\n      <p>Touch your U2F token to authenticate.</p>\n          <form method=\"POST\" action=\"finishAuthentication\" id=\"form\">\n              <input type=\"hidden\" name=\"tokenResponse\" id=\"tokenResponse\"/>\n              <input type=\"hidden\" name=\"username\" id=\"username\" value=\"${username}\"/>\n          </form>\n        <#break>\n    <#else>\n        <p>No devices are registered for this account.</p>\n    </#list>\n\n\n    <#include \"navigation.ftl\">\n    </body>\n</html>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/demo/view/finishAuthentication.ftl",
    "content": "<html>\n<head>\n<title>Java U2F Demo</title>\n</head>\n<body>\n\n    <#if success>\n      <p>\n        Successfully authenticated!\n      </p>\n    </#if>\n\n    <#list messages as message>\n      <p>${message}</p>\n    </#list>\n\n    <#include \"navigation.ftl\">\n\n</body>\n</html>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/demo/view/finishRegistration.ftl",
    "content": "<html>\n<head>\n<title>Java U2F Demo</title>\n</head>\n<body>\n\n    <p>Successfully registered device:</p>\n\n    <#list attestation.getVendorProperties()>\n        <p>Vendor metadata</p>\n\n        <#items as key, value>\n            <pre>\n                ${key}: ${value}\n            </pre>\n        </#items>\n    <#else>\n        <p>No vendor metadata present!</p>\n    </#list>\n\n    <#list attestation.getDeviceProperties()>\n        <p>Device metadata</p>\n\n        <#items as key, value>\n            <pre>\n                ${key}: ${value}\n            </pre>\n        </#items>\n    <#else>\n        <p>No device metadata present!</p>\n    </#list>\n\n    <#list attestation.getTransports()>\n        <p>Device transports: <#items as item>${item}<#sep>, </#sep></#items></p>\n    <#else>\n        <p>No device transports reported!</p>\n    </#list>\n\n    <p>Registration data</p>\n    <pre> ${registration} </pre>\n\n    <#include \"navigation.ftl\">\n\n</body>\n</html>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/demo/view/navigation.ftl",
    "content": "<h2>Navigation</h2>\n\n<ul>\n  <li><a href='/assets/registerIndex.html'>Register</a></li>\n  <li><a href='/assets/loginIndex.html'>Login</a></li>\n</ul>\n"
  },
  {
    "path": "u2flib-server-demo/src/main/resources/demo/view/register.ftl",
    "content": "<html>\n<head>\n<title>Java U2F Demo</title>\n\n<script src=\"/assets/u2f-api-1.1.js\"></script>\n\n<script>\nvar request = JSON.parse(\"${data?json_string?no_esc}\");\nsetTimeout(function() {\n    u2f.register(\n        request.appId,\n        request.registerRequests,\n        request.registeredKeys,\n        function(data) {\n            var form = document.getElementById('form');\n            var reg = document.getElementById('tokenResponse');\n            if(data.errorCode) {\n                switch (data.errorCode) {\n                    case 4:\n                        alert(\"This device is already registered.\");\n                        break;\n\n                    default:\n                        alert(\"U2F failed with error: \" + data.errorCode);\n                }\n            } else {\n                reg.value=JSON.stringify(data);\n                form.submit();\n            }\n        }\n    );\n}, 1000);\n</script>\n\n</head>\n    <body>\n    <p>Touch your U2F token.</p>\n        <form method=\"POST\" action=\"finishRegistration\" id=\"form\" onsubmit=\"return false;\">\n            <input type=\"hidden\" name=\"username\" value=\"${username}\"/>\n            <input type=\"hidden\" name=\"tokenResponse\" id=\"tokenResponse\"/>\n        </form>\n    </body>\n\n    <#include \"navigation.ftl\">\n</html>\n"
  }
]