Merge pull request #711 from dod-ccpo/pdf-signature-verification
Verify PDF signatures
This commit is contained in:
commit
27314b8120
2
Pipfile
2
Pipfile
@ -23,6 +23,7 @@ lockfile = "*"
|
||||
defusedxml = "*"
|
||||
"flask-rq2" = "*"
|
||||
simplejson = "*"
|
||||
asn1crypto = "*"
|
||||
|
||||
[dev-packages]
|
||||
bandit = "*"
|
||||
@ -39,6 +40,7 @@ pytest-cov = "*"
|
||||
selenium = "*"
|
||||
honcho = "*"
|
||||
blinker = "*"
|
||||
pytest-mock = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6.6"
|
||||
|
85
Pipfile.lock
generated
85
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "7aff94ddfb4f3f3ebf7f7910f3ade4eebd546b297cf72863a618824f87ec76fc"
|
||||
"sha256": "975303153664e6936b5118686cb7056e8135e7c8184b7c0c029fa120c9e0b67e"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -36,6 +36,7 @@
|
||||
"sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87",
|
||||
"sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.24.0"
|
||||
},
|
||||
"certifi": {
|
||||
@ -94,10 +95,10 @@
|
||||
},
|
||||
"croniter": {
|
||||
"hashes": [
|
||||
"sha256:5389776e54a5e285d0c8e7b9a7e139a4d590f96f32958b0822d6d1b2faa12c0d",
|
||||
"sha256:fbd72189a0ff38c27e953d15175c5fedafb953479559240a1afcf8e8e7523757"
|
||||
"sha256:79a5eeaa10a7d5fb9bdae54211b8c1d306e0ed481fa970934bf3197940650d6f",
|
||||
"sha256:c31adf6a9b0b1981d362538bfa57769acaade1d62f80c264f402ce1f8d1210b4"
|
||||
],
|
||||
"version": "==0.3.27"
|
||||
"version": "==0.3.28"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
@ -209,9 +210,9 @@
|
||||
},
|
||||
"mako": {
|
||||
"hashes": [
|
||||
"sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"
|
||||
"sha256:04092940c0df49b01f43daea4f5adcecd0e50ef6a4b222be5ac003d5d84b2843"
|
||||
],
|
||||
"version": "==1.0.7"
|
||||
"version": "==1.0.8"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
@ -333,11 +334,11 @@
|
||||
},
|
||||
"redis": {
|
||||
"hashes": [
|
||||
"sha256:724932360d48e5407e8f82e405ab3650a36ed02c7e460d1e6fddf0f038422b54",
|
||||
"sha256:9b19425a38fd074eb5795ff2b0d9a55b46a44f91f5347995f27e3ad257a7d775"
|
||||
"sha256:6946b5dca72e86103edc8033019cc3814c031232d339d5f4533b02ea85685175",
|
||||
"sha256:8ca418d2ddca1b1a850afa1680a7d2fd1f3322739271de4b704e0d4668449273"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.0"
|
||||
"version": "==3.2.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
@ -417,10 +418,10 @@
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
|
||||
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
|
||||
"sha256:590abe38f8be026d78457fe3b5200895b3543e58ac3fc1dd792c6333ea11af64",
|
||||
"sha256:ee11b0f0640c56fb491b43b38356c4b588b3202b415a1e03eacf1c5561c961cf"
|
||||
],
|
||||
"version": "==0.14.1"
|
||||
"version": "==0.15.0"
|
||||
},
|
||||
"wtforms": {
|
||||
"hashes": [
|
||||
@ -491,11 +492,11 @@
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739",
|
||||
"sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"
|
||||
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
|
||||
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==18.9b0"
|
||||
"version": "==19.3b0"
|
||||
},
|
||||
"blinker": {
|
||||
"hashes": [
|
||||
@ -554,10 +555,10 @@
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:33cd704aea07b4c28b3eb2c97d288a06918275dac0ecebdaf1bc8a48d98adb9e",
|
||||
"sha256:cabb249f4710888a2fc0e13e9a16c343d932033718ff62e1e9bc93a9d3a9122b"
|
||||
"sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de",
|
||||
"sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"
|
||||
],
|
||||
"version": "==4.3.2"
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"docopt": {
|
||||
"hashes": [
|
||||
@ -575,10 +576,10 @@
|
||||
},
|
||||
"faker": {
|
||||
"hashes": [
|
||||
"sha256:16342dca4d92bfc83bab6a7daf6650e0ab087605a66bc38f17523fdb01757910",
|
||||
"sha256:d871ea315b2dcba9138b8344f2c131a76ac62d6227ca39f69b0c889fec97376c"
|
||||
"sha256:00b7011757c4907546f17d0e47df098b542ea2b04c966ee0e80a493aae2c13c8",
|
||||
"sha256:745ac8b9c9526e338696e07b7f2e206e5e317e5744e22fdd7c2894bf19af41f1"
|
||||
],
|
||||
"version": "==1.0.2"
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"flask": {
|
||||
"hashes": [
|
||||
@ -612,10 +613,10 @@
|
||||
},
|
||||
"ipdb": {
|
||||
"hashes": [
|
||||
"sha256:7081c65ed7bfe7737f83fa4213ca8afd9617b42ff6b3f1daf9a3419839a2a00a"
|
||||
"sha256:dce2112557edfe759742ca2d0fee35c59c97b0cc7a05398b791079d78f1519ce"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.11"
|
||||
"version": "==0.12"
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
@ -851,6 +852,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==0.14.0"
|
||||
},
|
||||
"pytest-mock": {
|
||||
"hashes": [
|
||||
"sha256:4d0d06d173eecf172703219a71dbd4ade0e13904e6bbce1ce660e2e0dc78b5c4",
|
||||
"sha256:bfdf02789e3d197bd682a758cae0a4a18706566395fbe2803badcd1335e0173e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.10.1"
|
||||
},
|
||||
"pytest-watch": {
|
||||
"hashes": [
|
||||
"sha256:06136f03d5b361718b8d0d234042f7b2f203910d8568f63df2f866b547b3d4b9"
|
||||
@ -867,19 +876,19 @@
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:544a0050e76e9b60751c58617fa28c253ad5d23af2e5f0b1c250390bf90bb0df",
|
||||
"sha256:594bf80477a58b6fd53e8b3f24ccf965c25eeeb6e05e4b1fb18c82c2d2090603",
|
||||
"sha256:75e20ca689d0a2bf0c84f0e2028cc68ebef34b213fa66d73c410c53f870c49f4",
|
||||
"sha256:994da68a1dc1050f290f8017f044172360b608c0f2562b47645ecc69d7a61c0a",
|
||||
"sha256:ad902e00088c50bdced94a57b819c24fdadaeaed5494df7a9a67d63774f210fd",
|
||||
"sha256:b11aff75875ffc73541c4e4b1ac2f5e21717c1fc4396238943b9a44d962e74e1",
|
||||
"sha256:bc733b5a9047c3e4848c0e80eeacfa6a799139242606410260c5450d665ea58c",
|
||||
"sha256:d960c68931b96bb215f385baa8ef867b8ebac66af60fa06cc1008f963848c7ad",
|
||||
"sha256:dd461c04e6a91e4eef7d5b75c1fc1c7013d3f8d354033b16526baadddd524079",
|
||||
"sha256:e4d6b5d6218a06f3141189d75c93876dd525a6d15f1b00ef4f274726c93719f1",
|
||||
"sha256:f3c386fa12415bde8a0162745c4badf98fe171c6dfd67e54831f05ec88feeebb"
|
||||
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
|
||||
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
|
||||
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
|
||||
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
|
||||
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
|
||||
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
|
||||
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
|
||||
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
|
||||
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
|
||||
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
|
||||
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
|
||||
],
|
||||
"version": "==5.1b5"
|
||||
"version": "==5.1"
|
||||
},
|
||||
"selenium": {
|
||||
"hashes": [
|
||||
@ -978,10 +987,10 @@
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
|
||||
"sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
|
||||
"sha256:590abe38f8be026d78457fe3b5200895b3543e58ac3fc1dd792c6333ea11af64",
|
||||
"sha256:ee11b0f0640c56fb491b43b38356c4b588b3202b415a1e03eacf1c5561c961cf"
|
||||
],
|
||||
"version": "==0.14.1"
|
||||
"version": "==0.15.0"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
|
@ -52,7 +52,7 @@ def get_current_user():
|
||||
|
||||
def logout():
|
||||
if session.get("user_id"): # pragma: no branch
|
||||
del (session["user_id"])
|
||||
del session["user_id"]
|
||||
|
||||
|
||||
def _unprotected_route(request):
|
||||
|
224
atst/utils/pdf_verification.py
Normal file
224
atst/utils/pdf_verification.py
Normal file
@ -0,0 +1,224 @@
|
||||
import hashlib
|
||||
from OpenSSL import crypto
|
||||
from asn1crypto import cms, pem, core
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
|
||||
|
||||
class PDFSignature:
|
||||
def __init__(self, byte_range_start=None, crl_check=None, pdf=None):
|
||||
self._signers_cert = None
|
||||
self._openssl_loaded_certificate = None
|
||||
self.byte_range_start = byte_range_start
|
||||
self.crl_check = crl_check
|
||||
self.pdf = pdf
|
||||
|
||||
@property
|
||||
def byte_range(self):
|
||||
"""
|
||||
This returns an array of 4 numbers that represent the byte range of
|
||||
the PDF binary file that is signed by the certificate.
|
||||
|
||||
E.G: [0, 2045, 3012, 5012]
|
||||
|
||||
Bytes 0 to 2045 - represent part A of the signed file
|
||||
Bytes 2046 to 3012 - would contain the signature and certificate information
|
||||
Bytes 3013 to 5012 - represent part B of the signed file
|
||||
"""
|
||||
start = self.pdf.find(b"[", self.byte_range_start)
|
||||
stop = self.pdf.find(b"]", start)
|
||||
contents_range = [int(i, 10) for i in self.pdf[start + 1 : stop].split()]
|
||||
|
||||
return contents_range
|
||||
|
||||
@property
|
||||
def signed_binary_data(self):
|
||||
"""
|
||||
This is the binary data stored in the signature
|
||||
"""
|
||||
br = self.byte_range
|
||||
contents = self.pdf[br[0] + br[1] + 1 : br[2] - 1]
|
||||
data = []
|
||||
|
||||
for i in range(0, len(contents), 2):
|
||||
data.append(int(contents[i : i + 2], 16))
|
||||
|
||||
return cms.ContentInfo.load(bytes(data))["content"]
|
||||
|
||||
@property
|
||||
def signers_cert(self):
|
||||
"""
|
||||
This returns the certificate used to sign the PDF
|
||||
"""
|
||||
if self._signers_cert is None:
|
||||
for cert in self.signed_binary_data["certificates"]:
|
||||
if (
|
||||
self.signers_serial
|
||||
== cert.native["tbs_certificate"]["serial_number"]
|
||||
):
|
||||
cert = cert.dump()
|
||||
self._signers_cert = pem.armor("CERTIFICATE", cert)
|
||||
break
|
||||
|
||||
return self._signers_cert
|
||||
|
||||
@property
|
||||
def signers_serial(self):
|
||||
"""
|
||||
Return the signers serial from their certificate
|
||||
"""
|
||||
return self.signed_binary_data["signer_infos"][0]["sid"].native["serial_number"]
|
||||
|
||||
@property
|
||||
def hashing_algorithm(self):
|
||||
"""
|
||||
This is the hashing algorithm used to generate the hash of binary file content
|
||||
which is then signed by the certificate.
|
||||
|
||||
E.G. sha256, sha1
|
||||
"""
|
||||
return self.signed_binary_data["digest_algorithms"][0]["algorithm"].native
|
||||
|
||||
@property
|
||||
def openssl_loaded_certificate(self):
|
||||
if self._openssl_loaded_certificate is None:
|
||||
self._openssl_loaded_certificate = crypto.load_certificate(
|
||||
crypto.FILETYPE_PEM, self.signers_cert
|
||||
)
|
||||
return self._openssl_loaded_certificate
|
||||
|
||||
@property
|
||||
def cert_common_name(self):
|
||||
"""
|
||||
This returns the common name on the certificate. This might be a name or
|
||||
a DOD ID for example.
|
||||
"""
|
||||
return self.openssl_loaded_certificate.get_subject().commonName
|
||||
|
||||
@property
|
||||
def encrypted_hash_of_signed_document(self):
|
||||
"""
|
||||
This is the calculated hash of the PDF binary data stored in the
|
||||
signature. We calculate it outselves and then compare to this
|
||||
so we can see if data has changed.
|
||||
"""
|
||||
stored_hash = None
|
||||
|
||||
for attr in self.signed_binary_data["signer_infos"][0]["signed_attrs"]:
|
||||
if attr["type"].native == "message_digest":
|
||||
stored_hash = attr["values"].native[0]
|
||||
break
|
||||
|
||||
return stored_hash
|
||||
|
||||
@property
|
||||
def binary_data(self):
|
||||
"""
|
||||
Take the byte range and return the binary data for that rage.
|
||||
"""
|
||||
br = self.byte_range
|
||||
data1 = self.pdf[br[0] : br[0] + br[1]]
|
||||
data2 = self.pdf[br[2] : br[2] + br[3]]
|
||||
|
||||
return data1 + data2
|
||||
|
||||
@property
|
||||
def hashed_binary_data(self):
|
||||
"""
|
||||
Takes the data in the byte range and hashes it using
|
||||
the hashing algorithm specified in the signed PDF. We
|
||||
can later compare this to the encrypted_hash_of_signed_document.
|
||||
"""
|
||||
return getattr(hashlib, self.hashing_algorithm)(self.binary_data)
|
||||
|
||||
@property
|
||||
def is_cert_valid(self):
|
||||
"""
|
||||
Takes the signing certificate and runs it through the CRLCache
|
||||
checker. Returns a boolean.
|
||||
"""
|
||||
return self.crl_check(self.signers_cert)
|
||||
|
||||
@property
|
||||
def is_signature_valid(self):
|
||||
"""
|
||||
Get signed PDF signature and determine if it was actually signed
|
||||
by the certificate that it claims it was. Returns a boolean.
|
||||
"""
|
||||
public_key = self.openssl_loaded_certificate.get_pubkey().to_cryptography_key()
|
||||
attrs = self.signed_binary_data["signer_infos"][0]["signed_attrs"]
|
||||
signed_data = None
|
||||
|
||||
if attrs is not None and not isinstance(attrs, core.Void):
|
||||
signed_data = attrs.dump()
|
||||
signed_data = b"\x31" + signed_data[1:]
|
||||
else:
|
||||
signed_data = self.binary_data
|
||||
|
||||
try:
|
||||
public_key.verify(
|
||||
bytes(self.signed_binary_data["signer_infos"][0]["signature"]),
|
||||
signed_data,
|
||||
padding.PKCS1v15(),
|
||||
getattr(hashes, self.hashing_algorithm.upper())(),
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@property
|
||||
def to_dict(self):
|
||||
is_cert_valid = self.is_cert_valid
|
||||
is_signature_valid = self.is_signature_valid
|
||||
is_hash_valid = (
|
||||
self.hashed_binary_data.digest() == self.encrypted_hash_of_signed_document
|
||||
)
|
||||
|
||||
return {
|
||||
"cert_common_name": self.cert_common_name,
|
||||
"hashed_binary_data": self.hashed_binary_data.hexdigest(),
|
||||
"hashing_algorithm": self.hashing_algorithm,
|
||||
"is_valid": is_cert_valid and is_hash_valid and is_signature_valid,
|
||||
"is_valid_cert": is_cert_valid,
|
||||
"is_valid_hash": is_hash_valid,
|
||||
"is_valid_signature": is_signature_valid,
|
||||
"signers_serial": self.signers_serial,
|
||||
}
|
||||
|
||||
|
||||
def pdf_signature_validations(pdf=None, crl_check=None):
|
||||
"""
|
||||
As arguments we accept a pdf binary blob and a callable crl_check.
|
||||
An example implementation of the crl_check can be found in the
|
||||
tests (test/utils/test_pdf_verification.py)
|
||||
"""
|
||||
signatures = []
|
||||
start_byte = 0
|
||||
|
||||
while True:
|
||||
start = start_byte + 1
|
||||
n = pdf.find(b"/ByteRange", start)
|
||||
|
||||
if n == -1:
|
||||
break
|
||||
|
||||
signatures.append(
|
||||
PDFSignature(byte_range_start=n, crl_check=crl_check, pdf=pdf)
|
||||
)
|
||||
start_byte = n
|
||||
|
||||
response = {"result": None, "signature_count": len(signatures), "signatures": []}
|
||||
|
||||
for signature in signatures:
|
||||
sig = signature.to_dict
|
||||
response["signatures"].append(sig)
|
||||
|
||||
if not sig["is_valid"]:
|
||||
response["result"] = False
|
||||
elif response["result"] is not False:
|
||||
response["result"] = True
|
||||
|
||||
if len(signatures) == 0:
|
||||
response["result"] = False
|
||||
|
||||
return response
|
BIN
tests/fixtures/sally-darth-signed.pdf
vendored
Normal file
BIN
tests/fixtures/sally-darth-signed.pdf
vendored
Normal file
Binary file not shown.
BIN
tests/fixtures/signed-expired-cert.pdf
vendored
Normal file
BIN
tests/fixtures/signed-expired-cert.pdf
vendored
Normal file
Binary file not shown.
BIN
tests/fixtures/signed-pdf-not-dod.pdf
vendored
Normal file
BIN
tests/fixtures/signed-pdf-not-dod.pdf
vendored
Normal file
Binary file not shown.
145
tests/utils/test_pdf_verification.py
Normal file
145
tests/utils/test_pdf_verification.py
Normal file
@ -0,0 +1,145 @@
|
||||
import pytest
|
||||
|
||||
import cryptography
|
||||
from atst.domain.authnid.crl import CRLCache, CRLRevocationException
|
||||
from atst.utils.pdf_verification import pdf_signature_validations
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def crl_check():
|
||||
def _crl_check(signers_cert):
|
||||
try:
|
||||
cache = CRLCache(
|
||||
"ssl/server-certs/ca-chain.pem",
|
||||
crl_locations=["ssl/client-certs/client-ca.der.crl"],
|
||||
)
|
||||
return cache.crl_check(signers_cert)
|
||||
except CRLRevocationException:
|
||||
return False
|
||||
|
||||
return _crl_check
|
||||
|
||||
|
||||
def test_unsigned_pdf(crl_check):
|
||||
unsigned_pdf = open("tests/fixtures/sample.pdf", "rb").read()
|
||||
result = pdf_signature_validations(pdf=unsigned_pdf, crl_check=crl_check)
|
||||
|
||||
assert result == {"result": False, "signature_count": 0, "signatures": []}
|
||||
|
||||
|
||||
def test_valid_signed_pdf(crl_check):
|
||||
valid_signed_pdf = open("tests/fixtures/sally-darth-signed.pdf", "rb").read()
|
||||
result = pdf_signature_validations(pdf=valid_signed_pdf, crl_check=crl_check)
|
||||
|
||||
assert result == {
|
||||
"result": True,
|
||||
"signature_count": 2,
|
||||
"signatures": [
|
||||
{
|
||||
"cert_common_name": "WILLIAMS.SALLY.3453453453",
|
||||
"hashed_binary_data": "b879a15e19eece534dc63019d3fe539ff4a3efbf8e8f5403a8bdae26a9b713ea",
|
||||
"hashing_algorithm": "sha256",
|
||||
"is_valid": True,
|
||||
"is_valid_cert": True,
|
||||
"is_valid_hash": True,
|
||||
"is_valid_signature": True,
|
||||
"signers_serial": 9_662_248_800_192_484_626,
|
||||
},
|
||||
{
|
||||
"cert_common_name": "VADER.DARTH.9012345678",
|
||||
"hashed_binary_data": "d98339766c20a369219f236220d7b450111554acc902e242d015dd6d306c7809",
|
||||
"hashing_algorithm": "sha256",
|
||||
"is_valid": True,
|
||||
"is_valid_cert": True,
|
||||
"is_valid_hash": True,
|
||||
"is_valid_signature": True,
|
||||
"signers_serial": 9_662_248_800_192_484_627,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_signed_pdf_thats_been_modified(crl_check):
|
||||
valid_signed_pdf = open("tests/fixtures/sally-darth-signed.pdf", "rb").read()
|
||||
modified_pdf = valid_signed_pdf.replace(b"PDF-1.6", b"PDF-1.7")
|
||||
result = pdf_signature_validations(pdf=modified_pdf, crl_check=crl_check)
|
||||
|
||||
assert result == {
|
||||
"result": False,
|
||||
"signature_count": 2,
|
||||
"signatures": [
|
||||
{
|
||||
"cert_common_name": "WILLIAMS.SALLY.3453453453",
|
||||
"hashed_binary_data": "d1fb3c955b57f139331586276ba4abca90ecc5d36b53fe6bbbbbd8707d7124bb",
|
||||
"hashing_algorithm": "sha256",
|
||||
"is_valid": False,
|
||||
"is_valid_cert": True,
|
||||
"is_valid_hash": False,
|
||||
"is_valid_signature": True,
|
||||
"signers_serial": 9_662_248_800_192_484_626,
|
||||
},
|
||||
{
|
||||
"cert_common_name": "VADER.DARTH.9012345678",
|
||||
"hashed_binary_data": "75ef47824de4b5477c75665c5a90e39a2b8a8985422cf2f7f641661a7b5217a8",
|
||||
"hashing_algorithm": "sha256",
|
||||
"is_valid": False,
|
||||
"is_valid_cert": True,
|
||||
"is_valid_hash": False,
|
||||
"is_valid_signature": True,
|
||||
"signers_serial": 9_662_248_800_192_484_627,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_signed_pdf_that_has_invalid_signature(mocker):
|
||||
def mock_crl_check(_):
|
||||
return True
|
||||
|
||||
mocker.patch.object(
|
||||
cryptography.hazmat.backends.openssl.rsa._RSAPublicKey, "verify", Exception()
|
||||
)
|
||||
|
||||
valid_signed_pdf = open("tests/fixtures/signed-pdf-not-dod.pdf", "rb").read()
|
||||
result = pdf_signature_validations(pdf=valid_signed_pdf, crl_check=mock_crl_check)
|
||||
|
||||
assert result == {
|
||||
"result": False,
|
||||
"signature_count": 1,
|
||||
"signatures": [
|
||||
{
|
||||
"cert_common_name": "John B Harris",
|
||||
"hashed_binary_data": "3f0047e6cb5b9bb089254b20d174445c3ba4f513",
|
||||
"hashing_algorithm": "sha1",
|
||||
"is_valid": False,
|
||||
"is_valid_cert": True,
|
||||
"is_valid_hash": True,
|
||||
"is_valid_signature": False,
|
||||
"signers_serial": 514,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Need fixture file")
|
||||
def test_signed_pdf_dod_revoked(crl_check):
|
||||
signed_pdf_dod_revoked = open(
|
||||
"tests/fixtures/signed-pdf-dod_revoked.pdf", "rb"
|
||||
).read()
|
||||
result = pdf_signature_validations(pdf=signed_pdf_dod_revoked, crl_check=crl_check)
|
||||
|
||||
assert result == {
|
||||
"result": False,
|
||||
"signature_count": 1,
|
||||
"signatures": [
|
||||
{
|
||||
"cert_common_name": None,
|
||||
"hashed_binary_data": None,
|
||||
"hashing_algorithm": None,
|
||||
"is_valid": None,
|
||||
"is_valid_cert": None,
|
||||
"is_valid_hash": None,
|
||||
"signers_serial": None,
|
||||
}
|
||||
],
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user