diff options
author | Eran Messeri <eranm@google.com> | 2022-12-16 12:08:25 +0000 |
---|---|---|
committer | Eran Messeri <eranm@google.com> | 2022-12-16 12:08:40 +0000 |
commit | 5c9e36b036aa09554095f0228d73048b9cbe8ea9 (patch) | |
tree | 377bb113af9125b399c3ef49013385a6bd620dd5 | |
parent | 95e6c433f58f876697aca881fb4348b01a9f541a (diff) | |
parent | ed76d62a0f8ab6a012a5c405b56eb69c35ef2c7c (diff) | |
download | android-key-attestation-5c9e36b036aa09554095f0228d73048b9cbe8ea9.tar.gz |
Merge remote-tracking branch 'aosp/upstream-master' into merge_android_ka
Test: N/A
Bug: 257009517
Bug: 225139985
Change-Id: I2393efad3792dc10affc8c4b6ad0666a6c0ff773
60 files changed, 3669 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..28038a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# built application files +*.apk +*.ap_ +# files for the dex VM +*.dex +# Java class files +*.class +# generated files +bin/ +build/ +gen/ +out/ +# Local configuration file (sdk path, etc) +local.properties +# Eclipse project files +.classpath +.project +# Windows thumbnail db +.DS_Store +# Idea project fileS +*.iml +*.ipr +*.iws +.idea/ +# Gradle config files +.gradle/ +# Sandbox stuff +_sandbox +bazel-*/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a507046 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# How to become a contributor and submit your own code + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement +(CLA). + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual CLA] + (http://code.google.com/legal/individual-cla-v1.0.html). + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA] + (http://code.google.com/legal/corporate-cla-v1.0.html). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing a Patch + +1. Sign a Contributor License Agreement, if you have not yet done so (see + details above). +1. Create your change to the repo in question. + * Fork the desired repo, develop and test your code changes. + * Ensure that your code is clear and comprehensible. + * Ensure that your code has an appropriate set of unit tests which all pass. +1. Submit a pull request. +1. The repo owner will review your request. If it is approved, the change will + be merged. If it needs additional work, the repo owner will respond with + useful comments. + @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..399bc72 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +Android Key Attestation Library +=================================== + +This library uses the [Bouncy Castle ASN.1][1] parser to extract information +from an Android attestation data structure to verify that a key pair has been +generated in a hardware-protected environment of an Android device. It is +maintained in tandem with Android's key attestation capabilities and is meant +for production use. + +This repository contains a [server](server/src/main/java/com/android/example/) +sample code that shows how to validate an Android attestation certificate chain +outside the Android framework. This is the recommended best practice, since if +the Android device is rooted or otherwise compromised, on-device validation of +the attestation may be inaccurate. + +The entry point into the +[library itself](server/src/main/java/com/google/android/attestation/) +is `com.google.android.attestation.ParsedAttestationRecord.createParsedAttestationRecord`. + +For more details, see the documentation and the guide at +https://developer.android.com/training/articles/security-key-attestation.html . + +[1]: https://www.bouncycastle.org/ + + +Getting Started +--------------- + +See the [server](server/) sample for details. + +Support +------- + +- Stack Overflow: http://stackoverflow.com/questions/tagged/android + +If you've found an error in this sample, please file an issue: +https://github.com/google/android-key-attestation + +Patches are encouraged, and may be submitted by forking this project and +submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details. + +License +------- + +Copyright 2016, The Android Open Source Project, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..4012114 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,45 @@ +workspace( + name = "android-key-attestation", +) + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +RULES_JVM_EXTERNAL_TAG = "4.2" + +RULES_JVM_EXTERNAL_SHA = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca" + +http_archive( + name = "rules_jvm_external", + sha256 = RULES_JVM_EXTERNAL_SHA, + strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG, + url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, +) + +load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps") + +rules_jvm_external_deps() + +load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup") + +rules_jvm_external_setup() + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +maven_install( + artifacts = [ + # Bouncy Castle Cryptography APIs used for certificate verification + "org.bouncycastle:bcpkix-jdk15on:1.61", + "org.bouncycastle:bcprov-jdk15on:1.61", + + # Gson used for decoding certificate status list + "com.google.code.gson:gson:2.8.5", + + # Test libraries + "junit:junit:4.12", + "com.google.truth:truth:1.0", + "com.google.truth.extensions:truth-java8-extension:1.0", + ], + repositories = [ + "https://repo1.maven.org/maven2/", + ], +) diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..5764e74 --- /dev/null +++ b/server/README.md @@ -0,0 +1,82 @@ +Android Key Attestation Sample +============================== + +This sample illustrates how to use the [Bouncy Castle ASN.1][1] parser to extract information +from an Android attestation data structure to verify that a key pair has been +generated in an Android device. This sample demonstrates how to verify a certificate on a server. + +[1]: https://www.bouncycastle.org/ + +Introduction +------------ + +This example demonstrates the following tasks: + +1. Loading the certificates from [PEM/DER][2]-encoded strings. +1. Verifying the [X.509][3] certificate chain, up to the root. This includes checking that the root certificate is one of Google's root certificates listed in https://developer.android.com/training/articles/security-key-attestation. +1. Extracting the attestation extension data from the attestation + certificate. +1. Verifying (and printing) data elements from the attestation extension. + +For more information about the process of extracting attestation certificate +extension data, as well as the extension data schema, see the +[Key Attestation][4] Android developer training article. + +Note that this sample demonstrates the verification of a certificate on a server and not +on the Android framework. Although you can test the certificate and extensions directly +on a device, it is safer to run these checks on a separate server you can trust. + +[2]: https://developer.android.com/reference/java/security/KeyStore.html#getCertificateChain(java.lang.String) +[3]: https://developer.android.com/reference/javax/security/cert/X509Certificate.html +[4]: https://developer.android.com/training/articles/security-key-attestation.html + +Pre-requisites +-------------- + +- Up-to-date Java JDK +- [Bouncy Castle Cryptography Java APIs][5] (included as dependency in gradle build configuration). + +[5]: https://www.bouncycastle.org/java.html + +Getting Started +--------------- + +This sample uses the Gradle build system. To build this project, use the +`gradlew build` command or use "Import Project" in IntelliJ or Android Studio. + +Run the main method in `KeyAttestationExample` directly or use the +`gradlew run --args="<cert-directory>"` task to execute this sample. The `cert-directory` must +contain the certificate chain, one certificate per file in either DER or PEM encoding and the files +are read in alphabetical order. For example the provided +`/examples/pem/algorithm_EC_SecurityLevel_StrongBox/` can be used. + +Support +------- + +- Stack Overflow: http://stackoverflow.com/questions/tagged/android + +If you've found an error in this sample, please file an issue: +https://github.com/googlesamples/android-key-attestation + +Patches are encouraged, and may be submitted by forking this project and +submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details. + +License +------- + +Copyright 2016, The Android Open Source Project, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/server/build.gradle b/server/build.gradle new file mode 100644 index 0000000..d5cb1cc --- /dev/null +++ b/server/build.gradle @@ -0,0 +1,36 @@ +/* Copyright 2016, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'java' +apply plugin: 'application' + +// Set the main class for for Gradle 'run' task. +//noinspection GroovyUnusedAssignment +mainClassName = 'com.android.example.KeyAttestationExample' + +repositories { + mavenCentral() +} + +dependencies { + // Bouncy Castle Cryptography APIs used for certificate verification + compile 'org.bouncycastle:bcpkix-jdk15on:1.61' + // Gson used for decoding certificate status list + compile 'com.google.code.gson:gson:2.8.5' + // JUnit, Truth and Truth8 used for testing + testCompile 'junit:junit:4.12' + testCompile 'com.google.truth:truth:1.0' + testCompile 'com.google.truth.extensions:truth-java8-extension:1.0' +} diff --git a/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert0.der b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert0.der Binary files differnew file mode 100644 index 0000000..1d6e33a --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert0.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert1.der b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert1.der Binary files differnew file mode 100644 index 0000000..09e20d9 --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert1.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert2.der b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert2.der Binary files differnew file mode 100644 index 0000000..477c57f --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert2.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert3.der b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert3.der Binary files differnew file mode 100644 index 0000000..14ffdaa --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_StrongBox/cert3.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert0.der b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert0.der Binary files differnew file mode 100644 index 0000000..a6417a4 --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert0.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert1.der b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert1.der Binary files differnew file mode 100644 index 0000000..44b459a --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert1.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert2.der b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert2.der Binary files differnew file mode 100644 index 0000000..18edca3 --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert2.der diff --git a/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert3.der b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert3.der Binary files differnew file mode 100644 index 0000000..af8de75 --- /dev/null +++ b/server/examples/der/algorithm_EC_SecurityLevel_TEE/cert3.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert0.der b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert0.der Binary files differnew file mode 100644 index 0000000..32e5967 --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert0.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert1.der b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert1.der Binary files differnew file mode 100644 index 0000000..a3e5d72 --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert1.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert2.der b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert2.der Binary files differnew file mode 100644 index 0000000..2e6b13b --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert2.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert3.der b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert3.der Binary files differnew file mode 100644 index 0000000..14ffdaa --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_StrongBox/cert3.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert0.der b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert0.der Binary files differnew file mode 100644 index 0000000..929e614 --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert0.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert1.der b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert1.der Binary files differnew file mode 100644 index 0000000..7131c99 --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert1.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert2.der b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert2.der Binary files differnew file mode 100644 index 0000000..f404a78 --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert2.der diff --git a/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert3.der b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert3.der Binary files differnew file mode 100644 index 0000000..af8de75 --- /dev/null +++ b/server/examples/der/algorithm_RSA_SecurityLevel_TEE/cert3.der diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert0.pem b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert0.pem new file mode 100644 index 0000000..755aeba --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert0.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIID8zCCA5egAwIBAgIBATAMBggqhkjOPQQDAgUAMC8xGTAXBgNVBAUTEDY5N2JjNjRiNmNkNGMw +MWUxEjAQBgNVBAwMCVN0cm9uZ0JveDAeFw03MDAxMDEwMDAwMDBaFw0yODA1MjMyMzU5NTlaMB8x +HTAbBgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +M8o810z1VgBTtio2H1Gh5vA3ySYQ0/RIfn/uPQRCiHGZ1K7tvhQobsfa04rM5PAPuaZDmDnD86C5 +T9SL+msTVqOCArAwggKsMA4GA1UdDwEB/wQEAwIHgDCCApgGCisGAQQB1nkCAREEggKIMIIChAIB +AwoBAgIBBAoBAgQDYWJjBAAwggHNv4U9CAIGAWvSW/8Tv4VFggG7BIIBtzCCAbMxggGLMAwEB2Fu +ZHJvaWQCAR0wGQQUY29tLmFuZHJvaWQua2V5Y2hhaW4CAR0wGQQUY29tLmFuZHJvaWQuc2V0dGlu +Z3MCAR0wGQQUY29tLnF0aS5kaWFnc2VydmljZXMCAR0wGgQVY29tLmFuZHJvaWQuZHluc3lzdGVt +AgEdMB0EGGNvbS5hbmRyb2lkLmlucHV0ZGV2aWNlcwIBHTAfBBpjb20uYW5kcm9pZC5sb2NhbHRy +YW5zcG9ydAIBHTAfBBpjb20uYW5kcm9pZC5sb2NhdGlvbi5mdXNlZAIBHTAfBBpjb20uYW5kcm9p +ZC5zZXJ2ZXIudGVsZWNvbQIBHTAgBBtjb20uYW5kcm9pZC53YWxscGFwZXJiYWNrdXACAR0wIQQc +Y29tLmdvb2dsZS5TU1Jlc3RhcnREZXRlY3RvcgIBHTAiBB1jb20uZ29vZ2xlLmFuZHJvaWQuaGlk +ZGVubWVudQIBATAjBB5jb20uYW5kcm9pZC5wcm92aWRlcnMuc2V0dGluZ3MCAR0xIgQgMBqjywgR +NFAcRfFCKrxmwkIk/V3tX9yPF+aXF2/YZqowgZ2hCDEGAgECAgEDogMCAQOjBAICAQClBTEDAgEE +v4N3AgUAv4U+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAK +AQIEIHKNsSdPHxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgYC +BAE0FfG/hU8GAgQBNBXsMAwGCCqGSM49BAMCBQADSAAwRQIhAN82bz9RzrMXznZKgu61ktdu397w +VvW2Fj/ZKOkcy8p/AiAFhziu1TGVBklOdPH4usrPM/FxAvlOSUDQwj4HP/9PSg== +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert1.pem b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert1.pem new file mode 100644 index 0000000..091d5b0 --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert1.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIICMjCCAbegAwIBAgIKFClzmQlEZCZgeDAKBggqhkjOPQQDAjAvMRkwFwYDVQQFExA2OTdiYzY0 +YjZjZDRjMDFlMRIwEAYDVQQMDAlTdHJvbmdCb3gwHhcNMTgwMzIxMDQwOTE5WhcNMjgwMzE4MDQw +OTE5WjAvMRkwFwYDVQQFExBjY2NlMjYzZmQwOGRhYzNhMRIwEAYDVQQMDAlTdHJvbmdCb3gwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAARpw1ZoXy8BqjHbt0eMA+8QRMoK5aqokaKxWSMAqhkUisyx +jx3eqq5drIA6kYCBcbMeEaK1wskkdE4GnCXoLCOso4G6MIG3MB0GA1UdDgQWBBRR/V0W3sbZqtUt +A0+VyFs/c1wixTAfBgNVHSMEGDAWgBTdlrDyGscOhnpN5p1KzdLVm6b0+DAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwICBDBUBgNVHR8ETTBLMEmgR6BFhkNodHRwczovL2FuZHJvaWQuZ29v +Z2xlYXBpcy5jb20vYXR0ZXN0YXRpb24vY3JsLzE0Mjk3Mzk5MDk0NDY0MjY2MDc4MAoGCCqGSM49 +BAMCA2kAMGYCMQDID/CfPKBG4jksFjWd2uRWxcJcp3/pR1WorB2OmikDznwKNpyTUUnxw442Icwy +PkICMQCGvhARszM/jpxz5WdgbZNsKRVW6fLXlU1bXurrp4Cr36SkunBOrWXaX1Q9/hdjilk= +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert2.pem b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert2.pem new file mode 100644 index 0000000..b4c7aa0 --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert2.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAb+gAwIBAgIKBpaXYEQ3RICBojANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQFExBlMzVk +MzhjNjg5N2Q0N2U4MB4XDTE4MDMyMTA0MDkxOFoXDTI4MDMxODA0MDkxOFowLzEZMBcGA1UEBRMQ +Njk3YmM2NGI2Y2Q0YzAxZTESMBAGA1UEDAwJU3Ryb25nQm94MHYwEAYHKoZIzj0CAQYFK4EEACID +YgAEh8FMICv77jLds6f1v7+i24HWvfgCwr7KtZFjLfsNhgfVXbvH3uOJIHjrgIemkhtEGXVTs+PM +df4M9b+czx0q7UiavVrDy+EAV0EzBqAlL3HMjHoTZ/svm5ZqS5rzDravo4G2MIGzMB0GA1UdDgQW +BBTdlrDyGscOhnpN5p1KzdLVm6b0+DAfBgNVHSMEGDAWgBRTDkUbExXTrVzqjui8W7YUis0d6DAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwczov +L2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20vYXR0ZXN0YXRpb24vY3JsLzM1MDVBMEEyRkRDQzUxRTQw +DQYJKoZIhvcNAQELBQADggIBAA71Fgy7j41XkCE0CtKcM7wVM5E5s1ErWEbsRu8oWoLOy3Ty6GTU +VcNj08BXGaP1Rx0E7sub/U7cbsALeVG7mdzBWS9hW9GFkiiXYbDRN8eAk8EJcmWYe0j2P4EMgPM9 +rTm1lJRPNjCieahD/9QipC/RFpb7nmp5Ftx2Cb/On4qYJFuGTT/0uofZ87omU0d6bpaDw/5Fo6MI +24XoaxVfP02Zvni5DkvGFFLsYRsYyCTij1x3sWBYlkIjiahDOzj271SpkxqwqF4AYSVvsIExa677 +vonOxgPSAeS+48qx3MVW0eI3RsTAOPlcYgI98feD5HN7x3ZWD+uBYaLUul43mLamjJXK0HdH8rGm +8jGBCNKKTPNWVzQEMfVxo4wzPTbjMMuUbN7g8RBDf4wxhG+PSwEO3jwiHAvDfD5F9HUsffeX1cHZ +pMFiojvT6XLh2YsDHqtjRfSIY7AfVRl8TlnsF4nRDR4RrmYBmGeq/QWgZf0KOCWfjroBJbGN3GJx +9OPxig4VrEyzW3yWWjPuy5+F90eL5XXtp0DReY3zoNBeG7Ie9egkPWl2K4+AoU3eEuoZFE8FBkkX +9D5lhlWCfSdYRrVo46Psx8/cdTcHHJNi/cuZLszcDSXTGtZ8lWz1qgggWoT4F8gplodevGYAtntt +19XvsVkCRbdaELNUoxuli/LP +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert3.pem b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert3.pem new file mode 100644 index 0000000..07018b2 --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_StrongBox/cert3.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFXzCCA0egAwIBAgIINQWgov3MUeQwDQYJKoZIhvcNAQELBQAwGzEZMBcGA1UEBRMQZTM1ZDM4 +YzY4OTdkNDdlODAeFw0xODAzMjEwMzU1MDFaFw0yODAzMTgwMzU1MDFaMBsxGTAXBgNVBAUTEGUz +NWQzOGM2ODk3ZDQ3ZTgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCoQi070/6PH9BA +uJiBcTp8j5R2/Fj6kXFaSxsvUjJKRdi/FCOwUFBJfyhHiWJhga2iguIjAJuhZx5XlMj0pSY7buJi +sPqFknZhKdvfoi4C54j54D+XxCky1APVjD5uc203H+hrRlhh6x4/LTzSXWvb0YLjfOK07HvSddSR +CyKnPydI5bhyCb5QMtVKHzC4Axgx+BihwG1B4UQjOpZpXBHIFZ6EK/XBFeJX0tgrx0MCczkc1X0O +hNFKAYumCKcKyh4q2cGh7UwTRJIT4beIJVrOKDVwo3Fc50k3ICpOAc1zRGzRwupIKKGtW455KW1O +IyAtTJ4NqfIwrkQy5EJ9w/zDzjqFiDNdgTXaMtsz52jHUndfO3lzfvVdBjt2FWLRkWrFmd1tjQ6L +QQFPrSvUHo0XN5kIiOYdNlAyIwBZ51Zn/clU9he78UYAmXHdjfh04DXWgf+GGIFFdmg9tbiHwdqi +8yEcakKHL2TYNylmFJuKOkvLIS5iqEMu29OQyv+BJ1NNQtyn8o1D5f3K2a5qyoUcrL0Je2w28ByL +1JeFURO7wbzkXx9FWwH0stCs/dYq4JKJ8GxKN8aBOl/51atA1cdZAyI55B9ueAYwPevteORGDhEf +cKRaGiWJpSzIFLDmbL9uSpCAw5uqx5h1IRAX1OLi56EsVaFUedt8IqUaCkUcMQIDAQABo4GmMIGj +MB0GA1UdDgQWBBRTDkUbExXTrVzqjui8W7YUis0d6DAfBgNVHSMEGDAWgBRTDkUbExXTrVzqjui8 +W7YUis0d6DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDBABgNVHR8EOTA3MDWgM6Ax +hi9odHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20vYXR0ZXN0YXRpb24vY3JsLzANBgkqhkiG +9w0BAQsFAAOCAgEACHPElcXZEY6jItn3SnVn6gdOCvuFIrhB4J4fhxa6OlDCbxsylHHAbpyoOSJD +0R5dIBTRvz49ElfCpI9yp4l5tUOjWj2K3tMfQjhKN277jApdxbZ6MNac2u+Z3dwS+34YiiCJLVKh +VzWanXk/+9rmVnBzm7qvgan90j2SmH1oYIA3GowJL+1OWSjj6cBH1VrU7guVd7q+aCEQPhTesfbm +UkdUbM/TjUMyhf5SZ7WB8A28MnyYQcFttmGDsE1HZx4fTz8iXxjaph33alasmFUWGtG7KyzvbYUw +wUOmpPd32eT8ocx3B0Z3g+Vo2Cv2LzRRNp15FOf/Ag5HWloyIv3xrsBA2fa4xzOm3/t1Aq0ZViXL +LerKKmU/EJKo7t5gE0wtNebicNhUJaX2C+vShjfleBBwhs33a8b7dBFX/JMwU5+09N83WHRPZjCr +YuK3Uu4qANfJ+f1k+weqh6MpRxAZ6TT0OQt1XQz7rDAlq7AopfXRXI74OCNJf8d+2CPNVW8i5IHN +FjvyEz8ms1ETdw5n3uRbYIgYuCV2Pud3MCwaMTv2E0dLAqg8uJo0JYlhH7wbqidMm2ODRL5FaqWb +b2pfW0vbpCyGfyw0RQGTJAE7xVdvzfdun9HATDm6TbJFcejBdJImfwpZmDOBtDPIbsg5+QVE3lLF +6Licq13i5tIg5yQ= +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert0.pem b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert0.pem new file mode 100644 index 0000000..6d2a613 --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert0.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIID7jCCA5SgAwIBAgIBATAKBggqhkjOPQQDAjApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQxMzI2 +MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAbBgNV +BAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHkyl3epG +PODlaNT50JG1QK/DTFIz5vkasDfsOMQiKlcrbKwmCTfFJqJcz6z/CKt6x5edTL66YxaQ430d0Is3 +JKOCArMwggKvMA4GA1UdDwEB/wQEAwIHgDCCApsGCisGAQQB1nkCAREEggKLMIIChwIBAwoBAQIB +BAoBAQQDYWJjBAAwggHNv4U9CAIGAWTmEWf/v4VFggG7BIIBtzCCAbMxggGLMAwEB2FuZHJvaWQC +AR0wGQQUY29tLmFuZHJvaWQua2V5Y2hhaW4CAR0wGQQUY29tLmFuZHJvaWQuc2V0dGluZ3MCAR0w +GQQUY29tLnF0aS5kaWFnc2VydmljZXMCAR0wGgQVY29tLmFuZHJvaWQuZHluc3lzdGVtAgEdMB0E +GGNvbS5hbmRyb2lkLmlucHV0ZGV2aWNlcwIBHTAfBBpjb20uYW5kcm9pZC5sb2NhbHRyYW5zcG9y +dAIBHTAfBBpjb20uYW5kcm9pZC5sb2NhdGlvbi5mdXNlZAIBHTAfBBpjb20uYW5kcm9pZC5zZXJ2 +ZXIudGVsZWNvbQIBHTAgBBtjb20uYW5kcm9pZC53YWxscGFwZXJiYWNrdXACAR0wIQQcY29tLmdv +b2dsZS5TU1Jlc3RhcnREZXRlY3RvcgIBHTAiBB1jb20uZ29vZ2xlLmFuZHJvaWQuaGlkZGVubWVu +dQIBATAjBB5jb20uYW5kcm9pZC5wcm92aWRlcnMuc2V0dGluZ3MCAR0xIgQgMBqjywgRNFAcRfFC +KrxmwkIk/V3tX9yPF+aXF2/YZqowgaChCDEGAgECAgEDogMCAQOjBAICAQClBTEDAgEEqgMCAQG/ +g3cCBQC/hT4DAgEAv4VATDBKBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoB +AgQgco2xJ08fHPFXHeQ4CwSKVUrEo4Dnb1NVCDUpCEqTeAG/hUEDAgEAv4VCBQIDAxSzv4VOBQID +AxSzv4VPBQIDAxSzMAoGCCqGSM49BAMCA0gAMEUCIDsINbPXvgn8qN2V74vvO9RcuXe17dswxNkm +1vyx1BqCAiEAkicGYwvPJp4jAIZbD9++D+kQJQSJZyE3kT9ukSeSfHs= +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert1.pem b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert1.pem new file mode 100644 index 0000000..b8c664d --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert1.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIICJjCCAaugAwIBAgIKEyBjEXiWOIIJETAKBggqhkjOPQQDAjApMRkwFwYDVQQFExAyOTYwY2I5 +YWViZDUwNWY0MQwwCgYDVQQMDANURUUwHhcNMTgwMzIxMjA1ODU4WhcNMjgwMzE4MjA1ODU4WjAp +MRkwFwYDVQQFExAyZGM1OGIyZDFhMjQxMzI2MQwwCgYDVQQMDANURUUwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAATvMEBAHYIMvvupiQYIRlRceTcA6ZARblK+YPHYI01TXd3qVfLImEdHSp2LiG+c +8Xk4ORhOE9MfT/yukMlqDUSAo4G6MIG3MB0GA1UdDgQWBBT3sqQpymXuuJ9rHrXvzF6qRvAoJzAf +BgNVHSMEGDAWgBTZCjmyzP+wdzrAqIzS/zURX5DNxjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwICBDBUBgNVHR8ETTBLMEmgR6BFhkNodHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20v +YXR0ZXN0YXRpb24vY3JsLzEzMjA2MzExNzg5NjM4ODIwOTExMAoGCCqGSM49BAMCA2kAMGYCMQDW +h8lKjA7mypbTxqgg9kGPSnVuSKPrW1aOzO+ml3r1D1BJaYsTn+v0gPWaAyG09tQCMQCqP4ulQISn +1qhfvcSKYtgATw90q811e8LUvdfid9h6Ew15S9Q5Fs9x+lnnLKtGHoo= +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert2.pem b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert2.pem new file mode 100644 index 0000000..1d0bf11 --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert2.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIID0TCCAbmgAwIBAgIKA4gmZ2BliZaFfTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQFExBmOTIw +MDllODUzYjZiMDQ1MB4XDTE4MDMyMTIwNTM1M1oXDTI4MDMxODIwNTM1M1owKTEZMBcGA1UEBRMQ +Mjk2MGNiOWFlYmQ1MDVmNDEMMAoGA1UEDAwDVEVFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEjXEL +w7zFszCPjAS+Qn6zEFajdp6hD+53zSpN7egUzWfkFEh+wcLa4k67Fv2tKuekmLIhr4xidPllElgi +wAyeB33abMJ5BckSX1ayj4lnpMVvYRovl/5ZHIKNIJMN/hC6o4G2MIGzMB0GA1UdDgQWBBTZCjmy +zP+wdzrAqIzS/zURX5DNxjAfBgNVHSMEGDAWgBQ2YeEAfIgFCVGLRGxH/xpMyepPEjAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwczovL2FuZHJv +aWQuZ29vZ2xlYXBpcy5jb20vYXR0ZXN0YXRpb24vY3JsL0U4RkExOTYzMTREMkZBMTgwDQYJKoZI +hvcNAQELBQADggIBAJKrsCoJam9qc1ZaHeQWf2TAjHDDVylLuUvczTSuu8VhZJiU0+yKGbTNnA13 +n/dvn7rmMj8W6ObWL582Se9MBBdKtAVeCG7qYFq+m6kYFIfL8sOuutKXG5eW+8VY4he0hu+5eZiS +2e3mAZGYF9JwQTAv0H//6+9eLm/+j+L5tr2X4b7H6k2h4YW5HTIzIbVMvp/ic/zmv+RvrmYyXlia +z1xglRqrJ6EiFI9/KsYYsSSVgorlrXPyKOBeXGc2SFIVQmLwfW4gc9BC8V4qBDcpFK1kxOfnkyXl +oVZeat4yuYFSIdtJq9FErMFHfp+pDjIKRGeeWWrPzkbELqtHUSeSktbqN229c+86YRfScxpx5iJ0 +Jp2YsHkiYDFs+0LOex2RNdOKErGKZ+8QbMtVxUIQARTlhZkeLTLAWufFcclEbZkpoe6jo131VjAl +UrkQfK6dVVMx+gLhOffLjH9EmUOD8Y1an9DQCsZl1tpBN4WgInbRMlzBsKF17Z9BEW1f2A+Gn0mn +znZoq6t0OxsvRJTEHEgswQh4Vkr77UYSdcwQ0flrpD9Oe6ObnB0VSSCVo1yhPC39R35Z5KDhiNS8 +BkuPaJoUK8RBbkl5ay4O86LFfY9tdAoHT0A/2hmq/NZhz48K1C9AJGtS+0+zHwSatjWJ7rM/KXAh +J0Nln/nMaeteQy2f +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert3.pem b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert3.pem new file mode 100644 index 0000000..faab920 --- /dev/null +++ b/server/examples/pem/algorithm_EC_SecurityLevel_TEE/cert3.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAw +OWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBm +OTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHs +K7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfd +nJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL +/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04 +T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+R +hhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1 +Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgp +Zrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6 +tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8 +Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCB +ozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0Rs +R/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOg +MYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZI +hvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB +Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1t +wb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCU +JouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhu +Kug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsY +gBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ +79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYk +A3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZu +CuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Y +d6uuJOJENRaNVTzk +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert0.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert0.pem new file mode 100644 index 0000000..da4c811 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert0.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIFjjCCBHagAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMRkwFwYDVQQFExBjY2NlMjYzZmQwOGRh +YzNhMRIwEAYDVQQMDAlTdHJvbmdCb3gwHhcNNzAwMTAxMDAwMDAwWhcNMjgwNTIzMjM1OTU5WjAf +MR0wGwYDVQQDDBRBbmRyb2lkIEtleXN0b3JlIEtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAN/ps3b6b+nFF1AdwOApHr3zG8icT4AIkuMztfzTusXP88wDbzgh1Nsf/T4M9xaTy/jH +z4wukPjxAX88/u0mDinKb9SrgzDLfXyhV7PPus0wEqUfGaN62Bwx2ei3UmZCvyzLruCUUauABZsy +H7Gudj7F4yfwvveLlHEpRx4Up9UzrnkKV3Kgd8mQf67CVBE66AGRPXJzyBu7+4WDAA0lhAvE0XOy +Kac6Hl9CRxSBDX2QzAooVsjOaWjg6eKt+ed1lNYNdxNGNK4UX2e9LEWAJlI50p5jLx3g5Uksu1S9 +WS1u9OU5L52ccGa/OSkPON/qxVf/3Yx6DuuuupPs01RL/J8CAwEAAaOCAsMwggK/MA4GA1UdDwEB +/wQEAwIHgDCCAqsGCisGAQQB1nkCAREEggKbMIIClwIBAwoBAgIBBAoBAgQDYWJjBAAwggHNv4U9 +CAIGAWv2IqDMv4VFggG7BIIBtzCCAbMxggGLMAwEB2FuZHJvaWQCAR0wGQQUY29tLmFuZHJvaWQu +a2V5Y2hhaW4CAR0wGQQUY29tLmFuZHJvaWQuc2V0dGluZ3MCAR0wGQQUY29tLnF0aS5kaWFnc2Vy +dmljZXMCAR0wGgQVY29tLmFuZHJvaWQuZHluc3lzdGVtAgEdMB0EGGNvbS5hbmRyb2lkLmlucHV0 +ZGV2aWNlcwIBHTAfBBpjb20uYW5kcm9pZC5sb2NhbHRyYW5zcG9ydAIBHTAfBBpjb20uYW5kcm9p +ZC5sb2NhdGlvbi5mdXNlZAIBHTAfBBpjb20uYW5kcm9pZC5zZXJ2ZXIudGVsZWNvbQIBHTAgBBtj +b20uYW5kcm9pZC53YWxscGFwZXJiYWNrdXACAR0wIQQcY29tLmdvb2dsZS5TU1Jlc3RhcnREZXRl +Y3RvcgIBHTAiBB1jb20uZ29vZ2xlLmFuZHJvaWQuaGlkZGVubWVudQIBATAjBB5jb20uYW5kcm9p +ZC5wcm92aWRlcnMuc2V0dGluZ3MCAR0xIgQgMBqjywgRNFAcRfFCKrxmwkIk/V3tX9yPF+aXF2/Y +ZqowgbChCDEGAgECAgEDogMCAQGjBAICCAClBTEDAgEEpggxBgIBAwIBBb+BSAUCAwEAAb+DdwIF +AL+FPgMCAQC/hUBMMEoEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEACgECBCBy +jbEnTx8c8Vcd5DgLBIpVSsSjgOdvU1UINSkISpN4Ab+FQQMCAQC/hUIFAgMDFLO/hU4GAgQBNBXx +v4VPBgIEATQV7DANBgkqhkiG9w0BAQsFAAOCAQEAmu4L5Rs3q0EjeSJYIJVzGi4R/e9uNpS1IR5p +6sjwsWXIuS+KGNjE/bD9hiDDNW3EglUGU2Xp9cW/PhVTAp6hnappa1dZb5W4EUAwtedUsLsgBva1 +YKJD5/EB+6vaktJLaMqFRK1x8V11SAVi45hBU8wgALmGTIN9n0w+mxs4isab+unysXpnzcd7PRN/ +SdbyQ1AY2P52FDemV/VvMqUgfQTHLkf0Sjk3ydWDtpQ8xAppxhssHofWeh84FXSIJCSQ59FUCgb+ +aPdyvKNZPDoUfaX098cAs7JXTFRhfW64jOmoaNdcPfzSGFF7pMcsOWTo2vmCvKO+BRxsWDhPfB/k +1Q== +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert1.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert1.pem new file mode 100644 index 0000000..a9a4ba7 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert1.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIEnTCCAoWgAwIBAgIKFYRggggVNRl4dzANBgkqhkiG9w0BAQsFADAvMRkwFwYDVQQFExA2OTdi +YzY0YjZjZDRjMDFlMRIwEAYDVQQMDAlTdHJvbmdCb3gwHhcNMTgwMzIxMDQwOTE4WhcNMjgwMzE4 +MDQwOTE4WjAvMRkwFwYDVQQFExBjY2NlMjYzZmQwOGRhYzNhMRIwEAYDVQQMDAlTdHJvbmdCb3gw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC44jdUqUTiyjmNPPY3KlN088t8B7qsmChF +fIbZuNBHgr8mYY1Zhjs170QO9OqXUtjhmWN1dAl9NtEe0dL+RgaOYD+DcgjObj4VuCvxo6iMLd5A +yPUQT/js61g3mwC2XqZEEJPhen/KIX1cfDYYng4m3n8/IU/uVC1E1MaTMJDBZj+eaa9NOStB/Srj +/oQHl+3kzU72BtLL4vrmfbYhACv1RHRhA1CXIZeW6G8rlPeMCO1tjyXOQttG2vr/Dj/dnRFOFIQA +XCiIw3v63qah7eSWeS59/WncAS7VG8iEPqb/NwBdnGFiJkagsADsFWJO8HJ1PIpwQ1xC/qnALLP9 +Ec65AgMBAAGjgbowgbcwHQYDVR0OBBYEFAd28hRYKlUaWnBgG+hRJkH2Axy2MB8GA1UdIwQYMBaA +FCwYZ+XKkRf4peQMHg55sQUm/p/vMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMFQG +A1UdHwRNMEswSaBHoEWGQ2h0dHBzOi8vYW5kcm9pZC5nb29nbGVhcGlzLmNvbS9hdHRlc3RhdGlv +bi9jcmwvMTU4NDYwODIwODE1MzUxOTc4NzcwDQYJKoZIhvcNAQELBQADggIBACrYYBCoxXuVBmsH +v8rCDAsw0RNow3nJgGZjVlzPGO/Gpu/P1qkKb+ugu7BIvUdc4MScbxjy/9JXUZGwTwski4n3rTwm +SaMt5RP+eLTdKoLVkW1nh+FD6el3nKigN/vFqoAaHv3ayA/7ERGEN5MU8vIQKu4BajPjP2RALHV8 +vTBm0JZbv/fByrS55kqnNLlWGEgmyEC0oK+eG3sa/+ux3RJWgd6VyHhr8TdvV8NwLmEEa19DMiOT +SC/vEPytwRj33FHGpubLpGuoAV3KpCXXHBVNChL7nn1bjnvHY2XA2xpbWp9gdJvDk2f9ODNIpQPu +f0fwWUeYyG+xof3BynJkCcfxmT04oDEFT1ANO2+cHL6cE2hJ9vJ8nblqvNOfyRgNmYghVYXC19aq +lr6wJHun5Lw2Ldd7AGU4Rfvb8ckgf8tGs2mz4XbFLlOZVbDC9bRjwZ7F7ufPgDm0gN9qJHgG5dFe +60nnm6Veq2VGnRz2w8GtjGb1sRFae/tiMt7eRwCHHVGPXC7fCCWvWNbHn/2NhL+GzbGnkcU0wNEv +uCn2kFEmY/q0SZp10qBTC+ztAG4FXUVxdmDomq3sJXm3hxtMXvI948rGC1+C/xrrumUHLLZxJuQ/ +FB1p5pnJKaFKNZjhuNBraB4/sXn9BcJ+XiqHnpludX2/IVddtXiyKwNbsC1X +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert2.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert2.pem new file mode 100644 index 0000000..8153217 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert2.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFhTCCA22gAwIBAgIKBpaXYEQ3RICBoTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQFExBlMzVk +MzhjNjg5N2Q0N2U4MB4XDTE4MDMyMTA0MDkxOFoXDTI4MDMxODA0MDkxOFowLzEZMBcGA1UEBRMQ +Njk3YmM2NGI2Y2Q0YzAxZTESMBAGA1UEDAwJU3Ryb25nQm94MIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAxVPgrTkUevIEJF4VU1JUKet/hsNmQcpl0OwSKEpEFAj8lMusW5LV9U7ZZ6dX +K1SrJKQsC2Z9bFWL3oqDfy+oJnliwgtrSdttIOcGEE7m9QFh+UPhcQplIqCdyf4IGu8kxx2pWDD0 +ZLGG19B/NMiyTUKG9zSEkrOOd6t19KOxk+uVbfgQKRPAX4B9nq+EDZ4aH0mACFQomNpD3ndUMWzv +xFRR24xn0TjnYBfnxLqCYwfLhWpHpwKol1GQi/trUoCwsAsyO7bNQXnVomQa3D5yf8Z0a+xG8qXL +unWa4ydl/yEfWO+ahxov30dMzos1dD+gjc59Z4FkGZPoLZWh0CSKWbbVb0gL8aEKB9EMFQUlDqpO +hVzpWQ7xQTkUPlGiuUU0uPtCFe8SU1ZCpwD3nyArVgqTKoH20fciFSjlIIdjgXRZsVL0skO4ZmSm +fFndn25imE+dBbRUooV8sIdytDFnNxBritjM3gPD0LGqIqpykJkFeHfIe2jT1f+iEK50P/cgCOtZ +SpGbL6x9Vg2/2lPQjygynVsJ6U00fW1Z/s+hW8WxnfcRXSWBK1/2AC/6wNNpSriZJqjV0tFLO6WJ +vp8kOcUev3/lqgafgQHrwSJIRKGLTfaSlzfsghzNtCmawLDSs6ASv0PnYabxk4KbbhvdiL2eHysU +5G+Tk0HMeX24lPECAwEAAaOBtjCBszAdBgNVHQ4EFgQULBhn5cqRF/il5AweDnmxBSb+n+8wHwYD +VR0jBBgwFoAUUw5FGxMV061c6o7ovFu2FIrNHegwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAgQwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0 +dGVzdGF0aW9uL2NybC8zNTA1QTBBMkZEQ0M1MUU0MA0GCSqGSIb3DQEBCwUAA4ICAQCDsknVUCZN +IsdCxDechWvMXb+1lfYrKzc9MMstTHM5DNWR20Z7ZkC4dHRgYyhbdAIJCwGLkRhZ7+mIwKaz0Mkx +GB09mZd6bF0vzflDRbOdXb5Yo1rtL0caxUbkpSwNSvVVaIyCvNNfvwZSbqvRWyT8QXyijs5jsJyl +2UX7fFy01YXfW6TFMBwagDVS9R5iuvpGQqGRaoCAK5I0jY3h84fJkZM+eCb/WiwT6fpBSIpyMtgo +WadNKwsqiX0MWa40k/Gx0v0zK4esfYBO9pumZyL8TnnSZEKjwJOR9hSlB+i2c2I/Scc3gLKXirgF +6ZDM5wFYJJpEq3SVOYYUNxXYrw+RzHo/ostjjgrbJUACLR4kbRh/nZQFYCB9+N6E9QzA60VHySRY +V21UJQN6iqHcRUVz50tfmtF7raE2E6GfDmnAxBlvhPRQVgD7NLs8JNTKuZ4gyJ/r4Dx+V8MpJAyC +DarlCm+Mcl5c2pfjXa64tRSEvQ6/yJDAeBSylBb19ptIqDQLC09Q3PjNziRO4uLggRPZf/FBXBqT +trqp+rF2fQorTCWdspcoPzCOnswSZvYeixBNdUUbeXnDfRFgeH8uBsd0yOf3pAKLUPJfXfHwY0/9 +tzBe3iC8nFC+MkbZQHoklOovI67sfOmSbEKDtUffMf+oAHSxXpNxjY8CBVC8cFkEdA== +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert3.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert3.pem new file mode 100644 index 0000000..07018b2 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_StrongBox/cert3.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFXzCCA0egAwIBAgIINQWgov3MUeQwDQYJKoZIhvcNAQELBQAwGzEZMBcGA1UEBRMQZTM1ZDM4 +YzY4OTdkNDdlODAeFw0xODAzMjEwMzU1MDFaFw0yODAzMTgwMzU1MDFaMBsxGTAXBgNVBAUTEGUz +NWQzOGM2ODk3ZDQ3ZTgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCoQi070/6PH9BA +uJiBcTp8j5R2/Fj6kXFaSxsvUjJKRdi/FCOwUFBJfyhHiWJhga2iguIjAJuhZx5XlMj0pSY7buJi +sPqFknZhKdvfoi4C54j54D+XxCky1APVjD5uc203H+hrRlhh6x4/LTzSXWvb0YLjfOK07HvSddSR +CyKnPydI5bhyCb5QMtVKHzC4Axgx+BihwG1B4UQjOpZpXBHIFZ6EK/XBFeJX0tgrx0MCczkc1X0O +hNFKAYumCKcKyh4q2cGh7UwTRJIT4beIJVrOKDVwo3Fc50k3ICpOAc1zRGzRwupIKKGtW455KW1O +IyAtTJ4NqfIwrkQy5EJ9w/zDzjqFiDNdgTXaMtsz52jHUndfO3lzfvVdBjt2FWLRkWrFmd1tjQ6L +QQFPrSvUHo0XN5kIiOYdNlAyIwBZ51Zn/clU9he78UYAmXHdjfh04DXWgf+GGIFFdmg9tbiHwdqi +8yEcakKHL2TYNylmFJuKOkvLIS5iqEMu29OQyv+BJ1NNQtyn8o1D5f3K2a5qyoUcrL0Je2w28ByL +1JeFURO7wbzkXx9FWwH0stCs/dYq4JKJ8GxKN8aBOl/51atA1cdZAyI55B9ueAYwPevteORGDhEf +cKRaGiWJpSzIFLDmbL9uSpCAw5uqx5h1IRAX1OLi56EsVaFUedt8IqUaCkUcMQIDAQABo4GmMIGj +MB0GA1UdDgQWBBRTDkUbExXTrVzqjui8W7YUis0d6DAfBgNVHSMEGDAWgBRTDkUbExXTrVzqjui8 +W7YUis0d6DAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDBABgNVHR8EOTA3MDWgM6Ax +hi9odHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20vYXR0ZXN0YXRpb24vY3JsLzANBgkqhkiG +9w0BAQsFAAOCAgEACHPElcXZEY6jItn3SnVn6gdOCvuFIrhB4J4fhxa6OlDCbxsylHHAbpyoOSJD +0R5dIBTRvz49ElfCpI9yp4l5tUOjWj2K3tMfQjhKN277jApdxbZ6MNac2u+Z3dwS+34YiiCJLVKh +VzWanXk/+9rmVnBzm7qvgan90j2SmH1oYIA3GowJL+1OWSjj6cBH1VrU7guVd7q+aCEQPhTesfbm +UkdUbM/TjUMyhf5SZ7WB8A28MnyYQcFttmGDsE1HZx4fTz8iXxjaph33alasmFUWGtG7KyzvbYUw +wUOmpPd32eT8ocx3B0Z3g+Vo2Cv2LzRRNp15FOf/Ag5HWloyIv3xrsBA2fa4xzOm3/t1Aq0ZViXL +LerKKmU/EJKo7t5gE0wtNebicNhUJaX2C+vShjfleBBwhs33a8b7dBFX/JMwU5+09N83WHRPZjCr +YuK3Uu4qANfJ+f1k+weqh6MpRxAZ6TT0OQt1XQz7rDAlq7AopfXRXI74OCNJf8d+2CPNVW8i5IHN +FjvyEz8ms1ETdw5n3uRbYIgYuCV2Pud3MCwaMTv2E0dLAqg8uJo0JYlhH7wbqidMm2ODRL5FaqWb +b2pfW0vbpCyGfyw0RQGTJAE7xVdvzfdun9HATDm6TbJFcejBdJImfwpZmDOBtDPIbsg5+QVE3lLF +6Licq13i5tIg5yQ= +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert0.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert0.pem new file mode 100644 index 0000000..1afa630 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert0.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIGCDCCBHCgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQx +MzI2MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAb +BgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEApNVcnyN40MANMbbo2nMGNq2NNysDSjfLm0W3i6wPKf0ffCYkhWM4dCmQKKf50uAZTBeTit4c +NwXeZn3qellMlOsIN3Qc384rfN/8cikrRvUAgibz0Jy7STykjwa7x6tKwqITxbO8HqAhKo8/BQXU +xzrOdIg5ciy+UM7Vgh7a7ogen0KL2iGgrsalb1ti7Vlzb6vIJ4WzIC3TGD2sCkoPahghwqFDZZCo +/FzaLoNY0jAUX2mL+kf8aUaoxz7xA9FTvgara+1pLBR1s4c8xPS2HdZipcVXWfey0wujv1VAKs4+ +tXjKlHkYBHBBceEjxUtEmrapSQEdpHPv7Xh9Uanq4QIDAQABo4ICwTCCAr0wDgYDVR0PAQH/BAQD +AgeAMIICqQYKKwYBBAHWeQIBEQSCApkwggKVAgEDCgEBAgEECgEBBANhYmMEADCCAc2/hT0IAgYB +ZOYGEYe/hUWCAbsEggG3MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXlj +aGFpbgIBHTAZBBRjb20uYW5kcm9pZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNl +cwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZp +Y2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxv +Y2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5h +bmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9y +AgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnBy +b3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqjCB +rqEIMQYCAQICAQOiAwIBAaMEAgIIAKUFMQMCAQSmCDEGAgEDAgEFv4FIBQIDAQABv4N3AgUAv4U+ +AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIHKNsSdP +HxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgUCAwMUs7+FTwUC +AwMUszANBgkqhkiG9w0BAQsFAAOCAYEAJMIuzdNUdfrE6sIdmsnMn/scSG2odbphj8FkX9JGdF2S +OT599HuDY9qhvkru2Dza4sLKK3f4ViBhuR9lpfeprKvstxbtBO7jkLYfVn0ZRzHRHVEyiW5IVKh+ +qOXVJ9S1lMShOTlsaYJytLKIlcrRAZBEXZiNbzTuVh1CH6X9Ni1dog14snm+lcOeORdL9fht2CHa +u/caRnpWiZbjoAoJp0O89uBrRkXPpln51+3jPY6AFny30grNAvKguauDcPPhNV1yR+ylSsQi2gm3 +Rs4pgtlxFLMfZLgT0cbkl+9zk/QUqlpBP8ftUBsOI0ARr8xhFN3cvq9kXGLtJ9hEP9PRaflAFREk +DK3IBIbVcAFZBFoAQOdE9zy0+F5bQrznPGaZg4Dzhcx33qMDUTgHtWoy+k3ePGQMEtmoTTLgQywW +OIkXEoFqqGi9GKJXUT1KYi5NsigaYqu7FoN4Qsvs61pMUEfZSPP2AFwkA8uNFbmb9uxcxaGHCA8i +3i9VM6yOLIrP +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert1.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert1.pem new file mode 100644 index 0000000..afa0fa3 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert1.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIFDjCCAvagAwIBAgIJFIcgYhN4mUUVMA0GCSqGSIb3DQEBCwUAMCkxGTAXBgNVBAUTEDI5NjBj +YjlhZWJkNTA1ZjQxDDAKBgNVBAwMA1RFRTAeFw0xODAzMjEyMDU4NDhaFw0yODAzMTgyMDU4NDha +MCkxGTAXBgNVBAUTEDJkYzU4YjJkMWEyNDEzMjYxDDAKBgNVBAwMA1RFRTCCAaIwDQYJKoZIhvcN +AQEBBQADggGPADCCAYoCggGBALTJjMPjzdG6i/oPf+OuQpE0Pbamcs+t1F1jAeNJXcCSN+hL5kTz +PBto7WUX64+D74/SbwbHzSNsRptOwus2Fj4MXxdCcNjA+nYT19H++30ecY932WYtP6geamSZlZrN +rh4DRFGuSfOtQ2juw+alEEzuAfS5DigRP8RUjURyLXAQMIVGuIZoT1voB+H6OEv8ICallFkf7xI8 +ycKVYhOCNpNuf3tKELhaNqpZh7mAuaXMrxhsc7WiFsm6cBGY/UpZk/3koHUrHgCJxo3CJEYQUFEE ++du4yJ4KRSJabC/3GzE415gKPPvcFRm7/9yQWt4VY8X9F6EDkfsEYawDN+ZFN7sQSRuaFVA1+6U1 +UA0TlFjFCvAbMvlvb0RywIvaylpVQxCaFHtwMnOeQoMfic9baiinUHqeDdV85YtK5ZAXBkv0Cty/ +43Xlq4zJ4O/TZa/dqqrtYkdmzGjUu5ubtiT2AknFJjS/TwtDI7obVOgQYnStWQjZWBafVrm6l62B +eFuEqQIDAQABo4G4MIG1MB0GA1UdDgQWBBRopWEF5KUGUKoHHaveKWTVGPzLDDAfBgNVHSMEGDAW +gBT8ZOhxpzVk8i/b9lrISMYu2Rl7JDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDBS +BgNVHR8ESzBJMEegRaBDhkFodHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20vYXR0ZXN0YXRp +b24vY3JsLzE0ODcyMDYyMTM3ODk5NDUxNTANBgkqhkiG9w0BAQsFAAOCAgEAfdEIlzih1i06VvWE ++ZutfqoV5mvcFQb1ey8lzrvPhEPjm4SeKi6wLR1f1O12uOhYbAQzzOTmEuYsFqje3N6WGZVnXSXI +vxlUT0D3kVqgM48+dt+a27xxTMy3QEJNYzFdgOrrRIAzen5sXYqEhynP4pD40VEz1p0VFgQtGgtV +Bk/+jFg/aAZGmL2YRGeF4mIygSVsW0QCvoodNbPZ6qyZUIxDK1fTFXPy31PXvrP2e8IxMDMkD3aR +h3sKdzc498k/NrBbQG8lWwL/Fldx/BSORNNpJzXpHUYgPThaicZhsOesMBQoa+Mhl6czaCDQ8rQo +/lGs+wN7iyQ9o07/6uN7x4j1Olhh0bEH/edZenZEx3Jnmmfea42vck83L6SvHgprkIlGiq1vKoTk +703RsIS23RivRq9g3bI2DnVEDW3ZlbY/i3LxTj1d2DRuiOY6LTtqiIL+OPXAeeB4pIQyyTqtl8kK +xrOTSxZb+BCIKosrxFTaUlIjFeCexvQso9DMSOkt2LCx/ckJYxLZ+UW/azbzhikbRwSkTTD6GQ1X +WpLGm/DH+PZT6BRQTJt3H9b/IfCNI0j/Z+p7s4tug3x5yp2lFT/Yfz7lC7+nuX6EsAV3EwPSotY5 +eRNOwzMcuoQfW+Ngz5juQd4pixo/f6DLJTcZwO+uHSVSWRKtaODAtMw8sOQ= +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert2.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert2.pem new file mode 100644 index 0000000..0819e4c --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert2.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIKA4gmZ2BliZaFfDANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQFExBmOTIw +MDllODUzYjZiMDQ1MB4XDTE4MDMyMTIwNTMyMVoXDTI4MDMxODIwNTMyMVowKTEZMBcGA1UEBRMQ +Mjk2MGNiOWFlYmQ1MDVmNDEMMAoGA1UEDAwDVEVFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEArsj0RmYrjTICBSQVVcaXdb0lM/mrDf4hbQGxgzmBshG5hoIGmR3rWQ0iL6NNXELhsTji +f/15kPsSkZKtiCz9S1/1ZJeYP2BhenkNE7CesaYqDU52+IU2tXv91ee+AfELlp4jmyb4BbB7S8mg +4kB1VRyYhbelUsQZ/AB6SnPzqrhnIW5NR8fGV+1hkCQ/KlSc6JRUkcVAWtB/jvF/k8ji573tJG8q +56Qu5q4HX+zOeo8X6XcCqalO9uea8MTSs68gAJtWGCmSfqTpkoayTCz7/P02Ya5TbxncwMieOmVw +4iWI3VFRqS9x3GZx89ySECU+O1UkF/tOzYjCNWMCdbJRN5BgXIottTg/EdMQjheaRrWE9lD9DLzc +BH0rWDB3tzoNmdBAK3S5C6M/XtH+qu/whheEFkWmSkfK4N/rURfvILnahaaO1luWwBI4T/7jPykc +UYNyBcIKITT1U1LkTjsOEqMZXmwL+yOUsb6QoaFQdyj8QaFU39S6nIWPhgedzocLrXGJ1wSkt5si +d1Kd+PL5i2/foWdEkaXFd5SwLX72JmJbJ8kxbxJpUtfzXjH3awd6R8DyQCDHUCblOpaZOJOjbHmz +hQmdyqXpYdbjfVlb0y/Xlg2j4Z20RzXbv8wylAnvxuLeoqlGljRxaEOFZDBzWSJrrsLJOLqc23eW +lB7KAnMCAwEAAaOBtjCBszAdBgNVHQ4EFgQU/GTocac1ZPIv2/ZayEjGLtkZeyQwHwYDVR0jBBgw +FoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQw +UAYDVR0fBEkwRzBFoEOgQYY/aHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0 +aW9uL2NybC9FOEZBMTk2MzE0RDJGQTE4MA0GCSqGSIb3DQEBCwUAA4ICAQCqHtl3I1gfODJVBMZy +IsHouloSr2KlpLhyvC0gPdgvN5jkNtJPYzcfjtHyNDEBv1QRu1ezdb95kk4sU7o9M4YuwA9gOb2Q +guJ/ihaTSOW0hszqHjIX8HuYls/7D/mtdO1OQ5JD8a38hR9uhlNgn+mSiBvwr9gySVGWWSAu6O9e +ZYOaCfTgc2tx0iqNltXNbL0OJZ8wYoBOo2fT72/EjOsQDoDPILrEfl/B2ek1cEFcB7iSEWrf3tyq +hq1zpaRs5k2fWYqRuXGDg+B0QUh6cpC9bwbK5sBcOSOqtEZHBjEhIGZ1Sn6dRTCSk15yrMnl0U3E +BXFxWrecqr6o4VsMCvXFazpmDVutU1t9TUV8NaIFL2Yh7D5BnctIe8Q/zRTn/XBLy1IOu2b971el +Ms1BCd6T4KD5H9pT1RG8q8aN+tyu2lj3i0dx6ktwQKGPHeJJYW+1NZqFzBoLijnMNhV4lQhBHmdm +uy8YPDGZIwsiojNsJ+3lXM1mgypFrDj5d7bFerN4eB/8XsiCIf+j4dIeYMIrAIee4P/KH9N2eXaA +4O/EyojDlrlIKv0NHd50SZnAkxYJ8livW2wTyJd0SFu3go5kDpjFxFuI+cBPUZSMtFhjwCvZHv1n +mNrJGk4diUD3Taxpdax8Y49Ik7iLI2Gy7vKtXwIzR9iyBKCOtvbTlhJWlQ== +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert3.pem b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert3.pem new file mode 100644 index 0000000..faab920 --- /dev/null +++ b/server/examples/pem/algorithm_RSA_SecurityLevel_TEE/cert3.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAw +OWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBm +OTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHs +K7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfd +nJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL +/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04 +T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+R +hhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1 +Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgp +Zrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6 +tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8 +Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCB +ozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0Rs +R/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOg +MYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZI +hvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB +Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1t +wb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCU +JouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhu +Kug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsY +gBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ +79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYk +A3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZu +CuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Y +d6uuJOJENRaNVTzk +-----END CERTIFICATE-----
\ No newline at end of file diff --git a/server/gradle/wrapper/gradle-wrapper.jar b/server/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..13372ae --- /dev/null +++ b/server/gradle/wrapper/gradle-wrapper.jar diff --git a/server/gradle/wrapper/gradle-wrapper.properties b/server/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3e98a23 --- /dev/null +++ b/server/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,23 @@ +# +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +#Thu Aug 11 15:22:08 AEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-bin.zip diff --git a/server/gradlew b/server/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/server/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/server/gradlew.bat b/server/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/server/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/server/settings.gradle b/server/settings.gradle new file mode 100644 index 0000000..a97dce9 --- /dev/null +++ b/server/settings.gradle @@ -0,0 +1,16 @@ +/* Copyright 2016, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +rootProject.name = 'server' diff --git a/server/src/main/java/com/android/example/KeyAttestationExample.java b/server/src/main/java/com/android/example/KeyAttestationExample.java new file mode 100644 index 0000000..a503dc7 --- /dev/null +++ b/server/src/main/java/com/android/example/KeyAttestationExample.java @@ -0,0 +1,278 @@ +/* Copyright 2016, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.example; + +import static com.google.android.attestation.Constants.GOOGLE_ROOT_CERTIFICATE; +import static com.google.android.attestation.ParsedAttestationRecord.createParsedAttestationRecord; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.android.attestation.AttestationApplicationId; +import com.google.android.attestation.AttestationApplicationId.AttestationPackageInfo; +import com.google.android.attestation.CertificateRevocationStatus; +import com.google.android.attestation.AuthorizationList; +import com.google.android.attestation.ParsedAttestationRecord; +import com.google.android.attestation.RootOfTrust; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.bouncycastle.util.encoders.Base64; + +/** + * This is an illustration of how you can use the Bouncy Castle ASN.1 parser to extract information + * from an Android attestation data structure. On a secure server that you trust, create similar + * logic to verify that a key pair has been generated in an Android device. The app on the device + * must retrieve the key's certificate chain using KeyStore.getCertificateChain(), then send the + * contents to the trusted server. + * + * <p>In this example, the certificate chain includes hard-coded excerpts of each certificate. + * + * <p>This example demonstrates the following tasks: + * + * <p>1. Loading the certificates from PEM-encoded strings. + * + * <p>2. Verifying the certificate chain, up to the root. Note that this example does NOT require + * the root certificate to appear within Google's list of root certificates. However, if you're + * verifying the properties of hardware-backed keys on a device that ships with hardware-level key + * attestation, Android 7.0 (API level 24) or higher, and Google Play services, your production code + * should enforce this requirement. + * + * <p>3. Checking if any certificate in the chain has been revoked or suspended. + * + * <p>4. Extracting the attestation extension data from the attestation certificate. + * + * <p>5. Verifying (and printing) several important data elements from the attestation extension. + * + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class KeyAttestationExample { + + private KeyAttestationExample() {} + + public static void main(String[] args) + throws CertificateException, IOException, NoSuchProviderException, NoSuchAlgorithmException, + InvalidKeyException, SignatureException { + X509Certificate[] certs; + if (args.length == 1) { + String certFilesDir = args[0]; + certs = loadCertificates(certFilesDir); + } else { + throw new IOException("Expected path to a directory containing certificates as an argument."); + } + + verifyCertificateChain(certs); + + ParsedAttestationRecord parsedAttestationRecord = createParsedAttestationRecord(certs[0]); + + System.out.println("Attestation version: " + parsedAttestationRecord.attestationVersion); + System.out.println( + "Attestation Security Level: " + parsedAttestationRecord.attestationSecurityLevel.name()); + System.out.println("Keymaster Version: " + parsedAttestationRecord.keymasterVersion); + System.out.println( + "Keymaster Security Level: " + parsedAttestationRecord.keymasterSecurityLevel.name()); + + System.out.println( + "Attestation Challenge: " + + new String(parsedAttestationRecord.attestationChallenge, UTF_8)); + System.out.println("Unique ID: " + Arrays.toString(parsedAttestationRecord.uniqueId)); + + System.out.println("Software Enforced Authorization List:"); + AuthorizationList softwareEnforced = parsedAttestationRecord.softwareEnforced; + printAuthorizationList(softwareEnforced, "\t"); + + System.out.println("TEE Enforced Authorization List:"); + AuthorizationList teeEnforced = parsedAttestationRecord.teeEnforced; + printAuthorizationList(teeEnforced, "\t"); + } + + private static void printAuthorizationList(AuthorizationList authorizationList, String indent) { + // Detailed explanation of the keys and their values can be found here: + // https://source.android.com/security/keystore/tags + printOptional(authorizationList.purpose, indent + "Purpose(s)"); + printOptional(authorizationList.algorithm, indent + "Algorithm"); + printOptional(authorizationList.keySize, indent + "Key Size"); + printOptional(authorizationList.digest, indent + "Digest"); + printOptional(authorizationList.padding, indent + "Padding"); + printOptional(authorizationList.ecCurve, indent + "EC Curve"); + printOptional(authorizationList.rsaPublicExponent, indent + "RSA Public Exponent"); + System.out.println(indent + "Rollback Resistance: " + authorizationList.rollbackResistance); + printOptional(authorizationList.activeDateTime, indent + "Active DateTime"); + printOptional( + authorizationList.originationExpireDateTime, indent + "Origination Expire DateTime"); + printOptional(authorizationList.usageExpireDateTime, indent + "Usage Expire DateTime"); + System.out.println(indent + "No Auth Required: " + authorizationList.noAuthRequired); + printOptional(authorizationList.userAuthType, indent + "User Auth Type"); + printOptional(authorizationList.authTimeout, indent + "Auth Timeout"); + System.out.println(indent + "Allow While On Body: " + authorizationList.allowWhileOnBody); + System.out.println( + indent + + "Trusted User Presence Required: " + + authorizationList.trustedUserPresenceRequired); + System.out.println( + indent + "Trusted Confirmation Required: " + authorizationList.trustedConfirmationRequired); + System.out.println( + indent + "Unlocked Device Required: " + authorizationList.unlockedDeviceRequired); + System.out.println(indent + "All Applications: " + authorizationList.allApplications); + printOptional(authorizationList.applicationId, indent + "Application ID"); + printOptional(authorizationList.creationDateTime, indent + "Creation DateTime"); + printOptional(authorizationList.origin, indent + "Origin"); + System.out.println(indent + "Rollback Resistant: " + authorizationList.rollbackResistant); + if (authorizationList.rootOfTrust.isPresent()) { + System.out.println(indent + "Root Of Trust:"); + printRootOfTrust(authorizationList.rootOfTrust, indent + "\t"); + } + printOptional(authorizationList.osVersion, indent + "OS Version"); + printOptional(authorizationList.osPatchLevel, indent + "OS Patch Level"); + if (authorizationList.attestationApplicationId.isPresent()) { + System.out.println(indent + "Attestation Application ID:"); + printAttestationApplicationId(authorizationList.attestationApplicationId, indent + "\t"); + } + printOptional( + authorizationList.attestationApplicationIdBytes, + indent + "Attestation Application ID Bytes"); + printOptional(authorizationList.attestationIdBrand, indent + "Attestation ID Brand"); + printOptional(authorizationList.attestationIdDevice, indent + "Attestation ID Device"); + printOptional(authorizationList.attestationIdProduct, indent + "Attestation ID Product"); + printOptional(authorizationList.attestationIdSerial, indent + "Attestation ID Serial"); + printOptional(authorizationList.attestationIdImei, indent + "Attestation ID IMEI"); + printOptional(authorizationList.attestationIdMeid, indent + "Attestation ID MEID"); + printOptional( + authorizationList.attestationIdManufacturer, indent + "Attestation ID Manufacturer"); + printOptional(authorizationList.attestationIdModel, indent + "Attestation ID Model"); + printOptional(authorizationList.vendorPatchLevel, indent + "Vendor Patch Level"); + printOptional(authorizationList.bootPatchLevel, indent + "Boot Patch Level"); + } + + private static void printRootOfTrust(Optional<RootOfTrust> rootOfTrust, String indent) { + if (rootOfTrust.isPresent()) { + System.out.println( + indent + + "Verified Boot Key: " + + Base64.toBase64String(rootOfTrust.get().verifiedBootKey)); + System.out.println(indent + "Device Locked: " + rootOfTrust.get().deviceLocked); + System.out.println( + indent + "Verified Boot State: " + rootOfTrust.get().verifiedBootState.name()); + System.out.println( + indent + + "Verified Boot Hash: " + + Base64.toBase64String(rootOfTrust.get().verifiedBootHash)); + } + } + + private static void printAttestationApplicationId( + Optional<AttestationApplicationId> attestationApplicationId, String indent) { + if (attestationApplicationId.isPresent()) { + System.out.println(indent + "Package Infos (<package name>, <version>): "); + for (AttestationPackageInfo info : attestationApplicationId.get().packageInfos) { + System.out.println(indent + "\t" + info.packageName + ", " + info.version); + } + System.out.println(indent + "Signature Digests:"); + for (byte[] digest : attestationApplicationId.get().signatureDigests) { + System.out.println(indent + "\t" + Base64.toBase64String(digest)); + } + } + } + + private static <T> void printOptional(Optional<T> optional, String caption) { + if (optional.isPresent()) { + if (optional.get() instanceof byte[]) { + System.out.println(caption + ": " + Base64.toBase64String((byte[]) optional.get())); + } else { + System.out.println(caption + ": " + optional.get()); + } + } + } + + private static void verifyCertificateChain(X509Certificate[] certs) + throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException, IOException { + X509Certificate parent = certs[certs.length - 1]; + for (int i = certs.length - 1; i >= 0; i--) { + X509Certificate cert = certs[i]; + // Verify that the certificate has not expired. + cert.checkValidity(); + cert.verify(parent.getPublicKey()); + parent = cert; + try { + CertificateRevocationStatus certStatus = CertificateRevocationStatus + .fetchStatus(cert.getSerialNumber()); + if (certStatus != null) { + throw new CertificateException( + "Certificate revocation status is " + certStatus.status.name()); + } + } catch (IOException e) { + throw new IOException("Unable to fetch certificate status. Check connectivity."); + } + } + + // If the attestation is trustworthy and the device ships with hardware- + // level key attestation, Android 7.0 (API level 24) or higher, and + // Google Play services, the root certificate should be signed with the + // Google attestation root key. + X509Certificate secureRoot = + (X509Certificate) + CertificateFactory.getInstance("X.509") + .generateCertificate( + new ByteArrayInputStream(GOOGLE_ROOT_CERTIFICATE.getBytes(UTF_8))); + if (Arrays.equals( + secureRoot.getPublicKey().getEncoded(), + certs[certs.length - 1].getPublicKey().getEncoded())) { + System.out.println( + "The root certificate is correct, so this attestation is trustworthy, as long as none of" + + " the certificates in the chain have been revoked. A production-level system" + + " should check the certificate revocation lists using the distribution points that" + + " are listed in the intermediate and root certificates."); + } else { + System.out.println( + "The root certificate is NOT correct. The attestation was probably generated by" + + " software, not in secure hardware. This means that, although the attestation" + + " contents are probably valid and correct, there is no proof that they are in fact" + + " correct. If you're using a production-level system, you should now treat the" + + " properties of this attestation certificate as advisory only, and you shouldn't" + + " rely on this attestation certificate to provide security guarantees."); + } + } + + private static X509Certificate[] loadCertificates(String certFilesDir) + throws CertificateException, IOException { + // Load the attestation certificates from the directory in alphabetic order. + List<Path> records; + try (Stream<Path> pathStream = Files.walk(Paths.get(certFilesDir))) { + records = pathStream.filter(Files::isRegularFile).sorted().collect(Collectors.toList()); + } + X509Certificate[] certs = new X509Certificate[records.size()]; + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + for (int i = 0; i < records.size(); ++i) { + byte[] encodedCert = Files.readAllBytes(records.get(i)); + ByteArrayInputStream inputStream = new ByteArrayInputStream(encodedCert); + certs[i] = (X509Certificate) factory.generateCertificate(inputStream); + } + return certs; + } +} diff --git a/server/src/main/java/com/google/android/attestation/ASN1Parsing.java b/server/src/main/java/com/google/android/attestation/ASN1Parsing.java new file mode 100644 index 0000000..1735822 --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/ASN1Parsing.java @@ -0,0 +1,45 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1Integer; + +/** Utils to get java representation of ASN1 types. */ +class ASN1Parsing { + + static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) { + if (asn1Value instanceof ASN1Boolean) { + return ((ASN1Boolean) asn1Value).isTrue(); + } else { + throw new RuntimeException( + "Boolean value expected; found " + asn1Value.getClass().getName() + " instead."); + } + } + + static int getIntegerFromAsn1(ASN1Encodable asn1Value) { + if (asn1Value instanceof ASN1Integer) { + return ((ASN1Integer) asn1Value).getValue().intValueExact(); + } else if (asn1Value instanceof ASN1Enumerated) { + return ((ASN1Enumerated) asn1Value).getValue().intValueExact(); + } else { + throw new IllegalArgumentException( + "Integer value expected; found " + asn1Value.getClass().getName() + " instead."); + } + } +} diff --git a/server/src/main/java/com/google/android/attestation/AttestationApplicationId.java b/server/src/main/java/com/google/android/attestation/AttestationApplicationId.java new file mode 100644 index 0000000..ead446d --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/AttestationApplicationId.java @@ -0,0 +1,185 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.android.attestation.Constants.ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX; +import static com.google.android.attestation.Constants.ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX; +import static com.google.android.attestation.Constants.ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX; +import static com.google.android.attestation.Constants.ATTESTATION_PACKAGE_INFO_VERSION_INDEX; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.DEROctetString; + +/** + * This data structure reflects the Android platform's belief as to which apps are allowed to use + * the secret key material under attestation. The ID can comprise multiple packages if and only if + * multiple packages share the same UID. + */ +public class AttestationApplicationId implements Comparable<AttestationApplicationId> { + public final List<AttestationPackageInfo> packageInfos; + public final List<byte[]> signatureDigests; + + private AttestationApplicationId(DEROctetString attestationApplicationId) throws IOException { + ASN1Sequence attestationApplicationIdSequence = + (ASN1Sequence) ASN1Sequence.fromByteArray(attestationApplicationId.getOctets()); + ASN1Set attestationPackageInfos = + (ASN1Set) + attestationApplicationIdSequence.getObjectAt( + ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX); + this.packageInfos = new ArrayList<>(); + for (ASN1Encodable packageInfo : attestationPackageInfos) { + this.packageInfos.add(new AttestationPackageInfo((ASN1Sequence) packageInfo)); + } + + ASN1Set digests = + (ASN1Set) + attestationApplicationIdSequence.getObjectAt( + ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX); + this.signatureDigests = new ArrayList<>(); + for (ASN1Encodable digest : digests) { + this.signatureDigests.add(((ASN1OctetString) digest).getOctets()); + } + } + + AttestationApplicationId( + List<AttestationPackageInfo> packageInfos, List<byte[]> signatureDigests) { + this.packageInfos = packageInfos; + this.signatureDigests = signatureDigests; + } + + static AttestationApplicationId createAttestationApplicationId( + DEROctetString attestationApplicationId) { + if (attestationApplicationId == null) { + return null; + } + try { + return new AttestationApplicationId(attestationApplicationId); + } catch (IOException e) { + return null; + } + } + + @Override + public int compareTo(AttestationApplicationId other) { + int res = Integer.compare(packageInfos.size(), other.packageInfos.size()); + if (res != 0) { + return res; + } + for (int i = 0; i < packageInfos.size(); ++i) { + res = packageInfos.get(i).compareTo(other.packageInfos.get(i)); + if (res != 0) { + return res; + } + } + res = Integer.compare(signatureDigests.size(), other.signatureDigests.size()); + if (res != 0) { + return res; + } + ByteArrayComparator cmp = new ByteArrayComparator(); + for (int i = 0; i < signatureDigests.size(); ++i) { + res = cmp.compare(signatureDigests.get(i), other.signatureDigests.get(i)); + if (res != 0) { + return res; + } + } + return res; + } + + @Override + public boolean equals(Object o) { + return (o instanceof AttestationApplicationId) + && (compareTo((AttestationApplicationId) o) == 0); + } + + @Override + public int hashCode() { + return Objects.hash(packageInfos, Arrays.deepHashCode(signatureDigests.toArray())); + } + + /** Provides package's name and version number. */ + public static class AttestationPackageInfo implements Comparable<AttestationPackageInfo> { + public final String packageName; + public final long version; + + private AttestationPackageInfo(ASN1Sequence packageInfo) { + this.packageName = + new String( + ((ASN1OctetString) + packageInfo.getObjectAt(ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX)) + .getOctets(), + UTF_8); + this.version = + ((ASN1Integer) packageInfo.getObjectAt(ATTESTATION_PACKAGE_INFO_VERSION_INDEX)) + .getValue() + .longValue(); + } + + AttestationPackageInfo(String packageName, long version) { + this.packageName = packageName; + this.version = version; + } + + @Override + public int compareTo(AttestationPackageInfo other) { + int res = packageName.compareTo(other.packageName); + if (res != 0) { + return res; + } + res = Long.compare(version, other.version); + if (res != 0) { + return res; + } + return res; + } + + @Override + public boolean equals(Object o) { + return (o instanceof AttestationPackageInfo) && (compareTo((AttestationPackageInfo) o) == 0); + } + + @Override + public int hashCode() { + return Objects.hash(packageName, version); + } + } + + private static class ByteArrayComparator implements java.util.Comparator<byte[]> { + @Override + public int compare(byte[] a, byte[] b) { + int res = Integer.compare(a.length, b.length); + if (res != 0) { + return res; + } + for (int i = 0; i < a.length; ++i) { + res = Byte.compare(a[i], b[i]); + if (res != 0) { + return res; + } + } + return res; + } + } +} diff --git a/server/src/main/java/com/google/android/attestation/AuthorizationList.java b/server/src/main/java/com/google/android/attestation/AuthorizationList.java new file mode 100644 index 0000000..d86922d --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/AuthorizationList.java @@ -0,0 +1,747 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.android.attestation.AuthorizationList.UserAuthType.FINGERPRINT; +import static com.google.android.attestation.AuthorizationList.UserAuthType.PASSWORD; +import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_ANY; +import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_NONE; +import static com.google.android.attestation.Constants.KM_TAG_ACTIVE_DATE_TIME; +import static com.google.android.attestation.Constants.KM_TAG_ALGORITHM; +import static com.google.android.attestation.Constants.KM_TAG_ALLOW_WHILE_ON_BODY; +import static com.google.android.attestation.Constants.KM_TAG_ALL_APPLICATIONS; +import static com.google.android.attestation.Constants.KM_TAG_APPLICATION_ID; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_APPLICATION_ID; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_BRAND; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_DEVICE; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_IMEI; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_MANUFACTURER; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_MEID; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_MODEL; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_PRODUCT; +import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_SERIAL; +import static com.google.android.attestation.Constants.KM_TAG_AUTH_TIMEOUT; +import static com.google.android.attestation.Constants.KM_TAG_BOOT_PATCH_LEVEL; +import static com.google.android.attestation.Constants.KM_TAG_CREATION_DATE_TIME; +import static com.google.android.attestation.Constants.KM_TAG_DEVICE_UNIQUE_ATTESTATION; +import static com.google.android.attestation.Constants.KM_TAG_DIGEST; +import static com.google.android.attestation.Constants.KM_TAG_EC_CURVE; +import static com.google.android.attestation.Constants.KM_TAG_KEY_SIZE; +import static com.google.android.attestation.Constants.KM_TAG_NO_AUTH_REQUIRED; +import static com.google.android.attestation.Constants.KM_TAG_ORIGIN; +import static com.google.android.attestation.Constants.KM_TAG_ORIGINATION_EXPIRE_DATE_TIME; +import static com.google.android.attestation.Constants.KM_TAG_OS_PATCH_LEVEL; +import static com.google.android.attestation.Constants.KM_TAG_OS_VERSION; +import static com.google.android.attestation.Constants.KM_TAG_PADDING; +import static com.google.android.attestation.Constants.KM_TAG_PURPOSE; +import static com.google.android.attestation.Constants.KM_TAG_ROLLBACK_RESISTANCE; +import static com.google.android.attestation.Constants.KM_TAG_ROLLBACK_RESISTANT; +import static com.google.android.attestation.Constants.KM_TAG_ROOT_OF_TRUST; +import static com.google.android.attestation.Constants.KM_TAG_RSA_PUBLIC_EXPONENT; +import static com.google.android.attestation.Constants.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED; +import static com.google.android.attestation.Constants.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED; +import static com.google.android.attestation.Constants.KM_TAG_UNLOCKED_DEVICE_REQUIRED; +import static com.google.android.attestation.Constants.KM_TAG_USAGE_EXPIRE_DATE_TIME; +import static com.google.android.attestation.Constants.KM_TAG_USER_AUTH_TYPE; +import static com.google.android.attestation.Constants.KM_TAG_VENDOR_PATCH_LEVEL; +import static com.google.android.attestation.Constants.UINT32_MAX; + +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.DERTaggedObject; + +/** + * This data structure contains the key pair's properties themselves, as defined in the Keymaster + * hardware abstraction layer (HAL). You compare these values to the device's current state or to a + * set of expected values to verify that a key pair is still valid for use in your app. + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class AuthorizationList { + /** Specifies the types of user authenticators that may be used to authorize this key. */ + public enum UserAuthType { + USER_AUTH_TYPE_NONE, + PASSWORD, + FINGERPRINT, + USER_AUTH_TYPE_ANY + } + + public final Optional<Set<Integer>> purpose; + public final Optional<Integer> algorithm; + public final Optional<Integer> keySize; + public final Optional<Set<Integer>> digest; + public final Optional<Set<Integer>> padding; + public final Optional<Integer> ecCurve; + public final Optional<Long> rsaPublicExponent; + public final boolean rollbackResistance; + public final Optional<Instant> activeDateTime; + public final Optional<Instant> originationExpireDateTime; + public final Optional<Instant> usageExpireDateTime; + public final boolean noAuthRequired; + public final Optional<Set<UserAuthType>> userAuthType; + public final Optional<Duration> authTimeout; + public final boolean allowWhileOnBody; + public final boolean trustedUserPresenceRequired; + public final boolean trustedConfirmationRequired; + public final boolean unlockedDeviceRequired; + public final boolean allApplications; + public final Optional<byte[]> applicationId; + public final Optional<Instant> creationDateTime; + public final Optional<Integer> origin; + public final boolean rollbackResistant; + public final Optional<RootOfTrust> rootOfTrust; + public final Optional<Integer> osVersion; + public final Optional<Integer> osPatchLevel; + public final Optional<AttestationApplicationId> attestationApplicationId; + public final Optional<byte[]> attestationApplicationIdBytes; + public final Optional<byte[]> attestationIdBrand; + public final Optional<byte[]> attestationIdDevice; + public final Optional<byte[]> attestationIdProduct; + public final Optional<byte[]> attestationIdSerial; + public final Optional<byte[]> attestationIdImei; + public final Optional<byte[]> attestationIdMeid; + public final Optional<byte[]> attestationIdManufacturer; + public final Optional<byte[]> attestationIdModel; + public final Optional<Integer> vendorPatchLevel; + public final Optional<Integer> bootPatchLevel; + public final boolean individualAttestation; + + private AuthorizationList(ASN1Encodable[] authorizationList, int attestationVersion) { + Map<Integer, ASN1Primitive> authorizationMap = getAuthorizationMap(authorizationList); + this.purpose = findOptionalIntegerSetAuthorizationListEntry(authorizationMap, KM_TAG_PURPOSE); + this.algorithm = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_ALGORITHM); + this.keySize = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_KEY_SIZE); + this.digest = findOptionalIntegerSetAuthorizationListEntry(authorizationMap, KM_TAG_DIGEST); + this.padding = findOptionalIntegerSetAuthorizationListEntry(authorizationMap, KM_TAG_PADDING); + this.ecCurve = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_EC_CURVE); + this.rsaPublicExponent = + findOptionalLongAuthorizationListEntry(authorizationMap, KM_TAG_RSA_PUBLIC_EXPONENT); + this.rollbackResistance = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ROLLBACK_RESISTANCE); + this.activeDateTime = + findOptionalInstantMillisAuthorizationListEntry(authorizationMap, KM_TAG_ACTIVE_DATE_TIME); + this.originationExpireDateTime = + findOptionalInstantMillisAuthorizationListEntry( + authorizationMap, KM_TAG_ORIGINATION_EXPIRE_DATE_TIME); + this.usageExpireDateTime = + findOptionalInstantMillisAuthorizationListEntry( + authorizationMap, KM_TAG_USAGE_EXPIRE_DATE_TIME); + this.noAuthRequired = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_NO_AUTH_REQUIRED); + this.userAuthType = findOptionalUserAuthType(authorizationMap, KM_TAG_USER_AUTH_TYPE); + this.authTimeout = + findOptionalDurationSecondsAuthorizationListEntry(authorizationMap, KM_TAG_AUTH_TIMEOUT); + this.allowWhileOnBody = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ALLOW_WHILE_ON_BODY); + this.trustedUserPresenceRequired = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); + this.trustedConfirmationRequired = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); + this.unlockedDeviceRequired = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_UNLOCKED_DEVICE_REQUIRED); + this.allApplications = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ALL_APPLICATIONS); + this.applicationId = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_APPLICATION_ID); + this.creationDateTime = + findOptionalInstantMillisAuthorizationListEntry( + authorizationMap, KM_TAG_CREATION_DATE_TIME); + this.origin = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_ORIGIN); + this.rollbackResistant = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ROLLBACK_RESISTANT); + this.rootOfTrust = + Optional.ofNullable( + RootOfTrust.createRootOfTrust( + (ASN1Sequence) findAuthorizationListEntry(authorizationMap, KM_TAG_ROOT_OF_TRUST), + attestationVersion)); + this.osVersion = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_OS_VERSION); + this.osPatchLevel = + findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_OS_PATCH_LEVEL); + this.attestationApplicationId = + Optional.ofNullable( + AttestationApplicationId.createAttestationApplicationId( + (DEROctetString) + findAuthorizationListEntry( + authorizationMap, KM_TAG_ATTESTATION_APPLICATION_ID))); + this.attestationApplicationIdBytes = + findOptionalByteArrayAuthorizationListEntry( + authorizationMap, KM_TAG_ATTESTATION_APPLICATION_ID); + this.attestationIdBrand = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_BRAND); + this.attestationIdDevice = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_DEVICE); + this.attestationIdProduct = + findOptionalByteArrayAuthorizationListEntry( + authorizationMap, KM_TAG_ATTESTATION_ID_PRODUCT); + this.attestationIdSerial = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_SERIAL); + this.attestationIdImei = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_IMEI); + this.attestationIdMeid = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_MEID); + this.attestationIdManufacturer = + findOptionalByteArrayAuthorizationListEntry( + authorizationMap, KM_TAG_ATTESTATION_ID_MANUFACTURER); + this.attestationIdModel = + findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_MODEL); + this.vendorPatchLevel = + findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_VENDOR_PATCH_LEVEL); + this.bootPatchLevel = + findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_BOOT_PATCH_LEVEL); + this.individualAttestation = + findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_DEVICE_UNIQUE_ATTESTATION); + } + + private AuthorizationList(Builder builder) { + this.purpose = Optional.ofNullable(builder.purpose); + this.algorithm = Optional.ofNullable(builder.algorithm); + this.keySize = Optional.ofNullable(builder.keySize); + this.digest = Optional.ofNullable(builder.digest); + this.padding = Optional.ofNullable(builder.padding); + this.ecCurve = Optional.ofNullable(builder.ecCurve); + this.rsaPublicExponent = Optional.ofNullable(builder.rsaPublicExponent); + this.rollbackResistance = builder.rollbackResistance; + this.activeDateTime = Optional.ofNullable(builder.activeDateTime); + this.originationExpireDateTime = Optional.ofNullable(builder.originationExpireDateTime); + this.usageExpireDateTime = Optional.ofNullable(builder.usageExpireDateTime); + this.noAuthRequired = builder.noAuthRequired; + this.userAuthType = Optional.ofNullable(builder.userAuthType); + this.authTimeout = Optional.ofNullable(builder.authTimeout); + this.allowWhileOnBody = builder.allowWhileOnBody; + this.trustedUserPresenceRequired = builder.trustedUserPresenceRequired; + this.trustedConfirmationRequired = builder.trustedConfirmationRequired; + this.unlockedDeviceRequired = builder.unlockedDeviceRequired; + this.allApplications = builder.allApplications; + this.applicationId = Optional.ofNullable(builder.applicationId); + this.creationDateTime = Optional.ofNullable(builder.creationDateTime); + this.origin = Optional.ofNullable(builder.origin); + this.rollbackResistant = builder.rollbackResistant; + this.rootOfTrust = Optional.ofNullable(builder.rootOfTrust); + this.osVersion = Optional.ofNullable(builder.osVersion); + this.osPatchLevel = Optional.ofNullable(builder.osPatchLevel); + this.attestationApplicationId = Optional.ofNullable(builder.attestationApplicationId); + this.attestationApplicationIdBytes = Optional.ofNullable(builder.attestationApplicationIdBytes); + this.attestationIdBrand = Optional.ofNullable(builder.attestationIdBrand); + this.attestationIdDevice = Optional.ofNullable(builder.attestationIdDevice); + this.attestationIdProduct = Optional.ofNullable(builder.attestationIdProduct); + this.attestationIdSerial = Optional.ofNullable(builder.attestationIdSerial); + this.attestationIdImei = Optional.ofNullable(builder.attestationIdImei); + this.attestationIdMeid = Optional.ofNullable(builder.attestationIdMeid); + this.attestationIdManufacturer = Optional.ofNullable(builder.attestationIdManufacturer); + this.attestationIdModel = Optional.ofNullable(builder.attestationIdModel); + this.vendorPatchLevel = Optional.ofNullable(builder.vendorPatchLevel); + this.bootPatchLevel = Optional.ofNullable(builder.bootPatchLevel); + this.individualAttestation = builder.individualAttestation; + } + + static AuthorizationList createAuthorizationList( + ASN1Encodable[] authorizationList, int attestationVersion) { + return new AuthorizationList(authorizationList, attestationVersion); + } + + private static Map<Integer, ASN1Primitive> getAuthorizationMap( + ASN1Encodable[] authorizationList) { + Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>(); + for (ASN1Encodable entry : authorizationList) { + ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry; + authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject()); + } + return authorizationMap; + } + + private static ASN1Primitive findAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + return authorizationMap.getOrDefault(tag, null); + } + + private static Optional<Set<Integer>> findOptionalIntegerSetAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + ASN1Set asn1Set = (ASN1Set) findAuthorizationListEntry(authorizationMap, tag); + if (asn1Set == null) { + return Optional.empty(); + } + Set<Integer> entrySet = new HashSet<>(); + for (ASN1Encodable value : asn1Set) { + entrySet.add(ASN1Parsing.getIntegerFromAsn1(value)); + } + return Optional.of(entrySet); + } + + private static Optional<Duration> findOptionalDurationSecondsAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + Optional<Integer> seconds = findOptionalIntegerAuthorizationListEntry(authorizationMap, tag); + return seconds.map(Duration::ofSeconds); + } + + private static Optional<Integer> findOptionalIntegerAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + ASN1Primitive entry = findAuthorizationListEntry(authorizationMap, tag); + return Optional.ofNullable(entry).map(ASN1Parsing::getIntegerFromAsn1); + } + + private static Optional<Instant> findOptionalInstantMillisAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + Optional<Long> millis = findOptionalLongAuthorizationListEntry(authorizationMap, tag); + return millis.map(Instant::ofEpochMilli); + } + + private static Optional<Long> findOptionalLongAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + ASN1Integer longEntry = ((ASN1Integer) findAuthorizationListEntry(authorizationMap, tag)); + return Optional.ofNullable(longEntry).map(value -> value.getValue().longValue()); + } + + private static boolean findBooleanAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + return null != findAuthorizationListEntry(authorizationMap, tag); + } + + private static Optional<byte[]> findOptionalByteArrayAuthorizationListEntry( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(authorizationMap, tag); + return Optional.ofNullable(entry).map(ASN1OctetString::getOctets); + } + + private static Optional<Set<UserAuthType>> findOptionalUserAuthType( + Map<Integer, ASN1Primitive> authorizationMap, int tag) { + Optional<Long> userAuthType = findOptionalLongAuthorizationListEntry(authorizationMap, tag); + return userAuthType.map(AuthorizationList::userAuthTypeToEnum); + } + + // Visible for testing. + static Set<UserAuthType> userAuthTypeToEnum(long userAuthType) { + if (userAuthType == 0) { + return Set.of(USER_AUTH_TYPE_NONE); + } + + Set<UserAuthType> result = new HashSet<>(); + + if ((userAuthType & 1L) == 1L) { + result.add(PASSWORD); + } + if ((userAuthType & 2L) == 2L) { + result.add(FINGERPRINT); + } + if (userAuthType == UINT32_MAX) { + result.add(USER_AUTH_TYPE_ANY); + } + + if (result.isEmpty()) { + throw new IllegalArgumentException("Invalid User Auth Type."); + } + + return result; + } + + private static Long userAuthTypeToLong(Set<UserAuthType> userAuthType) { + if (userAuthType.contains(USER_AUTH_TYPE_NONE)) { + return 0L; + } + + Long result = 0L; + + for (UserAuthType type : userAuthType) { + switch (type) { + case PASSWORD: + result |= 1L; + break; + case FINGERPRINT: + result |= 2L; + break; + case USER_AUTH_TYPE_ANY: + result |= UINT32_MAX; + break; + default: + break; + } + } + + if (result == 0) { + throw new IllegalArgumentException("Invalid User Auth Type."); + } + + return result; + } + + public ASN1Sequence toAsn1Sequence() { + ASN1EncodableVector vector = new ASN1EncodableVector(); + addOptionalIntegerSet(KM_TAG_PURPOSE, this.purpose, vector); + addOptionalInteger(KM_TAG_ALGORITHM, this.algorithm, vector); + addOptionalInteger(KM_TAG_KEY_SIZE, this.keySize, vector); + addOptionalIntegerSet(KM_TAG_DIGEST, this.digest, vector); + addOptionalIntegerSet(KM_TAG_PADDING, this.padding, vector); + addOptionalInteger(KM_TAG_EC_CURVE, this.ecCurve, vector); + addOptionalLong(KM_TAG_RSA_PUBLIC_EXPONENT, this.rsaPublicExponent, vector); + addBoolean(KM_TAG_ROLLBACK_RESISTANCE, this.rollbackResistance, vector); + addOptionalInstant(KM_TAG_ACTIVE_DATE_TIME, this.activeDateTime, vector); + addOptionalInstant(KM_TAG_ORIGINATION_EXPIRE_DATE_TIME, this.originationExpireDateTime, vector); + addOptionalInstant(KM_TAG_USAGE_EXPIRE_DATE_TIME, this.usageExpireDateTime, vector); + addBoolean(KM_TAG_NO_AUTH_REQUIRED, this.noAuthRequired, vector); + addOptionalUserAuthType(KM_TAG_USER_AUTH_TYPE, this.userAuthType, vector); + addOptionalDuration(KM_TAG_AUTH_TIMEOUT, this.authTimeout, vector); + addBoolean(KM_TAG_ALLOW_WHILE_ON_BODY, this.allowWhileOnBody, vector); + addBoolean(KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED, this.trustedUserPresenceRequired, vector); + addBoolean(KM_TAG_TRUSTED_CONFIRMATION_REQUIRED, this.trustedConfirmationRequired, vector); + addBoolean(KM_TAG_UNLOCKED_DEVICE_REQUIRED, this.unlockedDeviceRequired, vector); + addBoolean(KM_TAG_ALL_APPLICATIONS, this.allApplications, vector); + addOptionalOctetString(KM_TAG_APPLICATION_ID, this.applicationId, vector); + addOptionalInstant(KM_TAG_CREATION_DATE_TIME, this.creationDateTime, vector); + addOptionalInteger(KM_TAG_ORIGIN, this.origin, vector); + addBoolean(KM_TAG_ROLLBACK_RESISTANT, this.rollbackResistant, vector); + addOptionalRootOfTrust(KM_TAG_ROOT_OF_TRUST, this.rootOfTrust, vector); + addOptionalInteger(KM_TAG_OS_VERSION, this.osVersion, vector); + addOptionalInteger(KM_TAG_OS_PATCH_LEVEL, this.osPatchLevel, vector); + addOptionalOctetString( + KM_TAG_ATTESTATION_APPLICATION_ID, this.attestationApplicationIdBytes, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_BRAND, this.attestationIdBrand, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_DEVICE, this.attestationIdDevice, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_PRODUCT, this.attestationIdProduct, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_SERIAL, this.attestationIdSerial, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_IMEI, this.attestationIdImei, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_MEID, this.attestationIdMeid, vector); + addOptionalOctetString( + KM_TAG_ATTESTATION_ID_MANUFACTURER, this.attestationIdManufacturer, vector); + addOptionalOctetString(KM_TAG_ATTESTATION_ID_MODEL, this.attestationIdModel, vector); + addOptionalInteger(KM_TAG_VENDOR_PATCH_LEVEL, this.vendorPatchLevel, vector); + addOptionalInteger(KM_TAG_BOOT_PATCH_LEVEL, this.bootPatchLevel, vector); + addBoolean(KM_TAG_DEVICE_UNIQUE_ATTESTATION, this.individualAttestation, vector); + return new DERSequence(vector); + } + + private static void addOptionalIntegerSet( + int tag, Optional<Set<Integer>> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + ASN1EncodableVector tmp = new ASN1EncodableVector(); + entry.get().forEach((Integer value) -> tmp.add(new ASN1Integer(value.longValue()))); + vector.add(new DERTaggedObject(tag, new DERSet(tmp))); + } + } + + private static void addOptionalInstant( + int tag, Optional<Instant> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, new ASN1Integer(entry.get().toEpochMilli()))); + } + } + + private static void addOptionalDuration( + int tag, Optional<Duration> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, new ASN1Integer(entry.get().getSeconds()))); + } + } + + private static void addBoolean(int tag, boolean entry, ASN1EncodableVector vector) { + if (entry) { + vector.add(new DERTaggedObject(tag, DERNull.INSTANCE)); + } + } + + private static void addOptionalInteger( + int tag, Optional<Integer> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, new ASN1Integer(entry.get()))); + } + } + + private static void addOptionalLong(int tag, Optional<Long> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, new ASN1Integer(entry.get()))); + } + } + + private static void addOptionalOctetString( + int tag, Optional<byte[]> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, new DEROctetString(entry.get()))); + } + } + + private static void addOptionalUserAuthType( + int tag, Optional<Set<UserAuthType>> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, new ASN1Integer(userAuthTypeToLong(entry.get())))); + } + } + + private static void addOptionalRootOfTrust( + int tag, Optional<RootOfTrust> entry, ASN1EncodableVector vector) { + if (entry.isPresent()) { + vector.add(new DERTaggedObject(tag, entry.get().toAsn1Sequence())); + } + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for an AuthorizationList. Any field not set will be made an Optional.empty or set with + * the default value. + */ + public static final class Builder { + + Set<Integer> purpose; + Integer algorithm; + Integer keySize; + Set<Integer> digest; + Set<Integer> padding; + Integer ecCurve; + Long rsaPublicExponent; + boolean rollbackResistance; + Instant activeDateTime; + Instant originationExpireDateTime; + Instant usageExpireDateTime; + boolean noAuthRequired; + Set<UserAuthType> userAuthType; + Duration authTimeout; + boolean allowWhileOnBody; + boolean trustedUserPresenceRequired; + boolean trustedConfirmationRequired; + boolean unlockedDeviceRequired; + boolean allApplications; + byte[] applicationId; + Instant creationDateTime; + Integer origin; + boolean rollbackResistant; + RootOfTrust rootOfTrust; + Integer osVersion; + Integer osPatchLevel; + AttestationApplicationId attestationApplicationId; + byte[] attestationApplicationIdBytes; + byte[] attestationIdBrand; + byte[] attestationIdDevice; + byte[] attestationIdProduct; + byte[] attestationIdSerial; + byte[] attestationIdImei; + byte[] attestationIdMeid; + byte[] attestationIdManufacturer; + byte[] attestationIdModel; + Integer vendorPatchLevel; + Integer bootPatchLevel; + boolean individualAttestation; + + public Builder setPurpose(Set<Integer> purpose) { + this.purpose = purpose; + return this; + } + + public Builder setAlgorithm(Integer algorithm) { + this.algorithm = algorithm; + return this; + } + + public Builder setKeySize(Integer keySize) { + this.keySize = keySize; + return this; + } + + public Builder setDigest(Set<Integer> digest) { + this.digest = digest; + return this; + } + + public Builder setPadding(Set<Integer> padding) { + this.padding = padding; + return this; + } + + public Builder setEcCurve(Integer ecCurve) { + this.ecCurve = ecCurve; + return this; + } + + public Builder setRsaPublicExponent(Long rsaPublicExponent) { + this.rsaPublicExponent = rsaPublicExponent; + return this; + } + + public Builder setRollbackResistance(boolean rollbackResistance) { + this.rollbackResistance = rollbackResistance; + return this; + } + + public Builder setActiveDateTime(Instant activeDateTime) { + this.activeDateTime = activeDateTime; + return this; + } + + public Builder setOriginationExpireDateTime(Instant originationExpireDateTime) { + this.originationExpireDateTime = originationExpireDateTime; + return this; + } + + public Builder setUsageExpireDateTime(Instant usageExpireDateTime) { + this.usageExpireDateTime = usageExpireDateTime; + return this; + } + + public Builder setNoAuthRequired(boolean noAuthRequired) { + this.noAuthRequired = noAuthRequired; + return this; + } + + public Builder setUserAuthType(Set<UserAuthType> userAuthType) { + this.userAuthType = userAuthType; + return this; + } + + public Builder setAuthTimeout(Duration authTimeout) { + this.authTimeout = authTimeout; + return this; + } + + public Builder setAllowWhileOnBody(boolean allowWhileOnBody) { + this.allowWhileOnBody = allowWhileOnBody; + return this; + } + + public Builder setTrustedUserPresenceRequired(boolean trustedUserPresenceRequired) { + this.trustedUserPresenceRequired = trustedUserPresenceRequired; + return this; + } + + public Builder setTrustedConfirmationRequired(boolean trustedConfirmationRequired) { + this.trustedConfirmationRequired = trustedConfirmationRequired; + return this; + } + + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + this.unlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + public Builder setAllApplications(boolean allApplications) { + this.allApplications = allApplications; + return this; + } + + public Builder setApplicationId(byte[] applicationId) { + this.applicationId = applicationId; + return this; + } + + public Builder setCreationDateTime(Instant creationDateTime) { + this.creationDateTime = creationDateTime; + return this; + } + + public Builder setOrigin(Integer origin) { + this.origin = origin; + return this; + } + + public Builder setRollbackResistant(boolean rollbackResistant) { + this.rollbackResistant = rollbackResistant; + return this; + } + + public Builder setRootOfTrust(RootOfTrust rootOfTrust) { + this.rootOfTrust = rootOfTrust; + return this; + } + + public Builder setOsVersion(Integer osVersion) { + this.osVersion = osVersion; + return this; + } + + public Builder setOsPatchLevel(Integer osPatchLevel) { + this.osPatchLevel = osPatchLevel; + return this; + } + + public Builder setAttestationApplicationId(AttestationApplicationId attestationApplicationId) { + this.attestationApplicationId = attestationApplicationId; + return this; + } + + public Builder setAttestationApplicationIdBytes(byte[] attestationApplicationIdBytes) { + this.attestationApplicationIdBytes = attestationApplicationIdBytes; + return this; + } + + public Builder setAttestationIdBrand(byte[] attestationIdBrand) { + this.attestationIdBrand = attestationIdBrand; + return this; + } + + public Builder setAttestationIdProduct(byte[] attestationIdProduct) { + this.attestationIdProduct = attestationIdProduct; + return this; + } + + public Builder setAttestationIdSerial(byte[] attestationIdSerial) { + this.attestationIdSerial = attestationIdSerial; + return this; + } + + public Builder setAttestationIdImei(byte[] attestationIdImei) { + this.attestationIdImei = attestationIdImei; + return this; + } + + public Builder setAttestationIdMeid(byte[] attestationIdMeid) { + this.attestationIdMeid = attestationIdMeid; + return this; + } + + public Builder setAttestationIdManufacturer(byte[] attestationIdManufacturer) { + this.attestationIdManufacturer = attestationIdManufacturer; + return this; + } + + public Builder setAttestationIdModel(byte[] attestationIdModel) { + this.attestationIdModel = attestationIdModel; + return this; + } + + public Builder setVendorPatchLevel(Integer vendorPatchLevel) { + this.vendorPatchLevel = vendorPatchLevel; + return this; + } + + public Builder setBootPatchLevel(Integer bootPatchLevel) { + this.bootPatchLevel = bootPatchLevel; + return this; + } + + public Builder setIndividualAttestation(boolean individualAttestation) { + this.individualAttestation = individualAttestation; + return this; + } + + public AuthorizationList build() { + return new AuthorizationList(this); + } + } +} diff --git a/server/src/main/java/com/google/android/attestation/BUILD b/server/src/main/java/com/google/android/attestation/BUILD new file mode 100644 index 0000000..d2c2ae2 --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/BUILD @@ -0,0 +1,19 @@ +package(default_visibility = ["//visibility:public"]) + +java_library( + name = "attestation", + srcs = [ + "ASN1Parsing.java", + "AttestationApplicationId.java", + "AuthorizationList.java", + "CertificateRevocationStatus.java", + "Constants.java", + "ParsedAttestationRecord.java", + "RootOfTrust.java", + ], + deps = [ + "@maven//:com_google_code_gson_gson", + "@maven//:org_bouncycastle_bcpkix_jdk15on", + "@maven//:org_bouncycastle_bcprov_jdk15on", + ], +) diff --git a/server/src/main/java/com/google/android/attestation/CertificateRevocationStatus.java b/server/src/main/java/com/google/android/attestation/CertificateRevocationStatus.java new file mode 100644 index 0000000..d31b2d8 --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/CertificateRevocationStatus.java @@ -0,0 +1,135 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.ByteArrayInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.util.HashMap; + + +/** + * Utils for fetching and decoding attestation certificate status. + */ +public class CertificateRevocationStatus { + + private static final String STATUS_URL = "https://android.googleapis.com/attestation/status"; + public final Status status; + public final Reason reason; + public final String comment; + public final String expires; + + public static HashMap<String, CertificateRevocationStatus> fetchAllEntries() throws IOException { + URL url = new URL(STATUS_URL); + InputStreamReader statusListReader = new InputStreamReader(url.openStream()); + return getEntryToStatusMap(statusListReader); + } + + public static HashMap<String, CertificateRevocationStatus> loadAllEntriesFromFile(String filePath) + throws IOException { + FileReader reader = new FileReader(filePath); + return getEntryToStatusMap(reader); + } + + private static HashMap<String, CertificateRevocationStatus> getEntryToStatusMap( + Reader statusListReader) { + JsonObject entries = + new JsonParser().parse(statusListReader).getAsJsonObject().getAsJsonObject("entries"); + + HashMap<String, CertificateRevocationStatus> serialNumberToStatus = new HashMap<>(); + for (String serialNumber : entries.keySet()) { + serialNumberToStatus.put( + serialNumber, + new Gson().fromJson(entries.get(serialNumber), CertificateRevocationStatus.class)); + } + + return serialNumberToStatus; + } + + public static CertificateRevocationStatus loadStatusFromFile(BigInteger serialNumber, + String filePath) + throws IOException { + return loadStatusFromFile(serialNumber.toString(16), filePath); + } + + public static CertificateRevocationStatus loadStatusFromFile(String serialNumber, String filePath) + throws IOException { + FileReader reader = new FileReader(filePath); + return decodeStatus(serialNumber, reader); + } + + + public static CertificateRevocationStatus fetchStatus(BigInteger serialNumber) + throws IOException { + return fetchStatus(serialNumber.toString(16)); + } + + public static CertificateRevocationStatus fetchStatus(String serialNumber) throws IOException { + URL url; + try { + url = new URL(STATUS_URL); + } catch (MalformedURLException e) { + throw new IllegalStateException(e); + } + + InputStreamReader statusListReader = new InputStreamReader(url.openStream()); + + return decodeStatus(serialNumber, statusListReader); + + } + + private static CertificateRevocationStatus decodeStatus(String serialNumber, + Reader statusListReader) { + if (serialNumber == null) { + throw new IllegalArgumentException("serialNumber cannot be null"); + } + serialNumber = serialNumber.toLowerCase(); + + JsonObject entries = new JsonParser().parse(statusListReader) + .getAsJsonObject() + .getAsJsonObject("entries"); + + if (!entries.has(serialNumber)) { + return null; + } + + return new Gson().fromJson(entries.get(serialNumber), CertificateRevocationStatus.class); + } + + public enum Status { + REVOKED, SUSPENDED + } + + public enum Reason { + UNSPECIFIED, KEY_COMPROMISE, CA_COMPROMISE, SUPERSEDED, SOFTWARE_FLAW + } + + public CertificateRevocationStatus() { + status = Status.REVOKED; + reason = Reason.UNSPECIFIED; + comment = null; + expires = null; + } +} diff --git a/server/src/main/java/com/google/android/attestation/Constants.java b/server/src/main/java/com/google/android/attestation/Constants.java new file mode 100644 index 0000000..7db2744 --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/Constants.java @@ -0,0 +1,125 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +/** Key Attestation constants */ +public class Constants { + + // The Google root certificate that must have been used to sign the root + // certificate in a real attestation certificate chain from a compliant + // device. + // (Note, the sample chain used here is not signed with this certificate.) + public static final String GOOGLE_ROOT_CERTIFICATE = + "-----BEGIN CERTIFICATE-----\n" + + "MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV" + + "BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy" + + "ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B" + + "AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS" + + "Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7" + + "tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj" + + "nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq" + + "C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ" + + "oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O" + + "JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg" + + "sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi" + + "igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M" + + "RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E" + + "aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um" + + "AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD" + + "VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO" + + "BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk" + + "Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD" + + "ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB" + + "Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m" + + "qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY" + + "DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm" + + "QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u" + + "JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD" + + "CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy" + + "ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD" + + "qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic" + + "MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1" + + "wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk\n" + + "-----END CERTIFICATE-----"; + static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17"; + static final int ATTESTATION_VERSION_INDEX = 0; + static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1; + static final int KEYMASTER_VERSION_INDEX = 2; + static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3; + static final int ATTESTATION_CHALLENGE_INDEX = 4; + static final int UNIQUE_ID_INDEX = 5; + static final int SW_ENFORCED_INDEX = 6; + static final int TEE_ENFORCED_INDEX = 7; + // Authorization list tags. The list is in this AOSP file: + // hardware/libhardware/include/hardware/keymaster_defs.h + static final int KM_TAG_PURPOSE = 1; + static final int KM_TAG_ALGORITHM = 2; + static final int KM_TAG_KEY_SIZE = 3; + static final int KM_TAG_DIGEST = 5; + static final int KM_TAG_PADDING = 6; + static final int KM_TAG_EC_CURVE = 10; + static final int KM_TAG_RSA_PUBLIC_EXPONENT = 200; + static final int KM_TAG_ROLLBACK_RESISTANCE = 303; + static final int KM_TAG_ACTIVE_DATE_TIME = 400; + static final int KM_TAG_ORIGINATION_EXPIRE_DATE_TIME = 401; + static final int KM_TAG_USAGE_EXPIRE_DATE_TIME = 402; + static final int KM_TAG_NO_AUTH_REQUIRED = 503; + static final int KM_TAG_USER_AUTH_TYPE = 504; + static final int KM_TAG_AUTH_TIMEOUT = 505; + static final int KM_TAG_ALLOW_WHILE_ON_BODY = 506; + static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = 507; + static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = 508; + static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = 509; + static final int KM_TAG_ALL_APPLICATIONS = 600; + static final int KM_TAG_APPLICATION_ID = 601; + static final int KM_TAG_CREATION_DATE_TIME = 701; + static final int KM_TAG_ORIGIN = 702; + static final int KM_TAG_ROLLBACK_RESISTANT = 703; + static final int KM_TAG_ROOT_OF_TRUST = 704; + static final int KM_TAG_OS_VERSION = 705; + static final int KM_TAG_OS_PATCH_LEVEL = 706; + static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709; + static final int KM_TAG_ATTESTATION_ID_BRAND = 710; + static final int KM_TAG_ATTESTATION_ID_DEVICE = 711; + static final int KM_TAG_ATTESTATION_ID_PRODUCT = 712; + static final int KM_TAG_ATTESTATION_ID_SERIAL = 713; + static final int KM_TAG_ATTESTATION_ID_IMEI = 714; + static final int KM_TAG_ATTESTATION_ID_MEID = 715; + static final int KM_TAG_ATTESTATION_ID_MANUFACTURER = 716; + static final int KM_TAG_ATTESTATION_ID_MODEL = 717; + static final int KM_TAG_VENDOR_PATCH_LEVEL = 718; + static final int KM_TAG_BOOT_PATCH_LEVEL = 719; + static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION = 720; + static final int ROOT_OF_TRUST_VERIFIED_BOOT_KEY_INDEX = 0; + static final int ROOT_OF_TRUST_DEVICE_LOCKED_INDEX = 1; + static final int ROOT_OF_TRUST_VERIFIED_BOOT_STATE_INDEX = 2; + static final int ROOT_OF_TRUST_VERIFIED_BOOT_HASH_INDEX = 3; + static final int ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX = 0; + static final int ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX = 1; + static final int ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX = 0; + static final int ATTESTATION_PACKAGE_INFO_VERSION_INDEX = 1; + // Some security values. The complete list is in this AOSP file: + // hardware/libhardware/include/hardware/keymaster_defs.h + static final int KM_SECURITY_LEVEL_SOFTWARE = 0; + static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1; + static final int KM_SECURITY_LEVEL_STRONG_BOX = 2; + static final int KM_VERIFIED_BOOT_STATE_VERIFIED = 0; + static final int KM_VERIFIED_BOOT_STATE_SELF_SIGNED = 1; + static final int KM_VERIFIED_BOOT_STATE_UNVERIFIED = 2; + static final int KM_VERIFIED_BOOT_STATE_FAILED = 3; + // Unsigned max value of 32-bit integer, 2^32 - 1 + static final long UINT32_MAX = (((long) Integer.MAX_VALUE) << 1) + 1; +} diff --git a/server/src/main/java/com/google/android/attestation/ParsedAttestationRecord.java b/server/src/main/java/com/google/android/attestation/ParsedAttestationRecord.java new file mode 100644 index 0000000..053baae --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/ParsedAttestationRecord.java @@ -0,0 +1,203 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.android.attestation.Constants.ATTESTATION_CHALLENGE_INDEX; +import static com.google.android.attestation.Constants.ATTESTATION_SECURITY_LEVEL_INDEX; +import static com.google.android.attestation.Constants.ATTESTATION_VERSION_INDEX; +import static com.google.android.attestation.Constants.KEYMASTER_SECURITY_LEVEL_INDEX; +import static com.google.android.attestation.Constants.KEYMASTER_VERSION_INDEX; +import static com.google.android.attestation.Constants.KEY_DESCRIPTION_OID; +import static com.google.android.attestation.Constants.KM_SECURITY_LEVEL_SOFTWARE; +import static com.google.android.attestation.Constants.KM_SECURITY_LEVEL_STRONG_BOX; +import static com.google.android.attestation.Constants.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; +import static com.google.android.attestation.Constants.SW_ENFORCED_INDEX; +import static com.google.android.attestation.Constants.TEE_ENFORCED_INDEX; +import static com.google.android.attestation.Constants.UNIQUE_ID_INDEX; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; + +/** Java representation of Key Attestation extension data. */ +public class ParsedAttestationRecord { + + public final int attestationVersion; + public final SecurityLevel attestationSecurityLevel; + public final int keymasterVersion; + public final SecurityLevel keymasterSecurityLevel; + public final byte[] attestationChallenge; + public final byte[] uniqueId; + public final AuthorizationList softwareEnforced; + public final AuthorizationList teeEnforced; + + private ParsedAttestationRecord(ASN1Sequence extensionData) { + this.attestationVersion = + ASN1Parsing.getIntegerFromAsn1(extensionData.getObjectAt(ATTESTATION_VERSION_INDEX)); + this.attestationSecurityLevel = + securityLevelToEnum( + ASN1Parsing.getIntegerFromAsn1( + extensionData.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX))); + this.keymasterVersion = + ASN1Parsing.getIntegerFromAsn1(extensionData.getObjectAt(KEYMASTER_VERSION_INDEX)); + this.keymasterSecurityLevel = + securityLevelToEnum( + ASN1Parsing.getIntegerFromAsn1( + extensionData.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX))); + this.attestationChallenge = + ((ASN1OctetString) extensionData.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets(); + this.uniqueId = ((ASN1OctetString) extensionData.getObjectAt(UNIQUE_ID_INDEX)).getOctets(); + this.softwareEnforced = + AuthorizationList.createAuthorizationList( + ((ASN1Sequence) extensionData.getObjectAt(SW_ENFORCED_INDEX)).toArray(), + attestationVersion); + this.teeEnforced = + AuthorizationList.createAuthorizationList( + ((ASN1Sequence) extensionData.getObjectAt(TEE_ENFORCED_INDEX)).toArray(), + attestationVersion); + } + + private ParsedAttestationRecord( + int attestationVersion, + SecurityLevel attestationSecurityLevel, + int keymasterVersion, + SecurityLevel keymasterSecurityLevel, + byte[] attestationChallenge, + byte[] uniqueId, + AuthorizationList softwareEnforced, + AuthorizationList teeEnforced) { + this.attestationVersion = attestationVersion; + this.attestationSecurityLevel = attestationSecurityLevel; + this.keymasterVersion = keymasterVersion; + this.keymasterSecurityLevel = keymasterSecurityLevel; + this.attestationChallenge = attestationChallenge; + this.uniqueId = uniqueId; + this.softwareEnforced = softwareEnforced; + this.teeEnforced = teeEnforced; + } + + public static ParsedAttestationRecord createParsedAttestationRecord(X509Certificate cert) + throws IOException { + ASN1Sequence extensionData = extractAttestationSequence(cert); + return new ParsedAttestationRecord(extensionData); + } + + public static ParsedAttestationRecord create(ASN1Sequence extensionData) { + return new ParsedAttestationRecord(extensionData); + } + + public static ParsedAttestationRecord create( + int attestationVersion, + SecurityLevel attestationSecurityLevel, + int keymasterVersion, + SecurityLevel keymasterSecurityLevel, + byte[] attestationChallenge, + byte[] uniqueId, + AuthorizationList softwareEnforced, + AuthorizationList teeEnforced) { + return new ParsedAttestationRecord( + attestationVersion, + attestationSecurityLevel, + keymasterVersion, + keymasterSecurityLevel, + attestationChallenge, + uniqueId, + softwareEnforced, + teeEnforced); + } + + private static SecurityLevel securityLevelToEnum(int securityLevel) { + switch (securityLevel) { + case KM_SECURITY_LEVEL_SOFTWARE: + return SecurityLevel.SOFTWARE; + case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: + return SecurityLevel.TRUSTED_ENVIRONMENT; + case KM_SECURITY_LEVEL_STRONG_BOX: + return SecurityLevel.STRONG_BOX; + default: + throw new IllegalArgumentException("Invalid security level."); + } + } + + private static int securityLevelToInt(SecurityLevel securityLevel) { + switch (securityLevel) { + case SOFTWARE: + return KM_SECURITY_LEVEL_SOFTWARE; + case TRUSTED_ENVIRONMENT: + return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; + case STRONG_BOX: + return KM_SECURITY_LEVEL_STRONG_BOX; + } + throw new IllegalArgumentException("Invalid security level."); + } + + private static ASN1Sequence extractAttestationSequence(X509Certificate attestationCert) + throws IOException { + byte[] attestationExtensionBytes = attestationCert.getExtensionValue(KEY_DESCRIPTION_OID); + if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) { + throw new IllegalArgumentException("Couldn't find the keystore attestation extension data."); + } + + ASN1Sequence decodedSequence; + try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) { + // The extension contains one object, a sequence, in the + // Distinguished Encoding Rules (DER)-encoded form. Get the DER + // bytes. + byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets(); + // Decode the bytes as an ASN1 sequence object. + try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) { + decodedSequence = (ASN1Sequence) seqInputStream.readObject(); + } + } + return decodedSequence; + } + + public ASN1Sequence toAsn1Sequence() { + ASN1Encodable[] vector = new ASN1Encodable[8]; + vector[ATTESTATION_VERSION_INDEX] = new ASN1Integer(this.attestationVersion); + vector[ATTESTATION_SECURITY_LEVEL_INDEX] = + new ASN1Enumerated(securityLevelToInt(this.attestationSecurityLevel)); + vector[KEYMASTER_VERSION_INDEX] = new ASN1Integer(this.keymasterVersion); + vector[KEYMASTER_SECURITY_LEVEL_INDEX] = + new ASN1Enumerated(securityLevelToInt(this.keymasterSecurityLevel)); + vector[ATTESTATION_CHALLENGE_INDEX] = new DEROctetString(this.attestationChallenge); + vector[UNIQUE_ID_INDEX] = new DEROctetString(this.uniqueId); + if (this.softwareEnforced != null) { + vector[SW_ENFORCED_INDEX] = this.softwareEnforced.toAsn1Sequence(); + } + if (this.teeEnforced != null) { + vector[TEE_ENFORCED_INDEX] = this.teeEnforced.toAsn1Sequence(); + } + return new DERSequence(vector); + } + + /** + * This indicates the extent to which a software feature, such as a key pair, is protected based + * on its location within the device. + */ + public enum SecurityLevel { + SOFTWARE, + TRUSTED_ENVIRONMENT, + STRONG_BOX + } +} diff --git a/server/src/main/java/com/google/android/attestation/RootOfTrust.java b/server/src/main/java/com/google/android/attestation/RootOfTrust.java new file mode 100644 index 0000000..7345cb8 --- /dev/null +++ b/server/src/main/java/com/google/android/attestation/RootOfTrust.java @@ -0,0 +1,126 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.android.attestation.Constants.KM_VERIFIED_BOOT_STATE_FAILED; +import static com.google.android.attestation.Constants.KM_VERIFIED_BOOT_STATE_SELF_SIGNED; +import static com.google.android.attestation.Constants.KM_VERIFIED_BOOT_STATE_UNVERIFIED; +import static com.google.android.attestation.Constants.KM_VERIFIED_BOOT_STATE_VERIFIED; +import static com.google.android.attestation.Constants.ROOT_OF_TRUST_DEVICE_LOCKED_INDEX; +import static com.google.android.attestation.Constants.ROOT_OF_TRUST_VERIFIED_BOOT_HASH_INDEX; +import static com.google.android.attestation.Constants.ROOT_OF_TRUST_VERIFIED_BOOT_KEY_INDEX; +import static com.google.android.attestation.Constants.ROOT_OF_TRUST_VERIFIED_BOOT_STATE_INDEX; + +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; + +/** This collection of values defines key information about the device's status. */ +public class RootOfTrust { + + public final byte[] verifiedBootKey; + public final boolean deviceLocked; + public final VerifiedBootState verifiedBootState; + public final byte[] verifiedBootHash; + + private RootOfTrust(ASN1Sequence rootOfTrust, int attestationVersion) { + this.verifiedBootKey = + ((ASN1OctetString) rootOfTrust.getObjectAt(ROOT_OF_TRUST_VERIFIED_BOOT_KEY_INDEX)) + .getOctets(); + this.deviceLocked = + ASN1Parsing.getBooleanFromAsn1(rootOfTrust.getObjectAt(ROOT_OF_TRUST_DEVICE_LOCKED_INDEX)); + this.verifiedBootState = + verifiedBootStateToEnum( + ASN1Parsing.getIntegerFromAsn1( + rootOfTrust.getObjectAt(ROOT_OF_TRUST_VERIFIED_BOOT_STATE_INDEX))); + if (attestationVersion >= 3) { + this.verifiedBootHash = + ((ASN1OctetString) rootOfTrust.getObjectAt(ROOT_OF_TRUST_VERIFIED_BOOT_HASH_INDEX)) + .getOctets(); + } else { + this.verifiedBootHash = null; + } + } + + static RootOfTrust createRootOfTrust(ASN1Sequence rootOfTrust, int attestationVersion) { + if (rootOfTrust == null) { + return null; + } + return new RootOfTrust(rootOfTrust, attestationVersion); + } + + private static VerifiedBootState verifiedBootStateToEnum(int securityLevel) { + switch (securityLevel) { + case KM_VERIFIED_BOOT_STATE_VERIFIED: + return VerifiedBootState.VERIFIED; + case KM_VERIFIED_BOOT_STATE_SELF_SIGNED: + return VerifiedBootState.SELF_SIGNED; + case KM_VERIFIED_BOOT_STATE_UNVERIFIED: + return VerifiedBootState.UNVERIFIED; + case KM_VERIFIED_BOOT_STATE_FAILED: + return VerifiedBootState.FAILED; + default: + throw new IllegalArgumentException("Invalid verified boot state."); + } + } + + private static int verifiedBootStateToInt(VerifiedBootState verifiedBootState) { + switch (verifiedBootState) { + case VERIFIED: + return KM_VERIFIED_BOOT_STATE_VERIFIED; + case SELF_SIGNED: + return KM_VERIFIED_BOOT_STATE_SELF_SIGNED; + case UNVERIFIED: + return KM_VERIFIED_BOOT_STATE_UNVERIFIED; + case FAILED: + return KM_VERIFIED_BOOT_STATE_FAILED; + } + throw new IllegalArgumentException("Invalid verified boot state."); + } + + /** + * This provides the device's current boot state, which represents the level of protection + * provided to the user and to apps after the device finishes booting. + */ + public enum VerifiedBootState { + VERIFIED, + SELF_SIGNED, + UNVERIFIED, + FAILED + } + + public ASN1Sequence toAsn1Sequence() { + ASN1Encodable[] rootOfTrustElements; + if (this.verifiedBootHash != null) { + rootOfTrustElements = new ASN1Encodable[4]; + rootOfTrustElements[ROOT_OF_TRUST_VERIFIED_BOOT_HASH_INDEX] = + new DEROctetString(this.verifiedBootHash); + } else { + rootOfTrustElements = new ASN1Encodable[3]; + } + rootOfTrustElements[ROOT_OF_TRUST_VERIFIED_BOOT_KEY_INDEX] = + new DEROctetString(this.verifiedBootKey); + rootOfTrustElements[ROOT_OF_TRUST_DEVICE_LOCKED_INDEX] = + ASN1Boolean.getInstance(this.deviceLocked); + rootOfTrustElements[ROOT_OF_TRUST_VERIFIED_BOOT_STATE_INDEX] = + new ASN1Enumerated(verifiedBootStateToInt(this.verifiedBootState)); + return new DERSequence(rootOfTrustElements); + } +} diff --git a/server/src/test/java/com/google/android/attestation/AttestationApplicationIdTest.java b/server/src/test/java/com/google/android/attestation/AttestationApplicationIdTest.java new file mode 100644 index 0000000..5d18f06 --- /dev/null +++ b/server/src/test/java/com/google/android/attestation/AttestationApplicationIdTest.java @@ -0,0 +1,106 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.android.attestation.AttestationApplicationId.AttestationPackageInfo; +import com.google.common.collect.ImmutableList; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.util.encoders.Base64; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link AttestationApplicationId}. */ +@RunWith(JUnit4.class) +public class AttestationApplicationIdTest { + + // Generated from certificate with RSA Algorithm and StrongBox Security Level + private static final DEROctetString ATTESTATION_APPLICATION_ID = + new DEROctetString( + Base64.decode( + "MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXljaGFpbgIBHTAZBBRjb20uYW5kcm9p" + + "ZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNlcwIBHTAaBBVjb20uYW5kcm9pZC5keW" + + "5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZpY2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxv" + + "Y2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxvY2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbm" + + "Ryb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5hbmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAh" + + "BBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9yAgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaW" + + "RkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnByb3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0" + + "UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqg==")); + + private static final ImmutableList<AttestationPackageInfo> EXPECTED_PACKAGE_INFOS = + ImmutableList.of( + new AttestationPackageInfo("android", 29L), + new AttestationPackageInfo("com.android.keychain", 29L), + new AttestationPackageInfo("com.android.settings", 29L), + new AttestationPackageInfo("com.qti.diagservices", 29L), + new AttestationPackageInfo("com.android.dynsystem", 29L), + new AttestationPackageInfo("com.android.inputdevices", 29L), + new AttestationPackageInfo("com.android.localtransport", 29L), + new AttestationPackageInfo("com.android.location.fused", 29L), + new AttestationPackageInfo("com.android.server.telecom", 29L), + new AttestationPackageInfo("com.android.wallpaperbackup", 29L), + new AttestationPackageInfo("com.google.SSRestartDetector", 29L), + new AttestationPackageInfo("com.google.android.hiddenmenu", 1L), + new AttestationPackageInfo("com.android.providers.settings", 29L)); + private static final ImmutableList<byte[]> EXPECTED_SIGNATURE_DIGESTS = + ImmutableList.of(Base64.decode("MBqjywgRNFAcRfFCKrxmwkIk/V3tX9yPF+aXF2/YZqo=\n")); + + private static final AttestationApplicationId EXPECTED_ATTESTATION_APPLICATION_ID = + new AttestationApplicationId(EXPECTED_PACKAGE_INFOS, EXPECTED_SIGNATURE_DIGESTS); + + @Test + public void testCreateAttestationApplicationId() { + AttestationApplicationId attestationApplicationId = + AttestationApplicationId.createAttestationApplicationId(ATTESTATION_APPLICATION_ID); + assertThat(attestationApplicationId).isEqualTo(EXPECTED_ATTESTATION_APPLICATION_ID); + } + + @Test + public void testCreateEmptyAttestationApplicationIdFromEmptyOrInvalidInput() { + assertThat(AttestationApplicationId.createAttestationApplicationId(null)).isNull(); + assertThat( + AttestationApplicationId.createAttestationApplicationId( + new DEROctetString("Invalid DEROctet String".getBytes(UTF_8)))) + .isNull(); + } + + @Test + public void testEquals() { + AttestationApplicationId attestationApplicationId = + AttestationApplicationId.createAttestationApplicationId(ATTESTATION_APPLICATION_ID); + AttestationApplicationId emptyAttestationApplicationId = + new AttestationApplicationId(ImmutableList.of(), ImmutableList.of()); + + assertThat(attestationApplicationId.equals(EXPECTED_ATTESTATION_APPLICATION_ID)).isTrue(); + assertThat(EXPECTED_ATTESTATION_APPLICATION_ID.equals(attestationApplicationId)).isTrue(); + + assertThat(attestationApplicationId.equals(emptyAttestationApplicationId)).isFalse(); + assertThat(emptyAttestationApplicationId.equals(attestationApplicationId)).isFalse(); + } + + @Test + public void testEqualObjectsHaveEqualHashCodes() { + AttestationApplicationId attestationApplicationId = + AttestationApplicationId.createAttestationApplicationId(ATTESTATION_APPLICATION_ID); + + assertThat(attestationApplicationId.equals(EXPECTED_ATTESTATION_APPLICATION_ID)).isTrue(); + assertThat(attestationApplicationId.hashCode()) + .isEqualTo(EXPECTED_ATTESTATION_APPLICATION_ID.hashCode()); + } +} diff --git a/server/src/test/java/com/google/android/attestation/AuthorizationListTest.java b/server/src/test/java/com/google/android/attestation/AuthorizationListTest.java new file mode 100644 index 0000000..637e8ca --- /dev/null +++ b/server/src/test/java/com/google/android/attestation/AuthorizationListTest.java @@ -0,0 +1,183 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.android.attestation.AuthorizationList.UserAuthType.FINGERPRINT; +import static com.google.android.attestation.AuthorizationList.UserAuthType.PASSWORD; +import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_ANY; +import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_NONE; +import static com.google.android.attestation.AuthorizationList.userAuthTypeToEnum; +import static com.google.android.attestation.Constants.UINT32_MAX; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableSet; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.time.Instant; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.util.encoders.Base64; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link AuthorizationList}. */ +@RunWith(JUnit4.class) +public class AuthorizationListTest { + + // Generated from certificate with RSA Algorithm and StrongBox Security Level + private static final String SW_ENFORCED_EXTENSION_DATA = + "MIIBzb+FPQgCBgFr9iKgzL+FRYIBuwSCAbcwggGzMYIBizAMBAdhbmRyb2lkAgEdMBkEFGNvbS5hbmRyb2lkLmtleWNo" + + "YWluAgEdMBkEFGNvbS5hbmRyb2lkLnNldHRpbmdzAgEdMBkEFGNvbS5xdGkuZGlhZ3NlcnZpY2VzAgEdMBoEFW" + + "NvbS5hbmRyb2lkLmR5bnN5c3RlbQIBHTAdBBhjb20uYW5kcm9pZC5pbnB1dGRldmljZXMCAR0wHwQaY29tLmFu" + + "ZHJvaWQubG9jYWx0cmFuc3BvcnQCAR0wHwQaY29tLmFuZHJvaWQubG9jYXRpb24uZnVzZWQCAR0wHwQaY29tLm" + + "FuZHJvaWQuc2VydmVyLnRlbGVjb20CAR0wIAQbY29tLmFuZHJvaWQud2FsbHBhcGVyYmFja3VwAgEdMCEEHGNv" + + "bS5nb29nbGUuU1NSZXN0YXJ0RGV0ZWN0b3ICAR0wIgQdY29tLmdvb2dsZS5hbmRyb2lkLmhpZGRlbm1lbnUCAQ" + + "EwIwQeY29tLmFuZHJvaWQucHJvdmlkZXJzLnNldHRpbmdzAgEdMSIEIDAao8sIETRQHEXxQiq8ZsJCJP1d7V/c" + + "jxfmlxdv2Gaq"; + private static final String TEE_ENFORCED_EXTENSION_DATA = + "MIGwoQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBK" + + "BCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgco2xJ08fHPFXHeQ4CwSKVUrEo4Dnb1" + + "NVCDUpCEqTeAG/hUEDAgEAv4VCBQIDAxSzv4VOBgIEATQV8b+FTwYCBAE0Few="; + private static final int ATTESTATION_VERSION = 3; + + // Some enum values, complete list can be found at: + // https://source.android.com/security/keystore/tags + private static final int PURPOSE_SIGN = 2; + private static final int PURPOSE_VERIFY = 3; + private static final int ALGORITHM_RSA = 1; + private static final int DIGEST_SHA_2_256 = 4; + private static final int PADDING_RSA_PSS = 3; + private static final int PADDING_RSA_1_5_SIGN = 5; + private static final int ORIGIN_GENERATED = 0; + + // 2019-07-15T14:56:32.972Z + private static final Instant EXPECTED_SW_CREATION_DATETIME = Instant.ofEpochMilli(1563202592972L); + private static final byte[] EXPECTED_SW_ATTESTATION_APPLICATION_ID_BYTES = + Base64.decode( + "MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXljaGFpbgIBHTAZBBRjb20uYW5kcm9pZC5z" + + "ZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNlcwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW" + + "0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZpY2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNw" + + "b3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxvY2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci" + + "50ZWxlY29tAgEdMCAEG2NvbS5hbmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNT" + + "UmVzdGFydERldGVjdG9yAgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS" + + "5hbmRyb2lkLnByb3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcX" + + "b9hmqg=="); + private static final ImmutableSet<Integer> EXPECTED_TEE_PURPOSE = + ImmutableSet.of(PURPOSE_SIGN, PURPOSE_VERIFY); + private static final Integer EXPECTED_TEE_ALGORITHM = ALGORITHM_RSA; + private static final Integer EXPECTED_TEE_KEY_SIZE = 2048; + private static final ImmutableSet<Integer> EXPECTED_TEE_DIGEST = + ImmutableSet.of(DIGEST_SHA_2_256); + private static final ImmutableSet<Integer> EXPECTED_TEE_PADDING = + ImmutableSet.of(PADDING_RSA_PSS, PADDING_RSA_1_5_SIGN); + private static final Long EXPECTED_TEE_RSA_PUBLIC_COMPONENT = 65537L; + private static final Integer EXPECTED_TEE_ORIGIN = ORIGIN_GENERATED; + private static final Integer EXPECTED_TEE_OS_VERSION = 0; + private static final Integer EXPECTED_TEE_OS_PATCH_LEVEL = 201907; + private static final Integer EXPECTED_TEE_VENDOR_PATCH_LEVEL = 20190705; + private static final Integer EXPECTED_TEE_BOOT_PATCH_LEVEL = 20190700; + + private static ASN1Encodable[] getEncodableAuthorizationList(String extensionData) + throws IOException { + byte[] extensionDataBytes = Base64.decode(extensionData); + return ((ASN1Sequence) ASN1Sequence.fromByteArray(extensionDataBytes)).toArray(); + } + + @Test + public void testCanParseAuthorizationListFromSwEnforced() throws IOException { + AuthorizationList authorizationList = + AuthorizationList.createAuthorizationList( + getEncodableAuthorizationList(SW_ENFORCED_EXTENSION_DATA), ATTESTATION_VERSION); + + assertThat(authorizationList.creationDateTime).hasValue(EXPECTED_SW_CREATION_DATETIME); + assertThat(authorizationList.rootOfTrust).isEmpty(); + assertThat(authorizationList.attestationApplicationId).isPresent(); + assertThat(authorizationList.attestationApplicationIdBytes) + .hasValue(EXPECTED_SW_ATTESTATION_APPLICATION_ID_BYTES); + assertThat(authorizationList.individualAttestation).isFalse(); + } + + @Test + public void testCanParseAuthorizationListFromTeeEnforced() throws IOException { + AuthorizationList authorizationList = + AuthorizationList.createAuthorizationList( + getEncodableAuthorizationList(TEE_ENFORCED_EXTENSION_DATA), ATTESTATION_VERSION); + + assertThat(authorizationList.purpose).hasValue(EXPECTED_TEE_PURPOSE); + assertThat(authorizationList.algorithm).hasValue(EXPECTED_TEE_ALGORITHM); + assertThat(authorizationList.keySize).hasValue(EXPECTED_TEE_KEY_SIZE); + assertThat(authorizationList.digest).hasValue(EXPECTED_TEE_DIGEST); + assertThat(authorizationList.padding).hasValue(EXPECTED_TEE_PADDING); + assertThat(authorizationList.rsaPublicExponent).hasValue(EXPECTED_TEE_RSA_PUBLIC_COMPONENT); + assertThat(authorizationList.noAuthRequired).isTrue(); + assertThat(authorizationList.origin).hasValue(EXPECTED_TEE_ORIGIN); + assertThat(authorizationList.rootOfTrust).isPresent(); + assertThat(authorizationList.osVersion).hasValue(EXPECTED_TEE_OS_VERSION); + assertThat(authorizationList.osPatchLevel).hasValue(EXPECTED_TEE_OS_PATCH_LEVEL); + assertThat(authorizationList.vendorPatchLevel).hasValue(EXPECTED_TEE_VENDOR_PATCH_LEVEL); + assertThat(authorizationList.bootPatchLevel).hasValue(EXPECTED_TEE_BOOT_PATCH_LEVEL); + assertThat(authorizationList.individualAttestation).isFalse(); + } + + @Test + public void testUserAuthTypeToEnum() { + assertThat(userAuthTypeToEnum(0L)).isEqualTo(Set.of(USER_AUTH_TYPE_NONE)); + assertThat(userAuthTypeToEnum(1L)).isEqualTo(Set.of(PASSWORD)); + assertThat(userAuthTypeToEnum(2L)).isEqualTo(Set.of(FINGERPRINT)); + assertThat(userAuthTypeToEnum(3L)).isEqualTo(Set.of(PASSWORD, FINGERPRINT)); + assertThat(userAuthTypeToEnum(UINT32_MAX)).isEqualTo(Set.of(PASSWORD, FINGERPRINT, USER_AUTH_TYPE_ANY)); + + + try { + userAuthTypeToEnum(4L); + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().contains("Invalid User Auth Type."); + } + } + + private static final String EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION = + "MIH0oQgxBgIBAgIBA6IDAgEBowQCAggApQUxAwIBBKYIMQYCAQMCAQW/gUgFAgMBAAG/g3cCBQC/hT4DAgEAv4VATDBK" + + "BCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQgEvR7Lf1t9nD6P2qyUmgiQ0mG+RixYn" + + "glj2TaAMZmHn2/hUEFAgMBrbC/hUIFAgMDFRi/hUYIBAZnb29nbGW/hUcHBAVzYXJnb7+FSAcEBXNhcmdvv4VM" + + "CAQGR29vZ2xlv4VNCgQIUGl4ZWwgM2G/hU4GAgQBND1lv4VPBgIEATQ9Zb+FUAIFAA=="; + + @Test + public void testCanParseIndividualAttestation() throws IOException { + AuthorizationList authorizationList = + AuthorizationList.createAuthorizationList( + getEncodableAuthorizationList(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION), + ATTESTATION_VERSION); + + assertThat(authorizationList.individualAttestation).isTrue(); + } + + @Test + public void testCreateAndParse() throws IOException { + AuthorizationList authorizationList = + AuthorizationList.createAuthorizationList( + getEncodableAuthorizationList(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION), + ATTESTATION_VERSION); + ASN1Sequence seq = authorizationList.toAsn1Sequence(); + assertThat(seq.getEncoded("DER")) + .isEqualTo(Base64.decode(EXTENTION_DATA_WITH_INDIVIDUAL_ATTESTATION)); + } +} diff --git a/server/src/test/java/com/google/android/attestation/BUILD b/server/src/test/java/com/google/android/attestation/BUILD new file mode 100644 index 0000000..680103e --- /dev/null +++ b/server/src/test/java/com/google/android/attestation/BUILD @@ -0,0 +1,47 @@ +java_test( + name = "AttestationApplicationIdTest", + srcs = ["AttestationApplicationIdTest.java"], + deps = [ + "//server/src/main/java/com/google/android/attestation", + "@maven//:com_google_truth_extensions_truth_java8_extension", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_bouncycastle_bcprov_jdk15on", + ], +) + +java_test( + name = "AuthorizationListTest", + srcs = ["AuthorizationListTest.java"], + deps = [ + "//server/src/main/java/com/google/android/attestation", + "@maven//:com_google_truth_extensions_truth_java8_extension", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_bouncycastle_bcprov_jdk15on", + ], +) + +java_test( + name = "ParsedAttestationRecordTest", + srcs = ["ParsedAttestationRecordTest.java"], + deps = [ + "//server/src/main/java/com/google/android/attestation", + "@maven//:com_google_truth_extensions_truth_java8_extension", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_bouncycastle_bcprov_jdk15on", + ], +) + +java_test( + name = "RootOfTrustTest", + srcs = ["RootOfTrustTest.java"], + deps = [ + "//server/src/main/java/com/google/android/attestation", + "@maven//:com_google_truth_extensions_truth_java8_extension", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + "@maven//:org_bouncycastle_bcprov_jdk15on", + ], +) diff --git a/server/src/test/java/com/google/android/attestation/CertificateRevocationStatusTest.java b/server/src/test/java/com/google/android/attestation/CertificateRevocationStatusTest.java new file mode 100644 index 0000000..fdeee19 --- /dev/null +++ b/server/src/test/java/com/google/android/attestation/CertificateRevocationStatusTest.java @@ -0,0 +1,103 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.android.attestation.CertificateRevocationStatus.Reason; +import com.google.android.attestation.CertificateRevocationStatus.Status; +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.HashSet; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test for {@link CertificateRevocationStatus}. + */ +@RunWith(JUnit4.class) +public final class CertificateRevocationStatusTest { + + private static final String TEST_STATUS_LIST_PATH = "src/test/resources/status.json"; + + // Certificate generated by TestDPC with RSA Algorithm and StrongBox Security Level + private static final String TEST_CERT = + "-----BEGIN CERTIFICATE-----\n" + + "MIIB8zCCAXqgAwIBAgIRAMxm6ak3E7bmQ7JsFYeXhvcwCgYIKoZIzj0EAwIwOTEM" + + "MAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA0ZjdlYzg1N2U4MDU3NDdjMWIxZWRhYWVm" + + "ODk1NDk2ZDAeFw0xOTA4MTQxOTU0MTBaFw0yOTA4MTExOTU0MTBaMDkxDDAKBgNV" + + "BAwMA1RFRTEpMCcGA1UEBRMgMzJmYmJiNmRiOGM5MTdmMDdhYzlhYjZhZTQ4MTAz" + + "YWEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQzg+sx9lLrkNIZwLYZerzL1bPK" + + "2zi75zFEuuI0fIr35DJND1B4Z8RPZ3djzo3FOdAObqvoZ4CZVxcY3iQ1ffMMo2Mw" + + "YTAdBgNVHQ4EFgQUzZOUqhJOO7wttSe9hYemjceVsgIwHwYDVR0jBBgwFoAUWlnI" + + "9iPzasns60heYXIP+h+Hz8owDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC" + + "AgQwCgYIKoZIzj0EAwIDZwAwZAIwUFz/AKheCOPaBiRGDk7LaSEDXVYmTr0VoU8T" + + "bIqrKGWiiMwsGEmW+Jdo8EcKVPIwAjAoO7n1ruFh+6mEaTAukc6T5BW4MnmYadkk" + + "FSIjzDAaJ6lAq+nmmGQ1KlZpqi4Z/VI=\n" + + "-----END CERTIFICATE-----"; + + @Test + public void loadTestSerial() throws Exception { + CertificateFactory factory = CertificateFactory.getInstance("X509"); + X509Certificate cert = + (X509Certificate) + factory.generateCertificate(new ByteArrayInputStream(TEST_CERT.getBytes(UTF_8))); + BigInteger serialNumber = cert.getSerialNumber(); + CertificateRevocationStatus statusEntry = CertificateRevocationStatus + .loadStatusFromFile(serialNumber, TEST_STATUS_LIST_PATH); + assertThat(statusEntry).isNotNull(); + assertThat(statusEntry.status).isEqualTo(Status.SUSPENDED); + assertThat(statusEntry.reason).isEqualTo(Reason.KEY_COMPROMISE); + } + + @Test + public void loadBadSerial() throws Exception { + assertThat(CertificateRevocationStatus.loadStatusFromFile("badbeef", TEST_STATUS_LIST_PATH)) + .isNull(); + assertThat(CertificateRevocationStatus.loadStatusFromFile(BigInteger.valueOf(0xbadbeef), TEST_STATUS_LIST_PATH)) + .isNull(); + } + + @Test + public void loadAllTestEntries() throws Exception { + HashSet<String> allTestSerialNumbers = new HashSet<>(); + allTestSerialNumbers.add("6681152659205225093"); + allTestSerialNumbers.add("8350192447815228107"); + allTestSerialNumbers.add("9408173275444922801"); + allTestSerialNumbers.add("11244410301401252959"); + allTestSerialNumbers.add("cc66e9a93713b6e643b26c15879786f7"); + + HashMap<String, CertificateRevocationStatus> statusMap = + CertificateRevocationStatus.loadAllEntriesFromFile(TEST_STATUS_LIST_PATH); + + assertThat(statusMap.keySet()).isEqualTo(allTestSerialNumbers); + assertThat(statusMap.get("8350192447815228107").status) + .isEqualTo(CertificateRevocationStatus.Status.REVOKED); + assertThat(statusMap.get("8350192447815228107").reason) + .isEqualTo(CertificateRevocationStatus.Reason.KEY_COMPROMISE); + assertThat(statusMap.get("cc66e9a93713b6e643b26c15879786f7").status) + .isEqualTo(CertificateRevocationStatus.Status.SUSPENDED); + assertThat(statusMap.get("cc66e9a93713b6e643b26c15879786f7").reason) + .isEqualTo(CertificateRevocationStatus.Reason.KEY_COMPROMISE); + assertThat(statusMap.get("cc66e9a93713b6e643b26c15879786f7").comment) + .isEqualTo("Entry for testing only"); + } +} diff --git a/server/src/test/java/com/google/android/attestation/ParsedAttestationRecordTest.java b/server/src/test/java/com/google/android/attestation/ParsedAttestationRecordTest.java new file mode 100644 index 0000000..678ea9a --- /dev/null +++ b/server/src/test/java/com/google/android/attestation/ParsedAttestationRecordTest.java @@ -0,0 +1,136 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.android.attestation.AuthorizationList.UserAuthType; +import com.google.android.attestation.ParsedAttestationRecord.SecurityLevel; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Set; +import org.bouncycastle.asn1.ASN1Sequence; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link ParsedAttestationRecord}. */ +@RunWith(JUnit4.class) +public class ParsedAttestationRecordTest { + + // Certificate generated by TestDPC with RSA Algorithm and StrongBox Security Level + private static final String CERT = + "-----BEGIN CERTIFICATE-----\n" + + "MIIGCDCCBHCgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQx" + + "MzI2MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAb" + + "BgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC" + + "AQEApNVcnyN40MANMbbo2nMGNq2NNysDSjfLm0W3i6wPKf0ffCYkhWM4dCmQKKf50uAZTBeTit4c" + + "NwXeZn3qellMlOsIN3Qc384rfN/8cikrRvUAgibz0Jy7STykjwa7x6tKwqITxbO8HqAhKo8/BQXU" + + "xzrOdIg5ciy+UM7Vgh7a7ogen0KL2iGgrsalb1ti7Vlzb6vIJ4WzIC3TGD2sCkoPahghwqFDZZCo" + + "/FzaLoNY0jAUX2mL+kf8aUaoxz7xA9FTvgara+1pLBR1s4c8xPS2HdZipcVXWfey0wujv1VAKs4+" + + "tXjKlHkYBHBBceEjxUtEmrapSQEdpHPv7Xh9Uanq4QIDAQABo4ICwTCCAr0wDgYDVR0PAQH/BAQD" + + "AgeAMIICqQYKKwYBBAHWeQIBEQSCApkwggKVAgEDCgEBAgEECgEBBANhYmMEADCCAc2/hT0IAgYB" + + "ZOYGEYe/hUWCAbsEggG3MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXlj" + + "aGFpbgIBHTAZBBRjb20uYW5kcm9pZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNl" + + "cwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZp" + + "Y2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxv" + + "Y2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5h" + + "bmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9y" + + "AgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnBy" + + "b3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqjCB" + + "rqEIMQYCAQICAQOiAwIBAaMEAgIIAKUFMQMCAQSmCDEGAgEDAgEFv4FIBQIDAQABv4N3AgUAv4U+" + + "AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIHKNsSdP" + + "HxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgUCAwMUs7+FTwUC" + + "AwMUszANBgkqhkiG9w0BAQsFAAOCAYEAJMIuzdNUdfrE6sIdmsnMn/scSG2odbphj8FkX9JGdF2S" + + "OT599HuDY9qhvkru2Dza4sLKK3f4ViBhuR9lpfeprKvstxbtBO7jkLYfVn0ZRzHRHVEyiW5IVKh+" + + "qOXVJ9S1lMShOTlsaYJytLKIlcrRAZBEXZiNbzTuVh1CH6X9Ni1dog14snm+lcOeORdL9fht2CHa" + + "u/caRnpWiZbjoAoJp0O89uBrRkXPpln51+3jPY6AFny30grNAvKguauDcPPhNV1yR+ylSsQi2gm3" + + "Rs4pgtlxFLMfZLgT0cbkl+9zk/QUqlpBP8ftUBsOI0ARr8xhFN3cvq9kXGLtJ9hEP9PRaflAFREk" + + "DK3IBIbVcAFZBFoAQOdE9zy0+F5bQrznPGaZg4Dzhcx33qMDUTgHtWoy+k3ePGQMEtmoTTLgQywW" + + "OIkXEoFqqGi9GKJXUT1KYi5NsigaYqu7FoN4Qsvs61pMUEfZSPP2AFwkA8uNFbmb9uxcxaGHCA8i" + + "3i9VM6yOLIrP\n" + + "-----END CERTIFICATE-----"; + + private static final int EXPECTED_ATTESTATION_VERSION = 3; + private static final SecurityLevel EXPECTED_ATTESTATION_SECURITY_LEVEL = + SecurityLevel.TRUSTED_ENVIRONMENT; + private static final int EXPECTED_KEYMASTER_VERSION = 4; + private static final SecurityLevel EXPECTED_KEYMASTER_SECURITY_LEVEL = + SecurityLevel.TRUSTED_ENVIRONMENT; + private static final byte[] EXPECTED_ATTESTATION_CHALLENGE = "abc".getBytes(UTF_8); + private static final byte[] EXPECTED_UNIQUE_ID = "".getBytes(UTF_8); + + private static X509Certificate getAttestationRecord(String certStr) throws CertificateException { + CertificateFactory factory = CertificateFactory.getInstance("X509"); + X509Certificate cert = + (X509Certificate) + factory.generateCertificate(new ByteArrayInputStream(certStr.getBytes(UTF_8))); + cert.checkValidity(); + return cert; + } + + @Test + public void testParseAttestationRecord() throws CertificateException, IOException { + X509Certificate x509Certificate = getAttestationRecord(CERT); + ParsedAttestationRecord attestationRecord = + ParsedAttestationRecord.createParsedAttestationRecord(x509Certificate); + + assertThat(attestationRecord.attestationVersion).isEqualTo(EXPECTED_ATTESTATION_VERSION); + assertThat(attestationRecord.attestationSecurityLevel) + .isEqualTo(EXPECTED_ATTESTATION_SECURITY_LEVEL); + assertThat(attestationRecord.keymasterVersion).isEqualTo(EXPECTED_KEYMASTER_VERSION); + assertThat(attestationRecord.keymasterSecurityLevel) + .isEqualTo(EXPECTED_KEYMASTER_SECURITY_LEVEL); + assertThat(attestationRecord.attestationChallenge).isEqualTo(EXPECTED_ATTESTATION_CHALLENGE); + assertThat(attestationRecord.uniqueId).isEqualTo(EXPECTED_UNIQUE_ID); + assertThat(attestationRecord.softwareEnforced).isNotNull(); + assertThat(attestationRecord.teeEnforced).isNotNull(); + } + + @Test + public void testCreateAndParseAttestationRecord() { + AuthorizationList.Builder teeEnforcedBuilder = AuthorizationList.builder(); + teeEnforcedBuilder.userAuthType = Set.of(UserAuthType.FINGERPRINT); + teeEnforcedBuilder.attestationIdBrand = "free food".getBytes(UTF_8); + ParsedAttestationRecord expected = + ParsedAttestationRecord.create( + /* attestationVersion= */ 2, + /* attestationSecurityLevel= */ SecurityLevel.TRUSTED_ENVIRONMENT, + /* keymasterVersion= */ 4, + /* keymasterSecurityLevel= */ SecurityLevel.SOFTWARE, + /* attestationChallenge= */ "abc".getBytes(UTF_8), + /* uniqueId= */ "foodplease".getBytes(UTF_8), + /* softwareEnforced= */ AuthorizationList.builder().build(), + /* teeEnforced= */ AuthorizationList.builder() + .setUserAuthType(Set.of(UserAuthType.FINGERPRINT)) + .setAttestationIdBrand("free food".getBytes(UTF_8)).build()); + ASN1Sequence seq = expected.toAsn1Sequence(); + ParsedAttestationRecord actual = ParsedAttestationRecord.create(seq); + assertThat(actual.attestationVersion).isEqualTo(expected.attestationVersion); + assertThat(actual.attestationSecurityLevel).isEqualTo(expected.attestationSecurityLevel); + assertThat(actual.keymasterVersion).isEqualTo(expected.keymasterVersion); + assertThat(actual.keymasterSecurityLevel).isEqualTo(expected.keymasterSecurityLevel); + assertThat(actual.attestationChallenge).isEqualTo(expected.attestationChallenge); + assertThat(actual.uniqueId).isEqualTo(expected.uniqueId); + assertThat(actual.teeEnforced.userAuthType).isEqualTo(expected.teeEnforced.userAuthType); + assertThat(actual.teeEnforced.attestationIdBrand) + .isEqualTo(expected.teeEnforced.attestationIdBrand); + } +} diff --git a/server/src/test/java/com/google/android/attestation/RootOfTrustTest.java b/server/src/test/java/com/google/android/attestation/RootOfTrustTest.java new file mode 100644 index 0000000..3f952a6 --- /dev/null +++ b/server/src/test/java/com/google/android/attestation/RootOfTrustTest.java @@ -0,0 +1,68 @@ +/* Copyright 2019, The Android Open Source Project, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.attestation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.android.attestation.RootOfTrust.VerifiedBootState; +import java.io.IOException; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.util.encoders.Base64; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link RootOfTrust}. */ +@RunWith(JUnit4.class) +public class RootOfTrustTest { + + // Generated from certificate with EC Algorithm and StrongBox Security Level + private static final String ROOT_OF_TRUST = + "MEoEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEACgECBCByjbEnTx8c8Vcd5DgLBIpVSsSjgOdvU1UI" + + "NSkISpN4AQ==\n"; + private static final int ATTESTATION_VERSION = 3; + + private static final byte[] EXPECTED_VERIFIED_BOOT_KEY = + Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + private static final boolean EXPECTED_DEVICE_LOCKED = false; + private static final VerifiedBootState EXPECTED_VERIFIED_BOOT_STATE = + VerifiedBootState.UNVERIFIED; + private static final byte[] EXPECTED_VERIFIED_BOOT_HASH = + Base64.decode("co2xJ08fHPFXHeQ4CwSKVUrEo4Dnb1NVCDUpCEqTeAE="); + + private static ASN1Sequence getRootOfTrustSequence(String rootOfTrustB64) throws IOException { + byte[] rootOfTrustBytes = Base64.decode(rootOfTrustB64); + return (ASN1Sequence) ASN1Sequence.fromByteArray(rootOfTrustBytes); + } + + @Test + public void testCreateRootOfTrust() throws IOException { + ASN1Sequence rootOfTrustSequence = getRootOfTrustSequence(ROOT_OF_TRUST); + RootOfTrust rootOfTrust = + RootOfTrust.createRootOfTrust(rootOfTrustSequence, ATTESTATION_VERSION); + + assertThat(rootOfTrust).isNotNull(); + assertThat(rootOfTrust.verifiedBootKey).isEqualTo(EXPECTED_VERIFIED_BOOT_KEY); + assertThat(rootOfTrust.deviceLocked).isEqualTo(EXPECTED_DEVICE_LOCKED); + assertThat(rootOfTrust.verifiedBootState).isEqualTo(EXPECTED_VERIFIED_BOOT_STATE); + assertThat(rootOfTrust.verifiedBootHash).isEqualTo(EXPECTED_VERIFIED_BOOT_HASH); + } + + @Test + public void testCreateEmptyRootOfTrust() { + assertThat(RootOfTrust.createRootOfTrust(null, ATTESTATION_VERSION)).isNull(); + } +} diff --git a/server/src/test/resources/status.json b/server/src/test/resources/status.json new file mode 100644 index 0000000..74db10c --- /dev/null +++ b/server/src/test/resources/status.json @@ -0,0 +1,25 @@ +{ + "entries": { + "6681152659205225093" : { + "status": "REVOKED", + "reason": "KEY_COMPROMISE" + }, + "8350192447815228107" : { + "status": "REVOKED", + "reason": "KEY_COMPROMISE" + }, + "9408173275444922801" : { + "status": "REVOKED", + "reason": "KEY_COMPROMISE" + }, + "11244410301401252959" : { + "status": "REVOKED", + "reason": "KEY_COMPROMISE" + }, + "cc66e9a93713b6e643b26c15879786f7" : { + "status": "SUSPENDED", + "reason": "KEY_COMPROMISE", + "comment": "Entry for testing only" + } + } +} |