├── .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 | [](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 | 
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 |
--------------------------------------------------------------------------------