From f7ecb0dfad2cc15d42eacefb13e8675dd664dd38 Mon Sep 17 00:00:00 2001 From: Emilia Kasper Date: Fri, 14 Oct 2016 15:58:23 +0200 Subject: [PATCH] Initial GitHub export --- .gitignore | 1 + BUILD | 106 ++ CONTRIBUTING | 43 + LICENSE | 202 +++ README.md | 140 +- WORKSPACE | 99 ++ build_defs.bzl | 119 ++ doc/bugs.md | 5 + doc/dh.md | 141 +++ doc/dsa.md | 192 +++ doc/ecdh.md | 58 + doc/index.md | 80 ++ doc/rsa.md | 130 ++ .../wycheproof/BouncyCastleAllTests.java | 53 + .../security/wycheproof/BouncyCastleTest.java | 55 + .../security/wycheproof/ConscryptTest.java | 51 + .../google/security/wycheproof/EcUtil.java | 454 +++++++ .../security/wycheproof/OpenJDKAllTests.java | 34 + .../security/wycheproof/OpenJDKTest.java | 36 + .../wycheproof/ProviderIndependentTest.java | 26 + .../security/wycheproof/RandomUtil.java | 185 +++ .../wycheproof/SpongyCastleAllTests.java | 54 + .../security/wycheproof/SpongyCastleTest.java | 56 + .../google/security/wycheproof/TestUtil.java | 99 ++ .../security/wycheproof/WycheproofRunner.java | 206 +++ .../wycheproof/testcases/AesEaxTest.java | 268 ++++ .../wycheproof/testcases/AesGcmTest.java | 405 ++++++ .../wycheproof/testcases/BasicTest.java | 45 + .../wycheproof/testcases/BigIntegerTest.java | 461 +++++++ .../testcases/CipherInputStreamTest.java | 279 ++++ .../testcases/CipherOutputStreamTest.java | 239 ++++ .../security/wycheproof/testcases/DhTest.java | 396 ++++++ .../wycheproof/testcases/DhiesTest.java | 157 +++ .../wycheproof/testcases/DsaTest.java | 655 ++++++++++ .../wycheproof/testcases/EcKeyTest.java | 230 ++++ .../wycheproof/testcases/EcdhTest.java | 1125 +++++++++++++++++ .../wycheproof/testcases/EcdsaTest.java | 462 +++++++ .../wycheproof/testcases/EciesTest.java | 347 +++++ .../testcases/RsaEncryptionTest.java | 138 ++ .../wycheproof/testcases/RsaKeyTest.java | 139 ++ .../testcases/RsaSignatureTest.java | 140 ++ 41 files changed, 8110 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 BUILD create mode 100644 CONTRIBUTING create mode 100644 LICENSE create mode 100644 WORKSPACE create mode 100644 build_defs.bzl create mode 100644 doc/bugs.md create mode 100644 doc/dh.md create mode 100644 doc/dsa.md create mode 100644 doc/ecdh.md create mode 100644 doc/index.md create mode 100644 doc/rsa.md create mode 100644 java/com/google/security/wycheproof/BouncyCastleAllTests.java create mode 100644 java/com/google/security/wycheproof/BouncyCastleTest.java create mode 100644 java/com/google/security/wycheproof/ConscryptTest.java create mode 100644 java/com/google/security/wycheproof/EcUtil.java create mode 100644 java/com/google/security/wycheproof/OpenJDKAllTests.java create mode 100644 java/com/google/security/wycheproof/OpenJDKTest.java create mode 100644 java/com/google/security/wycheproof/ProviderIndependentTest.java create mode 100644 java/com/google/security/wycheproof/RandomUtil.java create mode 100644 java/com/google/security/wycheproof/SpongyCastleAllTests.java create mode 100644 java/com/google/security/wycheproof/SpongyCastleTest.java create mode 100644 java/com/google/security/wycheproof/TestUtil.java create mode 100644 java/com/google/security/wycheproof/WycheproofRunner.java create mode 100644 java/com/google/security/wycheproof/testcases/AesEaxTest.java create mode 100644 java/com/google/security/wycheproof/testcases/AesGcmTest.java create mode 100644 java/com/google/security/wycheproof/testcases/BasicTest.java create mode 100644 java/com/google/security/wycheproof/testcases/BigIntegerTest.java create mode 100644 java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java create mode 100644 java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java create mode 100644 java/com/google/security/wycheproof/testcases/DhTest.java create mode 100644 java/com/google/security/wycheproof/testcases/DhiesTest.java create mode 100644 java/com/google/security/wycheproof/testcases/DsaTest.java create mode 100644 java/com/google/security/wycheproof/testcases/EcKeyTest.java create mode 100644 java/com/google/security/wycheproof/testcases/EcdhTest.java create mode 100644 java/com/google/security/wycheproof/testcases/EcdsaTest.java create mode 100644 java/com/google/security/wycheproof/testcases/EciesTest.java create mode 100644 java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java create mode 100644 java/com/google/security/wycheproof/testcases/RsaKeyTest.java create mode 100644 java/com/google/security/wycheproof/testcases/RsaSignatureTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac51a05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bazel-* diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..a47d747 --- /dev/null +++ b/BUILD @@ -0,0 +1,106 @@ +# Wycheproof tests + +java_library( + name = "utils", + srcs = [ + "java/com/google/security/wycheproof/EcUtil.java", + "java/com/google/security/wycheproof/RandomUtil.java", + "java/com/google/security/wycheproof/TestUtil.java", + ], +) + +common_deps = [ + ":utils", +] + +test_srcs = glob(["java/com/google/security/wycheproof/testcases/*.java"]) + ["java/com/google/security/wycheproof/WycheproofRunner.java"] + +# These targets run all tests. + +load(":build_defs.bzl", "bouncycastle_all_tests", "spongycastle_all_tests") + +# Generates BouncyCastleAllTests_1_xx target for all available versions, +# plus a BouncyCastleAllTests alias for latest stable. +# +# To test latest stable: +# $ bazel test BouncyCastleAllTests +# +# To test other versions, e.g., v1.52: +# $ bazel test BouncyCastleAllTests_1_52 +# +# To test all known versions (warning, will take a long time): +# $ bazel test BouncyCastleAllTest_* +bouncycastle_all_tests( + # This test takes a long time, because key generation for DSA and DH generate new parameters. + size = "large", + srcs = ["java/com/google/security/wycheproof/BouncyCastleAllTests.java"] + test_srcs, + test_class = "com.google.security.wycheproof.BouncyCastleAllTests", + deps = common_deps, +) + +# Generates SpongyCastleAllTests_1_xx target for all available versions, +# plus a SpongyCastleAllTests alias for latest stable. +# +# To test latest stable: +# $ bazel test SpongyCastleAllTests +# +# To test other versions, e.g., v1.52.0.0: +# $ bazel test SpongyCastleAllTests_1_52 +# +# To test all known versions (warning, will take a long time): +# $ bazel test SpongyCastleAllTests_* +spongycastle_all_tests( + # This test takes a long time, because key generation for DSA and DH generate new parameters. + size = "large", + srcs = ["java/com/google/security/wycheproof/SpongyCastleAllTests.java"] + test_srcs, + test_class = "com.google.security.wycheproof.SpongyCastleAllTests", + deps = common_deps, +) + +# These targets exclude slow tests. + +load(":build_defs.bzl", "bouncycastle_tests", "spongycastle_tests") + +# Generates BouncyCastleTest_1_xx target for all available versions, +# plus a BouncyCastleTest alias for latest stable. +# +# To test latest stable: +# $ bazel test BouncyCastleTest +# +# To test other versions, e.g., v1.52: +# $ bazel test BouncyCastleTest_1_52 +# +# To test all known versions: +# $ bazel test BouncyCastleTest_* +bouncycastle_tests( + size = "large", + srcs = ["java/com/google/security/wycheproof/BouncyCastleTest.java"] + test_srcs, + test_class = "com.google.security.wycheproof.BouncyCastleTest", + deps = common_deps, +) + +# Generates SpongyCastleTest_1_xx target for all available versions, +# plus a SpongyCastleTest alias for latest stable. +# +# To test latest stable: +# $ bazel test SpongyCastleTest +# +# To test other versions, e.g., v1.52.0.0: +# $ bazel test SpongyCastleTest_1_52 +# +# To test all known versions: +# $ bazel test SpongyCastleTest_* +spongycastle_tests( + size = "large", + srcs = ["java/com/google/security/wycheproof/SpongyCastleTest.java"] + test_srcs, + test_class = "com.google.security.wycheproof.SpongyCastleTest", + deps = common_deps, +) + +# Platform-independent tests +java_test( + name = "ProviderIndependentTest", + size = "small", + srcs = ["java/com/google/security/wycheproof/ProviderIndependentTest.java"] + test_srcs, + deps = common_deps, +) diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 0000000..fa97ad3 --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,43 @@ +Want to contribute? Great! First, read this page (including the small print at +the end). + +### Before you contribute +Before we can use your code, you must sign the +[Google Individual Contributor License Agreement] +(https://cla.developers.google.com/about/google-individual) +(CLA), which you can do online. The CLA is necessary mainly because you own the +copyright to your changes, even after your contribution becomes part of our +codebase, so we need your permission to use and distribute your code. We also +need to be sure of various other things—for instance that you'll tell us if you +know that your code infringes on other people's patents. You don't have to sign +the CLA until after you've submitted your code for review and a member has +approved it, but you must do it before we can put your code into our codebase. +Before you start working on a larger contribution, you should get in touch with +us first through the issue tracker with your idea so that we can help out and +possibly guide you. Coordinating up front makes it much easier to avoid +frustration later on. + +### Disclosure +If your tests uncover security vulnerabilities, please first report directly to +the maintainers of the libraries. You should only submit tests to us once the +bugs have been acknowledged or fixed. + +Google has several +[security reward programs](https://www.google.com/about/appsecurity/programs-home/) +that provide cash rewards for quality security research that identifies or fixes +security vulnerabilities in products that we provide or proactive security +improvements to select open-source products. If your tests found or helped fix +vulnerabilities that are in scope you should submit them to these programs. + +If you have any question with regard to disclosure, please email us at +security@google.com. + +### Code reviews +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. + +### The small print +Contributions made by corporations are covered by a different agreement than +the one above, the +[Software Grant and Corporate Contributor License Agreement] +(https://cla.developers.google.com/about/google-corporate). \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + 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: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) 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 + + (d) 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. \ No newline at end of file diff --git a/README.md b/README.md index 8ae5d14..b27f45e 100644 --- a/README.md +++ b/README.md @@ -1 +1,139 @@ -Placeholder to init branch +# Project Wycheproof +https://github.com/google/wycheproof + +*Project Wycheproof is named after +[Mount Wycheproof](https://en.wikipedia.org/wiki/Mount_Wycheproof), the smallest +mountain in the world. The main motivation for the project is to have a goal +that is achievable. The smaller the mountain the more likely it is to be able to +climb it.* + +## Introduction + +Project Wycheproof tests crypto libraries against known attacks. It is developed +and maintained by members of Google Security Team, but it is not an official +Google product. + +At Google, we rely on many third party cryptographic software libraries. +Unfortunately, in cryptography, subtle mistakes can have catastrophic +consequences, and we found that libraries fall into such implementation +pitfalls much too often and for much too long. Good implementation guidelines, +however, are hard to come by: understanding how to implement cryptography +securely requires digesting decades' worth of academic literature. We recognize +that software engineers fix and prevent bugs with unit testing, and we found +that cryptographic loopholes can be resolved by the same means. + +These observations have prompted us to develop Project Wycheproof, a collection +of unit tests that detect known weaknesses or check for expected behaviors of +some cryptographic algorithm. Project Wycheproof provides tests for most +cryptographic algorithms, including RSA, elliptic curve crypto and +authenticated encryption. Our cryptographers have systematically surveyed the +literature and implemented most known attacks. We have over 80 test cases which +have uncovered more than [40 bugs](doc/bugs.md). For +example, we found that we could recover the private key of widely-used DSA and +ECDHC implementations. + +While we are committed to develop as many attacks as possible, Project +Wycheproof is by no means complete. Passing the tests does not imply that the +library is secure, it just means that it is not vulnerable to the attacks that +Project Wycheproof tests for. Cryptographers are also constantly discovering +new attacks. Nevertheless, with Project Wycheproof developers and users now can +check their libraries against a large number of known attacks, without having +to spend years reading academic papers or become cryptographers themselves. + +For more information on the goals and strategies of Project Wycheproof, please +check out our [doc](doc/). + +### Coverage + +Project Wycheproof has tests for the most popular crypto algorithms, including + + - AES-EAX + - AES-GCM + - [DH](doc/dh.md) + - DHIES + - [DSA](doc/dsa.md) + - [ECDH](doc/ecdh.md) + - ECDSA + - ECIES + - [RSA](doc/rsa.md) + +The tests detect whether a library is vulnerable to many attacks, including + + - Invalid curve attacks + - Biased nonces in digital signature schemes + - Of course, all Bleichenbacher’s attacks + - And many more -- we have over 80 test cases + +### Usage + +Our first set of tests are written in Java, because Java has a common +cryptographic interface. This allowed us to test multiple providers with a +single test suite. While this interface is somewhat low level, and should not +be used directly, we still apply a "defense in depth" argument and expect that +the implementations are as robust as possible. For example, we consider weak +default values to be a significant security flaw. We are converting as many +tests into sets of test vectors to simplify porting the tests to other +languages. We provide ready-to-use test runners for Java Cryptography +Architecture providers such as [Bouncy Castle](http://bouncycastle.org), +[Conscrypt](https://conscrypt.org) and the default providers in +[OpenJDK](http://openjdk.java.net/). + +Project Wycheproof uses the [Bazel](https://bazel.build/) build system, you need +to [install](https://bazel.build/versions/master/docs/install.html) Bazel to run +the tests. + +Check out the tests + +``` +git checkout https://github.com/google/wycheproof.git +``` + +To test latest stable version of Bouncy Castle: + +``` +bazel test BouncyCastleAllTests +``` + +To test other versions, e.g., v1.52: + +``` +bazel test BouncyCastleAllTests_1_52 +``` + +To test all known versions (warning, will take a long time): + +``` +bazel test BouncyCastleAllTests_* +``` + +ConscryptAllTests and OpenJDKAllTests run the tests against +[Conscrypt](https://conscrypt.org) and [OpenJDK](http://openjdk.java.net/), +respectively. + +Some of the tests take a very long time to run. If you want to exclude those +tests, use either BouncyCastleTest, ConscryptTest or OpenJDKTest -- these +targets exclude all slow tests (i.e., those annotated with @SlowTest). + +### Hall of Bugs + +Here are some of the notable vulnerabilities that are uncovered by +Project Wycheproof: + + - OpenJDK's SHA1withDSA leaks private keys > 1024 bits + - Test: TODO(thaidn) + - This bug is the same as +[CVE-2003-0971 - GnuPG generated ElGamal signatures that leaked the private key] +(https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2003-0971) + + - Bouncy Castle's ECDHC leaks private keys + - Test: TODO(thaidn) + +### Contact and mailing list + +If you want to contribute, please read [CONTRIBUTING](CONTRIBUTING) and send us +pull requests. You can also report bugs or request new tests. + +If you'd like to talk to our developers or get notified about major new +tests, you may want to subscribe to our +[mailing list](https://groups.google.com/forum/#!forum/wycheproof-users). To +join, simply send an empty mail to wycheproof-users+subscribe@googlegroups.com. diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..800d7ce --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,99 @@ +maven_jar( + name = "bouncycastle_1_46", + artifact = "org.bouncycastle:bcprov-jdk15on:1.46", +) + +maven_jar( + name = "bouncycastle_1_47", + artifact = "org.bouncycastle:bcprov-jdk15on:1.47", +) + +maven_jar( + name = "bouncycastle_1_48", + artifact = "org.bouncycastle:bcprov-jdk15on:1.48", +) + +maven_jar( + name = "bouncycastle_1_49", + artifact = "org.bouncycastle:bcprov-jdk15on:1.49", +) + +maven_jar( + name = "bouncycastle_1_50", + artifact = "org.bouncycastle:bcprov-jdk15on:1.50", +) + +maven_jar( + name = "bouncycastle_1_51", + artifact = "org.bouncycastle:bcprov-jdk15on:1.51", +) + +maven_jar( + name = "bouncycastle_1_52", + artifact = "org.bouncycastle:bcprov-jdk15on:1.52", +) + +maven_jar( + name = "bouncycastle_1_53", + artifact = "org.bouncycastle:bcprov-jdk15on:1.53", +) + +maven_jar( + name = "bouncycastle_1_54", + artifact = "org.bouncycastle:bcprov-jdk15on:1.54", +) + +maven_jar( + name = "bouncycastle_1_55", + artifact = "org.bouncycastle:bcprov-jdk15on:1.55", +) + +maven_jar( + name = "spongycastle_core_1_50", + artifact = "com.madgag.spongycastle:core:1.50.0.0", +) + +maven_jar( + name = "spongycastle_prov_1_50", + artifact = "com.madgag.spongycastle:prov:1.50.0.0", +) + +maven_jar( + name = "spongycastle_core_1_51", + artifact = "com.madgag.spongycastle:core:1.51.0.0", +) + +maven_jar( + name = "spongycastle_prov_1_51", + artifact = "com.madgag.spongycastle:prov:1.51.0.0", +) + +maven_jar( + name = "spongycastle_core_1_52", + artifact = "com.madgag.spongycastle:core:1.52.0.0", +) + +maven_jar( + name = "spongycastle_prov_1_52", + artifact = "com.madgag.spongycastle:prov:1.52.0.0", +) + +maven_jar( + name = "spongycastle_core_1_53", + artifact = "com.madgag.spongycastle:core:1.53.0.0", +) + +maven_jar( + name = "spongycastle_prov_1_53", + artifact = "com.madgag.spongycastle:prov:1.53.0.0", +) + +maven_jar( + name = "spongycastle_core_1_54", + artifact = "com.madgag.spongycastle:core:1.54.0.0", +) + +maven_jar( + name = "spongycastle_prov_1_54", + artifact = "com.madgag.spongycastle:prov:1.54.0.0", +) diff --git a/build_defs.bzl b/build_defs.bzl new file mode 100644 index 0000000..13aa82b --- /dev/null +++ b/build_defs.bzl @@ -0,0 +1,119 @@ +bouncycastle_versions = range(49, 56) + +# These targets run all tests. +def bouncycastle_all_tests(srcs, deps, size, test_class): + """BouncyCastle version-specific tests.""" + + # Generates BouncyCastleAllTests_1_55, ..., BouncyCastleAllTests_1_49 + for version in bouncycastle_versions: + native.java_test( + name = "BouncyCastleAllTests_1_%s" % version, + srcs = srcs, + deps = deps + [ + "@bouncycastle_1_%s//jar" % version, + ], + size = size, + test_class = test_class, + ) + + # Latest stable. + # We can't use native.alias, because aliased tests are not run. + # So, we simply duplicate the test. + native.java_test( + name = "BouncyCastleAllTests", + srcs = srcs, + deps = deps + ["@bouncycastle_1_%s//jar" % max(bouncycastle_versions)], + size = size, + test_class = test_class, + ) + +# These targets exclude slow tests. +def bouncycastle_tests(srcs, deps, size, test_class): + """BouncyCastle version-specific tests.""" + + # Generates BouncyCastleTest_1_55, ..., BouncyCastleTest_1_49 + for version in bouncycastle_versions: + native.java_test( + name = "BouncyCastleTest_1_%s" % version, + srcs = srcs, + deps = deps + [ + "@bouncycastle_1_%s//jar" % version, + ], + size = size, + test_class = test_class, + ) + + # Latest stable. + # We can't use native.alias, because aliased tests are not run. + # So, we simply duplicate the test. + native.java_test( + name = "BouncyCastleTest", + srcs = srcs, + deps = deps + ["@bouncycastle_1_%s//jar" % max(bouncycastle_versions)], + size = size, + test_class = test_class, + ) + +spongycastle_versions = range(50, 55) + +# These targets run all tests. +def spongycastle_all_tests(srcs, deps, size, test_class): + """SpongyCastle version-specific tests.""" + + # Generates SpongyCastleAllTests_1_54, ..., SpongyCastleAllTests_1_50 + for version in spongycastle_versions: + native.java_test( + name = "SpongyCastleAllTests_1_%s" % version, + srcs = srcs, + deps = deps + [ + "@spongycastle_core_1_%s//jar" % version, + "@spongycastle_prov_1_%s//jar" % version, + ], + size = size, + test_class = test_class, + ) + + # Latest stable. + # We can't use native.alias, because aliased tests are not run. + # So, we simply duplicate the test. + native.java_test( + name = "SpongyCastleAllTests", + srcs = srcs, + deps = deps + [ + "@spongycastle_core_1_%s//jar" % max(spongycastle_versions), + "@spongycastle_prov_1_%s//jar" % max(spongycastle_versions), + ], + size = size, + test_class = test_class, + ) + +# These targets exclude slow tests. +def spongycastle_tests(srcs, deps, size, test_class): + """SpongyCastle version-specific tests.""" + + # Generates SpongyCastleTest_1_54, ..., SpongyCastleTest_1_50 + for version in spongycastle_versions: + native.java_test( + name = "SpongyCastleTest_1_%s" % version, + srcs = srcs, + deps = deps + [ + "@spongycastle_core_1_%s//jar" % version, + "@spongycastle_prov_1_%s//jar" % version, + ], + size = size, + test_class = test_class, + ) + + # Latest stable. + # We can't use native.alias, because aliased tests are not run. + # So, we simply duplicate the test. + native.java_test( + name = "SpongyCastleTest", + srcs = srcs, + deps = deps + [ + "@spongycastle_core_1_%s//jar" % max(spongycastle_versions), + "@spongycastle_prov_1_%s//jar" % max(spongycastle_versions), + ], + size = size, + test_class = test_class, + ) diff --git a/doc/bugs.md b/doc/bugs.md new file mode 100644 index 0000000..1533e3b --- /dev/null +++ b/doc/bugs.md @@ -0,0 +1,5 @@ +# List of bugs in common algorithms +* [RSA](rsa.md) +* [DSA](dsa.md) +* [ECDH](ecdh.md) +* [Diffie-Hellman](dh.md) diff --git a/doc/dh.md b/doc/dh.md new file mode 100644 index 0000000..ca8c410 --- /dev/null +++ b/doc/dh.md @@ -0,0 +1,141 @@ +# Diffie-Hellman + +## Subgroup confinement attacks + +The papers by van Oorshot and Wiener [OW96] rsp. Lim and Lee [LL98] show that +Diffie-Hellman keys can be found much faster if the short exponents are used and +if the multiplicative group modulo p contains small subgroups. In particular an +attacker can try to send a public key that is an element of a small subgroup. If +the receiver does not check for such elements then may be possible to find the +private key modulo the order of the small subgroup. Several countermeasures +against such attacks have been proposed: For example IKE uses fields of order p +where p is a safe prime (i.e. $$q=(p-1)/2),$$ hence the only elements of small +order are 1 and p-1. + +[NIST SP 800-56A] rev. 2, Section 5.5.1.1 only requires that the size of the +subgroup generated by the generator g is big enough to prevent the baby-step +giant-step algorithm. I.e. for 80-bit security p must be at least 1024 bits long +and the prime q must be at least 160 bits long. A 2048 bit prime p and a 224 bit +prime q are sufficient for 112 bit security. To avoid subgroup confinment +attacks NIST requires that public keys are validated, i.e. by checking that a +public key y satisfies the conditions $$2 \leq y \leq p-2$$ and $$y^q \mod p = +1$$ (Section 5.6.2.3.1). Further, after generating the shared secret $$z = +y_a^{x_b} \mod p$$ each party should check that $$z \neq 1.$$ RFC 2785 contains +similar recommendations. The public key validation described by NIST requires +that the order q of the generator g is known to the verifier. Unfortunately, the +order q is missing in [PKCS #3]. [PKCS #3] describes the Diffie-Hellman +parameters only by the values p, g and optionally the key size in bits. + +The class DHParameterSpec that defines the Diffie-Hellman parameters in JCE +contains the same values as [PKCS #3]. In particular, it does not contain the +order of the subgroup q. Moreover, the SUN provider uses the minimal sizes +specified by NIST for q. Essentially the provider reuses the parameters for DSA. + +Therefore, there is no guarantee that an implementation of Diffie-Hellman is secure against +subgroup confinement attacks. Without a key validation it is insecure to use the key-pair +generation from [NIST SP 800-56A] Section 5.6.1.1 (The key-pair generation there only requires that +static and ephemeral private keys are randomly chosen in the range \\(1..q-1)\\). + +To avoid big disasters the tests below require that key sizes are not minimal. I.e., currently +the tests require at least 512 bit keys for 1024 bit fields. We use this lower limit because that +is what the SUN provider is currently doing. + +TODO(bleichen): Find a reference supporting or disproving that decision. + +## Weak parameters + +The DH parameters must be carefully chosen to avoid security issues. A panel at +Eurocrypt'92 discussed the possiblity of trapdoors in DL based primitives +[Eurocrypt92 panel]. A. Lenstra pointed out that the primes chould be chosen +such that the special number field sieve can be used to compute discrete +logarithms. Gordon has analyzed methods to generate and detect weak parameters +[G92]. Section 4 of Gordons paper describes a method that can detect some +special cases, but no general method was given. Recently Fried et al. showed +that 1024 bit discrete logarithms with the special number field sieve are +feasible [FGHT16]. Moreover some libraries use primes that are susceptible to +this attack [FGHT16]. + +TODO(bleichen): So far not test for weak DH parameters has been implemented. +Possibly we should at least implement a test that detects special cases, so +that weak primes (such as the one used in libtomcrypt) are detected. + +DH implementations are sometimes misconfigured. Adrian et al. [WeakDh] analyzed +various implementations and found for example the following problems in the +parameters: p is sometimes composite, p-1 contains no large prime factor, q is +used instead of the generator g. + +## References +[Eurocrypt92 panel]: "The Eurocrypt'92 Controversial Issue Trapdoor Primes and Moduli", +EUROCRYPT '92, LNCS 658, pp. 194-199. + +[G92]: D. M. Gordon. "Designing and detecting trapdoors for discrete log +cryptosystems." CRYPTO’92, pp. 66–75. + +\[FGHT16]: J. Fried, P. Gaudry, N. Heininger, E. Thome. "A kilobit hidden SNFS +discrete logarithm computation". http://eprint.iacr.org/2016/961.pdf + +[OW96]: P. C. van Oorschot, M. J. Wiener, "On Diffie-Hellman key agreement with short exponents", +Eurocrypt 96, pp 332–343. + +[LL98]: C.H. Lim and P.J. Lee, +"A key recovery attack on discrete log-based schemes using a prime order subgroup", +CRYPTO' 98, pp 249–263. + +[WeakDh]: D. Adrian, K. Bhargavan, Z. Durumeric, P. Gaudry, M. Green, +J. A. Halderman, N. Heninger, D. Springall, E. Thomé, Luke Valenta, +B. VanderSloot, E. Wustrow, S. Zanella-Béguelink, P. Zimmermann, +"Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice" +https://weakdh.org/imperfect-forward-secrecy-ccs15.pdf + +[NIST SP 800-56A], revision 2, May 2013 +http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf + +[PKCS #3]: "Diffie–Hellman Key Agreement", +http://uk.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-3-diffie-hellman-key-agreement-standar.htm + +[RFC 2785]: R. Zuccherato, +"Methods for Avoiding 'Small-Subgroup' Attacks on the Diffie-Hellman Key Agreement Method for S/MIME", +March 2000 +https://www.ietf.org/rfc/rfc2785.txt + + diff --git a/doc/dsa.md b/doc/dsa.md new file mode 100644 index 0000000..0c2f631 --- /dev/null +++ b/doc/dsa.md @@ -0,0 +1,192 @@ +# DSA + +[TOC] + +The digital signature algorithm (DSA) is one of three signature schemes +descripted in the digital signature standard [DSS]. + +## Key generation + +4.2 Selection of Parameter Sizes and Hash Functions for DSA +The DSS specifies the following choices for the pair (L,N), +where L is the size of p in bits and N is the size of q in bits: + +L | N +---:|----: +1024| 160 +2048| 224 +2048| 256 +3072| 256 + +The tests expect the following properties of the parameters used during +key generation: + +* If only the parameter L is specified by the caller then N should be one + of the options proposed in [DSS]. +* If no size is specified then L should be at least 2048. This is the minimal + key size recommended by NIST for the period up to the year 2030. + +## Signature generation + +The DSA signature algorithm requires that each signature is computed with a new +one-time secret k. This secret value should be close to uniformly distributed. +If that is not the case then DSA signatures can leak the private key that was +used to generate the signature. Two methods for generating the one-time secrets +are described in FIPS PUB 186-4, Section B.5.1 or B.5.2 [DSS]. There is also the +possibility that the use of mismatched implementations for key generation and +signature generation are leaking the private keys. + +## Signature verification + +A DSA signature is a DER encoded tuple of two integers (r,s). To verify a +signature the verifier first checks $$0 < r < q$$ and $$0 < s < q$$. The +verifier then computes: + +$$ +\begin{array}{l} +w=s^{-1} \bmod q\\ +u1 = w \cdot H(m) \bmod q\\ +u2 = w \cdot r \bmod q\\ +\end{array} +$$ + +and then verifies that \\(r = (g^{u1}y^{u2} \bmod p) \bmod q\\) + +## Incorrect computations and range checks. + +Some libraries return 0 as the modular inverse of 0 or q. +This can happen if the library computes the modular +inverse of s as \\(w=s^{q-2} \mod q\\) (gpg4browsers) of simply +if the implementations is buggy (pycrypto). if additionally to such +a bug the range of r,s is not or incorrectly tested then it might +be feasible to forge signatures with the values (r=1, s=0) or (r=1, s=q). +In particular, if a library can be forced to compute \\(s^{-1} \mod q = 0\\) +then the verification would compute \\( w = u1 = u2 = 0 \\) and hence +\\( (g^{u1}y^{u2} \mod p) \mod q = 1 .\\) + +## Timing attacks + +TBD + +# Some notable failures of crypto libraries. + +## JDK + +The jdk8 implementation of SHA1withDSA previously checked the key size as follows: + +```java +@Override + protected void checkKey(DSAParams params) + throws InvalidKeyException { + int valueL = params.getP().bitLength(); + if (valueL > 1024) { + throw new InvalidKeyException("Key is too long for this algorithm"); + } + } +``` + +This check was reasonable, it partially ensures conformance with the NIST +standard. In most cases would prevent the attack described above. + +However, Oracle released a patch that removed the length verification in DSA in +jdk9: http://hg.openjdk.java.net/jdk9/dev/jdk/rev/edd7a67585a5 +https://bugs.openjdk.java.net/browse/JDK-8039921 + +The new code is here: +http://hg.openjdk.java.net/jdk9/dev/jdk/file/edd7a67585a5/src/java.base/share/classes/sun/security/provider/DSA.java + +The change was further backported to jdk8: +http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/3212f1631643 + +Doing this was a serious mistake. It easily allowed incorrect implementations. +While generating 2048 bit DSA keys in jdk7 was not yet supported, doing so in +jdk8 is. To trigger this bug in jdk7 an application had to use a key generated +by a third party library (e.g. OpenSSL). Now, it is possible to trigger the bug +just using JCE. Moreover, the excessive use of default values in JCE makes it +easy to go wrong and rather difficult to spot the errors. + +The bug was for example triggered by the following code snippet: + +```java + KeyPairGenerator keygen = KeyPairGenerator.getInstance("DSA"); + Keygen.initialize(2048); + KeyPair keypair = keygen.genKeyPair(); + Signature s = Signature.getInstance("DSA"); + s.initSign(keypair.getPrivate()); +``` + +The first three lines generate a 2048 bit DSA key. 2048 bits is currently the +smallest key size recommended by NIST. + +```java + KeyPairGenerator keygen = KeyPairGenerator.getInstance("DSA"); + Keygen.initialize(2048); + KeyPair keypair = keygen.genKeyPair(); +``` + +The key size specifies the size of p but not the size of q. The NIST standard +allows either 224 or 256 bits for the size of q. The selection typically depends +on the library. The Sun provider uses 224. Other libraries e.g. OpenSSL +generates by default a 256 bit q for 2048 bit DSA keys. + +The next line contains a default in the initialization + +```java +    Signature s = Signature.getInstance("DSA"); +``` +This line is equivalent to + +```java +    Signature s = Signature.getInstance("SHA1withDSA"); +``` +Hence the code above uses SHA1 but with DSA parameters generated for SHA-224 +or SHA-256 hashes. Allowing this combination by itself is already a mistake, +but a flawed implementaion made the situation even worse. + +The implementation of SHA1withDSA assumeed that the parameter q is 160 bits +long and used this assumption to generate a random 160-bit k when generating a +signature instead of choosing it uniformly in the range (1,q-1). +Hence, k severely biased. Attacks against DSA with biased k are well known. +Howgrave-Graham and Smart analyzed such a situation [HS99]. Their results +show that about 4 signatrues leak enough information to determine +the private key in a few milliseconds. +Nguyen analyzed a similar flaw in GPG [N04]. +I.e., Section 3.2 of Nguyens paper describes essentially the same attack as +used here. More generally, attacks based on lattice reduction were developed +to break a variety of cryptosystems such as the knapsack cryptosystem [O90]. + +## Further notes + +The short algorithm name “DSA” is misleading, since it hides the fact that +`Signature.getInstance(“DSA”)` is equivalent to +`Signature.getInstance(“SHA1withDSA”)`. To reduce the chance of a +misunderstanding short algorithm names should be deprecated. In JCE the hash +algorithm is defined by the algorithm. I.e. depending on the hash algorithm to +use one would call one of: + +```java + Signature.getInstance(“SHA1withDSA”); + Signature.getInstance(“SHA224withDSA”); + Signature.getInstance(“SHA256withDSA”); +``` + +A possible way to push such a change are code analysis tools. "DSA" is in good +company with other algorithm names “RSA”, “AES”, “DES”, all of which default to +weak algorithms. + +## References + +[HS99]: N.A. Howgrave-Graham, N.P. Smart, + “Lattice Attacks on Digital Signature Schemes” + http://www.hpl.hp.com/techreports/1999/HPL-1999-90.pdf + +[N04]: Phong Nguyen, “Can we trust cryptographic software? Cryptographic flaws + in Gnu privacy guard 1.2.3”, Eurocrypt 2004, + https://www.iacr.org/archive/eurocrypt2004/30270550/ProcEC04.pdf + +[O90]: A. M. Odlyzko, "The rise and fall of knapsack cryptosystems", Cryptology + and Computational Number Theory, pp.75-88, 1990 + +[DSS]: FIPS PUB 186-4, "Digital Signature Standard (DSS)", National Institute + of Standards and Technology, July 2013 + http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf diff --git a/doc/ecdh.md b/doc/ecdh.md new file mode 100644 index 0000000..c86b140 --- /dev/null +++ b/doc/ecdh.md @@ -0,0 +1,58 @@ + +# ECDH + +[TOC] + +##ECDH description: +See https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman + +##Bugs +Some libraries do not check if the elliptic curve points received from another +party are points on the curve. Encodings of public keys typically contain the +curve for the public key point. If such an encoding is used in the key exchange +then it is important to check that the public and secret key used to compute +the shared ECDH secret are using the same curve. +Some libraries fail to do this check. + +**Potential exploits:** +The damage done depends on the protocol that uses ECDH. E.g. if ECDH is used +with ephemeral keys then the damage is typically limited. If the EC keys are +static, i.e. used for multiple key exchanges then a failure to verify a public +point can disclose the private key used in the same protocol. +(To do: add papers describing the attack). + +##Libraries +**Sun JCE provider:** +ECDH does not check if the points are on the curve. +The implementer must do this. + +**Bouncycastle:** +The ECDH implementation does not check if the point is on the curve. +Furthermore, Bouncycastle does not even check if the public and private key are +on the same curve. It performs a point multiplication \\(x \cdot Y\\) over the +curve specified by the public key. + +**OpenSSL:** +Point verification is done in OpenSSL if the right functions are used. +Since OpenSSL is not well documented it is a bit tricky to find the right +functions. +(To do: maybe add an example). + +##Countermeasures +TODO: +* use point compression. Formats such as X509EncodedKeySpec +in Java include bits that indicate whether the point is compressed or not. +Hence an attacker can always choose to use uncompressed points as long as this +option is incorrectly implemented. +* check that public and private key use the same curve +* restrict the protocol to named curves +* reconstruct the public key explicitly using the parameters of the private + key. + +**Further recommendations:** +If possible I also check if the points are on the curve after point +multiplications on an elliptic curve in the hope to catch implementation +and hardware faults. + +## Some notable bugs: +* ECDHC in bouncy castle could be broken by modifying the order of the public key. diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..c664ba1 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,80 @@ +# Project Wycheproof + +This page describes the goals and strategies of project Wycheproof. See +[README](../README.md) for introduction to the project. + +## Defense in depth + +There are a number of tests where we check for expected behaviour +rather than exploitability. Examples: + +* default values: we expect that default values are reasonable and correspond + to recommendations by current standards. Concretely, in 2016 it is not OK + if an RSA key generation uses 1024 bits as default or digital signatures + use SHA-1 as default. +* timing attacks: any timing that relation between keys (or other sensitive) + data and the measured time fails the test. However tests are set up + such that too much noise during the test can prevent that a relation + is detected. +* wrong exceptions: The JCE interface often specifies the exceptions that + should be thrown when the input is invalid. We expect the specified + exceptions in the tests. +* leaking information through exceptions: While it is a good practice to not + return detailed logs to a sender, we consider text in exceptions as + information that a potential attacker can learn. For example padding + failures during decryption should not contain information about the + reason why a decryption failed. +* RSA PKCS #1 signatures: If a signature verification allows signatures + with lots of modifications, then RSA signatures can be forged for small + public exponents. Tests do not measure how many bytes can be modified. + Any accepted modification of the PKCS #1 padding fails the test. + +## Compatibility between providers + +One of the goals of Wycheproof is to test for compatibility issues. +Switching JCE providers should not introduce vulnerabilities simply because +the solution was developed with another provider. + +An example for this was the following observation: When using AES-GCM then +javax.crypto.CipherInputStream worked sort of with JCE and +org.bouncycastle.jcajce.io.CipherInputStream.java worked with BouncyCastle. +However, authentication was skipped in some cases when +javax.crypto.CipherInputStream was used with BouncyCastle. + +## Comparing cryptographic libraries is not a primary goal + +Because of the strategies mentioned above we expect that a comparison of +cryptographic libraries based on the bugs found would be biased: + +* Libraries used internally in Google get more attention. + Serious vulnerabilities in these libraries should be fixed at the time the + tests are added to Wycheproof. On the other hand it is also likely that + tests find a larger number of bugs in thsese libraries when old versions are + tested. +* Tests often check for expected behaviour and compatibility. + Excpected behaviour is often defined by a prominent library. + Pointing out such problems can therefore penalize smaller third party + libraries. +* We are working toward covering as many potential vulnerabilities as possible + with test vectors, because this simplifies porting the tests to other + languages or interfaces. Thus a single test case can cover multiple + vulnerabilities. + +We are not trying to remove this bias when this interferes with more important +goals such as early reporting. +Hence we are reluctant to publish comparisons. + + +## Thoughts on the design of cryptographic libraries + +We should promote robust interfaces with the goal to simplify +the use of the library, codereviews of applications using the +library and testing the library. + +* When cryptrographic primitives require randomness then the random + numbers should be chosen by the library. It shouldn't be possible + for a user to provide randomness. If the library itself chooses the + randomness then it is possible (at least to some degree) to check + that the random number generation is appropriate for the primitive. + If the user can provide the randomness then it is not possible to + catch this in our tests. diff --git a/doc/rsa.md b/doc/rsa.md new file mode 100644 index 0000000..09ee6d0 --- /dev/null +++ b/doc/rsa.md @@ -0,0 +1,130 @@ +# RSA + +[TOC] + +## RSA key generation + +**Default size:** If a library supports a key default size for RSA keys then +this key size should be at least 2048 bits. This limit is based on the minimum +recommendation of [NIST SP 800-57] part1 revision 4, Table 2, page 53. NIST +recommends a minimal security strength of 112 bits for keys used until 2030. 112 +bit security strength translates to a minimal key size of 2048 bits. Other +organizations recommend somewhat different sizes: [Enisa], Section 3.6 also +suggests that 2048-bit RSA keys provide a security strength of about 112 bits, +but recommends a security strength of 128 bits for near term systems, hence 3072 +bit RSA keys. [ECRYPT II], Section 13.3 suggests at least 2432 bits for new +keys. + +All the references above clearly state that keys smaller than 2048 bits should +only be used in legacy cases. Therefore, it seems wrong to use a default key +size smaller than 2048 bits. If a user really wants a small RSA key then such a +choice should be made by explicitly providing the desired key length during the +initalization of a key pair generator. + +According to https://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html +every implementation of the Java platform is required to implement RSA with both 1024 and +2048 bit key sizes. Hence a 2048 bit default should not lead to compatibility problems. + +**Cryptographically strong random numbers:** +So far the tests check that java.util.Random is not used. This needs to be +extended. + +**Other bugs:** +The public exponent e should be larger than 1 [CVE-1999-1444] + +## RSA PKCS #1 v1.5 encryption + +PKCS #1 v1.5 padding is susceptible to adaptive chosen ciphertext attacks and +hence should be avoided [B98]. The difficulty of exploiting protocols using +PKCS #1 v1.5 encryption often depends on the amount of information leaked after +decrypting corrupt ciphertexts. Implementations frequently leak information +about the decrypted plaintext in form of error messages. The content of the +error messages are extremely helpful to potential attackers. Bardou et al. +[BFKLSST12] analyze the difficult of attacks based on different types of +information leakage. Smart even describes an attack that only needs about 40 +chosen ciphertexts [S10], though in this case the encryption did not use PKCS #1 +padding. + +**Bugs** + +* Bouncycastle throws detailed exceptions: + InvalidCipherTextException("unknown block type") or + InvalidCipherTextException("block padding incorrect"). + + + +**Tests** +To test whether an implementation leaks more information than necessary a test decrypts +some random ciphertexts and catches the exceptions. If the exceptions are distinguishable +then the test assumes that unnecessary information about the padding is leaked. + +Due to the nature of unit tests not every attack can be detected this way. +Some attacks require a large number of ciphertexts to be detected if random ciphertexts +are used. For example Klima et al. [KPR03] describe an implementation flaw that could not +be detected with our test. + +Timing leakages because of differences in parsing the padding can leak information +(e.g. CVE-2015-7827). Such differences are too small to be reliably detectable in +unit tests. + +## RSA OAEP +Manger describes an chosen ciphertext attack against RSA in [M01]. +There are implementations that were subseptible to Mangers attack, +e.g. [CVE-2012-5081]. + +## RSA PKCS1 signatures +**Potential problems:** + + * Some libraries parse PKCS#1 padding during signature verification incorrectly. + * Some libraries determine the hash function from the signature + (rather than encoding this in the key) + Effect: + * If the verification is buggy then an attacker might be able to generate + signatures for keys with a small (i.e. e=3) public exponent. + * If the hash algorithm is not determined by in an authentic manner then preimage + attacks against weak hashes are possible, even if the hashes are not used + by the signer. + +**Countermeasures:** +A good way to implement RSA signature verification is described in the standard PKCS#1 +v.2.2 Section 8.2.2. +This standard proposes to reconstruct the padding during verification and +compare the padded hash to the value \\(s^e \bmod n\\) obtained from applying a public +key exponentiation to the signature s. +Since this is a recurring bug it makes also a lot of sense to avoid small +public exponents and prefer for example e=65537 . + +**List of broken implementations** +This is a large list. + +## References +[B98]: D. Bleichenbacher, "Chosen ciphertext attacks against protocols based on the RSA encryption + standard PKCS# 1" Crypto 98 + +[M01]: J. Manger, "A chosen ciphertext attack on RSA optimal asymmetric encryption padding (OAEP) + as standardized in PKCS# 1 v2.0", Crypto 2001 This paper shows that OAEP is susceptible + to a chosen ciphertext attack if error messages distinguish between different failure + condidtions. +[S10]: N. Smart, "Errors matter: Breaking RSA-based PIN encryption with thirty ciphertext validity + queries" RSA conference, 2010 This paper shows that padding oracle attacks can be + successful with even a small number of queries. + +[KPR03]: V. Klima, O. Pokorny, and T. Rosa, "Attacking RSA-based Sessions in SSL/TLS" + https://eprint.iacr.org/2003/052/ + +[BFKLSST12]: "Efficient padding oracle attacks on cryptographic hardware" +R. Bardou, R. Focardi, Y. Kawamoto, L. Simionato, G. Steel, J.K. Tsay, Crypto 2012 + +[NIST SP 800-57]: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf + +[Enisa]: "Algorithms, key size and parameters report – 2014" +https://www.enisa.europa.eu/publications/algorithms-key-size-and-parameters-report-2014 + +\[ECRYPT II]: Yearly Report on Algorithms and Keysizes (2011-2012), +http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf + +[CVE-1999-1444]: Alibaba 2.0 generated RSA key pairs with an exponent 1 + +[CVE-2012-5081]: Java JSSE provider leaked information through + exceptions and timing. Both the PKCS #1 padding and the OAEP padding were broken: + http://www-brs.ub.ruhr-uni-bochum.de/netahtml/HSS/Diss/MeyerChristopher/diss.pdf diff --git a/java/com/google/security/wycheproof/BouncyCastleAllTests.java b/java/com/google/security/wycheproof/BouncyCastleAllTests.java new file mode 100644 index 0000000..47a4dcf --- /dev/null +++ b/java/com/google/security/wycheproof/BouncyCastleAllTests.java @@ -0,0 +1,53 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * BouncyCastleAllTests runs all tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesEaxTest.class, + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + DhTest.class, + DhiesTest.class, + DsaTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + EciesTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class, +}) +@Provider(ProviderType.BOUNCY_CASTLE) +public final class BouncyCastleAllTests { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyThisProvider(new BouncyCastleProvider()); + } +} diff --git a/java/com/google/security/wycheproof/BouncyCastleTest.java b/java/com/google/security/wycheproof/BouncyCastleTest.java new file mode 100644 index 0000000..941b94f --- /dev/null +++ b/java/com/google/security/wycheproof/BouncyCastleTest.java @@ -0,0 +1,55 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Fast; +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * BouncyCastleTest excludes {@code @SlowTest} tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesEaxTest.class, + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + DhTest.class, + DhiesTest.class, + DsaTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + EciesTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class, +}) +@Provider(ProviderType.BOUNCY_CASTLE) +@Fast +public final class BouncyCastleTest { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyThisProvider(new BouncyCastleProvider()); + } +} diff --git a/java/com/google/security/wycheproof/ConscryptTest.java b/java/com/google/security/wycheproof/ConscryptTest.java new file mode 100644 index 0000000..c4f58d5 --- /dev/null +++ b/java/com/google/security/wycheproof/ConscryptTest.java @@ -0,0 +1,51 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Fast; +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.conscrypt.OpenSSLProvider; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Conscrypt is a Java security provider that uses OpenSSL. See {@link https://conscrypt.org/}. + * ConscryptTest excludes {@code @SlowTest} tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class +}) +@Provider(ProviderType.CONSCRYPT) +@Fast +public final class ConscryptTest { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyThisProvider(new OpenSSLProvider()); + } +} diff --git a/java/com/google/security/wycheproof/EcUtil.java b/java/com/google/security/wycheproof/EcUtil.java new file mode 100644 index 0000000..f464cba --- /dev/null +++ b/java/com/google/security/wycheproof/EcUtil.java @@ -0,0 +1,454 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECField; +import java.security.spec.ECFieldFp; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; + +/** + * Some utilities for testing Elliptic curve crypto. This code is for testing only and hasn't been + * reviewed for production. + */ +public class EcUtil { + /** + * Returns the ECParameterSpec for a named curve. Not every provider implements the + * AlgorithmParameters. Therefore, most test use alternative functions. + */ + public static ECParameterSpec getCurveSpec(String name) + throws NoSuchAlgorithmException, InvalidParameterSpecException { + AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC"); + parameters.init(new ECGenParameterSpec(name)); + return parameters.getParameterSpec(ECParameterSpec.class); + } + + /** + * Returns the ECParameterSpec for a named curve. Only a handful curves that are used in the tests + * are implemented. + */ + public static ECParameterSpec getCurveSpecRef(String name) throws NoSuchAlgorithmException { + if (name.equals("secp224r1")) { + return getNistP224Params(); + } else if (name.equals("secp256r1")) { + return getNistP256Params(); + } else if (name.equals("secp384r1")) { + return getNistP384Params(); + } else if (name.equals("secp521r1")) { + return getNistP521Params(); + } else if (name.equals("brainpoolp256r1")) { + return getBrainpoolP256r1Params(); + } else { + throw new NoSuchAlgorithmException("Curve not implemented:" + name); + } + } + + public static ECParameterSpec getNistCurveSpec( + String decimalP, String decimalN, String hexB, String hexGX, String hexGY) { + final BigInteger p = new BigInteger(decimalP); + final BigInteger n = new BigInteger(decimalN); + final BigInteger three = new BigInteger("3"); + final BigInteger a = p.subtract(three); + final BigInteger b = new BigInteger(hexB, 16); + final BigInteger gx = new BigInteger(hexGX, 16); + final BigInteger gy = new BigInteger(hexGY, 16); + final int h = 1; + ECFieldFp fp = new ECFieldFp(p); + java.security.spec.EllipticCurve curveSpec = new java.security.spec.EllipticCurve(fp, a, b); + ECPoint g = new ECPoint(gx, gy); + ECParameterSpec ecSpec = new ECParameterSpec(curveSpec, g, n, h); + return ecSpec; + } + + public static ECParameterSpec getNistP224Params() { + return getNistCurveSpec( + "26959946667150639794667015087019630673557916260026308143510066298881", + "26959946667150639794667015087019625940457807714424391721682722368061", + "b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", + "b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", + "bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"); + } + + public static ECParameterSpec getNistP256Params() { + return getNistCurveSpec( + "115792089210356248762697446949407573530086143415290314195533631308867097853951", + "115792089210356248762697446949407573529996955224135760342422259061068512044369", + "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", + "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"); + } + + public static ECParameterSpec getNistP384Params() { + return getNistCurveSpec( + "3940200619639447921227904010014361380507973927046544666794829340" + + "4245721771496870329047266088258938001861606973112319", + "3940200619639447921227904010014361380507973927046544666794690527" + + "9627659399113263569398956308152294913554433653942643", + "b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a" + + "c656398d8a2ed19d2a85c8edd3ec2aef", + "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38" + + "5502f25dbf55296c3a545e3872760ab7", + "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0" + + "0a60b1ce1d7e819d7a431d7c90ea0e5f"); + } + + public static ECParameterSpec getNistP521Params() { + return getNistCurveSpec( + "6864797660130609714981900799081393217269435300143305409394463459" + + "18554318339765605212255964066145455497729631139148085803712198" + + "7999716643812574028291115057151", + "6864797660130609714981900799081393217269435300143305409394463459" + + "18554318339765539424505774633321719753296399637136332111386476" + + "8612440380340372808892707005449", + "051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef10" + + "9e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", + "c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3d" + + "baa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", + "11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e6" + + "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650"); + } + + public static ECParameterSpec getBrainpoolP256r1Params() { + BigInteger p = + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16); + BigInteger a = + new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16); + BigInteger b = + new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16); + BigInteger x = + new BigInteger("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16); + BigInteger y = + new BigInteger("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16); + BigInteger n = + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16); + final int h = 1; + ECFieldFp fp = new ECFieldFp(p); + EllipticCurve curve = new EllipticCurve(fp, a, b); + ECPoint g = new ECPoint(x, y); + return new ECParameterSpec(curve, g, n, h); + } + + /** + * Compute the Legendre symbol of x mod p. This implementation is slow. Faster would be the + * computation for the Jacobi symbol. + * + * @param x an integer + * @param p a prime modulus + * @returns 1 if x is a quadratic residue, -1 if x is a non-quadratic residue and 0 if x and p are + * not coprime. + * @throws GeneralSecurityException when the computation shows that p is not prime. + */ + public static int legendre(BigInteger x, BigInteger p) throws GeneralSecurityException { + BigInteger q = p.subtract(BigInteger.ONE).shiftRight(1); + BigInteger t = x.modPow(q, p); + if (t.equals(BigInteger.ONE)) { + return 1; + } else if (t.equals(BigInteger.ZERO)) { + return 0; + } else if (t.add(BigInteger.ONE).equals(p)) { + return -1; + } else { + throw new GeneralSecurityException("p is not prime"); + } + } + + /** + * Computes a modular square root. Timing and exceptions can leak information about the inputs. + * Therefore this method must only be used in tests. + * + * @param x the square + * @param p the prime modulus + * @returns a value s such that s^2 mod p == x mod p + * @throws GeneralSecurityException if the square root could not be found. + */ + public static BigInteger modSqrt(BigInteger x, BigInteger p) throws GeneralSecurityException { + if (p.signum() != 1) { + throw new GeneralSecurityException("p must be positive"); + } + x = x.mod(p); + BigInteger squareRoot = null; + // Special case for x == 0. + // This check is necessary for Cipolla's algorithm. + if (x.equals(BigInteger.ZERO)) { + return x; + } + if (p.testBit(0) && p.testBit(1)) { + // Case p % 4 == 3 + // q = (p + 1) / 4 + BigInteger q = p.add(BigInteger.ONE).shiftRight(2); + squareRoot = x.modPow(q, p); + } else if (p.testBit(0) && !p.testBit(1)) { + // Case p % 4 == 1 + // For this case we use Cipolla's algorithm. + // This alogorithm is preferrable to Tonelli-Shanks for primes p where p-1 is divisible by + // a large power of 2, which is a frequent choice since it simplifies modular reduction. + BigInteger a = BigInteger.ONE; + BigInteger d = null; + while (true) { + d = a.multiply(a).subtract(x).mod(p); + // Computes the Legendre symbol. Using the Jacobi symbol would be a faster. Using Legendre + // has the advantage, that it detects a non prime p with high probability. + // On the other hand if p = q^2 then the Jacobi (d/p)==1 for almost all d's and thus + // using the Jacobi symbol here can result in an endless loop with invalid inputs. + int t = legendre(d, p); + if (t == -1) { + break; + } else { + a = a.add(BigInteger.ONE); + } + } + // Since d = a^2 - n is a non-residue modulo p, we have + // a - sqrt(d) == (a+sqrt(d))^p (mod p), + // and hence + // n == (a + sqrt(d))(a - sqrt(d) == (a+sqrt(d))^(p+1) (mod p). + // Thus if n is square then (a+sqrt(d))^((p+1)/2) (mod p) is a square root of n. + BigInteger q = p.add(BigInteger.ONE).shiftRight(1); + BigInteger u = a; + BigInteger v = BigInteger.ONE; + for (int bit = q.bitLength() - 2; bit >= 0; bit--) { + // Compute (u + v sqrt(d))^2 + BigInteger tmp = u.multiply(v); + u = u.multiply(u).add(v.multiply(v).mod(p).multiply(d)).mod(p); + v = tmp.add(tmp).mod(p); + if (q.testBit(bit)) { + tmp = u.multiply(a).add(v.multiply(d)).mod(p); + v = a.multiply(v).add(u).mod(p); + u = tmp; + } + } + squareRoot = u; + } + // The methods used to compute the square root only guarantee a correct result if the + // preconditions (i.e. p prime and x is a square) are satisfied. Otherwise the value is + // undefined. Hence, it is important to verify that squareRoot is indeed a square root. + if (squareRoot != null && squareRoot.multiply(squareRoot).mod(p).compareTo(x) != 0) { + throw new GeneralSecurityException("Could not find square root"); + } + return squareRoot; + } + + /** + * Returns the modulus of the field used by the curve specified in ecParams. + * + * @param curve must be a prime order elliptic curve + * @return the order of the finite field over which curve is defined. + */ + public static BigInteger getModulus(EllipticCurve curve) throws GeneralSecurityException { + java.security.spec.ECField field = curve.getField(); + if (field instanceof java.security.spec.ECFieldFp) { + return ((java.security.spec.ECFieldFp) field).getP(); + } else { + throw new GeneralSecurityException("Only curves over prime order fields are supported"); + } + } + + /** + * Returns the size of an element of the field over which the curve is defined. + * + * @param curve must be a prime order elliptic curve + * @return the size of an element in bits + */ + public static int fieldSizeInBits(EllipticCurve curve) throws GeneralSecurityException { + return getModulus(curve).subtract(BigInteger.ONE).bitLength(); + } + + /** + * Returns the size of an element of the field over which the curve is defined. + * + * @param curve must be a prime order elliptic curve + * @return the size of an element in bytes. + */ + public static int fieldSizeInBytes(EllipticCurve curve) throws GeneralSecurityException { + return (fieldSizeInBits(curve) + 7) / 8; + } + + /** + * Checks that a point is on a given elliptic curve. This method implements the partial public key + * validation routine from Section 5.6.2.6 of NIST SP 800-56A + * http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf A partial + * public key validation is sufficient for curves with cofactor 1. See Section B.3 of + * http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf The point validations above are + * taken from recommendations for ECDH, because parameter checks in ECDH are much more important + * than for the case of ECDSA. Performing this test for ECDSA keys is mainly a sanity check. + * + * @param point the point that needs verification + * @param ec the elliptic curve. This must be a curve over a prime order field. + * @throws GeneralSecurityException if the field is binary or if the point is not on the curve. + */ + public static void checkPointOnCurve(ECPoint point, EllipticCurve ec) + throws GeneralSecurityException { + BigInteger p = getModulus(ec); + BigInteger x = point.getAffineX(); + BigInteger y = point.getAffineY(); + if (x == null || y == null) { + throw new GeneralSecurityException("point is at infinity"); + } + // Check 0 <= x < p and 0 <= y < p. + if (x.signum() == -1 || x.compareTo(p) != -1) { + throw new GeneralSecurityException("x is out of range"); + } + if (y.signum() == -1 || y.compareTo(p) != -1) { + throw new GeneralSecurityException("y is out of range"); + } + // Check y^2 == x^3 + a x + b (mod p) + BigInteger lhs = y.multiply(y).mod(p); + BigInteger rhs = x.multiply(x).add(ec.getA()).multiply(x).add(ec.getB()).mod(p); + if (!lhs.equals(rhs)) { + throw new GeneralSecurityException("Point is not on curve"); + } + } + + /** + * Checks a public key. I.e. this checks that the point defining the public key is on the curve. + * + * @param key must be a key defined over a curve using a prime order field. + * @throws GeneralSecurityException if the key is not valid. + */ + public static void checkPublicKey(ECPublicKey key) throws GeneralSecurityException { + checkPointOnCurve(key.getW(), key.getParams().getCurve()); + } + + /** + * Decompress a point + * + * @param x The x-coordinate of the point + * @param bit0 true if the least significant bit of y is set. + * @param ecParams contains the curve of the point. This must be over a prime order field. + */ + public static ECPoint getPoint(BigInteger x, boolean bit0, ECParameterSpec ecParams) + throws GeneralSecurityException { + EllipticCurve ec = ecParams.getCurve(); + ECField field = ec.getField(); + if (!(field instanceof ECFieldFp)) { + throw new GeneralSecurityException("Only curves over prime order fields are supported"); + } + BigInteger p = ((java.security.spec.ECFieldFp) field).getP(); + if (x.compareTo(BigInteger.ZERO) == -1 || x.compareTo(p) != -1) { + throw new GeneralSecurityException("x is out of range"); + } + // Compute rhs == x^3 + a x + b (mod p) + BigInteger rhs = x.multiply(x).add(ec.getA()).multiply(x).add(ec.getB()).mod(p); + BigInteger y = modSqrt(rhs, p); + if (bit0 != y.testBit(0)) { + y = p.subtract(y).mod(p); + } + return new ECPoint(x, y); + } + + /** + * Decompress a point on an elliptic curve. + * + * @param bytes The compressed point. Its representation is z || x where z is 2+lsb(y) and x is + * using a unsigned fixed length big-endian representation. + * @param ecParams the specification of the curve. Only Weierstrass curves over prime order fields + * are implemented. + */ + public static ECPoint decompressPoint(byte[] bytes, ECParameterSpec ecParams) + throws GeneralSecurityException { + EllipticCurve ec = ecParams.getCurve(); + ECField field = ec.getField(); + if (!(field instanceof ECFieldFp)) { + throw new GeneralSecurityException("Only curves over prime order fields are supported"); + } + BigInteger p = ((java.security.spec.ECFieldFp) field).getP(); + int expectedLength = 1 + (p.bitLength() + 7) / 8; + if (bytes.length != expectedLength) { + throw new GeneralSecurityException("compressed point has wrong length"); + } + boolean lsb; + switch (bytes[0]) { + case 2: + lsb = false; + break; + case 3: + lsb = true; + break; + default: + throw new GeneralSecurityException("Invalid format"); + } + BigInteger x = new BigInteger(1, Arrays.copyOfRange(bytes, 1, bytes.length)); + if (x.compareTo(BigInteger.ZERO) == -1 || x.compareTo(p) != -1) { + throw new GeneralSecurityException("x is out of range"); + } + // Compute rhs == x^3 + a x + b (mod p) + BigInteger rhs = x.multiply(x).add(ec.getA()).multiply(x).add(ec.getB()).mod(p); + BigInteger y = modSqrt(rhs, p); + if (lsb != y.testBit(0)) { + y = p.subtract(y).mod(p); + } + return new ECPoint(x, y); + } + + /** + * Returns a weak public key of order 3 such that the public key point is on the curve specified + * in ecParams. This method is used to check ECC implementations for missing step in the + * verification of the public key. E.g. implementations of ECDH must verify that the public key + * contains a point on the curve as well as public and secret key are using the same curve. + * + * @param ecParams the parameters of the key to attack. This must be a curve in Weierstrass form + * over a prime order field. + * @return a weak EC group with a genrator of order 3. + */ + public static ECPublicKeySpec getWeakPublicKey(ECParameterSpec ecParams) + throws GeneralSecurityException { + EllipticCurve curve = ecParams.getCurve(); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); + keyGen.initialize(ecParams); + BigInteger p = getModulus(curve); + BigInteger three = new BigInteger("3"); + while (true) { + // Generate a point on the original curve + KeyPair keyPair = keyGen.generateKeyPair(); + ECPublicKey pub = (ECPublicKey) keyPair.getPublic(); + ECPoint w = pub.getW(); + BigInteger x = w.getAffineX(); + BigInteger y = w.getAffineY(); + // Find the curve parameters a,b such that 3*w = infinity. + // This is the case if the following equations are satisfied: + // 3x == l^2 (mod p) + // l == (3x^2 + a) / 2*y (mod p) + // y^2 == x^3 + ax + b (mod p) + BigInteger l; + try { + l = modSqrt(x.multiply(three), p); + } catch (GeneralSecurityException ex) { + continue; + } + BigInteger xSqr = x.multiply(x).mod(p); + BigInteger a = l.multiply(y.add(y)).subtract(xSqr.multiply(three)).mod(p); + BigInteger b = y.multiply(y).subtract(x.multiply(xSqr.add(a))).mod(p); + EllipticCurve newCurve = new EllipticCurve(curve.getField(), a, b); + // Just a sanity check. + checkPointOnCurve(w, newCurve); + // Cofactor and order are of course wrong. + ECParameterSpec spec = new ECParameterSpec(newCurve, w, p, 1); + return new ECPublicKeySpec(w, spec); + } + } +} diff --git a/java/com/google/security/wycheproof/OpenJDKAllTests.java b/java/com/google/security/wycheproof/OpenJDKAllTests.java new file mode 100644 index 0000000..a2b3b06 --- /dev/null +++ b/java/com/google/security/wycheproof/OpenJDKAllTests.java @@ -0,0 +1,34 @@ +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Tests for OpenJDK's providers: SunJCE, SunEC, etc. + * OpenJDKAllTests runs all tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + DhTest.class, + DsaTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class +}) +@Provider(ProviderType.OPENJDK) +public final class OpenJDKAllTests { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyOpenJDKProviders(); + } +} diff --git a/java/com/google/security/wycheproof/OpenJDKTest.java b/java/com/google/security/wycheproof/OpenJDKTest.java new file mode 100644 index 0000000..b326fef --- /dev/null +++ b/java/com/google/security/wycheproof/OpenJDKTest.java @@ -0,0 +1,36 @@ +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Fast; +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; + +/** + * Tests for OpenJDK's providers: SunJCE, SunEC, etc. + * OpenJDKTest excludes slow tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + DhTest.class, + DsaTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class +}) +@Provider(ProviderType.OPENJDK) +@Fast +public final class OpenJDKTest { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyOpenJDKProviders(); + } +} diff --git a/java/com/google/security/wycheproof/ProviderIndependentTest.java b/java/com/google/security/wycheproof/ProviderIndependentTest.java new file mode 100644 index 0000000..931fc64 --- /dev/null +++ b/java/com/google/security/wycheproof/ProviderIndependentTest.java @@ -0,0 +1,26 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +/** Provider independent tests */ +@RunWith(Suite.class) +@SuiteClasses({BigIntegerTest.class}) +public final class ProviderIndependentTest {} diff --git a/java/com/google/security/wycheproof/RandomUtil.java b/java/com/google/security/wycheproof/RandomUtil.java new file mode 100644 index 0000000..388c272 --- /dev/null +++ b/java/com/google/security/wycheproof/RandomUtil.java @@ -0,0 +1,185 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.Random; + +/** + * A collection of utilities for testing random number generators. So far this util simply checks + * that random numbers are not generated by java.util.Random. Eventually we plan to add detection + * for other random number generators too. + * + * @author bleichen@google.com (Daniel Bleichenbacher) + */ +public class RandomUtil { + // Constants for java.util.Random; + static final long A = 0x5DEECE66DL; + static final long A_INVERSE = 246154705703781L; + static final long C = 0xBL; + + /** Given a state of a java.util.Random object compute the next state. */ + protected static long nextState(long seed) { + return (seed * A + C) & ((1L << 48) - 1); + } + + /** Give the state after stepping java.util.Random n times. */ + protected static long step(long seed, long n) { + long a = A; + long c = C; + n = n & 0xffffffffffffL; + while (n != 0) { + if ((n & 1) == 1) { + seed = seed * a + c; + } + c = c * (a + 1); + a = a * a; + n = n >> 1; + } + return seed & 0xffffffffffffL; + } + + /** Given a state of a java.util.Random object compute the previous state. */ + protected static long previousState(long seed) { + return ((seed - C) * A_INVERSE) & ((1L << 48) - 1); + } + + /** Computes a seed that would initialize a java.util.Random object with a given state. */ + protected static long getSeedForState(long seed) { + return seed ^ A; + } + + protected static long getStateForSeed(long seed) { + return (seed ^ A) & 0xffffffffffffL; + } + + /** + * Given two subsequent outputs x0 and x1 from java.util.Random this function computes the + * internal state of java.util.Random after returning x0 or returns -1 if no such state exists. + */ + protected static long getState(int x0, int x1) { + long mask = (1L << 48) - 1; + long multiplier = A; + // The state of the random number generator after returning x0 is + // l0 + eps for some 0 <= eps < 2**16. + long l0 = ((long) x0 << 16) & mask; + // The state of the random number generator after returning x1 is + // l1 + delta for some 0 <= delta < 2**16. + long l1 = ((long) x1 << 16) & mask; + // We have l1 + delta = (l0 + eps)*multiplier + 0xBL (mod 2**48). + // This allows to find an upper bound w for eps * multiplier mod 2**48 + // by assuming delta = 2**16-1. + long w = (l1 - l0 * multiplier + 65535L - 0xBL) & mask; + // The reduction eps * multiplier mod 2**48 only cuts off at most 3 bits. + // Hence a simple search is sufficient. The maximal number of loops is 6. + for (long em = w; em < (multiplier << 16); em += 1L << 48) { + // If the high order bits of em are guessed correctly then + // em == eps * multiplier + 65535 - delta. + long eps = em / multiplier; + long state0 = l0 + eps; + long state1 = nextState(state0); + if ((state1 & 0xffffffff0000L) == l1) { + return state0; + } + } + return -1; + } + + /** + * Find a seed such that this integer is the result of + * + *
{@code
+   * Random rand = new Random();
+   * rand.setSeed(seed);
+   * return new BigInteger(k, rand);
+   * }
+ * + * where k is max(64, x.BitLength()); + * + *

