├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── csharp ├── README.md ├── RocaTest.sln └── RocaTest │ ├── App.config │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── RocaTest.cs │ ├── RocaTest.csproj │ ├── data │ ├── cert01.pem │ ├── cert02.pem │ ├── cert03.pem │ ├── cert04.pem │ ├── cert05.pem │ └── cert06.pem │ └── packages.config ├── docs ├── .gitignore ├── _config.yml ├── index.md └── roca_impact.png ├── java ├── BrokenKey.java └── README.md ├── roca ├── __init__.py ├── detect.py ├── detect_tls.py ├── generate.sage └── tests │ ├── README.md │ ├── __init__.py │ ├── data │ ├── cert01.pem │ ├── cert02.pem │ ├── cert03.pem │ ├── cert04.pem │ ├── cert05.pem │ ├── cert06.pem │ ├── csr03.pem │ ├── csr05.pem │ ├── key01.pgp │ ├── key02.pgp │ ├── key03.pgp │ ├── key04.pgp │ ├── mod01.txt │ ├── mod02.txt │ ├── mod03.txt │ ├── mod04.txt │ ├── mod05.txt │ ├── mod06.txt │ ├── mod07.txt │ ├── mod08.txt │ ├── mod09.txt │ ├── pkcs701.p7s │ ├── privkey03.pem │ ├── privkey04.pem │ ├── privkey05.pem │ ├── pubkey01.pem │ ├── pubkey02.pem │ ├── pubkey03.pem │ ├── ssh01.pub │ ├── ssh02.pub │ ├── ssh03.pub │ ├── ssh04.pub │ ├── ssh05.pub │ └── ssh06.pub │ ├── test_fingerprint.py │ └── test_tls.py ├── setup.cfg ├── setup.py ├── test-travis.sh └── test.sh /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 22 * * 3' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['python', 'csharp'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pyc 3 | Virtualenv 4 | dist/ 5 | build/ 6 | roca_detect.egg-info/ 7 | .env 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7.14" 4 | - "2.7" 5 | - "pypy" 6 | - "3.4" 7 | - "3.5" 8 | - "3.6" 9 | - "3.7" 10 | - "pypy3" 11 | install: 12 | - pip install . 13 | script: 14 | - PYTHONPATH=.:$PYTHONPATH ./test-travis.sh 15 | 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guide 2 | 3 | We are very happy to see an interest of the community in improving our detection for ROCA. 4 | We do appreciate it and thank you all! Issues and pull requests are welcome! 5 | 6 | With the rising number of feature requests and pull requests we need to set some basic rules to make repository 7 | tidy and working because we have only limited resources for managing this. 8 | 9 | ## Language ports 10 | 11 | In order to make development sustainable and ROCA tool working we would like to separate language ports to different repositories. 12 | 13 | Its in general better to have a separate project in the separate GIT repository. Moreover we cannot support large number 14 | of programming languages due to lack of resources and expertise. 15 | 16 | So if you created a new ROCA port we would be very happy to link it in our readme in `Language ports` section. 17 | Then please create a PR with the readme update. 18 | 19 | Please provide also test vectors. 20 | 21 | Thanks! 22 | 23 | ## Advanced features in python library 24 | 25 | We decided the original `detect.py` should do the basic stuff on the basic set of file formats. 26 | We don't wont to overcomplicate this detection tool by adding a lot of different command line switches and parameters. 27 | 28 | If you have some advanced feature implemented to it pls in a separate python file inside `roca` package. 29 | For the basic detection please import functions from `detect.py`. 30 | 31 | You can register your own tool in the `setup.py` so it has own command name, e.g., `roca-detect-censys` or 32 | `roca-detect-tls`. 33 | 34 | Please implement also tests for the new functionality. 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017, CRoCS, EnigmaBridge Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE 3 | include roca/tests/data/* 4 | include test.sh 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ROCA detection tool 2 | 3 | [![Build Status](https://travis-ci.org/crocs-muni/roca.svg?branch=master)](https://travis-ci.org/crocs-muni/roca) 4 | 5 | 6 | This tool is related to [ACM CCS 2017 conference paper #124 Return of the Coppersmith’s Attack: Practical Factorization of Widely Used RSA Moduli](https://crocs.fi.muni.cz/public/papers/rsa_ccs17). 7 | 8 | It enables you to test public RSA keys for a presence of the described vulnerability. 9 | 10 | *Update 4.11.2017*: Python 2.7, 3.4+ supported. 11 | 12 | *Update 30.10.2017*: The [paper](https://crocs.fi.muni.cz/_media/public/papers/nemec_roca_ccs17_preprint.pdf) of the attack is already online, 13 | [ACM version](https://dl.acm.org/citation.cfm?id=3133969). 14 | 15 | *Update 30.10.2017*: The discrete logarithm detector is now implemented in the Python and used as a default. It detects the structure 16 | in the primes exploited by the factorizing algorithm. 17 | 18 | Currently the tool supports the following key formats: 19 | 20 | - X509 Certificate, DER encoded, one per file, `*.der`, `*.crt` 21 | - X509 Certificate, PEM encoded, more per file, `*.pem` 22 | - X509 Certificate Signing Request, PEM encoded, more per file, `*.pem` 23 | - RSA PEM encoded private key, public key, more per file, `*.pem` (has to have correct header `-----BEGIN RSA...`) 24 | - SSH public key, `*.pub`, starting with "ssh-rsa", one per line 25 | - ASC encoded PGP key, `*.pgp`, `*.asc`. More per file, has to have correct header `-----BEGIN PGP...` 26 | - APK android application, `*.apk` 27 | - one modulus per line text file `*.txt`, modulus can be 28 | a) base64 encoded number, b) hex coded number, c) decimal coded number 29 | - JSON file with moduli, one record per line, record with modulus has 30 | key "mod" (int, base64, hex, dec encoding supported) 31 | certificate(s) with key "cert" / array of certificates with key "certs" are supported, base64 encoded DER. 32 | - LDIFF file - LDAP database dump. Any field ending with `;binary::` is attempted to decode as X509 certificate 33 | - Java Key Store file (JKS). Tries empty password & some common, specify more with `--jks-pass-file` 34 | - PKCS7 signature with user certificate 35 | 36 | The detection tool is intentionally one-file implementation for easy integration / manipulation. 37 | 38 | ## False positive 39 | 40 | False positive detection rates: 41 | 42 | * Moduli detector: 2^-27 43 | * Discrete logarithm detector: 2^-154 44 | 45 | Discrete logarithm detector is implemented only in the Python code, used as the default detection method. 46 | 47 | Java and C# code ports are unmaintained since the original publication and we don't plan to upgrade these 48 | detectors to the more precise method. However PR are welcome! 49 | 50 | ## Online checker 51 | 52 | https://keychest.net/roca 53 | 54 | The online checker is using the discrete logarithm detector algorithm. 55 | 56 | ## Install with pip 57 | 58 | Install the detector library + tool with `pip` (installs all dependencies): 59 | 60 | ``` 61 | pip install roca-detect 62 | ``` 63 | 64 | ## Local install 65 | 66 | Execute in the root folder of the package: 67 | 68 | ``` 69 | pip install --upgrade --find-links=. . 70 | ``` 71 | 72 | ## Dependencies 73 | 74 | It may be required to install additional dependencies so `pip` can install e.g. cryptography package. 75 | 76 | CentOS / RHEL: 77 | 78 | ``` 79 | sudo yum install python-devel python-pip gcc gcc-c++ make automake autoreconf libtool openssl-devel libffi-devel dialog 80 | ``` 81 | 82 | Ubuntu: 83 | ``` 84 | sudo apt-get install python-pip python-dev build-essential libssl-dev libffi-dev swig 85 | ``` 86 | 87 | ## Usage 88 | 89 | To print the basic usage: 90 | 91 | ``` 92 | # If installed with pip / manually 93 | roca-detect --help 94 | 95 | # Without installation (can miss dependencies) 96 | python roca/detect.py 97 | ``` 98 | 99 | The testing tool accepts multiple file names / directories as the input argument. 100 | It returns the report showing how many files has been fingerprinted (and which are those). 101 | 102 | **Example (no vulnerabilities found):** 103 | 104 | Running recursively on all my SSH keys and known_hosts: 105 | 106 | ``` 107 | $> roca-detect ~/.ssh 108 | 2017-10-16 13:39:21 [51272] INFO ### SUMMARY #################### 109 | 2017-10-16 13:39:21 [51272] INFO Records tested: 92 110 | 2017-10-16 13:39:21 [51272] INFO .. PEM certs: . . . 0 111 | 2017-10-16 13:39:21 [51272] INFO .. DER certs: . . . 0 112 | 2017-10-16 13:39:21 [51272] INFO .. RSA key files: . 16 113 | 2017-10-16 13:39:21 [51272] INFO .. PGP master keys: 0 114 | 2017-10-16 13:39:21 [51272] INFO .. PGP total keys: 0 115 | 2017-10-16 13:39:21 [51272] INFO .. SSH keys: . . . 76 116 | 2017-10-16 13:39:21 [51272] INFO .. APK keys: . . . 0 117 | 2017-10-16 13:39:21 [51272] INFO .. JSON keys: . . . 0 118 | 2017-10-16 13:39:21 [51272] INFO .. LDIFF certs: . . 0 119 | 2017-10-16 13:39:21 [51272] INFO .. JKS certs: . . . 0 120 | 2017-10-16 13:39:21 [51272] INFO .. PKCS7: . . . . . 0 121 | 2017-10-16 13:39:21 [51272] INFO No fingerprinted keys found (OK) 122 | 2017-10-16 13:39:21 [51272] INFO ################################ 123 | ``` 124 | 125 | **Example (vulnerabilities found):** 126 | 127 | Running recursively on all my SSH keys and known_hosts: 128 | 129 | ``` 130 | $> roca-detect ~/.ssh 131 | 2017-10-16 13:39:21 [51272] WARNING Fingerprint found in the Certificate 132 | ... 133 | 2017-10-16 13:39:21 [51272] INFO ### SUMMARY #################### 134 | 2017-10-16 13:39:21 [51272] INFO Records tested: 92 135 | 2017-10-16 13:39:21 [51272] INFO .. PEM certs: . . . 0 136 | 2017-10-16 13:39:21 [51272] INFO .. DER certs: . . . 0 137 | 2017-10-16 13:39:21 [51272] INFO .. RSA key files: . 16 138 | 2017-10-16 13:39:21 [51272] INFO .. PGP master keys: 0 139 | 2017-10-16 13:39:21 [51272] INFO .. PGP total keys: 0 140 | 2017-10-16 13:39:21 [51272] INFO .. SSH keys: . . . 76 141 | 2017-10-16 13:39:21 [51272] INFO .. APK keys: . . . 0 142 | 2017-10-16 13:39:21 [51272] INFO .. JSON keys: . . . 0 143 | 2017-10-16 13:39:21 [51272] INFO .. LDIFF certs: . . 0 144 | 2017-10-16 13:39:21 [51272] INFO .. JKS certs: . . . 0 145 | 2017-10-16 13:39:21 [51272] INFO .. PKCS7: . . . . . 0 146 | 2017-10-16 13:39:21 [51272] INFO Fingerprinted keys found: 1 147 | 2017-10-16 13:39:21 [51272] INFO WARNING: Potential vulnerability 148 | 2017-10-16 13:39:21 [51272] INFO ################################ 149 | ``` 150 | 151 | ## PGP key 152 | 153 | In order to test your PGP key you can export it from your email client or download it from the PGP key server such as 154 | https://pgp.mit.edu/ 155 | 156 | You can also use `gpg` command line utility to export your public key: 157 | 158 | ```bash 159 | gpg --armor --export your@email.com > mykey.asc 160 | ``` 161 | 162 | ## Advanced use case 163 | 164 | Detection tool extracts information about the key which can be displayed: 165 | 166 | ``` 167 | roca-detect.py --dump --flatten --indent ~/.ssh/ 168 | ``` 169 | 170 | ## TLS/SSL detection 171 | The `roca-detect-tls` detects certificates from remote TLS/SSL ports. Provide a file with a newline-delimited list of `address:port` entries and use that file as input. 172 | 173 | Example file: tls_list.txt 174 | ``` 175 | github.com:443 176 | google.com:443 177 | internal.example.com:8080 178 | ``` 179 | 180 | Then run: 181 | 182 | `roca-detect-tls tls_list.txt` 183 | 184 | ## Fake moduli 185 | 186 | It is possible to generate moduli that passes the moduli fingerprinting test but actually do not contain structure 187 | the factorization algorithm is using. Dlog moduli test do not mark those as positive. 188 | 189 | ## Advanced installation methods 190 | 191 | ### Virtual environment 192 | 193 | It is usually recommended to create a new python virtual environment for the project: 194 | 195 | ``` 196 | virtualenv ~/pyenv 197 | source ~/pyenv/bin/activate 198 | pip install --upgrade pip 199 | pip install --upgrade --find-links=. . 200 | ``` 201 | 202 | ### Separate Python 2.7.13 203 | 204 | We tested tool with Python 2.7.13 and it works (see Travis for more info). 205 | We have reports saying lower versions (<=2.6) do not work properly so we highly recommend using up to date Python 2.7 206 | 207 | Use `pyenv` to install a new Python version locally if you cannot / don't want to update system Python. 208 | 209 | It internally downloads Python sources and installs it to `~/.pyenv`. 210 | 211 | ``` 212 | git clone https://github.com/pyenv/pyenv.git ~/.pyenv 213 | echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc 214 | echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc 215 | echo 'eval "$(pyenv init -)"' >> ~/.bashrc 216 | exec $SHELL 217 | pyenv install 2.7.13 218 | pyenv local 2.7.13 219 | ``` 220 | 221 | ### Python 3 222 | 223 | Detection tools works also with Python 3.4+ 224 | 225 | ### Docker container 226 | 227 | Run via Docker container to avoid environment inconsistency. Dockerfile source can be audited at https://hub.docker.com/r/unnawut/roca-detect/. 228 | 229 | ``` 230 | docker run --rm -v /path/to/your/keys:/keys --network none unnawut/roca-detect 231 | ``` 232 | 233 | Make sure to use `--rm` and `--network none` flags to disable container's network connection and delete the container after running. 234 | 235 | 236 | ## Licensing 237 | 238 | Code is licensed under permissive MIT license. 239 | 240 | As there were requests on dual licensing under Apache 2.0 license (due to some doubts on compatibility) we are licensing 241 | the code also under Apache 2.0 license. 242 | 243 | Pick license that suits you better, either MIT or Apache 2.0. 244 | 245 | ## Language ports 246 | 247 | This section contains links to different GIT repositories with language ports 248 | 249 | - [Go](https://github.com/titanous/rocacheck) 250 | 251 | -------------------------------------------------------------------------------- /csharp/README.md: -------------------------------------------------------------------------------- 1 | # C# ROCA detector 2 | 3 | ROCA detector using the moduli detector. 4 | 5 | This detector port is unmaintained. Please refer to the original Python implementation for more details. 6 | 7 | -------------------------------------------------------------------------------- /csharp/RocaTest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RocaTest", "RocaTest\RocaTest.csproj", "{138AFECC-AB56-4ACD-81F6-494CFE98BFBB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {138AFECC-AB56-4ACD-81F6-494CFE98BFBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {138AFECC-AB56-4ACD-81F6-494CFE98BFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {138AFECC-AB56-4ACD-81F6-494CFE98BFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {138AFECC-AB56-4ACD-81F6-494CFE98BFBB}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {0F289EFD-BA30-45A5-8A68-D1B0191DB0F8} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /csharp/RocaTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /csharp/RocaTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Org.BouncyCastle.Crypto.Parameters; 4 | using Org.BouncyCastle.X509; 5 | 6 | namespace RocaTest 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | foreach (string certFile in Directory.GetFiles("data")) 13 | { 14 | if (TestCert(certFile)) 15 | Console.WriteLine(certFile + " - contains RSA public key vulnerable to ROCA (CVE-2017-15361)"); 16 | else 17 | Console.WriteLine(certFile + " - Certificate does not contain RSA public key vulnerable to ROCA (CVE-2017-15361)"); 18 | } 19 | } 20 | 21 | static bool TestCert(string certFile) 22 | { 23 | X509CertificateParser x509CertificateParser = new X509CertificateParser(); 24 | X509Certificate x509Certificate = x509CertificateParser.ReadCertificate(File.ReadAllBytes(certFile)); 25 | RsaKeyParameters rsaKeyParameters = x509Certificate.GetPublicKey() as RsaKeyParameters; 26 | return RocaTest.IsVulnerable(rsaKeyParameters); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /csharp/RocaTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RocaTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RocaTest")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("138afecc-ab56-4acd-81f6-494cfe98bfbb")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /csharp/RocaTest/RocaTest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on JAVA code created by Martin Paljak available at 3 | * https://github.com/crocs-muni/roca/blob/33d0344346ad6f6802ae3803a0e0e501eb06a024/java/BrokenKey.java 4 | * 5 | * https://github.com/crocs-muni/roca 6 | * 7 | * ROCA detector using the moduli detector. 8 | * This detector port is unmaintained. Please refer to the original Python implementation for more details. 9 | */ 10 | 11 | using Org.BouncyCastle.Crypto.Parameters; 12 | using Org.BouncyCastle.Math; 13 | 14 | namespace RocaTest 15 | { 16 | public static class RocaTest 17 | { 18 | private static BigInteger[] markers = new BigInteger[]{ 19 | new BigInteger("6"), 20 | new BigInteger("30"), 21 | new BigInteger("126"), 22 | new BigInteger("1026"), 23 | new BigInteger("5658"), 24 | new BigInteger("107286"), 25 | new BigInteger("199410"), 26 | new BigInteger("8388606"), 27 | new BigInteger("536870910"), 28 | new BigInteger("2147483646"), 29 | new BigInteger("67109890"), 30 | new BigInteger("2199023255550"), 31 | new BigInteger("8796093022206"), 32 | new BigInteger("140737488355326"), 33 | new BigInteger("5310023542746834"), 34 | new BigInteger("576460752303423486"), 35 | new BigInteger("1455791217086302986"), 36 | new BigInteger("147573952589676412926"), 37 | new BigInteger("20052041432995567486"), 38 | new BigInteger("6041388139249378920330"), 39 | new BigInteger("207530445072488465666"), 40 | new BigInteger("9671406556917033397649406"), 41 | new BigInteger("618970019642690137449562110"), 42 | new BigInteger("79228162521181866724264247298"), 43 | new BigInteger("2535301200456458802993406410750"), 44 | new BigInteger("1760368345969468176824550810518"), 45 | new BigInteger("50079290986288516948354744811034"), 46 | new BigInteger("473022961816146413042658758988474"), 47 | new BigInteger("10384593717069655257060992658440190"), 48 | new BigInteger("144390480366845522447407333004847678774"), 49 | new BigInteger("2722258935367507707706996859454145691646"), 50 | new BigInteger("174224571863520493293247799005065324265470"), 51 | new BigInteger("696898287454081973172991196020261297061886"), 52 | new BigInteger("713623846352979940529142984724747568191373310"), 53 | new BigInteger("1800793591454480341970779146165214289059119882"), 54 | new BigInteger("126304807362733370595828809000324029340048915994"), 55 | new BigInteger("11692013098647223345629478661730264157247460343806"), 56 | new BigInteger("187072209578355573530071658587684226515959365500926") 57 | }; 58 | 59 | private static int[] prims = new int[] { 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167 }; 60 | 61 | private static BigInteger[] primes = new BigInteger[prims.Length]; 62 | 63 | static RocaTest() 64 | { 65 | for (int i = 0; i < prims.Length; i++) 66 | { 67 | primes[i] = BigInteger.ValueOf(prims[i]); 68 | } 69 | } 70 | 71 | public static bool IsVulnerable(RsaKeyParameters rsaKey) 72 | { 73 | if (rsaKey == null) 74 | return false; 75 | 76 | for (int i = 0; i < primes.Length; i++) 77 | { 78 | if (BigInteger.One.ShiftLeft(rsaKey.Modulus.Remainder(primes[i]).IntValue).And(markers[i]).Equals(BigInteger.Zero)) 79 | { 80 | return false; 81 | } 82 | } 83 | 84 | return true; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /csharp/RocaTest/RocaTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {138AFECC-AB56-4ACD-81F6-494CFE98BFBB} 8 | Exe 9 | RocaTest 10 | RocaTest 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Portable.BouncyCastle.1.8.1.3\lib\net40\BouncyCastle.Crypto.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Always 49 | 50 | 51 | Always 52 | 53 | 54 | Always 55 | 56 | 57 | Always 58 | 59 | 60 | Always 61 | 62 | 63 | Always 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /csharp/RocaTest/data/cert01.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ 3 | MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT 4 | DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow 5 | SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT 6 | GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC 7 | AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF 8 | q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 9 | SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 10 | Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA 11 | a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj 12 | /PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T 13 | AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG 14 | CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv 15 | bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k 16 | c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw 17 | VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC 18 | ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz 19 | MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu 20 | Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF 21 | AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo 22 | uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ 23 | wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu 24 | X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG 25 | PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 26 | KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /csharp/RocaTest/data/cert02.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFETCCA/mgAwIBAgISBLwGPC99RzpJIpGR5fe085h1MA0GCSqGSIb3DQEBCwUA 3 | MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD 4 | ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNzA4MTEwODUwMDBaFw0x 5 | NzExMDkwODUwMDBaMCIxIDAeBgNVBAMTF2FuY2hvci5lbmlnbWFicmlkZ2UuY29t 6 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr2WWdhl/HbBtp6wRGgoO 7 | wXW4t+8HGS/80fa4VMMneF8af9PbfaRc1KMbKy5c4Ngjpo4oyK3xdHMWvszh/ldo 8 | BkA5rSBiJgyNGjTgWG3Om8EwkPzJ+4uLLAjtOujWGymBimaWiafZwdqwU7VX+40/ 9 | nET6rT4YxV5zmwDTyRJlWLyOmAzSrzdxJu9bE3QTZ3S4vTcOPBwUnbOVyPmWlrYo 10 | sQWUb+ogEG/iTRA6wGJmGpJI6MP2KOALMI0zlTqr5VUTLiGOdO9LV4cWtP5880Do 11 | 5gSjGb9umVHhlCYB00KRAy21SZnQnl0Dbd41RK01JWu7l9Xj//04Fmwh6ukZlUiF 12 | OwIDAQABo4ICFzCCAhMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF 13 | BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT/+523mWwe9kcK 14 | b8pG25FsYAK+lDAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggr 15 | BgEFBQcBAQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRz 16 | ZW5jcnlwdC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRz 17 | ZW5jcnlwdC5vcmcvMCIGA1UdEQQbMBmCF2FuY2hvci5lbmlnbWFicmlkZ2UuY29t 18 | MIH+BgNVHSAEgfYwgfMwCAYGZ4EMAQIBMIHmBgsrBgEEAYLfEwEBATCB1jAmBggr 19 | BgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwgasGCCsGAQUFBwIC 20 | MIGeDIGbVGhpcyBDZXJ0aWZpY2F0ZSBtYXkgb25seSBiZSByZWxpZWQgdXBvbiBi 21 | eSBSZWx5aW5nIFBhcnRpZXMgYW5kIG9ubHkgaW4gYWNjb3JkYW5jZSB3aXRoIHRo 22 | ZSBDZXJ0aWZpY2F0ZSBQb2xpY3kgZm91bmQgYXQgaHR0cHM6Ly9sZXRzZW5jcnlw 23 | dC5vcmcvcmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBAEjEX7Orx4EWqDA+ 24 | e5+a4nErwO6wVzH7gk6w6xdFGhYqvsYD4LSQzlWg+ETLXcvWIQMoTx5/rYtDxpG9 25 | 43qfZbdHjspVze2pfhR71sq76SZzthJw0tUeLFdo9NpFQziCe3fWP7eifX28oFbV 26 | He82hLWfgGFdJgJTPpLhwRYps6BLb8Le58YuVLChWqRw/JV5zSN3eHVQIejnpaf9 27 | Pwo3+WT/2BjyYLBUsbFBigZmy9/SHlfrQCBI2jxWYU2g4DNIZCekGOQqaJGKTBOz 28 | kgSivyzb4B8du0gaIq93rsLurueD/gJFSapDWWkX+OjXdL/kH1uyx+l+dUcSPc4t 29 | E0lRHOM= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /csharp/RocaTest/data/cert03.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEEzCCAvugAwIBAgIJAJ07mZHF8IutMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD 3 | VQQGEwJVSzESMBAGA1UECAwJQ2FtYnJpZGdlMRIwEAYDVQQHDAlDYW1icmlkZ2Ux 4 | GjAYBgNVBAoMEUVuaWdtYWJyaWRnZSBMdGQuMQswCQYDVQQLDAJJVDEZMBcGA1UE 5 | AwwQZW5pZ21hYnJpZGdlLmNvbTEkMCIGCSqGSIb3DQEJARYVaW5mb0BlbmlnbWFi 6 | cmlkZ2UuY29tMB4XDTE3MDkxNDEyNDgyNVoXDTQ0MDIxNTEyNDgyNVowgZ8xCzAJ 7 | BgNVBAYTAlVLMRIwEAYDVQQIDAlDYW1icmlkZ2UxEjAQBgNVBAcMCUNhbWJyaWRn 8 | ZTEaMBgGA1UECgwRRW5pZ21hYnJpZGdlIEx0ZC4xCzAJBgNVBAsMAklUMRkwFwYD 9 | VQQDDBBlbmlnbWFicmlkZ2UuY29tMSQwIgYJKoZIhvcNAQkBFhVpbmZvQGVuaWdt 10 | YWJyaWRnZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6aPxf 11 | u2BsTUKKuYs1XWYhTbT35HW19//dw0nHP31lmWUSGrFqQBTmxGXBEdhQIFhT0RP3 12 | EhJ+SiMKqX9IHu1kEPbDRfSOJfuBxC7GgV3DXN2Dng3pH7K+yEjQkQ+nfiwx+Z7J 13 | vMftFngSvXJDGLCt9JLWtHxc4FAYTod+pkBF+VAb9wVpz/0QLbcpn0npwAIir5EV 14 | 0Kraln3+dZt+RXboXDpP9ZQDkjG5C0SLkMMjYIO8+Bm8MY1TogGOybQB8QK6nuE9 15 | 3lkqQrAy3xNmidnFYZ21XVGYU6Qi0xAGJNLZ5Hghg3X0YMJpMO2ozQ2Uflj/6be+ 16 | JEU1bertmIIj4+0dAgMBAAGjUDBOMB0GA1UdDgQWBBR7Uga0wsLRKMtxpKw6wYCU 17 | V3w1rTAfBgNVHSMEGDAWgBR7Uga0wsLRKMtxpKw6wYCUV3w1rTAMBgNVHRMEBTAD 18 | AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAIX5GIGYilxT0YwbW98aCTZO+0/cacX/Ej 19 | Wx4hBxAI9k48KqqeebdbmexxscXPlEoPSD+tx++xmc7rDhDjwosCTa2YOaPsHUyV 20 | 83zUfP9i3j3+N6T42+R7XOLvCq7YyIKZj0WZr33JTFCrbuYFkZxKRFuvS1LPiUaI 21 | 8TbDHQ+1wG4S2PgClZpo2wXR9zfGKqNMbSb2qRgLI5f1Yz7So2euJDgbYAouPOlL 22 | uFE17TxjVsMmuNhFKTS+G+i/L1T4aa5w4IB459jXiSug64SF59m2HA7WsjXLnBQ+ 23 | QWmzZ9YctSkvSC5nZkjEt6KjyZjJrofatjsy31fF3T/niayJj+0b 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /csharp/RocaTest/data/cert04.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpTCCAYwCCQC2u0PIfFaGMjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMDE2MTkzODIxWhcNMTgxMDE2MTkzODIxWjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQJZ 5 | J7UrpeaMjJJou5IY83ZzYUymVBj0dFsUPNTuU/lJHJoOHC8jqVFjBq/784ZnuHG8 6 | DMguYPW7Gp+hWlZxp2XJ8huVh9gBFZZDcqODyIOw3L9sd1cGsx6v8+P9SIVZoIze 7 | og+al8TFm2uKjuykV9SoINSVCfdZM2MCvKGjaQsICRgR+Fjy6M6lpiNVrW4EHRk1 8 | 7aWSibWXaDtz4mV650v/x2Dk1RPMh9uTVZGOqgjTmLvl9oNdyHElIRubNrOgvHC5 9 | k6bLP30stAYd5z25cslCrfmVW2/kzZDwDQiK7ASvH17/kfIa9e1EYXx9uAk/lTZt 10 | smWAxK85neuU+bFBMFvhAgMBAAEwDQYJKoZIhvcNAQELBQADggECAAG7W49CYRUk 11 | YAFRGXu3M85MKOISyc/kkJ8nbHdV6GxJ05FkoDKbcbZ7nncJiIp2VMAMEIP4bRTJ 12 | 5U4g4vSZlmCs8BDmV3Ts/tbDCM6eqK+2hwjhUnCnmmsLt4xVUeAAsWUHl9AVtjzd 13 | oYlm1Kk20QBzNpsvM/gFS5B+duHvTSfELfoq9Pdfvmn2gEXJHe9scO8bfT3fm15z 14 | R6AUYsSsxAhup2Rix6jgJ14KGsh6uVm6jhz9aBTBcgx7iMuuP8zUbUE6nryHYXnR 15 | cSvuYSesTCoFfnL7elrZDak/n0jLfwUD80aWnReJfu9QQGdqdDnSG8lSQ1XPOC7O 16 | /hFW9l0TCzOE 17 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /csharp/RocaTest/data/cert05.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpTCCAYwCCQCEcnuDiu6k0DANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMDE2MTk0NjEwWhcNMTgxMDE2MTk0NjEwWjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQGp 5 | xaaH24+yow+C3/jdoVTCke8nsEsl4YW4qyDExqX2BIWf5Z7eVKCvfA5yiIAK1xk7 6 | tzGlvx4RcNehxmkw1B6j6yHe33uK9dQqkcNPcgRblu/VfLGCMB3cwmln5VCOmriJ 7 | mG1X6Dff5w+Z/DfEmPQ8F8O5sGTPcERD16Az4DqCFpeqvzD7Ke59M7N2eWWokFjj 8 | s5dUvTocAH0oojsM+nn51xGsbkxLT3ewY0sezWhZGvA8fVO2Kl8tkRY9TvT48mgx 9 | rkYe2F7F94jFEj/8Esh0IP3P6h0bSlLDUhgk+zmYNIc5tiqYbY52siibwuuLwG0T 10 | gUeFG4OgcYPSEFWfkGMhAgMBAAEwDQYJKoZIhvcNAQELBQADggECAAGZiTHt6Hgd 11 | yDjR9aH9O5oNvtmTPf6tJtKhIu1VEnWjxpXK35Dzmw5AvSSL4EVB/nAYx63eonfI 12 | 4BLM3YOPLFzrIJn6LE37LMug90vCz8zPOChWR7HTgroLyh3O2nPjvJL/SuoBya6g 13 | OwKg6gqF4Kmc4Q3BAY8QiDnQP3pIxF46WKq+r6/QqILuZRE9W7SwjTBP1jKmVztM 14 | 3tyL9kVl7RbRA9/ovXHlEfkN7lJqqYKMZvJZPsxm6VJQRsW1rl55bI8Vqys2DOnx 15 | Nm01jtxuMVukAWEj4J8ExCKnd5EhRO/fbOfbwUVythhIsevkdXUC3PiZhDcK+Cge 16 | PbJmeM8KehS3 17 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /csharp/RocaTest/data/cert06.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpDCCAYwCCQCvX1dxp53lPjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMDE2MTk0NjI4WhcNMTgxMDE2MTk0NjI4WjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb 5 | GJ1aMyQCCo8CnKN8qpjwTQiCtojYL8amNVXx9vOePer92imKYXJ4Ng/hVVV82pCa 6 | XF84VgvcrezH9Wwy9qAIbQmAfp081SbrAj0TZvWah2rJlVlnaeWZwRlRxzV8qaOr 7 | +6A1prucdQQef19maTBCQNA2Pji9kWvfrli+ClwM2DgASv9F2iC0fjAZagM98htX 8 | Q0apdqP/klQSD67BXVCgJFt1a/5j4/o6vB1tCV2fUat1rXW2En5RC/3jdL9ErWSk 9 | bT4tbHIk4m+EtB/Peugt9jTFT6eZ4kKwP+0mANJGC9iwZpyWd38gBpFNSIaLAccf 10 | +EeNpIs+c21uke9kpq3fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADZBh7oh7Wed 11 | PcbKIKqDZpk1GGV/Z9BYywft+kyutGhNYpX00gJBV7sYiCaLqpJMR+POGuLV4Fcu 12 | SiVzlPXvYKme4nL8JemGrmLd9JnCddMETiAdZ9Z4RizHj0jyle94ceWIhtletyt1 13 | tg7AfkRP6vUV69Mb0e1ieyOWPvAScAFMmlQ+QNG8bNL9+iVGtfsbEOX/ptuB/Enx 14 | tO/37ACqf0UeKczWC9rpRB7CtRzOLOmFS8aANGrd+lAp0TBkteOBhlmW6066qDL6 15 | cZBiL14ytOcU5LhfmcX6lrwUefOlLH/J8pLFb1g5NJxTooC17CFP/55Tab0Z1JER 16 | 7FpVsNDAPGM= 17 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /csharp/RocaTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crocs-muni/roca/df6071d502f68701427f8b1d409cab22055ad1b7/docs/.gitignore -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Public disclosure: Vulnerable RSA generation CVE-2017-15361 2 | 3 | ## TLDR 4 | 5 | A newly discovered vulnerability in generation of RSA keys used by a software library adopted in cryptographic smartcards, security tokens and other secure hardware chips manufactured by Infineon Technologies AG allows for a practical factorization attack, in which the attacker computes the private part of an RSA key. The attack is feasible for commonly used key lengths, including 1024 and 2048 bits, and affects chips manufactured as early as 2012, that are now commonplace. Assess your keys now with the provided 6 | [offline and online detection tools](https://crocs.fi.muni.cz/private/petrs/roca#mitigation_and_workarounds) and 7 | contact your vendor if you are affected. Major vendors including Microsoft, Google, HP, Lenovo, Fujitsu already 8 | released the software updates and guidelines for a mitigation. Full details including the factorization method 9 | will be released in 2 weeks at the [ACM CCS conference](https://www.sigsac.org/ccs/CCS2017) as 10 | 'The Return of Coppersmith's Attack: Practical Factorization of Widely Used RSA Moduli' (ROCA) research paper. 11 | 12 | Update: 13 | 14 | The [paper](https://crocs.fi.muni.cz/_media/public/papers/nemec_roca_ccs17_preprint.pdf) of the attack is already online, 15 | [ACM version](https://dl.acm.org/citation.cfm?id=3133969). 16 | 17 | ## Description of the vulnerability 18 | 19 | A security vulnerability was found in the implementation of RSA keypair generation in a cryptographic library used in a wide range of cryptographic chips produced by Infineon Technologies AG. The product is also integrated in authentication, signature and encryption tokens of other vendors and chips used for Trusted Boot of operating systems. The vulnerability is present in NIST FIPS 140-2 and CC EAL 5+ certified devices since at least the year 2012. 20 | 21 | ![ROCA Impact](roca_impact.png "ROCA Impact") 22 | 23 | The algorithmic vulnerability is characterized by a specific structure of the generated RSA primes, which makes factorization of commonly used key lengths including 1024 and 2048 bits practically possible. Only the knowledge of a public key is necessary and no physical access to the vulnerable device is required. The vulnerability does NOT depend on a weak or a faulty random number generator - all RSA keys generated by a vulnerable chip are impacted. The attack was practically verified for several randomly selected 1024-bit RSA keys and for several selected 2048-bit keys. 24 | 25 | The specific structure of the primes in question allows for a fast detection of vulnerable keys, even in very large datasets. This property is useful for mitigation (users can assess own keys for vulnerability), but also for potential attackers (keys vulnerable to factorization can be pre-selected, without undergoing time-consuming factorization attempts). 26 | The worst cases for the factorization of 1024 and 2048-bit keys are less than 3 CPU-months and 100 CPU-years, respectively, on a single core of a common recent CPU, while the expected time is half of that of the worst case. The factorization can be easily parallelized on multiple CPUs. Where //k// CPUs are available, the wall time required for the attack will be reduced //k-times// - allowing for practical factorization in order of hours or days. The worst-case price of the factorization on an Amazon AWS c4 computation instance is $76 for the 1024-bit key and about $40,000 for the 2048-bit key. 27 | 28 | The difficulty of the factorization attack is not the same for all key lengths and is NOT strictly increasing (some longer keys may take less time to factorize than other shorter ones). The following key length ranges are now considered practically factorizable (time complexity between hours to 1000 CPU years at maximum): 512 to 704 bits, 992 to 1216 bits and 1984 to 2144 bits. Note that 4096-bit RSA key is not practically factorizable now, but may become so, if the attack is improved. 29 | 30 | The time complexity and cost for the selected key lengths (Intel E5-2650 v3@3GHz Q2/2014): 31 | * 512 bit RSA keys ~ 2 CPU hours (the cost of $0.06); 32 | * 1024 bit RSA keys ~ 97 CPU days (the cost of $40-$80); 33 | * 2048 bit RSA keys ~ 140.8 CPU years, (the cost of $20,000 - $40,000). 34 | 35 | The vulnerability was found by a close inspection of a large number of RSA keys generated and exported from the manufacturer smartcards by researchers at CRoCS laboratory, Masaryk University, Enigma Bridge and Ca' Foscari University. The full results will be presented at an academic ACM Conference on Computer and Communications Security (ACM CCS '17) starting from October 30th. 36 | 37 | The vulnerability was disclosed to Infineon Technologies AG, following the responsible disclosure principle, in the first week of February with agreement of an 8 month period before a public disclosure. We cooperated with the manufacturer and other affected parties to help evaluate and mitigate this vulnerability during this period. Major vendors including Microsoft, Google, HP, Lenovo, Fujitsu already released the software updates and guidelines for a mitigation. We are now notifying general public and releasing tools for assessmnet of the individual keys. 38 | 39 | ## Impact 40 | A remote attacker can compute an RSA private key from the value of a public key. The private key can be misused for impersonation of a legitimate owner, decryption of sensitive messages, forgery of signatures (such as for software releases) and other related attacks. 41 | 42 | The actual impact of the vulnerability depends on the usage scenario, availability of the public keys and the lengths of keys used. We found and analyzed vulnerable keys in various domains including electronic citizen documents, authentication tokens, trusted boot devices, software package signing, TLS/HTTPS keys and PGP. The currently confirmed number of vulnerable keys found is about 760,000 but possibly up to two to three magnitudes more are vulnerable. The details will be presented in two weeks at the ACM CCS conference. 43 | 44 | The vulnerable chips are pervasive and not necessarily sold directly by Infineon Technologies AG, as the chips can be embedded inside devices of other manufacturers. 45 | 46 | ## Detection tools, mitigation and workarounds {#mitigation} 47 | 48 | The first step is to detect if you use a chip with the vulnerable library. As the vulnerability is present in the on-chip software library and not limited just to a particular batch of hardware, the only reliable way is to generate an RSA keypair on the device and test the public key by the provided tools (see below). It is recommended to test also the keys already in use. We believe the tools are very accurate - it is highly unlikely that a secure key would be flagged, as well as that a vulnerable key would be missed. 49 | 50 | We provide the following tools: 51 | * **Offline testers**: Python/Java/C++ applications and tutorials ([https://github.com/crocs-muni/roca](https://github.com/crocs-muni/roca)). We release all offline tools under the MIT license so it can be embedded into other testing applications and services. 52 | * **Online testers**: Upload public key to [https://keychest.net/roca](https://keychest.net/roca) or [https://keytester.cryptosense.com](https://keytester.cryptosense.com) to test your key. 53 | * **Email S-MIME/PGP tester**: Send a signed email to to obtain an automatic email response with the analysis of the signing key vulnerability. 54 | 55 | If a vulnerable key is found, then you should contact your device vendor for further advice. 56 | 57 | The following general advices may apply: 58 | * Apply the software update if available. 59 | * Replace the device with one without the vulnerable library. 60 | * Generate a secure RSA keypair outside the device (e.g., via the OpenSSL library) and import it to the device. We are not aware of any vulnerability in connection with the actual use of the key, only the generation phase has a confirmed vulnerability. 61 | * Use other cryptographic algorithm (e.g., ECC) instead of RSA on affected devices. 62 | * Apply additional risk management within your environment, if the RSA key in use is detected as vulnerable. 63 | * Use key lengths which are not currently impacted (e.g., 3936 bits) by our factorization method. Be aware: use this specific mitigation only as a last resort, as the attack may be improved. 64 | 65 | ## Team 66 | The vulnerability was discovered by Slovak and Czech security researchers from the Centre for Research on Cryptography and Security at Masaryk University, Czech Republic; Enigma Bridge Ltd, Cambridge, UK; and Ca' Foscari University of Venice, Italy. 67 | 68 | ## Updates 69 | * 2nd of November 2017 - Presentation of all details at the ACM CCS conference (to come) 70 | * 16th of October 2017 - The initial version of the public disclosure published 71 | * May to October 2017 - Cooperation with the manufacturer and other affected parties to help evaluate and mitigate the vulnerability 72 | * 1st of February - The vulnerability disclosed to Infineon Technologies AG 73 | * End of January - The vulnerability found 74 | 75 | 76 | ## Q&A 77 | 78 | **Q:** Is the RSA algorithm insecure in general after the disclosure of this vulnerability? 79 | 80 | **A:** No. If the RSA primes are generated as a truly random numbers, our method cannot be applied. For example, keys generated by the OpenSSL library are NOT affected. 81 | 82 | 83 | **Q:** How can I be sure that my keys are not affected? 84 | 85 | **A:** Run one of the provided detection tools ([Mitigation](#mitigation)). We believe that the tool has practically no false positives (if a key is flagged as vulnerable, it is vulnerable) and no false negatives (if a key is not detected as vulnerable, it is safe against our attack). 86 | 87 | 88 | **Q:** How can I mitigate the vulnerability? 89 | 90 | **A:** Contact your vendor and ask for help, apply the patch if available. 91 | 92 | 93 | **Q:** There was a recent (10th of October) security advisory regarding Trusted Platform Modules (TPM) by Microsoft, Google and other vendors. Is this connected to the vulnerability announced in this disclosure? 94 | 95 | **A:** Yes, this is the direct result of the vulnerability found as the affected devices are using TPMs with the vulnerable library. 96 | 97 | 98 | **Q:** Are ECC keys generated on the affected chips vulnerable? 99 | 100 | **A:** No. We found no vulnerability in the ECC key generation of the vulnerable library. 101 | 102 | 103 | **Q:** The Coppershmith's attack was already used to factorize keys from cryptographic smartcards. Is this an instance of the same vulnerability? 104 | 105 | **A:** No, this one is different. Daniel J. Bernstein, Yun-An Chang, Chen-Mou Cheng, Li-Ping Chou, Nadia Heninger, Tanja Lange and Nicko van Someren in 2013 devised a method to exploit flawed random number generator in Taiwanese citizen cards, present due to a manufacturing defect in some of the [cards](https://smartfacts.cr.yp.to/smartfacts-20130916.pdf). This new vulnerability applies to all chips equipped with the vulnerable library. 106 | 107 | 108 | **Q:** How was the vulnerability found? 109 | 110 | **A:** We analyzed the statistical properties of a large number of RSA keys generated and extracted from various smartcards during the previous research work published at [USENIX Security 2016 conference](https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/svenda). No reverse engineering of the chip or the library was performed. 111 | 112 | 113 | **Q:** Are FIDO U2F authentication tokens affected? 114 | 115 | **A:** No. The U2F specifications only allow for ECC-based authentication. 116 | 117 | 118 | 119 | ## Technical references 120 | 121 | * Infineon, Information on TPM firmware update for Microsoft Windows systems https://www.infineon.com/cms/en/product/promopages/tpm-update/?redirId=59160 122 | * Microsoft Vulnerability in TPM could allow Security Feature Bypass: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV170012 123 | * Google, The Chromium project Trusted Platform Module firmware vulnerability: https://sites.google.com/a/chromium.org/dev/chromium-os/tpm_firmware_update 124 | 125 | -------------------------------------------------------------------------------- /docs/roca_impact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crocs-muni/roca/df6071d502f68701427f8b1d409cab22055ad1b7/docs/roca_impact.png -------------------------------------------------------------------------------- /java/BrokenKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Credits: ported to Java by Martin Paljak 3 | * https://github.com/crocs-muni/roca 4 | * 5 | * ROCA detector using the moduli detector. 6 | * This detector port is unmaintained. Please refer to the original Python implementation for more details. 7 | */ 8 | import java.math.BigInteger; 9 | import java.security.cert.X509Certificate; 10 | import java.security.interfaces.RSAPublicKey; 11 | 12 | public class BrokenKey { 13 | private static final int[] prims = new int[]{3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 14 | 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167}; 15 | private static final BigInteger[] primes = new BigInteger[prims.length]; 16 | 17 | static { 18 | for (int i = 0; i < prims.length; i++) { 19 | primes[i] = BigInteger.valueOf(prims[i]); 20 | } 21 | } 22 | 23 | private static final BigInteger[] markers = new BigInteger[]{ 24 | new BigInteger("6"), 25 | new BigInteger("30"), 26 | new BigInteger("126"), 27 | new BigInteger("1026"), 28 | new BigInteger("5658"), 29 | new BigInteger("107286"), 30 | new BigInteger("199410"), 31 | new BigInteger("8388606"), 32 | new BigInteger("536870910"), 33 | new BigInteger("2147483646"), 34 | new BigInteger("67109890"), 35 | new BigInteger("2199023255550"), 36 | new BigInteger("8796093022206"), 37 | new BigInteger("140737488355326"), 38 | new BigInteger("5310023542746834"), 39 | new BigInteger("576460752303423486"), 40 | new BigInteger("1455791217086302986"), 41 | new BigInteger("147573952589676412926"), 42 | new BigInteger("20052041432995567486"), 43 | new BigInteger("6041388139249378920330"), 44 | new BigInteger("207530445072488465666"), 45 | new BigInteger("9671406556917033397649406"), 46 | new BigInteger("618970019642690137449562110"), 47 | new BigInteger("79228162521181866724264247298"), 48 | new BigInteger("2535301200456458802993406410750"), 49 | new BigInteger("1760368345969468176824550810518"), 50 | new BigInteger("50079290986288516948354744811034"), 51 | new BigInteger("473022961816146413042658758988474"), 52 | new BigInteger("10384593717069655257060992658440190"), 53 | new BigInteger("144390480366845522447407333004847678774"), 54 | new BigInteger("2722258935367507707706996859454145691646"), 55 | new BigInteger("174224571863520493293247799005065324265470"), 56 | new BigInteger("696898287454081973172991196020261297061886"), 57 | new BigInteger("713623846352979940529142984724747568191373310"), 58 | new BigInteger("1800793591454480341970779146165214289059119882"), 59 | new BigInteger("126304807362733370595828809000324029340048915994"), 60 | new BigInteger("11692013098647223345629478661730264157247460343806"), 61 | new BigInteger("187072209578355573530071658587684226515959365500926") 62 | }; 63 | 64 | public static boolean isAffected(X509Certificate c) { 65 | if (!(c.getPublicKey() instanceof RSAPublicKey)) 66 | return false; 67 | 68 | return isAffected((RSAPublicKey) c.getPublicKey()); 69 | } 70 | 71 | public static boolean isAffected(RSAPublicKey pubkey) { 72 | BigInteger modulus = pubkey.getModulus(); 73 | 74 | for (int i = 0; i < primes.length; i++) { 75 | if (BigInteger.ONE.shiftLeft(modulus.remainder(primes[i]).intValue()).and(markers[i]).equals(BigInteger.ZERO)) { 76 | return false; 77 | } 78 | } 79 | 80 | return true; 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /java/README.md: -------------------------------------------------------------------------------- 1 | # Java ROCA detector 2 | 3 | ROCA detector using the moduli detector. 4 | 5 | This detector port is unmaintained. Please refer to the original Python implementation for more details. 6 | 7 | Credits: Martin Paljak 8 | 9 | -------------------------------------------------------------------------------- /roca/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crocs-muni/roca/df6071d502f68701427f8b1d409cab22055ad1b7/roca/__init__.py -------------------------------------------------------------------------------- /roca/detect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Key fingerprinting 6 | 7 | The fingerprinter supports the following formats: 8 | 9 | - X509 Certificate, DER encoded, one per file, *.der, *.crt 10 | - X509 Certificate, PEM encoded, more per file, *.pem 11 | - RSA PEM encoded private key, public key, more per file, *.pem (has to have correct header -----BEGIN RSA...) 12 | - SSH public key, *.pub, starting with "ssh-rsa", one per line 13 | - ASC encoded PGP key, *.pgp, *.asc. More per file, has to have correct header -----BEGIN PGP... 14 | - APK android application, *.apk 15 | - one modulus per line text file *.txt, modulus can be 16 | a) base64 encoded number, b) hex coded number, c) decimal coded number 17 | - JSON file with moduli, one record per line, record with modulus has 18 | key "mod" (int, base64, hex, dec encoding supported) 19 | certificate(s) with key "cert" / array of certificates with key "certs" are supported, base64 encoded DER. 20 | - LDIFF file - LDAP database dump. Any field ending with ";binary::" is attempted to decode as X509 certificate 21 | - Java Key Store file (JKS). Tries empty password & some common, specify more with --jks-pass-file 22 | - PKCS7 signature with user certificate. 23 | 24 | Script requirements: 25 | 26 | - Tested on Python 2.7.13, 3.4, 3.5, 3.6 27 | - pip install cryptography pgpdump coloredlogs future six pycrypto>=2.6 python-dateutil pyx509_ph4 apk_parse_ph4 pyjks M2Crypto 28 | - some system packages are usually needed for pip to install dependencies (like gcc): 29 | sudo sudo yum install python-devel python-pip gcc gcc-c++ make automake autoreconf libtool openssl-devel libffi-devel dialog 30 | sudo apt-get install python-pip python-dev build-essential libssl-dev libffi-dev 31 | 32 | https://github.com/crocs-muni/roca 33 | https://roca.crocs.fi.muni.cz 34 | """ 35 | 36 | from future.utils import iteritems 37 | from builtins import bytes 38 | from past.builtins import basestring 39 | from past.builtins import long 40 | 41 | from functools import reduce 42 | 43 | import json 44 | import argparse 45 | import logging 46 | import coloredlogs 47 | import base64 48 | import hashlib 49 | import sys 50 | import os 51 | import re 52 | import math 53 | import itertools 54 | import binascii 55 | import collections 56 | import traceback 57 | import datetime 58 | from math import ceil, log 59 | 60 | 61 | # '%(asctime)s %(hostname)s %(name)s[%(process)d] %(levelname)s %(message)s' 62 | LOG_FORMAT = '%(asctime)s [%(process)d] %(levelname)s %(message)s' 63 | 64 | 65 | logger = logging.getLogger(__name__) 66 | coloredlogs.install(level=logging.INFO, fmt=LOG_FORMAT) 67 | 68 | 69 | # 70 | # Helper functions & classes 71 | # 72 | 73 | def strip_hex_prefix(x): 74 | """ 75 | Strips possible hex prefixes from the strings 76 | :param x: 77 | :return: 78 | """ 79 | if startswith(x, '0x'): 80 | return x[2:] 81 | if startswith(x, '\\x'): 82 | return x[2:] 83 | return x 84 | 85 | 86 | def error_message(e, message=None, cause=None): 87 | """ 88 | Formats exception message + cause 89 | :param e: 90 | :param message: 91 | :param cause: 92 | :return: formatted message, includes cause if any is set 93 | """ 94 | if message is None and cause is None: 95 | return None 96 | elif message is None: 97 | return '%s, caused by %r' % (e.__class__, cause) 98 | elif cause is None: 99 | return message 100 | else: 101 | return '%s, caused by %r' % (message, cause) 102 | 103 | 104 | def format_pgp_key(key): 105 | """ 106 | Formats PGP key in 16hex digits 107 | :param key: 108 | :return: 109 | """ 110 | if key is None: 111 | return None 112 | if isinstance(key, (int, long)): 113 | return '%016x' % key 114 | elif isinstance(key, list): 115 | return [format_pgp_key(x) for x in key] 116 | else: 117 | key = key.strip() 118 | key = strip_hex_prefix(key) 119 | return format_pgp_key(int(key, 16)) 120 | 121 | 122 | def defval(val, default=None): 123 | """ 124 | Returns val if is not None, default instead 125 | :param val: 126 | :param default: 127 | :return: 128 | """ 129 | return val if val is not None else default 130 | 131 | 132 | def defvalkey(js, key, default=None, take_none=True): 133 | """ 134 | Returns js[key] if set, otherwise default. Note js[key] can be None. 135 | :param js: 136 | :param key: 137 | :param default: 138 | :param take_none: 139 | :return: 140 | """ 141 | if js is None: 142 | return default 143 | if key not in js: 144 | return default 145 | if js[key] is None and not take_none: 146 | return default 147 | return js[key] 148 | 149 | 150 | def drop_none(arr): 151 | """ 152 | Drop none from the list 153 | :param arr: 154 | :return: 155 | """ 156 | if arr is None: 157 | return arr 158 | return [x for x in arr if x is not None] 159 | 160 | 161 | def drop_empty(arr): 162 | """ 163 | Drop empty array element 164 | :param arr: 165 | :return: 166 | """ 167 | return [x for x in arr if not isinstance(x, list) or len(x) > 0] 168 | 169 | 170 | def add_res(acc, elem): 171 | """ 172 | Adds results to the accumulator 173 | :param acc: 174 | :param elem: 175 | :return: 176 | """ 177 | if not isinstance(elem, list): 178 | elem = [elem] 179 | if acc is None: 180 | acc = [] 181 | for x in elem: 182 | acc.append(x) 183 | return acc 184 | 185 | 186 | def flatten(iterable): 187 | """ 188 | Non-recursive flatten. 189 | :param iterable: 190 | :return: 191 | """ 192 | try: 193 | iterator, sentinel, stack = iter(iterable), object(), [] 194 | except TypeError: 195 | yield iterable 196 | return 197 | 198 | while True: 199 | value = next(iterator, sentinel) 200 | if value is sentinel: 201 | if not stack: 202 | break 203 | iterator = stack.pop() 204 | elif isinstance(value, str): 205 | yield value 206 | else: 207 | try: 208 | new_iterator = iter(value) 209 | except TypeError: 210 | yield value 211 | else: 212 | stack.append(iterator) 213 | iterator = new_iterator 214 | 215 | 216 | def try_get_dn_part(subject, oid=None): 217 | """ 218 | Tries to extracts the OID from the X500 name. 219 | :param subject: 220 | :param oid: 221 | :return: 222 | """ 223 | try: 224 | if subject is None: 225 | return None 226 | if oid is None: 227 | return None 228 | 229 | for sub in subject: 230 | if oid is not None and sub.oid == oid: 231 | return sub.value 232 | except: 233 | pass 234 | return None 235 | 236 | 237 | def try_get_dn_string(subject, shorten=False): 238 | """ 239 | Returns DN as a string 240 | :param subject: 241 | :param shorten: 242 | :return: 243 | """ 244 | try: 245 | from cryptography.x509.oid import NameOID 246 | from cryptography.x509 import ObjectIdentifier 247 | oid_names = { 248 | getattr(NameOID, 'COMMON_NAME', ObjectIdentifier("2.5.4.3")): "CN", 249 | getattr(NameOID, 'COUNTRY_NAME', ObjectIdentifier("2.5.4.6")): "C", 250 | getattr(NameOID, 'LOCALITY_NAME', ObjectIdentifier("2.5.4.7")): "L", 251 | getattr(NameOID, 'STATE_OR_PROVINCE_NAME', ObjectIdentifier("2.5.4.8")): "ST", 252 | getattr(NameOID, 'STREET_ADDRESS', ObjectIdentifier("2.5.4.9")): "St", 253 | getattr(NameOID, 'ORGANIZATION_NAME', ObjectIdentifier("2.5.4.10")): "O", 254 | getattr(NameOID, 'ORGANIZATIONAL_UNIT_NAME', ObjectIdentifier("2.5.4.11")): "OU", 255 | getattr(NameOID, 'SERIAL_NUMBER', ObjectIdentifier("2.5.4.5")): "SN", 256 | getattr(NameOID, 'USER_ID', ObjectIdentifier("0.9.2342.19200300.100.1.1")): "userID", 257 | getattr(NameOID, 'DOMAIN_COMPONENT', ObjectIdentifier("0.9.2342.19200300.100.1.25")): "domainComponent", 258 | getattr(NameOID, 'EMAIL_ADDRESS', ObjectIdentifier("1.2.840.113549.1.9.1")): "emailAddress", 259 | getattr(NameOID, 'POSTAL_CODE', ObjectIdentifier("2.5.4.17")): "ZIP", 260 | } 261 | 262 | ret = [] 263 | try: 264 | for attribute in subject: 265 | oid = attribute.oid 266 | dot = oid.dotted_string 267 | oid_name = oid_names[oid] if shorten and oid in oid_names else oid._name 268 | val = attribute.value 269 | ret.append('%s: %s' % (oid_name, val)) 270 | except: 271 | pass 272 | return ', '.join(ret) 273 | 274 | except Exception as e: 275 | logger.warning('Unexpected error: %s' % e) 276 | return 'N/A' 277 | 278 | 279 | def utf8ize(x): 280 | """ 281 | Converts to utf8 if non-empty 282 | :param x: 283 | :return: 284 | """ 285 | if x is None: 286 | return None 287 | return x.encode('utf-8') 288 | 289 | 290 | def startswith(haystack, prefix): 291 | """ 292 | py3 comp startswith 293 | :param haystack: 294 | :param prefix: 295 | :return: 296 | """ 297 | if haystack is None: 298 | return None 299 | 300 | if sys.version_info[0] < 3: 301 | return haystack.startswith(prefix) 302 | 303 | return to_bytes(haystack).startswith(to_bytes(prefix)) 304 | 305 | 306 | def to_string(x): 307 | """ 308 | Utf8 conversion 309 | :param x: 310 | :return: 311 | """ 312 | if isinstance(x, bytes): 313 | return x.decode('utf-8') 314 | if isinstance(x, basestring): 315 | return x 316 | 317 | 318 | def to_bytes(x): 319 | """ 320 | Byte conv 321 | :param x: 322 | :return: 323 | """ 324 | if isinstance(x, bytes): 325 | return x 326 | if isinstance(x, basestring): 327 | return x.encode('utf-8') 328 | 329 | 330 | def contains(haystack, needle): 331 | """ 332 | py3 contains 333 | :param haystack: 334 | :param needle: 335 | :return: 336 | """ 337 | if sys.version_info[0] < 3: 338 | return needle in haystack 339 | else: 340 | return to_bytes(needle) in to_bytes(haystack) 341 | 342 | 343 | def strip_spaces(x): 344 | """ 345 | Strips spaces 346 | :param x: 347 | :return: 348 | """ 349 | x = x.replace(b' ', b'') 350 | x = x.replace(b'\t', b'') 351 | return x 352 | 353 | 354 | def strip_pem(x): 355 | """ 356 | Strips PEM to bare base64 encoded form 357 | :param x: 358 | :return: 359 | """ 360 | if x is None: 361 | return None 362 | 363 | x = to_string(x) 364 | pem = x.replace('-----BEGIN CERTIFICATE-----', '') 365 | pem = pem.replace('-----END CERTIFICATE-----', '') 366 | pem = re.sub(r'-----BEGIN .+?-----', '', pem) 367 | pem = re.sub(r'-----END .+?-----', '', pem) 368 | pem = pem.replace(' ', '') 369 | pem = pem.replace('\t', '') 370 | pem = pem.replace('\r', '') 371 | pem = pem.replace('\n', '') 372 | return pem.strip() 373 | 374 | 375 | def pem_to_der(x): 376 | """ 377 | Converts PEM to DER 378 | :param x: 379 | :return: 380 | """ 381 | if x is None: 382 | return None 383 | 384 | pem = strip_pem(x) 385 | return base64.b64decode(pem) 386 | 387 | 388 | def unix_time(dt): 389 | if dt is None: 390 | return None 391 | cur = datetime.datetime.utcfromtimestamp(0) 392 | if dt.tzinfo is not None: 393 | cur.replace(tzinfo=dt.tzinfo) 394 | return (dt - cur).total_seconds() 395 | 396 | 397 | class Tracelogger(object): 398 | """ 399 | Prints traceback to the debugging logger if not shown before 400 | """ 401 | 402 | def __init__(self, logger=None): 403 | self.logger = logger 404 | self._db = set() 405 | 406 | def log(self, cause=None, do_message=True, custom_msg=None): 407 | """ 408 | Loads exception data from the current exception frame - should be called inside the except block 409 | :return: 410 | """ 411 | message = error_message(self, cause=cause) 412 | exc_type, exc_value, exc_traceback = sys.exc_info() 413 | traceback_formatted = traceback.format_exc() 414 | traceback_val = traceback.extract_tb(exc_traceback) 415 | 416 | md5 = hashlib.md5(traceback_formatted.encode('utf-8')).hexdigest() 417 | 418 | if md5 in self._db: 419 | # self.logger.debug('Exception trace logged: %s' % md5) 420 | return 421 | 422 | if custom_msg is not None and cause is not None: 423 | self.logger.debug('%s : %s' % (custom_msg, cause)) 424 | elif custom_msg is not None: 425 | self.logger.debug(custom_msg) 426 | elif cause is not None: 427 | self.logger.debug('%s' % cause) 428 | 429 | self.logger.debug(traceback_formatted) 430 | self._db.add(md5) 431 | 432 | 433 | class AutoJSONEncoder(json.JSONEncoder): 434 | """ 435 | JSON encoder trying to_json() first 436 | """ 437 | def default(self, obj): 438 | try: 439 | return obj.to_json() 440 | except AttributeError: 441 | return self.default_classic(obj) 442 | 443 | def default_classic(self, o): 444 | from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers 445 | 446 | if isinstance(o, set): 447 | return list(o) 448 | elif isinstance(o, RSAPublicNumbers): 449 | return {'n': o.n, 'e': o.e} 450 | else: 451 | return super(AutoJSONEncoder, self).default(o) 452 | 453 | 454 | class TestResult(object): 455 | """ 456 | Fingerprint test result holder. 457 | """ 458 | def __init__(self, data=None, **kwargs): 459 | self._data = collections.OrderedDict(data if data is not None else {}) 460 | for key, value in iteritems(kwargs): 461 | self._data[key] = value 462 | 463 | @property 464 | def type(self): 465 | return defvalkey(self._data, 'type') 466 | 467 | @property 468 | def marked(self): 469 | return defvalkey(self._data, 'marked', False) 470 | 471 | @property 472 | def n(self): 473 | return defvalkey(self._data, 'n') 474 | 475 | @property 476 | def time_years(self): 477 | return defvalkey(self._data, 'time_years') 478 | 479 | @property 480 | def price_aws_c4(self): 481 | return defvalkey(self._data, 'price_aws_c4') 482 | 483 | def __getattr__(self, item): 484 | if item in self._data: 485 | return self._data[item] 486 | 487 | return None 488 | 489 | def to_json(self): 490 | self._data['marked'] = self.marked 491 | self._data['type'] = self.type 492 | return self._data 493 | 494 | 495 | class ImportException(Exception): 496 | """Import exception used with optional dependencies""" 497 | 498 | def __init__(self, message=None, cause=None): 499 | super(ImportException, self).__init__(message) 500 | 501 | 502 | class DlogFprint(object): 503 | """ 504 | Discrete logarithm (dlog) fingerprinter for ROCA. 505 | Exploits the mathematical prime structure described in the paper. 506 | 507 | No external python dependencies are needed (for sake of compatibility). 508 | Detection could be optimized using sympy / gmpy but that would add significant dependency overhead. 509 | """ 510 | def __init__(self, max_prime=167, generator=65537): 511 | self.primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 512 | 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167] 513 | 514 | self.max_prime = max_prime 515 | self.generator = generator 516 | self.m, self.phi_m = self.primorial(max_prime) 517 | 518 | self.phi_m_decomposition = DlogFprint.small_factors(self.phi_m, max_prime) 519 | self.generator_order = DlogFprint.element_order(generator, self.m, self.phi_m, self.phi_m_decomposition) 520 | self.generator_order_decomposition = DlogFprint.small_factors(self.generator_order, max_prime) 521 | logger.debug('Dlog fprint data: max prime: %s, generator: %s, m: %s, phi_m: %s, phi_m_dec: %s, ' 522 | 'generator_order: %s, generator_order_decomposition: %s' 523 | % (self.max_prime, self.generator, self.m, self.phi_m, self.phi_m_decomposition, 524 | self.generator_order, self.generator_order_decomposition)) 525 | 526 | def fprint(self, modulus): 527 | """ 528 | Returns True if fingerprint is present / detected. 529 | :param modulus: 530 | :return: 531 | """ 532 | if modulus <= 2: 533 | return False 534 | 535 | d = DlogFprint.discrete_log(modulus, self.generator, 536 | self.generator_order, self.generator_order_decomposition, self.m) 537 | return d is not None 538 | 539 | def primorial(self, max_prime=167): 540 | """ 541 | Returns primorial (and its totient) with max prime inclusive - product of all primes below the value 542 | :param max_prime: 543 | :param dummy: 544 | :return: primorial, phi(primorial) 545 | """ 546 | mprime = max(self.primes) 547 | if max_prime > mprime: 548 | raise ValueError('Current primorial implementation does not support values above %s' % mprime) 549 | 550 | primorial = 1 551 | phi_primorial = 1 552 | for prime in self.primes: 553 | primorial *= prime 554 | phi_primorial *= prime - 1 555 | return primorial, phi_primorial 556 | 557 | @staticmethod 558 | def prime3(a): 559 | """ 560 | Simple trial division prime detection 561 | :param a: 562 | :return: 563 | """ 564 | if a < 2: 565 | return False 566 | if a == 2 or a == 3: 567 | return True # manually test 2 and 3 568 | if a % 2 == 0 or a % 3 == 0: 569 | return False # exclude multiples of 2 and 3 570 | 571 | max_divisor = int(math.ceil(a ** 0.5)) 572 | d, i = 5, 2 573 | while d <= max_divisor: 574 | if a % d == 0: 575 | return False 576 | d += i 577 | i = 6 - i # this modifies 2 into 4 and vice versa 578 | 579 | return True 580 | 581 | @staticmethod 582 | def is_prime(a): 583 | return DlogFprint.prime3(a) 584 | 585 | @staticmethod 586 | def prime_factors(n, limit=None): 587 | """ 588 | Simple trial division factorization 589 | :param n: 590 | :param limit: 591 | :return: 592 | """ 593 | num = [] 594 | 595 | # add 2, 3 to list or prime factors and remove all even numbers(like sieve of ertosthenes) 596 | while n % 2 == 0: 597 | num.append(2) 598 | n = n // 2 599 | 600 | while n % 3 == 0: 601 | num.append(3) 602 | n = n // 3 603 | 604 | max_divisor = int(math.ceil(n ** 0.5)) if limit is None else limit 605 | d, i = 5, 2 606 | while d <= max_divisor: 607 | while n % d == 0: 608 | num.append(d) 609 | n = n // d 610 | 611 | d += i 612 | i = 6 - i # this modifies 2 into 4 and vice versa 613 | 614 | # if no is > 2 i.e no is a prime number that is only divisible by itself add it 615 | if n > 2: 616 | num.append(n) 617 | 618 | return num 619 | 620 | @staticmethod 621 | def factor_list_to_map(factors): 622 | """ 623 | Factor list to map factor -> power 624 | :param factors: 625 | :return: 626 | """ 627 | ret = {} 628 | for k, g in itertools.groupby(factors): 629 | ret[k] = len(list(g)) 630 | return ret 631 | 632 | @staticmethod 633 | def element_order(element, modulus, phi_m, phi_m_decomposition): 634 | """ 635 | Returns order of the element in Zmod(modulus) 636 | :param element: 637 | :param modulus: 638 | :param phi_m: phi(modulus) 639 | :param phi_m_decomposition: factorization of phi(modulus) 640 | :return: 641 | """ 642 | if element == 1: 643 | return 1 # by definition 644 | 645 | if pow(element, phi_m, modulus) != 1: 646 | return None # not an element of the group 647 | 648 | order = phi_m 649 | for factor, power in list(phi_m_decomposition.items()): 650 | for p in range(1, power + 1): 651 | next_order = order // factor 652 | if pow(element, next_order, modulus) == 1: 653 | order = next_order 654 | else: 655 | break 656 | return order 657 | 658 | @staticmethod 659 | def chinese_remainder(n, a): 660 | """ 661 | Solves CRT for moduli and remainders 662 | :param n: 663 | :param a: 664 | :return: 665 | """ 666 | sum = 0 667 | prod = reduce(lambda a, b: a * b, n) 668 | 669 | for n_i, a_i in zip(n, a): 670 | p = prod // n_i 671 | sum += a_i * DlogFprint.mul_inv(p, n_i) * p 672 | return sum % prod 673 | 674 | @staticmethod 675 | def mul_inv(a, b): 676 | """ 677 | Modular inversion a mod b 678 | :param a: 679 | :param b: 680 | :return: 681 | """ 682 | b0 = b 683 | x0, x1 = 0, 1 684 | if b == 1: 685 | return 1 686 | while a > 1: 687 | q = a // b 688 | a, b = b, a % b 689 | x0, x1 = x1 - q * x0, x0 690 | if x1 < 0: 691 | x1 += b0 692 | return x1 693 | 694 | @staticmethod 695 | def small_factors(x, max_prime): 696 | """ 697 | Factorizing x up to max_prime limit. 698 | :param x: 699 | :param max_prime: 700 | :return: 701 | """ 702 | factors = DlogFprint.prime_factors(x, limit=max_prime) 703 | return DlogFprint.factor_list_to_map(factors) 704 | 705 | @staticmethod 706 | def discrete_log(element, generator, generator_order, generator_order_decomposition, modulus): 707 | """ 708 | Simple discrete logarithm 709 | :param element: 710 | :param generator: 711 | :param generator_order: 712 | :param generator_order_decomposition: 713 | :param modulus: 714 | :return: 715 | """ 716 | if pow(element, generator_order, modulus) != 1: 717 | # logger.debug('Powmod not one') 718 | return None 719 | 720 | moduli = [] 721 | remainders = [] 722 | for prime, power in list(generator_order_decomposition.items()): 723 | prime_to_power = prime ** power 724 | order_div_prime_power = generator_order // prime_to_power # g.div(generator_order, prime_to_power) 725 | g_dash = pow(generator, order_div_prime_power, modulus) 726 | h_dash = pow(element, order_div_prime_power, modulus) 727 | found = False 728 | for i in range(0, prime_to_power): 729 | if pow(g_dash, i, modulus) == h_dash: 730 | remainders.append(i) 731 | moduli.append(prime_to_power) 732 | found = True 733 | break 734 | if not found: 735 | # logger.debug('Not found :(') 736 | return None 737 | 738 | ccrt = DlogFprint.chinese_remainder(moduli, remainders) 739 | return ccrt 740 | 741 | 742 | # 743 | # Main fingerprinting tool 744 | # 745 | 746 | class RocaFingerprinter(object): 747 | """ 748 | Key fingerprinter 749 | """ 750 | 751 | def __init__(self, **kwargs): 752 | self.args = None 753 | self.trace_logger = Tracelogger(logger) 754 | self.jks_passwords = ['', 'changeit', 'chageit', 'root', 'server', 'test', 'alias', 'jks', 755 | 'tomcat', 'www', 'web', 'https'] 756 | 757 | kwargs.setdefault('do_print', False) 758 | self.jks_file_passwords = kwargs.get('jks_file_passwords') 759 | self.do_print = kwargs.get('do_print') 760 | 761 | # Minimal modulo size to avoid false positives on the random data and very short moduli 762 | kwargs.setdefault('minimal_modulus_bits', 256) 763 | kwargs.setdefault('minimal_modulus', 2**kwargs.get('minimal_modulus_bits')) 764 | self.minimal_modulus_bits = kwargs.get('minimal_modulus_bits') 765 | self.minimal_modulus = kwargs.get('minimal_modulus') 766 | 767 | self.tested = 0 768 | self.num_rsa = 0 769 | self.num_pem_certs = 0 770 | self.num_pem_csr = 0 771 | self.num_der_certs = 0 772 | self.num_rsa_keys = 0 773 | self.num_pgp_masters = 0 774 | self.num_pgp_total = 0 775 | self.num_ssh = 0 776 | self.num_json = 0 777 | self.num_apk = 0 778 | self.num_ldiff_cert = 0 779 | self.num_jks_cert = 0 780 | self.num_pkcs7_cert = 0 781 | self.found = 0 782 | 783 | self.dlog_fprinter = DlogFprint() 784 | 785 | self.primes = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 786 | 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167] 787 | 788 | self.prints = [6, 30, 126, 1026, 5658, 107286, 199410, 8388606, 536870910, 2147483646, 67109890, 2199023255550, 789 | 8796093022206, 140737488355326, 5310023542746834, 576460752303423486, 1455791217086302986, 790 | 147573952589676412926, 20052041432995567486, 6041388139249378920330, 207530445072488465666, 791 | 9671406556917033397649406, 792 | 618970019642690137449562110, 793 | 79228162521181866724264247298, 794 | 2535301200456458802993406410750, 795 | 1760368345969468176824550810518, 796 | 50079290986288516948354744811034, 797 | 473022961816146413042658758988474, 798 | 10384593717069655257060992658440190, 799 | 144390480366845522447407333004847678774, 800 | 2722258935367507707706996859454145691646, 801 | 174224571863520493293247799005065324265470, 802 | 696898287454081973172991196020261297061886, 803 | 713623846352979940529142984724747568191373310, 804 | 1800793591454480341970779146165214289059119882, 805 | 126304807362733370595828809000324029340048915994, 806 | 11692013098647223345629478661730264157247460343806, 807 | 187072209578355573530071658587684226515959365500926] 808 | 809 | self.length_to_time_years = { 810 | 512: 0.000220562038803, 811 | 544: 0.00147111662211, 812 | 576: 0.00673857391044, 813 | 608: 0.0618100348655, 814 | 640: 0.281991193891, 815 | 672: 4.17998973277, 816 | 704: 39.5102151646, 817 | 736: 3473.56982013, 818 | 768: 342674.912512, 819 | 800: 89394704.8817, 820 | 832: 8359663659.84, 821 | 864: 44184838761000.0, 822 | 896: -1, 823 | 928: -1, 824 | 960: -1, 825 | 992: 0.0658249816453, 826 | 1024: 0.266074841608, 827 | 1056: 1.28258930217, 828 | 1088: 7.38296771318, 829 | 1120: 20.2173702373, 830 | 1152: 58.9125352286, 831 | 1184: 415.827799825, 832 | 1216: 1536.17130832, 833 | 1248: 5415.49876704, 834 | 1280: 46281.7555548, 835 | 1312: 208675.856834, 836 | 1344: 1586124.1447, 837 | 1376: 13481048.41, 838 | 1408: 102251985.84, 839 | 1440: 1520923586.93, 840 | 1472: 30924687905.9, 841 | 1504: 1933367534430.0, 842 | 1536: 135663316837000.0, 843 | 1568: 7582543380680000.0, 844 | 1600: 5.1035570593e+17, 845 | 1632: 3.8899705405e+19, 846 | 1664: 3.66527648803e+21, 847 | 1696: 3.77984169396e+23, 848 | 1728: 5.14819714267e+25, 849 | 1760: 6.24593092623e+27, 850 | 1792: 8.73499845222e+29, 851 | 1824: 1.87621309001e+32, 852 | 1856: 2.9671795418e+34, 853 | 1888: -1, 854 | 1920: -1, 855 | 1952: -1, 856 | 1984: 28.6856385392, 857 | 2016: 60.644701708, 858 | 2048: 140.849490658, 859 | 2080: 269.272545592, 860 | 2112: 724.550220558, 861 | 2144: 1262.66048991, 862 | 2176: 3833.6903835, 863 | 2208: 7049.61288162, 864 | 2240: 14511.7355032, 865 | 2272: 41968.716653, 866 | 2304: 105863.580849, 867 | 2336: 509819.310451, 868 | 2368: 863135.14224, 869 | 2400: 3730089.12073, 870 | 2432: 14337269.1935, 871 | 2464: 55914941.3902, 872 | 2496: 144036102.003, 873 | 2528: 972239354.935, 874 | 2560: 1732510677.27, 875 | 2592: 10345329708.8, 876 | 2624: 72172778459.7, 877 | 2656: 386464106155.0, 878 | 2688: 1706310772440.0, 879 | 2720: 14920435519400.0, 880 | 2752: 77755063482200.0, 881 | 2784: 1237655413740000.0, 882 | 2816: 7524587305980000.0, 883 | 2848: 4.66421299974e+16, 884 | 2880: 5.41036780376e+17, 885 | 2912: 6.07066413463e+18, 886 | 2944: 6.17088797501e+19, 887 | 2976: 4.35440413514e+20, 888 | 3008: 1.04496910207e+22, 889 | 3040: 2.91790333753e+23, 890 | 3072: 2.84373206239e+25, 891 | 3104: 1.21552661668e+27, 892 | 3136: 1.14739892383e+29, 893 | 3168: 7.03739127786e+30, 894 | 3200: 5.5123347741e+32, 895 | 3232: 5.46349546772e+34, 896 | 3264: 3.07923071536e+36, 897 | 3296: 4.88872482194e+37, 898 | 3328: 4.74614877952e+39, 899 | 3360: 5.94743522012e+41, 900 | 3392: 3.63042884553e+43, 901 | 3424: 3.15382165869e+45, 902 | 3456: 4.22631927496e+47, 903 | 3488: 4.57325850696e+50, 904 | 3520: 7.58105156459e+52, 905 | 3552: 8.44988925164e+54, 906 | 3584: 2.1141023018e+57, 907 | 3616: 2.95898599696e+59, 908 | 3648: 7.23723533e+61, 909 | 3680: 6.0951282339e+62, 910 | 3712: 1.06824345519e+65, 911 | 3744: 1.85662696289e+67, 912 | 3776: 5.64628786015e+69, 913 | 3808: 1.38273039654e+72, 914 | 3840: -1, 915 | 3872: -1, 916 | 3904: -1, 917 | 3936: -1, 918 | 3968: 47950588.0004, 919 | 4000: 134211454.052, 920 | 4032: 201770331.337, 921 | 4064: 613149724.539, 922 | 4096: 1283252196.93, 923 | } 924 | 925 | # args init 926 | parser = self.init_parser() 927 | self.args = parser.parse_args(args=[]) 928 | 929 | def is_acceptable_modulus(self, modulus): 930 | """ 931 | Tests if modulus isn't too small 932 | :param modulus: 933 | :return: 934 | """ 935 | return not self.minimal_modulus or modulus >= self.minimal_modulus 936 | 937 | def has_fingerprint_moduli(self, modulus): 938 | """ 939 | Returns true if the fingerprint was detected in the key 940 | :param modulus: 941 | :return: 942 | """ 943 | if not self.is_acceptable_modulus(modulus): 944 | return False 945 | 946 | self.tested += 1 947 | for i in range(0, len(self.primes)): 948 | if (1 << (modulus % self.primes[i])) & self.prints[i] == 0: 949 | return False 950 | 951 | self.found += 1 952 | return True 953 | 954 | def has_fingerprint_dlog(self, modulus): 955 | """ 956 | Exact fingerprint using mathematical structure of the primes 957 | :param modulus: 958 | :return: 959 | """ 960 | if not self.is_acceptable_modulus(modulus): 961 | return False 962 | 963 | self.tested += 1 964 | positive = self.dlog_fprinter.fprint(modulus) 965 | 966 | if positive: 967 | self.found += 1 968 | 969 | return positive 970 | 971 | def switch_fingerprint_method(self, old=False): 972 | """ 973 | Switches main fingerprinting method. 974 | 975 | :param old: if True old fingerprinting method will be used. 976 | :return: 977 | """ 978 | if old: 979 | self.has_fingerprint = self.has_fingerprint_moduli 980 | else: 981 | self.has_fingerprint = self.has_fingerprint_dlog 982 | 983 | has_fingerprint_real = has_fingerprint_moduli 984 | has_fingerprint = has_fingerprint_dlog 985 | 986 | def mark_and_add_effort(self, modulus, json_info): 987 | """ 988 | Inserts factorization effort for vulnerable modulus into json_info 989 | :param modulus: 990 | :param json_info: 991 | :return: 992 | """ 993 | META_AMZ_FACT = 92. / 152. # conversion from university cluster to AWS 994 | AMZ_C4_PRICE = 0.1 # price of 2 AWS CPUs per hour 995 | 996 | length = int(ceil(log(modulus, 2))) 997 | length_ceiling = int(ceil(length / 32)) * 32 998 | 999 | if length_ceiling in self.length_to_time_years: 1000 | effort_time = self.length_to_time_years[length_ceiling] 1001 | else: 1002 | effort_time = -1 1003 | if effort_time > 0: 1004 | effort_time *= META_AMZ_FACT # scaling to more powerful AWS CPU 1005 | effort_price = effort_time * 365.25 * 24 * 0.5 * AMZ_C4_PRICE 1006 | else: 1007 | effort_price = -1 1008 | json_info['marked'] = True 1009 | json_info['time_years'] = effort_time 1010 | json_info['price_aws_c4'] = effort_price 1011 | return json_info 1012 | 1013 | def file_matches_extensions(self, fname, extensions): 1014 | """ 1015 | True if file matches one of extensions 1016 | :param fname: 1017 | :param extensions: 1018 | :return: 1019 | """ 1020 | if not isinstance(extensions, list): 1021 | extensions = [extensions] 1022 | for ext in extensions: 1023 | if fname.endswith('.%s' % ext): 1024 | return True 1025 | return False 1026 | 1027 | def process_inputs(self): 1028 | """ 1029 | Processes input data 1030 | :return: 1031 | """ 1032 | ret = [] 1033 | files = self.args.files 1034 | if files is None: 1035 | return ret 1036 | 1037 | for fname in files: 1038 | if fname == '-': 1039 | if self.args.base64stdin: 1040 | for line in sys.stdin: 1041 | data = base64.b64decode(line) 1042 | ret.append(self.process_file(data, fname)) 1043 | 1044 | continue 1045 | else: 1046 | fh = sys.stdin 1047 | 1048 | elif fname.endswith('.tar') or fname.endswith('.tar.gz'): 1049 | sub = self.process_tar(fname) 1050 | ret.append(sub) 1051 | continue 1052 | 1053 | elif not os.path.isfile(fname): 1054 | sub = self.process_dir(fname) 1055 | ret.append(sub) 1056 | continue 1057 | 1058 | else: 1059 | fh = open(fname, 'rb') 1060 | 1061 | with fh: 1062 | data = fh.read() 1063 | sub = self.process_file(data, fname) 1064 | ret.append(sub) 1065 | 1066 | return ret 1067 | 1068 | def process_tar(self, fname): 1069 | """ 1070 | Tar(gz) archive processing 1071 | :param fname: 1072 | :return: 1073 | """ 1074 | import tarfile # lazy import, only when needed 1075 | ret = [] 1076 | with tarfile.open(fname) as tr: 1077 | members = tr.getmembers() 1078 | for member in members: 1079 | if not member.isfile(): 1080 | continue 1081 | fh = tr.extractfile(member) 1082 | sub = self.process_file(fh.read(), member.name) 1083 | ret.append(sub) 1084 | return ret 1085 | 1086 | def process_dir(self, dirname): 1087 | """ 1088 | Directory processing 1089 | :param dirname: 1090 | :return: 1091 | """ 1092 | ret = [] 1093 | sub_rec = [f for f in os.listdir(dirname)] 1094 | for fname in sub_rec: 1095 | full_path = os.path.join(dirname, fname) 1096 | 1097 | if os.path.isfile(full_path): 1098 | with open(full_path, 'rb') as fh: 1099 | sub = self.process_file(fh.read(), fname) 1100 | ret.append(sub) 1101 | 1102 | elif os.path.isdir(full_path): 1103 | sub = self.process_dir(full_path) 1104 | ret.append(sub) 1105 | return ret 1106 | 1107 | def process_file(self, data, name): 1108 | """ 1109 | Processes a single file 1110 | :param data: 1111 | :param name: 1112 | :return: 1113 | """ 1114 | try: 1115 | return self.process_file_autodetect(data, name) 1116 | 1117 | except Exception as e: 1118 | logger.debug('Exception processing file %s : %s' % (name, e)) 1119 | self.trace_logger.log(e) 1120 | 1121 | # autodetection fallback - all formats 1122 | ret = [] 1123 | logger.debug('processing %s as PEM' % name) 1124 | ret.append(self.process_pem(data, name)) 1125 | 1126 | logger.debug('processing %s as DER' % name) 1127 | ret.append(self.process_der(data, name)) 1128 | 1129 | logger.debug('processing %s as PGP' % name) 1130 | ret.append(self.process_pgp(data, name)) 1131 | 1132 | logger.debug('processing %s as SSH' % name) 1133 | ret.append(self.process_ssh(data, name)) 1134 | 1135 | logger.debug('processing %s as JSON' % name) 1136 | ret.append(self.process_json(data, name)) 1137 | 1138 | logger.debug('processing %s as APK' % name) 1139 | ret.append(self.process_apk(data, name)) 1140 | 1141 | logger.debug('processing %s as MOD' % name) 1142 | ret.append(self.process_mod(data, name)) 1143 | 1144 | logger.debug('processing %s as LDIFF' % name) 1145 | ret.append(self.process_ldiff(data, name)) 1146 | 1147 | logger.debug('processing %s as JKS' % name) 1148 | ret.append(self.process_jks(data, name)) 1149 | 1150 | logger.debug('processing %s as PKCS7' % name) 1151 | ret.append(self.process_pkcs7(data, name)) 1152 | return ret 1153 | 1154 | def process_file_autodetect(self, data, name): 1155 | """ 1156 | Processes a single file - format autodetection 1157 | :param data: 1158 | :param name: 1159 | :return: 1160 | """ 1161 | is_ssh_file = startswith(data, 'ssh-rsa') or contains(data, 'ssh-rsa ') 1162 | is_pgp_file = startswith(data, '-----BEGIN PGP') 1163 | is_pkcs7_file = startswith(data, '-----BEGIN PKCS7') 1164 | is_pem_file = startswith(data, '-----BEGIN') and not is_pgp_file 1165 | is_ldiff_file = contains(data, 'binary::') 1166 | 1167 | is_pgp = is_pgp_file or (self.file_matches_extensions(name, ['pgp', 'gpg', 'key', 'pub', 'asc']) 1168 | and not is_ssh_file 1169 | and not is_pem_file) 1170 | is_pgp |= self.args.file_pgp 1171 | 1172 | is_crt_ext = self.file_matches_extensions(name, ['der', 'crt', 'cer', 'cert', 'x509', 'key', 'pub', 'ca']) 1173 | 1174 | is_pem = self.file_matches_extensions(name, 'pem') or is_pem_file 1175 | is_pem |= self.args.file_pem 1176 | 1177 | is_der = not is_pem and not is_ssh_file and not is_pgp_file and is_crt_ext 1178 | is_der |= self.args.file_der 1179 | 1180 | is_ssh = self.file_matches_extensions(name, ['ssh', 'pub']) or is_ssh_file 1181 | is_ssh |= self.args.file_ssh 1182 | 1183 | is_apk = self.file_matches_extensions(name, 'apk') 1184 | 1185 | is_mod = self.file_matches_extensions(name, ['txt', 'mod', 'mods', 'moduli']) 1186 | is_mod |= not is_pem and not is_der and not is_pgp and not is_ssh_file and not is_apk 1187 | is_mod |= self.args.file_mod 1188 | 1189 | is_json = self.file_matches_extensions(name, ['json', 'js']) or startswith(data, '{') or startswith(data, '[') 1190 | is_json |= self.args.file_json 1191 | 1192 | is_ldiff = self.file_matches_extensions(name, ['ldiff', 'ldap']) or is_ldiff_file 1193 | is_ldiff |= self.args.file_ldiff 1194 | 1195 | is_jks = self.file_matches_extensions(name, ['jks', 'bks']) 1196 | is_pkcs7 = self.file_matches_extensions(name, ['pkcs7', 'p7s', 'p7']) 1197 | is_pkcs7 |= is_pkcs7_file 1198 | is_pkcs7 |= self.args.file_pkcs7 1199 | 1200 | det = is_pem or is_der or is_pgp or is_ssh or is_mod or is_json or is_apk or is_ldiff or is_jks 1201 | ret = [] 1202 | if is_pem: 1203 | logger.debug('processing %s as PEM' % name) 1204 | ret.append(self.process_pem(data, name)) 1205 | 1206 | if is_der: 1207 | logger.debug('processing %s as DER' % name) 1208 | ret.append(self.process_der(data, name)) 1209 | 1210 | if is_pgp: 1211 | logger.debug('processing %s as PGP' % name) 1212 | ret.append(self.process_pgp(data, name)) 1213 | 1214 | if is_ssh: 1215 | logger.debug('processing %s as SSH' % name) 1216 | ret.append(self.process_ssh(data, name)) 1217 | 1218 | if is_json: 1219 | logger.debug('processing %s as JSON' % name) 1220 | ret.append(self.process_json(data, name)) 1221 | 1222 | if is_apk: 1223 | logger.debug('processing %s as APK' % name) 1224 | ret.append(self.process_apk(data, name)) 1225 | 1226 | if is_mod: 1227 | logger.debug('processing %s as MOD' % name) 1228 | ret.append(self.process_mod(data, name)) 1229 | 1230 | if is_ldiff: 1231 | logger.debug('processing %s as LDIFF' % name) 1232 | ret.append(self.process_ldiff(data, name)) 1233 | 1234 | if is_jks: 1235 | logger.debug('processing %s as JKS' % name) 1236 | ret.append(self.process_jks(data, name)) 1237 | 1238 | if is_pkcs7: 1239 | logger.debug('processing %s as PKCS7' % name) 1240 | ret.append(self.process_pkcs7(data, name)) 1241 | 1242 | if not det: 1243 | logger.debug('Undetected (skipped) file: %s' % name) 1244 | return ret 1245 | 1246 | def process_pem(self, data, name): 1247 | """ 1248 | PEM processing - splitting further by the type of the records 1249 | :param data: 1250 | :param name: 1251 | :return: 1252 | """ 1253 | try: 1254 | ret = [] 1255 | data = to_string(data) 1256 | parts = re.split(r'-----BEGIN', data) 1257 | if len(parts) == 0: 1258 | return None 1259 | 1260 | if len(parts[0]) == 0: 1261 | parts.pop(0) 1262 | 1263 | crt_arr = ['-----BEGIN' + x for x in parts] 1264 | for idx, pem_rec in enumerate(crt_arr): 1265 | pem_rec = pem_rec.strip() 1266 | if len(pem_rec) == 0: 1267 | continue 1268 | 1269 | if startswith(pem_rec, '-----BEGIN CERTIFICATE REQUEST'): 1270 | return self.process_pem_csr(pem_rec, name, idx) 1271 | elif startswith(pem_rec, '-----BEGIN CERTIF'): 1272 | return self.process_pem_cert(pem_rec, name, idx) 1273 | elif startswith(pem_rec, '-----BEGIN '): # fallback 1274 | return self.process_pem_rsakey(pem_rec, name, idx) 1275 | return ret 1276 | 1277 | except Exception as e: 1278 | logger.debug('Exception processing PEM file %s : %s' % (name, e)) 1279 | self.trace_logger.log(e) 1280 | return None 1281 | 1282 | def process_pem_cert(self, data, name, idx): 1283 | """ 1284 | Processes PEM encoded certificate 1285 | :param data: 1286 | :param name: 1287 | :param idx: 1288 | :return: 1289 | """ 1290 | from cryptography.x509.base import load_der_x509_certificate 1291 | try: 1292 | x509 = load_der_x509_certificate(pem_to_der(data), self.get_backend()) 1293 | self.num_pem_certs += 1 1294 | return self.process_x509(x509, name=name, idx=idx, data=data, pem=True, source='pem-cert') 1295 | 1296 | except Exception as e: 1297 | logger.debug('PEM processing failed: %s' % e) 1298 | self.trace_logger.log(e) 1299 | 1300 | def process_pem_csr(self, data, name, idx): 1301 | """ 1302 | Processes PEM encoded certificate request PKCS#10 1303 | :param data: 1304 | :param name: 1305 | :param idx: 1306 | :return: 1307 | """ 1308 | from cryptography.x509.base import load_der_x509_csr 1309 | try: 1310 | csr = load_der_x509_csr(pem_to_der(data), self.get_backend()) 1311 | self.num_pem_csr += 1 1312 | return self.process_csr(csr, name=name, idx=idx, data=data, pem=True, source='pem-csr') 1313 | 1314 | except Exception as e: 1315 | logger.debug('PEM processing failed: %s' % e) 1316 | self.trace_logger.log(e) 1317 | 1318 | def process_pem_rsakey(self, data, name, idx): 1319 | """ 1320 | Processes PEM encoded RSA key 1321 | :param data: 1322 | :param name: 1323 | :param idx: 1324 | :return: 1325 | """ 1326 | from cryptography.hazmat.primitives.serialization import load_der_public_key 1327 | from cryptography.hazmat.primitives.serialization import load_der_private_key 1328 | try: 1329 | if startswith(data, '-----BEGIN RSA PUBLIC KEY') or startswith(data, '-----BEGIN PUBLIC KEY'): 1330 | rsa = load_der_public_key(pem_to_der(data), self.get_backend()) 1331 | public_numbers = rsa.public_numbers() 1332 | elif startswith(data, '-----BEGIN RSA PRIVATE KEY') or startswith(data, '-----BEGIN PRIVATE KEY'): 1333 | rsa = load_der_private_key(pem_to_der(data), None, self.get_backend()) 1334 | public_numbers = rsa.private_numbers().public_numbers 1335 | else: 1336 | return None 1337 | self.num_rsa_keys += 1 1338 | self.num_rsa += 1 1339 | 1340 | js = collections.OrderedDict() 1341 | js['type'] = 'pem-rsa-key' 1342 | js['fname'] = name 1343 | js['idx'] = idx 1344 | js['pem'] = data 1345 | js['e'] = '0x%x' % public_numbers.e 1346 | js['n'] = '0x%x' % public_numbers.n 1347 | 1348 | if self.has_fingerprint(public_numbers.n): 1349 | logger.warning('Fingerprint found in PEM RSA key %s ' % name) 1350 | self.mark_and_add_effort(public_numbers.n, js) 1351 | 1352 | if self.do_print: 1353 | print(json.dumps(js)) 1354 | 1355 | return TestResult(js) 1356 | 1357 | except Exception as e: 1358 | logger.debug('Pubkey loading error: %s : %s [%s] : %s' % (name, idx, data[:20], e)) 1359 | self.trace_logger.log(e) 1360 | 1361 | def process_der(self, data, name): 1362 | """ 1363 | DER processing 1364 | :param data: 1365 | :param name: 1366 | :return: 1367 | """ 1368 | from cryptography.x509.base import load_der_x509_certificate 1369 | try: 1370 | x509 = load_der_x509_certificate(data, self.get_backend()) 1371 | self.num_der_certs += 1 1372 | return self.process_x509(x509, name=name, pem=False, source='der-cert') 1373 | 1374 | except Exception as e: 1375 | logger.debug('DER processing failed: %s : %s' % (name, e)) 1376 | self.trace_logger.log(e) 1377 | 1378 | def process_x509(self, x509, name, idx=None, data=None, pem=True, source='', aux=None): 1379 | """ 1380 | Processing parsed X509 certificate 1381 | :param x509: 1382 | :param name: 1383 | :param idx: 1384 | :param data: 1385 | :param pem: 1386 | :param source: 1387 | :param aux: 1388 | :return: 1389 | """ 1390 | if x509 is None: 1391 | return 1392 | 1393 | from cryptography.hazmat.primitives import hashes 1394 | from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey 1395 | from cryptography.x509.oid import NameOID 1396 | 1397 | pub = x509.public_key() 1398 | if not isinstance(pub, RSAPublicKey): 1399 | return 1400 | 1401 | self.num_rsa += 1 1402 | pubnum = x509.public_key().public_numbers() 1403 | 1404 | js = collections.OrderedDict() 1405 | js['type'] = source 1406 | js['fname'] = name 1407 | js['idx'] = idx 1408 | js['fprint'] = binascii.hexlify(x509.fingerprint(hashes.SHA256())) 1409 | js['subject'] = utf8ize(try_get_dn_string(x509.subject, shorten=True)) 1410 | js['issuer'] = utf8ize(try_get_dn_string(x509.issuer, shorten=True)) 1411 | js['issuer_org'] = utf8ize(try_get_dn_part(x509.issuer, NameOID.ORGANIZATION_NAME)) 1412 | js['created_at'] = self.strtime(x509.not_valid_before) 1413 | js['created_at_utc'] = unix_time(x509.not_valid_before) 1414 | js['not_valid_after_utc'] = unix_time(x509.not_valid_after) 1415 | js['pem'] = data if pem else None 1416 | js['aux'] = aux 1417 | js['e'] = '0x%x' % pubnum.e 1418 | js['n'] = '0x%x' % pubnum.n 1419 | 1420 | if self.has_fingerprint(pubnum.n): 1421 | logger.warning('Fingerprint found in the Certificate %s idx %s ' % (name, idx)) 1422 | self.mark_and_add_effort(pubnum.n, js) 1423 | 1424 | if self.do_print: 1425 | print(json.dumps(js)) 1426 | 1427 | return TestResult(js) 1428 | 1429 | def process_csr(self, csr, name, idx=None, data=None, pem=True, source='', aux=None): 1430 | """ 1431 | Processing parsed X509 csr 1432 | :param csr: 1433 | :type csr: cryptography.x509.CertificateSigningRequest 1434 | :param name: 1435 | :param idx: 1436 | :param data: 1437 | :param pem: 1438 | :param source: 1439 | :param aux: 1440 | :return: 1441 | """ 1442 | if csr is None: 1443 | return 1444 | 1445 | from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey 1446 | 1447 | pub = csr.public_key() 1448 | if not isinstance(pub, RSAPublicKey): 1449 | return 1450 | 1451 | self.num_rsa += 1 1452 | pubnum = csr.public_key().public_numbers() 1453 | 1454 | js = collections.OrderedDict() 1455 | js['type'] = source 1456 | js['fname'] = name 1457 | js['idx'] = idx 1458 | js['subject'] = utf8ize(try_get_dn_string(csr.subject, shorten=True)) 1459 | js['pem'] = data if pem else None 1460 | js['aux'] = aux 1461 | js['e'] = '0x%x' % pubnum.e 1462 | js['n'] = '0x%x' % pubnum.n 1463 | 1464 | if self.has_fingerprint(pubnum.n): 1465 | logger.warning('Fingerprint found in the CSR %s idx %s ' % (name, idx)) 1466 | self.mark_and_add_effort(pubnum.n, js) 1467 | 1468 | if self.do_print: 1469 | print(json.dumps(js)) 1470 | 1471 | return TestResult(js) 1472 | 1473 | def process_pgp(self, data, name): 1474 | """ 1475 | PGP key processing 1476 | :param data: 1477 | :param name: 1478 | :return: 1479 | """ 1480 | ret = [] 1481 | try: 1482 | data = to_string(data) 1483 | parts = re.split(r'-{5,}BEGIN', data) 1484 | if len(parts) == 0: 1485 | return 1486 | 1487 | if len(parts[0]) == 0: 1488 | parts.pop(0) 1489 | 1490 | crt_arr = ['-----BEGIN' + x for x in parts] 1491 | for idx, pem_rec in enumerate(crt_arr): 1492 | try: 1493 | pem_rec = pem_rec.strip() 1494 | if len(pem_rec) == 0: 1495 | continue 1496 | 1497 | ret.append(self.process_pgp_raw(pem_rec.encode(), name, idx)) 1498 | 1499 | except Exception as e: 1500 | logger.error('Exception in processing PGP rec file %s: %s' % (name, e)) 1501 | self.trace_logger.log(e) 1502 | 1503 | except Exception as e: 1504 | logger.error('Exception in processing PGP file %s: %s' % (name, e)) 1505 | self.trace_logger.log(e) 1506 | return ret 1507 | 1508 | def process_pgp_raw(self, data, name, file_idx=None): 1509 | """ 1510 | Processes single PGP key 1511 | :param data: file data 1512 | :param name: file name 1513 | :param file_idx: index in the file 1514 | :return: 1515 | """ 1516 | try: 1517 | from pgpdump.data import AsciiData 1518 | from pgpdump.packet import SignaturePacket, PublicKeyPacket, PublicSubkeyPacket, UserIDPacket 1519 | 1520 | except Exception as e: 1521 | logger.warning('Could not import pgpdump, try running: pip install pgpdump') 1522 | return [TestResult(fname=name, type='pgp', error='cannot-import')] 1523 | 1524 | ret = [] 1525 | js_base = collections.OrderedDict() 1526 | 1527 | pgp_key_data = AsciiData(data) 1528 | packets = list(pgp_key_data.packets()) 1529 | self.num_pgp_masters += 1 1530 | 1531 | master_fprint = None 1532 | master_key_id = None 1533 | identities = [] 1534 | pubkeys = [] 1535 | sign_key_ids = [] 1536 | sig_cnt = 0 1537 | for idx, packet in enumerate(packets): 1538 | if isinstance(packet, PublicKeyPacket): 1539 | master_fprint = packet.fingerprint 1540 | master_key_id = format_pgp_key(packet.key_id) 1541 | pubkeys.append(packet) 1542 | elif isinstance(packet, PublicSubkeyPacket): 1543 | pubkeys.append(packet) 1544 | elif isinstance(packet, UserIDPacket): 1545 | identities.append(packet) 1546 | elif isinstance(packet, SignaturePacket): 1547 | sign_key_ids.append(packet.key_id) 1548 | sig_cnt += 1 1549 | 1550 | # Names / identities 1551 | ids_arr = [] 1552 | identity = None 1553 | for packet in identities: 1554 | idjs = collections.OrderedDict() 1555 | idjs['name'] = packet.user_name 1556 | idjs['email'] = packet.user_email 1557 | ids_arr.append(idjs) 1558 | 1559 | if identity is None: 1560 | identity = '%s <%s>' % (packet.user_name, packet.user_email) 1561 | 1562 | js_base['type'] = 'pgp' 1563 | js_base['fname'] = name 1564 | js_base['fname_idx'] = file_idx 1565 | js_base['master_key_id'] = master_key_id 1566 | js_base['master_fprint'] = master_fprint 1567 | js_base['identities'] = ids_arr 1568 | js_base['signatures_count'] = sig_cnt 1569 | js_base['packets_count'] = len(packets) 1570 | js_base['keys_count'] = len(pubkeys) 1571 | js_base['signature_keys'] = list(set(sign_key_ids)) 1572 | 1573 | # Public keys processing 1574 | for packet in pubkeys: 1575 | try: 1576 | self.num_pgp_total += 1 1577 | if packet.modulus is None: 1578 | continue 1579 | 1580 | self.num_rsa += 1 1581 | js = collections.OrderedDict(js_base) 1582 | js['created_at'] = self.strtime(packet.creation_time) 1583 | js['created_at_utc'] = unix_time(packet.creation_time) 1584 | js['is_master'] = master_fprint == packet.fingerprint 1585 | js['kid'] = format_pgp_key(packet.key_id) 1586 | js['bitsize'] = packet.modulus_bitlen 1587 | js['master_kid'] = master_key_id 1588 | js['e'] = '0x%x' % packet.exponent 1589 | js['n'] = '0x%x' % packet.modulus 1590 | 1591 | if self.has_fingerprint(packet.modulus): 1592 | self.mark_and_add_effort(packet.modulus, js) 1593 | logger.warning('Fingerprint found in PGP key %s key ID 0x%s' % (name, js['kid'])) 1594 | 1595 | if self.do_print: 1596 | print(json.dumps(js)) 1597 | 1598 | ret.append(TestResult(js)) 1599 | 1600 | except Exception as e: 1601 | logger.error('Excetion in processing the key: %s' % e) 1602 | self.trace_logger.log(e) 1603 | return ret 1604 | 1605 | def process_ssh(self, data, name): 1606 | """ 1607 | Processes SSH keys 1608 | :param data: 1609 | :param name: 1610 | :return: 1611 | """ 1612 | if data is None or len(data) == 0: 1613 | return 1614 | 1615 | ret = [] 1616 | try: 1617 | lines = [x.strip() for x in data.split(b'\n')] 1618 | for idx, line in enumerate(lines): 1619 | ret.append(self.process_ssh_line(line, name, idx)) 1620 | 1621 | except Exception as e: 1622 | logger.debug('Exception in processing SSH public key %s : %s' % (name, e)) 1623 | self.trace_logger.log(e) 1624 | return ret 1625 | 1626 | def process_ssh_line(self, data, name, idx): 1627 | """ 1628 | Processes single SSH key 1629 | :param data: 1630 | :param name: 1631 | :param idx: 1632 | :return: 1633 | """ 1634 | data = data.strip() 1635 | if not contains(data, 'ssh-rsa'): 1636 | return 1637 | 1638 | # strip ssh params / adjustments 1639 | try: 1640 | data = data[to_bytes(data).find(b'ssh-rsa'):] 1641 | except Exception as e: 1642 | pass 1643 | 1644 | from cryptography.hazmat.primitives.serialization import load_ssh_public_key 1645 | from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey 1646 | try: 1647 | key_obj = load_ssh_public_key(data, self.get_backend()) 1648 | self.num_ssh += 1 1649 | 1650 | if not isinstance(key_obj, RSAPublicKey): 1651 | return 1652 | 1653 | self.num_rsa += 1 1654 | numbers = key_obj.public_numbers() 1655 | 1656 | js = collections.OrderedDict() 1657 | js['type'] = 'ssh-rsa' 1658 | js['fname'] = name 1659 | js['idx'] = idx 1660 | js['e'] = '0x%x' % numbers.e 1661 | js['n'] = '0x%x' % numbers.n 1662 | js['ssh'] = data 1663 | 1664 | if self.has_fingerprint(numbers.n): 1665 | logger.warning('Fingerprint found in the SSH key %s idx %s ' % (name, idx)) 1666 | self.mark_and_add_effort(numbers.n, js) 1667 | 1668 | if self.do_print: 1669 | print(json.dumps(js)) 1670 | 1671 | return TestResult(js) 1672 | 1673 | except Exception as e: 1674 | logger.debug('Exception in processing SSH public key %s idx %s : %s' % (name, idx, e)) 1675 | self.trace_logger.log(e) 1676 | 1677 | def process_json(self, data, name): 1678 | """ 1679 | Processes as a JSON 1680 | :param data: 1681 | :param name: 1682 | :return: 1683 | """ 1684 | if data is None or len(data) == 0: 1685 | return 1686 | 1687 | ret = [] 1688 | try: 1689 | lines = [x.strip() for x in data.split(bytes(b'\n'))] 1690 | for idx, line in enumerate(lines): 1691 | ret.append(self.process_json_line(line, name, idx)) 1692 | 1693 | except Exception as e: 1694 | logger.debug('Exception in processing JSON %s : %s' % (name, e)) 1695 | self.trace_logger.log(e) 1696 | return ret 1697 | 1698 | def process_json_line(self, data, name, idx): 1699 | """ 1700 | Processes single json line 1701 | :param data: 1702 | :param name: 1703 | :param idx: 1704 | :return: 1705 | """ 1706 | data = data.strip() 1707 | if len(data) == 0: 1708 | return 1709 | 1710 | ret = [] 1711 | try: 1712 | js = json.loads(data) 1713 | self.num_json += 1 1714 | ret.append(self.process_json_rec(js, name, idx, [])) 1715 | 1716 | except Exception as e: 1717 | logger.debug('Exception in processing JSON %s idx %s : %s' % (name, idx, e)) 1718 | self.trace_logger.log(e) 1719 | return ret 1720 | 1721 | def process_json_rec(self, data, name, idx, sub_idx): 1722 | """ 1723 | Processes json rec - json object 1724 | :param data: 1725 | :param name: 1726 | :param idx: 1727 | :param sub_idx: 1728 | :return: 1729 | """ 1730 | ret = [] 1731 | if isinstance(data, list): 1732 | for kidx, rec in enumerate(data): 1733 | sub = self.process_json_rec(rec, name, idx, list(sub_idx + [kidx])) 1734 | ret.append(sub) 1735 | return ret 1736 | 1737 | if isinstance(data, dict): 1738 | for key in data: 1739 | rec = data[key] 1740 | sub = self.process_json_rec(rec, name, idx, list(sub_idx + [rec])) 1741 | ret.append(sub) 1742 | 1743 | if 'n' in data: 1744 | ret.append(self.process_js_mod(data['n'], name, idx, sub_idx)) 1745 | if 'mod' in data: 1746 | ret.append(self.process_js_mod(data['mod'], name, idx, sub_idx)) 1747 | if 'cert' in data: 1748 | ret.append(self.process_js_certs([data['cert']], name, idx, sub_idx)) 1749 | if 'certs' in data: 1750 | ret.append(self.process_js_certs(data['certs'], name, idx, sub_idx)) 1751 | return ret 1752 | 1753 | def process_js_mod(self, data, name, idx, sub_idx): 1754 | """ 1755 | Processes one moduli from JSON 1756 | :param data: 1757 | :param name: 1758 | :param idx: 1759 | :param sub_idx: 1760 | :return: 1761 | """ 1762 | if isinstance(data, (int, long)): 1763 | js = collections.OrderedDict() 1764 | js['type'] = 'js-mod-num' 1765 | js['fname'] = name 1766 | js['idx'] = idx 1767 | js['sub_idx'] = sub_idx 1768 | js['n'] = '0x%x' % data 1769 | 1770 | if self.has_fingerprint(data): 1771 | logger.warning('Fingerprint found in json int modulus %s idx %s %s' % (name, idx, sub_idx)) 1772 | self.mark_and_add_effort(data, js) 1773 | 1774 | if self.do_print: 1775 | print(json.dumps(js)) 1776 | 1777 | return TestResult(js) 1778 | 1779 | self.process_mod_line(data, name, idx, aux={'stype': 'json', 'sub_idx': sub_idx}) 1780 | 1781 | def process_js_certs(self, data, name, idx, sub_idx): 1782 | """ 1783 | Process one certificate from JSON 1784 | :param data: 1785 | :param name: 1786 | :param idx: 1787 | :param sub_idx: 1788 | :return: 1789 | """ 1790 | from cryptography.x509.base import load_der_x509_certificate 1791 | 1792 | ret = [] 1793 | for crt_hex in data: 1794 | try: 1795 | bindata = base64.b64decode(crt_hex) 1796 | x509 = load_der_x509_certificate(bindata, self.get_backend()) 1797 | 1798 | self.num_ldiff_cert += 1 1799 | sub = self.process_x509(x509, name=name, pem=False, source='ldiff-cert') 1800 | ret.append(sub) 1801 | 1802 | except Exception as e: 1803 | logger.debug('Error in line JSON cert file processing %s, idx %s, subidx %s : %s' 1804 | % (name, idx, sub_idx, e)) 1805 | self.trace_logger.log(e) 1806 | return ret 1807 | 1808 | def process_apk(self, data, name): 1809 | """ 1810 | Processes Android application 1811 | :param data: 1812 | :param name: 1813 | :return: 1814 | """ 1815 | try: 1816 | from apk_parse.apk import APK 1817 | except Exception as e: 1818 | logger.warning('Could not import apk_parse, try running: pip install apk_parse_ph4') 1819 | return [TestResult(fname=name, type='apk-pem-cert', error='cannot-import')] 1820 | 1821 | ret = [] 1822 | try: 1823 | from cryptography.x509.base import load_der_x509_certificate 1824 | apkf = APK(data, process_now=False, process_file_types=False, raw=True, 1825 | temp_dir=self.args.tmp_dir) 1826 | apkf.process() 1827 | self.num_apk += 1 1828 | 1829 | pem = apkf.cert_pem 1830 | aux = {'subtype': 'apk'} 1831 | 1832 | x509 = load_der_x509_certificate(pem_to_der(pem), self.get_backend()) 1833 | 1834 | sub = self.process_x509(x509, name=name, idx=0, data=data, pem=True, source='apk-pem-cert', aux=aux) 1835 | ret.append(sub) 1836 | 1837 | except Exception as e: 1838 | logger.debug('Exception in processing APK %s : %s' % (name, e)) 1839 | self.trace_logger.log(e) 1840 | return ret 1841 | 1842 | def process_mod(self, data, name): 1843 | """ 1844 | Processing one modulus per line 1845 | :param data: 1846 | :param name: 1847 | :return: 1848 | """ 1849 | ret = [] 1850 | try: 1851 | lines = [x.strip() for x in data.split(bytes(b'\n'))] 1852 | for idx, line in enumerate(lines): 1853 | sub = self.process_mod_line(line, name, idx) 1854 | ret.append(sub) 1855 | 1856 | except Exception as e: 1857 | logger.debug('Error in line mod file processing %s : %s' % (name, e)) 1858 | self.trace_logger.log(e) 1859 | return ret 1860 | 1861 | def process_mod_line(self, data, name, idx, aux=None): 1862 | """ 1863 | Processes one line mod 1864 | :param data: 1865 | :param name: 1866 | :param idx: 1867 | :param aux: 1868 | :return: 1869 | """ 1870 | if data is None or len(data) == 0: 1871 | return 1872 | 1873 | ret = [] 1874 | try: 1875 | if self.args.key_fmt_base64 or self.re_match(r'^[a-zA-Z0-9+/=\s\t]+$', data): 1876 | ret.append(self.process_mod_line_num(strip_spaces(data), name, idx, 'base64', aux)) 1877 | 1878 | if self.args.key_fmt_hex or self.re_match(r'^(0x)?[a-fA-F0-9\s\t]+$', data): 1879 | ret.append(self.process_mod_line_num(strip_spaces(data), name, idx, 'hex', aux)) 1880 | 1881 | if self.args.key_fmt_dec or self.re_match(r'^[0-9\s\t]+$', data): 1882 | ret.append(self.process_mod_line_num(strip_spaces(data), name, idx, 'dec', aux)) 1883 | 1884 | except Exception as e: 1885 | logger.debug('Error in line mod processing %s idx %s : %s' % (name, idx, e)) 1886 | self.trace_logger.log(e) 1887 | return ret 1888 | 1889 | def process_mod_line_num(self, data, name, idx, num_type='hex', aux=None): 1890 | """ 1891 | Processes particular number 1892 | :param data: 1893 | :param name: 1894 | :param idx: 1895 | :param num_type: 1896 | :param aux: 1897 | :return: 1898 | """ 1899 | try: 1900 | num = 0 1901 | if num_type == 'base64': 1902 | num = int(base64.b16encode(base64.b64decode(data)), 16) 1903 | elif num_type == 'hex': 1904 | num = int(strip_hex_prefix(data), 16) 1905 | elif num_type == 'dec': 1906 | num = int(data) 1907 | else: 1908 | raise ValueError('Unknown number format: %s' % num_type) 1909 | 1910 | js = collections.OrderedDict() 1911 | js['type'] = 'mod-%s' % num_type 1912 | js['fname'] = name 1913 | js['idx'] = idx 1914 | js['aux'] = aux 1915 | js['n'] = '0x%x' % num 1916 | 1917 | if self.has_fingerprint(num): 1918 | logger.warning('Fingerprint found in modulus %s idx %s ' % (name, idx)) 1919 | self.mark_and_add_effort(num, js) 1920 | 1921 | if self.do_print: 1922 | print(json.dumps(js)) 1923 | return TestResult(js) 1924 | 1925 | except Exception as e: 1926 | logger.debug('Exception in testing modulus %s idx %s : %s data: %s' % (name, idx, e, data[:30])) 1927 | self.trace_logger.log(e) 1928 | 1929 | def process_ldiff(self, data, name): 1930 | """ 1931 | Processes LDAP output 1932 | field;binary::blob 1933 | :param data: 1934 | :param name: 1935 | :return: 1936 | """ 1937 | from cryptography.x509.base import load_der_x509_certificate 1938 | reg = re.compile(r'binary::\s*([0-9a-zA-Z+/=\s\t\r\n]{20,})$', re.MULTILINE | re.DOTALL) 1939 | matches = re.findall(reg, str(data)) 1940 | 1941 | ret = [] 1942 | num_certs_found = 0 1943 | for idx, match in enumerate(matches): 1944 | match = re.sub('[\r\t\n\s]', '', match) 1945 | try: 1946 | bindata = base64.b64decode(match) 1947 | x509 = load_der_x509_certificate(bindata, self.get_backend()) 1948 | 1949 | self.num_ldiff_cert += 1 1950 | sub = self.process_x509(x509, name=name, pem=False, source='ldiff-cert') 1951 | ret.append(sub) 1952 | 1953 | except Exception as e: 1954 | logger.debug('Error in line ldiff file processing %s, idx %s, matchlen %s : %s' 1955 | % (name, idx, len(match), e)) 1956 | self.trace_logger.log(e) 1957 | return ret 1958 | 1959 | def process_jks(self, data, name): 1960 | """ 1961 | Processes Java Key Store file 1962 | :param data: 1963 | :param name: 1964 | :return: 1965 | """ 1966 | if self.jks_file_passwords is None and self.args.jks_pass_file is not None: 1967 | self.jks_file_passwords = [] 1968 | if not os.path.exists(self.args.jks_pass_file): 1969 | logger.warning('JKS password file %s does not exist' % self.args.jks_pass_file) 1970 | with open(self.args.jks_pass_file) as fh: 1971 | self.jks_file_passwords = sorted(list(set([x.strip() for x in fh]))) 1972 | 1973 | if self.jks_file_passwords is None: 1974 | self.jks_file_passwords = [] 1975 | 1976 | try: 1977 | ks = self.try_open_jks(data, name) 1978 | if ks is None: 1979 | logger.warning('Could not open JKS file: %s, password not valid, ' 1980 | 'try specify passwords in --jks-pass-file' % name) 1981 | return 1982 | 1983 | # certs 1984 | from cryptography.x509.base import load_der_x509_certificate 1985 | 1986 | ret = [] 1987 | for alias, cert in list(ks.certs.items()): 1988 | try: 1989 | x509 = load_der_x509_certificate(cert.cert, self.get_backend()) 1990 | 1991 | self.num_jks_cert += 1 1992 | sub = self.process_x509(x509, name=name, pem=False, source='jks-cert', aux='cert-%s' % alias) 1993 | ret.append(sub) 1994 | 1995 | except Exception as e: 1996 | logger.debug('Error in JKS cert processing %s, alias %s : %s' % (name, alias, e)) 1997 | self.trace_logger.log(e) 1998 | 1999 | # priv key chains 2000 | for alias, pk in list(ks.private_keys.items()): 2001 | for idx, cert in enumerate(pk.cert_chain): 2002 | try: 2003 | x509 = load_der_x509_certificate(cert[1], self.get_backend()) 2004 | 2005 | self.num_jks_cert += 1 2006 | sub = self.process_x509(x509, name=name, pem=False, source='jks-cert-chain', 2007 | aux='cert-chain-%s-%s' % (alias, idx)) 2008 | ret.append(sub) 2009 | 2010 | except Exception as e: 2011 | logger.debug('Error in JKS priv key cert-chain processing %s, alias %s %s : %s' 2012 | % (name, alias, idx, e)) 2013 | self.trace_logger.log(e) 2014 | return ret 2015 | 2016 | except ImportException: 2017 | return [TestResult(fname=name, type='jks-cert', error='cannot-import')] 2018 | 2019 | except Exception as e: 2020 | logger.warning('Exception in JKS processing: %s' % e) 2021 | return None 2022 | 2023 | def try_open_jks(self, data, name): 2024 | """ 2025 | Tries to guess JKS password 2026 | :param name: 2027 | :param data: 2028 | :return: 2029 | """ 2030 | try: 2031 | import jks 2032 | except: 2033 | logger.warning('Could not import jks, try running: pip install pyjks') 2034 | raise ImportException('Cannot import pyjks') 2035 | 2036 | pwdlist = sorted(list(set(self.jks_file_passwords + self.jks_passwords))) 2037 | for cur in pwdlist: 2038 | try: 2039 | return jks.KeyStore.loads(data, cur) 2040 | except Exception as e: 2041 | pass 2042 | return None 2043 | 2044 | def process_pkcs7(self, data, name): 2045 | """ 2046 | Process PKCS7 signature with certificate in it. 2047 | :param data: 2048 | :param name: 2049 | :return: 2050 | """ 2051 | from cryptography.hazmat.backends.openssl.backend import backend 2052 | from cryptography.hazmat.backends.openssl.x509 import _Certificate 2053 | 2054 | # DER conversion 2055 | is_pem = startswith(data, '-----') 2056 | if self.re_match(r'^[a-zA-Z0-9-\s+=/]+$', data): 2057 | is_pem = True 2058 | 2059 | try: 2060 | der = data 2061 | if is_pem: 2062 | data = data.decode('utf8') 2063 | data = re.sub(r'\s*-----\s*BEGIN\s+PKCS7\s*-----', '', data) 2064 | data = re.sub(r'\s*-----\s*END\s+PKCS7\s*-----', '', data) 2065 | der = base64.b64decode(data) 2066 | 2067 | bio = backend._bytes_to_bio(der) 2068 | pkcs7 = backend._lib.d2i_PKCS7_bio(bio.bio, backend._ffi.NULL) 2069 | backend.openssl_assert(pkcs7 != backend._ffi.NULL) 2070 | signers = backend._lib.PKCS7_get0_signers(pkcs7, backend._ffi.NULL, 0) 2071 | backend.openssl_assert(signers != backend._ffi.NULL) 2072 | backend.openssl_assert(backend._lib.sk_X509_num(signers) > 0) 2073 | x509_ptr = backend._lib.sk_X509_value(signers, 0) 2074 | backend.openssl_assert(x509_ptr != backend._ffi.NULL) 2075 | x509_ptr = backend._ffi.gc(x509_ptr, backend._lib.X509_free) 2076 | x509 = _Certificate(backend, x509_ptr) 2077 | 2078 | self.num_pkcs7_cert += 1 2079 | 2080 | return [self.process_x509(x509, name=name, pem=False, source='pkcs7-cert', aux='')] 2081 | 2082 | except Exception as e: 2083 | logger.debug('Error in PKCS7 processing %s: %s' % (name, e)) 2084 | self.trace_logger.log(e) 2085 | 2086 | # 2087 | # Helpers & worker 2088 | # 2089 | 2090 | def re_match(self, pattern, haystack, **kwargs): 2091 | """ 2092 | re.match py3 compat 2093 | :param pattern: 2094 | :param haystack: 2095 | :return: 2096 | """ 2097 | try: 2098 | return re.match(pattern, haystack.decode('utf8'), **kwargs) 2099 | 2100 | except Exception as e: 2101 | logger.debug('re.match exception: %s' % e) 2102 | self.trace_logger.log(e) 2103 | 2104 | def strtime(self, x): 2105 | """ 2106 | Simple time format 2107 | :param x: 2108 | :return: 2109 | """ 2110 | if x is None: 2111 | return x 2112 | return x.strftime('%Y-%m-%d') 2113 | 2114 | def get_backend(self, backend=None): 2115 | """ 2116 | Default crypto backend 2117 | :param backend: 2118 | :return: 2119 | """ 2120 | from cryptography.hazmat.backends import default_backend 2121 | return default_backend() if backend is None else backend 2122 | 2123 | def dump(self, ret): 2124 | """ 2125 | Dumps the return value 2126 | :param ret: 2127 | :return: 2128 | """ 2129 | if self.args.flatten: 2130 | ret = drop_none(flatten(ret)) 2131 | 2132 | logger.info('Dump: \n' + json.dumps(ret, cls=AutoJSONEncoder, indent=2 if self.args.indent else None)) 2133 | 2134 | def work(self): 2135 | """ 2136 | Entry point after argument processing. 2137 | :return: 2138 | """ 2139 | self.do_print = True 2140 | if self.args.old: 2141 | self.switch_fingerprint_method(True) 2142 | 2143 | ret = self.process_inputs() 2144 | 2145 | if self.args.dump: 2146 | self.dump(ret) 2147 | 2148 | logger.info('### SUMMARY ####################') 2149 | logger.info('Records tested: %s' % self.tested) 2150 | logger.info('.. PEM certs: . . . %s' % self.num_pem_certs) 2151 | logger.info('.. DER certs: . . . %s' % self.num_der_certs) 2152 | logger.info('.. RSA key files: . %s' % self.num_rsa_keys) 2153 | logger.info('.. PGP master keys: %s' % self.num_pgp_masters) 2154 | logger.info('.. PGP total keys: %s' % self.num_pgp_total) 2155 | logger.info('.. SSH keys: . . . %s' % self.num_ssh) 2156 | logger.info('.. APK keys: . . . %s' % self.num_apk) 2157 | logger.info('.. JSON keys: . . . %s' % self.num_json) 2158 | logger.info('.. LDIFF certs: . . %s' % self.num_ldiff_cert) 2159 | logger.info('.. JKS certs: . . . %s' % self.num_jks_cert) 2160 | logger.info('.. PKCS7: . . . . . %s' % self.num_pkcs7_cert) 2161 | logger.debug('. Total RSA keys . %s (# of keys RSA extracted & analyzed)' % self.num_rsa) 2162 | if self.found > 0: 2163 | logger.info('Fingerprinted keys found: %s' % self.found) 2164 | logger.info('WARNING: Potential vulnerability') 2165 | else: 2166 | logger.info('No fingerprinted keys found (OK)') 2167 | logger.info('################################') 2168 | 2169 | def init_parser(self): 2170 | """ 2171 | Init command line parser 2172 | :return: 2173 | """ 2174 | parser = argparse.ArgumentParser(description='ROCA Fingerprinter') 2175 | 2176 | parser.add_argument('--tmp', dest='tmp_dir', default='.', 2177 | help='Temporary dir for subprocessing (e.g. APK parsing scratch)') 2178 | 2179 | parser.add_argument('--debug', dest='debug', default=False, action='store_const', const=True, 2180 | help='Debugging logging') 2181 | 2182 | parser.add_argument('--dump', dest='dump', default=False, action='store_const', const=True, 2183 | help='Dump all processed info') 2184 | 2185 | parser.add_argument('--flatten', dest='flatten', default=False, action='store_const', const=True, 2186 | help='Flatten the dump') 2187 | 2188 | parser.add_argument('--indent', dest='indent', default=False, action='store_const', const=True, 2189 | help='Indent the dump') 2190 | 2191 | parser.add_argument('--old', dest='old', default=False, action='store_const', const=True, 2192 | help='Old fingerprinting algorithm - moduli detector') 2193 | 2194 | parser.add_argument('--base64-stdin', dest='base64stdin', default=False, action='store_const', const=True, 2195 | help='Decode STDIN as base64') 2196 | 2197 | parser.add_argument('--file-pem', dest='file_pem', default=False, action='store_const', const=True, 2198 | help='Force read as PEM encoded file') 2199 | 2200 | parser.add_argument('--file-der', dest='file_der', default=False, action='store_const', const=True, 2201 | help='Force read as DER encoded file') 2202 | 2203 | parser.add_argument('--file-pgp', dest='file_pgp', default=False, action='store_const', const=True, 2204 | help='Force read as PGP ASC encoded file') 2205 | 2206 | parser.add_argument('--file-ssh', dest='file_ssh', default=False, action='store_const', const=True, 2207 | help='Force read as SSH public key file') 2208 | 2209 | parser.add_argument('--file-mod', dest='file_mod', default=False, action='store_const', const=True, 2210 | help='Force read as One modulus per line') 2211 | 2212 | parser.add_argument('--file-json', dest='file_json', default=False, action='store_const', const=True, 2213 | help='Force read as JSON file') 2214 | 2215 | parser.add_argument('--file-ldiff', dest='file_ldiff', default=False, action='store_const', const=True, 2216 | help='Force read as LDIFF file') 2217 | 2218 | parser.add_argument('--file-pkcs7', dest='file_pkcs7', default=False, action='store_const', const=True, 2219 | help='Force read as PKCS7 file') 2220 | 2221 | parser.add_argument('--key-fmt-base64', dest='key_fmt_base64', default=False, action='store_const', const=True, 2222 | help='Modulus per line, base64 encoded') 2223 | 2224 | parser.add_argument('--key-fmt-hex', dest='key_fmt_hex', default=False, action='store_const', const=True, 2225 | help='Modulus per line, hex encoded') 2226 | 2227 | parser.add_argument('--key-fmt-dec', dest='key_fmt_dec', default=False, action='store_const', const=True, 2228 | help='Modulus per line, dec encoded') 2229 | 2230 | parser.add_argument('--jks-pass-file', dest='jks_pass_file', default=None, 2231 | help='Password file for JKS, one per line') 2232 | 2233 | parser.add_argument('files', nargs=argparse.ZERO_OR_MORE, default=[], 2234 | help='files to process') 2235 | return parser 2236 | 2237 | def main(self): 2238 | """ 2239 | Main entry point 2240 | :return: 2241 | """ 2242 | parser = self.init_parser() 2243 | self.args = parser.parse_args() 2244 | 2245 | if self.args.debug: 2246 | coloredlogs.install(level=logging.DEBUG, fmt=LOG_FORMAT) 2247 | 2248 | self.work() 2249 | 2250 | 2251 | def main(): 2252 | app = RocaFingerprinter() 2253 | app.main() 2254 | 2255 | 2256 | if __name__ == '__main__': 2257 | main() 2258 | 2259 | -------------------------------------------------------------------------------- /roca/detect_tls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | TLS fingerprinting 6 | 7 | Reads from one or more text files that contain a newline-separated list of 8 | address:port entries. Example: 9 | 10 | github.com:443 11 | google.com:443 12 | internal.example.com:8080 13 | 14 | 15 | Script requirements: 16 | 17 | - See detect.py requirements 18 | 19 | """ 20 | 21 | import sys 22 | import argparse 23 | import logging 24 | import coloredlogs 25 | from ssl import get_server_certificate 26 | from roca.detect import RocaFingerprinter 27 | 28 | 29 | logger = logging.getLogger(__name__) 30 | 31 | 32 | # 33 | # Main class 34 | # 35 | 36 | class RocaTLSFingerprinter(object): 37 | """ 38 | TLS fingerprinter 39 | """ 40 | 41 | def __init__(self): 42 | self.args = None 43 | self.roca = RocaFingerprinter() 44 | 45 | def process_tls(self, data, name): 46 | """ 47 | Remote TLS processing - one address:port per line 48 | :param data: 49 | :param name: 50 | :return: 51 | """ 52 | ret = [] 53 | try: 54 | lines = [x.strip() for x in data.split('\n')] 55 | for idx, line in enumerate(lines): 56 | if line == '': 57 | continue 58 | 59 | sub = self.process_host(line, name, idx) 60 | if sub is not None: 61 | ret.append(sub) 62 | 63 | except Exception as e: 64 | logger.error('Error in file processing %s : %s' % (name, e)) 65 | self.roca.trace_logger.log(e) 66 | return ret 67 | 68 | def process_host(self, host_spec, name, line_idx=0): 69 | """ 70 | One host spec processing 71 | :param host_spec: 72 | :param name: 73 | :param line_idx: 74 | :return: 75 | """ 76 | try: 77 | parts = host_spec.split(':', 1) 78 | host = parts[0].strip() 79 | port = parts[1] if len(parts) > 1 else 443 80 | pem_cert = self.get_server_certificate(host, port) 81 | if pem_cert: 82 | sub = self.roca.process_pem_cert(pem_cert, name, line_idx) 83 | return sub 84 | 85 | except Exception as e: 86 | logger.error('Error in file processing %s (%s) : %s' % (host_spec, name, e)) 87 | self.roca.trace_logger.log(e) 88 | 89 | def get_server_certificate(self, host, port): 90 | """ 91 | Gets the remote x.509 certificate 92 | :param host: 93 | :param port: 94 | :return: 95 | """ 96 | logger.info("Fetching server certificate from %s:%s" % (host,port)) 97 | try: 98 | return get_server_certificate((host, int(port))) 99 | except Exception as e: 100 | logger.error('Error getting server certificate from %s:%s: %s' % 101 | (host, port, e)) 102 | return False 103 | 104 | def process_inputs(self): 105 | """ 106 | Processes input data 107 | :return: 108 | """ 109 | ret = [] 110 | files = self.args.files 111 | if files is None: 112 | return ret 113 | 114 | for fname in files: 115 | 116 | # arguments are host specs 117 | if self.args.hosts: 118 | sub = self.process_host(fname, fname, 0) 119 | if sub is not None: 120 | ret.append(sub) 121 | continue 122 | 123 | # arguments are file names 124 | fh = open(fname, 'r') 125 | with fh: 126 | data = fh.read() 127 | sub = self.process_tls(data, fname) 128 | ret.append(sub) 129 | 130 | return ret 131 | 132 | def work(self): 133 | """ 134 | Entry point after argument processing. 135 | :return: 136 | """ 137 | self.roca.do_print = True 138 | ret = self.process_inputs() 139 | 140 | if self.args.dump: 141 | self.roca.dump(ret) 142 | 143 | if self.roca.found > 0: 144 | logger.info('Fingerprinted keys found: %s' % self.roca.found) 145 | logger.info('WARNING: Potential vulnerability') 146 | else: 147 | logger.info('No fingerprinted keys found (OK)') 148 | 149 | def init_parser(self): 150 | """ 151 | Init command line parser 152 | :return: 153 | """ 154 | parser = argparse.ArgumentParser(description='ROCA TLS Fingerprinter') 155 | 156 | parser.add_argument('--debug', dest='debug', default=False, action='store_const', const=True, 157 | help='Debugging logging') 158 | 159 | parser.add_argument('--dump', dest='dump', default=False, action='store_const', const=True, 160 | help='Dump all processed info') 161 | 162 | parser.add_argument('--flatten', dest='flatten', default=False, action='store_const', const=True, 163 | help='Flatten the dump') 164 | 165 | parser.add_argument('--indent', dest='indent', default=False, action='store_const', const=True, 166 | help='Indent the dump') 167 | 168 | parser.add_argument('--hosts', dest='hosts', default=False, action='store_const', const=True, 169 | help='Arguments are host names not file names') 170 | 171 | parser.add_argument('files', nargs=argparse.ZERO_OR_MORE, default=[], 172 | help='files to process') 173 | return parser 174 | 175 | def main(self): 176 | """ 177 | Main entry point 178 | :return: 179 | """ 180 | parser = self.init_parser() 181 | if len(sys.argv) < 2: 182 | parser.print_usage() 183 | sys.exit(0) 184 | 185 | self.args = parser.parse_args() 186 | self.roca.args.flatten = self.args.flatten 187 | self.roca.args.indent = self.args.indent 188 | 189 | if self.args.debug: 190 | coloredlogs.install(level=logging.DEBUG) 191 | self.roca.args.debug = True 192 | 193 | self.work() 194 | 195 | 196 | def main(): 197 | app = RocaTLSFingerprinter() 198 | app.main() 199 | 200 | 201 | if __name__ == "__main__": 202 | main() 203 | -------------------------------------------------------------------------------- /roca/generate.sage: -------------------------------------------------------------------------------- 1 | import sympy.ntheory as nt 2 | import random 3 | 4 | 5 | def prime_default(length=0): 6 | if length >= 3968: 7 | return 1427 # real bound 3968 8 | if length >= 1984: 9 | return 701 # 701 is used from 1984 rather than 2048 10 | if length >= 992: 11 | return 353 # 353 is used from 992 rather than 1024 12 | if length >= 512: 13 | return 167 14 | return 167 # no data for <512, but that doesn't matter anyway 15 | 16 | 17 | def m_default(length): 18 | return nt.primorial(prime_default(length), False) 19 | 20 | 21 | def gord(x): 22 | return Zmod(x)(65537).multiplicative_order() 23 | 24 | 25 | def randpri(g=65537, bits=512, M=None, exact_bits=False): 26 | if M is None: 27 | M = m_default(bits) 28 | 29 | prime_bits = bits // 2r 30 | M_bits = log(M, 2r) 31 | gm = Mod(g, M) # Zmod ring 32 | 33 | gordm = gord(M) 34 | max_a = gordm - 1r 35 | min_a = 2r 36 | 37 | ga = random.randint(min_a, max_a) 38 | u = lift(gm^ga) # g^a mod M, almost always lg(M) bits in size 39 | 40 | g_start = floor(prime_bits - M_bits) - 1 41 | gk_start = 2 ** g_start 42 | gk_end = 2 ** (g_start+1) 43 | 44 | while True: 45 | gk = random.randint(gk_start, gk_end) 46 | w = M * gk 47 | gp = w + u 48 | if exact_bits and prime_bits != round(log(gp, 2)): 49 | continue 50 | if Integer(gp).is_prime(proof=False): 51 | # print('gk: 0x%x\nga: 0x%x\ngp: 0x%x' % (gk, ga, gp)) 52 | return int(gk), int(ga), int(gp) 53 | 54 | 55 | def roca(bits=2048): 56 | while True: 57 | p_k, p_a, p = randpri(bits=bits, exact_bits=True) 58 | q_k, q_a, q = randpri(bits=bits, exact_bits=True) 59 | N = p * q 60 | 61 | n_bits = ceil(log(N, 2)) 62 | if n_bits == bits: 63 | return p, q 64 | 65 | 66 | # print(roca(2048)) 67 | -------------------------------------------------------------------------------- /roca/tests/README.md: -------------------------------------------------------------------------------- 1 | # ROCA test vectors 2 | 3 | Positive keys: 4 | 5 | * mod01.txt 6 | * mod02.txt 7 | * mod03.txt 8 | * mod08.txt 9 | * mod09.txt 10 | * key04.pgp 11 | * cert04.pem 12 | * cert05.pem 13 | * ssh06.pub 14 | * pubkey03.pem 15 | * privkey05.pem 16 | 17 | ## Fake moduli 18 | 19 | It is possible to generate moduli that passes the moduli fingerprinting test but actually do not contain structure 20 | the factorization algorithm is using. Dlog moduli test do not mark those as positive. 21 | 22 | 23 | -------------------------------------------------------------------------------- /roca/tests/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'dusanklinec' 2 | -------------------------------------------------------------------------------- /roca/tests/data/cert01.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ 3 | MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT 4 | DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow 5 | SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT 6 | GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC 7 | AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF 8 | q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 9 | SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 10 | Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA 11 | a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj 12 | /PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T 13 | AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG 14 | CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv 15 | bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k 16 | c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw 17 | VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC 18 | ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz 19 | MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu 20 | Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF 21 | AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo 22 | uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ 23 | wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu 24 | X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG 25 | PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 26 | KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /roca/tests/data/cert02.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFETCCA/mgAwIBAgISBLwGPC99RzpJIpGR5fe085h1MA0GCSqGSIb3DQEBCwUA 3 | MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD 4 | ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNzA4MTEwODUwMDBaFw0x 5 | NzExMDkwODUwMDBaMCIxIDAeBgNVBAMTF2FuY2hvci5lbmlnbWFicmlkZ2UuY29t 6 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr2WWdhl/HbBtp6wRGgoO 7 | wXW4t+8HGS/80fa4VMMneF8af9PbfaRc1KMbKy5c4Ngjpo4oyK3xdHMWvszh/ldo 8 | BkA5rSBiJgyNGjTgWG3Om8EwkPzJ+4uLLAjtOujWGymBimaWiafZwdqwU7VX+40/ 9 | nET6rT4YxV5zmwDTyRJlWLyOmAzSrzdxJu9bE3QTZ3S4vTcOPBwUnbOVyPmWlrYo 10 | sQWUb+ogEG/iTRA6wGJmGpJI6MP2KOALMI0zlTqr5VUTLiGOdO9LV4cWtP5880Do 11 | 5gSjGb9umVHhlCYB00KRAy21SZnQnl0Dbd41RK01JWu7l9Xj//04Fmwh6ukZlUiF 12 | OwIDAQABo4ICFzCCAhMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF 13 | BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT/+523mWwe9kcK 14 | b8pG25FsYAK+lDAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggr 15 | BgEFBQcBAQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRz 16 | ZW5jcnlwdC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRz 17 | ZW5jcnlwdC5vcmcvMCIGA1UdEQQbMBmCF2FuY2hvci5lbmlnbWFicmlkZ2UuY29t 18 | MIH+BgNVHSAEgfYwgfMwCAYGZ4EMAQIBMIHmBgsrBgEEAYLfEwEBATCB1jAmBggr 19 | BgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwgasGCCsGAQUFBwIC 20 | MIGeDIGbVGhpcyBDZXJ0aWZpY2F0ZSBtYXkgb25seSBiZSByZWxpZWQgdXBvbiBi 21 | eSBSZWx5aW5nIFBhcnRpZXMgYW5kIG9ubHkgaW4gYWNjb3JkYW5jZSB3aXRoIHRo 22 | ZSBDZXJ0aWZpY2F0ZSBQb2xpY3kgZm91bmQgYXQgaHR0cHM6Ly9sZXRzZW5jcnlw 23 | dC5vcmcvcmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBAEjEX7Orx4EWqDA+ 24 | e5+a4nErwO6wVzH7gk6w6xdFGhYqvsYD4LSQzlWg+ETLXcvWIQMoTx5/rYtDxpG9 25 | 43qfZbdHjspVze2pfhR71sq76SZzthJw0tUeLFdo9NpFQziCe3fWP7eifX28oFbV 26 | He82hLWfgGFdJgJTPpLhwRYps6BLb8Le58YuVLChWqRw/JV5zSN3eHVQIejnpaf9 27 | Pwo3+WT/2BjyYLBUsbFBigZmy9/SHlfrQCBI2jxWYU2g4DNIZCekGOQqaJGKTBOz 28 | kgSivyzb4B8du0gaIq93rsLurueD/gJFSapDWWkX+OjXdL/kH1uyx+l+dUcSPc4t 29 | E0lRHOM= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /roca/tests/data/cert03.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEEzCCAvugAwIBAgIJAJ07mZHF8IutMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD 3 | VQQGEwJVSzESMBAGA1UECAwJQ2FtYnJpZGdlMRIwEAYDVQQHDAlDYW1icmlkZ2Ux 4 | GjAYBgNVBAoMEUVuaWdtYWJyaWRnZSBMdGQuMQswCQYDVQQLDAJJVDEZMBcGA1UE 5 | AwwQZW5pZ21hYnJpZGdlLmNvbTEkMCIGCSqGSIb3DQEJARYVaW5mb0BlbmlnbWFi 6 | cmlkZ2UuY29tMB4XDTE3MDkxNDEyNDgyNVoXDTQ0MDIxNTEyNDgyNVowgZ8xCzAJ 7 | BgNVBAYTAlVLMRIwEAYDVQQIDAlDYW1icmlkZ2UxEjAQBgNVBAcMCUNhbWJyaWRn 8 | ZTEaMBgGA1UECgwRRW5pZ21hYnJpZGdlIEx0ZC4xCzAJBgNVBAsMAklUMRkwFwYD 9 | VQQDDBBlbmlnbWFicmlkZ2UuY29tMSQwIgYJKoZIhvcNAQkBFhVpbmZvQGVuaWdt 10 | YWJyaWRnZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6aPxf 11 | u2BsTUKKuYs1XWYhTbT35HW19//dw0nHP31lmWUSGrFqQBTmxGXBEdhQIFhT0RP3 12 | EhJ+SiMKqX9IHu1kEPbDRfSOJfuBxC7GgV3DXN2Dng3pH7K+yEjQkQ+nfiwx+Z7J 13 | vMftFngSvXJDGLCt9JLWtHxc4FAYTod+pkBF+VAb9wVpz/0QLbcpn0npwAIir5EV 14 | 0Kraln3+dZt+RXboXDpP9ZQDkjG5C0SLkMMjYIO8+Bm8MY1TogGOybQB8QK6nuE9 15 | 3lkqQrAy3xNmidnFYZ21XVGYU6Qi0xAGJNLZ5Hghg3X0YMJpMO2ozQ2Uflj/6be+ 16 | JEU1bertmIIj4+0dAgMBAAGjUDBOMB0GA1UdDgQWBBR7Uga0wsLRKMtxpKw6wYCU 17 | V3w1rTAfBgNVHSMEGDAWgBR7Uga0wsLRKMtxpKw6wYCUV3w1rTAMBgNVHRMEBTAD 18 | AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAIX5GIGYilxT0YwbW98aCTZO+0/cacX/Ej 19 | Wx4hBxAI9k48KqqeebdbmexxscXPlEoPSD+tx++xmc7rDhDjwosCTa2YOaPsHUyV 20 | 83zUfP9i3j3+N6T42+R7XOLvCq7YyIKZj0WZr33JTFCrbuYFkZxKRFuvS1LPiUaI 21 | 8TbDHQ+1wG4S2PgClZpo2wXR9zfGKqNMbSb2qRgLI5f1Yz7So2euJDgbYAouPOlL 22 | uFE17TxjVsMmuNhFKTS+G+i/L1T4aa5w4IB459jXiSug64SF59m2HA7WsjXLnBQ+ 23 | QWmzZ9YctSkvSC5nZkjEt6KjyZjJrofatjsy31fF3T/niayJj+0b 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /roca/tests/data/cert04.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpTCCAYwCCQC2u0PIfFaGMjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMDE2MTkzODIxWhcNMTgxMDE2MTkzODIxWjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQJZ 5 | J7UrpeaMjJJou5IY83ZzYUymVBj0dFsUPNTuU/lJHJoOHC8jqVFjBq/784ZnuHG8 6 | DMguYPW7Gp+hWlZxp2XJ8huVh9gBFZZDcqODyIOw3L9sd1cGsx6v8+P9SIVZoIze 7 | og+al8TFm2uKjuykV9SoINSVCfdZM2MCvKGjaQsICRgR+Fjy6M6lpiNVrW4EHRk1 8 | 7aWSibWXaDtz4mV650v/x2Dk1RPMh9uTVZGOqgjTmLvl9oNdyHElIRubNrOgvHC5 9 | k6bLP30stAYd5z25cslCrfmVW2/kzZDwDQiK7ASvH17/kfIa9e1EYXx9uAk/lTZt 10 | smWAxK85neuU+bFBMFvhAgMBAAEwDQYJKoZIhvcNAQELBQADggECAAG7W49CYRUk 11 | YAFRGXu3M85MKOISyc/kkJ8nbHdV6GxJ05FkoDKbcbZ7nncJiIp2VMAMEIP4bRTJ 12 | 5U4g4vSZlmCs8BDmV3Ts/tbDCM6eqK+2hwjhUnCnmmsLt4xVUeAAsWUHl9AVtjzd 13 | oYlm1Kk20QBzNpsvM/gFS5B+duHvTSfELfoq9Pdfvmn2gEXJHe9scO8bfT3fm15z 14 | R6AUYsSsxAhup2Rix6jgJ14KGsh6uVm6jhz9aBTBcgx7iMuuP8zUbUE6nryHYXnR 15 | cSvuYSesTCoFfnL7elrZDak/n0jLfwUD80aWnReJfu9QQGdqdDnSG8lSQ1XPOC7O 16 | /hFW9l0TCzOE 17 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /roca/tests/data/cert05.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpTCCAYwCCQCEcnuDiu6k0DANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMDE2MTk0NjEwWhcNMTgxMDE2MTk0NjEwWjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQGp 5 | xaaH24+yow+C3/jdoVTCke8nsEsl4YW4qyDExqX2BIWf5Z7eVKCvfA5yiIAK1xk7 6 | tzGlvx4RcNehxmkw1B6j6yHe33uK9dQqkcNPcgRblu/VfLGCMB3cwmln5VCOmriJ 7 | mG1X6Dff5w+Z/DfEmPQ8F8O5sGTPcERD16Az4DqCFpeqvzD7Ke59M7N2eWWokFjj 8 | s5dUvTocAH0oojsM+nn51xGsbkxLT3ewY0sezWhZGvA8fVO2Kl8tkRY9TvT48mgx 9 | rkYe2F7F94jFEj/8Esh0IP3P6h0bSlLDUhgk+zmYNIc5tiqYbY52siibwuuLwG0T 10 | gUeFG4OgcYPSEFWfkGMhAgMBAAEwDQYJKoZIhvcNAQELBQADggECAAGZiTHt6Hgd 11 | yDjR9aH9O5oNvtmTPf6tJtKhIu1VEnWjxpXK35Dzmw5AvSSL4EVB/nAYx63eonfI 12 | 4BLM3YOPLFzrIJn6LE37LMug90vCz8zPOChWR7HTgroLyh3O2nPjvJL/SuoBya6g 13 | OwKg6gqF4Kmc4Q3BAY8QiDnQP3pIxF46WKq+r6/QqILuZRE9W7SwjTBP1jKmVztM 14 | 3tyL9kVl7RbRA9/ovXHlEfkN7lJqqYKMZvJZPsxm6VJQRsW1rl55bI8Vqys2DOnx 15 | Nm01jtxuMVukAWEj4J8ExCKnd5EhRO/fbOfbwUVythhIsevkdXUC3PiZhDcK+Cge 16 | PbJmeM8KehS3 17 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /roca/tests/data/cert06.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICpDCCAYwCCQCvX1dxp53lPjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMDE2MTk0NjI4WhcNMTgxMDE2MTk0NjI4WjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCb 5 | GJ1aMyQCCo8CnKN8qpjwTQiCtojYL8amNVXx9vOePer92imKYXJ4Ng/hVVV82pCa 6 | XF84VgvcrezH9Wwy9qAIbQmAfp081SbrAj0TZvWah2rJlVlnaeWZwRlRxzV8qaOr 7 | +6A1prucdQQef19maTBCQNA2Pji9kWvfrli+ClwM2DgASv9F2iC0fjAZagM98htX 8 | Q0apdqP/klQSD67BXVCgJFt1a/5j4/o6vB1tCV2fUat1rXW2En5RC/3jdL9ErWSk 9 | bT4tbHIk4m+EtB/Peugt9jTFT6eZ4kKwP+0mANJGC9iwZpyWd38gBpFNSIaLAccf 10 | +EeNpIs+c21uke9kpq3fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADZBh7oh7Wed 11 | PcbKIKqDZpk1GGV/Z9BYywft+kyutGhNYpX00gJBV7sYiCaLqpJMR+POGuLV4Fcu 12 | SiVzlPXvYKme4nL8JemGrmLd9JnCddMETiAdZ9Z4RizHj0jyle94ceWIhtletyt1 13 | tg7AfkRP6vUV69Mb0e1ieyOWPvAScAFMmlQ+QNG8bNL9+iVGtfsbEOX/ptuB/Enx 14 | tO/37ACqf0UeKczWC9rpRB7CtRzOLOmFS8aANGrd+lAp0TBkteOBhlmW6066qDL6 15 | cZBiL14ytOcU5LhfmcX6lrwUefOlLH/J8pLFb1g5NJxTooC17CFP/55Tab0Z1JER 16 | 7FpVsNDAPGM= 17 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /roca/tests/data/csr03.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIC0TCCAbkCAQAwgYsxCzAJBgNVBAYTAkNaMQ0wCwYDVQQIDARCcm5vMQ0wCwYD 3 | VQQHDARCcm5vMRswGQYDVQQKDBJNYXNhcnlrIFVuaXZlcnNpdHkxJjAkBgNVBAsM 4 | HUZhY3VsdHkgb2YgSW5mb3JtYXRpY3MsIENSb0NTMRkwFwYDVQQDDBBjcm9jcy5m 5 | aS5tdW5pLmN6MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumj8X7tg 6 | bE1CirmLNV1mIU209+R1tff/3cNJxz99ZZllEhqxakAU5sRlwRHYUCBYU9ET9xIS 7 | fkojCql/SB7tZBD2w0X0jiX7gcQuxoFdw1zdg54N6R+yvshI0JEPp34sMfmeybzH 8 | 7RZ4Er1yQxiwrfSS1rR8XOBQGE6HfqZARflQG/cFac/9EC23KZ9J6cACIq+RFdCq 9 | 2pZ9/nWbfkV26Fw6T/WUA5IxuQtEi5DDI2CDvPgZvDGNU6IBjsm0AfECup7hPd5Z 10 | KkKwMt8TZonZxWGdtV1RmFOkItMQBiTS2eR4IYN19GDCaTDtqM0NlH5Y/+m3viRF 11 | NW3q7ZiCI+PtHQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAJ1m38NUOMlspvIh 12 | 7rXAXmrYKnBZ/L2slgwBo5whdbd/nKpjI/x24tNsnV3o+HHWA50zesyLJ1Z3xIZ5 13 | 5V3HX4YB5Fm3BEbNB6/ERzqRXMYoKLHLMXZJ3J8vReZx1jHtu4nGZ3iNBcc4+KFt 14 | hnChpGII3KI4nqbQpBKBhkqWKnGNrB0tn1I1pRmwrqrgKpp0YNbQRdygfyvAbCUc 15 | 4Cyh3eq6AZzUWqjizD3IJA3Xy1kvFhFuANeSEAygKwWUXHyKbYnuFishwBDaLTRV 16 | quLc4mc4ISTukvKLVj0W3/HA5rJMI8WOT2P8p9knFd73kcN0HKL6ujrnQXRRzSeD 17 | VQHdT3A= 18 | -----END CERTIFICATE REQUEST----- 19 | -------------------------------------------------------------------------------- /roca/tests/data/csr05.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIC0jCCAbkCAQAwgYsxCzAJBgNVBAYTAkNaMQ0wCwYDVQQIDARCcm5vMQ0wCwYD 3 | VQQHDARCcm5vMRswGQYDVQQKDBJNYXNhcnlrIFVuaXZlcnNpdHkxJjAkBgNVBAsM 4 | HUZhY3VsdHkgb2YgSW5mb3JtYXRpY3MsIENSb0NTMRkwFwYDVQQDDBBjcm9jcy5m 5 | aS5tdW5pLmN6MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEBqcWmh9uP 6 | sqMPgt/43aFUwpHvJ7BLJeGFuKsgxMal9gSFn+We3lSgr3wOcoiACtcZO7cxpb8e 7 | EXDXocZpMNQeo+sh3t97ivXUKpHDT3IEW5bv1XyxgjAd3MJpZ+VQjpq4iZhtV+g3 8 | 3+cPmfw3xJj0PBfDubBkz3BEQ9egM+A6ghaXqr8w+ynufTOzdnllqJBY47OXVL06 9 | HAB9KKI7DPp5+dcRrG5MS093sGNLHs1oWRrwPH1TtipfLZEWPU70+PJoMa5GHthe 10 | xfeIxRI//BLIdCD9z+odG0pSw1IYJPs5mDSHObYqmG2OdrIom8Lri8BtE4FHhRuD 11 | oHGD0hBVn5BjIQIDAQABoAAwDQYJKoZIhvcNAQELBQADggECAABFbW5YQEdkFcXw 12 | FCkNKvr7Xtnnihn+bsQgAFfHySK8oNxsviSoGbkz5SVjaldrHklFADoW1z58FDHW 13 | 7zx7YLA73aRHYZJ4BE8vys8oMqZFutS3lPJgN5GPcEiPYsBlgNWldTGe5h2JdbBp 14 | K+pz9h2NnaY4/1+z6iONieox+AhqljL0XHZYxIxrhu5LaJj0ufd3EMLC8Nudye61 15 | 5xLWhO2YrXuyhdoaoO5M6meDpMq+FjHjKN2NfyDg4FCdOs7AgyLKJHbjQFeNPIRj 16 | cbu4jtfN/vk1uVEc1M9WlcibklbikV1eO7dtmG9Td/HD8KQFyI08ytlN8YZJ7IMf 17 | d/I4QXTy 18 | -----END CERTIFICATE REQUEST----- 19 | -------------------------------------------------------------------------------- /roca/tests/data/key01.pgp: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: BCPG C# v1.6.1.0 3 | 4 | mQENBFnKMMEBCADEKMeJAT/GD5za2TZIy2nAwQEXbOP4rUbVbDMtSi/F8O4fURHZ 5 | 14hitmuEFB467RExjpnGrIh7/cCd8Bn1Tgrv8hIxEekiB3R/DQ/3y1xafggIfObl 6 | TuAWXJrRNn2xrQx/n89CFVZ7pIrhPLMOq/RW96L07q9OwQSygAU2SAgitfw+RjpS 7 | TgWjh0Hb1gouan8Bu1dGR0guVJwHQfE7fOSKYq7YRtjJe6J7ra79mwZA+OY8+4Gg 8 | KpwutJFLTYNqpOi6MhqxJTOkMN1wEyU2kd1RkAhAiEgHkT0u2zFYO365E1H1O7ud 9 | 7/4QHaX4EVTv3J8D9ErflPKnedfl6uqtu2thABEBAAG0FmR1c2FuQGVuaWdtYWJy 10 | aWRnZS5jb22JARwEEAECAAYFAlnKMMEACgkQGTRgLToKJ/6X+wf/T2r9mneBqmxn 11 | 5bY3iaQ0hJngFXMTp3GYpzYIxYewFZx345u0FY49NHx4AqFDDjFLg/13sSPpalZK 12 | HV0cs+yzRnuASLa6PXyq3TKKu6PT+ApO7f+CcKN+y/qGW/dC89Md7T6tUwdAFwsP 13 | nJ9rsFBCDFuC+TP3ImUnsWBZq1xgRm9cjnOdFU5v8PmGm4swNwwa65TLQYJ6PKIH 14 | VCInLi1WyI5l6KCjZW+jHqsp4SxZK+JezMwLZ/ZYoELGIVZz4v6ycZyIoxMRnClH 15 | kux7+oYiGdgs+CH3t87Mz0IrRSutRlVyeX4urgUp/PndQGprlnE3h+XqJY3ZvObq 16 | zudae7FiMg== 17 | =A18o 18 | -----END PGP PUBLIC KEY BLOCK----- 19 | -------------------------------------------------------------------------------- /roca/tests/data/key02.pgp: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: Keybase OpenPGP v1.0.0 3 | Comment: https://keybase.io/crypto 4 | 5 | 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 53 | VQEQfj/R9tlv5XhcIg8gnwXIZaGb 54 | =BPAW 55 | -----END PGP PUBLIC KEY BLOCK----- 56 | -------------------------------------------------------------------------------- /roca/tests/data/key03.pgp: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | Version: Keybase OpenPGP v1.0.0 3 | Comment: https://keybase.io/crypto 4 | 5 | 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 179 | NEfplIPJrFzo7R+MHXb2Q8attg== 180 | =hlWd 181 | -----END PGP PRIVATE KEY BLOCK----- 182 | -------------------------------------------------------------------------------- /roca/tests/data/key04.pgp: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v2 3 | 4 | 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 72 | =Y+BI 73 | -----END PGP PUBLIC KEY BLOCK----- 74 | -------------------------------------------------------------------------------- /roca/tests/data/mod01.txt: -------------------------------------------------------------------------------- 1 | 0x944e13208a280c37efc31c3114485e590192adbb8e11c87cad60cdef0037ce99278330d3f471a2538fa667802ed2a3c44a8b7dea826e888d0aa341fd664f7fa7 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod02.txt: -------------------------------------------------------------------------------- 1 | 0xa23475d6b86e7deb1e1483747c0e1aaa1390f24dcbc0d05399d025c985861601d91ba7a1ce5dd9d9721fac7b65cbf73f9fa39399f6cbf33edc6e094f9eda639f 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod03.txt: -------------------------------------------------------------------------------- 1 | 0x9dcd7420e420c270a68060eb8b5aa7457a9f6648f86fafed2775cc1f79ae962f1e63f1b9e51c55f9ea6fdc7332e9b0058c9ff2eb1fc89855029cf6aafe21e689feb38ac9ffb12f1ce371bb5d0e9aae64161abce74b4244bf68f1edb4411f756a097c8929c14740245ea355f8332d1aa36bb50c7dcb4505da8de02e4e53d38f8df06097da7524df0df93db48eac8cc4af2d3a16257eb2b2adca6ba464e5974fa92550d31b1babf161e7692d00b86e3dd886ecab4abfbe1a8f8cc263ff4af55cdb45f4249a5193925f5e983d762f0b6800943eb0267b08f42ed311ce1f5c86eacbc8ce4d3cb7a2fee1ef5641423a1cf4114e5ecf50034a60a412523af9e6f4aa11 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod04.txt: -------------------------------------------------------------------------------- 1 | 0xaf659676197f1db06da7ac111a0a0ec175b8b7ef07192ffcd1f6b854c327785f1a7fd3db7da45cd4a31b2b2e5ce0d823a68e28c8adf1747316becce1fe5768064039ad2062260c8d1a34e0586dce9bc13090fcc9fb8b8b2c08ed3ae8d61b29818a669689a7d9c1dab053b557fb8d3f9c44faad3e18c55e739b00d3c9126558bc8e980cd2af377126ef5b1374136774b8bd370e3c1c149db395c8f99696b628b105946fea20106fe24d103ac062661a9248e8c3f628e00b308d33953aabe555132e218e74ef4b578716b4fe7cf340e8e604a319bf6e9951e1942601d34291032db54999d09e5d036dde3544ad35256bbb97d5e3fffd38166c21eae9199548853b 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod05.txt: -------------------------------------------------------------------------------- 1 | 0xba68fc5fbb606c4d428ab98b355d66214db4f7e475b5f7ffddc349c73f7d659965121ab16a4014e6c465c111d850205853d113f712127e4a230aa97f481eed6410f6c345f48e25fb81c42ec6815dc35cdd839e0de91fb2bec848d0910fa77e2c31f99ec9bcc7ed167812bd724318b0adf492d6b47c5ce050184e877ea64045f9501bf70569cffd102db7299f49e9c00222af9115d0aada967dfe759b7e4576e85c3a4ff594039231b90b448b90c3236083bcf819bc318d53a2018ec9b401f102ba9ee13dde592a42b032df136689d9c5619db55d519853a422d3100624d2d9e478218375f460c26930eda8cd0d947e58ffe9b7be2445356deaed988223e3ed1d 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod06.txt: -------------------------------------------------------------------------------- 1 | 0x8dcd7420e420c270a68060eb8b5aa7457a9f6648f86fafed2775cc1f79ae962f1e63f1b9e51c55f9ea6fdc7332e9b0058c9ff2eb1fc89855029cf6aafe21e689feb38ac9ffb12f1ce371bb5d0e9aae64161abce74b4244bf68f1edb4411f756a097c8929c14740245ea355f8332d1aa36bb50c7dcb4505da8de02e4e53d38f8df06097da7524df0df93db48eac8cc4af2d3a16257eb2b2adca6ba464e5974fa92550d31b1babf161e7692d00b86e3dd886ecab4abfbe1a8f8cc263ff4af55cdb45f4249a5193925f5e983d762f0b6800943eb0267b08f42ed311ce1f5c86eacbc8ce4d3cb7a2fee1ef5641423a1cf4114e5ecf50034a60a412523af9e6f4aa11 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod07.txt: -------------------------------------------------------------------------------- 1 | 0x9dcd7420e420c270a68060eb8b5aa7457a9f6648f86fafed2775cc1f79ae962f1e63f1b9e51c55f9ea6fdc7332e9b0058c9ff2eb1fc89855029cf6aafe21e689feb38ac9ffb12f1ce371bb5d0e9aae64161abce74b4244bf68f1edb4411f756a097c8929c14740245ea355f8332d1aa36bb50c7dcb4505da8de02e4e53d38f8df06097da7524df0df93db48eac8cc4af2d3a16257eb2b2adca6ba464e5974fa92550d31b1babf161e7692d00b86e3dd886ecab4abfbe1a8f8cc263ff4af55cdb45f4249a5193925f5e983d762f0b6800943eb0267b08f42ed311ce1f5c86eacbc8ce4d3cb7a2fee1ef5641423a1cf4114e5ecf50034a60a412523af9e6f4aa21 2 | -------------------------------------------------------------------------------- /roca/tests/data/mod08.txt: -------------------------------------------------------------------------------- 1 | 0x1db349e1f58f5d41f65aa5b0ffa6ae0cea80de6c170b2b4abf9c358d79494ca59cab008f5ee92759b91d497e2131701047f953935a163e91db5460c82566f4df0c05bca02a8576f7c4e916b274e2f307b56fc8a20ad452b1b54d3924726a3b2329da882c7b8e102379dfe7a8c6b01d62e7ff0fd58e1e9822f6e32b5b0486561fb -------------------------------------------------------------------------------- /roca/tests/data/mod09.txt: -------------------------------------------------------------------------------- 1 | 0xc39a928060db513091b604eadad719ed94e246de8fe15fb76efab5cfabbc3fc5486fe2ef2a5a5a8472f0c5be089edec071fb284df4467fa1fa6c8f0b013d3173fe636744666289bf67b5c998c87b59015d94bb9c20d30a70cfe275e42fd3c6f44d4c5252f9c6b6803f92afd928153bdb90ef08ede934c076f33543f9a1c362130929b9a1324c5ebee6831c251c48a7dee24d75ac538ce9a58d7ff09c12a5d772b6c33f4265a4e4830bd73ad221d24d4d3f092e24279c15fc1d10ca69561f4f071313c86b11ab36a5dd28df392f581834eecfdc9e183a466198e33cf01d273bd41d4b7e288cc66f24eba04676bd2090f6f73830336a317bc2d423b558b9d8f632e12af7b0e7fc9bdb69282e81fdf41a68cf4e85f01fc343dfe52a3ad01f67459d380165159e1209ef1ce4ecd26648e061faf0896551f7f1f92d6b6a19af0ab7f1f092d7e168bcec0ee478b8618489a8967a71e5e0a5c197662d8e9756f090a177109d4a34ae20a18c5629efea5ca66623960fc325f779cdae891a7ec051f00e837b8f2fb9fd19f6a35832ca0e5ce8375972c42a308f7d5ca0434022c131874087e826ed737994e46fbe44f5f1e908934f6033e6851ebd05447dddbaa51c0671849cafd3a503cdc150101914edf33b140c168c3dbea4791e74ea619ab1b31c90691de1d425b746b222a2b26ed54222cbcaf27a054f9f66f77be6466198c33e37dd -------------------------------------------------------------------------------- /roca/tests/data/pkcs701.p7s: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | 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 31 | AAAAAAA= 32 | -----END PKCS7----- 33 | -------------------------------------------------------------------------------- /roca/tests/data/privkey03.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6aPxfu2BsTUKK 3 | uYs1XWYhTbT35HW19//dw0nHP31lmWUSGrFqQBTmxGXBEdhQIFhT0RP3EhJ+SiMK 4 | qX9IHu1kEPbDRfSOJfuBxC7GgV3DXN2Dng3pH7K+yEjQkQ+nfiwx+Z7JvMftFngS 5 | vXJDGLCt9JLWtHxc4FAYTod+pkBF+VAb9wVpz/0QLbcpn0npwAIir5EV0Kraln3+ 6 | dZt+RXboXDpP9ZQDkjG5C0SLkMMjYIO8+Bm8MY1TogGOybQB8QK6nuE93lkqQrAy 7 | 3xNmidnFYZ21XVGYU6Qi0xAGJNLZ5Hghg3X0YMJpMO2ozQ2Uflj/6be+JEU1bert 8 | mIIj4+0dAgMBAAECggEASUje9y/uP4W8RFefn/JBJZSsMczSbiC6qc+N4NRYEypU 9 | LuLpnvDkhrjtnBLM7OtablFDPR4QwS+aSSXL0Wm/QFje+nQ1KfFKyIB9NzNCznxi 10 | 5cysgVsh5ZlN7xkog1u6Okd9oqgiHVZ5DD0If9JkG2+MjvZT8huJwZSV9jecCyXG 11 | EdNOeFtWFAIltpy4EyPeZB0bq1ZwWbbERRsYDCin5HOpZ7AF7qPB8rl7OsK8aN0K 12 | sqrG56fquAx3IkR4CRZXyXepeke/oRa4At+OVGa1aSfu1pQd66iwuPb0MFdBkN9T 13 | XnA5w/e1U4pSSk4TLY0E+mOE+eKKHiUs3sz6/Cw9oQKBgQDa5sERRX8Nd4iveHNe 14 | vIGuDuMcKhdwHJSCONQgDqBg/0+xoB/GySXWVEvdVh7281Tj3flvbxQwhIZ6BxmG 15 | ceU7eaw+u4O4Y31wUwwyGTBGJMezKuncfhionvzG0Tbe8FoE8qwyQSkKBC3Lv2Pd 16 | +VzshxxWxygMLmN4FpTtCD39KQKBgQDaAJD5XJDHszD4isjexKNkSHbXOQY02ULL 17 | wLV4x9v4qGZtDS3jQnRhIbmwwJkVK9kTHwRzxLSnVsf++Q0vfBdBcCYpybEkLl6K 18 | XkXoG9QmBm500w2yxGrZowTxlqpFG488llPy45TJmeekL5MzSTdvwbKMTSolkOtR 19 | rxAfru061QKBgQDSqwFj/d4ot8F0HhfqNMEZInqvwFKOMvM54DdGIs12jD/GUrRC 20 | O5OOkdHxUdC7GbO5GQfVlrF3gBNrnSmTmkli09ZRCWXbLuSqFULHdDZhS0RlygcH 21 | jNNudJQRQ4frRqVb3bCbLNqyKRNp4ufZ2QL1H5LGcSS5lGtmR1VMSYJfAQKBgCUt 22 | 3Yu5nrOAOwTYXoOnk5ohxarU/dr7hqnLNuffZgOVbMqCaeYNSl3aa5nabwEuVjot 23 | mwvHa2vyn+87OGV1y12Mq+WKkVxzUApXI6L/RAIsLKBd8kMfvn1ZkRlGsclRdu9F 24 | UBHngFOVnOzidD9V6E92AGZvLpXzjj6DFcPhiHH1AoGAH7uKnGvX5eykLyWhgyEE 25 | kleQI48ykVm4k9vUwy0O3kdX5TNXdlj9D4nfqg+/hbnRMlfMxzF6+KugP98pvNz2 26 | BnywtcDU/CAw1rjyVH8JBd9e/gq9QR2rqZHKIOUAHRVmeO/G7z0J8WgnPA8X9MyC 27 | R7DpDlcIpIJDWGxQSPpHH8g= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /roca/tests/data/privkey04.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAuifwOO3sz3yHUO4Kg67/ovYtMvzj6X8lf25wimtdIjoAkq/h 3 | xe44vaNkNxsCtTi+oVmw0ZGUwxpU4P5q64LlA6gxwF5cYj7L7G3FXjm0ICduZkp/ 4 | jicOGU/I5kYjnyjjPgt9aozfVVfC3bqJ3WYNXw4oxb11HemqvRvQcC+4PY13JMsv 5 | kz/sXUFFSjcILQuGONNuAoTtlfJ81SsmlSpQlHfsefYk/SDoZQSagP0b5dAU6Qik 6 | K0CjuP//Y7+ihtzZBKisWjia84H2U35U7cjbKYw2AzLlytjUr0fB/7aeuk4eRmqM 7 | VQwCYDtvUpdQlJtM3YrbRw0wUvMEtxXgc0CGVQIDAQABAoIBAE8E4wCXZj1Q4/Oj 8 | h80L/LGFA/yvwmL8OMOV8qmfF2KO8m1RwWmOTIGbGNAXZBfe5XSCKP9cZ6RFyh/T 9 | fzlHT8U0oSHCLD773v4WPoBXyDEZ68/58Zk61M5DwsiUn0xRQ9yzbeJLuAV3Al2y 10 | cBEN38Eotn/xgwRWisZJRzpH11IwwCK7WYOZj7b1HRzFWxn+F3n4Pa8LVwjc+8ol 11 | sSMkSSfNQCINkY2g+M44lHIliL+gU6oFftvgySn+JwS/b/XNwS2qq0AmvVZiCrjJ 12 | KdyrvZpayZKflCs0kowXcYC1LxDFdby5wftXwxOdujXBwPPuU+OWMJakt1r7qCsG 13 | lBRohqECgYEA4ZopS6kP+hhZG+DHXELtMPKmdqUpcJu6JJ4J8bRcA0+M+lW79Lg1 14 | UfcwiLDEhjkTVHol8ofxv3GGmzDXS5i1kOcebvHbomJfDYjqnyjITSBVicV30bDb 15 | J91JcpLCOyC/lDWAtdfOAjiP7FG2MJbsJeuJ6ehZB/lbtnzZBPFdRE0CgYEA0z0i 16 | jiGJ59Q1FWEhowfzYaaU/iTo9IE6PlP0yx39BPcw5xYyS+uiYQRPjxQDz2imApEz 17 | GItW3qiaHWWeFE/zCkftag8gzTH2VQydM23XWnlxFwfScfeoy4EFpfQvm7T5leOt 18 | 6jV8E2TBxhfgWsmiFiIoqJcTEhLoYHguWCmV7ikCgYBv1eQyxZRN/lPuJfeuHN9I 19 | LIGovydT017rWAzF7QrP+VeBoY6TOd30y/0bRcWmZBTZA9Rb7lhA9x4rQIL5zrHz 20 | Kx9DEFI4pVtJcl4cmDLzSNwed+113Z5GnMQUL/xpI0rVRdCQVHKjVzbBh6AxZh+a 21 | jLDJa4z3Dv4P8MQEV0JM4QKBgFdM49QTvlfGVu3wB6PGLGrGGrtx66jpI3YQ4pFz 22 | YldEBbzranv/9UrdKjg6kFyOplVXRuO06oD480CE97DzXcF4+oCjoe3L4T9DCFDx 23 | 61ej6v4WqH2bWfJI71mnsmlKgP/WadDqq82kDYVd5sX+oPM+yMjsLuCAWfNMl/Kt 24 | tR4JAoGAboQvB3Kf6iAn47U9o06/mdZQE5JiHRu0lOwMQHXT+2UUU8W+YbYwAcOP 25 | VS7PV8RiLDdFm9pccx2SGGMx33ohFQRKMtPu9jYOQ3+asDTWq8LCukbfoCSOVHRV 26 | /MACQBIbxlg4veBSVVTezB0/bk1lZ7A2Nr96IYs2zg/0H9FhJek= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /roca/tests/data/privkey05.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEBqcWmh9uPsqMPgt/43aFUwpHvJ7BLJeGFuKsgxMal9gSFn+We3lSgr3wOcoiA 3 | CtcZO7cxpb8eEXDXocZpMNQeo+sh3t97ivXUKpHDT3IEW5bv1XyxgjAd3MJpZ+VQjpq4iZhtV+g3 4 | 3+cPmfw3xJj0PBfDubBkz3BEQ9egM+A6ghaXqr8w+ynufTOzdnllqJBY47OXVL06HAB9KKI7DPp5 5 | +dcRrG5MS093sGNLHs1oWRrwPH1TtipfLZEWPU70+PJoMa5GHthexfeIxRI//BLIdCD9z+odG0pS 6 | w1IYJPs5mDSHObYqmG2OdrIom8Lri8BtE4FHhRuDoHGD0hBVn5BjIQIDAQABAoIBADRqRKoM+Y/A 7 | SzSNZhHCvrCFTq/QIDA1p0SorIwM+Hlk7R5WIky0lmOEHjMzYq/y8wRK9S22bXkwLxuXVXPRaUQ8 8 | uhq54Hem88uZzjWFj8vGtRfPFfs0+TgoJtnHfTdRDM7DmRJtYBOMWntWdXcabVbIhivh70cqRv0j 9 | GnHIdOt2kgzOOgLL2qPD46lhsvmRsJ2qa8eTBOGVkTCraL/AVTL5K7NSnjO07YzINu4b63k7nrvH 10 | 24DXPVPd0trFo8P+GL0u9onbDPeXvjAjcISpK37nzYXOfvPqcQ+nG4rvAZxuIEdL7kFDSRcdegbL 11 | JAuyYDsBFIVINUNKWGI9wwOeDsECgYEBFc0LykL2bABN6n6F3xQkg8X7PFFPdItKj6ipx++x/GHf 12 | VjCUq18jHtNmBdjVmq9r3PPElljFHVesmnczQ3LnJFLPTgQTu06NILf69Hagga2RY8VRQXhWkTCs 13 | POo+tlrskpj5knSpyHEtjDMqPsuAQDYdjyXUYFDMdiqptuUw3IUCgYEBiFvaBdiiRC6ByhINyX2N 14 | 56rClxxX7UW/3dFO1oUygMnmQmKlamZMi5PTpktVzT7YU2X1TcdqI2ceBPTtKOeWWPZcYEfm0VP7 15 | a7xRRvLtARfwjlBGt0KzoUS2gjFj54ZlYPkxsXxvCt0TXG6GTt2/GUZwTOwCpYxTU5GhF0JmDO0C 16 | gYBvYTqs6ZQrUSPsNLYv7r+FJcfJM0CMFZzbZGBODUhrTEJjKC8zVn2aSl/JUFq0Hkk82W+kaAr/ 17 | 7LsDY29mR5ds9NaxDOlsW94xlBJGGZnY5GI6gk5F1Z2/sKgd9D0Yl/PqAvNtSOEZwGbmo0z4yFav 18 | aAMJ9bzksQCJECnjbpjckQKBgQDjchOOBgnyql8Uq6iQYBct78BD5utVoXRa+uXP14DyRZgUbajk 19 | aA8Bbp1nrg48H605k0ZDY1E02qb9Sppcdvuh8JaWPbghKqT5zD31egdH9f++D+0eeS02VGXUTRtT 20 | 8IW+VFapQ6//RS7fCzcSattHpJyfCsVyAEmhjS8sE5Ki9QKBgGs75w9+SZiZiOlaBhuCf5KK4gwv 21 | lRDUSMycvT/aWh1LZO71VT1nwths7Lx10xdWa76mJhvjbWMUZKbOgEPECoqPV20O7U52Jmwh/a0l 22 | 4+8optwPUa8vdvy7KZVkVth/xCL4mJOF3UGN0qUlwm3d9H6VXCUYcxf0idonxCD1ndzQ 23 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /roca/tests/data/pubkey01.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuifwOO3sz3yHUO4Kg67/ 3 | ovYtMvzj6X8lf25wimtdIjoAkq/hxe44vaNkNxsCtTi+oVmw0ZGUwxpU4P5q64Ll 4 | A6gxwF5cYj7L7G3FXjm0ICduZkp/jicOGU/I5kYjnyjjPgt9aozfVVfC3bqJ3WYN 5 | Xw4oxb11HemqvRvQcC+4PY13JMsvkz/sXUFFSjcILQuGONNuAoTtlfJ81SsmlSpQ 6 | lHfsefYk/SDoZQSagP0b5dAU6QikK0CjuP//Y7+ihtzZBKisWjia84H2U35U7cjb 7 | KYw2AzLlytjUr0fB/7aeuk4eRmqMVQwCYDtvUpdQlJtM3YrbRw0wUvMEtxXgc0CG 8 | VQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /roca/tests/data/pubkey02.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PUBLIC KEY----- 2 | MIIBCgKCAQEAuifwOO3sz3yHUO4Kg67/ovYtMvzj6X8lf25wimtdIjoAkq/hxe44 3 | vaNkNxsCtTi+oVmw0ZGUwxpU4P5q64LlA6gxwF5cYj7L7G3FXjm0ICduZkp/jicO 4 | GU/I5kYjnyjjPgt9aozfVVfC3bqJ3WYNXw4oxb11HemqvRvQcC+4PY13JMsvkz/s 5 | XUFFSjcILQuGONNuAoTtlfJ81SsmlSpQlHfsefYk/SDoZQSagP0b5dAU6QikK0Cj 6 | uP//Y7+ihtzZBKisWjia84H2U35U7cjbKYw2AzLlytjUr0fB/7aeuk4eRmqMVQwC 7 | YDtvUpdQlJtM3YrbRw0wUvMEtxXgc0CGVQIDAQAB 8 | -----END RSA PUBLIC KEY----- 9 | -------------------------------------------------------------------------------- /roca/tests/data/pubkey03.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEBqcWmh9uPsqMPgt/43aFU 3 | wpHvJ7BLJeGFuKsgxMal9gSFn+We3lSgr3wOcoiACtcZO7cxpb8eEXDXocZpMNQe 4 | o+sh3t97ivXUKpHDT3IEW5bv1XyxgjAd3MJpZ+VQjpq4iZhtV+g33+cPmfw3xJj0 5 | PBfDubBkz3BEQ9egM+A6ghaXqr8w+ynufTOzdnllqJBY47OXVL06HAB9KKI7DPp5 6 | +dcRrG5MS093sGNLHs1oWRrwPH1TtipfLZEWPU70+PJoMa5GHthexfeIxRI//BLI 7 | dCD9z+odG0pSw1IYJPs5mDSHObYqmG2OdrIom8Lri8BtE4FHhRuDoHGD0hBVn5Bj 8 | IQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /roca/tests/data/ssh01.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3j6eenO3McysG1UDLCAgk1w2ifVN1W1FPqezvnlYcQj7tQ+7YMZGBQYHZTZnNS0ZQbE0qxrL0phStdQZbVi2teyfKsgxr53oBOrDwtb1Z1RUmrQqlZaLXUiWvio2wxE0ZTdXXMik1oDTYXm2OfFm7Kdrhw4MFYPFWw8eAfnWW80QrXAB/L9HjH+An7j0788QojuiCK5I/EeRHKujfKs1r/CWjwq+uEUIiKFdkFCPVExxFdJwazPbz1HNn35lfEEtu7UUYphy/yUmhFJnKvCDcmMfO7u4/BhH4STSFkTReE6P+pmQ9RU98yrhp0qMn5SLma7evNQi4gFZNe7GxCvr1 root@local 2 | -------------------------------------------------------------------------------- /roca/tests/data/ssh02.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+hHcbAj+4kRacNsmrwJ3fBfO/nJcghYP9GHAOgzf28JKOIL8GZVZT7iLvRtCii8mQxvN7qO5LLBSYVPjlONt6mxDunzzCGxdZYQ8ZplR5pYrpKRC2FTXbyLr0mJFekCzSF+iSkf/vxVGXaDdoK/VbZHTSlnMxuBDGx/5qx6mWuRh3Uq9496+bYGIIIxKT67ZNFJ6lh8CKmepnfNRtp3zFmRsNV8OFn28jSIDA0Aa0/YvvuYVrCv8A6iDUoCyjGx4GgfnFS17o16gNDIfOT58kcq9IruMSamzHOR8gphoyrYPkSOO+OKa15Y3VllIYuLNrRwRR+ffMLUvkTVU1A+Nv root@local 2 | -------------------------------------------------------------------------------- /roca/tests/data/ssh03.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6H6d+gl1D69uC9CeV+0pB9bkkRiKlxZq3q1WLwVR51gAe1szd+cDjqrZyOCJERYRozbLsE3A+7G1eErX5vh7V3CI3NYPAlzguS2lSdzNXVtQbxnqSwCQ8LGjfSxJ9OZ/2lMopfLbFwMIiPxx2Fk21frp1bvMWfH8HsetE4yyiLvZzYzxQt5NiLfqdur2o02wwIW/vubd7KgHxuUXgwbMEZcCFO1Q5dX+oRQgdA8LvZAAS+GS2ZKnm1FIBD6764UsxxaEoHb5BwQEnbQsqpVm/Rps1SOwld/EReU2tQaB1AZDYpwImNVoM3cmuLS6wSCNN/z1cVNSLDFYYZjPCr/+zIe4JuL7d4a0Q8ZYDnRa1f2IyMM3QBk20KRYMVBM7zBx1PL+XSTJvEPAly+Rs1T3fn1iMRG/mUwf+2oPcMu6xJ/ic0YXwo2G81ktIOIc36JZHK24XjhWr/Stesh66CPavEX3ZylesfB7dWMGHgo96pS1jIiPn7OgxLXA5qXalUI74TJR0P0tcFGpE6x2OA/S1tRWmrSrmmvaIIZcq0dH+7FJw4tCXdBfFgbHovIj5qxUVTnyqZ+M14/iUJpTxVJIZECjeu9tzNoy2yvoIPbMcpiQ6Id3aOGLpgFO/5JIqkLLoACyTFIcRb5uSuFhxN6kLrkA1+oqrWzuh47Lu7aYA6w== root@local -------------------------------------------------------------------------------- /roca/tests/data/ssh04.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQC4IjmTt5XvRfnauliaaKERjiGOHD89RfytsJ/otcFpYYUA96lKg/PzYc8BS+SKcUe3iy1JhA6/rphvGPLGKMJfEXH2edoaUcH62kIxB6uAL7f5/a0lchD5IP1TfTH4PUJFUlt1sEPlTrN6WQx/Brogsbk8L+XmavW4lZl0D5K4wrIkXLAbH41qdDrEzVWwVwp1qg/fCJ8ude0hc4sc6Z8ezrn5am7Wd/gaYaHRCwGfPay8zgzq/20qRtpbsgtE9kRpURY0ddbS3LWoqeqNiTAnM4POL/IYHfinhS7TKVh+wNedlmWkSHJjaVdceK9m4dPCIXfwfqasOYccP1rtHlRepJPS0neGawLy4uStXZWK07Nq3oRSV7Xi5C/1Kg/0CQttP4W7fBrXnXfxliu1bwc5w5YTmZEaRmLv4RLXW/+LaREuWFHX20fFEE8ORwGM0G+YmeokN9+I2EGCEn9wbPxx74uSfW9HgfXE/OwXzjICJlaI61VsHQi4H7t2AAW4FBiC5XCjXsBJPppkwcXbs2bqiT4IRZPuMTsixVwA01uKcQ4DBQTHpcp6+R7xIQttV5hZIn50WTnzgGSsZP9/RqSWYhmNxzPJJeMCUdIX7YTBkfMpnalicN+NFpN7Plq8hzgrUk6xMeITMsLPywBigwqozy4bAWjEBy8S5c7bK6Z/MSZy0DdjOW1KurYXj6LNZ/6Qn6+Hyeugw/tAs+lUZX6YBOomNJGVgLjI4YZAlRL7fi+l7FexM36aV4sB/BnH76R1K8TMIL1TNIH45CrasdS22ACdKq94tqJXyVcMXC/Rm2S3NvUn3drz3iS4INhPcJCs3Hsi+/HOv0z5LiJ0YOLUbyxGGuec/qIezX50XkiOuZsOaONBHmoWAtmDjDgDgxokjb8AjeDu848/CpfxzlI2zJIjAsSnZdggForiooj8QLuI/YjHZo40eKJ+MZ22jdFsPohKPhcYL0Y5dL0jjTI/UEEp8ZD14QM5+C9YI78gKod3YtHzovHMksy6zjCnsxLpQk8oFaac6EiX9gAHZ7mIBYtcslyUMnYhP3RzipsGbug14KZujtvj8z0k7d5J5pIyQH7oEnxckp9+whrwfkGimfmE1XRQY6b5a+zPMb/gt/MnxKeBbYI10/Oab4IwctmsfatsXfJL2+yHltp7EBCOv229co4Er6J8Pw3lRJeRtmaFlVG3exy0l4eR+2DH8CRvYDfuRSnURCwQj+9LzLKlvfWVInr12iHt395yvF8riQ88Xe3otbTnNdbDe6t3RNlMcjMLJ8ohrzSFqocF2MmYkkbE6wB3KeB7wYxK96x/dsw6gXakvFhHpvGGZ4PwxT7hIQAME991Nw/AcNFC9x6R root@local -------------------------------------------------------------------------------- /roca/tests/data/ssh05.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDb19j3SxjX7aaUJ9Y7zd4yWbiEORGpzb1bFzgK1efX3L59NNUMTJuGtCvkEeBheCC9rh0qb1Bn1qLr3lOBM8eGaHmdFxCReltKLlwAZ2n9j4lXIbjA7fQ1405678rn5306rZQKn5/vS71KdpbDnS+urQ3X2ID0cjeANrudctCl0w== root@local 2 | -------------------------------------------------------------------------------- /roca/tests/data/ssh06.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQGpxaaH24+yow+C3/jdoVTCke8nsEsl4YW4qyDExqX2BIWf5Z7eVKCvfA5yiIAK1xk7tzGlvx4RcNehxmkw1B6j6yHe33uK9dQqkcNPcgRblu/VfLGCMB3cwmln5VCOmriJmG1X6Dff5w+Z/DfEmPQ8F8O5sGTPcERD16Az4DqCFpeqvzD7Ke59M7N2eWWokFjjs5dUvTocAH0oojsM+nn51xGsbkxLT3ewY0sezWhZGvA8fVO2Kl8tkRY9TvT48mgxrkYe2F7F94jFEj/8Esh0IP3P6h0bSlLDUhgk+zmYNIc5tiqYbY52siibwuuLwG0TgUeFG4OgcYPSEFWfkGMh -------------------------------------------------------------------------------- /roca/tests/test_fingerprint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from roca.detect import RocaFingerprinter, flatten, drop_none, AutoJSONEncoder 5 | import random 6 | import base64 7 | import unittest 8 | import pkg_resources 9 | 10 | 11 | __author__ = 'dusanklinec' 12 | 13 | 14 | class FprintTest(unittest.TestCase): 15 | """Simple Fingerprint tests""" 16 | 17 | def __init__(self, *args, **kwargs): 18 | super(FprintTest, self).__init__(*args, **kwargs) 19 | self.inputs = [] 20 | self.positive_samples = [ 21 | 'mod01.txt', 'mod02.txt', 'mod03.txt', 'mod08.txt', 'mod09.txt', 'key04.pgp', 22 | 'cert04.pem', 'cert05.pem', 'ssh06.pub', 'pubkey03.pem', 'privkey05.pem', 'csr05.pem' 23 | ] 24 | 25 | def setUp(self): 26 | """ 27 | Loads testing certs 28 | :return: 29 | """ 30 | fls = pkg_resources.resource_listdir(__name__, 'data') 31 | fls = [x for x in fls if 32 | x.endswith('.pem') or 33 | x.endswith('.txt') or 34 | x.endswith('.pub') or 35 | x.endswith('.pgp') or 36 | x.endswith('.p7s')] 37 | 38 | for fname in fls: 39 | self.inputs.append((fname, self._get_res(fname))) 40 | 41 | def tearDown(self): 42 | """ 43 | Cleanup 44 | :return: 45 | """ 46 | 47 | def _get_res(self, name): 48 | """ 49 | Loads resource 50 | :param name: 51 | :return: 52 | """ 53 | resource_package = __name__ 54 | resource_path = '/'.join(('data', name)) 55 | return pkg_resources.resource_string(resource_package, resource_path) 56 | 57 | def test_primorial(self): 58 | """ 59 | Simple primorial test 60 | :return: 61 | """ 62 | fp = RocaFingerprinter() 63 | m, phi = fp.dlog_fprinter.primorial(167) 64 | 65 | self.assertEqual(m, 962947420735983927056946215901134429196419130606213075415963491270) 66 | self.assertEqual(phi, 103869096713434131141462689130396531045414801386011361280000000000) 67 | 68 | def fprint_subtest(self, fprinter): 69 | """ 70 | Basic fingerprinter test 71 | :param fprinter: 72 | :return: 73 | """ 74 | self.assertGreaterEqual(len(self.inputs), 19, 'Some inputs are missing') 75 | 76 | for fname, data in self.inputs: 77 | ret = drop_none(flatten(fprinter.process_file(data, fname))) 78 | self.assertGreaterEqual(len(ret), 1, 'At least one result expected') 79 | 80 | if fname.endswith('.txt'): 81 | self.assertEqual(len(ret), 1, 'Hex mod input epxected result count is 1, not %s' % len(ret)) 82 | self.assertEqual('mod-hex', ret[0].type, 'File type detection failed') 83 | 84 | for sub in ret: 85 | self.assertIsNone(sub.error, 'Unexpected error with file %s : %s' % (fname, sub.error)) 86 | self.assertEqual(fname, sub.fname, 'Filename mismatch') 87 | self.assertIsNotNone(sub.n, 'Modulus is empty') 88 | self.assertGreaterEqual(len(sub.n), 10, 'Modulus is too short') 89 | 90 | if fname in self.positive_samples: 91 | self.assertTrue(sub.marked, 'False negative detection on fingerprinted modulus: %s' % fname) 92 | else: 93 | self.assertFalse(sub.marked, 'False positive detection on non-fingerprinted modulus %s' % fname) 94 | 95 | def test_fprint_moduli(self): 96 | """ 97 | Test fingerprints 98 | :return: 99 | """ 100 | fprinter = RocaFingerprinter() 101 | fprinter.switch_fingerprint_method(True) 102 | self.assertEqual(fprinter.has_fingerprint, fprinter.has_fingerprint_moduli) 103 | self.fprint_subtest(fprinter) 104 | 105 | def test_fprint_dlog(self): 106 | """ 107 | Test fingerprints - dlog method 108 | :return: 109 | """ 110 | fprinter = RocaFingerprinter() 111 | fprinter.switch_fingerprint_method(False) 112 | self.assertEqual(fprinter.has_fingerprint, fprinter.has_fingerprint_dlog) 113 | self.fprint_subtest(fprinter) 114 | 115 | def test_fake_mods(self): 116 | """ 117 | Fake modulus - positive by old method, negative by dlog method 118 | :return: 119 | """ 120 | fprinter_moduli = RocaFingerprinter() 121 | fprinter_dlog = RocaFingerprinter() 122 | 123 | fprinter_moduli.switch_fingerprint_method(True) 124 | fprinter_dlog.switch_fingerprint_method(False) 125 | 126 | fake_mods = [ 127 | 72414128973967872688332736535017614208620368242015797102796086827882754006260204061799547004983731426777596445658889886861027045033760014948048067467947960318205453778973975867532910489227970143091184725760290172935872752222320334100097730483600892686353719855397381316384244147860260153198611313067061116317, 128 | 138345973265163614694352477004469191286459111070564516299381495188624946671065451924677704628892476011773201612117597303315530745738015579159106259811204610647347662150159814774894784524366138220226431598991937057442020694466365850170057813627445465881816985939192840583168136876337135351417253708364245923133, 129 | 60828061238058485055546209519792949600733446820687109700021551748017165461584398468231416589035982737511901116540885252089750919566836183252857475484338913841291095222387779067564971983760619814471104385614285949705697261702933899278537712383849527252611987939947273446907728942539157463443880598175266703887, 130 | 89537174583470428126368559122733093792771267470145172261204043737374783295746735770977275377863486022234354929860172558745137290320682223277990399066906105916137221611218740752167456538787695936927885201842306174187254104613077236292789788064415254034851612157179926431228950237250716554645306298514263908811, 131 | 106012262050781200106909327696665817682864459575156325008493049499578896429709338134583432679663875229243561266610327817994340312544432988100581205278527652776797129380732775139010348828146830433114148695545628251479471386655045041620711043660854155633665634387716779804052874716264079804365681200926368878541, 132 | 98047438997515280074792701497622826536971900999197902469801657195397532486098993597664864573258672004307512856021908739030566926963054863998516343042890871462635847748128662678669505745602483507470396856696130729682439145004123654578085621747843892166517045343501758093052905936670793527374604000360320123643, 133 | 48107859163997579694864893504886560528514286462252528518753580114887818750322128218646068205130016527298578884960923054955072510745551784643455001265439464144751949280776912819502350769576504234639747590328838926697492194274517633949025303073843002256820311079628923909309353364288514091223779706534988233353, 134 | 95193465162148191217844814565725476305218769551232988131351154011966624770961320486395571928859840281251870272532908194236488486001519948267693888769624645565515338392649711205572847326959985300653388437498035890136677149175252923464027081780534454300684502200232492689914254307896522690747556123142494605401, 135 | 168489441676498254188251084620317730514271361861339944910962927459542807975808481932360687459768443345754506784027235857753721317508760585508375364503886428020727192193297507131796130690248490645731180656182669717283026722056291513041731668953221561576833358510127447655469989100576330621949276177058083620677, 136 | 127791896675045040395064468573109425010219774613093240038326469106056928318395608480165676859635771156871330139281596703754063207095456065784747740162970184880536590144647003492364759169502217854388165521994268206346785403332622195574508223431200064645819724980706217912475871209058082764033948277691466089251 137 | ] 138 | 139 | for idx, mod in enumerate(fake_mods): 140 | r1 = fprinter_moduli.process_mod_line_num(mod, str(idx), 0, num_type='dec') 141 | r2 = fprinter_dlog.process_mod_line_num(mod, str(idx), 0, num_type='dec') 142 | self.assertTrue(r1.marked, 'Moduli detector should have detect %d mod' % idx) 143 | self.assertFalse(r2.marked, 'Dlog detector should have detect %d mod' % idx) 144 | 145 | 146 | if __name__ == "__main__": 147 | unittest.main() # pragma: no cover 148 | 149 | 150 | -------------------------------------------------------------------------------- /roca/tests/test_tls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from roca.detect import RocaFingerprinter, flatten, drop_none, AutoJSONEncoder 5 | import random 6 | import base64 7 | import unittest 8 | import pkg_resources 9 | 10 | from roca.detect_tls import RocaTLSFingerprinter 11 | 12 | __author__ = 'dusanklinec' 13 | 14 | 15 | class TlsTest(unittest.TestCase): 16 | """TLS fingerprint test""" 17 | 18 | def __init__(self, *args, **kwargs): 19 | super(TlsTest, self).__init__(*args, **kwargs) 20 | 21 | def setUp(self): 22 | """ 23 | Loads testing certs 24 | :return: 25 | """ 26 | 27 | def tearDown(self): 28 | """ 29 | Cleanup 30 | :return: 31 | """ 32 | 33 | def test_net(self): 34 | """ 35 | Test university web - internet access 36 | TODO: implement in a way internet access is not needed 37 | :return: 38 | """ 39 | tls_detect = RocaTLSFingerprinter() 40 | res = tls_detect.process_tls('google.com', 'google.com') 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() # pragma: no cover 45 | 46 | 47 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from setuptools import setup 4 | from setuptools import find_packages 5 | 6 | version = '1.2.12' 7 | 8 | # Please update tox.ini when modifying dependency version requirements 9 | install_requires = [ 10 | 'cryptography>=1.2.3', 11 | 'setuptools>=1.0', 12 | 'six', 13 | 'future', 14 | 'coloredlogs', 15 | 'pgpdump', 16 | 'python-dateutil', 17 | ] 18 | 19 | dev_extras = [ 20 | 'nose', 21 | 'pep8', 22 | 'tox', 23 | ] 24 | 25 | docs_extras = [ 26 | 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 27 | 'sphinx_rtd_theme', 28 | 'sphinxcontrib-programoutput', 29 | ] 30 | 31 | apk_jks_extras = [ 32 | 'apk_parse_ph4>=0.1.7', 33 | 'pyjks', 34 | ] 35 | 36 | try: 37 | import pypandoc 38 | long_description = pypandoc.convert('README.md', 'rst') 39 | long_description = long_description.replace("\r", '') 40 | 41 | except(IOError, ImportError): 42 | import io 43 | with io.open('README.md', encoding="utf-8") as f: 44 | long_description = f.read() 45 | 46 | setup( 47 | name='roca-detect', 48 | version=version, 49 | description='ROCA key detector / fingerprinter tool', 50 | long_description=long_description, 51 | url='https://github.com/crocs-muni/roca', 52 | author='Dusan Klinec', 53 | author_email='dusan.klinec@gmail.com', 54 | license='MIT', 55 | classifiers=[ 56 | 'Development Status :: 3 - Alpha', 57 | 'Intended Audience :: Developers', 58 | 'Programming Language :: Python', 59 | 'Programming Language :: Python :: 2', 60 | 'Programming Language :: Python :: 2.7', 61 | 'Programming Language :: Python :: 3', 62 | 'Programming Language :: Python :: 3.4', 63 | 'Programming Language :: Python :: 3.5', 64 | 'Programming Language :: Python :: 3.6', 65 | 'Topic :: Security', 66 | ], 67 | 68 | packages=find_packages(), 69 | include_package_data=True, 70 | python_requires='>=2.7.10,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', 71 | install_requires=install_requires, 72 | extras_require={ 73 | 'dev': dev_extras, 74 | 'docs': docs_extras, 75 | 'apk-jks': apk_jks_extras, 76 | }, 77 | 78 | entry_points={ 79 | 'console_scripts': [ 80 | 'roca-detect = roca.detect:main', 81 | 'roca-detect-tls = roca.detect_tls:main', 82 | ], 83 | } 84 | ) 85 | -------------------------------------------------------------------------------- /test-travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python -m unittest discover $* 3 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python -m unittest discover $* 3 | --------------------------------------------------------------------------------