-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crypto material plugin: converted to new base class
- Loading branch information
Showing
4 changed files
with
207 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 56 additions & 38 deletions
94
src/plugins/analysis/crypto_material/internal/key_parser.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,71 +1,89 @@ | ||
from __future__ import annotations | ||
|
||
import logging | ||
from struct import unpack | ||
from typing import TYPE_CHECKING | ||
|
||
import OpenSSL | ||
import OpenSSL.crypto as ssl | ||
from cryptography.hazmat.primitives import serialization | ||
from cryptography.hazmat.primitives.serialization import pkcs12 | ||
|
||
from helperFunctions.data_conversion import make_unicode_string | ||
|
||
TLV_KNOWN_STARTS = [0x30] | ||
if TYPE_CHECKING: | ||
import io | ||
|
||
TLV_KNOWN_STARTS = {0x30} | ||
LENGTH_TO_FORMAT = { | ||
1: '>b', | ||
2: '>h', | ||
4: '>i', | ||
} | ||
DER_LIMIT = 0x80 | ||
DER_HEADER_SIZE = 2 | ||
|
||
def _get_start_and_size_of_der_field(binary=None, offset=None): | ||
if binary[offset + 1] > 127: # noqa: PLR2004 | ||
length_of_length = binary[offset + 1] ^ 0x80 | ||
logging.debug(f'[LOG] - Length {length_of_length}') | ||
form_string = _determine_format_string(length_of_length) | ||
return ( | ||
offset + 2 + length_of_length, | ||
unpack(form_string, binary[(offset + 2) : (offset + 2 + length_of_length)])[0], | ||
) | ||
return offset + 2, binary[offset + 1] | ||
|
||
def _read_der_key(file_handle: io.FileIO, offset: int) -> bytes | None: | ||
file_handle.seek(offset + 1) | ||
value = int.from_bytes(file_handle.read(1), byteorder='little') | ||
# The field at offset + 1 is the length field. If the value is > 0x80, the field contains only the size (+0x80) | ||
# and the actual length is in the next field | ||
if value >= DER_LIMIT: | ||
value ^= DER_LIMIT | ||
logging.debug(f'[LOG] - Length {value}') | ||
length = unpack(_determine_format_string(value), file_handle.read(value))[0] + value | ||
else: | ||
length = value | ||
# we need to reset the file pointer because we need the entire key (including the length field) | ||
file_handle.seek(offset) | ||
return file_handle.read(length + DER_HEADER_SIZE) | ||
|
||
def _determine_format_string(length=None): | ||
if length not in [1, 2, 4]: | ||
logging.warning('Unregular format in DER encoding') | ||
return None | ||
formats = ['>b', '>h', None, '>i'] | ||
return formats[length - 1] | ||
|
||
def _determine_format_string(length: int | None) -> str | None: | ||
if length not in LENGTH_TO_FORMAT: | ||
raise ValueError('Irregular format in DER encoding') | ||
return LENGTH_TO_FORMAT[length] | ||
|
||
|
||
def read_asn1_key(binary: bytes, offset: int): | ||
if binary[offset] not in TLV_KNOWN_STARTS: | ||
def read_asn1_key(file_handle: io.FileIO, offset: int): | ||
file_handle.seek(offset) | ||
start = int.from_bytes(file_handle.read(1), byteorder='little') | ||
if start not in TLV_KNOWN_STARTS: | ||
return None | ||
start, size = _get_start_and_size_of_der_field(binary=binary, offset=offset) | ||
try: | ||
key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_ASN1, binary[offset : start + size]) | ||
return make_unicode_string(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_TEXT, key)) | ||
except OpenSSL.crypto.Error: | ||
file_handle.seek(offset) | ||
key_data = _read_der_key(file_handle=file_handle, offset=offset) | ||
key = ssl.load_privatekey(ssl.FILETYPE_ASN1, key_data) | ||
return make_unicode_string(ssl.dump_privatekey(ssl.FILETYPE_TEXT, key)) | ||
except ssl.Error: | ||
logging.debug('Found PKCS#8 key signature, but looks false positive') | ||
return None | ||
except TypeError: | ||
logging.warning('Found PKCS#8 key signature but openssl binding could not decode it.') | ||
return None | ||
|
||
|
||
def read_pkcs_cert(binary: bytes, offset: int): | ||
if binary[offset] not in TLV_KNOWN_STARTS: | ||
def read_pkcs_cert(file_handle: io.FileIO, offset: int): | ||
file_handle.seek(offset) | ||
value = int.from_bytes(file_handle.read(1), byteorder='little') | ||
if value not in TLV_KNOWN_STARTS: | ||
return None | ||
start, size = _get_start_and_size_of_der_field(binary=binary, offset=offset) | ||
try: | ||
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates( | ||
binary[offset : start + size], None | ||
) | ||
x509_cert = OpenSSL.crypto.load_certificate( | ||
OpenSSL.crypto.FILETYPE_PEM, certificate.public_bytes(serialization.Encoding.PEM) | ||
) | ||
return make_unicode_string(OpenSSL.crypto.dump_certificate(type=OpenSSL.crypto.FILETYPE_TEXT, cert=x509_cert)) | ||
key_data = _read_der_key(file_handle=file_handle, offset=offset) | ||
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(key_data, None) | ||
x509_cert = ssl.load_certificate(ssl.FILETYPE_PEM, certificate.public_bytes(serialization.Encoding.PEM)) | ||
return make_unicode_string(ssl.dump_certificate(type=ssl.FILETYPE_TEXT, cert=x509_cert)) | ||
except ValueError: | ||
logging.debug('Found PKCS#12 certificate, but passphrase is missing or false positive.') | ||
return None | ||
|
||
|
||
def read_ssl_cert(binary: bytes, start: int, end: int): | ||
def read_ssl_cert(file_handle: io.FileIO, start: int, end: int): | ||
try: | ||
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, binary[start : end + 25]) | ||
return make_unicode_string(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_TEXT, cert)) | ||
except OpenSSL.crypto.Error: | ||
file_handle.seek(start) | ||
key_data = file_handle.read(end - start + 25) | ||
cert = ssl.load_certificate(ssl.FILETYPE_PEM, key_data) | ||
return make_unicode_string(ssl.dump_certificate(ssl.FILETYPE_TEXT, cert)) | ||
except ssl.Error: | ||
logging.debug('Found SSL certificate signature, but looks false positive') | ||
return None |
Oops, something went wrong.