Returns -1 if no such seed exists. + */ + // TODO(bleichen): We want to detect cases where some of the bits + // (i.e. most significant bits or least significant bits have + // been modified. Often this happens during the generation + // of primes or other things. + // TODO(bleichen): This method is incomplete. + protected static long getSeedFor(java.math.BigInteger x) { + byte[] bytes = x.toByteArray(); + if (bytes.length == 0) { + return -1; + } + ByteBuffer buffer = ByteBuffer.allocate(8); + int offset = bytes[0] == 0 ? 1 : 0; + if (bytes.length - offset < 8) { + int size = bytes.length - offset; + buffer.position(8 - size); + buffer.put(bytes, offset, size); + } else { + buffer.put(bytes, offset, 8); + } + buffer.flip(); + buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN); + int x0 = buffer.getInt(); + int x1 = buffer.getInt(); + long state = getState(x0, x1); + if (state == -1) { + return -1; + } + return getSeedForState(previousState(state)); + } + + /** Attempts to find a seed such that it generates the prime p. Returns -1 if no seed is found. */ + static long getSeedForPrime(BigInteger p) { + int confidence = 64; + Random rand = new Random(); + int size = p.bitLength(); + // Prime generation often sets the most significant bit. + // Hence, clearing the most significant bit can help to find + // the seed used for the prime generation. + for (BigInteger x : new BigInteger[] {p, p.clearBit(size - 1)}) { + long seed = getSeedFor(x); + if (seed != -1) { + rand.setSeed(seed); + BigInteger q = new BigInteger(size, confidence, rand); + if (q.equals(p)) { + return seed; + } + } + } + return -1; + } + + /** + * Checks whether p is a random prime. A prime generated with a secure random number generator + * passes with probability > 1-2^{-32}. No checks are performed for primes smaller than 96 bits. + * + * @throws GeneralSecurityException if the prime was generated using java.util.Random + */ + static void checkPrime(BigInteger p) throws GeneralSecurityException { + // We can't reliably detect java.util.Random for small primes. + if (p.bitLength() < 96) { + return; + } + long seed = getSeedForPrime(p); + if (seed != -1) { + throw new GeneralSecurityException( + "java.util.Random with seed " + seed + " was likely used to generate prime"); + } + } +} diff --git a/java/com/google/security/wycheproof/SpongyCastleAllTests.java b/java/com/google/security/wycheproof/SpongyCastleAllTests.java new file mode 100644 index 0000000..5566827 --- /dev/null +++ b/java/com/google/security/wycheproof/SpongyCastleAllTests.java @@ -0,0 +1,54 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +/** + * SpongyCastleTest excludes {@code @SlowTest} tests. + * SpongyCastleAllTests runs all tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesEaxTest.class, + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + DhTest.class, + DhiesTest.class, + DsaTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + EciesTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class, +}) +@Provider(ProviderType.SPONGY_CASTLE) +public final class SpongyCastleAllTests { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyThisProvider(new BouncyCastleProvider()); + } +} diff --git a/java/com/google/security/wycheproof/SpongyCastleTest.java b/java/com/google/security/wycheproof/SpongyCastleTest.java new file mode 100644 index 0000000..73c8ca1 --- /dev/null +++ b/java/com/google/security/wycheproof/SpongyCastleTest.java @@ -0,0 +1,56 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import com.google.security.wycheproof.WycheproofRunner.Fast; +import com.google.security.wycheproof.WycheproofRunner.Provider; +import com.google.security.wycheproof.WycheproofRunner.ProviderType; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite.SuiteClasses; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +/** + * Spongy Castle is a Bouncy Castle clone with a few changes to make it work on Android. + * SpongyCastleTest excludes {@code @SlowTest} tests. + */ +@RunWith(WycheproofRunner.class) +@SuiteClasses({ + AesEaxTest.class, + AesGcmTest.class, + BasicTest.class, + CipherInputStreamTest.class, + CipherOutputStreamTest.class, + DhTest.class, + DhiesTest.class, + DsaTest.class, + EcKeyTest.class, + EcdhTest.class, + EcdsaTest.class, + EciesTest.class, + RsaEncryptionTest.class, + RsaKeyTest.class, + RsaSignatureTest.class, +}) +@Provider(ProviderType.SPONGY_CASTLE) +@Fast +public final class SpongyCastleTest { + @BeforeClass + public static void setUp() throws Exception { + TestUtil.installOnlyThisProvider(new BouncyCastleProvider()); + } +} diff --git a/java/com/google/security/wycheproof/TestUtil.java b/java/com/google/security/wycheproof/TestUtil.java new file mode 100644 index 0000000..ae21adc --- /dev/null +++ b/java/com/google/security/wycheproof/TestUtil.java @@ -0,0 +1,99 @@ +/** + * @license + * 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. + */ + +package com.google.security.wycheproof; + +import java.nio.ByteBuffer; +import java.security.Provider; +import java.security.Security; + +/** Test utilities */ +public class TestUtil { + + public static String bytesToHex(byte[] bytes) { + // bytesToHex is used to convert output from Cipher. + // cipher.update can return null, which is equivalent to returning + // no plaitext rsp. ciphertext. + if (bytes == null) { + return ""; + } + String chars = "0123456789abcdef"; + StringBuilder result = new StringBuilder(2 * bytes.length); + for (byte b : bytes) { + // convert to unsigned + int val = b & 0xff; + result.append(chars.charAt(val / 16)); + result.append(chars.charAt(val % 16)); + } + return result.toString(); + } + + /** + * Returns a hexadecimal representation of the bytes written to ByteBuffer (i.e. all the bytes + * before position()). + */ + public static String byteBufferToHex(ByteBuffer buffer) { + ByteBuffer tmp = buffer.duplicate(); + tmp.flip(); + byte[] bytes = new byte[tmp.remaining()]; + tmp.get(bytes); + return bytesToHex(bytes); + } + + public static byte[] hexToBytes(String hex) throws IllegalArgumentException { + if (hex.length() % 2 != 0) { + throw new IllegalArgumentException("Expected a string of even length"); + } + int size = hex.length() / 2; + byte[] result = new byte[size]; + for (int i = 0; i < size; i++) { + int hi = Character.digit(hex.charAt(2 * i), 16); + int lo = Character.digit(hex.charAt(2 * i + 1), 16); + if ((hi == -1) || (lo == -1)) { + throw new IllegalArgumentException("input is not hexadecimal"); + } + result[i] = (byte) (16 * hi + lo); + } + return result; + } + + public static void installOnlyThisProvider(Provider provider) { + for (Provider p : Security.getProviders()) { + Security.removeProvider(p.getName()); + } + Security.insertProviderAt(provider, 1); + } + + public static void installOnlyOpenJDKProviders() throws Exception { + for (Provider p : Security.getProviders()) { + Security.removeProvider(p.getName()); + } + installOpenJDKProvider("com.sun.net.ssl.internal.ssl.Provider"); + installOpenJDKProvider("com.sun.crypto.provider.SunJCE"); + installOpenJDKProvider("com.sun.security.sasl.Provider"); + installOpenJDKProvider("org.jcp.xml.dsig.internal.dom.XMLDSigRI"); + installOpenJDKProvider("sun.security.ec.SunEC"); + installOpenJDKProvider("sun.security.jgss.SunProvider"); + installOpenJDKProvider("sun.security.provider.Sun"); + installOpenJDKProvider("sun.security.rsa.SunRsaSign"); + installOpenJDKProvider("sun.security.smartcardio.SunPCSC"); + } + + private static void installOpenJDKProvider(String className) throws Exception { + Provider provider = (Provider) Class.forName(className).getConstructor().newInstance(); + Security.insertProviderAt(provider, 1); + } +} diff --git a/java/com/google/security/wycheproof/WycheproofRunner.java b/java/com/google/security/wycheproof/WycheproofRunner.java new file mode 100644 index 0000000..e4da431 --- /dev/null +++ b/java/com/google/security/wycheproof/WycheproofRunner.java @@ -0,0 +1,206 @@ +/** + * @license + * Copyright 2013 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. + */ + +package com.google.security.wycheproof; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Arrays; +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.Suite; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + *

A custom JUnit4 runner that, with annotations, allows choosing tests to run on a specific + * provider. To use it, annotate a runner class with {@code RunWith(WycheproofRunner.class)}, and + * {@code SuiteClasses({AesGcmTest.class, ...})}. When you run this class, it will run all the tests + * in all the suite classes. + * + *

To exclude certain tests, a runner class should be annotated with {@code @Provider} which + * indicates the target provider. Test exclusion is defined as follows: + *